mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-04 13:29:59 -05:00
stream-restore: Fix NULL preferred device handling
When an application sets a device for a newly created stream, we treat that as a temporary setting, and don't save it as the preferred device for future streams. The handling for this was broken, however: if the stream already had a preferred device saved in the stream-restore database, that was unset. This was a regression introduced inbc0e728320and70bbbcdc84. These commits tried to detect in subscribe_callback() when the preferred device is cleared, but as a side effect the preferred device started to get cleared from the database also when a stream was created with a device set by the application. There's no way for subscribe_callback() to distinguish the different cases of the preferred device being NULL. This problem is solved by using the PREFERRED_SINK/SOURCE_CHANGED hooks. The hooks are only called when the preferred device really changes. Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/1063 Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/535>
This commit is contained in:
parent
737ebcdfdb
commit
8a87af380a
1 changed files with 101 additions and 45 deletions
|
|
@ -1294,7 +1294,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
|||
/* These are only used when D-Bus is enabled, but in order to reduce ifdef
|
||||
* clutter these are defined here unconditionally. */
|
||||
bool created_new_entry = true;
|
||||
bool device_updated = false;
|
||||
bool volume_updated = false;
|
||||
bool mute_updated = false;
|
||||
|
||||
|
|
@ -1350,25 +1349,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
|||
|
||||
mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
|
||||
}
|
||||
|
||||
if (sink_input->preferred_sink != NULL || !created_new_entry) {
|
||||
pa_sink *s = NULL;
|
||||
|
||||
pa_xfree(entry->device);
|
||||
entry->device = pa_xstrdup(sink_input->preferred_sink);
|
||||
entry->device_valid = true;
|
||||
if (!entry->device)
|
||||
entry->device_valid = false;
|
||||
|
||||
device_updated = !created_new_entry && !pa_safe_streq(entry->device, old->device);
|
||||
pa_xfree(entry->card);
|
||||
entry->card = NULL;
|
||||
entry->card_valid = false;
|
||||
if (entry->device_valid && (s = pa_namereg_get(c, entry->device, PA_NAMEREG_SINK)) && s->card) {
|
||||
entry->card = pa_xstrdup(s->card->name);
|
||||
entry->card_valid = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pa_source_output *source_output;
|
||||
|
||||
|
|
@ -1410,26 +1390,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
|||
|
||||
mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
|
||||
}
|
||||
|
||||
if (source_output->preferred_source != NULL || !created_new_entry) {
|
||||
pa_source *s = NULL;
|
||||
|
||||
pa_xfree(entry->device);
|
||||
entry->device = pa_xstrdup(source_output->preferred_source);
|
||||
entry->device_valid = true;
|
||||
|
||||
if (!entry->device)
|
||||
entry->device_valid = false;
|
||||
|
||||
device_updated = !created_new_entry && !pa_safe_streq(entry->device, old->device);
|
||||
pa_xfree(entry->card);
|
||||
entry->card = NULL;
|
||||
entry->card_valid = false;
|
||||
if (entry->device_valid && (s = pa_namereg_get(c, entry->device, PA_NAMEREG_SOURCE)) && s->card) {
|
||||
entry->card = pa_xstrdup(s->card->name);
|
||||
entry->card_valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pa_assert(entry);
|
||||
|
|
@ -1446,12 +1406,12 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
|||
entry_free(old);
|
||||
}
|
||||
|
||||
pa_log_info("Storing volume/mute/device for stream %s.", name);
|
||||
pa_log_info("Storing volume/mute for stream %s.", name);
|
||||
|
||||
if (entry_write(u, name, entry, true)) {
|
||||
trigger_save(u);
|
||||
} else {
|
||||
pa_log_error("Could not store volume/mute/device for stream %s.", name);
|
||||
pa_log_error("Could not store volume/mute for stream %s.", name);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
|
|
@ -1460,8 +1420,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
|||
pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
|
||||
send_new_entry_signal(de);
|
||||
} else {
|
||||
if (device_updated)
|
||||
send_device_updated_signal(de, entry);
|
||||
if (volume_updated)
|
||||
send_volume_updated_signal(de, entry);
|
||||
if (mute_updated)
|
||||
|
|
@ -1469,7 +1427,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
|||
}
|
||||
#else
|
||||
/* Silence compiler warnings */
|
||||
(void) device_updated;
|
||||
(void) volume_updated;
|
||||
(void) mute_updated;
|
||||
#endif
|
||||
|
|
@ -1582,6 +1539,79 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
|
|||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static void update_preferred_device(struct userdata *u, const char *name, const char *device, const char *card) {
|
||||
struct entry *old;
|
||||
struct entry *entry;
|
||||
#ifdef HAVE_DBUS
|
||||
bool created_new_entry = false;
|
||||
struct dbus_entry *de;
|
||||
#endif
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(name);
|
||||
|
||||
if ((old = entry_read(u, name)))
|
||||
entry = entry_copy(old);
|
||||
else {
|
||||
entry = entry_new();
|
||||
#ifdef HAVE_DBUS
|
||||
created_new_entry = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
pa_xfree(entry->device);
|
||||
entry->device = pa_xstrdup(device);
|
||||
entry->device_valid = !!device;
|
||||
|
||||
pa_xfree(entry->card);
|
||||
entry->card = pa_xstrdup(card);
|
||||
entry->card_valid = !!card;
|
||||
|
||||
pa_log_info("Storing device for stream %s.", name);
|
||||
|
||||
entry_write(u, name, entry, true);
|
||||
trigger_save(u);
|
||||
|
||||
#if HAVE_DBUS
|
||||
if (!(de = pa_hashmap_get(u->dbus_entries, name))) {
|
||||
de = dbus_entry_new(u, name);
|
||||
pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
|
||||
send_new_entry_signal(de);
|
||||
} else {
|
||||
/* We send a D-Bus signal when the device changes, but not when the
|
||||
* card changes. That's becaues the D-Bus interface doesn't expose the
|
||||
* card field to clients at all. */
|
||||
if (!created_new_entry && !pa_safe_streq(entry->device, old->device))
|
||||
send_device_updated_signal(de, entry);
|
||||
}
|
||||
#endif
|
||||
|
||||
entry_free(entry);
|
||||
if (old)
|
||||
entry_free(old);
|
||||
}
|
||||
|
||||
static pa_hook_result_t sink_input_preferred_sink_changed_cb(pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
|
||||
char *name;
|
||||
pa_sink *sink;
|
||||
const char *card_name;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(sink_input);
|
||||
pa_assert(u);
|
||||
|
||||
if (!(name = pa_proplist_get_stream_group(sink_input->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
|
||||
return PA_HOOK_OK;
|
||||
|
||||
if (sink_input->preferred_sink && (sink = pa_namereg_get(c, sink_input->preferred_sink, PA_NAMEREG_SINK)) && sink->card)
|
||||
card_name = sink->card->name;
|
||||
|
||||
update_preferred_device(u, name, sink_input->preferred_sink, card_name);
|
||||
pa_xfree(name);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
|
||||
char *name;
|
||||
struct entry *e;
|
||||
|
|
@ -1690,6 +1720,27 @@ static pa_hook_result_t source_output_fixate_hook_callback(pa_core *c, pa_source
|
|||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static pa_hook_result_t source_output_preferred_source_changed_cb(pa_core *c, pa_source_output *source_output, struct userdata *u) {
|
||||
char *name;
|
||||
pa_source *source;
|
||||
const char *card_name;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(source_output);
|
||||
pa_assert(u);
|
||||
|
||||
if (!(name = pa_proplist_get_stream_group(source_output->proplist, "source-output", IDENTIFICATION_PROPERTY)))
|
||||
return PA_HOOK_OK;
|
||||
|
||||
if (source_output->preferred_source && (source = pa_namereg_get(c, source_output->preferred_source, PA_NAMEREG_SOURCE)) && source->card)
|
||||
card_name = source->card->name;
|
||||
|
||||
update_preferred_device(u, name, source_output->preferred_source, card_name);
|
||||
pa_xfree(name);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static int fill_db(struct userdata *u, const char *filename) {
|
||||
FILE *f;
|
||||
int n = 0;
|
||||
|
|
@ -2317,6 +2368,11 @@ int pa__init(pa_module*m) {
|
|||
/* A little bit earlier than module-intended-roles ... */
|
||||
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
|
||||
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
|
||||
|
||||
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PREFERRED_SINK_CHANGED], PA_HOOK_NORMAL,
|
||||
(pa_hook_cb_t) sink_input_preferred_sink_changed_cb, u);
|
||||
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PREFERRED_SOURCE_CHANGED], PA_HOOK_NORMAL,
|
||||
(pa_hook_cb_t) source_output_preferred_source_changed_cb, u);
|
||||
}
|
||||
|
||||
if (restore_volume || restore_muted) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue