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