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
;
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
, mtime_t
*delay
)
207 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
211 if (tinfo
.denom
== 0 || 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;
220 const int64_t i_out_frames
= BytesToFrames(p_sys
, p_sys
->i_out_size
);
221 *delay
= FramesToUs(p_sys
, i_out_frames
+ p_sys
->i_render_frames
)
222 + p_sys
->i_dev_latency_us
+ i_render_time_us
- mdate();
229 ca_Flush(audio_output_t
*p_aout
, bool wait
)
231 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
236 while (p_sys
->i_out_size
> 0)
240 ca_ClearOutBuffers(p_aout
);
244 /* Calculate the duration of the circular buffer, in order to wait
245 * for the render thread to play it all */
246 const mtime_t i_frame_us
=
247 FramesToUs(p_sys
, BytesToFrames(p_sys
, p_sys
->i_out_size
)) + 10000;
255 assert(!p_sys
->b_do_flush
);
257 ca_ClearOutBuffers(p_aout
);
260 p_sys
->b_do_flush
= true;
262 vlc_sem_wait(&p_sys
->flush_sem
);
267 p_sys
->i_render_host_time
= 0;
268 p_sys
->i_render_frames
= 0;
273 ca_Pause(audio_output_t
* p_aout
, bool pause
, mtime_t date
)
275 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
279 p_sys
->b_paused
= pause
;
284 ca_Play(audio_output_t
* p_aout
, block_t
* p_block
)
286 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
288 /* Do the channel reordering */
289 if (p_sys
->chans_to_reorder
)
290 aout_ChannelReorder(p_block
->p_buffer
, p_block
->i_buffer
,
291 p_sys
->chans_to_reorder
, p_sys
->chan_table
,
297 const size_t i_avalaible_bytes
=
298 __MIN(p_block
->i_buffer
, p_sys
->i_out_max_size
- p_sys
->i_out_size
);
300 if (unlikely(i_avalaible_bytes
!= p_block
->i_buffer
))
302 /* Not optimal but unlikely code path. */
306 block_t
*p_new
= block_Alloc(i_avalaible_bytes
);
309 block_Release(p_block
);
313 memcpy(p_new
->p_buffer
, p_block
->p_buffer
, i_avalaible_bytes
);
315 p_block
->p_buffer
+= i_avalaible_bytes
;
316 p_block
->i_buffer
-= i_avalaible_bytes
;
320 block_ChainLastAppend(&p_sys
->pp_out_last
, p_new
);
321 p_sys
->i_out_size
+= i_avalaible_bytes
;
326 block_Release(p_block
);
330 const mtime_t i_frame_us
=
331 FramesToUs(p_sys
, BytesToFrames(p_sys
, p_block
->i_buffer
));
333 /* Wait for the render buffer to play the remaining data */
340 block_ChainLastAppend(&p_sys
->pp_out_last
, p_block
);
341 p_sys
->i_out_size
+= i_avalaible_bytes
;
344 } while (p_block
!= NULL
);
346 size_t i_underrun_size
= p_sys
->i_underrun_size
;
347 p_sys
->i_underrun_size
= 0;
351 if (i_underrun_size
> 0)
352 msg_Warn(p_aout
, "underrun of %zu bytes", i_underrun_size
);
356 ca_Initialize(audio_output_t
*p_aout
, const audio_sample_format_t
*fmt
,
357 mtime_t i_dev_latency_us
)
359 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
361 p_sys
->i_underrun_size
= 0;
362 p_sys
->b_paused
= false;
363 p_sys
->i_render_host_time
= 0;
364 p_sys
->i_render_frames
= 0;
366 p_sys
->i_rate
= fmt
->i_rate
;
367 p_sys
->i_bytes_per_frame
= fmt
->i_bytes_per_frame
;
368 p_sys
->i_frame_length
= fmt
->i_frame_length
;
369 p_sys
->chans_to_reorder
= 0;
371 msg_Dbg(p_aout
, "Current device has a latency of %lld us",
374 /* TODO VLC can't handle latency higher than 1 seconds */
375 if (i_dev_latency_us
> 1000000)
377 i_dev_latency_us
= 1000000;
378 msg_Warn(p_aout
, "VLC can't handle this device latency, lowering it to "
379 "%lld", i_dev_latency_us
);
381 p_sys
->i_dev_latency_us
= i_dev_latency_us
;
383 /* setup circular buffer */
384 size_t i_audiobuffer_size
= fmt
->i_rate
* fmt
->i_bytes_per_frame
385 / p_sys
->i_frame_length
;
386 if (fmt
->channel_type
== AUDIO_CHANNEL_TYPE_AMBISONICS
)
388 /* lower latency: 200 ms of buffering. XXX: Decrease when VLC's core
389 * can handle lower audio latency */
390 p_sys
->i_out_max_size
= i_audiobuffer_size
/ 5;
394 /* 2 seconds of buffering */
395 p_sys
->i_out_max_size
= i_audiobuffer_size
* 2;
398 ca_ClearOutBuffers(p_aout
);
404 ca_Uninitialize(audio_output_t
*p_aout
)
406 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
407 ca_ClearOutBuffers(p_aout
);
408 p_sys
->i_out_max_size
= 0;
412 ca_SetAliveState(audio_output_t
*p_aout
, bool alive
)
414 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
418 bool b_sem_post
= false;
419 p_sys
->b_paused
= !alive
;
420 if (!alive
&& p_sys
->b_do_flush
)
422 ca_ClearOutBuffers(p_aout
);
423 p_sys
->b_do_flush
= false;
430 vlc_sem_post(&p_sys
->flush_sem
);
434 au_NewOutputInstance(audio_output_t
*p_aout
, OSType comp_sub_type
)
436 AudioComponentDescription desc
= {
437 .componentType
= kAudioUnitType_Output
,
438 .componentSubType
= comp_sub_type
,
439 .componentManufacturer
= kAudioUnitManufacturer_Apple
,
441 .componentFlagsMask
= 0,
444 AudioComponent au_component
;
445 au_component
= AudioComponentFindNext(NULL
, &desc
);
446 if (au_component
== NULL
)
448 msg_Err(p_aout
, "cannot find any AudioComponent, PCM output failed");
453 OSStatus err
= AudioComponentInstanceNew(au_component
, &au
);
456 ca_LogErr("cannot open AudioComponent, PCM output failed");
462 /*****************************************************************************
463 * RenderCallback: This function is called everytime the AudioUnit wants
464 * us to provide some more audio data.
465 * Don't print anything during normal playback, calling blocking function from
466 * this callback is not allowed.
467 *****************************************************************************/
469 RenderCallback(void *p_data
, AudioUnitRenderActionFlags
*ioActionFlags
,
470 const AudioTimeStamp
*inTimeStamp
, UInt32 inBusNumber
,
471 UInt32 inNumberFrames
, AudioBufferList
*ioData
)
473 VLC_UNUSED(ioActionFlags
);
474 VLC_UNUSED(inTimeStamp
);
475 VLC_UNUSED(inBusNumber
);
477 uint64_t i_host_time
= (inTimeStamp
->mFlags
& kAudioTimeStampHostTimeValid
)
478 ? inTimeStamp
->mHostTime
: 0;
480 ca_Render(p_data
, inNumberFrames
, i_host_time
, ioData
->mBuffers
[0].mData
,
481 ioData
->mBuffers
[0].mDataByteSize
);
486 static AudioChannelLayout
*
487 GetLayoutDescription(audio_output_t
*p_aout
,
488 const AudioChannelLayout
*outlayout
)
490 AudioFormatPropertyID id
;
493 /* We need to "fill out" the ChannelLayout, because there are multiple
494 * ways that it can be set */
495 if (outlayout
->mChannelLayoutTag
== kAudioChannelLayoutTag_UseChannelBitmap
)
497 id
= kAudioFormatProperty_ChannelLayoutForBitmap
;
498 size
= sizeof(UInt32
);
499 data
= &outlayout
->mChannelBitmap
;
503 id
= kAudioFormatProperty_ChannelLayoutForTag
;
504 size
= sizeof(AudioChannelLayoutTag
);
505 data
= &outlayout
->mChannelLayoutTag
;
509 OSStatus err
= AudioFormatGetPropertyInfo(id
, size
, data
, ¶m_size
);
513 AudioChannelLayout
*reslayout
= malloc(param_size
);
514 if (reslayout
== NULL
)
517 err
= AudioFormatGetProperty(id
, size
, data
, ¶m_size
, reslayout
);
518 if (err
!= noErr
|| reslayout
->mNumberChannelDescriptions
== 0)
520 msg_Err(p_aout
, "insufficient number of output channels");
529 MapOutputLayout(audio_output_t
*p_aout
, audio_sample_format_t
*fmt
,
530 const AudioChannelLayout
*outlayout
, bool *warn_configuration
)
532 /* Fill VLC physical_channels from output layout */
533 fmt
->i_physical_channels
= 0;
534 uint32_t i_original
= fmt
->i_physical_channels
;
535 AudioChannelLayout
*reslayout
= NULL
;
537 if (outlayout
== NULL
)
539 msg_Dbg(p_aout
, "not output layout, default to Stereo");
540 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
544 if (outlayout
->mChannelLayoutTag
!=
545 kAudioChannelLayoutTag_UseChannelDescriptions
)
547 reslayout
= GetLayoutDescription(p_aout
, outlayout
);
548 if (reslayout
== NULL
)
550 outlayout
= reslayout
;
553 if (i_original
== AOUT_CHAN_CENTER
554 || outlayout
->mNumberChannelDescriptions
< 2)
556 /* We only need Mono or cannot output more than 1 channel */
557 fmt
->i_physical_channels
= AOUT_CHAN_CENTER
;
558 msg_Dbg(p_aout
, "output layout of AUHAL has 1 channel");
560 else if (i_original
== (AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
)
561 || outlayout
->mNumberChannelDescriptions
< 3)
563 /* We only need Stereo or cannot output more than 2 channels */
564 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
565 msg_Dbg(p_aout
, "output layout of AUHAL is Stereo");
569 assert(outlayout
->mNumberChannelDescriptions
> 0);
571 msg_Dbg(p_aout
, "output layout of AUHAL has %i channels",
572 outlayout
->mNumberChannelDescriptions
);
574 /* maps auhal channels to vlc ones */
575 static const unsigned i_auhal_channel_mapping
[] = {
576 [kAudioChannelLabel_Left
] = AOUT_CHAN_LEFT
,
577 [kAudioChannelLabel_Right
] = AOUT_CHAN_RIGHT
,
578 [kAudioChannelLabel_Center
] = AOUT_CHAN_CENTER
,
579 [kAudioChannelLabel_LFEScreen
] = AOUT_CHAN_LFE
,
580 [kAudioChannelLabel_LeftSurround
] = AOUT_CHAN_REARLEFT
,
581 [kAudioChannelLabel_RightSurround
] = AOUT_CHAN_REARRIGHT
,
582 /* needs to be swapped with rear */
583 [kAudioChannelLabel_RearSurroundLeft
] = AOUT_CHAN_MIDDLELEFT
,
584 /* needs to be swapped with rear */
585 [kAudioChannelLabel_RearSurroundRight
] = AOUT_CHAN_MIDDLERIGHT
,
586 [kAudioChannelLabel_CenterSurround
] = AOUT_CHAN_REARCENTER
588 static const size_t i_auhal_size
= sizeof(i_auhal_channel_mapping
)
589 / sizeof(i_auhal_channel_mapping
[0]);
591 /* We want more than stereo and we can do that */
592 for (unsigned i
= 0; i
< outlayout
->mNumberChannelDescriptions
; i
++)
594 AudioChannelLabel chan
=
595 outlayout
->mChannelDescriptions
[i
].mChannelLabel
;
597 msg_Dbg(p_aout
, "this is channel: %d", (int) chan
);
599 if (chan
< i_auhal_size
&& i_auhal_channel_mapping
[chan
] > 0)
600 fmt
->i_physical_channels
|= i_auhal_channel_mapping
[chan
];
602 msg_Dbg(p_aout
, "found nonrecognized channel %d at index "
605 if (fmt
->i_physical_channels
== 0)
607 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
608 if (warn_configuration
)
609 *warn_configuration
= true;
616 aout_FormatPrepare(fmt
);
618 msg_Dbg(p_aout
, "selected %d physical channels for device output",
619 aout_FormatNbChannels(fmt
));
620 msg_Dbg(p_aout
, "VLC will output: %s", aout_FormatPrintChannels(fmt
));
626 SetupInputLayout(audio_output_t
*p_aout
, const audio_sample_format_t
*fmt
,
627 AudioChannelLayoutTag
*inlayout_tag
)
629 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
630 uint32_t chans_out
[AOUT_CHAN_MAX
];
632 /* Some channel abbreviations used below:
637 * Rs - right surround
638 * Cs - center surround
639 * Rls - rear left surround
640 * Rrs - rear right surround
643 * Lsd - left surround direct
644 * Rsd - right surround direct
648 * Vhl - vertical height left
649 * Vhc - vertical height center
650 * Vhr - vertical height right
651 * Lt - left matrix total. for matrix encoded stereo.
652 * Rt - right matrix total. for matrix encoded stereo. */
654 switch (aout_FormatNbChannels(fmt
))
657 *inlayout_tag
= kAudioChannelLayoutTag_Mono
;
660 *inlayout_tag
= kAudioChannelLayoutTag_Stereo
;
663 if (fmt
->i_physical_channels
& AOUT_CHAN_CENTER
) /* L R C */
664 *inlayout_tag
= kAudioChannelLayoutTag_DVD_7
;
665 else if (fmt
->i_physical_channels
& AOUT_CHAN_LFE
) /* L R LFE */
666 *inlayout_tag
= kAudioChannelLayoutTag_DVD_4
;
669 if (fmt
->i_physical_channels
& (AOUT_CHAN_CENTER
| AOUT_CHAN_LFE
)) /* L R C LFE */
670 *inlayout_tag
= kAudioChannelLayoutTag_DVD_10
;
671 else if (fmt
->i_physical_channels
& AOUT_CHANS_REAR
) /* L R Ls Rs */
672 *inlayout_tag
= kAudioChannelLayoutTag_DVD_3
;
673 else if (fmt
->i_physical_channels
& AOUT_CHANS_CENTER
) /* L R C Cs */
674 *inlayout_tag
= kAudioChannelLayoutTag_DVD_3
;
677 if (fmt
->i_physical_channels
& (AOUT_CHAN_CENTER
)) /* L R Ls Rs C */
678 *inlayout_tag
= kAudioChannelLayoutTag_DVD_19
;
679 else if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
)) /* L R Ls Rs LFE */
680 *inlayout_tag
= kAudioChannelLayoutTag_DVD_18
;
683 if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
))
685 /* L R Ls Rs C LFE */
686 *inlayout_tag
= kAudioChannelLayoutTag_DVD_20
;
688 chans_out
[0] = AOUT_CHAN_LEFT
;
689 chans_out
[1] = AOUT_CHAN_RIGHT
;
690 chans_out
[2] = AOUT_CHAN_REARLEFT
;
691 chans_out
[3] = AOUT_CHAN_REARRIGHT
;
692 chans_out
[4] = AOUT_CHAN_CENTER
;
693 chans_out
[5] = AOUT_CHAN_LFE
;
695 p_sys
->chans_to_reorder
=
696 aout_CheckChannelReorder(NULL
, chans_out
,
697 fmt
->i_physical_channels
,
699 if (p_sys
->chans_to_reorder
)
700 msg_Dbg(p_aout
, "channel reordering needed for 5.1 output");
705 *inlayout_tag
= kAudioChannelLayoutTag_AudioUnit_6_0
;
707 chans_out
[0] = AOUT_CHAN_LEFT
;
708 chans_out
[1] = AOUT_CHAN_RIGHT
;
709 chans_out
[2] = AOUT_CHAN_REARLEFT
;
710 chans_out
[3] = AOUT_CHAN_REARRIGHT
;
711 chans_out
[4] = AOUT_CHAN_CENTER
;
712 chans_out
[5] = AOUT_CHAN_REARCENTER
;
714 p_sys
->chans_to_reorder
=
715 aout_CheckChannelReorder(NULL
, chans_out
,
716 fmt
->i_physical_channels
,
718 if (p_sys
->chans_to_reorder
)
719 msg_Dbg(p_aout
, "channel reordering needed for 6.0 output");
723 /* L R C LFE Ls Rs Cs */
724 *inlayout_tag
= kAudioChannelLayoutTag_MPEG_6_1_A
;
726 chans_out
[0] = AOUT_CHAN_LEFT
;
727 chans_out
[1] = AOUT_CHAN_RIGHT
;
728 chans_out
[2] = AOUT_CHAN_CENTER
;
729 chans_out
[3] = AOUT_CHAN_LFE
;
730 chans_out
[4] = AOUT_CHAN_REARLEFT
;
731 chans_out
[5] = AOUT_CHAN_REARRIGHT
;
732 chans_out
[6] = AOUT_CHAN_REARCENTER
;
734 p_sys
->chans_to_reorder
=
735 aout_CheckChannelReorder(NULL
, chans_out
,
736 fmt
->i_physical_channels
,
738 if (p_sys
->chans_to_reorder
)
739 msg_Dbg(p_aout
, "channel reordering needed for 6.1 output");
743 if (fmt
->i_physical_channels
& (AOUT_CHAN_LFE
))
745 /* L R C LFE Ls Rs Rls Rrs */
746 *inlayout_tag
= kAudioChannelLayoutTag_MPEG_7_1_C
;
748 chans_out
[0] = AOUT_CHAN_LEFT
;
749 chans_out
[1] = AOUT_CHAN_RIGHT
;
750 chans_out
[2] = AOUT_CHAN_CENTER
;
751 chans_out
[3] = AOUT_CHAN_LFE
;
752 chans_out
[4] = AOUT_CHAN_MIDDLELEFT
;
753 chans_out
[5] = AOUT_CHAN_MIDDLERIGHT
;
754 chans_out
[6] = AOUT_CHAN_REARLEFT
;
755 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
759 /* Lc C Rc L R Ls Cs Rs */
760 *inlayout_tag
= kAudioChannelLayoutTag_DTS_8_0_B
;
762 chans_out
[0] = AOUT_CHAN_MIDDLELEFT
;
763 chans_out
[1] = AOUT_CHAN_CENTER
;
764 chans_out
[2] = AOUT_CHAN_MIDDLERIGHT
;
765 chans_out
[3] = AOUT_CHAN_LEFT
;
766 chans_out
[4] = AOUT_CHAN_RIGHT
;
767 chans_out
[5] = AOUT_CHAN_REARLEFT
;
768 chans_out
[6] = AOUT_CHAN_REARCENTER
;
769 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
771 p_sys
->chans_to_reorder
=
772 aout_CheckChannelReorder(NULL
, chans_out
,
773 fmt
->i_physical_channels
,
775 if (p_sys
->chans_to_reorder
)
776 msg_Dbg(p_aout
, "channel reordering needed for 7.1 / 8.0 output");
779 /* Lc C Rc L R Ls Cs Rs LFE */
780 *inlayout_tag
= kAudioChannelLayoutTag_DTS_8_1_B
;
781 chans_out
[0] = AOUT_CHAN_MIDDLELEFT
;
782 chans_out
[1] = AOUT_CHAN_CENTER
;
783 chans_out
[2] = AOUT_CHAN_MIDDLERIGHT
;
784 chans_out
[3] = AOUT_CHAN_LEFT
;
785 chans_out
[4] = AOUT_CHAN_RIGHT
;
786 chans_out
[5] = AOUT_CHAN_REARLEFT
;
787 chans_out
[6] = AOUT_CHAN_REARCENTER
;
788 chans_out
[7] = AOUT_CHAN_REARRIGHT
;
789 chans_out
[8] = AOUT_CHAN_LFE
;
791 p_sys
->chans_to_reorder
=
792 aout_CheckChannelReorder(NULL
, chans_out
,
793 fmt
->i_physical_channels
,
795 if (p_sys
->chans_to_reorder
)
796 msg_Dbg(p_aout
, "channel reordering needed for 8.1 output");
804 au_Initialize(audio_output_t
*p_aout
, AudioUnit au
, audio_sample_format_t
*fmt
,
805 const AudioChannelLayout
*outlayout
, mtime_t i_dev_latency_us
,
806 bool *warn_configuration
)
809 AudioChannelLayoutTag inlayout_tag
;
811 if (warn_configuration
)
812 *warn_configuration
= false;
814 /* Set the desired format */
815 AudioStreamBasicDescription desc
;
816 if (aout_BitsPerSample(fmt
->i_format
) != 0)
819 fmt
->i_format
= VLC_CODEC_FL32
;
820 ret
= MapOutputLayout(p_aout
, fmt
, outlayout
, warn_configuration
);
821 if (ret
!= VLC_SUCCESS
)
824 ret
= SetupInputLayout(p_aout
, fmt
, &inlayout_tag
);
825 if (ret
!= VLC_SUCCESS
)
828 desc
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
;
829 desc
.mChannelsPerFrame
= aout_FormatNbChannels(fmt
);
830 desc
.mBitsPerChannel
= 32;
832 else if (AOUT_FMT_SPDIF(fmt
))
835 fmt
->i_format
= VLC_CODEC_SPDIFL
;
836 fmt
->i_bytes_per_frame
= 4;
837 fmt
->i_frame_length
= 1;
839 inlayout_tag
= kAudioChannelLayoutTag_Stereo
;
841 desc
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
|
842 kLinearPCMFormatFlagIsPacked
; /* S16LE */
843 desc
.mChannelsPerFrame
= 2;
844 desc
.mBitsPerChannel
= 16;
849 desc
.mSampleRate
= fmt
->i_rate
;
850 desc
.mFormatID
= kAudioFormatLinearPCM
;
851 desc
.mFramesPerPacket
= 1;
852 desc
.mBytesPerFrame
= desc
.mBitsPerChannel
* desc
.mChannelsPerFrame
/ 8;
853 desc
.mBytesPerPacket
= desc
.mBytesPerFrame
* desc
.mFramesPerPacket
;
855 OSStatus err
= AudioUnitSetProperty(au
, kAudioUnitProperty_StreamFormat
,
856 kAudioUnitScope_Input
, 0, &desc
,
860 ca_LogErr("failed to set stream format");
863 msg_Dbg(p_aout
, STREAM_FORMAT_MSG("Current AU format: " , desc
));
865 /* Retrieve actual format */
866 err
= AudioUnitGetProperty(au
, kAudioUnitProperty_StreamFormat
,
867 kAudioUnitScope_Input
, 0, &desc
,
868 &(UInt32
) { sizeof(desc
) });
871 ca_LogErr("failed to set stream format");
875 /* Set the IOproc callback */
876 const AURenderCallbackStruct callback
= {
877 .inputProc
= RenderCallback
,
878 .inputProcRefCon
= p_aout
,
881 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_SetRenderCallback
,
882 kAudioUnitScope_Input
, 0, &callback
,
886 ca_LogErr("failed to setup render callback");
890 /* Set the input_layout as the layout VLC will use to feed the AU unit.
891 * Yes, it must be the INPUT scope */
892 AudioChannelLayout inlayout
= {
893 .mChannelLayoutTag
= inlayout_tag
,
895 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_AudioChannelLayout
,
896 kAudioUnitScope_Input
, 0, &inlayout
,
900 ca_LogErr("failed to setup input layout");
905 err
= AudioUnitInitialize(au
);
909 ca_LogErr("AudioUnitInitialize failed");
913 ret
= ca_Initialize(p_aout
, fmt
, i_dev_latency_us
);
914 if (ret
!= VLC_SUCCESS
)
916 AudioUnitUninitialize(au
);
924 au_Uninitialize(audio_output_t
*p_aout
, AudioUnit au
)
926 OSStatus err
= AudioUnitUninitialize(au
);
928 ca_LogWarn("AudioUnitUninitialize failed");
930 ca_Uninitialize(p_aout
);