input: add an input_item_t arg to input_CreateFilename()
[vlc.git] / modules / audio_output / coreaudio_common.c
bloba5598c62a794be0895221d509630532f68a98d2a
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>
28 #import <dlfcn.h>
29 #import <mach/mach_time.h>
31 static struct
33 void (*lock)(os_unfair_lock *lock);
34 void (*unlock)(os_unfair_lock *lock);
35 } unfair_lock;
37 static mach_timebase_info_data_t tinfo;
39 static inline uint64_t
40 BytesToFrames(struct aout_sys_common *p_sys, size_t i_bytes)
42 return i_bytes * p_sys->i_frame_length / p_sys->i_bytes_per_frame;
45 static inline vlc_tick_t
46 FramesToUs(struct aout_sys_common *p_sys, uint64_t i_nb_frames)
48 return i_nb_frames * CLOCK_FREQ / p_sys->i_rate;
51 static void
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;
63 static void
64 ca_init_once(void)
66 unfair_lock.lock = dlsym(RTLD_DEFAULT, "os_unfair_lock_lock");
67 if (!unfair_lock.lock)
68 return;
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;
77 static void
78 lock_init(struct aout_sys_common *p_sys)
80 if (unfair_lock.lock)
81 p_sys->lock.unfair = OS_UNFAIR_LOCK_INIT;
82 else
83 vlc_mutex_init(&p_sys->lock.mutex);
86 static void
87 lock_destroy(struct aout_sys_common *p_sys)
89 if (!unfair_lock.lock)
90 vlc_mutex_destroy(&p_sys->lock.mutex);
93 static void
94 lock_lock(struct aout_sys_common *p_sys)
96 if (unfair_lock.lock)
97 unfair_lock.lock(&p_sys->lock.unfair);
98 else
99 vlc_mutex_lock(&p_sys->lock.mutex);
102 static void
103 lock_unlock(struct aout_sys_common *p_sys)
105 if (unfair_lock.lock)
106 unfair_lock.unlock(&p_sys->lock.unfair);
107 else
108 vlc_mutex_unlock(&p_sys->lock.mutex);
111 void
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);
120 lock_init(p_sys);
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;
129 void
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);
135 lock_destroy(p_sys);
138 /* Called from render callbacks. No lock, wait, and IO here */
139 void
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;
145 lock_lock(p_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);
158 if (p_sys->b_paused)
159 goto drop;
161 size_t i_copied = 0;
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);
175 else
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;
188 /* Pad with 0 */
189 if (i_requested > 0)
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);
196 lock_unlock(p_sys);
197 return;
199 drop:
200 memset(p_output, 0, i_requested);
201 lock_unlock(p_sys);
205 ca_TimeGet(audio_output_t *p_aout, vlc_tick_t *delay)
207 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
209 if (unlikely(tinfo.denom == 0))
210 return -1;
212 lock_lock(p_sys);
214 vlc_tick_t i_render_delay;
215 if (likely(p_sys->i_render_host_time != 0))
217 const uint64_t i_render_time_us = p_sys->i_render_host_time
218 * tinfo.numer / tinfo.denom / 1000;
219 i_render_delay = i_render_time_us - vlc_tick_now();
221 else
222 i_render_delay = 0;
224 const int64_t i_out_frames = BytesToFrames(p_sys, p_sys->i_out_size);
225 *delay = FramesToUs(p_sys, i_out_frames + p_sys->i_render_frames)
226 + p_sys->i_dev_latency_us + i_render_delay;
228 lock_unlock(p_sys);
229 return 0;
232 void
233 ca_Flush(audio_output_t *p_aout, bool wait)
235 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
237 lock_lock(p_sys);
238 if (wait)
240 while (p_sys->i_out_size > 0)
242 if (p_sys->b_paused)
244 ca_ClearOutBuffers(p_aout);
245 break;
248 /* Calculate the duration of the circular buffer, in order to wait
249 * for the render thread to play it all */
250 const vlc_tick_t i_frame_us =
251 FramesToUs(p_sys, BytesToFrames(p_sys, p_sys->i_out_size)) + VLC_TICK_FROM_MS(10);
252 lock_unlock(p_sys);
253 vlc_tick_sleep(i_frame_us);
254 lock_lock(p_sys);
257 else
259 assert(!p_sys->b_do_flush);
260 if (p_sys->b_paused)
261 ca_ClearOutBuffers(p_aout);
262 else
264 p_sys->b_do_flush = true;
265 lock_unlock(p_sys);
266 vlc_sem_wait(&p_sys->flush_sem);
267 lock_lock(p_sys);
271 p_sys->i_render_host_time = 0;
272 p_sys->i_render_frames = 0;
273 lock_unlock(p_sys);
276 void
277 ca_Pause(audio_output_t * p_aout, bool pause, vlc_tick_t date)
279 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
280 VLC_UNUSED(date);
282 lock_lock(p_sys);
283 p_sys->b_paused = pause;
284 lock_unlock(p_sys);
287 void
288 ca_Play(audio_output_t * p_aout, block_t * p_block, vlc_tick_t date)
290 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
292 /* Do the channel reordering */
293 if (p_sys->chans_to_reorder)
294 aout_ChannelReorder(p_block->p_buffer, p_block->i_buffer,
295 p_sys->chans_to_reorder, p_sys->chan_table,
296 VLC_CODEC_FL32);
298 lock_lock(p_sys);
301 const size_t i_avalaible_bytes =
302 __MIN(p_block->i_buffer, p_sys->i_out_max_size - p_sys->i_out_size);
304 if (unlikely(i_avalaible_bytes != p_block->i_buffer))
306 /* Not optimal but unlikely code path. */
308 lock_unlock(p_sys);
310 block_t *p_new = block_Alloc(i_avalaible_bytes);
311 if (!p_new)
313 block_Release(p_block);
314 return;
317 memcpy(p_new->p_buffer, p_block->p_buffer, i_avalaible_bytes);
319 p_block->p_buffer += i_avalaible_bytes;
320 p_block->i_buffer -= i_avalaible_bytes;
322 lock_lock(p_sys);
324 block_ChainLastAppend(&p_sys->pp_out_last, p_new);
325 p_sys->i_out_size += i_avalaible_bytes;
327 if (p_sys->b_paused)
329 lock_unlock(p_sys);
330 block_Release(p_block);
331 return;
334 const vlc_tick_t i_frame_us =
335 FramesToUs(p_sys, BytesToFrames(p_sys, p_block->i_buffer));
337 /* Wait for the render buffer to play the remaining data */
338 lock_unlock(p_sys);
339 vlc_tick_sleep(i_frame_us);
340 lock_lock(p_sys);
342 else
344 block_ChainLastAppend(&p_sys->pp_out_last, p_block);
345 p_sys->i_out_size += i_avalaible_bytes;
346 p_block = NULL;
348 } while (p_block != NULL);
350 size_t i_underrun_size = p_sys->i_underrun_size;
351 p_sys->i_underrun_size = 0;
353 lock_unlock(p_sys);
355 if (i_underrun_size > 0)
356 msg_Warn(p_aout, "underrun of %zu bytes", i_underrun_size);
358 (void) date;
362 ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
363 vlc_tick_t i_dev_latency_us)
365 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
367 p_sys->i_underrun_size = 0;
368 p_sys->b_paused = false;
369 p_sys->i_render_host_time = 0;
370 p_sys->i_render_frames = 0;
372 p_sys->i_rate = fmt->i_rate;
373 p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame;
374 p_sys->i_frame_length = fmt->i_frame_length;
375 p_sys->chans_to_reorder = 0;
377 msg_Dbg(p_aout, "Current device has a latency of %lld us",
378 i_dev_latency_us);
380 /* TODO VLC can't handle latency higher than 1 seconds */
381 if (i_dev_latency_us > 1000000)
383 i_dev_latency_us = 1000000;
384 msg_Warn(p_aout, "VLC can't handle this device latency, lowering it to "
385 "%lld", i_dev_latency_us);
387 p_sys->i_dev_latency_us = i_dev_latency_us;
389 /* setup circular buffer */
390 size_t i_audiobuffer_size = fmt->i_rate * fmt->i_bytes_per_frame
391 / p_sys->i_frame_length;
392 if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
394 /* lower latency: 200 ms of buffering. XXX: Decrease when VLC's core
395 * can handle lower audio latency */
396 p_sys->i_out_max_size = i_audiobuffer_size / 5;
398 else
400 /* 2 seconds of buffering */
401 p_sys->i_out_max_size = i_audiobuffer_size * 2;
404 ca_ClearOutBuffers(p_aout);
406 return VLC_SUCCESS;
409 void
410 ca_Uninitialize(audio_output_t *p_aout)
412 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
413 ca_ClearOutBuffers(p_aout);
414 p_sys->i_out_max_size = 0;
417 void
418 ca_SetAliveState(audio_output_t *p_aout, bool alive)
420 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
422 lock_lock(p_sys);
424 bool b_sem_post = false;
425 p_sys->b_paused = !alive;
426 if (!alive && p_sys->b_do_flush)
428 ca_ClearOutBuffers(p_aout);
429 p_sys->b_do_flush = false;
430 b_sem_post = true;
433 lock_unlock(p_sys);
435 if (b_sem_post)
436 vlc_sem_post(&p_sys->flush_sem);
439 AudioUnit
440 au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type)
442 AudioComponentDescription desc = {
443 .componentType = kAudioUnitType_Output,
444 .componentSubType = comp_sub_type,
445 .componentManufacturer = kAudioUnitManufacturer_Apple,
446 .componentFlags = 0,
447 .componentFlagsMask = 0,
450 AudioComponent au_component;
451 au_component = AudioComponentFindNext(NULL, &desc);
452 if (au_component == NULL)
454 msg_Err(p_aout, "cannot find any AudioComponent, PCM output failed");
455 return NULL;
458 AudioUnit au;
459 OSStatus err = AudioComponentInstanceNew(au_component, &au);
460 if (err != noErr)
462 ca_LogErr("cannot open AudioComponent, PCM output failed");
463 return NULL;
465 return au;
468 /*****************************************************************************
469 * RenderCallback: This function is called everytime the AudioUnit wants
470 * us to provide some more audio data.
471 * Don't print anything during normal playback, calling blocking function from
472 * this callback is not allowed.
473 *****************************************************************************/
474 static OSStatus
475 RenderCallback(void *p_data, AudioUnitRenderActionFlags *ioActionFlags,
476 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
477 UInt32 inNumberFrames, AudioBufferList *ioData)
479 VLC_UNUSED(ioActionFlags);
480 VLC_UNUSED(inTimeStamp);
481 VLC_UNUSED(inBusNumber);
483 uint64_t i_host_time = (inTimeStamp->mFlags & kAudioTimeStampHostTimeValid)
484 ? inTimeStamp->mHostTime : 0;
486 ca_Render(p_data, inNumberFrames, i_host_time, ioData->mBuffers[0].mData,
487 ioData->mBuffers[0].mDataByteSize);
489 return noErr;
492 static AudioChannelLayout *
493 GetLayoutDescription(audio_output_t *p_aout,
494 const AudioChannelLayout *outlayout)
496 AudioFormatPropertyID id;
497 UInt32 size;
498 const void *data;
499 /* We need to "fill out" the ChannelLayout, because there are multiple
500 * ways that it can be set */
501 if (outlayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
503 id = kAudioFormatProperty_ChannelLayoutForBitmap;
504 size = sizeof(UInt32);
505 data = &outlayout->mChannelBitmap;
507 else
509 id = kAudioFormatProperty_ChannelLayoutForTag;
510 size = sizeof(AudioChannelLayoutTag);
511 data = &outlayout->mChannelLayoutTag;
514 UInt32 param_size;
515 OSStatus err = AudioFormatGetPropertyInfo(id, size, data, &param_size);
516 if (err != noErr)
517 return NULL;
519 AudioChannelLayout *reslayout = malloc(param_size);
520 if (reslayout == NULL)
521 return NULL;
523 err = AudioFormatGetProperty(id, size, data, &param_size, reslayout);
524 if (err != noErr || reslayout->mNumberChannelDescriptions == 0)
526 msg_Err(p_aout, "insufficient number of output channels");
527 free(reslayout);
528 return NULL;
531 return reslayout;
534 static int
535 MapOutputLayout(audio_output_t *p_aout, audio_sample_format_t *fmt,
536 const AudioChannelLayout *outlayout, bool *warn_configuration)
538 /* Fill VLC physical_channels from output layout */
539 fmt->i_physical_channels = 0;
540 uint32_t i_original = fmt->i_physical_channels;
541 AudioChannelLayout *reslayout = NULL;
543 if (outlayout == NULL)
545 msg_Dbg(p_aout, "not output layout, default to Stereo");
546 fmt->i_physical_channels = AOUT_CHANS_STEREO;
547 goto end;
550 if (outlayout->mChannelLayoutTag !=
551 kAudioChannelLayoutTag_UseChannelDescriptions)
553 reslayout = GetLayoutDescription(p_aout, outlayout);
554 if (reslayout == NULL)
555 return VLC_EGENERIC;
556 outlayout = reslayout;
559 if (i_original == AOUT_CHAN_CENTER
560 || outlayout->mNumberChannelDescriptions < 2)
562 /* We only need Mono or cannot output more than 1 channel */
563 fmt->i_physical_channels = AOUT_CHAN_CENTER;
564 msg_Dbg(p_aout, "output layout of AUHAL has 1 channel");
566 else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
567 || outlayout->mNumberChannelDescriptions < 3)
569 /* We only need Stereo or cannot output more than 2 channels */
570 fmt->i_physical_channels = AOUT_CHANS_STEREO;
571 msg_Dbg(p_aout, "output layout of AUHAL is Stereo");
573 else
575 assert(outlayout->mNumberChannelDescriptions > 0);
577 msg_Dbg(p_aout, "output layout of AUHAL has %i channels",
578 outlayout->mNumberChannelDescriptions);
580 /* maps auhal channels to vlc ones */
581 static const unsigned i_auhal_channel_mapping[] = {
582 [kAudioChannelLabel_Left] = AOUT_CHAN_LEFT,
583 [kAudioChannelLabel_Right] = AOUT_CHAN_RIGHT,
584 [kAudioChannelLabel_Center] = AOUT_CHAN_CENTER,
585 [kAudioChannelLabel_LFEScreen] = AOUT_CHAN_LFE,
586 [kAudioChannelLabel_LeftSurround] = AOUT_CHAN_REARLEFT,
587 [kAudioChannelLabel_RightSurround] = AOUT_CHAN_REARRIGHT,
588 /* needs to be swapped with rear */
589 [kAudioChannelLabel_RearSurroundLeft] = AOUT_CHAN_MIDDLELEFT,
590 /* needs to be swapped with rear */
591 [kAudioChannelLabel_RearSurroundRight] = AOUT_CHAN_MIDDLERIGHT,
592 [kAudioChannelLabel_CenterSurround] = AOUT_CHAN_REARCENTER
594 static const size_t i_auhal_size = sizeof(i_auhal_channel_mapping)
595 / sizeof(i_auhal_channel_mapping[0]);
597 /* We want more than stereo and we can do that */
598 for (unsigned i = 0; i < outlayout->mNumberChannelDescriptions; i++)
600 AudioChannelLabel chan =
601 outlayout->mChannelDescriptions[i].mChannelLabel;
602 #ifndef NDEBUG
603 msg_Dbg(p_aout, "this is channel: %d", (int) chan);
604 #endif
605 if (chan < i_auhal_size && i_auhal_channel_mapping[chan] > 0)
606 fmt->i_physical_channels |= i_auhal_channel_mapping[chan];
607 else
608 msg_Dbg(p_aout, "found nonrecognized channel %d at index "
609 "%d", chan, i);
611 if (fmt->i_physical_channels == 0)
613 fmt->i_physical_channels = AOUT_CHANS_STEREO;
614 if (warn_configuration)
615 *warn_configuration = true;
620 end:
621 free(reslayout);
622 aout_FormatPrepare(fmt);
624 msg_Dbg(p_aout, "selected %d physical channels for device output",
625 aout_FormatNbChannels(fmt));
626 msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
628 return VLC_SUCCESS;
631 static int
632 SetupInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt,
633 AudioChannelLayoutTag *inlayout_tag)
635 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
636 uint32_t chans_out[AOUT_CHAN_MAX];
638 /* Some channel abbreviations used below:
639 * L - left
640 * R - right
641 * C - center
642 * Ls - left surround
643 * Rs - right surround
644 * Cs - center surround
645 * Rls - rear left surround
646 * Rrs - rear right surround
647 * Lw - left wide
648 * Rw - right wide
649 * Lsd - left surround direct
650 * Rsd - right surround direct
651 * Lc - left center
652 * Rc - right center
653 * Ts - top surround
654 * Vhl - vertical height left
655 * Vhc - vertical height center
656 * Vhr - vertical height right
657 * Lt - left matrix total. for matrix encoded stereo.
658 * Rt - right matrix total. for matrix encoded stereo. */
660 switch (aout_FormatNbChannels(fmt))
662 case 1:
663 *inlayout_tag = kAudioChannelLayoutTag_Mono;
664 break;
665 case 2:
666 *inlayout_tag = kAudioChannelLayoutTag_Stereo;
667 break;
668 case 3:
669 if (fmt->i_physical_channels & AOUT_CHAN_CENTER) /* L R C */
670 *inlayout_tag = kAudioChannelLayoutTag_DVD_7;
671 else if (fmt->i_physical_channels & AOUT_CHAN_LFE) /* L R LFE */
672 *inlayout_tag = kAudioChannelLayoutTag_DVD_4;
673 break;
674 case 4:
675 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE)) /* L R C LFE */
676 *inlayout_tag = kAudioChannelLayoutTag_DVD_10;
677 else if (fmt->i_physical_channels & AOUT_CHANS_REAR) /* L R Ls Rs */
678 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
679 else if (fmt->i_physical_channels & AOUT_CHANS_CENTER) /* L R C Cs */
680 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
681 break;
682 case 5:
683 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER)) /* L R Ls Rs C */
684 *inlayout_tag = kAudioChannelLayoutTag_DVD_19;
685 else if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) /* L R Ls Rs LFE */
686 *inlayout_tag = kAudioChannelLayoutTag_DVD_18;
687 break;
688 case 6:
689 if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
691 /* L R Ls Rs C LFE */
692 *inlayout_tag = kAudioChannelLayoutTag_DVD_20;
694 chans_out[0] = AOUT_CHAN_LEFT;
695 chans_out[1] = AOUT_CHAN_RIGHT;
696 chans_out[2] = AOUT_CHAN_REARLEFT;
697 chans_out[3] = AOUT_CHAN_REARRIGHT;
698 chans_out[4] = AOUT_CHAN_CENTER;
699 chans_out[5] = AOUT_CHAN_LFE;
701 p_sys->chans_to_reorder =
702 aout_CheckChannelReorder(NULL, chans_out,
703 fmt->i_physical_channels,
704 p_sys->chan_table);
705 if (p_sys->chans_to_reorder)
706 msg_Dbg(p_aout, "channel reordering needed for 5.1 output");
708 else
710 /* L R Ls Rs C Cs */
711 *inlayout_tag = kAudioChannelLayoutTag_AudioUnit_6_0;
713 chans_out[0] = AOUT_CHAN_LEFT;
714 chans_out[1] = AOUT_CHAN_RIGHT;
715 chans_out[2] = AOUT_CHAN_REARLEFT;
716 chans_out[3] = AOUT_CHAN_REARRIGHT;
717 chans_out[4] = AOUT_CHAN_CENTER;
718 chans_out[5] = AOUT_CHAN_REARCENTER;
720 p_sys->chans_to_reorder =
721 aout_CheckChannelReorder(NULL, chans_out,
722 fmt->i_physical_channels,
723 p_sys->chan_table);
724 if (p_sys->chans_to_reorder)
725 msg_Dbg(p_aout, "channel reordering needed for 6.0 output");
727 break;
728 case 7:
729 /* L R C LFE Ls Rs Cs */
730 *inlayout_tag = kAudioChannelLayoutTag_MPEG_6_1_A;
732 chans_out[0] = AOUT_CHAN_LEFT;
733 chans_out[1] = AOUT_CHAN_RIGHT;
734 chans_out[2] = AOUT_CHAN_CENTER;
735 chans_out[3] = AOUT_CHAN_LFE;
736 chans_out[4] = AOUT_CHAN_REARLEFT;
737 chans_out[5] = AOUT_CHAN_REARRIGHT;
738 chans_out[6] = AOUT_CHAN_REARCENTER;
740 p_sys->chans_to_reorder =
741 aout_CheckChannelReorder(NULL, chans_out,
742 fmt->i_physical_channels,
743 p_sys->chan_table);
744 if (p_sys->chans_to_reorder)
745 msg_Dbg(p_aout, "channel reordering needed for 6.1 output");
747 break;
748 case 8:
749 if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
751 /* L R C LFE Ls Rs Rls Rrs */
752 *inlayout_tag = kAudioChannelLayoutTag_MPEG_7_1_C;
754 chans_out[0] = AOUT_CHAN_LEFT;
755 chans_out[1] = AOUT_CHAN_RIGHT;
756 chans_out[2] = AOUT_CHAN_CENTER;
757 chans_out[3] = AOUT_CHAN_LFE;
758 chans_out[4] = AOUT_CHAN_MIDDLELEFT;
759 chans_out[5] = AOUT_CHAN_MIDDLERIGHT;
760 chans_out[6] = AOUT_CHAN_REARLEFT;
761 chans_out[7] = AOUT_CHAN_REARRIGHT;
763 else
765 /* Lc C Rc L R Ls Cs Rs */
766 *inlayout_tag = kAudioChannelLayoutTag_DTS_8_0_B;
768 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
769 chans_out[1] = AOUT_CHAN_CENTER;
770 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
771 chans_out[3] = AOUT_CHAN_LEFT;
772 chans_out[4] = AOUT_CHAN_RIGHT;
773 chans_out[5] = AOUT_CHAN_REARLEFT;
774 chans_out[6] = AOUT_CHAN_REARCENTER;
775 chans_out[7] = AOUT_CHAN_REARRIGHT;
777 p_sys->chans_to_reorder =
778 aout_CheckChannelReorder(NULL, chans_out,
779 fmt->i_physical_channels,
780 p_sys->chan_table);
781 if (p_sys->chans_to_reorder)
782 msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output");
783 break;
784 case 9:
785 /* Lc C Rc L R Ls Cs Rs LFE */
786 *inlayout_tag = kAudioChannelLayoutTag_DTS_8_1_B;
787 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
788 chans_out[1] = AOUT_CHAN_CENTER;
789 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
790 chans_out[3] = AOUT_CHAN_LEFT;
791 chans_out[4] = AOUT_CHAN_RIGHT;
792 chans_out[5] = AOUT_CHAN_REARLEFT;
793 chans_out[6] = AOUT_CHAN_REARCENTER;
794 chans_out[7] = AOUT_CHAN_REARRIGHT;
795 chans_out[8] = AOUT_CHAN_LFE;
797 p_sys->chans_to_reorder =
798 aout_CheckChannelReorder(NULL, chans_out,
799 fmt->i_physical_channels,
800 p_sys->chan_table);
801 if (p_sys->chans_to_reorder)
802 msg_Dbg(p_aout, "channel reordering needed for 8.1 output");
803 break;
806 return VLC_SUCCESS;
810 au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
811 const AudioChannelLayout *outlayout, vlc_tick_t i_dev_latency_us,
812 bool *warn_configuration)
814 int ret;
815 AudioChannelLayoutTag inlayout_tag;
817 if (warn_configuration)
818 *warn_configuration = false;
820 /* Set the desired format */
821 AudioStreamBasicDescription desc;
822 if (aout_BitsPerSample(fmt->i_format) != 0)
824 /* PCM */
825 fmt->i_format = VLC_CODEC_FL32;
826 ret = MapOutputLayout(p_aout, fmt, outlayout, warn_configuration);
827 if (ret != VLC_SUCCESS)
828 return ret;
830 ret = SetupInputLayout(p_aout, fmt, &inlayout_tag);
831 if (ret != VLC_SUCCESS)
832 return ret;
834 desc.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
835 desc.mChannelsPerFrame = aout_FormatNbChannels(fmt);
836 desc.mBitsPerChannel = 32;
838 else if (AOUT_FMT_SPDIF(fmt))
840 /* Passthrough */
841 fmt->i_format = VLC_CODEC_SPDIFL;
842 fmt->i_bytes_per_frame = 4;
843 fmt->i_frame_length = 1;
845 inlayout_tag = kAudioChannelLayoutTag_Stereo;
847 desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
848 kLinearPCMFormatFlagIsPacked; /* S16LE */
849 desc.mChannelsPerFrame = 2;
850 desc.mBitsPerChannel = 16;
852 else
853 return VLC_EGENERIC;
855 desc.mSampleRate = fmt->i_rate;
856 desc.mFormatID = kAudioFormatLinearPCM;
857 desc.mFramesPerPacket = 1;
858 desc.mBytesPerFrame = desc.mBitsPerChannel * desc.mChannelsPerFrame / 8;
859 desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket;
861 OSStatus err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat,
862 kAudioUnitScope_Input, 0, &desc,
863 sizeof(desc));
864 if (err != noErr)
866 ca_LogErr("failed to set stream format");
867 return VLC_EGENERIC;
869 msg_Dbg(p_aout, STREAM_FORMAT_MSG("Current AU format: " , desc));
871 /* Retrieve actual format */
872 err = AudioUnitGetProperty(au, kAudioUnitProperty_StreamFormat,
873 kAudioUnitScope_Input, 0, &desc,
874 &(UInt32) { sizeof(desc) });
875 if (err != noErr)
877 ca_LogErr("failed to set stream format");
878 return VLC_EGENERIC;
881 /* Set the IOproc callback */
882 const AURenderCallbackStruct callback = {
883 .inputProc = RenderCallback,
884 .inputProcRefCon = p_aout,
887 err = AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback,
888 kAudioUnitScope_Input, 0, &callback,
889 sizeof(callback));
890 if (err != noErr)
892 ca_LogErr("failed to setup render callback");
893 return VLC_EGENERIC;
896 /* Set the input_layout as the layout VLC will use to feed the AU unit.
897 * Yes, it must be the INPUT scope */
898 AudioChannelLayout inlayout = {
899 .mChannelLayoutTag = inlayout_tag,
901 err = AudioUnitSetProperty(au, kAudioUnitProperty_AudioChannelLayout,
902 kAudioUnitScope_Input, 0, &inlayout,
903 sizeof(inlayout));
904 if (err != noErr)
906 ca_LogErr("failed to setup input layout");
907 return VLC_EGENERIC;
910 /* AU init */
911 err = AudioUnitInitialize(au);
913 if (err != noErr)
915 ca_LogErr("AudioUnitInitialize failed");
916 return VLC_EGENERIC;
919 ret = ca_Initialize(p_aout, fmt, i_dev_latency_us);
920 if (ret != VLC_SUCCESS)
922 AudioUnitUninitialize(au);
923 return VLC_EGENERIC;
926 return VLC_SUCCESS;
929 void
930 au_Uninitialize(audio_output_t *p_aout, AudioUnit au)
932 OSStatus err = AudioUnitUninitialize(au);
933 if (err != noErr)
934 ca_LogWarn("AudioUnitUninitialize failed");
936 ca_Uninitialize(p_aout);