[PATCH 10/11] skoa-router: Implement a stream rescue policy for profile switch situations

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

 



The implemented logic is copied from module-alsa-card. Now the same
logic works across all card implementations, not just ALSA.

The policy in short: the goal is to keep streams connected to the same
card before and after a profile switch.
---
 src/modules/module-skoa-router.c | 84 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/src/modules/module-skoa-router.c b/src/modules/module-skoa-router.c
index 9406fef..32dafda 100644
--- a/src/modules/module-skoa-router.c
+++ b/src/modules/module-skoa-router.c
@@ -25,6 +25,7 @@
 
 #include "module-skoa-router-symdef.h"
 
+#include <pulsecore/device-prototype.h>
 #include <pulsecore/i18n.h>
 #include <pulsecore/module.h>
 #include <pulsecore/router.h>
@@ -38,6 +39,87 @@ struct userdata {
     pa_router *router;
 };
 
+static int router_set_card_profile_cb(pa_router *router, pa_card *card, pa_card_profile *profile, bool save) {
+    pa_device_prototype *prototype;
+    void *state;
+    pa_queue *sink_inputs = NULL;
+    pa_queue *source_outputs = NULL;
+    int r;
+
+    pa_assert(router);
+    pa_assert(card);
+    pa_assert(profile);
+
+    /* When the profile changes, the sinks and sources of the old profile are
+     * removed (at least if they aren't part of the new profile too), and this
+     * raises the question what happens to streams that are connected to the
+     * removed devices. Here we implement a policy to move those streams to the
+     * devices of the new profile if possible. It's unclear whether this is
+     * actually a good policy, but that's what we have historically been doing
+     * (previously in the ALSA card code), and nothing better has been
+     * proposed. */
+
+    PA_HASHMAP_FOREACH(prototype, card->active_profile->device_prototypes, state) {
+        if (!card->recreate_devices_on_profile_switch && pa_hashmap_get(profile->device_prototypes, prototype))
+            /* This device is a part of both the old and new profile, so
+             * there's no need to rescue any streams from this device. */
+            continue;
+
+        if (prototype->sink) {
+            sink_inputs = pa_sink_move_all_start(prototype->sink, sink_inputs);
+
+            if (!pa_queue_isempty(sink_inputs))
+                pa_log_debug("Started to move sink inputs away from sink %s.", prototype->sink->name);
+        }
+
+        if (prototype->source) {
+            source_outputs = pa_source_move_all_start(prototype->source, source_outputs);
+
+            if (!pa_queue_isempty(source_outputs))
+                pa_log_debug("Started to move source outputs away from source %s.", prototype->source->name);
+        }
+    }
+
+    r = pa_card_set_profile(card, profile, save, true);
+
+    if (r >= 0 && (sink_inputs || source_outputs)) {
+        PA_HASHMAP_FOREACH(prototype, profile->device_prototypes, state) {
+            if (prototype->sink && sink_inputs && !pa_queue_isempty(sink_inputs)) {
+                pa_sink_move_all_finish(prototype->sink, sink_inputs, false);
+                pa_log_debug("Finished moving sink inputs to sink %s.", prototype->sink->name);
+                sink_inputs = NULL;
+            }
+
+            if (prototype->source && source_outputs && !pa_queue_isempty(source_outputs)) {
+                pa_source_move_all_finish(prototype->source, source_outputs, false);
+                pa_log_debug("Finished moving source outputs to source %s.", prototype->source->name);
+                source_outputs = NULL;
+            }
+
+            if (!sink_inputs && !source_outputs)
+                break;
+        }
+    }
+
+    if (sink_inputs) {
+        if (!pa_queue_isempty(sink_inputs))
+            pa_log_debug("Profile change failed or there are no sinks in the new profile, "
+                         "have to fail the sink input rescue operation.");
+
+        pa_sink_move_all_fail(sink_inputs);
+    }
+
+    if (source_outputs) {
+        if (!pa_queue_isempty(source_outputs))
+            pa_log_debug("Profile change failed or there are no sources in the new profile, "
+                         "have to fail the source output rescue operation.");
+
+        pa_source_move_all_fail(source_outputs);
+    }
+
+    return r;
+}
+
 int pa__init(pa_module *module) {
     struct userdata *u;
     int r;
@@ -50,6 +132,8 @@ int pa__init(pa_module *module) {
     if (!u->router)
         goto fail;
 
+    u->router->set_card_profile = router_set_card_profile_cb;
+
     r = pa_router_put(u->router);
 
     if (r < 0)
-- 
1.8.3.1



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

  Powered by Linux