Discussion:
[pulseaudio-discuss] [PATCH 0/4] The tentative fix of the issue #185
Hui Wang
2018-11-05 01:47:12 UTC
Permalink
This is a tentative fix of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/185

The patch is implemented based on Tanu's design:
https://lists.freedesktop.org/archives/pulseaudio-discuss/2018-October/030531.html

So far, the patches only cover the sink and sink_input, don't touch
the source and source_output yet. Based on these patches, I tested the switch
between HDMI output and speaker, it worked as expected. If the way of changing
sink/sink_input is basically correct and is worth to apply to the
source/source_output, I will continue to write patches for them too.


Hui Wang (4):
change bool save_sink to char *preferred_sink
move stream after default sink is changed.
move streams to new appeared sinks if they prefer these sinks
move stream when the active_port changes

src/modules/dbus/iface-core.c | 2 +-
src/modules/module-default-device-restore.c | 2 +-
src/modules/module-device-manager.c | 2 +-
src/modules/module-intended-roles.c | 2 +-
src/modules/module-stream-restore.c | 47 ++++++++----
src/modules/module-switch-on-connect.c | 28 +------
src/pulsecore/cli-command.c | 2 +-
src/pulsecore/core.c | 9 ++-
src/pulsecore/core.h | 4 +-
src/pulsecore/device-port.c | 18 ++++-
src/pulsecore/protocol-native.c | 2 +-
src/pulsecore/sink-input.c | 25 +++++--
src/pulsecore/sink-input.h | 7 +-
src/pulsecore/sink.c | 83 ++++++++++++++++++++-
src/pulsecore/sink.h | 3 +
15 files changed, 172 insertions(+), 64 deletions(-)
--
2.17.1
Hui Wang
2018-11-05 01:47:13 UTC
Permalink
And don't move the stream in the module-stream-restore anymore,
And the preferred_sink is only set when user is calling the
move_to() and the module-stream-restore maintains the saving and
deleting of preferred_sink.

If the target of move_to() is default_sink, preferred_sink will be
cleared and the entry->device will be cleared too from database.

