vlc | branch: master | Thomas Guillem <[email protected]> | Tue Feb 28 13:02:25 2017 +0100| [854a3caea195dfc9eaa3622fc1171cbf9244c217] | committer: Thomas Guillem
coreaudio: factor AudioUnit initialization Factor StartAnalog for both iOS and macOS into au_Initialize(). > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=854a3caea195dfc9eaa3622fc1171cbf9244c217 --- modules/audio_output/audiounit_ios.m | 86 +----- modules/audio_output/auhal.c | 428 +---------------------------- modules/audio_output/coreaudio_common.c | 469 ++++++++++++++++++++++++++++++++ modules/audio_output/coreaudio_common.h | 8 +- 4 files changed, 493 insertions(+), 498 deletions(-) diff --git a/modules/audio_output/audiounit_ios.m b/modules/audio_output/audiounit_ios.m index 451d3c1..1c757ca 100644 --- a/modules/audio_output/audiounit_ios.m +++ b/modules/audio_output/audiounit_ios.m @@ -165,28 +165,6 @@ Play(audio_output_t * p_aout, block_t * p_block) ca_Play(p_aout, p_block); } -/***************************************************************************** - * RenderCallback: This function is called everytime the AudioUnit wants - * us to provide some more audio data. - * Don't print anything during normal playback, calling blocking function from - * this callback is not allowed. - *****************************************************************************/ -static OSStatus -RenderCallback(void *p_data, AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, - UInt32 inNumberFrames, AudioBufferList *ioData) -{ - VLC_UNUSED(ioActionFlags); - VLC_UNUSED(inTimeStamp); - VLC_UNUSED(inBusNumber); - VLC_UNUSED(inNumberFrames); - - ca_Render(p_data, ioData->mBuffers[0].mData, - ioData->mBuffers[0].mDataByteSize); - - return noErr; -} - #pragma mark initialization /* @@ -196,13 +174,15 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt) { struct aout_sys_t *p_sys = p_aout->sys; - AURenderCallbackStruct callback; OSStatus status; /* Activate the AVAudioSession */ if (avas_SetActive(p_aout, true, 0) != VLC_SUCCESS) return VLC_EGENERIC; + fmt->i_format = VLC_CODEC_FL32; + fmt->i_physical_channels = fmt->i_original_channels = AOUT_CHANS_STEREO; + p_sys->au_unit = au_NewOutputInstance(p_aout, kAudioUnitSubType_RemoteIO); if (p_sys->au_unit == NULL) goto error; @@ -214,64 +194,12 @@ StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt) if (status != noErr) msg_Warn(p_aout, "failed to set IO mode (%i)", (int)status); - /* Get the current format */ - AudioStreamBasicDescription streamDescription; - streamDescription.mSampleRate = fmt->i_rate; - fmt->i_format = VLC_CODEC_FL32; - fmt->i_physical_channels = fmt->i_original_channels = AOUT_CHANS_STEREO; - streamDescription.mFormatID = kAudioFormatLinearPCM; - streamDescription.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; // FL32 - streamDescription.mChannelsPerFrame = aout_FormatNbChannels(fmt); - streamDescription.mFramesPerPacket = 1; - streamDescription.mBitsPerChannel = 32; - streamDescription.mBytesPerFrame = streamDescription.mBitsPerChannel * streamDescription.mChannelsPerFrame / 8; - streamDescription.mBytesPerPacket = streamDescription.mBytesPerFrame * streamDescription.mFramesPerPacket; - - /* Set the desired format */ - status = AudioUnitSetProperty(p_sys->au_unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &streamDescription, - sizeof(streamDescription)); - if (status != noErr) { - msg_Err(p_aout, "failed to set stream format (%i)", (int)status); - goto error; - } - msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , streamDescription)); - - /* Retrieve actual format */ - status = AudioUnitGetProperty(p_sys->au_unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &streamDescription, - & (UInt32) { sizeof(streamDescription) }); - if (status != noErr) - msg_Warn(p_aout, "failed to verify stream format (%i)", (int)status); - msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , streamDescription)); - - /* Do the last VLC aout setups */ - aout_FormatPrepare(fmt); - - /* set the IOproc callback */ - callback.inputProc = RenderCallback; - callback.inputProcRefCon = p_aout; - - status = AudioUnitSetProperty(p_sys->au_unit, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &callback, - sizeof(callback)); - if (status != noErr) { - msg_Err(p_aout, "render callback setup failed (%i)", (int)status); - goto error; - } - - /* AU init */ - status = AudioUnitInitialize(p_sys->au_unit); - if (status != noErr) { - msg_Err(p_aout, "failed to init AudioUnit (%i)", (int)status); + int ret = au_Initialize(p_aout, p_sys->au_unit, fmt, NULL); + if (ret != VLC_SUCCESS) goto error; - } - int ret = ca_Init(p_aout, fmt, AUDIO_BUFFER_SIZE_IN_SECONDS * fmt->i_rate * - fmt->i_bytes_per_frame); + ret = ca_Init(p_aout, fmt, AUDIO_BUFFER_SIZE_IN_SECONDS * fmt->i_rate * + fmt->i_bytes_per_frame); if (ret != VLC_SUCCESS) { AudioUnitUninitialize(p_sys->au_unit); diff --git a/modules/audio_output/auhal.c b/modules/audio_output/auhal.c index 256a94a..eb6607e 100644 --- a/modules/audio_output/auhal.c +++ b/modules/audio_output/auhal.c @@ -910,29 +910,6 @@ MuteSet(audio_output_t * p_aout, bool mute) #pragma mark - #pragma mark actual playback -/***************************************************************************** - * RenderCallbackAnalog: This function is called everytime the AudioUnit wants - * us to provide some more audio data. - * Don't print anything during normal playback, calling blocking function from - * this callback is not allowed. - *****************************************************************************/ -static OSStatus -RenderCallbackAnalog(void *p_data, AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, UInt32 inNumberFrames, - AudioBufferList *ioData) -{ - VLC_UNUSED(ioActionFlags); - VLC_UNUSED(inTimeStamp); - VLC_UNUSED(inBusNumber); - VLC_UNUSED(inNumberFrames); - - ca_Render(p_data, ioData->mBuffers[0].mData, - ioData->mBuffers[0].mDataByteSize); - - return noErr; -} - /* * RenderCallbackSPDIF: callback for SPDIF audio output */ @@ -970,16 +947,8 @@ StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt) { struct aout_sys_t *p_sys = p_aout->sys; OSStatus err = noErr; - UInt32 i_param_size = 0; - int i_original; - AudioStreamBasicDescription DeviceFormat; - AudioChannelLayout *layout; - AURenderCallbackStruct input; - p_sys->c.chans_to_reorder = 0; - - SInt32 currentMinorSystemVersion; - if (Gestalt(gestaltSystemVersionMinor, ¤tMinorSystemVersion) != noErr) - msg_Err(p_aout, "failed to check OSX version"); + UInt32 i_param_size; + AudioChannelLayout *layout = NULL; p_sys->au_unit = au_NewOutputInstance(p_aout, kAudioUnitSubType_HALOutput); if (p_sys->au_unit == NULL) @@ -998,416 +967,39 @@ StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt) goto error; } - /* Get the current format */ - i_param_size = sizeof(AudioStreamBasicDescription); - - err = AudioUnitGetProperty(p_sys->au_unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, 0, &DeviceFormat, - &i_param_size); - - if (err != noErr) - { - msg_Err(p_aout, "failed to detect supported stream formats [%4.4s]", - (const char *)&err); - goto error; - } - else - msg_Dbg(p_aout, STREAM_FORMAT_MSG("current format is: ", DeviceFormat)); - /* Get the channel layout of the device side of the unit (vlc -> unit -> * device) */ err = AudioUnitGetPropertyInfo(p_sys->au_unit, kAudioDevicePropertyPreferredChannelLayout, kAudioUnitScope_Output, 0, &i_param_size, NULL); - if (err == noErr) { layout = (AudioChannelLayout *)malloc(i_param_size); + if (layout == NULL) + goto error; OSStatus err = AudioUnitGetProperty(p_sys->au_unit, kAudioDevicePropertyPreferredChannelLayout, kAudioUnitScope_Output, 0, layout, &i_param_size); - if (err != noErr) goto error; - - /* We need to "fill out" the ChannelLayout, because there are multiple - * ways that it can be set */ - if (layout->mChannelLayoutTag == - kAudioChannelLayoutTag_UseChannelBitmap) - { - /* bitmap defined channellayout */ - err = - AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap, - sizeof(UInt32), &layout->mChannelBitmap, - &i_param_size, layout); - } - else if (layout->mChannelLayoutTag != - kAudioChannelLayoutTag_UseChannelDescriptions) - { - /* layouttags defined channellayout */ - err = - AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag, - sizeof(AudioChannelLayoutTag), - &layout->mChannelLayoutTag, - &i_param_size, layout); - } - - if (err != noErr || layout->mNumberChannelDescriptions == 0) - { - msg_Err(p_aout, "insufficient number of output channels"); - free(layout); - goto error; - } - - msg_Dbg(p_aout, "layout of AUHAL has %i channels" , - layout->mNumberChannelDescriptions); - - /* Initialize the VLC core channel count */ - fmt->i_physical_channels = 0; - i_original = fmt->i_original_channels & AOUT_CHAN_PHYSMASK; - - if (i_original == AOUT_CHAN_CENTER - || layout->mNumberChannelDescriptions < 2) - { - /* We only need Mono or cannot output more than 1 channel */ - fmt->i_physical_channels = AOUT_CHAN_CENTER; - } - else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) - || layout->mNumberChannelDescriptions < 3) - { - /* We only need Stereo or cannot output more than 2 channels */ - fmt->i_physical_channels = AOUT_CHANS_STEREO; - } - else - { - // maps auhal channels to vlc ones - static const unsigned i_auhal_channel_mapping[] = { - [kAudioChannelLabel_Left] = AOUT_CHAN_LEFT, - [kAudioChannelLabel_Right] = AOUT_CHAN_RIGHT, - [kAudioChannelLabel_Center] = AOUT_CHAN_CENTER, - [kAudioChannelLabel_LFEScreen] = AOUT_CHAN_LFE, - [kAudioChannelLabel_LeftSurround] = AOUT_CHAN_REARLEFT, - [kAudioChannelLabel_RightSurround] = AOUT_CHAN_REARRIGHT, - /* needs to be swapped with rear */ - [kAudioChannelLabel_RearSurroundLeft] = AOUT_CHAN_MIDDLELEFT, - /* needs to be swapped with rear */ - [kAudioChannelLabel_RearSurroundRight] = AOUT_CHAN_MIDDLERIGHT, - [kAudioChannelLabel_CenterSurround] = AOUT_CHAN_REARCENTER - }; - - /* We want more than stereo and we can do that */ - for (unsigned i = 0; i < layout->mNumberChannelDescriptions; i++) - { -#ifndef NDEBUG - msg_Dbg(p_aout, "this is channel: %d", - (int)layout->mChannelDescriptions[i].mChannelLabel); -#endif - - AudioChannelLabel chan = - layout->mChannelDescriptions[i].mChannelLabel; - const size_t i_auhal_size = sizeof(i_auhal_channel_mapping) - / sizeof(i_auhal_channel_mapping[0]); - if (chan < i_auhal_size && i_auhal_channel_mapping[chan] > 0) - fmt->i_physical_channels |= i_auhal_channel_mapping[chan]; - else - msg_Dbg(p_aout, "found nonrecognized channel %d at index " - "%d", chan, i); - } - if (fmt->i_physical_channels == 0) - { - fmt->i_physical_channels = AOUT_CHANS_STEREO; - msg_Err(p_aout, "You should configure your speaker layout with " - "Audio Midi Setup in /Applications/Utilities. VLC will " - "output Stereo only."); - vlc_dialog_display_error(p_aout, - _("Audio device is not configured"), "%s", - _("You should configure your speaker layout with " - "\"Audio Midi Setup\" in /Applications/" - "Utilities. VLC will output Stereo only.")); - } - } - free(layout); } else - { msg_Warn(p_aout, "device driver does not support " "kAudioDevicePropertyPreferredChannelLayout - using stereo " "fallback [%4.4s]", (const char *)&err); - fmt->i_physical_channels = AOUT_CHANS_STEREO; - } - fmt->i_original_channels = fmt->i_physical_channels; - - msg_Dbg(p_aout, "selected %d physical channels for device output", - aout_FormatNbChannels(fmt)); - msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt)); - - /* Now we set the INPUT layout of the AU */ - AudioChannelLayout input_layout; - memset (&input_layout, 0, sizeof(input_layout)); - uint32_t chans_out[AOUT_CHAN_MAX]; - - /* Some channel abbreviations used below: - * L - left - * R - right - * C - center - * Ls - left surround - * Rs - right surround - * Cs - center surround - * Rls - rear left surround - * Rrs - rear right surround - * Lw - left wide - * Rw - right wide - * Lsd - left surround direct - * Rsd - right surround direct - * Lc - left center - * Rc - right center - * Ts - top surround - * Vhl - vertical height left - * Vhc - vertical height center - * Vhr - vertical height right - * Lt - left matrix total. for matrix encoded stereo. - * Rt - right matrix total. for matrix encoded stereo. */ - - switch(aout_FormatNbChannels(fmt)) - { - case 1: - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; - break; - case 2: - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; - break; - case 3: - if (fmt->i_physical_channels & AOUT_CHAN_CENTER) /* L R C */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; - else if (fmt->i_physical_channels & AOUT_CHAN_LFE) /* L R LFE */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; - break; - case 4: - if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE)) /* L R C LFE */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; - else if (fmt->i_physical_channels & AOUT_CHANS_REAR) /* L R Ls Rs */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; - else if (fmt->i_physical_channels & AOUT_CHANS_CENTER) /* L R C Cs */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; - break; - case 5: - if (fmt->i_physical_channels & (AOUT_CHAN_CENTER)) /* L R Ls Rs C */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; - else if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) /* L R Ls Rs LFE */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; - break; - case 6: - if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) - { - /* L R Ls Rs C LFE */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; - - chans_out[0] = AOUT_CHAN_LEFT; - chans_out[1] = AOUT_CHAN_RIGHT; - chans_out[2] = AOUT_CHAN_REARLEFT; - chans_out[3] = AOUT_CHAN_REARRIGHT; - chans_out[4] = AOUT_CHAN_CENTER; - chans_out[5] = AOUT_CHAN_LFE; - - p_sys->c.chans_to_reorder = - aout_CheckChannelReorder(NULL, chans_out, - fmt->i_physical_channels, - p_sys->c.chan_table); - if (p_sys->c.chans_to_reorder) - msg_Dbg(p_aout, "channel reordering needed for 5.1 output"); - } - else - { - /* L R Ls Rs C Cs */ - input_layout.mChannelLayoutTag = - kAudioChannelLayoutTag_AudioUnit_6_0; - - chans_out[0] = AOUT_CHAN_LEFT; - chans_out[1] = AOUT_CHAN_RIGHT; - chans_out[2] = AOUT_CHAN_REARLEFT; - chans_out[3] = AOUT_CHAN_REARRIGHT; - chans_out[4] = AOUT_CHAN_CENTER; - chans_out[5] = AOUT_CHAN_REARCENTER; - - p_sys->c.chans_to_reorder = - aout_CheckChannelReorder(NULL, chans_out, - fmt->i_physical_channels, - p_sys->c.chan_table); - if (p_sys->c.chans_to_reorder) - msg_Dbg(p_aout, "channel reordering needed for 6.0 output"); - } - break; - case 7: - /* L R C LFE Ls Rs Cs */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; - - chans_out[0] = AOUT_CHAN_LEFT; - chans_out[1] = AOUT_CHAN_RIGHT; - chans_out[2] = AOUT_CHAN_CENTER; - chans_out[3] = AOUT_CHAN_LFE; - chans_out[4] = AOUT_CHAN_REARLEFT; - chans_out[5] = AOUT_CHAN_REARRIGHT; - chans_out[6] = AOUT_CHAN_REARCENTER; - - p_sys->c.chans_to_reorder = - aout_CheckChannelReorder(NULL, chans_out, - fmt->i_physical_channels, - p_sys->c.chan_table); - if (p_sys->c.chans_to_reorder) - msg_Dbg(p_aout, "channel reordering needed for 6.1 output"); - - break; - case 8: - if (fmt->i_physical_channels & (AOUT_CHAN_LFE) - || currentMinorSystemVersion < 7) - { - /* L R C LFE Ls Rs Rls Rrs */ - input_layout.mChannelLayoutTag = - kAudioChannelLayoutTag_MPEG_7_1_C; - - chans_out[0] = AOUT_CHAN_LEFT; - chans_out[1] = AOUT_CHAN_RIGHT; - chans_out[2] = AOUT_CHAN_CENTER; - chans_out[3] = AOUT_CHAN_LFE; - chans_out[4] = AOUT_CHAN_MIDDLELEFT; - chans_out[5] = AOUT_CHAN_MIDDLERIGHT; - chans_out[6] = AOUT_CHAN_REARLEFT; - chans_out[7] = AOUT_CHAN_REARRIGHT; - - if (!(fmt->i_physical_channels & (AOUT_CHAN_LFE))) - msg_Warn(p_aout, "8.0 audio output not supported on OS X " - "10.%i, layout will be incorrect", - currentMinorSystemVersion); - } -#ifdef MAC_OS_X_VERSION_10_7 - else - { - /* Lc C Rc L R Ls Cs Rs */ - input_layout.mChannelLayoutTag = - kAudioChannelLayoutTag_DTS_8_0_B; - - chans_out[0] = AOUT_CHAN_MIDDLELEFT; - chans_out[1] = AOUT_CHAN_CENTER; - chans_out[2] = AOUT_CHAN_MIDDLERIGHT; - chans_out[3] = AOUT_CHAN_LEFT; - chans_out[4] = AOUT_CHAN_RIGHT; - chans_out[5] = AOUT_CHAN_REARLEFT; - chans_out[6] = AOUT_CHAN_REARCENTER; - chans_out[7] = AOUT_CHAN_REARRIGHT; - } -#endif - p_sys->c.chans_to_reorder = - aout_CheckChannelReorder(NULL, chans_out, - fmt->i_physical_channels, - p_sys->c.chan_table); - if (p_sys->c.chans_to_reorder) - msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output"); - break; - case 9: - if (currentMinorSystemVersion < 7) - { - msg_Warn(p_aout, "8.1 audio output not supported on OS X 10.%i", - currentMinorSystemVersion); - break; - } - -#ifdef MAC_OS_X_VERSION_10_7 - /* Lc C Rc L R Ls Cs Rs LFE */ - input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DTS_8_1_B; - chans_out[0] = AOUT_CHAN_MIDDLELEFT; - chans_out[1] = AOUT_CHAN_CENTER; - chans_out[2] = AOUT_CHAN_MIDDLERIGHT; - chans_out[3] = AOUT_CHAN_LEFT; - chans_out[4] = AOUT_CHAN_RIGHT; - chans_out[5] = AOUT_CHAN_REARLEFT; - chans_out[6] = AOUT_CHAN_REARCENTER; - chans_out[7] = AOUT_CHAN_REARRIGHT; - chans_out[8] = AOUT_CHAN_LFE; - - p_sys->c.chans_to_reorder = - aout_CheckChannelReorder(NULL, chans_out, - fmt->i_physical_channels, - p_sys->c.chan_table); - if (p_sys->c.chans_to_reorder) - msg_Dbg(p_aout, "channel reordering needed for 8.1 output"); -#endif - break; - } - - /* Set up the format to be used */ - DeviceFormat.mSampleRate = fmt->i_rate; - DeviceFormat.mFormatID = kAudioFormatLinearPCM; - - /* We use float 32 since this is VLC's endorsed format */ - fmt->i_format = VLC_CODEC_FL32; - DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; - DeviceFormat.mBitsPerChannel = 32; - DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels(fmt); - - /* Calculate framesizes and stuff */ - DeviceFormat.mFramesPerPacket = 1; - DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel - * DeviceFormat.mChannelsPerFrame / 8; - DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame - * DeviceFormat.mFramesPerPacket; - - /* Set the desired format */ - i_param_size = sizeof(AudioStreamBasicDescription); - err = AudioUnitSetProperty(p_sys->au_unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &DeviceFormat, - i_param_size); - if (err != noErr) - goto error; - - msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , DeviceFormat)); - - /* Retrieve actual format */ - err = AudioUnitGetProperty(p_sys->au_unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &DeviceFormat, - &i_param_size); - if (err != noErr) - goto error; - - msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is ", - DeviceFormat)); /* Do the last VLC aout setups */ - aout_FormatPrepare(fmt); - - /* set the IOproc callback */ - input.inputProc = RenderCallbackAnalog; - input.inputProcRefCon = p_aout; - - err = AudioUnitSetProperty(p_sys->au_unit, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &input, sizeof(input)); - if (err != noErr) - goto error; - - /* Set the input_layout as the layout VLC will use to feed the AU unit. - * Yes, it must be the INPUT scope */ - err = AudioUnitSetProperty(p_sys->au_unit, - kAudioUnitProperty_AudioChannelLayout, - kAudioUnitScope_Input, 0, &input_layout, - sizeof(input_layout)); - if (err != noErr) - goto error; - - /* AU initiliaze */ - err = AudioUnitInitialize(p_sys->au_unit); - if (err != noErr) - { - msg_Err(p_aout, "AudioUnitInitialize failed: [%4.4s]", - (const char *)&err); + fmt->i_format = VLC_CODEC_FL32; + int ret = au_Initialize(p_aout, p_sys->au_unit, fmt, layout); + if (ret != VLC_SUCCESS) goto error; - } - int ret = ca_Init(p_aout, fmt, AUDIO_BUFFER_SIZE_IN_SECONDS * fmt->i_rate * - fmt->i_bytes_per_frame); + ret = ca_Init(p_aout, fmt, AUDIO_BUFFER_SIZE_IN_SECONDS * fmt->i_rate * + fmt->i_bytes_per_frame); if (ret != VLC_SUCCESS) { AudioUnitUninitialize(p_sys->au_unit); @@ -1427,9 +1019,11 @@ StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt) VolumeSet(p_aout, p_sys->f_volume); MuteSet(p_aout, p_sys->b_mute); + free(layout); return VLC_SUCCESS; error: AudioComponentInstanceDispose(p_sys->au_unit); + free(layout); return VLC_EGENERIC; } diff --git a/modules/audio_output/coreaudio_common.c b/modules/audio_output/coreaudio_common.c index 0ca4986..e84cbc4 100644 --- a/modules/audio_output/coreaudio_common.c +++ b/modules/audio_output/coreaudio_common.c @@ -23,6 +23,12 @@ *****************************************************************************/ #import "coreaudio_common.h" +#import <CoreAudio/CoreAudioTypes.h> + +#if !TARGET_OS_IPHONE +#import <CoreServices/CoreServices.h> +#import <vlc_dialog.h> +#endif static inline uint64_t BytesToFrames(struct aout_sys_common *p_sys, size_t i_bytes) @@ -188,6 +194,7 @@ ca_Init(audio_output_t *p_aout, const audio_sample_format_t *fmt, p_sys->i_rate = fmt->i_rate; p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame; p_sys->i_frame_length = fmt->i_frame_length; + p_sys->chans_to_reorder = 0; p_aout->play = ca_Play; p_aout->pause = ca_Pause; @@ -234,3 +241,465 @@ au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type) } return au; } + +/***************************************************************************** + * RenderCallback: This function is called everytime the AudioUnit wants + * us to provide some more audio data. + * Don't print anything during normal playback, calling blocking function from + * this callback is not allowed. + *****************************************************************************/ +static OSStatus +RenderCallback(void *p_data, AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, + UInt32 inNumberFrames, AudioBufferList *ioData) +{ + VLC_UNUSED(ioActionFlags); + VLC_UNUSED(inTimeStamp); + VLC_UNUSED(inBusNumber); + VLC_UNUSED(inNumberFrames); + + ca_Render(p_data, ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize); + + return noErr; +} + +static AudioChannelLayout * +GetLayoutDescription(audio_output_t *p_aout, + const AudioChannelLayout *outlayout) +{ + AudioFormatPropertyID id; + UInt32 size; + const void *data; + /* We need to "fill out" the ChannelLayout, because there are multiple + * ways that it can be set */ + if (outlayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) + { + id = kAudioFormatProperty_ChannelLayoutForBitmap; + size = sizeof(UInt32); + data = &outlayout->mChannelBitmap; + } + else + { + id = kAudioFormatProperty_ChannelLayoutForTag; + size = sizeof(AudioChannelLayoutTag); + data = &outlayout->mChannelLayoutTag; + } + + UInt32 param_size; + OSStatus err = AudioFormatGetPropertyInfo(id, size, data, ¶m_size); + if (err != noErr) + return NULL; + + AudioChannelLayout *reslayout = malloc(param_size); + if (reslayout == NULL) + return NULL; + + err = AudioFormatGetProperty(id, size, data, ¶m_size, reslayout); + if (err != noErr || reslayout->mNumberChannelDescriptions == 0) + { + msg_Err(p_aout, "insufficient number of output channels"); + free(reslayout); + return NULL; + } + + return reslayout; +} + +static int +MapOutputLayout(audio_output_t *p_aout, audio_sample_format_t *fmt, + const AudioChannelLayout *outlayout) +{ + /* Fill VLC physical_channels from output layout */ + fmt->i_physical_channels = 0; + uint32_t i_original = fmt->i_original_channels & AOUT_CHAN_PHYSMASK; + AudioChannelLayout *reslayout = NULL; + + if (outlayout == NULL) + { + msg_Dbg(p_aout, "not output layout, default to Stereo"); + fmt->i_physical_channels = AOUT_CHANS_STEREO; + goto end; + } + + if (outlayout->mChannelLayoutTag != + kAudioChannelLayoutTag_UseChannelDescriptions) + { + reslayout = GetLayoutDescription(p_aout, outlayout); + if (reslayout == NULL) + return VLC_EGENERIC; + outlayout = reslayout; + } + + if (i_original == AOUT_CHAN_CENTER + || outlayout->mNumberChannelDescriptions < 2) + { + /* We only need Mono or cannot output more than 1 channel */ + fmt->i_physical_channels = AOUT_CHAN_CENTER; + msg_Dbg(p_aout, "output layout of AUHAL has 1 channel"); + } + else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) + || outlayout->mNumberChannelDescriptions < 3) + { + /* We only need Stereo or cannot output more than 2 channels */ + fmt->i_physical_channels = AOUT_CHANS_STEREO; + msg_Dbg(p_aout, "output layout of AUHAL is Stereo"); + } + else + { + assert(outlayout->mNumberChannelDescriptions > 0); + + msg_Dbg(p_aout, "output layout of AUHAL has %i channels", + outlayout->mNumberChannelDescriptions); + + /* maps auhal channels to vlc ones */ + static const unsigned i_auhal_channel_mapping[] = { + [kAudioChannelLabel_Left] = AOUT_CHAN_LEFT, + [kAudioChannelLabel_Right] = AOUT_CHAN_RIGHT, + [kAudioChannelLabel_Center] = AOUT_CHAN_CENTER, + [kAudioChannelLabel_LFEScreen] = AOUT_CHAN_LFE, + [kAudioChannelLabel_LeftSurround] = AOUT_CHAN_REARLEFT, + [kAudioChannelLabel_RightSurround] = AOUT_CHAN_REARRIGHT, + /* needs to be swapped with rear */ + [kAudioChannelLabel_RearSurroundLeft] = AOUT_CHAN_MIDDLELEFT, + /* needs to be swapped with rear */ + [kAudioChannelLabel_RearSurroundRight] = AOUT_CHAN_MIDDLERIGHT, + [kAudioChannelLabel_CenterSurround] = AOUT_CHAN_REARCENTER + }; + static const size_t i_auhal_size = sizeof(i_auhal_channel_mapping) + / sizeof(i_auhal_channel_mapping[0]); + + /* We want more than stereo and we can do that */ + for (unsigned i = 0; i < outlayout->mNumberChannelDescriptions; i++) + { + AudioChannelLabel chan = + outlayout->mChannelDescriptions[i].mChannelLabel; +#ifndef NDEBUG + msg_Dbg(p_aout, "this is channel: %d", (int) chan); +#endif + if (chan < i_auhal_size && i_auhal_channel_mapping[chan] > 0) + fmt->i_physical_channels |= i_auhal_channel_mapping[chan]; + else + msg_Dbg(p_aout, "found nonrecognized channel %d at index " + "%d", chan, i); + } + if (fmt->i_physical_channels == 0) + { + fmt->i_physical_channels = AOUT_CHANS_STEREO; + msg_Err(p_aout, "You should configure your speaker layout with " + "Audio Midi Setup in /Applications/Utilities. VLC will " + "output Stereo only."); +#if !TARGET_OS_IPHONE + vlc_dialog_display_error(p_aout, + _("Audio device is not configured"), "%s", + _("You should configure your speaker layout with " + "\"Audio Midi Setup\" in /Applications/" + "Utilities. VLC will output Stereo only.")); +#endif + } + } + +end: + free(reslayout); + fmt->i_original_channels = fmt->i_physical_channels; + aout_FormatPrepare(fmt); + + msg_Dbg(p_aout, "selected %d physical channels for device output", + aout_FormatNbChannels(fmt)); + msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt)); + + return VLC_SUCCESS; +} + +static int +SetupInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt, + AudioChannelLayoutTag *inlayout_tag) +{ + struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys; + uint32_t chans_out[AOUT_CHAN_MAX]; + +#if TARGET_OS_IPHONE + const bool b_8x_support = false; +#else + SInt32 osx_min_version; + if (Gestalt(gestaltSystemVersionMinor, &osx_min_version) != noErr) + msg_Err(p_aout, "failed to check OSX version"); + const bool b_8x_support = osx_min_version >= 7; +#endif + + /* Some channel abbreviations used below: + * L - left + * R - right + * C - center + * Ls - left surround + * Rs - right surround + * Cs - center surround + * Rls - rear left surround + * Rrs - rear right surround + * Lw - left wide + * Rw - right wide + * Lsd - left surround direct + * Rsd - right surround direct + * Lc - left center + * Rc - right center + * Ts - top surround + * Vhl - vertical height left + * Vhc - vertical height center + * Vhr - vertical height right + * Lt - left matrix total. for matrix encoded stereo. + * Rt - right matrix total. for matrix encoded stereo. */ + + switch (aout_FormatNbChannels(fmt)) + { + case 1: + *inlayout_tag = kAudioChannelLayoutTag_Mono; + break; + case 2: + *inlayout_tag = kAudioChannelLayoutTag_Stereo; + break; + case 3: + if (fmt->i_physical_channels & AOUT_CHAN_CENTER) /* L R C */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_7; + else if (fmt->i_physical_channels & AOUT_CHAN_LFE) /* L R LFE */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_4; + break; + case 4: + if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE)) /* L R C LFE */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_10; + else if (fmt->i_physical_channels & AOUT_CHANS_REAR) /* L R Ls Rs */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_3; + else if (fmt->i_physical_channels & AOUT_CHANS_CENTER) /* L R C Cs */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_3; + break; + case 5: + if (fmt->i_physical_channels & (AOUT_CHAN_CENTER)) /* L R Ls Rs C */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_19; + else if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) /* L R Ls Rs LFE */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_18; + break; + case 6: + if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) + { + /* L R Ls Rs C LFE */ + *inlayout_tag = kAudioChannelLayoutTag_DVD_20; + + chans_out[0] = AOUT_CHAN_LEFT; + chans_out[1] = AOUT_CHAN_RIGHT; + chans_out[2] = AOUT_CHAN_REARLEFT; + chans_out[3] = AOUT_CHAN_REARRIGHT; + chans_out[4] = AOUT_CHAN_CENTER; + chans_out[5] = AOUT_CHAN_LFE; + + p_sys->chans_to_reorder = + aout_CheckChannelReorder(NULL, chans_out, + fmt->i_physical_channels, + p_sys->chan_table); + if (p_sys->chans_to_reorder) + msg_Dbg(p_aout, "channel reordering needed for 5.1 output"); + } + else + { + /* L R Ls Rs C Cs */ + *inlayout_tag = kAudioChannelLayoutTag_AudioUnit_6_0; + + chans_out[0] = AOUT_CHAN_LEFT; + chans_out[1] = AOUT_CHAN_RIGHT; + chans_out[2] = AOUT_CHAN_REARLEFT; + chans_out[3] = AOUT_CHAN_REARRIGHT; + chans_out[4] = AOUT_CHAN_CENTER; + chans_out[5] = AOUT_CHAN_REARCENTER; + + p_sys->chans_to_reorder = + aout_CheckChannelReorder(NULL, chans_out, + fmt->i_physical_channels, + p_sys->chan_table); + if (p_sys->chans_to_reorder) + msg_Dbg(p_aout, "channel reordering needed for 6.0 output"); + } + break; + case 7: + /* L R C LFE Ls Rs Cs */ + *inlayout_tag = kAudioChannelLayoutTag_MPEG_6_1_A; + + chans_out[0] = AOUT_CHAN_LEFT; + chans_out[1] = AOUT_CHAN_RIGHT; + chans_out[2] = AOUT_CHAN_CENTER; + chans_out[3] = AOUT_CHAN_LFE; + chans_out[4] = AOUT_CHAN_REARLEFT; + chans_out[5] = AOUT_CHAN_REARRIGHT; + chans_out[6] = AOUT_CHAN_REARCENTER; + + p_sys->chans_to_reorder = + aout_CheckChannelReorder(NULL, chans_out, + fmt->i_physical_channels, + p_sys->chan_table); + if (p_sys->chans_to_reorder) + msg_Dbg(p_aout, "channel reordering needed for 6.1 output"); + + break; + case 8: + if (fmt->i_physical_channels & (AOUT_CHAN_LFE) || !b_8x_support) + { + /* L R C LFE Ls Rs Rls Rrs */ + *inlayout_tag = kAudioChannelLayoutTag_MPEG_7_1_C; + + chans_out[0] = AOUT_CHAN_LEFT; + chans_out[1] = AOUT_CHAN_RIGHT; + chans_out[2] = AOUT_CHAN_CENTER; + chans_out[3] = AOUT_CHAN_LFE; + chans_out[4] = AOUT_CHAN_MIDDLELEFT; + chans_out[5] = AOUT_CHAN_MIDDLERIGHT; + chans_out[6] = AOUT_CHAN_REARLEFT; + chans_out[7] = AOUT_CHAN_REARRIGHT; + + if (!(fmt->i_physical_channels & (AOUT_CHAN_LFE))) + msg_Warn(p_aout, "8.0 audio output not supported on this " + "device, layout will be incorrect"); + } +#ifdef MAC_OS_X_VERSION_10_7 + else + { + /* Lc C Rc L R Ls Cs Rs */ + *inlayout_tag = kAudioChannelLayoutTag_DTS_8_0_B; + + chans_out[0] = AOUT_CHAN_MIDDLELEFT; + chans_out[1] = AOUT_CHAN_CENTER; + chans_out[2] = AOUT_CHAN_MIDDLERIGHT; + chans_out[3] = AOUT_CHAN_LEFT; + chans_out[4] = AOUT_CHAN_RIGHT; + chans_out[5] = AOUT_CHAN_REARLEFT; + chans_out[6] = AOUT_CHAN_REARCENTER; + chans_out[7] = AOUT_CHAN_REARRIGHT; + } +#endif + p_sys->chans_to_reorder = + aout_CheckChannelReorder(NULL, chans_out, + fmt->i_physical_channels, + p_sys->chan_table); + if (p_sys->chans_to_reorder) + msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output"); + break; + case 9: + if (!b_8x_support) + { + msg_Warn(p_aout, "8.1 audio output not supported on this device"); + break; + } + +#ifdef MAC_OS_X_VERSION_10_7 + /* Lc C Rc L R Ls Cs Rs LFE */ + *inlayout_tag = kAudioChannelLayoutTag_DTS_8_1_B; + chans_out[0] = AOUT_CHAN_MIDDLELEFT; + chans_out[1] = AOUT_CHAN_CENTER; + chans_out[2] = AOUT_CHAN_MIDDLERIGHT; + chans_out[3] = AOUT_CHAN_LEFT; + chans_out[4] = AOUT_CHAN_RIGHT; + chans_out[5] = AOUT_CHAN_REARLEFT; + chans_out[6] = AOUT_CHAN_REARCENTER; + chans_out[7] = AOUT_CHAN_REARRIGHT; + chans_out[8] = AOUT_CHAN_LFE; + + p_sys->chans_to_reorder = + aout_CheckChannelReorder(NULL, chans_out, + fmt->i_physical_channels, + p_sys->chan_table); + if (p_sys->chans_to_reorder) + msg_Dbg(p_aout, "channel reordering needed for 8.1 output"); +#endif + break; + } + + return VLC_SUCCESS; +} + +int +au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt, + const AudioChannelLayout *outlayout) +{ + assert(fmt->i_format == VLC_CODEC_FL32); + + int ret = MapOutputLayout(p_aout, fmt, outlayout); + if (ret != VLC_SUCCESS) + return ret; + + AudioChannelLayoutTag inlayout_tag; + ret = SetupInputLayout(p_aout, fmt, &inlayout_tag); + if (ret != VLC_SUCCESS) + return ret; + + /* Set the desired format */ + AudioStreamBasicDescription desc; + desc.mSampleRate = fmt->i_rate; + desc.mFormatID = kAudioFormatLinearPCM; + desc.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; + desc.mChannelsPerFrame = aout_FormatNbChannels(fmt); + desc.mFramesPerPacket = 1; + desc.mBitsPerChannel = 32; + desc.mBytesPerFrame = desc.mBitsPerChannel * desc.mChannelsPerFrame / 8; + desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket; + + OSStatus err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &desc, + sizeof(desc)); + if (err != noErr) + { + msg_Err(p_aout, "failed to set stream format [%4.4s]", + (const char *)&err); + return VLC_EGENERIC; + } + msg_Dbg(p_aout, STREAM_FORMAT_MSG("Current AU format: " , desc)); + + /* Retrieve actual format */ + err = AudioUnitGetProperty(au, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &desc, + &(UInt32) { sizeof(desc) }); + if (err != noErr) + { + msg_Err(p_aout, "failed to verify stream format [%4.4s]", + (const char *)&err); + return VLC_EGENERIC; + } + + /* Set the IOproc callback */ + const AURenderCallbackStruct callback = { + .inputProc = RenderCallback, + .inputProcRefCon = p_aout, + }; + + err = AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &callback, + sizeof(callback)); + if (err != noErr) + { + msg_Err(p_aout, "failed to setup render callback [%4.4s]", + (const char *)&err); + return VLC_EGENERIC; + } + + /* Set the input_layout as the layout VLC will use to feed the AU unit. + * Yes, it must be the INPUT scope */ + AudioChannelLayout inlayout = { + .mChannelLayoutTag = inlayout_tag, + }; + err = AudioUnitSetProperty(au, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Input, 0, &inlayout, + sizeof(inlayout)); + if (err != noErr) + { + msg_Err(p_aout, "failed to setup input layout [%4.4s]", + (const char *)&err); + return VLC_EGENERIC; + } + + /* AU init */ + err = AudioUnitInitialize(au); + + if (err != noErr) + { + msg_Err(p_aout, "AudioUnitInitialize failed: [%4.4s]", + (const char *)&err); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} diff --git a/modules/audio_output/coreaudio_common.h b/modules/audio_output/coreaudio_common.h index f6a532a..21831cf 100644 --- a/modules/audio_output/coreaudio_common.h +++ b/modules/audio_output/coreaudio_common.h @@ -53,11 +53,11 @@ struct aout_sys_common int i_rate; unsigned int i_bytes_per_frame; unsigned int i_frame_length; + uint8_t chans_to_reorder; + uint8_t chan_table[AOUT_CHAN_MAX]; /* The following need to set by the caller */ - uint8_t chans_to_reorder; - uint8_t chan_table[AOUT_CHAN_MAX]; /* The time the device needs to process the data. In samples. */ uint32_t i_device_latency; }; @@ -78,3 +78,7 @@ int ca_Init(audio_output_t *p_aout, const audio_sample_format_t *fmt, void ca_Clean(audio_output_t *p_aout); AudioUnit au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type); + +int au_Initialize(audio_output_t *p_aout, AudioUnit au, + audio_sample_format_t *fmt, + const AudioChannelLayout *outlayout); _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
