From 70802d519f9130696959346aa413f7faeeed7621 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 14 Feb 2015 12:43:55 +0100 Subject: ao_coreaudio: add support for hotplug notifications This commit adds notifications for hot plugging of devices. It also extends the old behaviour of the `audio-out-detected-device` property which is now backed by the hotplugging code. This allows clients to be notified when the actual audio output device changes. Maybe hotplugging should be supported for ao_coreaudio_exclusive too, but it's device selection code is a bit fragile. --- TOOLS/lua/audio-hotplug-test.lua | 5 ++ audio/out/ao.c | 10 ++-- audio/out/ao.h | 2 +- audio/out/ao_coreaudio.c | 107 ++++++++++++++++++++++++++++++++------- player/command.c | 13 +++-- player/command.h | 1 + 6 files changed, 111 insertions(+), 27 deletions(-) diff --git a/TOOLS/lua/audio-hotplug-test.lua b/TOOLS/lua/audio-hotplug-test.lua index 8dedc68cbe..f27b793151 100644 --- a/TOOLS/lua/audio-hotplug-test.lua +++ b/TOOLS/lua/audio-hotplug-test.lua @@ -6,3 +6,8 @@ mp.observe_property("audio-device-list", "native", function(name, val) print(" - '" .. e.name .. "' (" .. e.description .. ")") end end) + +mp.observe_property("audio-out-detected-device", "native", function(name, val) + print("Detected audio device changed:") + print(" - '" .. val) +end) diff --git a/audio/out/ao.c b/audio/out/ao.c index a2fa2fb104..c7065f386a 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -450,11 +450,6 @@ bool ao_untimed(struct ao *ao) return ao->untimed; } -const char *ao_get_detected_device(struct ao *ao) -{ - return ao->detected_device; -} - // --- struct ao_hotplug { @@ -502,6 +497,11 @@ bool ao_hotplug_check_update(struct ao_hotplug *hp) return false; } +const char *ao_hotplug_get_detected_device(struct ao_hotplug *hp) +{ + return hp->ao->detected_device; +} + // The return value is valid until the next call to this API. struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp) { diff --git a/audio/out/ao.h b/audio/out/ao.h index 7b85ec80ba..3a028bb402 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -80,7 +80,6 @@ void ao_uninit(struct ao *ao); void ao_get_format(struct ao *ao, struct mp_audio *format); const char *ao_get_name(struct ao *ao); const char *ao_get_description(struct ao *ao); -const char *ao_get_detected_device(struct ao *ao); bool ao_untimed(struct ao *ao); int ao_play(struct ao *ao, void **data, int samples, int flags); int ao_control(struct ao *ao, enum aocontrol cmd, void *arg); @@ -100,6 +99,7 @@ struct ao_hotplug *ao_hotplug_create(struct mpv_global *global, struct input_ctx *input_ctx); void ao_hotplug_destroy(struct ao_hotplug *hp); bool ao_hotplug_check_update(struct ao_hotplug *hp); +const char *ao_hotplug_get_detected_device(struct ao_hotplug *hp); struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp); void ao_print_devices(struct mpv_global *global, struct mp_log *log); diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c index 0eab220421..99f0572723 100644 --- a/audio/out/ao_coreaudio.c +++ b/audio/out/ao_coreaudio.c @@ -143,14 +143,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) static bool init_chmap(struct ao *ao); static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd); -static int init(struct ao *ao) -{ - if (AF_FORMAT_IS_IEC61937(ao->format)) { - MP_WARN(ao, "detected IEC61937, redirecting to coreaudio_exclusive\n"); - ao->redirect = "coreaudio_exclusive"; - return CONTROL_ERROR; - } - +static bool reinit_device(struct ao *ao) { struct priv *p = ao->priv; OSStatus err = ca_select_device(ao, ao->device, &p->device); @@ -161,6 +154,23 @@ static int init(struct ao *ao) CHECK_CA_ERROR("failed to get device UID"); ao->detected_device = talloc_steal(ao, uid); + return true; + +coreaudio_error: + return false; +} + +static int init(struct ao *ao) +{ + if (AF_FORMAT_IS_IEC61937(ao->format)) { + MP_WARN(ao, "detected IEC61937, redirecting to coreaudio_exclusive\n"); + ao->redirect = "coreaudio_exclusive"; + return CONTROL_ERROR; + } + + if (!reinit_device(ao)) + goto coreaudio_error; + if (!init_chmap(ao)) goto coreaudio_error; @@ -361,6 +371,67 @@ static void uninit(struct ao *ao) AudioComponentInstanceDispose(p->audio_unit); } +static OSStatus hotplug_cb(AudioObjectID id, UInt32 naddr, + const AudioObjectPropertyAddress addr[], + void *ctx) { + reinit_device(ctx); + ao_hotplug_event(ctx); + return noErr; +} + +static uint32_t hotplug_properties[] = { + kAudioHardwarePropertyDevices, + kAudioHardwarePropertyDefaultOutputDevice +}; + +static int hotplug_init(struct ao *ao) +{ + if (!reinit_device(ao)) + goto coreaudio_error; + + OSStatus err = noErr; + for (int i = 0; i < MP_ARRAY_SIZE(hotplug_properties); i++) { + AudioObjectPropertyAddress addr = { + hotplug_properties[i], + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + err = AudioObjectAddPropertyListener( + kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao); + if (err != noErr) { + char *c1 = fourcc_repr(ao, hotplug_properties[i]); + char *c2 = fourcc_repr(ao, err); + MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2); + goto coreaudio_error; + } + } + + return 0; + +coreaudio_error: + return -1; +} + +static void hotplug_uninit(struct ao *ao) +{ + OSStatus err = noErr; + for (int i = 0; i < MP_ARRAY_SIZE(hotplug_properties); i++) { + AudioObjectPropertyAddress addr = { + hotplug_properties[i], + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + err = AudioObjectRemovePropertyListener( + kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao); + if (err != noErr) { + char *c1 = fourcc_repr(ao, hotplug_properties[i]); + char *c2 = fourcc_repr(ao, err); + MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2); + } + } +} + + // Channel Mapping functions static const int speaker_map[][2] = { { kAudioChannelLabel_Left, MP_SPEAKER_ID_FL }, @@ -513,13 +584,15 @@ coreaudio_error: #define OPT_BASE_STRUCT struct priv const struct ao_driver audio_out_coreaudio = { - .description = "CoreAudio AudioUnit", - .name = "coreaudio", - .uninit = uninit, - .init = init, - .control = control, - .pause = stop, - .resume = start, - .list_devs = ca_get_device_list, - .priv_size = sizeof(struct priv), + .description = "CoreAudio AudioUnit", + .name = "coreaudio", + .uninit = uninit, + .init = init, + .control = control, + .pause = stop, + .resume = start, + .hotplug_init = hotplug_init, + .hotplug_uninit = hotplug_uninit, + .list_devs = ca_get_device_list, + .priv_size = sizeof(struct priv), }; diff --git a/player/command.c b/player/command.c index 8e3d38ebfe..b8a0a7b5ce 100644 --- a/player/command.c +++ b/player/command.c @@ -1578,10 +1578,12 @@ static int mp_property_ao(void *ctx, struct m_property *p, int action, void *arg static int mp_property_ao_detected_device(void *ctx,struct m_property *prop, int action, void *arg) { - MPContext *mpctx = ctx; + struct MPContext *mpctx = ctx; + struct command_ctx *cmd = mpctx->command_ctx; if (!mpctx->ao) return M_PROPERTY_UNAVAILABLE; - return m_property_strdup_ro(action, arg, ao_get_detected_device(mpctx->ao)); + const char *d = ao_hotplug_get_detected_device(cmd->hotplug); + return m_property_strdup_ro(action, arg, d); } /// Audio delay (RW) @@ -3524,7 +3526,7 @@ static const char *const *const mp_event_property_change[] = { "detected-hwdec"), E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate", "samplerate", "channels", "audio", "volume", "mute", "balance", - "volume-restore-data", "current-ao", "audio-out-detected-device"), + "volume-restore-data", "current-ao"), E(MPV_EVENT_SEEK, "seeking", "core-idle"), E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle"), E(MPV_EVENT_METADATA_UPDATE, "metadata", "filtered-metadata"), @@ -3534,6 +3536,7 @@ static const char *const *const mp_event_property_change[] = { E(MP_EVENT_WIN_RESIZE, "window-scale"), E(MP_EVENT_WIN_STATE, "window-minimized", "display-names"), E(MP_EVENT_AUDIO_DEVICES, "audio-device-list"), + E(MP_EVENT_DETECTED_AUDIO_DEVICE, "audio-out-detected-device"), }; #undef E @@ -4875,8 +4878,10 @@ static void command_event(struct MPContext *mpctx, int event, void *arg) // This is a bit messy: ao_hotplug wakes up the player, and then we have // to recheck the state. Then the client(s) will read the property. - if (ctx->hotplug && ao_hotplug_check_update(ctx->hotplug)) + if (ctx->hotplug && ao_hotplug_check_update(ctx->hotplug)) { mp_notify_property(mpctx, "audio-device-list"); + mp_notify_property(mpctx, "audio-out-detected-device"); + } } void mp_notify(struct MPContext *mpctx, int event, void *arg) diff --git a/player/command.h b/player/command.h index 96a58b1f80..945d686786 100644 --- a/player/command.h +++ b/player/command.h @@ -48,6 +48,7 @@ enum { MP_EVENT_WIN_RESIZE, MP_EVENT_WIN_STATE, MP_EVENT_AUDIO_DEVICES, + MP_EVENT_DETECTED_AUDIO_DEVICE, }; bool mp_hook_test_completion(struct MPContext *mpctx, char *type); -- cgit v1.2.3