diff options
author | Stefano Pigozzi <stefano.pigozzi@gmail.com> | 2013-06-23 11:18:54 +0200 |
---|---|---|
committer | Stefano Pigozzi <stefano.pigozzi@gmail.com> | 2013-07-22 21:53:17 +0200 |
commit | 1640ce326276afa64528bfb10c20ae1cae3f1ad5 (patch) | |
tree | 84f453b8df61d1083a2902fe8d1c091287e02324 /audio/out | |
parent | f9a31bc3d9fba6f42f5d8c9dd0afccbe5f690589 (diff) | |
download | mpv-1640ce326276afa64528bfb10c20ae1cae3f1ad5.tar.bz2 mpv-1640ce326276afa64528bfb10c20ae1cae3f1ad5.tar.xz |
ao_coreaudio: refactor initialization
The initialization is split more clearly between compressed and lpcm case.
For the compressed case, format selection is simplified a lot and negotiation
removed. The way it was written it just passed back to the core the original
requested format, not what was found available on hardware.
Since this is most likely useless for the compressed case, I didn't bother
with this. In the future I'd like to split this AO in two one that only uses
the AUHAL and the other with direct access to the hardware so that even
passthrough of lcpm can be possible. This would decrease the latency,
audiophiles would like that.
Diffstat (limited to 'audio/out')
-rw-r--r-- | audio/out/ao_coreaudio.c | 656 | ||||
-rw-r--r-- | audio/out/ao_coreaudio_common.c | 22 |
2 files changed, 282 insertions, 396 deletions
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c index acab24799b..205078d2be 100644 --- a/audio/out/ao_coreaudio.c +++ b/audio/out/ao_coreaudio.c @@ -53,33 +53,31 @@ static void print_buffer(struct mp_ring *buffer) talloc_free(tctx); } +struct priv_d { + AudioDeviceIOProcID renderCallback; /* Render callback used for SPDIF */ + pid_t i_hog_pid; /* Keeps the pid of our hog status. */ + AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */ + int i_stream_index; /* The index of i_stream_id in an AudioBufferList */ + AudioStreamBasicDescription stream_format; /* The format we changed the stream to */ + int b_changed_mixing; /* Whether we need to set the mixing mode back */ + int b_stream_format_changed; /* Flag for main thread to reset stream's format to digital and reset buffer */ + int b_muted; /* Are we muted in digital mode? */ +}; + struct priv { AudioDeviceID i_selected_dev; /* Keeps DeviceID of the selected device. */ int b_supports_digital; /* Does the currently selected device support digital mode? */ int b_digital; /* Are we running in digital mode? */ - int b_muted; /* Are we muted in digital mode? */ - - AudioDeviceIOProcID renderCallback; /* Render callback used for SPDIF */ /* AudioUnit */ AudioUnit theOutputUnit; - /* CoreAudio SPDIF mode specific */ - pid_t i_hog_pid; /* Keeps the pid of our hog status. */ - AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */ - int i_stream_index; /* The index of i_stream_id in an AudioBufferList */ - AudioStreamBasicDescription stream_format; /* The format we changed the stream to */ - AudioStreamBasicDescription sfmt_revert; /* The original format of the stream */ - int b_revert; /* Whether we need to revert the stream format */ - int b_changed_mixing; /* Whether we need to set the mixing mode back */ - int b_stream_format_changed; /* Flag for main thread to reset stream's format to digital and reset buffer */ - - /* Original common part */ int packetSize; int paused; struct mp_ring *buffer; + struct priv_d *digital; }; static int get_ring_size(struct ao *ao) @@ -107,12 +105,13 @@ static OSStatus render_cb_digital( const void *in_data, const AudioTimeStamp *in_ts, AudioBufferList *out_data, const AudioTimeStamp *out_ts, void *ctx) { - struct ao *ao = ctx; - struct priv *p = ao->priv; - AudioBuffer buf = out_data->mBuffers[p->i_stream_index]; - int requested = buf.mDataByteSize; + struct ao *ao = ctx; + struct priv *p = ao->priv; + struct priv_d *d = p->digital; + AudioBuffer buf = out_data->mBuffers[d->i_stream_index]; + int requested = buf.mDataByteSize; - if (p->b_muted) + if (d->b_muted) mp_ring_drain(p->buffer, requested); else mp_ring_read(p->buffer, buf.mData, requested); @@ -130,8 +129,9 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) case AOCONTROL_GET_VOLUME: control_vol = (ao_control_vol_t *)arg; if (p->b_digital) { + struct priv_d *d = p->digital; // Digital output has no volume adjust. - int vol = p->b_muted ? 0 : 100; + int vol = d->b_muted ? 0 : 100; *control_vol = (ao_control_vol_t) { .left = vol, .right = vol, }; @@ -149,6 +149,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) control_vol = (ao_control_vol_t *)arg; if (p->b_digital) { + struct priv_d *d = p->digital; // Digital output can not set volume. Here we have to return true // to make mixer forget it. Else mixer will add a soft filter, // that's not we expected and the filter not support ac3 stream @@ -157,9 +158,9 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) // Although not support set volume, but at least we support mute. // MPlayer set mute by set volume to zero, we handle it. if (control_vol->left == 0 && control_vol->right == 0) - p->b_muted = 1; + d->b_muted = 1; else - p->b_muted = 0; + d->b_muted = 0; return CONTROL_TRUE; } @@ -177,7 +178,6 @@ coreaudio_error: return CONTROL_ERROR; } -static int OpenSPDIF(struct ao *ao); static int AudioStreamChangeFormat(AudioStreamID i_stream_id, AudioStreamBasicDescription change_format); @@ -222,25 +222,17 @@ static void print_help(void) free(devs); } +static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd); +static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd); + static int init(struct ao *ao, char *params) { - // int rate, int channels, int format, int flags) - struct priv *p = talloc_zero(ao, struct priv); - ao->priv = p; - - AudioStreamBasicDescription inDesc; - AudioComponentDescription desc; - AudioComponent comp; - AURenderCallbackStruct renderCallback; OSStatus err; - UInt32 size, maxFrames, b_alive; - char *psz_name; - AudioDeviceID devid_def = 0; - int device_id = 0, display_help = 0; + int device_opt = -1, help_opt = 0; const opt_t subopts[] = { - {"device_id", OPT_ARG_INT, &device_id, NULL}, - {"help", OPT_ARG_BOOL, &display_help, NULL}, + {"device_id", OPT_ARG_INT, &device_opt, NULL}, + {"help", OPT_ARG_BOOL, &help_opt, NULL}, {NULL} }; @@ -249,190 +241,147 @@ static int init(struct ao *ao, char *params) return 0; } - if (display_help) + if (help_opt) print_help(); - ca_msg(MSGL_V, "init([%dHz][%dch][%s][%d])\n", - ao->samplerate, ao->channels.num, af_fmt2str_short(ao->format), 0); + struct priv *p = talloc_zero(ao, struct priv); + *p = (struct priv) { + .i_selected_dev = 0, + .b_supports_digital = 0, + .b_digital = 0, + }; + + struct priv_d *d= talloc_zero(p, struct priv_d); + *d = (struct priv_d) { + .b_muted = 0, + .b_stream_format_changed = 0, + .i_hog_pid = -1, + .i_stream_id = 0, + .i_stream_index = -1, + .b_changed_mixing = 0, + }; - p->i_selected_dev = 0; - p->b_supports_digital = 0; - p->b_digital = 0; - p->b_muted = 0; - p->b_stream_format_changed = 0; - p->i_hog_pid = -1; - p->i_stream_id = 0; - p->i_stream_index = -1; - p->b_revert = 0; - p->b_changed_mixing = 0; + p->digital = d; + ao->priv = p; ao->per_application_mixer = true; ao->no_persistent_volume = true; - if (device_id == 0) { - /* Find the ID of the default Device. */ + AudioDeviceID selected_device = 0; + if (device_opt < 0) { + // device not set by user, get the default one err = GetAudioProperty(kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, - sizeof(UInt32), &devid_def); - if (err != noErr) { - ca_msg(MSGL_WARN, - "could not get default audio device: [%4.4s]\n", - (char *)&err); - goto err_out; - } + sizeof(uint32_t), &selected_device); + CHECK_CA_ERROR("could not get default audio device"); } else { - devid_def = device_id; + selected_device = device_opt; } - /* Retrieve the name of the device. */ - err = GetAudioPropertyString(devid_def, + char *device_name; + err = GetAudioPropertyString(selected_device, kAudioObjectPropertyName, - &psz_name); - if (err != noErr) { - ca_msg(MSGL_WARN, - "could not get default audio device name: [%4.4s]\n", - (char *)&err); - goto err_out; - } + &device_name); - ca_msg(MSGL_V, - "got audio output device ID: %" PRIu32 " Name: %s\n", devid_def, - psz_name); + CHECK_CA_ERROR("could not get selected audio device name"); - /* Probe whether device support S/PDIF stream output if input is AC3 stream. */ - if (AF_FORMAT_IS_AC3(ao->format)) { - if (AudioDeviceSupportsDigital(devid_def)) - p->b_supports_digital = 1; - ca_msg(MSGL_V, - "probe default audio output device about support for digital s/pdif output: %d\n", - p->b_supports_digital); - } + ca_msg(MSGL_V, + "selected audio output device: %s (%" PRIu32 ")\n", + device_name, selected_device); - free(psz_name); + free(device_name); // Save selected device id - p->i_selected_dev = devid_def; + p->i_selected_dev = selected_device; struct mp_chmap_sel chmap_sel = {0}; mp_chmap_sel_add_waveext(&chmap_sel); if (!ao_chmap_sel_adjust(ao, &chmap_sel, &ao->channels)) - goto err_out; - - // Build Description for the input format - inDesc.mSampleRate = ao->samplerate; - inDesc.mFormatID = - p->b_supports_digital ? kAudioFormat60958AC3 : kAudioFormatLinearPCM; - inDesc.mChannelsPerFrame = ao->channels.num; - inDesc.mBitsPerChannel = af_fmt2bits(ao->format); - - if ((ao->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) { - // float - inDesc.mFormatFlags = kAudioFormatFlagIsFloat | - kAudioFormatFlagIsPacked; - } else if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI) { - // signed int - inDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger | - kAudioFormatFlagIsPacked; - } else { - // unsigned int - inDesc.mFormatFlags = kAudioFormatFlagIsPacked; - } + goto coreaudio_error; + + // Build ASBD for the input format + AudioStreamBasicDescription asbd; + asbd.mSampleRate = ao->samplerate; + asbd.mFormatID = p->b_supports_digital ? + kAudioFormat60958AC3 : kAudioFormatLinearPCM; + asbd.mChannelsPerFrame = ao->channels.num; + asbd.mBitsPerChannel = af_fmt2bits(ao->format); + asbd.mFormatFlags = kAudioFormatFlagIsPacked; + + if ((ao->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) + asbd.mFormatFlags |= kAudioFormatFlagIsFloat; + + if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI) + asbd.mFormatFlags |= kAudioFormatFlagIsSignedInteger; + if ((ao->format & AF_FORMAT_END_MASK) == AF_FORMAT_BE) - inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian; - - inDesc.mFramesPerPacket = 1; - p->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = - inDesc.mFramesPerPacket * - ao->channels.num * - (inDesc.mBitsPerChannel / 8); - ca_print_asbd("source format:", &inDesc); - - if (p->b_supports_digital) { - b_alive = 1; - err = GetAudioProperty(p->i_selected_dev, - kAudioDevicePropertyDeviceIsAlive, - sizeof(UInt32), &b_alive); - if (err != noErr) - ca_msg(MSGL_WARN, - "could not check whether device is alive: [%4.4s]\n", - (char *)&err); - if (!b_alive) - ca_msg(MSGL_WARN, "device is not alive\n"); + asbd.mFormatFlags |= kAudioFormatFlagIsBigEndian; - /* S/PDIF output need device in HogMode. */ - err = GetAudioProperty(p->i_selected_dev, - kAudioDevicePropertyHogMode, - sizeof(pid_t), &p->i_hog_pid); - if (err != noErr) { - /* This is not a fatal error. Some drivers simply don't support this property. */ - ca_msg(MSGL_WARN, - "could not check whether device is hogged: [%4.4s]\n", - (char *)&err); - p->i_hog_pid = -1; - } + asbd.mFramesPerPacket = 1; + p->packetSize = asbd.mBytesPerPacket = asbd.mBytesPerFrame = + asbd.mFramesPerPacket * asbd.mChannelsPerFrame * + (asbd.mBitsPerChannel / 8); - if (p->i_hog_pid != -1 && p->i_hog_pid != getpid()) { - ca_msg(MSGL_WARN, - "Selected audio device is exclusively in use by another program.\n"); - goto err_out; - } - p->stream_format = inDesc; - return OpenSPDIF(ao); + ca_print_asbd("source format:", &asbd); + + /* Probe whether device support S/PDIF stream output if input is AC3 stream. */ + if (AF_FORMAT_IS_AC3(ao->format)) { + if (AudioDeviceSupportsDigital(selected_device)) + p->b_supports_digital = 1; } - /* original analog output code */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = - (device_id == - 0) ? kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; + if (p->b_supports_digital) + return init_digital(ao, asbd); + else + return init_lpcm(ao, asbd); + +coreaudio_error: + return CONTROL_FALSE; +} - comp = AudioComponentFindNext(NULL, &desc); //Finds an component that meets the desc spec's +static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd) +{ + OSStatus err; + uint32_t size; + struct priv *p = ao->priv; + + AudioComponentDescription desc = (AudioComponentDescription) { + .componentType = kAudioUnitType_Output, + .componentSubType = kAudioUnitSubType_HALOutput, + .componentManufacturer = kAudioUnitManufacturer_Apple, + .componentFlags = 0, + .componentFlagsMask = 0, + }; + + AudioComponent comp = AudioComponentFindNext(NULL, &desc); if (comp == NULL) { - ca_msg(MSGL_WARN, "Unable to find Output Unit component\n"); - goto err_out; + ca_msg(MSGL_ERR, "unable to find audio component\n"); + goto coreaudio_error; } - err = AudioComponentInstanceNew(comp, &(p->theOutputUnit)); //gains access to the services provided by the component - if (err) { - ca_msg(MSGL_WARN, - "Unable to open Output Unit component: [%4.4s]\n", (char *)&err); - goto err_out; - } + err = AudioComponentInstanceNew(comp, &(p->theOutputUnit)); + CHECK_CA_ERROR("unable to open audio component"); // Initialize AudioUnit err = AudioUnitInitialize(p->theOutputUnit); - if (err) { - ca_msg(MSGL_WARN, - "Unable to initialize Output Unit component: [%4.4s]\n", - (char *)&err); - goto err_out1; - } + CHECK_CA_ERROR_L(coreaudio_error_component, + "unable to initialize audio unit"); - size = sizeof(AudioStreamBasicDescription); + size = sizeof(AudioStreamBasicDescription); err = AudioUnitSetProperty(p->theOutputUnit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &inDesc, size); + kAudioUnitScope_Input, 0, &asbd, size); - if (err) { - ca_msg(MSGL_WARN, "Unable to set the input format: [%4.4s]\n", - (char *)&err); - goto err_out2; - } + CHECK_CA_ERROR_L(coreaudio_error_audiounit, + "unable to set the input format on the audio unit"); - size = sizeof(UInt32); + size = sizeof(uint32_t); err = AudioUnitGetProperty(p->theOutputUnit, kAudioDevicePropertyBufferSize, kAudioUnitScope_Input, 0, &maxFrames, &size); - if (err) { - ca_msg(MSGL_WARN, - "AudioUnitGetProperty returned [%4.4s] when getting kAudioDevicePropertyBufferSize\n", - (char *)&err); - goto err_out2; - } + CHECK_CA_ERROR_L(coreaudio_error_audiounit, + "unable to get buffersize from audio unit"); //Set the Current Device to the Default Output Unit. err = AudioUnitSetProperty(p->theOutputUnit, @@ -440,225 +389,176 @@ static int init(struct ao *ao, char *params) kAudioUnitScope_Global, 0, &p->i_selected_dev, sizeof(p->i_selected_dev)); - ao->samplerate = inDesc.mSampleRate; - - if (!ao_chmap_sel_get_def(ao, &chmap_sel, &ao->channels, - inDesc.mChannelsPerFrame)) - goto err_out2; - - ao->bps = ao->samplerate * inDesc.mBytesPerFrame; - p->buffer = mp_ring_new(p, get_ring_size(ao)); + p->buffer = mp_ring_new(p, get_ring_size(ao)); print_buffer(p->buffer); - renderCallback.inputProc = render_cb_lpcm; - renderCallback.inputProcRefCon = ao; + AURenderCallbackStruct render_cb = (AURenderCallbackStruct) { + .inputProc = render_cb_lpcm, + .inputProcRefCon = ao, + }; + err = AudioUnitSetProperty(p->theOutputUnit, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &renderCallback, + kAudioUnitScope_Input, 0, &render_cb, sizeof(AURenderCallbackStruct)); - if (err) { - ca_msg(MSGL_WARN, - "Unable to set the render callback: [%4.4s]\n", (char *)&err); - goto err_out2; - } - reset(ao); + CHECK_CA_ERROR_L(coreaudio_error_audiounit, + "unable to set render callback on audio unit"); + reset(ao); return CONTROL_OK; -err_out2: +coreaudio_error_audiounit: AudioUnitUninitialize(p->theOutputUnit); -err_out1: +coreaudio_error_component: AudioComponentInstanceDispose(p->theOutputUnit); -err_out: +coreaudio_error: return CONTROL_FALSE; } -/***************************************************************************** -* Setup a encoded digital stream (SPDIF) -*****************************************************************************/ -static int OpenSPDIF(struct ao *ao) +static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd) { struct priv *p = ao->priv; + struct priv_d *d = p->digital; OSStatus err = noErr; - UInt32 i_param_size, b_mix = 0; - Boolean b_writeable = 0; - AudioStreamID *p_streams = NULL; - int i, i_streams = 0; AudioObjectPropertyAddress p_addr; + uint32_t size; + + uint32_t is_alive = 1; + err = GetAudioProperty(p->i_selected_dev, + kAudioDevicePropertyDeviceIsAlive, + sizeof(uint32_t), &is_alive); + + CHECK_CA_WARN( "could not check whether device is alive"); + + if (!is_alive) + ca_msg(MSGL_WARN, "device is not alive\n"); + + d->stream_format = asbd; - /* Start doing the SPDIF setup process. */ p->b_digital = 1; - /* Hog the device. */ - p->i_hog_pid = getpid(); + d->i_hog_pid = getpid(); err = SetAudioProperty(p->i_selected_dev, kAudioDevicePropertyHogMode, - sizeof(p->i_hog_pid), &p->i_hog_pid); - if (err != noErr) { - ca_msg(MSGL_WARN, "failed to set hogmode: [%4.4s]\n", - (char *)&err); - p->i_hog_pid = -1; - goto err_out; + sizeof(d->i_hog_pid), &d->i_hog_pid); + + if (! CHECK_CA_WARN("faild to set hogmode")) { + d->i_hog_pid = -1; + goto coreaudio_error; } - p_addr.mSelector = kAudioDevicePropertySupportsMixing; - p_addr.mScope = kAudioObjectPropertyScopeGlobal; - p_addr.mElement = kAudioObjectPropertyElementMaster; + p_addr = (AudioObjectPropertyAddress) { + .mSelector = kAudioDevicePropertySupportsMixing, + .mScope = kAudioObjectPropertyScopeGlobal, + .mElement = kAudioObjectPropertyElementMaster, + }; /* Set mixable to false if we are allowed to. */ if (AudioObjectHasProperty(p->i_selected_dev, &p_addr)) { - /* Set mixable to false if we are allowed to. */ + Boolean b_writeable = 0; + err = IsAudioPropertySettable(p->i_selected_dev, kAudioDevicePropertySupportsMixing, &b_writeable); - err = GetAudioProperty(p->i_selected_dev, - kAudioDevicePropertySupportsMixing, - sizeof(UInt32), &b_mix); - if (err == noErr && b_writeable) { - b_mix = 0; + + if (b_writeable) { + uint32_t mix = 0; err = SetAudioProperty(p->i_selected_dev, kAudioDevicePropertySupportsMixing, - sizeof(UInt32), &b_mix); - p->b_changed_mixing = 1; - } - if (err != noErr) { - ca_msg(MSGL_WARN, "failed to set mixmode: [%4.4s]\n", - (char *)&err); - goto err_out; + sizeof(uint32_t), &mix); + CHECK_CA_ERROR("failed to set mixmode"); + d->b_changed_mixing = 1; } } + AudioStreamID *streams = NULL; /* Get a list of all the streams on this device. */ - i_param_size = GetAudioPropertyArray(p->i_selected_dev, - kAudioDevicePropertyStreams, - kAudioDevicePropertyScopeOutput, - (void **)&p_streams); - - if (!i_param_size) { - ca_msg(MSGL_WARN, "could not get number of streams.\n"); - goto err_out; + size = GetAudioPropertyArray(p->i_selected_dev, + kAudioDevicePropertyStreams, + kAudioDevicePropertyScopeOutput, + (void **)&streams); + + if (!size) { + ca_msg(MSGL_WARN, "could not get number of streams."); + goto coreaudio_error; } - i_streams = i_param_size / sizeof(AudioStreamID); - - ca_msg(MSGL_V, "current device stream number: %d\n", i_streams); + int streams_n = size / sizeof(AudioStreamID); - for (i = 0; i < i_streams && p->i_stream_index < 0; ++i) { - /* Find a stream with a cac3 stream. */ - AudioStreamRangedDescription *p_format_list = NULL; - int i_formats = 0, j = 0, b_digital = 0; + // TODO: ++i is quite fishy in here. Investigate! + for (int i = 0; i < streams_n && d->i_stream_index < 0; ++i) { + bool digital = AudioStreamSupportsDigital(streams[i]); - i_param_size = GetGlobalAudioPropertyArray(p_streams[i], - kAudioStreamPropertyAvailablePhysicalFormats, - (void **)&p_format_list); + if (digital) { + /* Find a stream with a cac3 stream. */ + AudioStreamRangedDescription *formats = NULL; + size = GetGlobalAudioPropertyArray(streams[i], + kAudioStreamPropertyAvailablePhysicalFormats, + (void **)&formats); - if (!i_param_size) { - ca_msg(MSGL_WARN, - "Could not get number of stream formats.\n"); - continue; - } - - i_formats = i_param_size / sizeof(AudioStreamRangedDescription); - - /* Check if one of the supported formats is a digital format. */ - for (j = 0; j < i_formats; ++j) { - if (p_format_list[j].mFormat.mFormatID == 'IAC3' || - p_format_list[j].mFormat.mFormatID == 'iac3' || - p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 || - p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) { - b_digital = 1; - break; + if (!size) { + ca_msg(MSGL_WARN, "could not get number of stream formats.\n"); + continue; // try next one } - } - if (b_digital) { + int formats_n = size / sizeof(AudioStreamRangedDescription); /* If this stream supports a digital (cac3) format, then set it. */ - int i_requested_rate_format = -1; - int i_current_rate_format = -1; - int i_backup_rate_format = -1; - - p->i_stream_id = p_streams[i]; - p->i_stream_index = i; - - if (p->b_revert == 0) { - /* Retrieve the original format of this stream first if not done so already. */ - err = GetAudioProperty(p->i_stream_id, - kAudioStreamPropertyPhysicalFormat, - sizeof(p->sfmt_revert), - &p->sfmt_revert); - if (err != noErr) { - ca_msg(MSGL_WARN, - "Could not retrieve the original stream format: [%4.4s]\n", - (char *)&err); - free(p_format_list); - continue; - } - p->b_revert = 1; - } - - for (j = 0; j < i_formats; ++j) - if (p_format_list[j].mFormat.mFormatID == 'IAC3' || - p_format_list[j].mFormat.mFormatID == 'iac3' || - p_format_list[j].mFormat.mFormatID == - kAudioFormat60958AC3 || - p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) { - if (p_format_list[j].mFormat.mSampleRate == - p->stream_format.mSampleRate) { - i_requested_rate_format = j; + int req_rate_format = -1; + int max_rate_format = -1; + + d->i_stream_id = streams[i]; + d->i_stream_index = i; + + // TODO: ++j is fishy. was like this in the original code. Investigate! + for (int j = 0; j < formats_n; ++j) + if (AudioFormatIsDigital(asbd)) { + // select the digital format that has exactly the same + // samplerate. If an exact match cannot be found, select + // the format with highest samplerate as backup. + if (formats[j].mFormat.mSampleRate == + d->stream_format.mSampleRate) { + req_rate_format = j; break; - } - if (p_format_list[j].mFormat.mSampleRate == - p->sfmt_revert.mSampleRate) - i_current_rate_format = j; - else if (i_backup_rate_format < 0 || - p_format_list[j].mFormat.mSampleRate > - p_format_list[i_backup_rate_format].mFormat. - mSampleRate) - i_backup_rate_format = j; + } else if (max_rate_format < 0 || + formats[j].mFormat.mSampleRate > + formats[max_rate_format].mFormat.mSampleRate) + max_rate_format = j; } - if (i_requested_rate_format >= 0) /* We prefer to output at the samplerate of the original audio. */ - p->stream_format = - p_format_list[i_requested_rate_format].mFormat; - else if (i_current_rate_format >= 0) /* If not possible, we will try to use the current samplerate of the device. */ - p->stream_format = - p_format_list[i_current_rate_format].mFormat; + if (req_rate_format >= 0) + d->stream_format = formats[req_rate_format].mFormat; else - p->stream_format = p_format_list[i_backup_rate_format].mFormat; - /* And if we have to, any digital format will be just fine (highest rate possible). */ + d->stream_format = formats[max_rate_format].mFormat; + + free(formats); } - free(p_format_list); } - free(p_streams); - if (p->i_stream_index < 0) { - ca_msg(MSGL_WARN, - "Cannot find any digital output stream format when OpenSPDIF().\n"); - goto err_out; - } + free(streams); - ca_print_asbd("original stream format:", &p->sfmt_revert); + if (d->i_stream_index < 0) { + ca_msg(MSGL_WARN, "can't find any digital output stream format"); + goto coreaudio_error; + } - if (!AudioStreamChangeFormat(p->i_stream_id, p->stream_format)) - goto err_out; + if (!AudioStreamChangeFormat(d->i_stream_id, d->stream_format)) + goto coreaudio_error; - p_addr.mSelector = kAudioDevicePropertyDeviceHasChanged; - p_addr.mScope = kAudioObjectPropertyScopeGlobal; - p_addr.mElement = kAudioObjectPropertyElementMaster; + p_addr = (AudioObjectPropertyAddress) { + .mSelector = kAudioDevicePropertyDeviceHasChanged, + .mScope = kAudioObjectPropertyScopeGlobal, + .mElement = kAudioObjectPropertyElementMaster, + }; - const int *stream_format_changed = &(p->b_stream_format_changed); + const int *stream_format_changed = &(d->b_stream_format_changed); err = AudioObjectAddPropertyListener(p->i_selected_dev, &p_addr, ca_device_listener, (void *)stream_format_changed); - if (err != noErr) - ca_msg(MSGL_WARN, - "AudioDeviceAddPropertyListener for kAudioDevicePropertyDeviceHasChanged failed: [%4.4s]\n", - (char *)&err); - + CHECK_CA_ERROR("cannot install format change listener during init"); /* FIXME: If output stream is not native byte-order, we need change endian somewhere. */ /* Although there's no such case reported. */ @@ -666,60 +566,39 @@ static int OpenSPDIF(struct ao *ao) if (!(p->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)) #else /* tell mplayer that we need a byteswap on AC3 streams, */ - if (p->stream_format.mFormatID & kAudioFormat60958AC3) + if (d->stream_format.mFormatID & kAudioFormat60958AC3) ao->format = AF_FORMAT_AC3_LE; - - if (p->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian) -#endif + else if (d->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian) ca_msg(MSGL_WARN, - "Output stream has non-native byte order, digital output may fail.\n"); - + "Stream has non-native byte order, digital output may fail"); +#endif - ao->samplerate = p->stream_format.mSampleRate; - mp_chmap_from_channels(&ao->channels, p->stream_format.mChannelsPerFrame); + ao->samplerate = d->stream_format.mSampleRate; + mp_chmap_from_channels(&ao->channels, d->stream_format.mChannelsPerFrame); ao->bps = ao->samplerate * - (p->stream_format.mBytesPerPacket / - p->stream_format.mFramesPerPacket); - - p->buffer = mp_ring_new(p, get_ring_size(ao)); + (d->stream_format.mBytesPerPacket / + d->stream_format.mFramesPerPacket); + p->buffer = mp_ring_new(p, get_ring_size(ao)); print_buffer(p->buffer); - /* Create IOProc callback. */ err = AudioDeviceCreateIOProcID(p->i_selected_dev, (AudioDeviceIOProc)render_cb_digital, (void *)ao, - &p->renderCallback); + &d->renderCallback); - if (err != noErr || p->renderCallback == NULL) { - ca_msg(MSGL_WARN, "AudioDeviceAddIOProc failed: [%4.4s]\n", - (char *)&err); - goto err_out1; - } + CHECK_CA_ERROR("failed to register digital render callback"); reset(ao); return CONTROL_TRUE; -err_out1: - if (p->b_revert) - AudioStreamChangeFormat(p->i_stream_id, p->sfmt_revert); -err_out: - if (p->b_changed_mixing && p->sfmt_revert.mFormatID != - kAudioFormat60958AC3) { - int b_mix = 1; - err = SetAudioProperty(p->i_selected_dev, - kAudioDevicePropertySupportsMixing, - sizeof(int), &b_mix); - if (err != noErr) - ca_msg(MSGL_WARN, "failed to set mixmode: [%4.4s]\n", - (char *)&err); - } - if (p->i_hog_pid == getpid()) { - p->i_hog_pid = -1; +coreaudio_error: + if (d->i_hog_pid == getpid()) { + d->i_hog_pid = -1; err = SetAudioProperty(p->i_selected_dev, kAudioDevicePropertyHogMode, - sizeof(p->i_hog_pid), &p->i_hog_pid); + sizeof(d->i_hog_pid), &d->i_hog_pid); if (err != noErr) ca_msg(MSGL_WARN, "Could not release hogmode: [%4.4s]\n", (char *)&err); @@ -815,18 +694,18 @@ static int AudioStreamChangeFormat(AudioStreamID i_stream_id, static int play(struct ao *ao, void *output_samples, int num_bytes, int flags) { struct priv *p = ao->priv; - int wrote, b_digital; + struct priv_d *d = p->digital; // Check whether we need to reset the digital output stream. - if (p->b_digital && p->b_stream_format_changed) { - p->b_stream_format_changed = 0; - b_digital = AudioStreamSupportsDigital(p->i_stream_id); + if (p->b_digital && d->b_stream_format_changed) { + d->b_stream_format_changed = 0; + int b_digital = AudioStreamSupportsDigital(d->i_stream_id); if (b_digital) { /* Current stream supports digital format output, let's set it. */ ca_msg(MSGL_V, "Detected current stream supports digital, try to restore digital output...\n"); - if (!AudioStreamChangeFormat(p->i_stream_id, p->stream_format)) + if (!AudioStreamChangeFormat(d->i_stream_id, d->stream_format)) ca_msg(MSGL_WARN, "Restoring digital output failed.\n"); else { @@ -839,7 +718,7 @@ static int play(struct ao *ao, void *output_samples, int num_bytes, int flags) "Detected current stream does not support digital.\n"); } - wrote = mp_ring_write(p->buffer, output_samples, num_bytes); + int wrote = mp_ring_write(p->buffer, output_samples, num_bytes); audio_resume(ao); return wrote; @@ -888,24 +767,21 @@ static void uninit(struct ao *ao, bool immed) AudioUnitUninitialize(p->theOutputUnit); AudioComponentInstanceDispose(p->theOutputUnit); } else { + struct priv_d *d = p->digital; /* Stop device. */ - err = AudioDeviceStop(p->i_ |