[PATCH 1/3] core: Don't let moving filter sources/sinks become the default source/sink

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

 



When a virtual sink is loaded and there is only one sound card, then during
a profile switch, all sinks and sources can become temporarily unavailable.
In that situation, the virtual sink will become the default sink, although
it is not connected to a master sink due to the move operation initiated by
the profile switch. If module-always sink is loaded, it will load a null-sink
in that situation. If also module-switch-on-connect is present, it will change
the default sink to the new null sink and try to move the sink-inputs from the
virtual sink to the null sink. This leads to a segfault because the master
sink of the virtual sink is invalid.

This patch fixes the issue by disallowing a moving virtual sink to become the
default sink.

The same applies to the source side.

The is_filter_{sink,source}_moving() functions were stolen from one of Tanu's
previous unapplied patches.

Buglink: https://bugs.freedesktop.org/show_bug.cgi?id=100277
---
 src/pulsecore/core.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 52e51db1..91a9da84 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -258,6 +258,23 @@ void pa_core_set_configured_default_source(pa_core *core, pa_source *source) {
     pa_core_update_default_source(core);
 }
 
+/* Test if a sink is a moving filter sink */
+static bool is_filter_sink_moving(pa_sink *s) {
+    pa_sink *sink = s;
+
+    if (!sink)
+        return false;
+
+    while (sink->input_to_master) {
+        sink = sink->input_to_master->sink;
+
+        if (!sink)
+            return true;
+    }
+
+    return false;
+}
+
 /* a  < b  ->  return -1
  * a == b  ->  return  0
  * a  > b  ->  return  1 */
@@ -266,6 +283,12 @@ static int compare_sinks(pa_sink *a, pa_sink *b) {
 
     core = a->core;
 
+    /* A moving filter sink is always worse than any other sink */
+    if (is_filter_sink_moving(a) && !is_filter_sink_moving(b))
+        return -1;
+    if (!is_filter_sink_moving(a) && is_filter_sink_moving(b))
+        return 1;
+
     /* Available sinks always beat unavailable sinks. */
     if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
             && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
@@ -314,6 +337,10 @@ void pa_core_update_default_sink(pa_core *core) {
             best = sink;
     }
 
+    /* A moving filter sink cannot be the default sink */
+    if (is_filter_sink_moving(best))
+        best = NULL;
+
     old_default_sink = core->default_sink;
 
     if (best == old_default_sink)
@@ -332,6 +359,23 @@ void pa_core_update_default_sink(pa_core *core) {
     pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink);
 }
 
+/* Test if a source is a moving filter source */
+static bool is_filter_source_moving(pa_source *o) {
+    pa_source *source = o;
+
+    if (!source)
+        return false;
+
+    while (source->output_from_master) {
+        source = source->output_from_master->source;
+
+        if (!source)
+            return true;
+    }
+
+    return false;
+}
+
 /* a  < b  ->  return -1
  * a == b  ->  return  0
  * a  > b  ->  return  1 */
@@ -340,6 +384,12 @@ static int compare_sources(pa_source *a, pa_source *b) {
 
     core = a->core;
 
+    /* A moving filter source is always worse than any other source */
+    if (is_filter_source_moving(a) && !is_filter_source_moving(b))
+        return -1;
+    if (!is_filter_source_moving(a) && is_filter_source_moving(b))
+        return 1;
+
     /* Available sources always beat unavailable sources. */
     if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
             && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
@@ -398,6 +448,10 @@ void pa_core_update_default_source(pa_core *core) {
             best = source;
     }
 
+    /* A moving filter source cannot be the default source */
+    if (is_filter_source_moving(best))
+        best = NULL;
+
     old_default_source = core->default_source;
 
     if (best == old_default_source)
-- 
2.11.0



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

  Powered by Linux