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