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.w...@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;


_______________________________________________
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Reply via email to