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 <mach/mach_time.h>
33 void (*lock
)(os_unfair_lock
*lock
);
34 void (*unlock
)(os_unfair_lock
*lock
);
37 static mach_timebase_info_data_t tinfo
;
39 static inline uint64_t
40 BytesToFrames(struct aout_sys_common
*p_sys
, size_t i_bytes
)
42 return i_bytes
* p_sys
->i_frame_length
/ p_sys
->i_bytes_per_frame
;
45 static inline vlc_tick_t
46 FramesToUs(struct aout_sys_common
*p_sys
, uint64_t i_nb_frames
)
48 return i_nb_frames
* CLOCK_FREQ
/ p_sys
->i_rate
;
52 ca_ClearOutBuffers(audio_output_t
*p_aout
)
54 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
56 block_ChainRelease(p_sys
->p_out_chain
);
57 p_sys
->p_out_chain
= NULL
;
58 p_sys
->pp_out_last
= &p_sys
->p_out_chain
;
60 p_sys
->i_out_size
= 0;
66 unfair_lock
.lock
= dlsym(RTLD_DEFAULT
, "os_unfair_lock_lock");
67 if (!unfair_lock
.lock
)
69 unfair_lock
.unlock
= dlsym(RTLD_DEFAULT
, "os_unfair_lock_unlock");
70 if (!unfair_lock
.unlock
)
71 unfair_lock
.lock
= NULL
;
73 if (mach_timebase_info(&tinfo
) != KERN_SUCCESS
)
74 tinfo
.numer
= tinfo
.denom
= 0;
78 lock_init(struct aout_sys_common
*p_sys
)
81 p_sys
->lock
.unfair
= OS_UNFAIR_LOCK_INIT
;
83 vlc_mutex_init(&p_sys
->lock
.mutex
);
87 lock_destroy(struct aout_sys_common
*p_sys
)
89 if (!unfair_lock
.lock
)
90 vlc_mutex_destroy(&p_sys
->lock
.mutex
);
94 lock_lock(struct aout_sys_common
*p_sys
)
97 unfair_lock
.lock(&p_sys
->lock
.unfair
);
99 vlc_mutex_lock(&p_sys
->lock
.mutex
);
103 lock_unlock(struct aout_sys_common
*p_sys
)
105 if (unfair_lock
.lock
)
106 unfair_lock
.unlock(&p_sys
->lock
.unfair
);
108 vlc_mutex_unlock(&p_sys
->lock
.mutex
);
112 ca_Open(audio_output_t
*p_aout
)
114 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
116 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
117 pthread_once(&once
, ca_init_once
);
119 vlc_sem_init(&p_sys
->flush_sem
, 0);
121 p_sys
->p_out_chain
= NULL
;
123 p_aout
->play
= ca_Play
;
124 p_aout
->pause
= ca_Pause
;
125 p_aout
->flush
= ca_Flush
;
126 p_aout
->time_get
= ca_TimeGet
;
130 ca_Close(audio_output_t
*p_aout
)
132 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
134 vlc_sem_destroy(&p_sys
->flush_sem
);
138 /* Called from render callbacks. No lock, wait, and IO here */
140 ca_Render(audio_output_t
*p_aout
, uint32_t i_frames
, uint64_t i_host_time
,
141 uint8_t *p_output
, size_t i_requested
)
143 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
147 p_sys
->i_render_host_time
= i_host_time
;
148 p_sys
->i_render_frames
= i_frames
;
150 if (p_sys
->b_do_flush
)
152 ca_ClearOutBuffers(p_aout
);
153 /* Signal that the renderer is flushed */
154 p_sys
->b_do_flush
= false;
155 vlc_sem_post(&p_sys
->flush_sem
);
162 block_t
*p_block
= p_sys
->p_out_chain
;
163 while (p_block
!= NULL
&& i_requested
!= 0)
165 size_t i_tocopy
= __MIN(i_requested
, p_block
->i_buffer
);
166 memcpy(&p_output
[i_copied
], p_block
->p_buffer
, i_tocopy
);
167 i_requested
-= i_tocopy
;
168 i_copied
+= i_tocopy
;
169 if (i_tocopy
== p_block
->i_buffer
)
171 block_t
*p_release
= p_block
;
172 p_block
= p_block
->p_next
;
173 block_Release(p_release
);
177 assert(i_requested
== 0);
179 p_block
->p_buffer
+= i_tocopy
;
180 p_block
->i_buffer
-= i_tocopy
;
183 p_sys
->p_out_chain
= p_block
;
184 if (!p_sys
->p_out_chain
)
185 p_sys
->pp_out_last
= &p_sys
->p_out_chain
;
186 p_sys
->i_out_size
-= i_copied
;
191 assert(p_sys
->i_out_size
== 0);
192 p_sys
->i_underrun_size
+= i_requested
;
193 memset(&p_output
[i_copied
], 0, i_requested
);
200 memset(p_output
, 0, i_requested
);
205 ca_TimeGet(audio_output_t
*p_aout
, vlc_tick_t
*delay
)
207 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
209 if (unlikely(tinfo
.denom
== 0))
214 vlc_tick_t i_render_delay
;
215 if (likely(p_sys
->i_render_host_time
!= 0))
217 const uint64_t i_render_time_us
= p_sys
->i_render_host_time
218 * tinfo
.numer
/ tinfo
.denom
/ 1000;
219 i_render_delay
= i_render_time_us
- vlc_tick_now();
224 const int64_t i_out_frames
= BytesToFrames(p_sys
, p_sys
->i_out_size
);
225 *delay
= FramesToUs(p_sys
, i_out_frames
+ p_sys
->i_render_frames
)
226 + p_sys
->i_dev_latency_us
+ i_render_delay
;
233 ca_Flush(audio_output_t
*p_aout
, bool wait
)
235 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
240 while (p_sys
->i_out_size
> 0)
244 ca_ClearOutBuffers(p_aout
);
248 /* Calculate the duration of the circular buffer, in order to wait
249 * for the render thread to play it all */
250 const vlc_tick_t i_frame_us
=
251 FramesToUs(p_sys
, BytesToFrames(p_sys
, p_sys
->i_out_size
)) + VLC_TICK_FROM_MS(10);
253 vlc_tick_sleep(i_frame_us
);
259 assert(!p_sys
->b_do_flush
);
261 ca_ClearOutBuffers(p_aout
);
264 p_sys
->b_do_flush
= true;
266 vlc_sem_wait(&p_sys
->flush_sem
);
271 p_sys
->i_render_host_time
= 0;
272 p_sys
->i_render_frames
= 0;
277 ca_Pause(audio_output_t
* p_aout
, bool pause
, vlc_tick_t date
)
279 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
283 p_sys
->b_paused
= pause
;
288 ca_Play(audio_output_t
* p_aout
, block_t
* p_block
, vlc_tick_t date
)
290 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
292 /* Do the channel reordering */
293 if (p_sys
->chans_to_reorder
)
294 aout_ChannelReorder(p_block
->p_buffer
, p_block
->i_buffer
,
295 p_sys
->chans_to_reorder
, p_sys
->chan_table
,
301 const size_t i_avalaible_bytes
=
302 __MIN(p_block
->i_buffer
, p_sys
->i_out_max_size
- p_sys
->i_out_size
);
304 if (unlikely(i_avalaible_bytes
!= p_block
->i_buffer
))
306 /* Not optimal but unlikely code path. */
310 block_t
*p_new
= block_Alloc(i_avalaible_bytes
);
313 block_Release(p_block
);
317 memcpy(p_new
->p_buffer
, p_block
->p_buffer
, i_avalaible_bytes
);
319 p_block
->p_buffer
+= i_avalaible_bytes
;
320 p_block
->i_buffer
-= i_avalaible_bytes
;
324 block_ChainLastAppend(&p_sys
->pp_out_last
, p_new
);
325 p_sys
->i_out_size
+= i_avalaible_bytes
;
330 block_Release(p_block
);
334 const vlc_tick_t i_frame_us
=
335 FramesToUs(p_sys
, BytesToFrames(p_sys
, p_block
->i_buffer
));
337 /* Wait for the render buffer to play the remaining data */
339 vlc_tick_sleep(i_frame_us
);
344 block_ChainLastAppend(&p_sys
->pp_out_last
, p_block
);
345 p_sys
->i_out_size
+= i_avalaible_bytes
;
348 } while (p_block
!= NULL
);
350 size_t i_underrun_size
= p_sys
->i_underrun_size
;
351 p_sys
->i_underrun_size
= 0;
355 if (i_underrun_size
> 0)
356 msg_Warn(p_aout
, "underrun of %zu bytes", i_underrun_size
);
362 ca_Initialize(audio_output_t
*p_aout
, const audio_sample_format_t
*fmt
,
363 vlc_tick_t i_dev_latency_us
)
365 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
367 p_sys
->i_underrun_size
= 0;
368 p_sys
->b_paused
= false;
369 p_sys
->i_render_host_time
= 0;
370 p_sys
->i_render_frames
= 0;
372 p_sys
->i_rate
= fmt
->i_rate
;
373 p_sys
->i_bytes_per_frame
= fmt
->i_bytes_per_frame
;
374 p_sys
->i_frame_length
= fmt
->i_frame_length
;
375 p_sys
->chans_to_reorder
= 0;
377 msg_Dbg(p_aout
, "Current device has a latency of %lld us",
380 /* TODO VLC can't handle latency higher than 1 seconds */
381 if (i_dev_latency_us
> 1000000)
383 i_dev_latency_us
= 1000000;
384 msg_Warn(p_aout
, "VLC can't handle this device latency, lowering it to "
385 "%lld", i_dev_latency_us
);
387 p_sys
->i_dev_latency_us
= i_dev_latency_us
;
389 /* setup circular buffer */
390 size_t i_audiobuffer_size
= fmt
->i_rate
* fmt
->i_bytes_per_frame
391 / p_sys
->i_frame_length
;
392 if (fmt
->channel_type
== AUDIO_CHANNEL_TYPE_AMBISONICS
)
394 /* lower latency: 200 ms of buffering. XXX: Decrease when VLC's core
395 * can handle lower audio latency */
396 p_sys
->i_out_max_size
= i_audiobuffer_size
/ 5;
400 /* 2 seconds of buffering */
401 p_sys
->i_out_max_size
= i_audiobuffer_size
* 2;
404 ca_ClearOutBuffers(p_aout
);
410 ca_Uninitialize(audio_output_t
*p_aout
)
412 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
413 ca_ClearOutBuffers(p_aout
);
414 p_sys
->i_out_max_size
= 0;
418 ca_SetAliveState(audio_output_t
*p_aout
, bool alive
)
420 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
424 bool b_sem_post
= false;
425 p_sys
->b_paused
= !alive
;
426 if (!alive
&& p_sys
->b_do_flush
)
428 ca_ClearOutBuffers(p_aout
);
429 p_sys
->b_do_flush
= false;
436 vlc_sem_post(&p_sys
->flush_sem
);
440 au_NewOutputInstance(audio_output_t
*p_aout
, OSType comp_sub_type
)
442 AudioComponentDescription desc
= {
443 .componentType
= kAudioUnitType_Output
,
444 .componentSubType
= comp_sub_type
,
445 .componentManufacturer
= kAudioUnitManufacturer_Apple
,
447 .componentFlagsMask
= 0,
450 AudioComponent au_component
;
451 au_component
= AudioComponentFindNext(NULL
, &desc
);
452 if (au_component
== NULL
)
454 msg_Err(p_aout
, "cannot find any AudioComponent, PCM output failed");
459 OSStatus err
= AudioComponentInstanceNew(au_component
, &au
);
462 ca_LogErr("cannot open AudioComponent, PCM output failed");
468 /*****************************************************************************
469 * RenderCallback: This function is called everytime the AudioUnit wants
470 * us to provide some more audio data.
471 * Don't print anything during normal playback, calling blocking function from
472 * this callback is not allowed.
473 *****************************************************************************/
475 RenderCallback(void *p_data
, AudioUnitRenderActionFlags
*ioActionFlags
,
476 const AudioTimeStamp
*inTimeStamp
, UInt32 inBusNumber
,
477 UInt32 inNumberFrames
, AudioBufferList
*ioData
)
479 VLC_UNUSED(ioActionFlags
);
480 VLC_UNUSED(inTimeStamp
);
481 VLC_UNUSED(inBusNumber
);
483 uint64_t i_host_time
= (inTimeStamp
->mFlags
& kAudioTimeStampHostTimeValid
)
484 ? inTimeStamp
->mHostTime
: 0;
486 ca_Render(p_data
, inNumberFrames
, i_host_time
, ioData
->mBuffers
[0].mData
,
487 ioData
->mBuffers
[0].mDataByteSize
);
492 static AudioChannelLayout
*
493 GetLayoutDescription(audio_output_t
*p_aout
,
494 const AudioChannelLayout
*outlayout
)
496 AudioFormatPropertyID id
;
499 /* We need to "fill out" the ChannelLayout, because there are multiple
500 * ways that it can be set */
501 if (outlayout
->mChannelLayoutTag
== kAudioChannelLayoutTag_UseChannelBitmap
)
503 id
= kAudioFormatProperty_ChannelLayoutForBitmap
;
504 size
= sizeof(UInt32
);
505 data
= &outlayout
->mChannelBitmap
;
509 id
= kAudioFormatProperty_ChannelLayoutForTag
;
510 size
= sizeof(AudioChannelLayoutTag
);
511 data
= &outlayout
->mChannelLayoutTag
;
515 OSStatus err
= AudioFormatGetPropertyInfo(id
, size
, data
, ¶m_size
);
519 AudioChannelLayout
*reslayout
= malloc(param_size
);
520 if (reslayout
== NULL
)
523 err
= AudioFormatGetProperty(id
, size
, data
, ¶m_size
, reslayout
);
524 if (err
!= noErr
|| reslayout
->mNumberChannelDescriptions
== 0)
526 msg_Err(p_aout
, "insufficient number of output channels");
535 MapOutputLayout(audio_output_t
*p_aout
, audio_sample_format_t
*fmt
,
536 const AudioChannelLayout
*outlayout
, bool *warn_configuration
)
538 /* Fill VLC physical_channels from output layout */
539 fmt
->i_physical_channels
= 0;
540 uint32_t i_original
= fmt
->i_physical_channels
;
541 AudioChannelLayout
*reslayout
= NULL
;
543 if (outlayout
== NULL
)
545 msg_Dbg(p_aout
, "not output layout, default to Stereo");
546 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
550 if (outlayout
->mChannelLayoutTag
!=
551 kAudioChannelLayoutTag_UseChannelDescriptions
)
553 reslayout
= GetLayoutDescription(p_aout
, outlayout
);
554 if (reslayout
== NULL
)
556 outlayout
= reslayout
;
559 if (i_original
== AOUT_CHAN_CENTER
560 || outlayout
->mNumberChannelDescriptions
< 2)
562 /* We only need Mono or cannot output more than 1 channel */
563 fmt
->i_physical_channels
= AOUT_CHAN_CENTER
;
564 msg_Dbg(p_aout
, "output layout of AUHAL has 1 channel");
566 else if (i_original
== (AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
)
567 || outlayout
->mNumberChannelDescriptions
< 3)
569 /* We only need Stereo or cannot output more than 2 channels */
570 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
571 msg_Dbg(p_aout
, "output layout of AUHAL is Stereo");
575 assert(outlayout
->mNumberChannelDescriptions
> 0);
577 msg_Dbg(p_aout
, "output layout of AUHAL has %i channels",
578 outlayout
->mNumberChannelDescriptions
);
580 /* maps auhal channels to vlc ones */
581 static const unsigned i_auhal_channel_mapping
[] = {
582 [kAudioChannelLabel_Left
] = AOUT_CHAN_LEFT
,
583 [kAudioChannelLabel_Right
] = AOUT_CHAN_RIGHT
,
584 [kAudioChannelLabel_Center
] = AOUT_CHAN_CENTER
,
585 [kAudioChannelLabel_LFEScreen
] = AOUT_CHAN_LFE
,
586 [kAudioChannelLabel_LeftSurround
] = AOUT_CHAN_REARLEFT
,
587 [kAudioChannelLabel_RightSurround
] = AOUT_CHAN_REARRIGHT
,
588 /* needs to be swapped with rear */
589 [kAudioChannelLabel_RearSurroundLeft
] = AOUT_CHAN_MIDDLELEFT
,
590 /* needs to be swapped with rear */
591 [kAudioChannelLabel_RearSurroundRight
] = AOUT_CHAN_MIDDLERIGHT
,
592 [kAudioChannelLabel_CenterSurround
] = AOUT_CHAN_REARCENTER
594 static const size_t i_auhal_size
= sizeof(i_auhal_channel_mapping
)
595 / sizeof(i_auhal_channel_mapping
[0]);
597 /* We want more than stereo and we can do that */
598 for (unsigned i
= 0; i
< outlayout
->mNumberChannelDescriptions
; i
++)
600 AudioChannelLabel chan
=
601 outlayout
->mChannelDescriptions
[i
].mChannelLabel
;
603 msg_Dbg(p_aout
, "this is channel: %d", (int) chan
);
605 if (chan
< i_auhal_size
&& i_auhal_channel_mapping
[chan
] > 0)
606 fmt
->i_physical_channels
|= i_auhal_channel_mapping
[chan
];
608 msg_Dbg(p_aout
, "found nonrecognized channel %d at index "
611 if (fmt
->i_physical_channels
== 0)
613 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
614 if (warn_configuration
)
615 *warn_configuration
= true;
622 aout_FormatPrepare(fmt
);
624 msg_Dbg(p_aout
, "selected %d physical channels for device output",
625 aout_FormatNbChannels(fmt
));
626 msg_Dbg(p_aout
, "VLC will output: %s", aout_FormatPrintChannels(fmt
));
632 SetupInputLayout(audio_output_t
*p_aout
, const audio_sample_format_t
*fmt
,
633 AudioChannelLayoutTag
*inlayout_tag
)
635 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
636 uint32_t chans_out
[AOUT_CHAN_MAX
];
638 /* Some channel abbreviations used below:
643 * Rs - right surround
644 * Cs - center surround
645 * Rls - rear left surround
646 * Rrs - rear right surround
649 * Lsd - left surround direct
650 * Rsd - right surround direct
654 * Vhl - vertical height left
655 * Vhc - vertical height center
656 * Vhr - vertical height right
657 * Lt - left matrix total. for matrix encoded stereo.
658 * Rt - right matrix total. for matrix encoded stereo. */
660 switch (aout_FormatNbChannels(fmt
))
663 *inlayout_tag
= kAudioChannelLayoutTag_Mono
;
666 *inlayout_tag
= kAudioChannelLayoutTag_Stereo
;
669 if (fmt
->i_physical_channels
& AOUT_CHAN_CENTER
) /* L R C */
670 *inlayout_tag
= kAudioChannelLayoutTag_DVD_7
;
671 else if (fmt
->i_physical_channels
& AOUT_CHAN_LFE
) /* L R LFE */
672 *inlayout_tag
= kAudioChannelLayoutTag_DVD_4
;
675 if (fmt
->i_physical_channels
& (AOUT_CHAN_CENTER
| AOUT_CHAN_LFE
)) /* L R C LFE */
676 *inlayout_tag
= kAudioChannelLayoutTag_DVD_10
;
677 else if (fmt
->i_physical_channels
& AOUT_CHANS_REAR
) /* L R Ls Rs */
678 *inlayout_tag
= kAudioChannelLayoutTag_DVD_3
;
679 else if (fmt
->i_physical_channels
& AOUT_CHANS_CENTER
) /* L R C Cs */
680 *inlayout_tag
= kAudioChannelLayoutTag_DVD_3
;
683 if (fmt
->i_physical_channels
& (AOUT_CHAN_CENTER
)) /* L R Ls Rs C */
684 *inlayout_tag
= kAudioChannelLayoutTag_DVD_19
;
685 else if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
)) /* L R Ls Rs LFE */
686 *inlayout_tag
= kAudioChannelLayoutTag_DVD_18
;
689 if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
))
691 /* L R Ls Rs C LFE */
692 *inlayout_tag
= kAudioChannelLayoutTag_DVD_20
;
694 chans_out
[0] = AOUT_CHAN_LEFT
;
695 chans_out
[1] = AOUT_CHAN_RIGHT
;
696 chans_out
[2] = AOUT_CHAN_REARLEFT
;
697 chans_out
[3] = AOUT_CHAN_REARRIGHT
;
698 chans_out
[4] = AOUT_CHAN_CENTER
;
699 chans_out
[5] = AOUT_CHAN_LFE
;
701 p_sys
->chans_to_reorder
=
702 aout_CheckChannelReorder(NULL
, chans_out
,
703 fmt
->i_physical_channels
,
705 if (p_sys
->chans_to_reorder
)
706 msg_Dbg(p_aout
, "channel reordering needed for 5.1 output");
711 *inlayout_tag
= kAudioChannelLayoutTag_AudioUnit_6_0
;
713 chans_out
[0] = AOUT_CHAN_LEFT
;
714 chans_out
[1] = AOUT_CHAN_RIGHT
;
715 chans_out
[2] = AOUT_CHAN_REARLEFT
;
716 chans_out
[3] = AOUT_CHAN_REARRIGHT
;
717 chans_out
[4] = AOUT_CHAN_CENTER
;
718 chans_out
[5] = AOUT_CHAN_REARCENTER
;
720 p_sys
->chans_to_reorder
=
721 aout_CheckChannelReorder(NULL
, chans_out
,
722 fmt
->i_physical_channels
,
724 if (p_sys
->chans_to_reorder
)
725 msg_Dbg(p_aout
, "channel reordering needed for 6.0 output");
729 /* L R C LFE Ls Rs Cs */
730 *inlayout_tag
= kAudioChannelLayoutTag_MPEG_6_1_A
;
732 chans_out
[0] = AOUT_CHAN_LEFT
;
733 chans_out
[1] = AOUT_CHAN_RIGHT
;
734 chans_out
[2] = AOUT_CHAN_CENTER
;
735 chans_out
[3] = AOUT_CHAN_LFE
;
736 chans_out
[4] = AOUT_CHAN_REARLEFT
;
737 chans_out
[5] = AOUT_CHAN_REARRIGHT
;
738 chans_out
[6] = AOUT_CHAN_REARCENTER
;
740 p_sys
->chans_to_reorder
=
741 aout_CheckChannelReorder(NULL
, chans_out
,
742 fmt
->i_physical_channels
,
744 if (p_sys
->chans_to_reorder
)
745 msg_Dbg(p_aout
, "channel reordering needed for 6.1 output");
749 if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
))
751 /* L R C LFE Ls Rs Rls Rrs */
752 *inlayout_tag
= kAudioChannelLayoutTag_MPEG_7_1_C
;
754 chans_out
[0] = AOUT_CHAN_LEFT
;
755 chans_out
[1] = AOUT_CHAN_RIGHT
;
756 chans_out
[2] = AOUT_CHAN_CENTER
;
757 chans_out
[3] = AOUT_CHAN_LFE
;
758 chans_out
[4] = AOUT_CHAN_MIDDLELEFT
;
759 chans_out
[5] = AOUT_CHAN_MIDDLERIGHT
;
760 chans_out
[6] = AOUT_CHAN_REARLEFT
;
761 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
765 /* Lc C Rc L R Ls Cs Rs */
766 *inlayout_tag
= kAudioChannelLayoutTag_DTS_8_0_B
;
768 chans_out
[0] = AOUT_CHAN_MIDDLELEFT
;
769 chans_out
[1] = AOUT_CHAN_CENTER
;
770 chans_out
[2] = AOUT_CHAN_MIDDLERIGHT
;
771 chans_out
[3] = AOUT_CHAN_LEFT
;
772 chans_out
[4] = AOUT_CHAN_RIGHT
;
773 chans_out
[5] = AOUT_CHAN_REARLEFT
;
774 chans_out
[6] = AOUT_CHAN_REARCENTER
;
775 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
777 p_sys
->chans_to_reorder
=
778 aout_CheckChannelReorder(NULL
, chans_out
,
779 fmt
->i_physical_channels
,
781 if (p_sys
->chans_to_reorder
)
782 msg_Dbg(p_aout
, "channel reordering needed for 7.1 / 8.0 output");
785 /* Lc C Rc L R Ls Cs Rs LFE */
786 *inlayout_tag
= kAudioChannelLayoutTag_DTS_8_1_B
;
787 chans_out
[0] = AOUT_CHAN_MIDDLELEFT
;
788 chans_out
[1] = AOUT_CHAN_CENTER
;
789 chans_out
[2] = AOUT_CHAN_MIDDLERIGHT
;
790 chans_out
[3] = AOUT_CHAN_LEFT
;
791 chans_out
[4] = AOUT_CHAN_RIGHT
;
792 chans_out
[5] = AOUT_CHAN_REARLEFT
;
793 chans_out
[6] = AOUT_CHAN_REARCENTER
;
794 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
795 chans_out
[8] = AOUT_CHAN_LFE
;
797 p_sys
->chans_to_reorder
=
798 aout_CheckChannelReorder(NULL
, chans_out
,
799 fmt
->i_physical_channels
,
801 if (p_sys
->chans_to_reorder
)
802 msg_Dbg(p_aout
, "channel reordering needed for 8.1 output");
810 au_Initialize(audio_output_t
*p_aout
, AudioUnit au
, audio_sample_format_t
*fmt
,
811 const AudioChannelLayout
*outlayout
, vlc_tick_t i_dev_latency_us
,
812 bool *warn_configuration
)
815 AudioChannelLayoutTag inlayout_tag
;
817 if (warn_configuration
)
818 *warn_configuration
= false;
820 /* Set the desired format */
821 AudioStreamBasicDescription desc
;
822 if (aout_BitsPerSample(fmt
->i_format
) != 0)
825 fmt
->i_format
= VLC_CODEC_FL32
;
826 ret
= MapOutputLayout(p_aout
, fmt
, outlayout
, warn_configuration
);
827 if (ret
!= VLC_SUCCESS
)
830 ret
= SetupInputLayout(p_aout
, fmt
, &inlayout_tag
);
831 if (ret
!= VLC_SUCCESS
)
834 desc
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
;
835 desc
.mChannelsPerFrame
= aout_FormatNbChannels(fmt
);
836 desc
.mBitsPerChannel
= 32;
838 else if (AOUT_FMT_SPDIF(fmt
))
841 fmt
->i_format
= VLC_CODEC_SPDIFL
;
842 fmt
->i_bytes_per_frame
= 4;
843 fmt
->i_frame_length
= 1;
845 inlayout_tag
= kAudioChannelLayoutTag_Stereo
;
847 desc
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
|
848 kLinearPCMFormatFlagIsPacked
; /* S16LE */
849 desc
.mChannelsPerFrame
= 2;
850 desc
.mBitsPerChannel
= 16;
855 desc
.mSampleRate
= fmt
->i_rate
;
856 desc
.mFormatID
= kAudioFormatLinearPCM
;
857 desc
.mFramesPerPacket
= 1;
858 desc
.mBytesPerFrame
= desc
.mBitsPerChannel
* desc
.mChannelsPerFrame
/ 8;
859 desc
.mBytesPerPacket
= desc
.mBytesPerFrame
* desc
.mFramesPerPacket
;
861 OSStatus err
= AudioUnitSetProperty(au
, kAudioUnitProperty_StreamFormat
,
862 kAudioUnitScope_Input
, 0, &desc
,
866 ca_LogErr("failed to set stream format");
869 msg_Dbg(p_aout
, STREAM_FORMAT_MSG("Current AU format: " , desc
));
871 /* Retrieve actual format */
872 err
= AudioUnitGetProperty(au
, kAudioUnitProperty_StreamFormat
,
873 kAudioUnitScope_Input
, 0, &desc
,
874 &(UInt32
) { sizeof(desc
) });
877 ca_LogErr("failed to set stream format");
881 /* Set the IOproc callback */
882 const AURenderCallbackStruct callback
= {
883 .inputProc
= RenderCallback
,
884 .inputProcRefCon
= p_aout
,
887 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_SetRenderCallback
,
888 kAudioUnitScope_Input
, 0, &callback
,
892 ca_LogErr("failed to setup render callback");
896 /* Set the input_layout as the layout VLC will use to feed the AU unit.
897 * Yes, it must be the INPUT scope */
898 AudioChannelLayout inlayout
= {
899 .mChannelLayoutTag
= inlayout_tag
,
901 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_AudioChannelLayout
,
902 kAudioUnitScope_Input
, 0, &inlayout
,
906 ca_LogErr("failed to setup input layout");
911 err
= AudioUnitInitialize(au
);
915 ca_LogErr("AudioUnitInitialize failed");
919 ret
= ca_Initialize(p_aout
, fmt
, i_dev_latency_us
);
920 if (ret
!= VLC_SUCCESS
)
922 AudioUnitUninitialize(au
);
930 au_Uninitialize(audio_output_t
*p_aout
, AudioUnit au
)
932 OSStatus err
= AudioUnitUninitialize(au
);
934 ca_LogWarn("AudioUnitUninitialize failed");
936 ca_Uninitialize(p_aout
);