summaryrefslogtreecommitdiffstats
path: root/audio/out/ao_coreaudio_device.c
diff options
context:
space:
mode:
authorStefano Pigozzi <stefano.pigozzi@gmail.com>2014-07-01 19:05:43 +0200
committerStefano Pigozzi <stefano.pigozzi@gmail.com>2014-07-02 21:43:07 +0200
commitf317d24a3972b65349d7860da48822fbd058f36c (patch)
treeff63e78cf4a7951db1cb4d9dcdeb7b3486bc4534 /audio/out/ao_coreaudio_device.c
parenta8ef70b0f8a5d010be5810e52e4210967079e806 (diff)
downloadmpv-f317d24a3972b65349d7860da48822fbd058f36c.tar.bz2
mpv-f317d24a3972b65349d7860da48822fbd058f36c.tar.xz
ao_coreaudio: move device related functions to the new AO
Diffstat (limited to 'audio/out/ao_coreaudio_device.c')
-rw-r--r--audio/out/ao_coreaudio_device.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/audio/out/ao_coreaudio_device.c b/audio/out/ao_coreaudio_device.c
index 89fd9305f0..a0610a34d5 100644
--- a/audio/out/ao_coreaudio_device.c
+++ b/audio/out/ao_coreaudio_device.c
@@ -50,6 +50,261 @@ static void audio_pause(struct ao *ao);
static void audio_resume(struct ao *ao);
static void reset(struct ao *ao);
+static bool ca_format_is_digital(AudioStreamBasicDescription asbd)
+{
+ switch (asbd.mFormatID)
+ case 'IAC3':
+ case 'iac3':
+ case kAudioFormat60958AC3:
+ case kAudioFormatAC3:
+ return true;
+ return false;
+}
+
+static bool ca_stream_supports_digital(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_digital(asbd)) {
+ talloc_free(formats);
+ return true;
+ }
+ }
+
+ talloc_free(formats);
+coreaudio_error:
+ return false;
+}
+
+static bool ca_device_supports_digital(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_digital(ao, streams[i])) {
+ talloc_free(streams);
+ return true;
+ }
+ }
+
+ talloc_free(streams);
+
+coreaudio_error:
+ return false;
+}
+
+static OSStatus ca_property_listener(
+ AudioObjectPropertySelector selector,
+ AudioObjectID object, uint32_t n_addresses,
+ const AudioObjectPropertyAddress addresses[],
+ void *data)
+{
+ void *talloc_ctx = talloc_new(NULL);
+
+ for (int i = 0; i < n_addresses; i++) {
+ if (addresses[i].mSelector == selector) {
+ if (data) *(volatile int *)data = 1;
+ break;
+ }
+ }
+ talloc_free(talloc_ctx);
+ return noErr;
+}
+
+static OSStatus ca_stream_listener(
+ AudioObjectID object, uint32_t n_addresses,
+ const AudioObjectPropertyAddress addresses[],
+ void *data)
+{
+ return ca_property_listener(kAudioStreamPropertyPhysicalFormat,
+ object, n_addresses, addresses, data);
+}
+
+static OSStatus ca_device_listener(
+ AudioObjectID object, uint32_t n_addresses,
+ const AudioObjectPropertyAddress addresses[],
+ void *data)
+{
+ return ca_property_listener(kAudioDevicePropertyDeviceHasChanged,
+ object, n_addresses, addresses, data);
+}
+
+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_device_listening(AudioDeviceID device,
+ void *flag, bool enabled)
+{
+ AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
+ .mSelector = kAudioDevicePropertyDeviceHasChanged,
+ .mScope = kAudioObjectPropertyScopeGlobal,
+ .mElement = kAudioObjectPropertyElementMaster,
+ };
+
+ if (enabled) {
+ return AudioObjectAddPropertyListener(
+ device, &p_addr, ca_device_listener, flag);
+ } else {
+ return AudioObjectRemovePropertyListener(
+ device, &p_addr, ca_device_listener, flag);
+ }
+}
+
+static OSStatus ca_enable_device_listener(AudioDeviceID device, void *flag) {
+ return ca_change_device_listening(device, flag, true);
+}
+
+static OSStatus ca_disable_device_listener(AudioDeviceID device, void *flag) {
+ return ca_change_device_listening(device, flag, false);
+}
+
+static bool ca_change_format(struct ao *ao, AudioStreamID stream,
+ AudioStreamBasicDescription change_format)
+{
+ OSStatus err = noErr;
+ AudioObjectPropertyAddress p_addr;
+ volatile int stream_format_changed = 0;
+
+ ca_print_asbd(ao, "setting stream format:", &change_format);
+
+ /* Install the callback. */
+ p_addr = (AudioObjectPropertyAddress) {
+ .mSelector = kAudioStreamPropertyPhysicalFormat,
+ .mScope = kAudioObjectPropertyScopeGlobal,
+ .mElement = kAudioObjectPropertyElementMaster,
+ };
+
+ err = AudioObjectAddPropertyListener(stream, &p_addr, ca_stream_listener,
+ (void *)&stream_format_changed);
+ if (!CHECK_CA_WARN("can't add property listener during format change")) {
+ return false;
+ }
+
+ /* Change the format. */
+ err = CA_SET(stream, kAudioStreamPropertyPhysicalFormat, &change_format);
+ if (!CHECK_CA_WARN("error changing physical format")) {
+ return false;
+ }
+
+ /* The AudioStreamSetProperty is not only asynchronious,
+ * it is also not Atomic, in its behaviour.
+ * Therefore we check 5 times before we really give up. */
+ bool format_set = false;
+ for (int i = 0; !format_set && i < 5; i++) {
+ for (int j = 0; !stream_format_changed && j < 50; j++)
+ mp_sleep_us(10000);
+
+ if (stream_format_changed) {
+ stream_format_changed = 0;
+ } else {
+ MP_VERBOSE(ao, "reached timeout\n");
+ }
+
+ AudioStreamBasicDescription actual_format;
+ err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format);
+
+ ca_print_asbd(ao, "actual format in use:", &actual_format);
+ if (actual_format.mSampleRate == change_format.mSampleRate &&
+ actual_format.mFormatID == change_format.mFormatID &&
+ actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
+ format_set = true;
+ }
+ }
+
+ err = AudioObjectRemovePropertyListener(stream, &p_addr, ca_stream_listener,
+ (void *)&stream_format_changed);
+
+ if (!CHECK_CA_WARN("can't remove property listener")) {
+ return false;
+ }
+
+ return format_set;
+}
+
+
struct priv {
AudioDeviceID device; // selected device