From 7556f367d6bfde152570b84d48da7f3f8b066f53 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 2 Jun 2015 21:13:44 +0200 Subject: ao_coreaudio_exclusive: move generic functions to utils --- audio/out/ao_coreaudio_exclusive.c | 208 +------------------------------------ audio/out/ao_coreaudio_utils.c | 198 ++++++++++++++++++++++++++++++++++- audio/out/ao_coreaudio_utils.h | 10 ++ 3 files changed, 209 insertions(+), 207 deletions(-) (limited to 'audio') diff --git a/audio/out/ao_coreaudio_exclusive.c b/audio/out/ao_coreaudio_exclusive.c index a08db7622d..e96c73c7ed 100644 --- a/audio/out/ao_coreaudio_exclusive.c +++ b/audio/out/ao_coreaudio_exclusive.c @@ -42,213 +42,11 @@ #include "audio/format.h" #include "osdep/timer.h" #include "osdep/atomics.h" -#include "osdep/semaphore.h" #include "options/m_option.h" #include "common/msg.h" #include "audio/out/ao_coreaudio_properties.h" #include "audio/out/ao_coreaudio_utils.h" -static bool ca_format_is_compressed(AudioStreamBasicDescription asbd) -{ - switch (asbd.mFormatID) - case 'IAC3': - case 'iac3': - case kAudioFormat60958AC3: - case kAudioFormatAC3: - return true; - return false; -} - -static bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream) -{ - AudioStreamRangedDescription *formats = NULL; - size_t n_formats; - - OSStatus err = - CA_GET_ARY(stream, kAudioStreamPropertyAvailablePhysicalFormats, - &formats, &n_formats); - - CHECK_CA_ERROR("Could not get number of stream formats."); - - for (int i = 0; i < n_formats; i++) { - AudioStreamBasicDescription asbd = formats[i].mFormat; - ca_print_asbd(ao, "supported format:", &(asbd)); - if (ca_format_is_compressed(asbd)) { - talloc_free(formats); - return true; - } - } - - talloc_free(formats); -coreaudio_error: - return false; -} - -static bool ca_device_supports_compressed(struct ao *ao, AudioDeviceID device) -{ - AudioStreamID *streams = NULL; - size_t n_streams; - - /* Retrieve all the output streams. */ - OSStatus err = - CA_GET_ARY_O(device, kAudioDevicePropertyStreams, &streams, &n_streams); - - CHECK_CA_ERROR("could not get number of streams."); - - for (int i = 0; i < n_streams; i++) { - if (ca_stream_supports_compressed(ao, streams[i])) { - talloc_free(streams); - return true; - } - } - - talloc_free(streams); - -coreaudio_error: - return false; -} - -static OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid) { - *pid = getpid(); - OSStatus err = CA_SET(device, kAudioDevicePropertyHogMode, pid); - if (err != noErr) - *pid = -1; - - return err; -} - -static OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid) { - if (*pid == getpid()) { - *pid = -1; - return CA_SET(device, kAudioDevicePropertyHogMode, &pid); - } - return noErr; -} - -static OSStatus ca_change_mixing(struct ao *ao, AudioDeviceID device, - uint32_t val, bool *changed) { - *changed = false; - - AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) { - .mSelector = kAudioDevicePropertySupportsMixing, - .mScope = kAudioObjectPropertyScopeGlobal, - .mElement = kAudioObjectPropertyElementMaster, - }; - - if (AudioObjectHasProperty(device, &p_addr)) { - OSStatus err; - Boolean writeable = 0; - err = CA_SETTABLE(device, kAudioDevicePropertySupportsMixing, - &writeable); - - if (!CHECK_CA_WARN("can't tell if mixing property is settable")) { - return err; - } - - if (!writeable) - return noErr; - - err = CA_SET(device, kAudioDevicePropertySupportsMixing, &val); - if (err != noErr) - return err; - - if (!CHECK_CA_WARN("can't set mix mode")) { - return err; - } - - *changed = true; - } - - return noErr; -} - -static OSStatus ca_disable_mixing(struct ao *ao, - AudioDeviceID device, bool *changed) { - return ca_change_mixing(ao, device, 0, changed); -} - -static OSStatus ca_enable_mixing(struct ao *ao, - AudioDeviceID device, bool changed) { - if (changed) { - bool dont_care = false; - return ca_change_mixing(ao, device, 1, &dont_care); - } - - return noErr; -} - -static OSStatus ca_change_format_listener( - AudioObjectID object, uint32_t n_addresses, - const AudioObjectPropertyAddress addresses[], - void *data) -{ - sem_t *sem = data; - sem_post(sem); - return noErr; -} - -static bool ca_change_format(struct ao *ao, AudioStreamID stream, - AudioStreamBasicDescription change_format) -{ - OSStatus err = noErr; - bool format_set = false; - - ca_print_asbd(ao, "setting stream format:", &change_format); - - sem_t wakeup; - if (sem_init(&wakeup, 0, 0)) { - MP_WARN(ao, "OOM\n"); - return false; - } - - /* Install the callback. */ - AudioObjectPropertyAddress p_addr = { - .mSelector = kAudioStreamPropertyPhysicalFormat, - .mScope = kAudioObjectPropertyScopeGlobal, - .mElement = kAudioObjectPropertyElementMaster, - }; - - err = AudioObjectAddPropertyListener(stream, &p_addr, - ca_change_format_listener, - &wakeup); - CHECK_CA_ERROR("can't add property listener during format change"); - - /* Change the format. */ - err = CA_SET(stream, kAudioStreamPropertyPhysicalFormat, &change_format); - CHECK_CA_ERROR("error changing physical format"); - - /* The AudioStreamSetProperty is not only asynchronous, - * it is also not Atomic, in its behaviour. */ - struct timespec timeout = mp_rel_time_to_timespec(0.5); - AudioStreamBasicDescription actual_format = {0}; - while (1) { - err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format); - if (!CHECK_CA_WARN("could not retrieve physical format")) - break; - - format_set = ca_asbd_equals(&change_format, &actual_format); - if (format_set) - break; - - if (sem_timedwait(&wakeup, &timeout)) { - MP_VERBOSE(ao, "reached timeout\n"); - break; - } - } - - ca_print_asbd(ao, "actual format in use:", &actual_format); - - err = AudioObjectRemovePropertyListener(stream, &p_addr, - ca_change_format_listener, - &wakeup); - CHECK_CA_ERROR("can't remove property listener"); - -coreaudio_error: - sem_destroy(&wakeup); - return format_set; -} - - struct priv { AudioDeviceID device; // selected device @@ -428,7 +226,7 @@ static int init(struct ao *ao) p->stream_idx = i; for (int j = 0; j < n_formats; j++) - if (ca_format_is_compressed(formats[j].mFormat)) { + if (ca_formatid_is_compressed(formats[j].mFormat.mFormatID)) { // select the compressed format that has exactly the same // samplerate. If an exact match cannot be found, select // the format with highest samplerate as backup. @@ -461,7 +259,7 @@ static int init(struct ao *ao) &p->original_asbd); CHECK_CA_ERROR("could not get stream's original physical format"); - if (!ca_change_format(ao, p->stream, p->stream_asbd)) + if (!ca_change_physical_format_sync(ao, p->stream, p->stream_asbd)) goto coreaudio_error; err = enable_property_listener(ao, true); @@ -523,7 +321,7 @@ static void uninit(struct ao *ao) err = AudioDeviceDestroyIOProcID(p->device, p->render_cb); CHECK_CA_WARN("failed to remove device render callback"); - if (!ca_change_format(ao, p->stream, p->original_asbd)) + if (!ca_change_physical_format_sync(ao, p->stream, p->original_asbd)) MP_WARN(ao, "can't revert to original device format"); err = ca_enable_mixing(ao, p->device, p->changed_mixing); diff --git a/audio/out/ao_coreaudio_utils.c b/audio/out/ao_coreaudio_utils.c index 6c26dbb7c3..912497b2a2 100644 --- a/audio/out/ao_coreaudio_utils.c +++ b/audio/out/ao_coreaudio_utils.c @@ -28,6 +28,7 @@ #include "audio/out/ao_coreaudio_properties.h" #include "osdep/timer.h" #include "osdep/endian.h" +#include "osdep/semaphore.h" #include "audio/format.h" CFStringRef cfstr_from_cstr(char *str) @@ -194,7 +195,7 @@ void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd) ca_fill_asbd_raw(asbd, ao->format, ao->samplerate, ao->channels.num); } -static bool ca_formatid_is_digital(uint32_t formatid) +bool ca_formatid_is_compressed(uint32_t formatid) { switch (formatid) case 'IAC3': @@ -208,7 +209,7 @@ static bool ca_formatid_is_digital(uint32_t formatid) // This might be wrong, but for now it's sufficient for us. static uint32_t ca_normalize_formatid(uint32_t formatID) { - return ca_formatid_is_digital(formatID) ? kAudioFormat60958AC3 : formatID; + return ca_formatid_is_compressed(formatID) ? kAudioFormat60958AC3 : formatID; } bool ca_asbd_equals(const AudioStreamBasicDescription *a, @@ -317,3 +318,196 @@ int64_t ca_get_latency(const AudioTimeStamp *ts) return (out - now) * 1e-3; } + +bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream) +{ + AudioStreamRangedDescription *formats = NULL; + size_t n_formats; + + OSStatus err = + CA_GET_ARY(stream, kAudioStreamPropertyAvailablePhysicalFormats, + &formats, &n_formats); + + CHECK_CA_ERROR("Could not get number of stream formats."); + + for (int i = 0; i < n_formats; i++) { + AudioStreamBasicDescription asbd = formats[i].mFormat; + ca_print_asbd(ao, "supported format:", &(asbd)); + if (ca_formatid_is_compressed(asbd.mFormatID)) { + talloc_free(formats); + return true; + } + } + + talloc_free(formats); +coreaudio_error: + return false; +} + +bool ca_device_supports_compressed(struct ao *ao, AudioDeviceID device) +{ + AudioStreamID *streams = NULL; + size_t n_streams; + + /* Retrieve all the output streams. */ + OSStatus err = + CA_GET_ARY_O(device, kAudioDevicePropertyStreams, &streams, &n_streams); + + CHECK_CA_ERROR("could not get number of streams."); + + for (int i = 0; i < n_streams; i++) { + if (ca_stream_supports_compressed(ao, streams[i])) { + talloc_free(streams); + return true; + } + } + + talloc_free(streams); + +coreaudio_error: + return false; +} + +OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid) +{ + *pid = getpid(); + OSStatus err = CA_SET(device, kAudioDevicePropertyHogMode, pid); + if (err != noErr) + *pid = -1; + + return err; +} + +OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid) +{ + if (*pid == getpid()) { + *pid = -1; + return CA_SET(device, kAudioDevicePropertyHogMode, &pid); + } + return noErr; +} + +static OSStatus ca_change_mixing(struct ao *ao, AudioDeviceID device, + uint32_t val, bool *changed) +{ + *changed = false; + + AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) { + .mSelector = kAudioDevicePropertySupportsMixing, + .mScope = kAudioObjectPropertyScopeGlobal, + .mElement = kAudioObjectPropertyElementMaster, + }; + + if (AudioObjectHasProperty(device, &p_addr)) { + OSStatus err; + Boolean writeable = 0; + err = CA_SETTABLE(device, kAudioDevicePropertySupportsMixing, + &writeable); + + if (!CHECK_CA_WARN("can't tell if mixing property is settable")) { + return err; + } + + if (!writeable) + return noErr; + + err = CA_SET(device, kAudioDevicePropertySupportsMixing, &val); + if (err != noErr) + return err; + + if (!CHECK_CA_WARN("can't set mix mode")) { + return err; + } + + *changed = true; + } + + return noErr; +} + +OSStatus ca_disable_mixing(struct ao *ao, AudioDeviceID device, bool *changed) +{ + return ca_change_mixing(ao, device, 0, changed); +} + +OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed) +{ + if (changed) { + bool dont_care = false; + return ca_change_mixing(ao, device, 1, &dont_care); + } + + return noErr; +} + +static OSStatus ca_change_format_listener( + AudioObjectID object, uint32_t n_addresses, + const AudioObjectPropertyAddress addresses[], + void *data) +{ + sem_t *sem = data; + sem_post(sem); + return noErr; +} + +bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream, + AudioStreamBasicDescription change_format) +{ + OSStatus err = noErr; + bool format_set = false; + + ca_print_asbd(ao, "setting stream format:", &change_format); + + sem_t wakeup; + if (sem_init(&wakeup, 0, 0)) { + MP_WARN(ao, "OOM\n"); + return false; + } + + /* Install the callback. */ + AudioObjectPropertyAddress p_addr = { + .mSelector = kAudioStreamPropertyPhysicalFormat, + .mScope = kAudioObjectPropertyScopeGlobal, + .mElement = kAudioObjectPropertyElementMaster, + }; + + err = AudioObjectAddPropertyListener(stream, &p_addr, + ca_change_format_listener, + &wakeup); + CHECK_CA_ERROR("can't add property listener during format change"); + + /* Change the format. */ + err = CA_SET(stream, kAudioStreamPropertyPhysicalFormat, &change_format); + CHECK_CA_ERROR("error changing physical format"); + + /* The AudioStreamSetProperty is not only asynchronous, + * it is also not Atomic, in its behaviour. */ + struct timespec timeout = mp_rel_time_to_timespec(0.5); + AudioStreamBasicDescription actual_format = {0}; + while (1) { + err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format); + if (!CHECK_CA_WARN("could not retrieve physical format")) + break; + + format_set = ca_asbd_equals(&change_format, &actual_format); + if (format_set) + break; + + if (sem_timedwait(&wakeup, &timeout)) { + MP_VERBOSE(ao, "reached timeout\n"); + break; + } + } + + ca_print_asbd(ao, "actual format in use:", &actual_format); + + err = AudioObjectRemovePropertyListener(stream, &p_addr, + ca_change_format_listener, + &wakeup); + CHECK_CA_ERROR("can't remove property listener"); + +coreaudio_error: + sem_destroy(&wakeup); + return format_set; +} + diff --git a/audio/out/ao_coreaudio_utils.h b/audio/out/ao_coreaudio_utils.h index 50498a969e..477531e9f2 100644 --- a/audio/out/ao_coreaudio_utils.h +++ b/audio/out/ao_coreaudio_utils.h @@ -54,6 +54,7 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message); void ca_get_device_list(struct ao *ao, struct ao_device_list *list); OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device); +bool ca_formatid_is_compressed(uint32_t formatid); void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd); void ca_print_asbd(struct ao *ao, const char *description, const AudioStreamBasicDescription *asbd); @@ -67,4 +68,13 @@ bool ca_asbd_is_better(AudioStreamBasicDescription *req, int64_t ca_frames_to_us(struct ao *ao, uint32_t frames); int64_t ca_get_latency(const AudioTimeStamp *ts); +bool ca_device_supports_compressed(struct ao *ao, AudioDeviceID device); +bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream); +OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid); +OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid); +OSStatus ca_disable_mixing(struct ao *ao, AudioDeviceID device, bool *changed); +OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed); +bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream, + AudioStreamBasicDescription change_format); + #endif /* MPV_COREAUDIO_UTILS_H */ -- cgit v1.2.3