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