5f8a633bee4a74600e0e50b56a547f299f6b7947
[vlc.git] / modules / audio_output / coreaudio_common.c
1 /*****************************************************************************
2  * coreaudio_common.c: Common AudioUnit code for iOS and macOS
3  *****************************************************************************
4  * Copyright (C) 2005 - 2017 VLC authors and VideoLAN
5  *
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>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #import "coreaudio_common.h"
26 #import <CoreAudio/CoreAudioTypes.h>
27
28 static inline uint64_t
29 BytesToFrames(struct aout_sys_common *p_sys, size_t i_bytes)
30 {
31     return i_bytes * p_sys->i_frame_length / p_sys->i_bytes_per_frame;
32 }
33
34 static inline vlc_tick_t
35 FramesToUs(struct aout_sys_common *p_sys, uint64_t i_nb_frames)
36 {
37     return vlc_tick_from_samples(i_nb_frames, p_sys->i_rate);
38 }
39
40 static inline size_t
41 FramesToBytes(struct aout_sys_common *p_sys, uint64_t i_frames)
42 {
43     return i_frames * p_sys->i_bytes_per_frame / p_sys->i_frame_length;
44 }
45
46 static inline uint64_t
47 UsToFrames(struct aout_sys_common *p_sys, vlc_tick_t i_us)
48 {
49     return samples_from_vlc_tick(i_us, p_sys->i_rate);
50 }
51
52 static inline vlc_tick_t
53 HostTimeToTick(struct aout_sys_common *p_sys, uint64_t i_host_time)
54 {
55     return VLC_TICK_FROM_NS(i_host_time * p_sys->tinfo.numer / p_sys->tinfo.denom);
56 }
57
58 static inline uint64_t
59 TickToHostTime(struct aout_sys_common *p_sys, vlc_tick_t i_us)
60 {
61     return NS_FROM_VLC_TICK(i_us * p_sys->tinfo.denom / p_sys->tinfo.numer);
62 }
63
64 static void
65 ca_ClearOutBuffers(audio_output_t *p_aout)
66 {
67     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
68
69     block_ChainRelease(p_sys->p_out_chain);
70     p_sys->p_out_chain = NULL;
71     p_sys->pp_out_last = &p_sys->p_out_chain;
72
73     p_sys->i_out_size = 0;
74 }
75
76 static inline void
77 lock_init(struct aout_sys_common *p_sys)
78 {
79     if (likely(os_unfair_lock_lock))
80         p_sys->lock.unfair = OS_UNFAIR_LOCK_INIT;
81     else
82         vlc_mutex_init(&p_sys->lock.mutex);
83 }
84
85 static inline void
86 lock_lock(struct aout_sys_common *p_sys)
87 {
88     if (likely(os_unfair_lock_lock))
89         os_unfair_lock_lock(&p_sys->lock.unfair);
90     else
91         vlc_mutex_lock(&p_sys->lock.mutex);
92 }
93
94 static inline void
95 lock_unlock(struct aout_sys_common *p_sys)
96 {
97     if (likely(os_unfair_lock_lock))
98         os_unfair_lock_unlock(&p_sys->lock.unfair);
99     else
100         vlc_mutex_unlock(&p_sys->lock.mutex);
101 }
102
103 int
104 ca_Open(audio_output_t *p_aout)
105 {
106     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
107
108     if (mach_timebase_info(&p_sys->tinfo) != KERN_SUCCESS)
109         return VLC_EGENERIC;
110
111     assert(p_sys->tinfo.denom != 0 && p_sys->tinfo.numer != 0);
112
113     vlc_sem_init(&p_sys->flush_sem, 0);
114     lock_init(p_sys);
115     p_sys->p_out_chain = NULL;
116
117     p_aout->play = ca_Play;
118     p_aout->pause = ca_Pause;
119     p_aout->flush = ca_Flush;
120     p_aout->time_get = ca_TimeGet;
121
122     return VLC_SUCCESS;
123 }
124
125 /* Called from render callbacks. No lock, wait, and IO here */
126 void
127 ca_Render(audio_output_t *p_aout, uint32_t i_frames, uint64_t i_host_time,
128           uint8_t *p_output, size_t i_requested)
129 {
130     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
131
132     lock_lock(p_sys);
133
134     if (p_sys->b_do_flush)
135     {
136         ca_ClearOutBuffers(p_aout);
137         /* Signal that the renderer is flushed */
138         p_sys->b_do_flush = false;
139         vlc_sem_post(&p_sys->flush_sem);
140     }
141
142     if (p_sys->b_paused || unlikely(p_sys->i_first_render_host_time == 0))
143         goto drop;
144
145     /* Start deferred: write silence (zeros) until we reach the first render
146      * host time. */
147     if (unlikely(p_sys->i_first_render_host_time > i_host_time ))
148     {
149         /* Convert the requested bytes into host time and check that it does
150          * not overlap between the first_render host time and the current one.
151          * */
152         const size_t i_requested_us =
153             FramesToUs(p_sys, BytesToFrames(p_sys, i_requested));
154         const uint64_t i_requested_host_time =
155             TickToHostTime(p_sys, i_requested_us);
156         if (p_sys->i_first_render_host_time >= i_host_time + i_requested_host_time)
157         {
158             /* Fill the buffer with silence */
159             goto drop;
160         }
161
162         /* Write silence to reach the first_render host time */
163         const vlc_tick_t i_silence_us =
164             HostTimeToTick(p_sys, p_sys->i_first_render_host_time - i_host_time);
165
166         const uint64_t i_silence_bytes =
167             FramesToBytes(p_sys, UsToFrames(p_sys, i_silence_us));
168         assert(i_silence_bytes <= i_requested);
169         memset(p_output, 0, i_silence_bytes);
170
171         i_requested -= i_silence_bytes;
172
173         /* Start the first rendering */
174     }
175
176     p_sys->i_render_host_time = i_host_time;
177     p_sys->i_render_frames = i_frames;
178
179     size_t i_copied = 0;
180     block_t *p_block = p_sys->p_out_chain;
181     while (p_block != NULL && i_requested != 0)
182     {
183         size_t i_tocopy = __MIN(i_requested, p_block->i_buffer);
184         memcpy(&p_output[i_copied], p_block->p_buffer, i_tocopy);
185         i_requested -= i_tocopy;
186         i_copied += i_tocopy;
187         if (i_tocopy == p_block->i_buffer)
188         {
189             block_t *p_release = p_block;
190             p_block = p_block->p_next;
191             block_Release(p_release);
192         }
193         else
194         {
195             assert(i_requested == 0);
196
197             p_block->p_buffer += i_tocopy;
198             p_block->i_buffer -= i_tocopy;
199         }
200     }
201     p_sys->p_out_chain = p_block;
202     if (!p_sys->p_out_chain)
203         p_sys->pp_out_last = &p_sys->p_out_chain;
204     p_sys->i_out_size -= i_copied;
205
206     /* Pad with 0 */
207     if (i_requested > 0)
208     {
209         assert(p_sys->i_out_size == 0);
210         p_sys->i_underrun_size += i_requested;
211         memset(&p_output[i_copied], 0, i_requested);
212     }
213
214     lock_unlock(p_sys);
215     return;
216
217 drop:
218     memset(p_output, 0, i_requested);
219     lock_unlock(p_sys);
220 }
221
222 static vlc_tick_t
223 ca_GetLatencyLocked(audio_output_t *p_aout)
224 {
225     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
226
227     const int64_t i_out_frames = BytesToFrames(p_sys, p_sys->i_out_size);
228     return FramesToUs(p_sys, i_out_frames + p_sys->i_render_frames)
229            + p_sys->i_dev_latency_us;
230 }
231
232 int
233 ca_TimeGet(audio_output_t *p_aout, vlc_tick_t *delay)
234 {
235     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
236
237     lock_lock(p_sys);
238
239     if (p_sys->i_render_host_time == 0 || p_sys->i_first_render_host_time == 0)
240     {
241         /* Not yet started (or reached the first_render host time) */
242         lock_unlock(p_sys);
243         return -1;
244     }
245
246     uint64_t i_render_delay_host_time = p_sys->i_render_host_time
247                                       - mach_absolute_time();
248     const vlc_tick_t i_render_delay =
249         HostTimeToTick(p_sys, i_render_delay_host_time);
250
251     *delay = ca_GetLatencyLocked(p_aout) + i_render_delay;
252     lock_unlock(p_sys);
253     return 0;
254 }
255
256 void
257 ca_Flush(audio_output_t *p_aout)
258 {
259     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
260
261     lock_lock(p_sys);
262
263     assert(!p_sys->b_do_flush);
264     if (p_sys->b_paused)
265         ca_ClearOutBuffers(p_aout);
266     else
267     {
268         p_sys->b_do_flush = true;
269         lock_unlock(p_sys);
270         vlc_sem_wait(&p_sys->flush_sem);
271         lock_lock(p_sys);
272     }
273
274     p_sys->i_render_host_time = p_sys->i_first_render_host_time = 0;
275     p_sys->i_render_frames = 0;
276     lock_unlock(p_sys);
277
278     p_sys->b_played = false;
279 }
280
281 void
282 ca_Pause(audio_output_t * p_aout, bool pause, vlc_tick_t date)
283 {
284     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
285     VLC_UNUSED(date);
286
287     lock_lock(p_sys);
288     p_sys->b_paused = pause;
289     lock_unlock(p_sys);
290 }
291
292 void
293 ca_Play(audio_output_t * p_aout, block_t * p_block, vlc_tick_t date)
294 {
295     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
296
297     /* Do the channel reordering */
298     if (p_sys->chans_to_reorder)
299        aout_ChannelReorder(p_block->p_buffer, p_block->i_buffer,
300                            p_sys->chans_to_reorder, p_sys->chan_table,
301                            VLC_CODEC_FL32);
302
303     lock_lock(p_sys);
304
305     if (p_sys->i_render_host_time == 0)
306     {
307         /* Setup the first render time, this date must be updated until the
308          * first (non-silence/zero) frame is rendered by the render callback.
309          * Once the rendering is truly started, the date can be ignored. */
310
311         /* We can't convert date to host time directly since the clock source
312          * may be different (MONOTONIC vs continue during sleep). The solution
313          * is to convert it to a relative time and then add it to
314          * mach_absolute_time() */
315         const vlc_tick_t first_render_delay = date - vlc_tick_now()
316                                             - ca_GetLatencyLocked(p_aout);
317         p_sys->i_first_render_host_time
318             = mach_absolute_time() + TickToHostTime(p_sys, first_render_delay);
319     }
320
321     do
322     {
323         const size_t i_avalaible_bytes =
324             __MIN(p_block->i_buffer, p_sys->i_out_max_size - p_sys->i_out_size);
325
326         if (unlikely(i_avalaible_bytes != p_block->i_buffer))
327         {
328             /* Not optimal but unlikely code path. */
329
330             lock_unlock(p_sys);
331
332             block_t *p_new = block_Alloc(i_avalaible_bytes);
333             if (!p_new)
334             {
335                 block_Release(p_block);
336                 return;
337             }
338
339             memcpy(p_new->p_buffer, p_block->p_buffer, i_avalaible_bytes);
340
341             p_block->p_buffer += i_avalaible_bytes;
342             p_block->i_buffer -= i_avalaible_bytes;
343
344             lock_lock(p_sys);
345
346             block_ChainLastAppend(&p_sys->pp_out_last, p_new);
347             p_sys->i_out_size += i_avalaible_bytes;
348
349             if (p_sys->b_paused)
350             {
351                 lock_unlock(p_sys);
352                 block_Release(p_block);
353                 return;
354             }
355
356             const vlc_tick_t i_frame_us =
357                 FramesToUs(p_sys, BytesToFrames(p_sys, p_block->i_buffer));
358
359             /* Wait for the render buffer to play the remaining data */
360             lock_unlock(p_sys);
361             vlc_tick_sleep(i_frame_us);
362             lock_lock(p_sys);
363         }
364         else
365         {
366             block_ChainLastAppend(&p_sys->pp_out_last, p_block);
367             p_sys->i_out_size += i_avalaible_bytes;
368             p_block = NULL;
369         }
370     } while (p_block != NULL);
371
372     size_t i_underrun_size = p_sys->i_underrun_size;
373     p_sys->i_underrun_size = 0;
374
375     lock_unlock(p_sys);
376
377     if (!p_sys->b_played)
378         p_sys->b_played = true;
379     else if (i_underrun_size > 0)
380         msg_Warn(p_aout, "underrun of %zu bytes", i_underrun_size);
381
382     (void) date;
383 }
384
385 int
386 ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
387               vlc_tick_t i_dev_latency_us)
388 {
389     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
390
391     p_sys->i_underrun_size = 0;
392     p_sys->b_paused = false;
393     p_sys->i_render_host_time = p_sys->i_first_render_host_time = 0;
394     p_sys->i_render_frames = 0;
395
396     p_sys->i_rate = fmt->i_rate;
397     p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame;
398     p_sys->i_frame_length = fmt->i_frame_length;
399     p_sys->chans_to_reorder = 0;
400
401     p_sys->i_dev_latency_us = i_dev_latency_us;
402
403     /* setup circular buffer */
404     size_t i_audiobuffer_size = fmt->i_rate * fmt->i_bytes_per_frame
405                               / p_sys->i_frame_length;
406     if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
407     {
408         /* lower latency: 200 ms of buffering. XXX: Decrease when VLC's core
409          * can handle lower audio latency */
410         p_sys->i_out_max_size = i_audiobuffer_size / 5;
411     }
412     else
413     {
414         /* 2 seconds of buffering */
415         p_sys->i_out_max_size = i_audiobuffer_size * 2;
416     }
417
418     ca_ClearOutBuffers(p_aout);
419     p_sys->b_played = false;
420
421     return VLC_SUCCESS;
422 }
423
424 void
425 ca_Uninitialize(audio_output_t *p_aout)
426 {
427     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
428     ca_ClearOutBuffers(p_aout);
429     p_sys->i_out_max_size = 0;
430 }
431
432 void
433 ca_SetAliveState(audio_output_t *p_aout, bool alive)
434 {
435     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
436
437     lock_lock(p_sys);
438
439     bool b_sem_post = false;
440     p_sys->b_paused = !alive;
441     if (!alive && p_sys->b_do_flush)
442     {
443         ca_ClearOutBuffers(p_aout);
444         p_sys->b_played = false;
445         p_sys->b_do_flush = false;
446         b_sem_post = true;
447     }
448
449     lock_unlock(p_sys);
450
451     if (b_sem_post)
452         vlc_sem_post(&p_sys->flush_sem);
453 }
454
455 void ca_SetDeviceLatency(audio_output_t *p_aout, vlc_tick_t i_dev_latency_us)
456 {
457     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
458
459     lock_lock(p_sys);
460     p_sys->i_dev_latency_us = i_dev_latency_us;
461     lock_unlock(p_sys);
462 }
463
464 AudioUnit
465 au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type)
466 {
467     AudioComponentDescription desc = {
468         .componentType = kAudioUnitType_Output,
469         .componentSubType = comp_sub_type,
470         .componentManufacturer = kAudioUnitManufacturer_Apple,
471         .componentFlags = 0,
472         .componentFlagsMask = 0,
473     };
474
475     AudioComponent au_component;
476     au_component = AudioComponentFindNext(NULL, &desc);
477     if (au_component == NULL)
478     {
479         msg_Err(p_aout, "cannot find any AudioComponent, PCM output failed");
480         return NULL;
481     }
482
483     AudioUnit au;
484     OSStatus err = AudioComponentInstanceNew(au_component, &au);
485     if (err != noErr)
486     {
487         ca_LogErr("cannot open AudioComponent, PCM output failed");
488         return NULL;
489     }
490     return au;
491 }
492
493 /*****************************************************************************
494  * RenderCallback: This function is called everytime the AudioUnit wants
495  * us to provide some more audio data.
496  * Don't print anything during normal playback, calling blocking function from
497  * this callback is not allowed.
498  *****************************************************************************/
499 static OSStatus
500 RenderCallback(void *p_data, AudioUnitRenderActionFlags *ioActionFlags,
501                const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
502                UInt32 inNumberFrames, AudioBufferList *ioData)
503 {
504     VLC_UNUSED(ioActionFlags);
505     VLC_UNUSED(inTimeStamp);
506     VLC_UNUSED(inBusNumber);
507
508     uint64_t i_host_time = (inTimeStamp->mFlags & kAudioTimeStampHostTimeValid)
509                          ? inTimeStamp->mHostTime : 0;
510
511     ca_Render(p_data, inNumberFrames, i_host_time, ioData->mBuffers[0].mData,
512               ioData->mBuffers[0].mDataByteSize);
513
514     return noErr;
515 }
516
517 static AudioChannelLayout *
518 GetLayoutDescription(audio_output_t *p_aout,
519                      const AudioChannelLayout *outlayout)
520 {
521     AudioFormatPropertyID id;
522     UInt32 size;
523     const void *data;
524     /* We need to "fill out" the ChannelLayout, because there are multiple
525      * ways that it can be set */
526     if (outlayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
527     {
528         id = kAudioFormatProperty_ChannelLayoutForBitmap;
529         size = sizeof(UInt32);
530         data = &outlayout->mChannelBitmap;
531     }
532     else
533     {
534         id = kAudioFormatProperty_ChannelLayoutForTag;
535         size = sizeof(AudioChannelLayoutTag);
536         data = &outlayout->mChannelLayoutTag;
537     }
538
539     UInt32 param_size;
540     OSStatus err = AudioFormatGetPropertyInfo(id, size, data, &param_size);
541     if (err != noErr)
542         return NULL;
543
544     AudioChannelLayout *reslayout = malloc(param_size);
545     if (reslayout == NULL)
546         return NULL;
547
548     err = AudioFormatGetProperty(id, size, data, &param_size, reslayout);
549     if (err != noErr || reslayout->mNumberChannelDescriptions == 0)
550     {
551         msg_Err(p_aout, "insufficient number of output channels");
552         free(reslayout);
553         return NULL;
554     }
555
556     return reslayout;
557 }
558
559 static int
560 MapOutputLayout(audio_output_t *p_aout, audio_sample_format_t *fmt,
561                 const AudioChannelLayout *outlayout, bool *warn_configuration)
562 {
563     /* Fill VLC physical_channels from output layout */
564     fmt->i_physical_channels = 0;
565     uint32_t i_original = fmt->i_physical_channels;
566     AudioChannelLayout *reslayout = NULL;
567
568     if (outlayout == NULL)
569     {
570         msg_Dbg(p_aout, "not output layout, default to Stereo");
571         fmt->i_physical_channels = AOUT_CHANS_STEREO;
572         goto end;
573     }
574
575     if (outlayout->mChannelLayoutTag !=
576         kAudioChannelLayoutTag_UseChannelDescriptions)
577     {
578         reslayout = GetLayoutDescription(p_aout, outlayout);
579         if (reslayout == NULL)
580             return VLC_EGENERIC;
581         outlayout = reslayout;
582     }
583
584     if (i_original == AOUT_CHAN_CENTER
585      || outlayout->mNumberChannelDescriptions < 2)
586     {
587         /* We only need Mono or cannot output more than 1 channel */
588         fmt->i_physical_channels = AOUT_CHAN_CENTER;
589         msg_Dbg(p_aout, "output layout of AUHAL has 1 channel");
590     }
591     else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
592           || outlayout->mNumberChannelDescriptions < 3)
593     {
594         /* We only need Stereo or cannot output more than 2 channels */
595         fmt->i_physical_channels = AOUT_CHANS_STEREO;
596         msg_Dbg(p_aout, "output layout of AUHAL is Stereo");
597     }
598     else
599     {
600         assert(outlayout->mNumberChannelDescriptions > 0);
601
602         msg_Dbg(p_aout, "output layout of AUHAL has %i channels",
603                 outlayout->mNumberChannelDescriptions);
604
605         /* maps auhal channels to vlc ones */
606         static const unsigned i_auhal_channel_mapping[] = {
607             [kAudioChannelLabel_Left]           = AOUT_CHAN_LEFT,
608             [kAudioChannelLabel_Right]          = AOUT_CHAN_RIGHT,
609             [kAudioChannelLabel_Center]         = AOUT_CHAN_CENTER,
610             [kAudioChannelLabel_LFEScreen]      = AOUT_CHAN_LFE,
611             [kAudioChannelLabel_LeftSurround]   = AOUT_CHAN_REARLEFT,
612             [kAudioChannelLabel_RightSurround]  = AOUT_CHAN_REARRIGHT,
613             /* needs to be swapped with rear */
614             [kAudioChannelLabel_RearSurroundLeft]  = AOUT_CHAN_MIDDLELEFT,
615             /* needs to be swapped with rear */
616             [kAudioChannelLabel_RearSurroundRight] = AOUT_CHAN_MIDDLERIGHT,
617             [kAudioChannelLabel_CenterSurround] = AOUT_CHAN_REARCENTER
618         };
619         static const size_t i_auhal_size = sizeof(i_auhal_channel_mapping)
620                                          / sizeof(i_auhal_channel_mapping[0]);
621
622         /* We want more than stereo and we can do that */
623         for (unsigned i = 0; i < outlayout->mNumberChannelDescriptions; i++)
624         {
625             AudioChannelLabel chan =
626                 outlayout->mChannelDescriptions[i].mChannelLabel;
627 #ifndef NDEBUG
628             msg_Dbg(p_aout, "this is channel: %d", (int) chan);
629 #endif
630             if (chan < i_auhal_size && i_auhal_channel_mapping[chan] > 0)
631                 fmt->i_physical_channels |= i_auhal_channel_mapping[chan];
632             else
633                 msg_Dbg(p_aout, "found nonrecognized channel %d at index "
634                         "%d", chan, i);
635         }
636         if (fmt->i_physical_channels == 0)
637         {
638             fmt->i_physical_channels = AOUT_CHANS_STEREO;
639             if (warn_configuration)
640                 *warn_configuration = true;
641         }
642
643     }
644
645 end:
646     free(reslayout);
647     aout_FormatPrepare(fmt);
648
649     msg_Dbg(p_aout, "selected %d physical channels for device output",
650             aout_FormatNbChannels(fmt));
651     msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
652
653     return VLC_SUCCESS;
654 }
655
656 static int
657 SetupInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt,
658                  AudioChannelLayoutTag *inlayout_tag)
659 {
660     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
661     uint32_t chans_out[AOUT_CHAN_MAX];
662
663     /* Some channel abbreviations used below:
664      * L - left
665      * R - right
666      * C - center
667      * Ls - left surround
668      * Rs - right surround
669      * Cs - center surround
670      * Rls - rear left surround
671      * Rrs - rear right surround
672      * Lw - left wide
673      * Rw - right wide
674      * Lsd - left surround direct
675      * Rsd - right surround direct
676      * Lc - left center
677      * Rc - right center
678      * Ts - top surround
679      * Vhl - vertical height left
680      * Vhc - vertical height center
681      * Vhr - vertical height right
682      * Lt - left matrix total. for matrix encoded stereo.
683      * Rt - right matrix total. for matrix encoded stereo. */
684
685     switch (aout_FormatNbChannels(fmt))
686     {
687         case 1:
688             *inlayout_tag = kAudioChannelLayoutTag_Mono;
689             break;
690         case 2:
691             *inlayout_tag = kAudioChannelLayoutTag_Stereo;
692             break;
693         case 3:
694             if (fmt->i_physical_channels & AOUT_CHAN_CENTER) /* L R C */
695                 *inlayout_tag = kAudioChannelLayoutTag_DVD_7;
696             else if (fmt->i_physical_channels & AOUT_CHAN_LFE) /* L R LFE */
697                 *inlayout_tag = kAudioChannelLayoutTag_DVD_4;
698             break;
699         case 4:
700             if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE)) /* L R C LFE */
701                 *inlayout_tag = kAudioChannelLayoutTag_DVD_10;
702             else if (fmt->i_physical_channels & AOUT_CHANS_REAR) /* L R Ls Rs */
703                 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
704             else if (fmt->i_physical_channels & AOUT_CHANS_CENTER) /* L R C Cs */
705                 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
706             break;
707         case 5:
708             if (fmt->i_physical_channels & (AOUT_CHAN_CENTER)) /* L R Ls Rs C */
709                 *inlayout_tag = kAudioChannelLayoutTag_DVD_19;
710             else if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) /* L R Ls Rs LFE */
711                 *inlayout_tag = kAudioChannelLayoutTag_DVD_18;
712             break;
713         case 6:
714             if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
715             {
716                 /* L R Ls Rs C LFE */
717                 *inlayout_tag = kAudioChannelLayoutTag_DVD_20;
718
719                 chans_out[0] = AOUT_CHAN_LEFT;
720                 chans_out[1] = AOUT_CHAN_RIGHT;
721                 chans_out[2] = AOUT_CHAN_REARLEFT;
722                 chans_out[3] = AOUT_CHAN_REARRIGHT;
723                 chans_out[4] = AOUT_CHAN_CENTER;
724                 chans_out[5] = AOUT_CHAN_LFE;
725
726                 p_sys->chans_to_reorder =
727                     aout_CheckChannelReorder(NULL, chans_out,
728                                              fmt->i_physical_channels,
729                                              p_sys->chan_table);
730                 if (p_sys->chans_to_reorder)
731                     msg_Dbg(p_aout, "channel reordering needed for 5.1 output");
732             }
733             else
734             {
735                 /* L R Ls Rs C Cs */
736                 *inlayout_tag = kAudioChannelLayoutTag_AudioUnit_6_0;
737
738                 chans_out[0] = AOUT_CHAN_LEFT;
739                 chans_out[1] = AOUT_CHAN_RIGHT;
740                 chans_out[2] = AOUT_CHAN_REARLEFT;
741                 chans_out[3] = AOUT_CHAN_REARRIGHT;
742                 chans_out[4] = AOUT_CHAN_CENTER;
743                 chans_out[5] = AOUT_CHAN_REARCENTER;
744
745                 p_sys->chans_to_reorder =
746                     aout_CheckChannelReorder(NULL, chans_out,
747                                              fmt->i_physical_channels,
748                                              p_sys->chan_table);
749                 if (p_sys->chans_to_reorder)
750                     msg_Dbg(p_aout, "channel reordering needed for 6.0 output");
751             }
752             break;
753         case 7:
754             /* L R C LFE Ls Rs Cs */
755             *inlayout_tag = kAudioChannelLayoutTag_MPEG_6_1_A;
756
757             chans_out[0] = AOUT_CHAN_LEFT;
758             chans_out[1] = AOUT_CHAN_RIGHT;
759             chans_out[2] = AOUT_CHAN_CENTER;
760             chans_out[3] = AOUT_CHAN_LFE;
761             chans_out[4] = AOUT_CHAN_REARLEFT;
762             chans_out[5] = AOUT_CHAN_REARRIGHT;
763             chans_out[6] = AOUT_CHAN_REARCENTER;
764
765             p_sys->chans_to_reorder =
766                 aout_CheckChannelReorder(NULL, chans_out,
767                                          fmt->i_physical_channels,
768                                          p_sys->chan_table);
769             if (p_sys->chans_to_reorder)
770                 msg_Dbg(p_aout, "channel reordering needed for 6.1 output");
771
772             break;
773         case 8:
774             if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
775             {
776                 /* L R C LFE Ls Rs Rls Rrs */
777                 *inlayout_tag = kAudioChannelLayoutTag_MPEG_7_1_C;
778
779                 chans_out[0] = AOUT_CHAN_LEFT;
780                 chans_out[1] = AOUT_CHAN_RIGHT;
781                 chans_out[2] = AOUT_CHAN_CENTER;
782                 chans_out[3] = AOUT_CHAN_LFE;
783                 chans_out[4] = AOUT_CHAN_MIDDLELEFT;
784                 chans_out[5] = AOUT_CHAN_MIDDLERIGHT;
785                 chans_out[6] = AOUT_CHAN_REARLEFT;
786                 chans_out[7] = AOUT_CHAN_REARRIGHT;
787             }
788             else
789             {
790                 /* Lc C Rc L R Ls Cs Rs */
791                 *inlayout_tag = kAudioChannelLayoutTag_DTS_8_0_B;
792
793                 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
794                 chans_out[1] = AOUT_CHAN_CENTER;
795                 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
796                 chans_out[3] = AOUT_CHAN_LEFT;
797                 chans_out[4] = AOUT_CHAN_RIGHT;
798                 chans_out[5] = AOUT_CHAN_REARLEFT;
799                 chans_out[6] = AOUT_CHAN_REARCENTER;
800                 chans_out[7] = AOUT_CHAN_REARRIGHT;
801             }
802             p_sys->chans_to_reorder =
803                 aout_CheckChannelReorder(NULL, chans_out,
804                                          fmt->i_physical_channels,
805                                          p_sys->chan_table);
806             if (p_sys->chans_to_reorder)
807                 msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output");
808             break;
809         case 9:
810             /* Lc C Rc L R Ls Cs Rs LFE */
811             *inlayout_tag = kAudioChannelLayoutTag_DTS_8_1_B;
812             chans_out[0] = AOUT_CHAN_MIDDLELEFT;
813             chans_out[1] = AOUT_CHAN_CENTER;
814             chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
815             chans_out[3] = AOUT_CHAN_LEFT;
816             chans_out[4] = AOUT_CHAN_RIGHT;
817             chans_out[5] = AOUT_CHAN_REARLEFT;
818             chans_out[6] = AOUT_CHAN_REARCENTER;
819             chans_out[7] = AOUT_CHAN_REARRIGHT;
820             chans_out[8] = AOUT_CHAN_LFE;
821
822             p_sys->chans_to_reorder =
823                 aout_CheckChannelReorder(NULL, chans_out,
824                                          fmt->i_physical_channels,
825                                          p_sys->chan_table);
826             if (p_sys->chans_to_reorder)
827                 msg_Dbg(p_aout, "channel reordering needed for 8.1 output");
828             break;
829     }
830
831     return VLC_SUCCESS;
832 }
833
834 int
835 au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
836               const AudioChannelLayout *outlayout, vlc_tick_t i_dev_latency_us,
837               bool *warn_configuration)
838 {
839     int ret;
840     AudioChannelLayoutTag inlayout_tag;
841
842     if (warn_configuration)
843         *warn_configuration = false;
844
845     /* Set the desired format */
846     AudioStreamBasicDescription desc;
847     if (aout_BitsPerSample(fmt->i_format) != 0)
848     {
849         /* PCM */
850         fmt->i_format = VLC_CODEC_FL32;
851         ret = MapOutputLayout(p_aout, fmt, outlayout, warn_configuration);
852         if (ret != VLC_SUCCESS)
853             return ret;
854
855         ret = SetupInputLayout(p_aout, fmt, &inlayout_tag);
856         if (ret != VLC_SUCCESS)
857             return ret;
858
859         desc.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
860         desc.mChannelsPerFrame = aout_FormatNbChannels(fmt);
861         desc.mBitsPerChannel = 32;
862     }
863     else if (AOUT_FMT_SPDIF(fmt))
864     {
865         /* Passthrough */
866         fmt->i_format = VLC_CODEC_SPDIFL;
867         fmt->i_bytes_per_frame = 4;
868         fmt->i_frame_length = 1;
869
870         inlayout_tag = kAudioChannelLayoutTag_Stereo;
871
872         desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
873                             kLinearPCMFormatFlagIsPacked; /* S16LE */
874         desc.mChannelsPerFrame = 2;
875         desc.mBitsPerChannel = 16;
876     }
877     else
878         return VLC_EGENERIC;
879
880     desc.mSampleRate = fmt->i_rate;
881     desc.mFormatID = kAudioFormatLinearPCM;
882     desc.mFramesPerPacket = 1;
883     desc.mBytesPerFrame = desc.mBitsPerChannel * desc.mChannelsPerFrame / 8;
884     desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket;
885
886     OSStatus err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat,
887                                         kAudioUnitScope_Input, 0, &desc,
888                                         sizeof(desc));
889     if (err != noErr)
890     {
891         ca_LogErr("failed to set stream format");
892         return VLC_EGENERIC;
893     }
894     msg_Dbg(p_aout, STREAM_FORMAT_MSG("Current AU format: " , desc));
895
896     /* Retrieve actual format */
897     err = AudioUnitGetProperty(au, kAudioUnitProperty_StreamFormat,
898                                kAudioUnitScope_Input, 0, &desc,
899                                &(UInt32) { sizeof(desc) });
900     if (err != noErr)
901     {
902         ca_LogErr("failed to set stream format");
903         return VLC_EGENERIC;
904     }
905
906     /* Set the IOproc callback */
907     const AURenderCallbackStruct callback = {
908         .inputProc = RenderCallback,
909         .inputProcRefCon = p_aout,
910     };
911
912     err = AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback,
913                                kAudioUnitScope_Input, 0, &callback,
914                                sizeof(callback));
915     if (err != noErr)
916     {
917         ca_LogErr("failed to setup render callback");
918         return VLC_EGENERIC;
919     }
920
921     /* Set the input_layout as the layout VLC will use to feed the AU unit.
922      * Yes, it must be the INPUT scope */
923     AudioChannelLayout inlayout = {
924         .mChannelLayoutTag = inlayout_tag,
925     };
926     err = AudioUnitSetProperty(au, kAudioUnitProperty_AudioChannelLayout,
927                                kAudioUnitScope_Input, 0, &inlayout,
928                                sizeof(inlayout));
929     if (err != noErr)
930     {
931         ca_LogErr("failed to setup input layout");
932         return VLC_EGENERIC;
933     }
934
935     /* AU init */
936     err = AudioUnitInitialize(au);
937
938     if (err != noErr)
939     {
940         ca_LogErr("AudioUnitInitialize failed");
941         return VLC_EGENERIC;
942     }
943
944     ret = ca_Initialize(p_aout, fmt, i_dev_latency_us);
945     if (ret != VLC_SUCCESS)
946     {
947         AudioUnitUninitialize(au);
948         return VLC_EGENERIC;
949     }
950
951     return VLC_SUCCESS;
952 }
953
954 void
955 au_Uninitialize(audio_output_t *p_aout, AudioUnit au)
956 {
957     OSStatus err = AudioUnitUninitialize(au);
958     if (err != noErr)
959         ca_LogWarn("AudioUnitUninitialize failed");
960
961     ca_Uninitialize(p_aout);
962 }