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 "gstosxcoreaudiocommon.h"
25 
26 void
gst_core_audio_remove_render_callback(GstCoreAudio * core_audio)27 gst_core_audio_remove_render_callback (GstCoreAudio * core_audio)
28 {
29   AURenderCallbackStruct input;
30   OSStatus status;
31 
32   /* Deactivate the render callback by calling SetRenderCallback
33    * with a NULL inputProc.
34    */
35   input.inputProc = NULL;
36   input.inputProcRefCon = NULL;
37 
38   status = AudioUnitSetProperty (core_audio->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0,        /* N/A for global */
39       &input, sizeof (input));
40 
41   if (status) {
42     GST_WARNING_OBJECT (core_audio->osxbuf,
43         "Failed to remove render callback %d", (int) status);
44   }
45 
46   /* Remove the RenderNotify too */
47   status = AudioUnitRemoveRenderNotify (core_audio->audiounit,
48       (AURenderCallback) gst_core_audio_render_notify, core_audio);
49 
50   if (status) {
51     GST_WARNING_OBJECT (core_audio->osxbuf,
52         "Failed to remove render notify callback %d", (int) status);
53   }
54 
55   /* We're deactivated.. */
56   core_audio->io_proc_needs_deactivation = FALSE;
57   core_audio->io_proc_active = FALSE;
58 }
59 
60 OSStatus
gst_core_audio_render_notify(GstCoreAudio * core_audio,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,unsigned int inBusNumber,unsigned int inNumberFrames,AudioBufferList * ioData)61 gst_core_audio_render_notify (GstCoreAudio * core_audio,
62     AudioUnitRenderActionFlags * ioActionFlags,
63     const AudioTimeStamp * inTimeStamp,
64     unsigned int inBusNumber,
65     unsigned int inNumberFrames, AudioBufferList * ioData)
66 {
67   /* Before rendering a frame, we get the PreRender notification.
68    * Here, we detach the RenderCallback if we've been paused.
69    *
70    * This is necessary (rather than just directly detaching it) to
71    * work around some thread-safety issues in CoreAudio
72    */
73   if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) {
74     if (core_audio->io_proc_needs_deactivation) {
75       gst_core_audio_remove_render_callback (core_audio);
76     }
77   }
78 
79   return noErr;
80 }
81 
82 gboolean
gst_core_audio_io_proc_start(GstCoreAudio * core_audio)83 gst_core_audio_io_proc_start (GstCoreAudio * core_audio)
84 {
85   OSStatus status;
86   AURenderCallbackStruct input;
87   AudioUnitPropertyID callback_type;
88 
89   GST_DEBUG_OBJECT (core_audio->osxbuf,
90       "osx ring buffer start ioproc: %p device_id %lu",
91       core_audio->element->io_proc, (gulong) core_audio->device_id);
92   if (!core_audio->io_proc_active) {
93     callback_type = core_audio->is_src ?
94         kAudioOutputUnitProperty_SetInputCallback :
95         kAudioUnitProperty_SetRenderCallback;
96 
97     input.inputProc = (AURenderCallback) core_audio->element->io_proc;
98     input.inputProcRefCon = core_audio->osxbuf;
99 
100     status = AudioUnitSetProperty (core_audio->audiounit, callback_type, kAudioUnitScope_Global, 0,     /* N/A for global */
101         &input, sizeof (input));
102 
103     if (status) {
104       GST_ERROR_OBJECT (core_audio->osxbuf,
105           "AudioUnitSetProperty failed: %d", (int) status);
106       return FALSE;
107     }
108     // ### does it make sense to do this notify stuff for input mode?
109     status = AudioUnitAddRenderNotify (core_audio->audiounit,
110         (AURenderCallback) gst_core_audio_render_notify, core_audio);
111 
112     if (status) {
113       GST_ERROR_OBJECT (core_audio->osxbuf,
114           "AudioUnitAddRenderNotify failed %d", (int) status);
115       return FALSE;
116     }
117     core_audio->io_proc_active = TRUE;
118   }
119 
120   core_audio->io_proc_needs_deactivation = FALSE;
121 
122   status = AudioOutputUnitStart (core_audio->audiounit);
123   if (status) {
124     GST_ERROR_OBJECT (core_audio->osxbuf, "AudioOutputUnitStart failed: %d",
125         (int) status);
126     return FALSE;
127   }
128   return TRUE;
129 }
130 
131 gboolean
gst_core_audio_io_proc_stop(GstCoreAudio * core_audio)132 gst_core_audio_io_proc_stop (GstCoreAudio * core_audio)
133 {
134   OSErr status;
135 
136   GST_DEBUG_OBJECT (core_audio->osxbuf,
137       "osx ring buffer stop ioproc: %p device_id %lu",
138       core_audio->element->io_proc, (gulong) core_audio->device_id);
139 
140   status = AudioOutputUnitStop (core_audio->audiounit);
141   if (status) {
142     GST_WARNING_OBJECT (core_audio->osxbuf,
143         "AudioOutputUnitStop failed: %d", (int) status);
144   }
145   // ###: why is it okay to directly remove from here but not from pause() ?
146   if (core_audio->io_proc_active) {
147     gst_core_audio_remove_render_callback (core_audio);
148   }
149   return TRUE;
150 }
151 
152 AudioBufferList *
buffer_list_alloc(UInt32 channels,UInt32 size,gboolean interleaved)153 buffer_list_alloc (UInt32 channels, UInt32 size, gboolean interleaved)
154 {
155   AudioBufferList *list;
156   gsize list_size;
157   UInt32 num_buffers, n;
158 
159   num_buffers = interleaved ? 1 : channels;
160   /* AudioBufferList member mBuffers is variable-length array */
161   list_size = G_STRUCT_OFFSET (AudioBufferList, mBuffers[num_buffers]);
162   list = (AudioBufferList *) g_malloc (list_size);
163 
164   list->mNumberBuffers = num_buffers;
165   for (n = 0; n < num_buffers; ++n) {
166     /* See http://lists.apple.com/archives/coreaudio-api/2015/Feb/msg00027.html */
167     list->mBuffers[n].mNumberChannels = interleaved ? channels : 1;
168     /* AudioUnitRender will keep overwriting mDataByteSize */
169     list->mBuffers[n].mDataByteSize = size;
170     list->mBuffers[n].mData = g_malloc (size);
171   }
172 
173   return list;
174 }
175 
176 void
buffer_list_free(AudioBufferList * list)177 buffer_list_free (AudioBufferList * list)
178 {
179   UInt32 n;
180 
181   if (list == NULL)
182     return;
183 
184   for (n = 0; n < list->mNumberBuffers; ++n) {
185     g_free (list->mBuffers[n].mData);
186   }
187 
188   g_free (list);
189 }
190 
191 gboolean
gst_core_audio_bind_device(GstCoreAudio * core_audio)192 gst_core_audio_bind_device (GstCoreAudio * core_audio)
193 {
194   OSStatus status;
195 
196   /* Specify which device we're using. */
197   GST_DEBUG_OBJECT (core_audio->osxbuf, "Bind AudioUnit to device %d",
198       (int) core_audio->device_id);
199   status = AudioUnitSetProperty (core_audio->audiounit,
200       kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0,
201       &core_audio->device_id, sizeof (AudioDeviceID));
202   if (status) {
203     GST_ERROR_OBJECT (core_audio->osxbuf, "Failed binding to device: %d",
204         (int) status);
205     goto audiounit_error;
206   }
207   return TRUE;
208 
209 audiounit_error:
210   if (core_audio->recBufferList) {
211     buffer_list_free (core_audio->recBufferList);
212     core_audio->recBufferList = NULL;
213   }
214   return FALSE;
215 }
216 
217 static gboolean
_core_audio_set_property(GstCoreAudio * core_audio,AudioUnitPropertyID inID,void * inData,UInt32 inDataSize)218 _core_audio_set_property (GstCoreAudio * core_audio, AudioUnitPropertyID inID,
219     void *inData, UInt32 inDataSize)
220 {
221   OSStatus status;
222   AudioUnitScope scope;
223   AudioUnitElement element;
224 
225   scope = CORE_AUDIO_INNER_SCOPE (core_audio);
226   element = CORE_AUDIO_ELEMENT (core_audio);
227 
228   status =
229       AudioUnitSetProperty (core_audio->audiounit, inID, scope, element, inData,
230       inDataSize);
231 
232   if (status != noErr) {
233     GST_WARNING_OBJECT (core_audio->osxbuf,
234         "Failed to set Audio Unit property: %d", (int) status);
235     return FALSE;;
236   }
237 
238   return TRUE;
239 }
240 
241 /* The AudioUnit must be uninitialized before calling this */
242 gboolean
gst_core_audio_set_channel_layout(GstCoreAudio * core_audio,gint channels,GstCaps * caps)243 gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
244     gint channels, GstCaps * caps)
245 {
246   AudioChannelLayout *layout = NULL;
247   gboolean ret;
248   gsize layoutSize;
249   gint i;
250   GstStructure *structure;
251   GstAudioChannelPosition positions[GST_OSX_AUDIO_MAX_CHANNEL];
252   guint64 channel_mask;
253 
254   g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE);
255 
256   /* Determine the channel positions */
257   structure = gst_caps_get_structure (caps, 0);
258   channel_mask = 0;
259   gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK, &channel_mask,
260       NULL);
261 
262   if (channel_mask != 0)
263     gst_audio_channel_positions_from_mask (channels, channel_mask, positions);
264 
265   /* AudioChannelLayout member mChannelDescriptions is variable-length array */
266   layoutSize =
267       G_STRUCT_OFFSET (AudioChannelLayout, mChannelDescriptions[channels]);
268   layout = g_malloc (layoutSize);
269 
270   /* Fill out the AudioChannelLayout */
271   layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
272   layout->mChannelBitmap = 0;   /* Not used */
273   layout->mNumberChannelDescriptions = channels;
274   for (i = 0; i < channels; i++) {
275     if (channel_mask != 0) {
276       layout->mChannelDescriptions[i].mChannelLabel =
277           gst_audio_channel_position_to_core_audio (positions[i], i);
278     } else {
279       /* Discrete channel numbers are ORed into this */
280       layout->mChannelDescriptions[i].mChannelLabel =
281           kAudioChannelLabel_Discrete_0 | i;
282     }
283 
284     /* Others unused */
285     layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
286     layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
287     layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
288     layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
289   }
290 
291   /* Sets GStreamer-ordered channel layout on the inner scope.
292    * Reordering between the inner scope and outer scope is handled
293    * by the Audio Unit itself. */
294   ret = _core_audio_set_property (core_audio,
295       kAudioUnitProperty_AudioChannelLayout, layout, layoutSize);
296 
297   g_free (layout);
298   return ret;
299 }
300 
301 /* The AudioUnit must be uninitialized before calling this */
302 gboolean
gst_core_audio_set_format(GstCoreAudio * core_audio,AudioStreamBasicDescription format)303 gst_core_audio_set_format (GstCoreAudio * core_audio,
304     AudioStreamBasicDescription format)
305 {
306   GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
307 
308   return _core_audio_set_property (core_audio, kAudioUnitProperty_StreamFormat,
309       &format, sizeof (AudioStreamBasicDescription));
310 }
311 
312 gboolean
gst_core_audio_open_device(GstCoreAudio * core_audio,OSType sub_type,const gchar * adesc)313 gst_core_audio_open_device (GstCoreAudio * core_audio, OSType sub_type,
314     const gchar * adesc)
315 {
316   AudioComponentDescription desc;
317   AudioComponent comp;
318   OSStatus status;
319   AudioUnit unit;
320   UInt32 enableIO;
321 
322   desc.componentType = kAudioUnitType_Output;
323   desc.componentSubType = sub_type;
324   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
325   desc.componentFlags = 0;
326   desc.componentFlagsMask = 0;
327 
328   comp = AudioComponentFindNext (NULL, &desc);
329 
330   if (comp == NULL) {
331     GST_WARNING_OBJECT (core_audio->osxbuf, "Couldn't find %s component",
332         adesc);
333     return FALSE;
334   }
335 
336   status = AudioComponentInstanceNew (comp, &unit);
337 
338   if (status) {
339     GST_ERROR_OBJECT (core_audio->osxbuf, "Couldn't open %s component %d",
340         adesc, (int) status);
341     return FALSE;
342   }
343 
344   if (core_audio->is_src) {
345     /* enable input */
346     enableIO = 1;
347     status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1,   /* 1 = input element */
348         &enableIO, sizeof (enableIO));
349 
350     if (status) {
351       AudioComponentInstanceDispose (unit);
352       GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to enable input: %d",
353           (int) status);
354       return FALSE;
355     }
356 
357     /* disable output */
358     enableIO = 0;
359     status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0,  /* 0 = output element */
360         &enableIO, sizeof (enableIO));
361 
362     if (status) {
363       AudioComponentInstanceDispose (unit);
364       GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to disable output: %d",
365           (int) status);
366       return FALSE;
367     }
368   }
369 
370   GST_DEBUG_OBJECT (core_audio->osxbuf, "Created %s AudioUnit: %p", adesc,
371       unit);
372   core_audio->audiounit = unit;
373   return TRUE;
374 }
375 
376 AudioChannelLabel
gst_audio_channel_position_to_core_audio(GstAudioChannelPosition position,int channel)377 gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
378     position, int channel)
379 {
380   switch (position) {
381     case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
382       return kAudioChannelLabel_Left;
383     case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
384       return kAudioChannelLabel_Right;
385     case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
386       return kAudioChannelLabel_CenterSurround;
387     case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
388       return kAudioChannelLabel_LeftSurround;
389     case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
390       return kAudioChannelLabel_RightSurround;
391     case GST_AUDIO_CHANNEL_POSITION_LFE1:
392       return kAudioChannelLabel_LFEScreen;
393     case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
394       return kAudioChannelLabel_Center;
395     case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
396       return kAudioChannelLabel_LeftSurroundDirect;
397     case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
398       return kAudioChannelLabel_RightSurroundDirect;
399     case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
400       return kAudioChannelLabel_LeftCenter;
401     case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
402       return kAudioChannelLabel_RightCenter;
403     case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT:
404       return kAudioChannelLabel_TopBackLeft;
405     case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER:
406       return kAudioChannelLabel_TopBackCenter;
407     case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT:
408       return kAudioChannelLabel_TopBackRight;
409     case GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT:
410       return kAudioChannelLabel_LeftWide;
411     case GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT:
412       return kAudioChannelLabel_RightWide;
413     case GST_AUDIO_CHANNEL_POSITION_LFE2:
414       return kAudioChannelLabel_LFE2;
415     case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT:
416       return kAudioChannelLabel_VerticalHeightLeft;
417     case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT:
418       return kAudioChannelLabel_VerticalHeightRight;
419     case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER:
420       return kAudioChannelLabel_VerticalHeightCenter;
421 
422       /* Special position values */
423     case GST_AUDIO_CHANNEL_POSITION_NONE:
424       return kAudioChannelLabel_Discrete_0 | channel;
425     case GST_AUDIO_CHANNEL_POSITION_MONO:
426       return kAudioChannelLabel_Mono;
427 
428       /* Following positions are unmapped --
429        * i.e. mapped to kAudioChannelLabel_Unknown: */
430     case GST_AUDIO_CHANNEL_POSITION_TOP_CENTER:
431     case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT:
432     case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT:
433     case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER:
434     case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT:
435     case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT:
436     case GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT:
437     case GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT:
438     default:
439       return kAudioChannelLabel_Unknown;
440   }
441 }
442 
443 /* Performs a best-effort conversion. 'channel' is used for warnings only. */
444 GstAudioChannelPosition
gst_core_audio_channel_label_to_gst(AudioChannelLabel label,int channel,gboolean warn)445 gst_core_audio_channel_label_to_gst (AudioChannelLabel label,
446     int channel, gboolean warn)
447 {
448   switch (label) {
449     case kAudioChannelLabel_Left:
450       return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
451     case kAudioChannelLabel_Right:
452       return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
453     case kAudioChannelLabel_Center:
454       return GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
455     case kAudioChannelLabel_LFEScreen:
456       return GST_AUDIO_CHANNEL_POSITION_LFE1;
457     case kAudioChannelLabel_LeftSurround:
458       return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
459     case kAudioChannelLabel_RightSurround:
460       return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
461     case kAudioChannelLabel_LeftSurroundDirect:
462       return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
463     case kAudioChannelLabel_RightSurroundDirect:
464       return GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
465     case kAudioChannelLabel_CenterSurround:
466       return GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
467     case kAudioChannelLabel_LeftCenter:
468       return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
469     case kAudioChannelLabel_RightCenter:
470       return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
471     case kAudioChannelLabel_TopBackLeft:
472       return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT;
473     case kAudioChannelLabel_TopBackCenter:
474       return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER;
475     case kAudioChannelLabel_TopBackRight:
476       return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT;
477     case kAudioChannelLabel_LeftWide:
478       return GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT;
479     case kAudioChannelLabel_RightWide:
480       return GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT;
481     case kAudioChannelLabel_LFE2:
482       return GST_AUDIO_CHANNEL_POSITION_LFE2;
483     case kAudioChannelLabel_VerticalHeightLeft:
484       return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT;
485     case kAudioChannelLabel_VerticalHeightRight:
486       return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT;
487     case kAudioChannelLabel_VerticalHeightCenter:
488       return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER;
489 
490       /* Special position values */
491 
492     case kAudioChannelLabel_Mono:
493       /* GST_AUDIO_CHANNEL_POSITION_MONO is only for 1-channel layouts */
494       return GST_AUDIO_CHANNEL_POSITION_INVALID;
495     case kAudioChannelLabel_Discrete:
496       return GST_AUDIO_CHANNEL_POSITION_NONE;
497 
498       /*
499          Following labels are unmapped --
500          i.e. mapped to GST_AUDIO_CHANNEL_POSITION_INVALID:
501        */
502     case kAudioChannelLabel_RearSurroundLeft:
503     case kAudioChannelLabel_RearSurroundRight:
504     case kAudioChannelLabel_TopCenterSurround:
505     case kAudioChannelLabel_LeftTotal:
506     case kAudioChannelLabel_RightTotal:
507     case kAudioChannelLabel_HearingImpaired:
508     case kAudioChannelLabel_Narration:
509     case kAudioChannelLabel_DialogCentricMix:
510     case kAudioChannelLabel_CenterSurroundDirect:
511     case kAudioChannelLabel_Haptic:
512     default:
513       if (label >> 16 != 0) {   /* kAudioChannelLabel_Discrete_N */
514         /* no way to store discrete channel order */
515         if (warn)
516           GST_WARNING
517               ("Core Audio channel %u labeled kAudioChannelLabel_Discrete_%u -- discrete order will be lost",
518               channel, ((unsigned int) label) & 0xFFFF);
519         return GST_AUDIO_CHANNEL_POSITION_NONE;
520       } else {
521         if (warn)
522           GST_WARNING
523               ("Core Audio channel %u has unsupported label %d and will be skipped",
524               channel, (int) label);
525         return GST_AUDIO_CHANNEL_POSITION_INVALID;
526       }
527   }
528 }
529 
530 void
gst_core_audio_dump_channel_layout(AudioChannelLayout * channel_layout)531 gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
532 {
533   UInt32 i;
534 
535   GST_DEBUG ("mChannelLayoutTag: 0x%lx",
536       (unsigned long) channel_layout->mChannelLayoutTag);
537   GST_DEBUG ("mChannelBitmap: 0x%lx",
538       (unsigned long) channel_layout->mChannelBitmap);
539   GST_DEBUG ("mNumberChannelDescriptions: %lu",
540       (unsigned long) channel_layout->mNumberChannelDescriptions);
541   for (i = 0; i < channel_layout->mNumberChannelDescriptions; i++) {
542     AudioChannelDescription *channel_desc =
543         &channel_layout->mChannelDescriptions[i];
544     GST_DEBUG ("  mChannelLabel: 0x%lx mChannelFlags: 0x%lx "
545         "mCoordinates[0]: %f mCoordinates[1]: %f "
546         "mCoordinates[2]: %f",
547         (unsigned long) channel_desc->mChannelLabel,
548         (unsigned long) channel_desc->mChannelFlags,
549         channel_desc->mCoordinates[0], channel_desc->mCoordinates[1],
550         channel_desc->mCoordinates[2]);
551   }
552 }
553