Signed-off-by: Hui Wang <***@canonical.com>
---
src/modules/module-device-manager.c | 2 +-
src/modules/module-intended-roles.c | 2 +-
src/modules/module-stream-restore.c | 47 +++++++++++++++++---------
src/modules/module-switch-on-connect.c | 2 +-
src/pulsecore/sink-input.c | 25 +++++++++++---
src/pulsecore/sink-input.h | 7 ++--
6 files changed, 59 insertions(+), 26 deletions(-)

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 15fdaaaa2..36e71584e 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -657,7 +657,7 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) {
pa_assert(u->do_routing);

/* Don't override user or application routing requests. */
- if (si->save_sink || si->sink_requested_by_application)
+ if (si->preferred_sink != NULL || si->sink_requested_by_application)
return;

/* Skip this if it is already in the process of being moved anyway */
diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c
index adee51c20..98e4c241f 100644
--- a/src/modules/module-intended-roles.c
+++ b/src/modules/module-intended-roles.c
@@ -175,7 +175,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
if (si->sink == sink)
continue;

- if (si->save_sink)
+ if (si->preferred_sink != NULL)
continue;

/* Skip this if it is already in the process of being moved
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 228e9e447..276957c25 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -1311,19 +1311,29 @@ 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->save_sink) {
+ if (sink_input->preferred_sink != NULL || !created_new_entry) {
pa_xfree(entry->device);
- entry->device = pa_xstrdup(sink_input->sink->name);
- entry->device_valid = true;

- device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry->device, old->device));
- if (sink_input->sink->card) {
+ if (sink_input->preferred_sink != NULL) {
+ entry->device = pa_xstrdup(sink_input->preferred_sink);
+ entry->device_valid = true;
+ } else {
+ entry->device = NULL;
+ entry->device_valid = false;
+ }
+
+ device_updated = !created_new_entry && (!old->device_valid || !entry->device_valid || !pa_streq(entry->device, old->device));
+
+ if (entry->device_valid == false) {
+ pa_xfree(entry->card);
+ entry->card = NULL;
+ entry->card_valid = false;
+ } else if (sink_input->sink->card) {
pa_xfree(entry->card);
entry->card = pa_xstrdup(sink_input->sink->card->name);
entry->card_valid = true;
}
}
-
} else {
pa_source_output *source_output;

@@ -1641,7 +1651,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
if (si->sink == sink)
continue;

- if (si->save_sink)
+ if (si->preferred_sink != NULL)
continue;

/* Skip this if it is already in the process of being moved
@@ -1665,7 +1675,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct

if ((e = entry_read(u, name))) {
if (e->device_valid && pa_streq(e->device, sink->name))
- pa_sink_input_move_to(si, sink, true);
+ si->preferred_sink = pa_xstrdup(sink->name);

entry_free(e);
}
@@ -1764,8 +1774,10 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str

if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) &&
d != sink &&
- PA_SINK_IS_LINKED(d->state))
- pa_sink_input_move_to(si, d, true);
+ PA_SINK_IS_LINKED(d->state)) {
+ pa_xfree(si->preferred_sink);
+ si->preferred_sink = pa_xstrdup(d->name);
+ }
}

entry_free(e);
@@ -1942,12 +1954,13 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) {

if (u->restore_device) {
if (!e->device_valid) {
- if (si->save_sink) {
+ if (si->preferred_sink != NULL) {
pa_log_info("Ensuring device is not saved for stream %s.", name);
/* If the device is not valid we should make sure the
save flag is cleared as the user may have specifically
removed the sink element from the rule. */
- si->save_sink = false;
+ pa_xfree(si->preferred_sink);
+ si->preferred_sink = NULL;
/* This is cheating a bit. The sink input itself has not changed
but the rules governing its routing have, so we fire this event
such that other routing modules (e.g. module-device-manager)
@@ -1956,7 +1969,8 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) {
}
} else if ((s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
pa_log_info("Restoring device for stream %s.", name);
- pa_sink_input_move_to(si, s, true);
+ pa_xfree(si->preferred_sink);
+ si->preferred_sink = pa_xstrdup(s->name);
}
}
}
@@ -2176,9 +2190,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio

entry->muted = muted;
entry->muted_valid = true;
-
- entry->device = pa_xstrdup(device);
- entry->device_valid = device && !!entry->device[0];
+ if (device && !pa_streq(device, m->core->default_sink->name) && !pa_streq(device, m->core->default_source->name)) {
+ entry->device = pa_xstrdup(device);
+ entry->device_valid = device && !!entry->device[0];
+ }

if (entry->device_valid && !pa_namereg_is_valid_name(entry->device)) {
entry_free(entry);
diff --git a/src/modules/module-switch-on-connect.c b/src/modules/module-switch-on-connect.c
index ebdd6aad0..faa0f289f 100644
--- a/src/modules/module-switch-on-connect.c
+++ b/src/modules/module-switch-on-connect.c
@@ -112,7 +112,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void*
}

PA_IDXSET_FOREACH(i, old_default_sink->inputs, idx) {
- if (i->save_sink || !PA_SINK_INPUT_IS_LINKED(i->state))
+ if (i->preferred_sink != NULL || !PA_SINK_INPUT_IS_LINKED(i->state))
continue;

if (pa_sink_input_move_to(i, sink, false) < 0)
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 312ec4a97..1031051a7 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -190,7 +190,8 @@ bool pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, b
if (!data->req_formats) {
/* We're not working with the extended API */
data->sink = s;
- data->save_sink = save;
+ if (save)
+ data->preferred_sink = pa_xstrdup(s->name);
data->sink_requested_by_application = requested_by_application;
} else {
/* Extended API: let's see if this sink supports the formats the client can provide */
@@ -199,7 +200,8 @@ bool pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, b
if (formats && !pa_idxset_isempty(formats)) {
/* Sink supports at least one of the requested formats */
data->sink = s;
- data->save_sink = save;
+ if (save)
+ data->preferred_sink = pa_xstrdup(s->name);
data->sink_requested_by_application = requested_by_application;
if (data->nego_formats)
pa_idxset_free(data->nego_formats, (pa_free_cb_t) pa_format_info_free);
@@ -226,7 +228,7 @@ bool pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset

if (data->sink) {
/* Trigger format negotiation */
- return pa_sink_input_new_data_set_sink(data, data->sink, data->save_sink, data->sink_requested_by_application);
+ return pa_sink_input_new_data_set_sink(data, data->sink, false, data->sink_requested_by_application);
}

return true;
@@ -519,7 +521,7 @@ int pa_sink_input_new(
pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
i->volume_writable = data->volume_writable;
i->save_volume = data->save_volume;
- i->save_sink = data->save_sink;
+ i->preferred_sink = pa_xstrdup(data->preferred_sink);
i->save_muted = data->save_muted;

i->muted = data->muted;
@@ -777,6 +779,9 @@ static void sink_input_free(pa_object *o) {
if (i->volume_factor_sink_items)
pa_hashmap_free(i->volume_factor_sink_items);

+ if (i->preferred_sink)
+ pa_xfree(i->preferred_sink);
+
pa_xfree(i->driver);
pa_xfree(i);
}
@@ -1914,7 +1919,17 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save) {
i->moving(i, dest);

i->sink = dest;
- i->save_sink = save;
+
+ /* save == true, means user is calling the move_to() and want to
+ save the preferred_sinke if the sink is not default_sink */
+ if (save) {
+ pa_xfree(i->preferred_sink);
+ if (dest == dest->core->default_sink)
+ i->preferred_sink = NULL;
+ else
+ i->preferred_sink = pa_xstrdup(dest->name);
+ }
+
pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);

PA_HASHMAP_FOREACH(v, i->volume_factor_sink_items, state)
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 9e90291ca..537cec9a1 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -123,7 +123,8 @@ struct pa_sink_input {
* set is worth remembering, i.e. was explicitly chosen by the
* user and not automatically. module-stream-restore looks for
* this.*/
- bool save_sink:1, save_volume:1, save_muted:1;
+ char *preferred_sink;
+ bool save_volume:1, save_muted:1;

pa_resample_method_t requested_resample_method, actual_resample_method;

@@ -300,6 +301,8 @@ typedef struct pa_sink_input_new_data {
pa_idxset *req_formats;
pa_idxset *nego_formats;

+ char *preferred_sink;
+
pa_cvolume volume;
bool muted:1;
pa_hashmap *volume_factor_items, *volume_factor_sink_items;
@@ -314,7 +317,7 @@ typedef struct pa_sink_input_new_data {

bool volume_writable:1;

- bool save_sink:1, save_volume:1, save_muted:1;
+ bool save_volume:1, save_muted:1;
} pa_sink_input_new_data;

pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
--
2.17.1
Hui Wang
2018-11-05 01:47:14 UTC
Permalink
When default sink changes, the stream should be moved to new
default_sink too. If it is user to call change default function,
all stream will move to new default sink unconditionally; if it
is not, will check if stream binds to old_default_sink and the
active_port staus of old_default_sink, then it will move the
stream conditionally.

Signed-off-by: Hui Wang <***@canonical.com>
---
src/modules/dbus/iface-core.c | 2 +-
src/modules/module-default-device-restore.c | 2 +-
src/modules/module-switch-on-connect.c | 28 ++-------------
src/pulsecore/cli-command.c | 2 +-
src/pulsecore/core.c | 9 +++--
src/pulsecore/core.h | 4 +--
src/pulsecore/device-port.c | 2 +-
src/pulsecore/protocol-native.c | 2 +-
src/pulsecore/sink.c | 39 +++++++++++++++++++--
src/pulsecore/sink.h | 1 +
10 files changed, 52 insertions(+), 39 deletions(-)

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 5229c0467..9763480d2 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -721,7 +721,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBu
return;
}

- pa_core_set_configured_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)->name);
+ pa_core_set_configured_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)->name, true);

