Hello Tanu, Could you please take a look at this patch, you are the maintainer and recently contributed couple of commits to module-switch-on-port-available.c. :-) Thanks in advance. Hui. On 04/27/2017 11:20 AM, Hui Wang wrote: > Suppose your machine has two sound cards as below: > > Card#0(HDA INTEL HDMI)-> Sink#0(hdmi-stereo)->hdmi-output(priority: 5900) > Card#1(HDA INTEL PCH)->Sink#1(analog-stereo)->headphones(priority: 9000) > > If neither hdmi cable nor headphone plug into the machine, the default > sink will randomly be Sink#0 or Sink#1, let us assume it is Sink#1, > then users plug hdmi cable into the machine, the port hdmi-output will > change to the state PA_AVAILABLE_YES, so the Sink#0 has a port with > state YES, while the Sink#1 still has a port with state NO, in this > situation it is reasonable to change the default_sink to Sink#0, but > current code can't do that. > > Let us suppose another situation, both hdmi cable and headphone are > plugged into the machine, and the Sink#0 is the default sink, if users > unplug the hdmi cable, the port hdmi-output is changed to NO while > the port headphone is still kept YES, in this situation it is > reasonable to switch the default_sink to Sink#1, but current code > can't do that. > > This issue also applies to the pa_source as well. > > If there is only one sound card and both Sink#0 and Sink#1 belong to > this card (latest Intel platform like skylake or kabylake only > has one sound card), there is no issue. It looks like the current > switch-on-port-available can't handle multiple sound cards well. > > To fix it, adding a function to check and change default sink or > source after executing switch_to_port() or switch_from_port(). > > Signed-off-by: Hui Wang <hui.wang at canonical.com> > --- > src/modules/module-switch-on-port-available.c | 145 ++++++++++++++++++++++++++ > 1 file changed, 145 insertions(+) > > diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c > index b9a0f3b..e1113ea 100644 > --- a/src/modules/module-switch-on-port-available.c > +++ b/src/modules/module-switch-on-port-available.c > @@ -26,6 +26,7 @@ > #include <pulsecore/core-util.h> > #include <pulsecore/device-port.h> > #include <pulsecore/hashmap.h> > +#include <pulsecore/namereg.h> > > #include "module-switch-on-port-available-symdef.h" > > @@ -272,6 +273,148 @@ static bool switch_from_port(pa_device_port *port) { > return false; > } > > +/* This function refers to sink_put_hook_callback() in the module-switch-on-connect.c */ > +static void move_to_new_default_sink(pa_core *c, pa_sink *ori_sink, pa_sink *new_sink) > +{ > + pa_sink_input *i; > + uint32_t idx; > + > + pa_namereg_set_default_sink(c, new_sink); > + /* Now move all old inputs over */ > + if (pa_idxset_size(ori_sink->inputs) <= 0) { > + pa_log_debug("No sink inputs to move away."); > + return; > + } > + > + PA_IDXSET_FOREACH(i, ori_sink->inputs, idx) { > + if (i->save_sink || !PA_SINK_INPUT_IS_LINKED(i->state)) > + continue; > + > + if (pa_sink_input_move_to(i, new_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)), 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); > + } > +} > + > +/* This function refers to source_put_hook_callback() in the module-switch-on-connect.c */ > +static void move_to_new_default_source(pa_core *c, pa_source *ori_src, pa_source *new_src) > +{ > + pa_source_output *o; > + uint32_t idx; > + > + pa_namereg_set_default_source(c, new_src); > + > + /* Now move all old outputs over */ > + if (pa_idxset_size(ori_src->outputs) <= 0) { > + pa_log_debug("No source outputs to move away."); > + return; > + } > + > + PA_IDXSET_FOREACH(o, ori_src->outputs, idx) { > + if (o->save_source || !PA_SOURCE_OUTPUT_IS_LINKED(o->state)) > + continue; > + > + if (pa_source_output_move_to(o, new_src, false) < 0) > + pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index, > + pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), new_src->name); > + else > + pa_log_info("Successfully moved source output %u \"%s\" to %s.", o->index, > + pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), new_src->name); > + } > +} > + > +/* > + When a port changes to PA_AVAILABLE_YES, the original code tries to set it to > + be the active_port in its own sink/source. After that we also need to check if > + its sink/source is the default_sink/source or not, if it is not, it is possible > + that all ports in the default_sink/source are in the state PA_AVAILABLE_NO, > + in that case it is reasonable to replace the default_sink/source with this sink/source. > + To do so, we will check the conditions as below: > + - the default_sink/source doesn't have active_port > + - the active_port of default_sink/source is in the state PA_AVAILABLE_NO > + - the active_port of the above is in the state PA_AVAILABLE_YES, but its priority > + is lower than this port's priority > + If at least one of the 3 conditions is met, we will change the default_sink/source. > + > + > + When a port changes to PA_AVAILABLE_NO, the original code tries to look for the best > + port under the same snd_card to replace it as the active_port. After that we also need > + to check if this port belongs to the default_sink/source, if it is, it is possible that > + all ports in the default_sink/source are in the state PA_AVAILABLE_NO, while other > + sinks/sources have better ports than default_sink/source, so it is reasonable to replace > + the default_sink/source with a better sink/source. To do so we will enumerate all > + sinks/sources and all ports in them, if a sink/source has a port with the state PA_AVAILABLE_YES > + and the port has the highest priority, we will set this sink/source to be the default_sink/source. > + */ > +static void check_and_change_default_sink_source(pa_core *c, pa_device_port *port, bool is_avail) > +{ > + struct port_pointers pp = find_port_pointers(port); > + pa_sink *def_sink, *p_sink = pp.sink; > + pa_source *def_src, *p_src = pp.source; > + > + switch (port->direction) { > + case PA_DIRECTION_OUTPUT: > + def_sink = pa_namereg_get_default_sink(c); > + if (is_avail) { > + if (def_sink == p_sink) > + return; > + if (!def_sink->active_port || def_sink->active_port->available == PA_AVAILABLE_NO || > + def_sink->active_port->priority < port->priority) > + move_to_new_default_sink(c, def_sink, p_sink); > + } else { > + uint32_t state_s; > + void *state_p; > + pa_device_port *i, *bport = NULL; > + pa_sink *bsink = NULL; > + > + if (def_sink != p_sink) > + return; > + PA_IDXSET_FOREACH(p_sink, c->sinks, state_s) { > + PA_HASHMAP_FOREACH(i, p_sink->ports, state_p) { > + if (i->available == PA_AVAILABLE_YES && (!bport || bport->priority < i->priority)) { > + bport = i; > + bsink = p_sink; > + } > + } > + } > + if (bsink) > + move_to_new_default_sink(c, def_sink, bsink); > + } > + break; > + > + case PA_DIRECTION_INPUT: > + def_src = pa_namereg_get_default_source(c); > + if (is_avail) { > + if (def_src == p_src) > + return; > + if (!def_src->active_port || def_src->active_port->available == PA_AVAILABLE_NO || > + def_src->active_port->priority < port->priority) > + move_to_new_default_source(c, def_src, p_src); > + } else { > + uint32_t state_s; > + void *state_p; > + pa_device_port *i, *bport = NULL; > + pa_source *bsrc = NULL; > + > + if (def_src != p_src) > + return; > + PA_IDXSET_FOREACH(p_src, c->sources, state_s) { > + PA_HASHMAP_FOREACH(i, p_src->ports, state_p) { > + if (i->available == PA_AVAILABLE_YES && (!bport || bport->priority < i->priority)) { > + bport = i; > + bsrc = p_src; > + } > + } > + } > + if (bsrc) > + move_to_new_default_source(c, def_src, bsrc); > + } > + break; > + } > +} > > static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) { > pa_assert(port); > @@ -289,9 +432,11 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port > switch (port->available) { > case PA_AVAILABLE_YES: > switch_to_port(port); > + check_and_change_default_sink_source(c, port, true); > break; > case PA_AVAILABLE_NO: > switch_from_port(port); > + check_and_change_default_sink_source(c, port, false); > break; > default: > break;