/* * GStreamer * Copyright (C) 2012-2013 Fluendo S.A. * Authors: Josep Torra Vallès * Andoni Morales Alastruey * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #include "gstosxcoreaudio.h" #include "gstosxcoreaudiocommon.h" GST_DEBUG_CATEGORY_STATIC (osx_audio_debug); #define GST_CAT_DEFAULT osx_audio_debug G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT); #ifdef HAVE_IOS #include "gstosxcoreaudioremoteio.c" #else #include "gstosxcoreaudiohal.c" #endif static void gst_core_audio_class_init (GstCoreAudioClass * klass) { } static void gst_core_audio_init (GstCoreAudio * core_audio) { core_audio->is_passthrough = FALSE; core_audio->device_id = kAudioDeviceUnknown; core_audio->is_src = FALSE; core_audio->audiounit = NULL; #ifndef HAVE_IOS core_audio->hog_pid = -1; core_audio->disabled_mixing = FALSE; #endif } /************************** * Public API * *************************/ GstCoreAudio * gst_core_audio_new (GstObject * osxbuf) { GstCoreAudio *core_audio; core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL); core_audio->osxbuf = osxbuf; return core_audio; } gboolean gst_core_audio_close (GstCoreAudio * core_audio) { AudioComponentInstanceDispose (core_audio->audiounit); core_audio->audiounit = NULL; return TRUE; } gboolean gst_core_audio_open (GstCoreAudio * core_audio) { return gst_core_audio_open_impl (core_audio); } gboolean gst_core_audio_start_processing (GstCoreAudio * core_audio) { return gst_core_audio_start_processing_impl (core_audio); } gboolean gst_core_audio_pause_processing (GstCoreAudio * core_audio) { return gst_core_audio_pause_processing_impl (core_audio); } gboolean gst_core_audio_stop_processing (GstCoreAudio * core_audio) { return gst_core_audio_stop_processing_impl (core_audio); } gboolean gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio, gdouble rate, guint * samples, gdouble * latency) { return gst_core_audio_get_samples_and_latency_impl (core_audio, rate, samples, latency); } gboolean gst_core_audio_initialize (GstCoreAudio * core_audio, AudioStreamBasicDescription format, GstCaps * caps, gboolean is_passthrough) { guint32 frame_size; OSStatus status; GST_DEBUG_OBJECT (core_audio, "Initializing: passthrough:%d caps:%" GST_PTR_FORMAT, is_passthrough, caps); if (!gst_core_audio_initialize_impl (core_audio, format, caps, is_passthrough, &frame_size)) { goto error; } if (core_audio->is_src) { /* create AudioBufferList needed for recording */ core_audio->recBufferSize = frame_size * format.mBytesPerFrame; core_audio->recBufferList = buffer_list_alloc (format.mChannelsPerFrame, core_audio->recBufferSize, /* Currently always TRUE (i.e. interleaved) */ !(format.mFormatFlags & kAudioFormatFlagIsNonInterleaved)); } /* Initialize the AudioUnit */ status = AudioUnitInitialize (core_audio->audiounit); if (status) { GST_ERROR_OBJECT (core_audio, "Failed to initialise AudioUnit: %d", (int) status); goto error; } return TRUE; error: buffer_list_free (core_audio->recBufferList); core_audio->recBufferList = NULL; return FALSE; } void gst_core_audio_unitialize (GstCoreAudio * core_audio) { AudioUnitUninitialize (core_audio->audiounit); buffer_list_free (core_audio->recBufferList); core_audio->recBufferList = NULL; } void gst_core_audio_set_volume (GstCoreAudio * core_audio, gfloat volume) { AudioUnitSetParameter (core_audio->audiounit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, (float) volume, 0); } gboolean gst_core_audio_select_device (GstCoreAudio * core_audio) { return gst_core_audio_select_device_impl (core_audio); } void gst_core_audio_init_debug (void) { GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0, "OSX Audio Elements"); } gboolean gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id) { return gst_core_audio_audio_device_is_spdif_avail_impl (device_id); } gboolean gst_core_audio_parse_channel_layout (AudioChannelLayout * layout, gint channels, guint64 * channel_mask, GstAudioChannelPosition * pos) { gint i; gboolean ret = TRUE; g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE); switch (channels) { case 0: pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE; break; case 1: pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO; break; case 2: pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; *channel_mask |= GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT); *channel_mask |= GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT); break; default: for (i = 0; i < channels; i++) { switch (layout->mChannelDescriptions[i].mChannelLabel) { case kAudioChannelLabel_Left: pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; break; case kAudioChannelLabel_Right: pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; break; case kAudioChannelLabel_Center: pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; break; case kAudioChannelLabel_LFEScreen: pos[i] = GST_AUDIO_CHANNEL_POSITION_LFE1; break; case kAudioChannelLabel_LeftSurround: pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT; break; case kAudioChannelLabel_RightSurround: pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT; break; case kAudioChannelLabel_RearSurroundLeft: pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT; break; case kAudioChannelLabel_RearSurroundRight: pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT; break; case kAudioChannelLabel_CenterSurround: pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER; break; default: GST_WARNING ("unrecognized channel: %d", (int) layout->mChannelDescriptions[i].mChannelLabel); *channel_mask = 0; ret = FALSE; break; } } } return ret; } GstCaps * gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd, AudioChannelLayout * layout) { GstAudioInfo info; GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN; GstAudioChannelPosition pos[64] = { GST_AUDIO_CHANNEL_POSITION_INVALID, }; gint rate, channels, bps, endianness; guint64 channel_mask; gboolean sign, interleaved; if (asbd->mFormatID != kAudioFormatLinearPCM) { GST_WARNING ("Only linear PCM is supported"); goto error; } if (!(asbd->mFormatFlags & kAudioFormatFlagIsPacked)) { GST_WARNING ("Only packed formats supported"); goto error; } if (asbd->mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) { GST_WARNING ("Fixed point audio is unsupported"); goto error; } rate = asbd->mSampleRate; if (rate == kAudioStreamAnyRate) rate = GST_AUDIO_DEF_RATE; channels = asbd->mChannelsPerFrame; if (channels == 0) { /* The documentation says this should not happen! */ channels = 1; } bps = asbd->mBitsPerChannel; endianness = asbd->mFormatFlags & kAudioFormatFlagIsBigEndian ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; sign = asbd->mFormatID & kAudioFormatFlagIsSignedInteger ? TRUE : FALSE; interleaved = asbd->mFormatFlags & kAudioFormatFlagIsNonInterleaved ? TRUE : FALSE; if (asbd->mFormatFlags & kAudioFormatFlagIsFloat) { if (bps == 32) { if (endianness == G_LITTLE_ENDIAN) format = GST_AUDIO_FORMAT_F32LE; else format = GST_AUDIO_FORMAT_F32BE; } else if (bps == 64) { if (endianness == G_LITTLE_ENDIAN) format = GST_AUDIO_FORMAT_F64LE; else format = GST_AUDIO_FORMAT_F64BE; } } else { format = gst_audio_format_build_integer (sign, endianness, bps, bps); } if (format == GST_AUDIO_FORMAT_UNKNOWN) { GST_WARNING ("Unsupported sample format"); goto error; } if (!gst_core_audio_parse_channel_layout (layout, channels, &channel_mask, pos)) { GST_WARNING ("Failed to parse channel layout"); goto error; } gst_audio_info_set_format (&info, format, rate, channels, pos); return gst_audio_info_to_caps (&info); error: return NULL; }