pa_dbus_send_empty_reply(conn, msg);
}
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index c4dbad99f..33e74c071 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -69,7 +69,7 @@ static void load(struct userdata *u) {
pa_log_warn("Invalid sink name: %s", ln);
else {
pa_log_info("Restoring default sink '%s'.", ln);
- pa_core_set_configured_default_sink(u->core, ln);
+ pa_core_set_configured_default_sink(u->core, ln, false);
}

} else if (errno != ENOENT)
diff --git a/src/modules/module-switch-on-connect.c b/src/modules/module-switch-on-connect.c
index faa0f289f..4f01c701f 100644
--- a/src/modules/module-switch-on-connect.c
+++ b/src/modules/module-switch-on-connect.c
@@ -54,9 +54,6 @@ struct userdata {
};

static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
- pa_sink_input *i;
- uint32_t idx;
- pa_sink *old_default_sink;
const char *s;
struct userdata *u = userdata;

@@ -85,7 +82,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void*

/* No default sink, nothing to move away, just set the new default */
if (!c->default_sink) {
- pa_core_set_configured_default_sink(c, sink->name);
+ pa_core_set_configured_default_sink(c, sink->name, false);
return PA_HOOK_OK;
}

@@ -100,29 +97,8 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void*
return PA_HOOK_OK;
}

