[V3 PATCH] rescue-streams: try to find best one using availability and priority

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Recently met a problem: when I disconnect the bluetooth headset, the
pulseaudio automatically switch the sound to sink of HDMI output
instead of the sink of internal speaker even though there is no HDMI
cable connected.

To fix this problem, I want to change the rule of selecting the target
sink if the default sink is not available. (same rules apply to the
source selecting):
construct a new hashmap with all ports (of all relevant sinks) and
then call find_best on the new hashmap to find the best port, finally
find the corresponding sink using the best port.

Signed-off-by: Hui Wang <hui.wang at canonical.com>
---
diff Vs V2:
1) The default sink is not the root cause for this problem, so
i don't remove it in this version.

2) Use pa_idxset_trivial_* funcs to replace pa_idxset_string_* funcs
and use port pointers as key rather than the name

3) fix the wrong indentation

4) add a fallback sink in case the sink has no ports.

 src/modules/module-rescue-streams.c | 99 +++++++++++++++++++++++++++++++++----
 1 file changed, 89 insertions(+), 10 deletions(-)

diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index 7035a35..265711c 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -52,9 +52,56 @@ struct userdata {
         *source_output_move_fail_slot;
 };
 
+static pa_source* find_source_from_port(pa_core *c, pa_device_port *port) {
+    pa_source *target;
+    uint32_t idx;
+    void *state;
+    pa_device_port *p;
+
+    if (!port)
+        return NULL;
+
+    PA_IDXSET_FOREACH(target, c->sources, idx)
+        PA_HASHMAP_FOREACH(p, target->ports, state)
+            if (port == p)
+                return target;
+
+    return NULL;
+}
+
+static pa_sink* find_sink_from_port(pa_core *c, pa_device_port *port) {
+    pa_sink *target;
+    uint32_t idx;
+    void *state;
+    pa_device_port *p;
+
+    if (!port)
+        return NULL;
+
+    PA_IDXSET_FOREACH(target, c->sinks, idx)
+        PA_HASHMAP_FOREACH(p, target->ports, state)
+            if (port == p)
+                return target;
+
+    return NULL;
+}
+
+static void build_group_ports(pa_hashmap *g_ports, pa_hashmap *s_ports) {
+    void *state;
+    pa_device_port *p;
+
+    if (!g_ports || !s_ports)
+        return;
+
+    PA_HASHMAP_FOREACH(p, s_ports, state)
+        pa_hashmap_put(g_ports, p, p);
+}
+
 static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) {
-    pa_sink *target, *def;
+    pa_sink *target, *def, *fb_sink = NULL;
     uint32_t idx;
+    pa_hashmap *all_ports;
+    pa_device_port *best_port;
 
     pa_assert(c);
     pa_assert(i);
@@ -64,6 +111,8 @@ static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip
     if (def && def != skip && pa_sink_input_may_move_to(i, def))
         return def;
 
+    all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
     PA_IDXSET_FOREACH(target, c->sinks, idx) {
         if (target == def)
             continue;
@@ -74,12 +123,25 @@ static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip
         if (!PA_SINK_IS_LINKED(pa_sink_get_state(target)))
             continue;
 
-        if (pa_sink_input_may_move_to(i, target))
-            return target;
+        if (!pa_sink_input_may_move_to(i, target))
+            continue;
+
+        fb_sink = target;
+
+        build_group_ports(all_ports, target->ports);
     }
 
-    pa_log_debug("No evacuation sink found.");
-    return NULL;
+    best_port = pa_device_port_find_best(all_ports);
+
+    pa_hashmap_free(all_ports);
+
+    if(!best_port) {
+	pa_log_debug("No evacuation sink found.");
+        target = fb_sink;
+    } else
+	target = find_sink_from_port(c, best_port);
+
+    return target;
 }
 
 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
@@ -141,8 +203,10 @@ static pa_hook_result_t sink_input_move_fail_hook_callback(pa_core *c, pa_sink_i
 }
 
 static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_source *skip) {
-    pa_source *target, *def;
+    pa_source *target, *def, *fb_source = NULL;
     uint32_t idx;
+    pa_hashmap *all_ports;
+    pa_device_port *best_port;
 
     pa_assert(c);
     pa_assert(o);
@@ -152,6 +216,8 @@ static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_sou
     if (def && def != skip && pa_source_output_may_move_to(o, def))
         return def;
 
+    all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
     PA_IDXSET_FOREACH(target, c->sources, idx) {
         if (target == def)
             continue;
@@ -165,12 +231,25 @@ static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_sou
         if (!PA_SOURCE_IS_LINKED(pa_source_get_state(target)))
             continue;
 
-        if (pa_source_output_may_move_to(o, target))
-            return target;
+        if (!pa_source_output_may_move_to(o, target))
+            continue;
+
+        fb_source = target;
+
+        build_group_ports(all_ports, target->ports);
     }
 
-    pa_log_debug("No evacuation source found.");
-    return NULL;
+    best_port = pa_device_port_find_best(all_ports);
+
+    pa_hashmap_free(all_ports);
+
+    if(!best_port) {
+        pa_log_debug("No evacuation source found.");
+        target = fb_source;
+    } else
+        target = find_source_from_port(c, best_port);
+
+    return target;
 }
 
 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) {
-- 
1.8.1.2



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux