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>
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.
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.
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.
24 #include "gstosxcoreaudio.h"
25 #include "gstosxcoreaudiocommon.h"
27 GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
28 #define GST_CAT_DEFAULT osx_audio_debug
30 G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
33 #include "gstosxcoreaudioremoteio.c"
35 #include "gstosxcoreaudiohal.c"
40 gst_core_audio_class_init (GstCoreAudioClass * klass)
45 gst_core_audio_init (GstCoreAudio * core_audio)
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;
54 core_audio->hog_pid = -1;
55 core_audio->disabled_mixing = FALSE;
60 _is_outer_scope (AudioUnitScope scope, AudioUnitElement element)
63 (scope == kAudioUnitScope_Input && element == 1) ||
64 (scope == kAudioUnitScope_Output && element == 0);
68 _audio_unit_property_listener (void *inRefCon, AudioUnit inUnit,
69 AudioUnitPropertyID inID, AudioUnitScope inScope,
70 AudioUnitElement inElement)
72 GstCoreAudio *core_audio;
74 core_audio = GST_CORE_AUDIO (inRefCon);
75 g_assert (inUnit == core_audio->audiounit);
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
85 * This merely "refreshes" our PREFERRED caps. */
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
93 core_audio->cached_caps_valid = FALSE;
99 /**************************
101 *************************/
104 gst_core_audio_new (GstObject * osxbuf)
106 GstCoreAudio *core_audio;
108 core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
109 core_audio->osxbuf = osxbuf;
110 core_audio->cached_caps = NULL;
115 gst_core_audio_close (GstCoreAudio * core_audio)
119 /* Uninitialize the AudioUnit */
120 status = AudioUnitUninitialize (core_audio->audiounit);
122 GST_ERROR_OBJECT (core_audio, "Failed to uninitialize AudioUnit: %d",
127 AudioUnitRemovePropertyListenerWithUserData (core_audio->audiounit,
128 kAudioUnitProperty_AudioChannelLayout, _audio_unit_property_listener,
130 AudioUnitRemovePropertyListenerWithUserData (core_audio->audiounit,
131 kAudioUnitProperty_StreamFormat, _audio_unit_property_listener,
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);
138 AudioComponentInstanceDispose (core_audio->audiounit);
139 core_audio->audiounit = NULL;
144 gst_core_audio_open (GstCoreAudio * core_audio)
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);
152 if (!gst_core_audio_open_impl (core_audio))
155 /* Add property listener */
156 status = AudioUnitAddPropertyListener (core_audio->audiounit,
157 kAudioUnitProperty_AudioChannelLayout, _audio_unit_property_listener,
159 if (status != noErr) {
160 GST_ERROR_OBJECT (core_audio, "Failed to add audio channel layout property "
161 "listener for AudioUnit: %d", (int) status);
163 status = AudioUnitAddPropertyListener (core_audio->audiounit,
164 kAudioUnitProperty_StreamFormat, _audio_unit_property_listener,
166 if (status != noErr) {
167 GST_ERROR_OBJECT (core_audio, "Failed to add stream format property "
168 "listener for AudioUnit: %d", (int) status);
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);
175 GST_ERROR_OBJECT (core_audio, "Failed to initialize AudioUnit: %d",
184 gst_core_audio_start_processing (GstCoreAudio * core_audio)
186 return gst_core_audio_start_processing_impl (core_audio);
190 gst_core_audio_pause_processing (GstCoreAudio * core_audio)
192 return gst_core_audio_pause_processing_impl (core_audio);
196 gst_core_audio_stop_processing (GstCoreAudio * core_audio)
198 return gst_core_audio_stop_processing_impl (core_audio);
202 gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
203 gdouble rate, guint * samples, gdouble * latency)
205 return gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
210 gst_core_audio_initialize (GstCoreAudio * core_audio,
211 AudioStreamBasicDescription format, GstCaps * caps, gboolean is_passthrough)
215 GST_DEBUG_OBJECT (core_audio,
216 "Initializing: passthrough:%d caps:%" GST_PTR_FORMAT, is_passthrough,
219 if (!gst_core_audio_initialize_impl (core_audio, format, caps,
220 is_passthrough, &frame_size)) {
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));
237 gst_core_audio_uninitialize (GstCoreAudio * core_audio)
239 buffer_list_free (core_audio->recBufferList);
240 core_audio->recBufferList = NULL;
244 gst_core_audio_set_volume (GstCoreAudio * core_audio, gfloat volume)
246 AudioUnitSetParameter (core_audio->audiounit, kHALOutputParam_Volume,
247 kAudioUnitScope_Global, 0, (float) volume, 0);
251 gst_core_audio_select_device (GstCoreAudio * core_audio)
253 return gst_core_audio_select_device_impl (core_audio);
257 gst_core_audio_init_debug (void)
259 GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
260 "OSX Audio Elements");
264 gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id)
266 return gst_core_audio_audio_device_is_spdif_avail_impl (device_id);
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.) */
273 _is_core_audio_layout_positioned (AudioChannelLayout * layout)
277 g_assert (layout->mChannelLayoutTag ==
278 kAudioChannelLayoutTag_UseChannelDescriptions);
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);
285 if (p >= 0) /* not special positition */
293 _core_audio_parse_channel_descriptions (AudioChannelLayout * layout,
294 guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
299 g_assert (layout->mChannelLayoutTag ==
300 kAudioChannelLayoutTag_UseChannelDescriptions);
302 positioned = _is_core_audio_layout_positioned (layout);
305 /* Go over all labels, either taking only positioned or only
306 * unpositioned channels, up to GST_OSX_AUDIO_MAX_CHANNEL channels.
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.
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);
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)) {
326 *channel_mask |= G_GUINT64_CONSTANT (1) << p;
329 if (*channels == GST_OSX_AUDIO_MAX_CHANNEL)
330 break; /* not to overflow */
336 gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
337 guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
339 g_assert (channels != NULL);
340 g_assert (channel_mask != NULL);
341 g_assert (layout != NULL);
343 if (layout->mChannelLayoutTag ==
344 kAudioChannelLayoutTag_UseChannelDescriptions) {
346 switch (layout->mNumberChannelDescriptions) {
349 pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE;
355 pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
361 pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
362 pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
366 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
367 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
370 _core_audio_parse_channel_descriptions (layout, channels, channel_mask,
374 } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Mono) {
376 pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
380 } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Stereo ||
381 layout->mChannelLayoutTag == kAudioChannelLayoutTag_StereoHeadphones ||
382 layout->mChannelLayoutTag == kAudioChannelLayoutTag_Binaural) {
384 pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
385 pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
389 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
390 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
392 } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Quadraphonic) {
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;
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);
406 } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Pentagonal) {
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;
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);
422 } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Cube) {
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;
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);
447 ("AudioChannelLayoutTag: %u not yet supported",
448 layout->mChannelLayoutTag);
455 /* Converts an AudioStreamBasicDescription to preferred caps.
457 * These caps will indicate the AU element's canonical format, which won't
458 * make Core Audio resample nor convert.
460 * NOTE ON MULTI-CHANNEL AUDIO:
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.)
469 * Naturally, the number of channels indicated by 'channels' can be lower
470 * than the AU element's total number of channels.
473 gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
474 AudioChannelLayout * layout)
477 GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
478 guint rate, channels, bps, endianness;
479 guint64 channel_mask;
480 gboolean sign, interleaved;
481 GstAudioChannelPosition pos[GST_OSX_AUDIO_MAX_CHANNEL];
483 if (asbd->mFormatID != kAudioFormatLinearPCM) {
484 GST_WARNING ("Only linear PCM is supported");
488 if (!(asbd->mFormatFlags & kAudioFormatFlagIsPacked)) {
489 GST_WARNING ("Only packed formats supported");
493 if (asbd->mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) {
494 GST_WARNING ("Fixed point audio is unsupported");
498 rate = asbd->mSampleRate;
499 if (rate == kAudioStreamAnyRate) {
500 GST_WARNING ("No sample rate");
504 bps = asbd->mBitsPerChannel;
505 endianness = asbd->mFormatFlags & kAudioFormatFlagIsBigEndian ?
506 G_BIG_ENDIAN : G_LITTLE_ENDIAN;
507 sign = asbd->mFormatFlags & kAudioFormatFlagIsSignedInteger ? TRUE : FALSE;
508 interleaved = asbd->mFormatFlags & kAudioFormatFlagIsNonInterleaved ?
511 if (asbd->mFormatFlags & kAudioFormatFlagIsFloat) {
513 if (endianness == G_LITTLE_ENDIAN)
514 format = GST_AUDIO_FORMAT_F32LE;
516 format = GST_AUDIO_FORMAT_F32BE;
518 } else if (bps == 64) {
519 if (endianness == G_LITTLE_ENDIAN)
520 format = GST_AUDIO_FORMAT_F64LE;
522 format = GST_AUDIO_FORMAT_F64BE;
525 format = gst_audio_format_build_integer (sign, endianness, bps, bps);
528 if (format == GST_AUDIO_FORMAT_UNKNOWN) {
529 GST_WARNING ("Unsupported sample format");
534 if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
537 ("Failed to parse channel layout, best effort channels layout mapping will be used");
543 /* The AU can have arbitrary channel order, but we're using GstAudioInfo
544 * which supports only the GStreamer channel order.
545 * Also, we're eventually producing caps, which only have channel-mask
546 * (whose implied order is the GStreamer channel order). */
547 gst_audio_channel_positions_to_valid_order (pos, channels);
549 gst_audio_info_set_format (&info, format, rate, channels, pos);
551 channels = MIN (asbd->mChannelsPerFrame, GST_OSX_AUDIO_MAX_CHANNEL);
552 gst_audio_info_set_format (&info, format, rate, channels, NULL);
555 return gst_audio_info_to_caps (&info);
562 _core_audio_get_property (GstCoreAudio * core_audio, gboolean outer,
563 AudioUnitPropertyID inID, void *inData, UInt32 * inDataSize)
566 AudioUnitScope scope;
567 AudioUnitElement element;
570 CORE_AUDIO_OUTER_SCOPE (core_audio) : CORE_AUDIO_INNER_SCOPE (core_audio);
571 element = CORE_AUDIO_ELEMENT (core_audio);
574 AudioUnitGetProperty (core_audio->audiounit, inID, scope, element, inData,
577 return status == noErr;
581 _core_audio_get_stream_format (GstCoreAudio * core_audio,
582 AudioStreamBasicDescription * asbd, gboolean outer)
586 size = sizeof (AudioStreamBasicDescription);
587 return _core_audio_get_property (core_audio, outer,
588 kAudioUnitProperty_StreamFormat, asbd, &size);
592 gst_core_audio_get_channel_layout (GstCoreAudio * core_audio, gboolean outer)
595 AudioChannelLayout *layout;
597 if (core_audio->is_src) {
598 GST_WARNING_OBJECT (core_audio,
599 "gst_core_audio_get_channel_layout not supported on source.");
603 if (!_core_audio_get_property (core_audio, outer,
604 kAudioUnitProperty_AudioChannelLayout, NULL, &size)) {
605 GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
609 layout = g_malloc (size);
610 if (!_core_audio_get_property (core_audio, outer,
611 kAudioUnitProperty_AudioChannelLayout, layout, &size)) {
612 GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
620 #define STEREO_CHANNEL_MASK \
621 (GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) | \
622 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT))
625 gst_core_audio_probe_caps (GstCoreAudio * core_audio, GstCaps * in_caps)
628 gboolean spdif_allowed;
629 AudioChannelLayout *layout;
630 AudioStreamBasicDescription outer_asbd;
631 gboolean got_outer_asbd;
632 GstCaps *caps = NULL;
633 guint64 channel_mask;
635 /* Get the ASBD of the outer scope (i.e. input scope of Input,
636 * output scope of Output).
637 * This ASBD indicates the hardware format. */
639 _core_audio_get_stream_format (core_audio, &outer_asbd, TRUE);
641 /* Collect info about the HW capabilites and preferences */
643 gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id);
644 if (!core_audio->is_src)
645 layout = gst_core_audio_get_channel_layout (core_audio, TRUE);
647 layout = NULL; /* no supported for sources */
649 GST_DEBUG_OBJECT (core_audio, "Selected device ID: %u SPDIF allowed: %d",
650 (unsigned) core_audio->device_id, spdif_allowed);
653 if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
655 GST_WARNING_OBJECT (core_audio, "Failed to parse channel layout");
659 /* If available, start with the preferred caps. */
661 caps = gst_core_audio_asbd_to_caps (&outer_asbd, layout);
664 } else if (got_outer_asbd) {
665 channels = outer_asbd.mChannelsPerFrame;
667 /* If available, start with the preferred caps */
668 caps = gst_core_audio_asbd_to_caps (&outer_asbd, NULL);
670 GST_ERROR_OBJECT (core_audio,
671 "Unable to get any information about hardware");
675 /* Append the allowed subset based on the template caps */
677 caps = gst_caps_new_empty ();
678 for (i = 0; i < gst_caps_get_size (in_caps); i++) {
681 in_s = gst_caps_get_structure (in_caps, i);
683 if (gst_structure_has_name (in_s, "audio/x-ac3") ||
684 gst_structure_has_name (in_s, "audio/x-dts")) {
686 gst_caps_append_structure (caps, gst_structure_copy (in_s));
691 out_s = gst_structure_copy (in_s);
692 gst_structure_set (out_s, "channels", G_TYPE_INT, channels, NULL);
693 if (channel_mask != 0) {
694 /* positioned layout */
695 gst_structure_set (out_s,
696 "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
698 /* unpositioned layout */
699 gst_structure_remove_field (out_s, "channel-mask");
703 if (core_audio->is_src && got_outer_asbd
704 && outer_asbd.mSampleRate != kAudioStreamAnyRate) {
705 /* According to Core Audio engineer, AUHAL does not support sample rate conversion.
706 * on sources. Therefore, we fixate the sample rate.
708 * "You definitely cannot do rate conversion as part of getting input from AUHAL.
709 * That's the most common cause of those "cannot do in current context" errors."
710 * http://lists.apple.com/archives/coreaudio-api/2006/Sep/msg00088.html
712 gst_structure_set (out_s, "rate", G_TYPE_INT,
713 (gint) outer_asbd.mSampleRate, NULL);
717 /* Special cases for upmixing and downmixing.
718 * Other than that, the AUs don't upmix or downmix multi-channel audio,
719 * e.g. if you push 5.1-surround audio to a stereo configuration,
720 * the left and right channels will be played accordingly,
721 * and the rest will be dropped. */
723 /* If have mono, then also offer stereo since CoreAudio downmixes to it */
724 GstStructure *stereo = gst_structure_copy (out_s);
725 gst_structure_remove_field (out_s, "channel-mask");
726 gst_structure_set (stereo, "channels", G_TYPE_INT, 2,
727 "channel-mask", GST_TYPE_BITMASK, STEREO_CHANNEL_MASK, NULL);
728 gst_caps_append_structure (caps, stereo);
729 gst_caps_append_structure (caps, out_s);
730 } else if (channels == 2 && (channel_mask == 0
731 || channel_mask == STEREO_CHANNEL_MASK)) {
732 /* If have stereo channels, then also offer mono since CoreAudio
734 GstStructure *mono = gst_structure_copy (out_s);
735 gst_structure_set (mono, "channels", G_TYPE_INT, 1, NULL);
736 gst_structure_remove_field (mono, "channel-mask");
737 gst_structure_set (out_s, "channel-mask", GST_TYPE_BITMASK,
738 STEREO_CHANNEL_MASK, NULL);
740 gst_caps_append_structure (caps, out_s);
741 gst_caps_append_structure (caps, mono);
743 /* Otherwhise just add the caps */
744 gst_caps_append_structure (caps, out_s);
749 GST_DEBUG_OBJECT (core_audio, "Probed caps:%" GST_PTR_FORMAT, caps);