- old_default_sink = c->default_sink;
-
/* Actually do the switch to the new sink */
- pa_core_set_configured_default_sink(c, sink->name);
-
- /* Now move all old inputs over */
- if (pa_idxset_size(old_default_sink->inputs) <= 0) {
- pa_log_debug("No sink inputs to move away.");
- return PA_HOOK_OK;
- }
-
- PA_IDXSET_FOREACH(i, old_default_sink->inputs, idx) {
- if (i->preferred_sink != NULL || !PA_SINK_INPUT_IS_LINKED(i->state))
- continue;
-
- if (pa_sink_input_move_to(i, sink, false) < 0)
- pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
- pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
- else
- pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
- pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
- }
-
+ pa_core_set_configured_default_sink(c, sink->name, false);
return PA_HOOK_OK;
}

diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 5205349bd..cc7addaa1 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -1036,7 +1036,7 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b
}

if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK)))
- pa_core_set_configured_default_sink(c, s->name);
+ pa_core_set_configured_default_sink(c, s->name, true);
else
pa_strbuf_printf(buf, "Sink %s does not exist.\n", n);

diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index cc4a6f38b..7d85e1788 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -226,7 +226,7 @@ static void core_free(pa_object *o) {
pa_xfree(c);
}

-void pa_core_set_configured_default_sink(pa_core *core, const char *sink) {
+void pa_core_set_configured_default_sink(pa_core *core, const char *sink, bool from_user) {
char *old_sink;

pa_assert(core);
@@ -241,7 +241,7 @@ void pa_core_set_configured_default_sink(pa_core *core, const char *sink) {
pa_log_info("configured_default_sink: %s -> %s",
old_sink ? old_sink : "(unset)", sink ? sink : "(unset)");

- pa_core_update_default_sink(core);
+ pa_core_update_default_sink(core, from_user);

finish:
pa_xfree(old_sink);
@@ -306,7 +306,7 @@ static int compare_sinks(pa_sink *a, pa_sink *b) {
return 0;
}

-void pa_core_update_default_sink(pa_core *core) {
+void pa_core_update_default_sink(pa_core *core, bool from_user) {
pa_sink *best = NULL;
pa_sink *sink;
uint32_t idx;
@@ -343,6 +343,9 @@ void pa_core_update_default_sink(pa_core *core) {

pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink);
+
+ /* try to move the streams from old_default_sink to the new default sink conditionally */
+ pa_sink_move_streams_from_oldsink_to_newsink(old_default_sink, core->default_sink, from_user);
}

/* a < b -> return -1
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 38622f618..82573f001 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -243,7 +243,7 @@ enum {

pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size);

-void pa_core_set_configured_default_sink(pa_core *core, const char *sink);
+void pa_core_set_configured_default_sink(pa_core *core, const char *sink, bool from_user);
void pa_core_set_configured_default_source(pa_core *core, const char *source);

/* These should be called whenever something changes that may affect the
@@ -255,7 +255,7 @@ void pa_core_set_configured_default_source(pa_core *core, const char *source);
* pa_core_update_default_source() internally, so it's sufficient to only call
* pa_core_update_default_sink() when something happens that affects the sink
* ordering. */
-void pa_core_update_default_sink(pa_core *core);
+void pa_core_update_default_sink(pa_core *core, bool from_user);
void pa_core_update_default_source(pa_core *core);

void pa_core_set_exit_idle_time(pa_core *core, int time);
diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
index fb1277215..464c3f8a2 100644
--- a/src/pulsecore/device-port.c
+++ b/src/pulsecore/device-port.c
@@ -96,7 +96,7 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
* default sink/source, so port availability changes may affect the
* default sink/source choice. */
if (p->direction == PA_DIRECTION_OUTPUT)
- pa_core_update_default_sink(p->core);
+ pa_core_update_default_sink(p->core, false);
else
pa_core_update_default_source(p->core);

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 09e5aa3d5..7c6e5fb06 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -4354,7 +4354,7 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman
sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);

- pa_core_set_configured_default_sink(c->protocol->core, sink->name);
+ pa_core_set_configured_default_sink(c->protocol->core, sink->name, true);
}

pa_pstream_send_simple_ack(c->pstream, tag);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 566367f9e..63a3456e7 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -721,7 +721,7 @@ void pa_sink_put(pa_sink* s) {

/* This function must be called after the PA_CORE_HOOK_SINK_PUT hook,
* because module-switch-on-connect needs to know the old default sink */
- pa_core_update_default_sink(s->core);
+ pa_core_update_default_sink(s->core, false);
}

/* Called from main context */
@@ -750,7 +750,7 @@ void pa_sink_unlink(pa_sink* s) {
pa_namereg_unregister(s->core, s->name);
pa_idxset_remove_by_data(s->core->sinks, s, NULL);

- pa_core_update_default_sink(s->core);
+ pa_core_update_default_sink(s->core, false);

if (s->card)
pa_idxset_remove_by_data(s->card->sinks, s, NULL);
@@ -3417,7 +3417,7 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
pa_sink_set_port_latency_offset(s, s->active_port->latency_offset);

/* The active port affects the default sink selection. */
- pa_core_update_default_sink(s->core);
+ pa_core_update_default_sink(s->core, false);

pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);

@@ -3886,3 +3886,36 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s);
}
+
+void pa_sink_move_streams_from_oldsink_to_newsink(pa_sink *old_sink, pa_sink *new_sink, bool from_user) {
+ pa_sink_input *i;
+ uint32_t idx;
+ pa_device_port *o_active_p;
+
+ if (old_sink == new_sink)
+ return;
+
+ if (old_sink == NULL)
+ return;
+
+ o_active_p = old_sink->active_port;
+ if (pa_idxset_size(old_sink->inputs) > 0) {
+ PA_IDXSET_FOREACH(i, old_sink->inputs, idx) {
+ if (!PA_SINK_INPUT_IS_LINKED(i->state))
+ continue;
+
+ if (!from_user && i->preferred_sink != NULL && pa_safe_streq(old_sink->name, i->preferred_sink))
+ if (o_active_p->available != PA_AVAILABLE_NO)
+ continue;
+
+ if (pa_sink_input_move_to(i, new_sink, from_user) < 0)
+ pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
+ pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), new_sink->name);
+ else
+ pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
+ pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), new_sink->name);
+ }
+ }
+
+ return;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 8f254408f..f207a094c 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -561,4 +561,5 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume);
#define pa_sink_assert_io_context(s) \
pa_assert(pa_thread_mq_get() || !PA_SINK_IS_LINKED((s)->state))

