• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com>
4  *   Authors: Josep Torra Vallès <josep@fluendo.com>
5  *            Andoni Morales Alastruey <amorales@fluendo.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  */
23 
24 #include "gstosxcoreaudio.h"
25 #include "gstosxcoreaudiocommon.h"
26 
27 GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
28 #define GST_CAT_DEFAULT osx_audio_debug
29 
30 G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
31 
32 #ifdef HAVE_IOS
33 #include "gstosxcoreaudioremoteio.c"
34 #else
35 #include "gstosxcoreaudiohal.c"
36 #endif
37 
38 
39 static void
gst_core_audio_class_init(GstCoreAudioClass * klass)40 gst_core_audio_class_init (GstCoreAudioClass * klass)
41 {
42 }
43 
44 static void
gst_core_audio_init(GstCoreAudio * core_audio)45 gst_core_audio_init (GstCoreAudio * core_audio)
46 {
47   core_audio->is_passthrough = FALSE;
48   core_audio->device_id = kAudioDeviceUnknown;
49   core_audio->is_src = FALSE;
50   core_audio->audiounit = NULL;
51   core_audio->cached_caps = NULL;
52   core_audio->cached_caps_valid = FALSE;
53 #ifndef HAVE_IOS
54   core_audio->hog_pid = -1;
55   core_audio->disabled_mixing = FALSE;
56 #endif
57 }
58 
59 static gboolean
_is_outer_scope(AudioUnitScope scope,AudioUnitElement element)60 _is_outer_scope (AudioUnitScope scope, AudioUnitElement element)
61 {
62   return
63       (scope == kAudioUnitScope_Input && element == 1) ||
64       (scope == kAudioUnitScope_Output && element == 0);
65 }
66 
67 static void
_audio_unit_property_listener(void * inRefCon,AudioUnit inUnit,AudioUnitPropertyID inID,AudioUnitScope inScope,AudioUnitElement inElement)68 _audio_unit_property_listener (void *inRefCon, AudioUnit inUnit,
69     AudioUnitPropertyID inID, AudioUnitScope inScope,
70     AudioUnitElement inElement)
71 {
72   GstCoreAudio *core_audio;
73 
74   core_audio = GST_CORE_AUDIO (inRefCon);
75   g_assert (inUnit == core_audio->audiounit);
76 
77   switch (inID) {
78     case kAudioUnitProperty_AudioChannelLayout:
79     case kAudioUnitProperty_StreamFormat:
80       if (_is_outer_scope (inScope, inElement)) {
81         /* We don't push gst_event_new_caps here (for src),
82          * nor gst_event_new_reconfigure (for sink), since Core Audio continues
83          * to happily function with the old format, doing conversion/resampling
84          * as needed.
85          * This merely "refreshes" our PREFERRED caps. */
86 
87         /* This function is called either from a Core Audio thread
88          * or as a result of a Core Audio API (e.g. AudioUnitInitialize)
89          * from our own thread. In the latter case, osxbuf can be
90          * already locked (GStreamer's mutex is not recursive).
91          * For this reason we use a boolean flag instead of nullifying
92          * cached_caps. */
93         core_audio->cached_caps_valid = FALSE;
94       }
95       break;
96   }
97 }
98 
99 /**************************
100  *       Public API       *
101  *************************/
102 
103 GstCoreAudio *
gst_core_audio_new(GstObject * osxbuf)104 gst_core_audio_new (GstObject * osxbuf)
105 {
106   GstCoreAudio *core_audio;
107 
108   core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
109   core_audio->osxbuf = osxbuf;
110   core_audio->cached_caps = NULL;
111   return core_audio;
112 }
113 
114 gboolean
gst_core_audio_close(GstCoreAudio * core_audio)115 gst_core_audio_close (GstCoreAudio * core_audio)
116 {
117   OSStatus status;
118 
119   /* Uninitialize the AudioUnit */
120   status = AudioUnitUninitialize (core_audio->audiounit);
121   if (status) {
122     GST_ERROR_OBJECT (core_audio, "Failed to uninitialize AudioUnit: %d",
123         (int) status);
124     return FALSE;
125   }
126 
127   AudioUnitRemovePropertyListenerWithUserData (core_audio->audiounit,
128       kAudioUnitProperty_AudioChannelLayout, _audio_unit_property_listener,
129       core_audio);
130   AudioUnitRemovePropertyListenerWithUserData (core_audio->audiounit,
131       kAudioUnitProperty_StreamFormat, _audio_unit_property_listener,
132       core_audio);
133 
134   /* core_audio->osxbuf is already locked at this point */
135   core_audio->cached_caps_valid = FALSE;
136   gst_caps_replace (&core_audio->cached_caps, NULL);
137 
138   AudioComponentInstanceDispose (core_audio->audiounit);
139   core_audio->audiounit = NULL;
140   return TRUE;
141 }
142 
143 gboolean
gst_core_audio_open(GstCoreAudio * core_audio)144 gst_core_audio_open (GstCoreAudio * core_audio)
145 {
146   OSStatus status;
147 
148   /* core_audio->osxbuf is already locked at this point */
149   core_audio->cached_caps_valid = FALSE;
150   gst_caps_replace (&core_audio->cached_caps, NULL);
151 
152   if (!gst_core_audio_open_impl (core_audio))
153     return FALSE;
154 
155   /* Add property listener */
156   status = AudioUnitAddPropertyListener (core_audio->audiounit,
157       kAudioUnitProperty_AudioChannelLayout, _audio_unit_property_listener,
158       core_audio);
159   if (status != noErr) {
160     GST_ERROR_OBJECT (core_audio, "Failed to add audio channel layout property "
161         "listener for AudioUnit: %d", (int) status);
162   }
163   status = AudioUnitAddPropertyListener (core_audio->audiounit,
164       kAudioUnitProperty_StreamFormat, _audio_unit_property_listener,
165       core_audio);
166   if (status != noErr) {
167     GST_ERROR_OBJECT (core_audio, "Failed to add stream format property "
168         "listener for AudioUnit: %d", (int) status);
169   }
170 
171   /* Initialize the AudioUnit. We keep the audio unit initialized early so that
172    * we can probe the underlying device. */
173   status = AudioUnitInitialize (core_audio->audiounit);
174   if (status) {
175     GST_ERROR_OBJECT (core_audio, "Failed to initialize AudioUnit: %d",
176         (int) status);
177     return FALSE;
178   }
179 
180   return TRUE;
181 }
182 
183 gboolean
gst_core_audio_start_processing(GstCoreAudio * core_audio)184 gst_core_audio_start_processing (GstCoreAudio * core_audio)
185 {
186   return gst_core_audio_start_processing_impl (core_audio);
187 }
188 
189 gboolean
gst_core_audio_pause_processing(GstCoreAudio * core_audio)190 gst_core_audio_pause_processing (GstCoreAudio * core_audio)
191 {
192   return gst_core_audio_pause_processing_impl (core_audio);
193 }
194 
195 gboolean
gst_core_audio_stop_processing(GstCoreAudio * core_audio)196 gst_core_audio_stop_processing (GstCoreAudio * core_audio)
197 {
198   return gst_core_audio_stop_processing_impl (core_audio);
199 }
200 
201 gboolean
gst_core_audio_get_samples_and_latency(GstCoreAudio * core_audio,gdouble rate,guint * samples,gdouble * latency)202 gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
203     gdouble rate, guint * samples, gdouble * latency)
204 {
205   return gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
206       samples, latency);
207 }
208 
209 gboolean
gst_core_audio_initialize(GstCoreAudio * core_audio,AudioStreamBasicDescription format,GstCaps * caps,gboolean is_passthrough)210 gst_core_audio_initialize (GstCoreAudio * core_audio,
211     AudioStreamBasicDescription format, GstCaps * caps, gboolean is_passthrough)
212 {
213   guint32 frame_size;
214 
215   GST_DEBUG_OBJECT (core_audio,
216       "Initializing: passthrough:%d caps:%" GST_PTR_FORMAT, is_passthrough,
217       caps);
218 
219   if (!gst_core_audio_initialize_impl (core_audio, format, caps,
220           is_passthrough, &frame_size)) {
221     return FALSE;
222   }
223 
224   if (core_audio->is_src) {
225     /* create AudioBufferList needed for recording */
226     core_audio->recBufferSize = frame_size * format.mBytesPerFrame;
227     core_audio->recBufferList =
228         buffer_list_alloc (format.mChannelsPerFrame, core_audio->recBufferSize,
229         /* Currently always TRUE (i.e. interleaved) */
230         !(format.mFormatFlags & kAudioFormatFlagIsNonInterleaved));
231   }
232 
233   return TRUE;
234 }
235 
236 void
gst_core_audio_uninitialize(GstCoreAudio * core_audio)237 gst_core_audio_uninitialize (GstCoreAudio * core_audio)
238 {
239   buffer_list_free (core_audio->recBufferList);
240   core_audio->recBufferList = NULL;
241 }
242 
243 void
gst_core_audio_set_volume(GstCoreAudio * core_audio,gfloat volume)244 gst_core_audio_set_volume (GstCoreAudio * core_audio, gfloat volume)
245 {
246   AudioUnitSetParameter (core_audio->audiounit, kHALOutputParam_Volume,
247       kAudioUnitScope_Global, 0, (float) volume, 0);
248 }
249 
250 gboolean
gst_core_audio_select_device(GstCoreAudio * core_audio)251 gst_core_audio_select_device (GstCoreAudio * core_audio)
252 {
253   return gst_core_audio_select_device_impl (core_audio);
254 }
255 
256 void
gst_core_audio_init_debug(void)257 gst_core_audio_init_debug (void)
258 {
259   GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
260       "OSX Audio Elements");
261 }
262 
263 gboolean
gst_core_audio_audio_device_is_spdif_avail(AudioDeviceID device_id)264 gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id)
265 {
266   return gst_core_audio_audio_device_is_spdif_avail_impl (device_id);
267 }
268 
269 /* Does the channel have at least one positioned channel?
270  * (GStreamer is more strict than Core Audio, in that it requires either
271  * all channels to be positioned, or all unpositioned.) */
272 static gboolean
_is_core_audio_layout_positioned(AudioChannelLayout * layout)273 _is_core_audio_layout_positioned (AudioChannelLayout * layout)
274 {
275   guint i;
276 
277   g_assert (layout->mChannelLayoutTag ==
278       kAudioChannelLayoutTag_UseChannelDescriptions);
279 
280   for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
281     GstAudioChannelPosition p =
282         gst_core_audio_channel_label_to_gst
283         (layout->mChannelDescriptions[i].mChannelLabel, i, FALSE);
284 
285     if (p >= 0)                 /* not special positition */
286       return TRUE;
287   }
288 
289   return FALSE;
290 }
291 
292 static void
_core_audio_parse_channel_descriptions(AudioChannelLayout * layout,guint * channels,guint64 * channel_mask,GstAudioChannelPosition * pos)293 _core_audio_parse_channel_descriptions (AudioChannelLayout * layout,
294     guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
295 {
296   gboolean positioned;
297   guint i;
298 
299   g_assert (layout->mChannelLayoutTag ==
300       kAudioChannelLayoutTag_UseChannelDescriptions);
301 
302   positioned = _is_core_audio_layout_positioned (layout);
303   *channel_mask = 0;
304 
305   /* Go over all labels, either taking only positioned or only
306    * unpositioned channels, up to GST_OSX_AUDIO_MAX_CHANNEL channels.
307    *
308    * The resulting 'pos' array will contain either:
309    *  - only regular (>= 0) positions
310    *  - only GST_AUDIO_CHANNEL_POSITION_NONE positions
311    * in a compact form, skipping over all unsupported positions.
312    */
313   *channels = 0;
314   for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
315     GstAudioChannelPosition p =
316         gst_core_audio_channel_label_to_gst
317         (layout->mChannelDescriptions[i].mChannelLabel, i, TRUE);
318 
319     /* In positioned layouts, skip all unpositioned channels.
320      * In unpositioned layouts, skip all invalid channels. */
321     if ((positioned && p >= 0) ||
322         (!positioned && p == GST_AUDIO_CHANNEL_POSITION_NONE)) {
323 
324       if (pos)
325         pos[*channels] = p;
326       *channel_mask |= G_GUINT64_CONSTANT (1) << p;
327       ++(*channels);
328 
329       if (*channels == GST_OSX_AUDIO_MAX_CHANNEL)
330         break;                  /* not to overflow */
331     }
332   }
333 }
334 
335 gboolean
gst_core_audio_parse_channel_layout(AudioChannelLayout * layout,guint * channels,guint64 * channel_mask,GstAudioChannelPosition * pos)336 gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
337     guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
338 {
339   g_assert (channels != NULL);
340   g_assert (channel_mask != NULL);
341   g_assert (layout != NULL);
342 
343   if (layout->mChannelLayoutTag ==
344       kAudioChannelLayoutTag_UseChannelDescriptions) {
345 
346     switch (layout->mNumberChannelDescriptions) {
347       case 0:
348         if (pos)
349           pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE;
350         *channels = 0;
351         *channel_mask = 0;
352         return TRUE;
353       case 1:
354         if (pos)
355           pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
356         *channels = 1;
357         *channel_mask = 0;
358         return TRUE;
359       case 2:
360         if (pos) {
361           pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
362           pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
363         }
364         *channels = 2;
365         *channel_mask =
366             GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
367             GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
368         return TRUE;
369       default:
370         _core_audio_parse_channel_descriptions (layout, channels, channel_mask,
371             pos);
372         return TRUE;
373     }
374   } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Mono) {
375     if (pos)
376       pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
377     *channels = 1;
378     *channel_mask = 0;
379     return TRUE;
380   } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Stereo ||
381       layout->mChannelLayoutTag == kAudioChannelLayoutTag_StereoHeadphones ||
382       layout->mChannelLayoutTag == kAudioChannelLayoutTag_Binaural) {
383     if (pos) {
384       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
385       pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
386     }
387     *channels = 2;
388     *channel_mask =
389         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
390         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
391     return TRUE;
392   } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Quadraphonic) {
393     if (pos) {
394       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
395       pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
396       pos[2] = GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT;
397       pos[3] = GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT;
398     }
399     *channels = 4;
400     *channel_mask =
401         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
402         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT) |
403         GST_AUDIO_CHANNEL_POSITION_MASK (SURROUND_LEFT) |
404         GST_AUDIO_CHANNEL_POSITION_MASK (SURROUND_RIGHT);
405     return TRUE;
406   } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Pentagonal) {
407     if (pos) {
408       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
409       pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
410       pos[2] = GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT;
411       pos[3] = GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT;
412       pos[4] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
413     }
414     *channels = 5;
415     *channel_mask =
416         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
417         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT) |
418         GST_AUDIO_CHANNEL_POSITION_MASK (SURROUND_LEFT) |
419         GST_AUDIO_CHANNEL_POSITION_MASK (SURROUND_RIGHT) |
420         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_CENTER);
421     return TRUE;
422   } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Cube) {
423     if (pos) {
424       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
425       pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
426       pos[2] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
427       pos[3] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
428       pos[4] = GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT;
429       pos[5] = GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT;
430       pos[6] = GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT;
431       pos[7] = GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT;
432 
433     }
434     *channels = 8;
435     *channel_mask =
436         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
437         GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT) |
438         GST_AUDIO_CHANNEL_POSITION_MASK (REAR_LEFT) |
439         GST_AUDIO_CHANNEL_POSITION_MASK (REAR_RIGHT) |
440         GST_AUDIO_CHANNEL_POSITION_MASK (TOP_FRONT_LEFT) |
441         GST_AUDIO_CHANNEL_POSITION_MASK (TOP_FRONT_RIGHT) |
442         GST_AUDIO_CHANNEL_POSITION_MASK (TOP_REAR_LEFT) |
443         GST_AUDIO_CHANNEL_POSITION_MASK (TOP_REAR_RIGHT);
444     return TRUE;
445   } else {
446     GST_WARNING
447         ("AudioChannelLayoutTag: %u not yet supported",
448         layout->mChannelLayoutTag);
449     *channels = 0;
450     *channel_mask = 0;
451     return FALSE;
452   }
453 }
454 
455 /* Converts an AudioStreamBasicDescription to preferred caps.
456  *
457  * These caps will indicate the AU element's canonical format, which won't
458  * make Core Audio resample nor convert.
459  *
460  * NOTE ON MULTI-CHANNEL AUDIO:
461  *
462  * If layout is not NULL, resulting caps will only include the subset
463  * of channels supported by GStreamer. If the Core Audio layout contained
464  * ANY positioned channels, then ONLY positioned channels will be included
465  * in the resulting caps. Otherwise, resulting caps will be unpositioned,
466  * and include only unpositioned channels.
467  * (Channels with unsupported AudioChannelLabel will be skipped either way.)
468  *
469  * Naturally, the number of channels indicated by 'channels' can be lower
470  * than the AU element's total number of channels.
471  */
472 GstCaps *
gst_core_audio_asbd_to_caps(AudioStreamBasicDescription * asbd,AudioChannelLayout * layout)473 gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
474     AudioChannelLayout * layout)
475 {
476   GstAudioInfo info;
477   GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
478   guint rate, channels, bps, endianness;
479   guint64 channel_mask;
480   gboolean sign;
481   GstAudioChannelPosition pos[GST_OSX_AUDIO_MAX_CHANNEL];
482 
483   if (asbd->mFormatID != kAudioFormatLinearPCM) {
484     GST_WARNING ("Only linear PCM is supported");
485     goto error;
486   }
487 
488   if (!(asbd->mFormatFlags & kAudioFormatFlagIsPacked)) {
489     GST_WARNING ("Only packed formats supported");
490     goto error;
491   }
492 
493   if (asbd->mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) {
494     GST_WARNING ("Fixed point audio is unsupported");
495     goto error;
496   }
497 
498   rate = asbd->mSampleRate;
499   if (rate == kAudioStreamAnyRate) {
500     GST_WARNING ("No sample rate");
501     goto error;
502   }
503 
504   bps = asbd->mBitsPerChannel;
505   endianness = asbd->mFormatFlags & kAudioFormatFlagIsBigEndian ?
506       G_BIG_ENDIAN : G_LITTLE_ENDIAN;
507   sign = asbd->mFormatFlags & kAudioFormatFlagIsSignedInteger ? TRUE : FALSE;
508 
509   if (asbd->mFormatFlags & kAudioFormatFlagIsFloat) {
510     if (bps == 32) {
511       if (endianness == G_LITTLE_ENDIAN)
512         format = GST_AUDIO_FORMAT_F32LE;
513       else
514         format = GST_AUDIO_FORMAT_F32BE;
515 
516     } else if (bps == 64) {
517       if (endianness == G_LITTLE_ENDIAN)
518         format = GST_AUDIO_FORMAT_F64LE;
519       else
520         format = GST_AUDIO_FORMAT_F64BE;
521     }
522   } else {
523     format = gst_audio_format_build_integer (sign, endianness, bps, bps);
524   }
525 
526   if (format == GST_AUDIO_FORMAT_UNKNOWN) {
527     GST_WARNING ("Unsupported sample format");
528     goto error;
529   }
530 
531   if (layout) {
532     if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
533             pos)) {
534       GST_WARNING
535           ("Failed to parse channel layout, best effort channels layout mapping will be used");
536       layout = NULL;
537     }
538   }
539 
540   if (layout) {
541     /* The AU can have arbitrary channel order, but we're using GstAudioInfo
542      * which supports only the GStreamer channel order.
543      * Also, we're eventually producing caps, which only have channel-mask
544      * (whose implied order is the GStreamer channel order). */
545     gst_audio_channel_positions_to_valid_order (pos, channels);
546 
547     gst_audio_info_set_format (&info, format, rate, channels, pos);
548   } else {
549     channels = MIN (asbd->mChannelsPerFrame, GST_OSX_AUDIO_MAX_CHANNEL);
550     gst_audio_info_set_format (&info, format, rate, channels, NULL);
551   }
552 
553   return gst_audio_info_to_caps (&info);
554 
555 error:
556   return NULL;
557 }
558 
559 static gboolean
_core_audio_get_property(GstCoreAudio * core_audio,gboolean outer,AudioUnitPropertyID inID,void * inData,UInt32 * inDataSize)560 _core_audio_get_property (GstCoreAudio * core_audio, gboolean outer,
561     AudioUnitPropertyID inID, void *inData, UInt32 * inDataSize)
562 {
563   OSStatus status;
564   AudioUnitScope scope;
565   AudioUnitElement element;
566 
567   scope = outer ?
568       CORE_AUDIO_OUTER_SCOPE (core_audio) : CORE_AUDIO_INNER_SCOPE (core_audio);
569   element = CORE_AUDIO_ELEMENT (core_audio);
570 
571   status =
572       AudioUnitGetProperty (core_audio->audiounit, inID, scope, element, inData,
573       inDataSize);
574 
575   return status == noErr;
576 }
577 
578 static gboolean
_core_audio_get_stream_format(GstCoreAudio * core_audio,AudioStreamBasicDescription * asbd,gboolean outer)579 _core_audio_get_stream_format (GstCoreAudio * core_audio,
580     AudioStreamBasicDescription * asbd, gboolean outer)
581 {
582   UInt32 size;
583 
584   size = sizeof (AudioStreamBasicDescription);
585   return _core_audio_get_property (core_audio, outer,
586       kAudioUnitProperty_StreamFormat, asbd, &size);
587 }
588 
589 AudioChannelLayout *
gst_core_audio_get_channel_layout(GstCoreAudio * core_audio,gboolean outer)590 gst_core_audio_get_channel_layout (GstCoreAudio * core_audio, gboolean outer)
591 {
592   UInt32 size;
593   AudioChannelLayout *layout;
594 
595   if (core_audio->is_src) {
596     GST_WARNING_OBJECT (core_audio,
597         "gst_core_audio_get_channel_layout not supported on source.");
598     return NULL;
599   }
600 
601   if (!_core_audio_get_property (core_audio, outer,
602           kAudioUnitProperty_AudioChannelLayout, NULL, &size)) {
603     GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
604     return NULL;
605   }
606 
607   layout = g_malloc (size);
608   if (!_core_audio_get_property (core_audio, outer,
609           kAudioUnitProperty_AudioChannelLayout, layout, &size)) {
610     GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
611     g_free (layout);
612     return NULL;
613   }
614 
615   return layout;
616 }
617 
618 #define STEREO_CHANNEL_MASK \
619   (GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) | \
620    GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT))
621 
622 GstCaps *
gst_core_audio_probe_caps(GstCoreAudio * core_audio,GstCaps * in_caps)623 gst_core_audio_probe_caps (GstCoreAudio * core_audio, GstCaps * in_caps)
624 {
625   guint i, channels;
626   gboolean spdif_allowed;
627   AudioChannelLayout *layout;
628   AudioStreamBasicDescription outer_asbd;
629   gboolean got_outer_asbd;
630   GstCaps *caps = NULL;
631   guint64 channel_mask;
632 
633   /* Get the ASBD of the outer scope (i.e. input scope of Input,
634    * output scope of Output).
635    * This ASBD indicates the hardware format. */
636   got_outer_asbd =
637       _core_audio_get_stream_format (core_audio, &outer_asbd, TRUE);
638 
639   /* Collect info about the HW capabilities and preferences */
640   spdif_allowed =
641       gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id);
642   if (!core_audio->is_src)
643     layout = gst_core_audio_get_channel_layout (core_audio, TRUE);
644   else
645     layout = NULL;              /* no supported for sources */
646 
647   GST_DEBUG_OBJECT (core_audio, "Selected device ID: %u SPDIF allowed: %d",
648       (unsigned) core_audio->device_id, spdif_allowed);
649 
650   if (layout) {
651     if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
652             NULL)) {
653       GST_WARNING_OBJECT (core_audio, "Failed to parse channel layout");
654       channel_mask = 0;
655     }
656 
657     /* If available, start with the preferred caps. */
658     if (got_outer_asbd)
659       caps = gst_core_audio_asbd_to_caps (&outer_asbd, layout);
660 
661     g_free (layout);
662   } else if (got_outer_asbd) {
663     channels = outer_asbd.mChannelsPerFrame;
664     channel_mask = 0;
665     /* If available, start with the preferred caps */
666     caps = gst_core_audio_asbd_to_caps (&outer_asbd, NULL);
667   } else {
668     GST_ERROR_OBJECT (core_audio,
669         "Unable to get any information about hardware");
670     return NULL;
671   }
672 
673   /* Append the allowed subset based on the template caps  */
674   if (!caps)
675     caps = gst_caps_new_empty ();
676   for (i = 0; i < gst_caps_get_size (in_caps); i++) {
677     GstStructure *in_s;
678 
679     in_s = gst_caps_get_structure (in_caps, i);
680 
681     if (gst_structure_has_name (in_s, "audio/x-ac3") ||
682         gst_structure_has_name (in_s, "audio/x-dts")) {
683       if (spdif_allowed) {
684         gst_caps_append_structure (caps, gst_structure_copy (in_s));
685       }
686     } else {
687       GstStructure *out_s;
688 
689       out_s = gst_structure_copy (in_s);
690       gst_structure_set (out_s, "channels", G_TYPE_INT, channels, NULL);
691       if (channel_mask != 0) {
692         /* positioned layout */
693         gst_structure_set (out_s,
694             "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
695       } else {
696         /* unpositioned layout */
697         gst_structure_remove_field (out_s, "channel-mask");
698       }
699 
700 #ifndef HAVE_IOS
701       if (core_audio->is_src && got_outer_asbd
702           && outer_asbd.mSampleRate != kAudioStreamAnyRate) {
703         /* According to Core Audio engineer, AUHAL does not support sample rate conversion.
704          * on sources. Therefore, we fixate the sample rate.
705          *
706          * "You definitely cannot do rate conversion as part of getting input from AUHAL.
707          *  That's the most common cause of those "cannot do in current context" errors."
708          * http://lists.apple.com/archives/coreaudio-api/2006/Sep/msg00088.html
709          */
710         gst_structure_set (out_s, "rate", G_TYPE_INT,
711             (gint) outer_asbd.mSampleRate, NULL);
712       }
713 #endif
714 
715       /* Special cases for upmixing and downmixing.
716        * Other than that, the AUs don't upmix or downmix multi-channel audio,
717        * e.g. if you push 5.1-surround audio to a stereo configuration,
718        * the left and right channels will be played accordingly,
719        * and the rest will be dropped. */
720       if (channels == 1) {
721         /* If have mono, then also offer stereo since CoreAudio downmixes to it */
722         GstStructure *stereo = gst_structure_copy (out_s);
723         gst_structure_remove_field (out_s, "channel-mask");
724         gst_structure_set (stereo, "channels", G_TYPE_INT, 2,
725             "channel-mask", GST_TYPE_BITMASK, STEREO_CHANNEL_MASK, NULL);
726         gst_caps_append_structure (caps, stereo);
727         gst_caps_append_structure (caps, out_s);
728       } else if (channels == 2 && (channel_mask == 0
729               || channel_mask == STEREO_CHANNEL_MASK)) {
730         /* If have stereo channels, then also offer mono since CoreAudio
731          * upmixes it. */
732         GstStructure *mono = gst_structure_copy (out_s);
733         gst_structure_set (mono, "channels", G_TYPE_INT, 1, NULL);
734         gst_structure_remove_field (mono, "channel-mask");
735         gst_structure_set (out_s, "channel-mask", GST_TYPE_BITMASK,
736             STEREO_CHANNEL_MASK, NULL);
737 
738         gst_caps_append_structure (caps, out_s);
739         gst_caps_append_structure (caps, mono);
740       } else {
741         /* Otherwise just add the caps */
742         gst_caps_append_structure (caps, out_s);
743       }
744     }
745   }
746 
747   GST_DEBUG_OBJECT (core_audio, "Probed caps:%" GST_PTR_FORMAT, caps);
748   return caps;
749 }
750