mirror of
https://github.com/mpv-player/mpv.git
synced 2026-10-16 22:40:53 +08:00
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:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user