+void pa_sink_move_streams_from_oldsink_to_newsink(pa_sink *old_sink, pa_sink *new_sink, bool from_user);
#endif
--
2.17.1
Hui Wang
2018-11-05 01:47:15 UTC
Permalink
When a new sink appears, all streams that have their "preferred_sink"
set to the new sink should be moved to the new sink.

Signed-off-by: Hui Wang <***@canonical.com>
---
src/pulsecore/sink.c | 31 +++++++++++++++++++++++++++++++
src/pulsecore/sink.h | 1 +
2 files changed, 32 insertions(+)

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 63a3456e7..a2a390beb 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -722,6 +722,8 @@ void pa_sink_put(pa_sink* s) {
/* This function must be called after the PA_CORE_HOOK_SINK_PUT hook,
* because module-switch-on-connect needs to know the old default sink */
pa_core_update_default_sink(s->core, false);
+
+ pa_sink_bind_preferred_stream_to_a_sink(s);
}

/* Called from main context */
@@ -3919,3 +3921,32 @@ void pa_sink_move_streams_from_oldsink_to_newsink(pa_sink *old_sink, pa_sink *ne

return;
}
+
+void pa_sink_bind_preferred_stream_to_a_sink(pa_sink *s) {
+ pa_sink_input *si;
+ uint32_t idx;
+ pa_core *c;
+
+ if (!s)
+ return;
+
+ c = s->core;
+ PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
+ if (si->sink == s)
+ continue;
+
+ /* Skip this sink input if it is connecting a filter sink to
+ * the master */
+ if (si->origin_sink)
+ continue;
+
+ /* It might happen that a stream and a sink are set up at the
+ same time, in which case we want to make sure we don't
+ interfere with that */
+ if (!PA_SINK_INPUT_IS_LINKED(si->state))
+ continue;
+
+ if (si->preferred_sink != NULL && pa_streq(si->preferred_sink, s->name))
+ pa_sink_input_move_to(si, s, false);
+ }
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index f207a094c..24e4678b1 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -562,4 +562,5 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume);
pa_assert(pa_thread_mq_get() || !PA_SINK_IS_LINKED((s)->state))

