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.
This commit is contained in:
Stefano Pigozzi
2015-02-14 12:43:55 +01:00
parent 9aaec7cffb
commit 70802d519f
6 changed files with 111 additions and 27 deletions
+5
View File
@@ -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)
+5 -5
View File
@@ -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)
{
+1 -1
View File
@@ -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);
+90 -17
View File
@@ -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),
};
+9 -4
View File
@@ -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)
+1
View File
@@ -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);