1 /*****************************************************************************
2 * coreaudio_common.c: Common AudioUnit code for iOS and macOS
3 *****************************************************************************
4 * Copyright (C) 2005 - 2017 VLC authors and VideoLAN
6 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
7 * Felix Paul Kühne <fkuehne at videolan dot org>
8 * David Fuhrmann <david dot fuhrmann at googlemail dot com>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 #import "coreaudio_common.h"
26 #import <CoreAudio/CoreAudioTypes.h>
29 #import <CoreServices/CoreServices.h>
30 #import <vlc_dialog.h>
33 static inline uint64_t
34 BytesToFrames(struct aout_sys_common
*p_sys
, size_t i_bytes
)
36 return i_bytes
* p_sys
->i_frame_length
/ p_sys
->i_bytes_per_frame
;
40 FramesToUs(struct aout_sys_common
*p_sys
, uint64_t i_nb_frames
)
42 return i_nb_frames
* CLOCK_FREQ
/ p_sys
->i_rate
;
45 /* Called from render callbacks. No lock, wait, and IO here */
47 ca_Render(audio_output_t
*p_aout
, uint8_t *p_output
, size_t i_requested
)
49 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
51 if (atomic_load_explicit(&p_sys
->b_paused
, memory_order_relaxed
))
53 memset(p_output
, 0, i_requested
);
57 /* Pull audio from buffer */
59 void *p_data
= TPCircularBufferTail(&p_sys
->circular_buffer
,
64 size_t i_tocopy
= __MIN(i_requested
, (size_t) i_available
);
68 memcpy(p_output
, p_data
, i_tocopy
);
69 TPCircularBufferConsume(&p_sys
->circular_buffer
, i_tocopy
);
73 if (i_requested
> i_tocopy
)
75 atomic_fetch_add(&p_sys
->i_underrun_size
, i_requested
- i_tocopy
);
76 memset(&p_output
[i_tocopy
], 0, i_requested
- i_tocopy
);
81 ca_TimeGet(audio_output_t
*p_aout
, mtime_t
*delay
)
83 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
86 TPCircularBufferTail(&p_sys
->circular_buffer
, &i_bytes
);
88 int64_t i_frames
= BytesToFrames(p_sys
, i_bytes
);
89 *delay
= FramesToUs(p_sys
, i_frames
) + p_sys
->i_dev_latency_us
;
95 ca_Flush(audio_output_t
*p_aout
, bool wait
)
97 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
103 while (TPCircularBufferTail(&p_sys
->circular_buffer
, &i_bytes
) != NULL
)
105 /* Calculate the duration of the circular buffer, in order to wait
106 * for the render thread to play it all */
107 const mtime_t i_frame_us
=
108 FramesToUs(p_sys
, BytesToFrames(p_sys
, i_bytes
)) + 10000;
110 msleep(i_frame_us
/ 2);
115 /* flush circular buffer if data is left */
116 TPCircularBufferClear(&p_sys
->circular_buffer
);
121 ca_Pause(audio_output_t
* p_aout
, bool pause
, mtime_t date
)
123 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
126 atomic_store_explicit(&p_sys
->b_paused
, pause
, memory_order_relaxed
);
130 ca_Play(audio_output_t
* p_aout
, block_t
* p_block
)
132 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
134 /* Do the channel reordering */
135 if (p_sys
->chans_to_reorder
)
136 aout_ChannelReorder(p_block
->p_buffer
, p_block
->i_buffer
,
137 p_sys
->chans_to_reorder
, p_sys
->chan_table
,
140 /* move data to buffer */
141 while (!TPCircularBufferProduceBytes(&p_sys
->circular_buffer
,
142 p_block
->p_buffer
, p_block
->i_buffer
))
144 if (atomic_load_explicit(&p_sys
->b_paused
, memory_order_relaxed
))
146 msg_Warn(p_aout
, "dropping block because the circular buffer is "
151 /* Try to play what we can */
152 int32_t i_avalaible_bytes
;
153 TPCircularBufferHead(&p_sys
->circular_buffer
, &i_avalaible_bytes
);
154 assert(i_avalaible_bytes
>= 0);
155 if (unlikely((size_t) i_avalaible_bytes
>= p_block
->i_buffer
))
159 TPCircularBufferProduceBytes(&p_sys
->circular_buffer
,
160 p_block
->p_buffer
, i_avalaible_bytes
);
162 p_block
->p_buffer
+= i_avalaible_bytes
;
163 p_block
->i_buffer
-= i_avalaible_bytes
;
165 /* Wait for the render buffer to play the remaining data */
166 const mtime_t i_frame_us
=
167 FramesToUs(p_sys
, BytesToFrames(p_sys
, p_block
->i_buffer
));
168 msleep(i_frame_us
/ 2);
171 unsigned i_underrun_size
= atomic_exchange(&p_sys
->i_underrun_size
, 0);
172 if (i_underrun_size
> 0)
173 msg_Warn(p_aout
, "underrun of %u bytes", i_underrun_size
);
175 block_Release(p_block
);
179 ca_Initialize(audio_output_t
*p_aout
, const audio_sample_format_t
*fmt
,
180 mtime_t i_dev_latency_us
)
182 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
184 atomic_init(&p_sys
->i_underrun_size
, 0);
185 atomic_init(&p_sys
->b_paused
, false);
187 p_sys
->i_rate
= fmt
->i_rate
;
188 p_sys
->i_bytes_per_frame
= fmt
->i_bytes_per_frame
;
189 p_sys
->i_frame_length
= fmt
->i_frame_length
;
190 p_sys
->chans_to_reorder
= 0;
192 msg_Dbg(p_aout
, "Current device has a latency of %lld us",
195 /* TODO VLC can't handle latency higher than 1 seconds */
196 if (i_dev_latency_us
> 1000000)
198 i_dev_latency_us
= 1000000;
199 msg_Warn(p_aout
, "VLC can't handle this device latency, lowering it to "
200 "%lld", i_dev_latency_us
);
202 p_sys
->i_dev_latency_us
= i_dev_latency_us
;
204 /* setup circular buffer */
205 size_t i_audiobuffer_size
= fmt
->i_rate
* fmt
->i_bytes_per_frame
206 / p_sys
->i_frame_length
;
207 if (fmt
->channel_type
== AUDIO_CHANNEL_TYPE_AMBISONICS
)
209 /* low latency: 40 ms of buffering */
210 i_audiobuffer_size
= i_audiobuffer_size
/ 25;
214 /* 2 seconds of buffering */
215 i_audiobuffer_size
= i_audiobuffer_size
* AOUT_MAX_ADVANCE_TIME
218 if (!TPCircularBufferInit(&p_sys
->circular_buffer
, i_audiobuffer_size
))
221 p_aout
->play
= ca_Play
;
222 p_aout
->pause
= ca_Pause
;
223 p_aout
->flush
= ca_Flush
;
224 p_aout
->time_get
= ca_TimeGet
;
229 ca_Uninitialize(audio_output_t
*p_aout
)
231 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
233 /* clean-up circular buffer */
234 TPCircularBufferCleanup(&p_sys
->circular_buffer
);
238 au_NewOutputInstance(audio_output_t
*p_aout
, OSType comp_sub_type
)
240 AudioComponentDescription desc
= {
241 .componentType
= kAudioUnitType_Output
,
242 .componentSubType
= comp_sub_type
,
243 .componentManufacturer
= kAudioUnitManufacturer_Apple
,
245 .componentFlagsMask
= 0,
248 AudioComponent au_component
;
249 au_component
= AudioComponentFindNext(NULL
, &desc
);
250 if (au_component
== NULL
)
252 msg_Err(p_aout
, "cannot find any AudioComponent, PCM output failed");
257 OSStatus err
= AudioComponentInstanceNew(au_component
, &au
);
260 ca_LogErr("cannot open AudioComponent, PCM output failed");
266 /*****************************************************************************
267 * RenderCallback: This function is called everytime the AudioUnit wants
268 * us to provide some more audio data.
269 * Don't print anything during normal playback, calling blocking function from
270 * this callback is not allowed.
271 *****************************************************************************/
273 RenderCallback(void *p_data
, AudioUnitRenderActionFlags
*ioActionFlags
,
274 const AudioTimeStamp
*inTimeStamp
, UInt32 inBusNumber
,
275 UInt32 inNumberFrames
, AudioBufferList
*ioData
)
277 VLC_UNUSED(ioActionFlags
);
278 VLC_UNUSED(inTimeStamp
);
279 VLC_UNUSED(inBusNumber
);
280 VLC_UNUSED(inNumberFrames
);
282 ca_Render(p_data
, ioData
->mBuffers
[0].mData
,
283 ioData
->mBuffers
[0].mDataByteSize
);
288 static AudioChannelLayout
*
289 GetLayoutDescription(audio_output_t
*p_aout
,
290 const AudioChannelLayout
*outlayout
)
292 AudioFormatPropertyID id
;
295 /* We need to "fill out" the ChannelLayout, because there are multiple
296 * ways that it can be set */
297 if (outlayout
->mChannelLayoutTag
== kAudioChannelLayoutTag_UseChannelBitmap
)
299 id
= kAudioFormatProperty_ChannelLayoutForBitmap
;
300 size
= sizeof(UInt32
);
301 data
= &outlayout
->mChannelBitmap
;
305 id
= kAudioFormatProperty_ChannelLayoutForTag
;
306 size
= sizeof(AudioChannelLayoutTag
);
307 data
= &outlayout
->mChannelLayoutTag
;
311 OSStatus err
= AudioFormatGetPropertyInfo(id
, size
, data
, ¶m_size
);
315 AudioChannelLayout
*reslayout
= malloc(param_size
);
316 if (reslayout
== NULL
)
319 err
= AudioFormatGetProperty(id
, size
, data
, ¶m_size
, reslayout
);
320 if (err
!= noErr
|| reslayout
->mNumberChannelDescriptions
== 0)
322 msg_Err(p_aout
, "insufficient number of output channels");
331 MapOutputLayout(audio_output_t
*p_aout
, audio_sample_format_t
*fmt
,
332 const AudioChannelLayout
*outlayout
)
334 /* Fill VLC physical_channels from output layout */
335 fmt
->i_physical_channels
= 0;
336 uint32_t i_original
= fmt
->i_physical_channels
;
337 AudioChannelLayout
*reslayout
= NULL
;
339 if (outlayout
== NULL
)
341 msg_Dbg(p_aout
, "not output layout, default to Stereo");
342 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
346 if (outlayout
->mChannelLayoutTag
!=
347 kAudioChannelLayoutTag_UseChannelDescriptions
)
349 reslayout
= GetLayoutDescription(p_aout
, outlayout
);
350 if (reslayout
== NULL
)
352 outlayout
= reslayout
;
355 if (i_original
== AOUT_CHAN_CENTER
356 || outlayout
->mNumberChannelDescriptions
< 2)
358 /* We only need Mono or cannot output more than 1 channel */
359 fmt
->i_physical_channels
= AOUT_CHAN_CENTER
;
360 msg_Dbg(p_aout
, "output layout of AUHAL has 1 channel");
362 else if (i_original
== (AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
)
363 || outlayout
->mNumberChannelDescriptions
< 3)
365 /* We only need Stereo or cannot output more than 2 channels */
366 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
367 msg_Dbg(p_aout
, "output layout of AUHAL is Stereo");
371 assert(outlayout
->mNumberChannelDescriptions
> 0);
373 msg_Dbg(p_aout
, "output layout of AUHAL has %i channels",
374 outlayout
->mNumberChannelDescriptions
);
376 /* maps auhal channels to vlc ones */
377 static const unsigned i_auhal_channel_mapping
[] = {
378 [kAudioChannelLabel_Left
] = AOUT_CHAN_LEFT
,
379 [kAudioChannelLabel_Right
] = AOUT_CHAN_RIGHT
,
380 [kAudioChannelLabel_Center
] = AOUT_CHAN_CENTER
,
381 [kAudioChannelLabel_LFEScreen
] = AOUT_CHAN_LFE
,
382 [kAudioChannelLabel_LeftSurround
] = AOUT_CHAN_REARLEFT
,
383 [kAudioChannelLabel_RightSurround
] = AOUT_CHAN_REARRIGHT
,
384 /* needs to be swapped with rear */
385 [kAudioChannelLabel_RearSurroundLeft
] = AOUT_CHAN_MIDDLELEFT
,
386 /* needs to be swapped with rear */
387 [kAudioChannelLabel_RearSurroundRight
] = AOUT_CHAN_MIDDLERIGHT
,
388 [kAudioChannelLabel_CenterSurround
] = AOUT_CHAN_REARCENTER
390 static const size_t i_auhal_size
= sizeof(i_auhal_channel_mapping
)
391 / sizeof(i_auhal_channel_mapping
[0]);
393 /* We want more than stereo and we can do that */
394 for (unsigned i
= 0; i
< outlayout
->mNumberChannelDescriptions
; i
++)
396 AudioChannelLabel chan
=
397 outlayout
->mChannelDescriptions
[i
].mChannelLabel
;
399 msg_Dbg(p_aout
, "this is channel: %d", (int) chan
);
401 if (chan
< i_auhal_size
&& i_auhal_channel_mapping
[chan
] > 0)
402 fmt
->i_physical_channels
|= i_auhal_channel_mapping
[chan
];
404 msg_Dbg(p_aout
, "found nonrecognized channel %d at index "
407 if (fmt
->i_physical_channels
== 0)
409 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
410 msg_Err(p_aout
, "You should configure your speaker layout with "
411 "Audio Midi Setup in /Applications/Utilities. VLC will "
412 "output Stereo only.");
413 #if !TARGET_OS_IPHONE
414 vlc_dialog_display_error(p_aout
,
415 _("Audio device is not configured"), "%s",
416 _("You should configure your speaker layout with "
417 "\"Audio Midi Setup\" in /Applications/"
418 "Utilities. VLC will output Stereo only."));
422 if (aout_FormatNbChannels(fmt
) >= 8
423 && fmt
->i_physical_channels
!= AOUT_CHANS_7_1
)
426 const bool b_8x_support
= true;
428 SInt32 osx_min_version
;
429 if (Gestalt(gestaltSystemVersionMinor
, &osx_min_version
) != noErr
)
430 msg_Err(p_aout
, "failed to check OSX version");
431 const bool b_8x_support
= osx_min_version
>= 7;
436 msg_Warn(p_aout
, "8.0 audio output not supported on this "
437 "device, layout will be incorrect");
438 fmt
->i_physical_channels
= AOUT_CHANS_7_1
;
446 aout_FormatPrepare(fmt
);
448 msg_Dbg(p_aout
, "selected %d physical channels for device output",
449 aout_FormatNbChannels(fmt
));
450 msg_Dbg(p_aout
, "VLC will output: %s", aout_FormatPrintChannels(fmt
));
456 SetupInputLayout(audio_output_t
*p_aout
, const audio_sample_format_t
*fmt
,
457 AudioChannelLayoutTag
*inlayout_tag
)
459 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
460 uint32_t chans_out
[AOUT_CHAN_MAX
];
462 /* Some channel abbreviations used below:
467 * Rs - right surround
468 * Cs - center surround
469 * Rls - rear left surround
470 * Rrs - rear right surround
473 * Lsd - left surround direct
474 * Rsd - right surround direct
478 * Vhl - vertical height left
479 * Vhc - vertical height center
480 * Vhr - vertical height right
481 * Lt - left matrix total. for matrix encoded stereo.
482 * Rt - right matrix total. for matrix encoded stereo. */
484 switch (aout_FormatNbChannels(fmt
))
487 *inlayout_tag
= kAudioChannelLayoutTag_Mono
;
490 *inlayout_tag
= kAudioChannelLayoutTag_Stereo
;
493 if (fmt
->i_physical_channels
& AOUT_CHAN_CENTER
) /* L R C */
494 *inlayout_tag
= kAudioChannelLayoutTag_DVD_7
;
495 else if (fmt
->i_physical_channels
& AOUT_CHAN_LFE
) /* L R LFE */
496 *inlayout_tag
= kAudioChannelLayoutTag_DVD_4
;
499 if (fmt
->i_physical_channels
& (AOUT_CHAN_CENTER
| AOUT_CHAN_LFE
)) /* L R C LFE */
500 *inlayout_tag
= kAudioChannelLayoutTag_DVD_10
;
501 else if (fmt
->i_physical_channels
& AOUT_CHANS_REAR
) /* L R Ls Rs */
502 *inlayout_tag
= kAudioChannelLayoutTag_DVD_3
;
503 else if (fmt
->i_physical_channels
& AOUT_CHANS_CENTER
) /* L R C Cs */
504 *inlayout_tag
= kAudioChannelLayoutTag_DVD_3
;
507 if (fmt
->i_physical_channels
& (AOUT_CHAN_CENTER
)) /* L R Ls Rs C */
508 *inlayout_tag
= kAudioChannelLayoutTag_DVD_19
;
509 else if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
)) /* L R Ls Rs LFE */
510 *inlayout_tag
= kAudioChannelLayoutTag_DVD_18
;
513 if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
))
515 /* L R Ls Rs C LFE */
516 *inlayout_tag
= kAudioChannelLayoutTag_DVD_20
;
518 chans_out
[0] = AOUT_CHAN_LEFT
;
519 chans_out
[1] = AOUT_CHAN_RIGHT
;
520 chans_out
[2] = AOUT_CHAN_REARLEFT
;
521 chans_out
[3] = AOUT_CHAN_REARRIGHT
;
522 chans_out
[4] = AOUT_CHAN_CENTER
;
523 chans_out
[5] = AOUT_CHAN_LFE
;
525 p_sys
->chans_to_reorder
=
526 aout_CheckChannelReorder(NULL
, chans_out
,
527 fmt
->i_physical_channels
,
529 if (p_sys
->chans_to_reorder
)
530 msg_Dbg(p_aout
, "channel reordering needed for 5.1 output");
535 *inlayout_tag
= kAudioChannelLayoutTag_AudioUnit_6_0
;
537 chans_out
[0] = AOUT_CHAN_LEFT
;
538 chans_out
[1] = AOUT_CHAN_RIGHT
;
539 chans_out
[2] = AOUT_CHAN_REARLEFT
;
540 chans_out
[3] = AOUT_CHAN_REARRIGHT
;
541 chans_out
[4] = AOUT_CHAN_CENTER
;
542 chans_out
[5] = AOUT_CHAN_REARCENTER
;
544 p_sys
->chans_to_reorder
=
545 aout_CheckChannelReorder(NULL
, chans_out
,
546 fmt
->i_physical_channels
,
548 if (p_sys
->chans_to_reorder
)
549 msg_Dbg(p_aout
, "channel reordering needed for 6.0 output");
553 /* L R C LFE Ls Rs Cs */
554 *inlayout_tag
= kAudioChannelLayoutTag_MPEG_6_1_A
;
556 chans_out
[0] = AOUT_CHAN_LEFT
;
557 chans_out
[1] = AOUT_CHAN_RIGHT
;
558 chans_out
[2] = AOUT_CHAN_CENTER
;
559 chans_out
[3] = AOUT_CHAN_LFE
;
560 chans_out
[4] = AOUT_CHAN_REARLEFT
;
561 chans_out
[5] = AOUT_CHAN_REARRIGHT
;
562 chans_out
[6] = AOUT_CHAN_REARCENTER
;
564 p_sys
->chans_to_reorder
=
565 aout_CheckChannelReorder(NULL
, chans_out
,
566 fmt
->i_physical_channels
,
568 if (p_sys
->chans_to_reorder
)
569 msg_Dbg(p_aout
, "channel reordering needed for 6.1 output");
573 if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
))
575 /* L R C LFE Ls Rs Rls Rrs */
576 *inlayout_tag
= kAudioChannelLayoutTag_MPEG_7_1_C
;
578 chans_out
[0] = AOUT_CHAN_LEFT
;
579 chans_out
[1] = AOUT_CHAN_RIGHT
;
580 chans_out
[2] = AOUT_CHAN_CENTER
;
581 chans_out
[3] = AOUT_CHAN_LFE
;
582 chans_out
[4] = AOUT_CHAN_MIDDLELEFT
;
583 chans_out
[5] = AOUT_CHAN_MIDDLERIGHT
;
584 chans_out
[6] = AOUT_CHAN_REARLEFT
;
585 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
589 /* Lc C Rc L R Ls Cs Rs */
590 *inlayout_tag
= kAudioChannelLayoutTag_DTS_8_0_B
;
592 chans_out
[0] = AOUT_CHAN_MIDDLELEFT
;
593 chans_out
[1] = AOUT_CHAN_CENTER
;
594 chans_out
[2] = AOUT_CHAN_MIDDLERIGHT
;
595 chans_out
[3] = AOUT_CHAN_LEFT
;
596 chans_out
[4] = AOUT_CHAN_RIGHT
;
597 chans_out
[5] = AOUT_CHAN_REARLEFT
;
598 chans_out
[6] = AOUT_CHAN_REARCENTER
;
599 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
601 p_sys
->chans_to_reorder
=
602 aout_CheckChannelReorder(NULL
, chans_out
,
603 fmt
->i_physical_channels
,
605 if (p_sys
->chans_to_reorder
)
606 msg_Dbg(p_aout
, "channel reordering needed for 7.1 / 8.0 output");
609 /* Lc C Rc L R Ls Cs Rs LFE */
610 *inlayout_tag
= kAudioChannelLayoutTag_DTS_8_1_B
;
611 chans_out
[0] = AOUT_CHAN_MIDDLELEFT
;
612 chans_out
[1] = AOUT_CHAN_CENTER
;
613 chans_out
[2] = AOUT_CHAN_MIDDLERIGHT
;
614 chans_out
[3] = AOUT_CHAN_LEFT
;
615 chans_out
[4] = AOUT_CHAN_RIGHT
;
616 chans_out
[5] = AOUT_CHAN_REARLEFT
;
617 chans_out
[6] = AOUT_CHAN_REARCENTER
;
618 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
619 chans_out
[8] = AOUT_CHAN_LFE
;
621 p_sys
->chans_to_reorder
=
622 aout_CheckChannelReorder(NULL
, chans_out
,
623 fmt
->i_physical_channels
,
625 if (p_sys
->chans_to_reorder
)
626 msg_Dbg(p_aout
, "channel reordering needed for 8.1 output");
634 au_Initialize(audio_output_t
*p_aout
, AudioUnit au
, audio_sample_format_t
*fmt
,
635 const AudioChannelLayout
*outlayout
, mtime_t i_dev_latency_us
)
638 AudioChannelLayoutTag inlayout_tag
;
640 /* Set the desired format */
641 AudioStreamBasicDescription desc
;
642 if (aout_BitsPerSample(fmt
->i_format
) != 0)
645 fmt
->i_format
= VLC_CODEC_FL32
;
646 ret
= MapOutputLayout(p_aout
, fmt
, outlayout
);
647 if (ret
!= VLC_SUCCESS
)
650 ret
= SetupInputLayout(p_aout
, fmt
, &inlayout_tag
);
651 if (ret
!= VLC_SUCCESS
)
654 desc
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
;
655 desc
.mChannelsPerFrame
= aout_FormatNbChannels(fmt
);
656 desc
.mBitsPerChannel
= 32;
658 else if (AOUT_FMT_SPDIF(fmt
))
661 fmt
->i_format
= VLC_CODEC_SPDIFL
;
662 fmt
->i_bytes_per_frame
= 4;
663 fmt
->i_frame_length
= 1;
665 inlayout_tag
= kAudioChannelLayoutTag_Stereo
;
667 desc
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
|
668 kLinearPCMFormatFlagIsPacked
; /* S16LE */
669 desc
.mChannelsPerFrame
= 2;
670 desc
.mBitsPerChannel
= 16;
675 desc
.mSampleRate
= fmt
->i_rate
;
676 desc
.mFormatID
= kAudioFormatLinearPCM
;
677 desc
.mFramesPerPacket
= 1;
678 desc
.mBytesPerFrame
= desc
.mBitsPerChannel
* desc
.mChannelsPerFrame
/ 8;
679 desc
.mBytesPerPacket
= desc
.mBytesPerFrame
* desc
.mFramesPerPacket
;
681 OSStatus err
= AudioUnitSetProperty(au
, kAudioUnitProperty_StreamFormat
,
682 kAudioUnitScope_Input
, 0, &desc
,
686 ca_LogErr("failed to set stream format");
689 msg_Dbg(p_aout
, STREAM_FORMAT_MSG("Current AU format: " , desc
));
691 /* Retrieve actual format */
692 err
= AudioUnitGetProperty(au
, kAudioUnitProperty_StreamFormat
,
693 kAudioUnitScope_Input
, 0, &desc
,
694 &(UInt32
) { sizeof(desc
) });
697 ca_LogErr("failed to set stream format");
701 /* Set the IOproc callback */
702 const AURenderCallbackStruct callback
= {
703 .inputProc
= RenderCallback
,
704 .inputProcRefCon
= p_aout
,
707 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_SetRenderCallback
,
708 kAudioUnitScope_Input
, 0, &callback
,
712 ca_LogErr("failed to setup render callback");
716 /* Set the input_layout as the layout VLC will use to feed the AU unit.
717 * Yes, it must be the INPUT scope */
718 AudioChannelLayout inlayout
= {
719 .mChannelLayoutTag
= inlayout_tag
,
721 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_AudioChannelLayout
,
722 kAudioUnitScope_Input
, 0, &inlayout
,
726 ca_LogErr("failed to setup input layout");
731 err
= AudioUnitInitialize(au
);
735 ca_LogErr("AudioUnitInitialize failed");
739 ret
= ca_Initialize(p_aout
, fmt
, i_dev_latency_us
);
740 if (ret
!= VLC_SUCCESS
)
742 AudioUnitUninitialize(au
);
750 au_Uninitialize(audio_output_t
*p_aout
, AudioUnit au
)
752 OSStatus err
= AudioUnitUninitialize(au
);
754 ca_LogWarn("AudioUnitUninitialize failed");
756 ca_Uninitialize(p_aout
);