void pa_sink_move_streams_from_oldsink_to_newsink(pa_sink *old_sink, pa_sink *new_sink, bool from_user);
+void pa_sink_bind_preferred_stream_to_a_sink(pa_sink *s);
#endif
--
2.17.1
Hui Wang
2018-11-05 01:47:16 UTC
Permalink
When the active port of a sink becomes unavailable, all streams from
that sink should be moved to the default sink.

When the active port of an existing sink changes state from
unavailable, all streams that have their "preferred_sink"
set to the new sink should be moved to the new sink.

Signed-off-by: Hui Wang <***@canonical.com>
---
src/pulsecore/device-port.c | 16 ++++++++++++++++
src/pulsecore/sink.c | 13 +++++++++++++
src/pulsecore/sink.h | 1 +
3 files changed, 30 insertions(+)

diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
index 464c3f8a2..2604c9051 100644
--- a/src/pulsecore/device-port.c
+++ b/src/pulsecore/device-port.c
@@ -92,6 +92,7 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
* be created before port objects, and then p->card could be non-NULL for
* the whole lifecycle of pa_device_port. */
if (p->card && p->card->linked) {
+ pa_sink *sink;
/* A sink or source whose active port is unavailable can't be the
* default sink/source, so port availability changes may affect the
* default sink/source choice. */
@@ -102,6 +103,21 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {

pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p);
+
+ sink = pa_sink_get_sink_from_device_port(p);
+ if (!sink)
+ return;
+ switch (p->direction) {
+ case PA_DIRECTION_OUTPUT:
+ if (sink->active_port->available == PA_AVAILABLE_NO)
+ pa_sink_move_streams_from_oldsink_to_newsink(sink, p->core->default_sink, false);
+ else
+ pa_sink_bind_preferred_stream_to_a_sink(sink);
+
+ break;
+ case PA_DIRECTION_INPUT:
+ break;
+ }
}
}

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a2a390beb..9ebc18fa1 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -3950,3 +3950,16 @@ void pa_sink_bind_preferred_stream_to_a_sink(pa_sink *s) {
pa_sink_input_move_to(si, s, false);
}
}
+
+pa_sink *pa_sink_get_sink_from_device_port(pa_device_port *p) {
+ pa_sink *rs = NULL;
+ pa_sink *sink;
+ uint32_t state;
+
+ PA_IDXSET_FOREACH(sink, p->card->sinks, state)
+ if (p == pa_hashmap_get(sink->ports, p->name)) {
+ rs = sink;
+ break;
+ }
+ return rs;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 24e4678b1..693344ac3 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -563,4 +563,5 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume);

void pa_sink_move_streams_from_oldsink_to_newsink(pa_sink *old_sink, pa_sink *new_sink, bool from_user);
void pa_sink_bind_preferred_stream_to_a_sink(pa_sink *s);
+pa_sink *pa_sink_get_sink_from_device_port(pa_device_port *p);
#endif
--
2.17.1
Continue reading on narkive:
Loading...