[pulseaudio-discuss] [PATCH v2 09/10] New module: module-skoa-router

Tanu Kaskinen tanu.kaskinen at linux.intel.com
Mon Dec 9 10:31:46 PST 2013


This module shall be our default router. Currently it implements the
same stream rescue policy for profile switch situations as
module-alsa-card does (module-alsa-card shouldn't implement any
routing policies, so the next thing I'll do is to remove that code
from module-alsa-card). The goal of the policy is to keep streams
connected to the same card before and after a profile switch. In the
future, this module should gain more functionality.

What's the deal with the module name? What does "skoa" mean? It
doesn't mean anything, or rather, it can mean whatever you want it to
mean. I have my own meaning for the word.

The reason behind such a strange name is that the module doesn't have
any more definite purpose or scope than to be our default router
module for now. module-default-router would be otherwise a good name,
but it would cause trouble if we ever want to make some completely
different module our default router. What would we do in that
situation? The first idea that comes to mind is to keep the module
name, but swap the implementation. That's not a good idea, however,
because if the old version supported some configuration parameters
that the new version doesn't support, then old configuration files
would break. The next idea is to keep the name module-default-router
for the old version, and call the new module something else, and load
the new version in the default configuration instead of
module-default-router. But that then causes infinite confusion, if a
module called "module-default-router" isn't actually the default
router. So, module-default-router isn't a good name, and I didn't
have any better ideas than coming up with a random name that doesn't
mean anything.
---
 src/Makefile.am                  |   8 ++
 src/daemon/default.pa.in         |   3 +
 src/daemon/system.pa.in          |   3 +
 src/modules/module-skoa-router.c | 167 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 181 insertions(+)
 create mode 100644 src/modules/module-skoa-router.c

diff --git a/src/Makefile.am b/src/Makefile.am
index e09d48a..8e8f987 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1076,6 +1076,7 @@ modlibexec_LTLIBRARIES += \
 		module-cli-protocol-tcp.la \
 		module-experimental-router.la \
 		module-simple-protocol-tcp.la \
+		module-skoa-router.la \
 		module-null-sink.la \
 		module-null-source.la \
 		module-sine-source.la \
@@ -1363,6 +1364,7 @@ SYMDEF_FILES = \
 		module-pipe-source-symdef.h \
 		module-simple-protocol-tcp-symdef.h \
 		module-simple-protocol-unix-symdef.h \
+		module-skoa-router-symdef.h \
 		module-native-protocol-tcp-symdef.h \
 		module-native-protocol-unix-symdef.h \
 		module-native-protocol-fd-symdef.h \
@@ -1917,6 +1919,12 @@ module_intended_roles_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_intended_roles_la_LIBADD = $(MODULE_LIBADD)
 module_intended_roles_la_CFLAGS = $(AM_CFLAGS)
 
+# The current default router module
+module_skoa_router_la_SOURCES = modules/module-skoa-router.c
+module_skoa_router_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_skoa_router_la_LIBADD = $(MODULE_LIBADD)
+module_skoa_router_la_CFLAGS = $(AM_CFLAGS)
+
 # An experimental router module
 module_experimental_router_la_SOURCES = modules/module-experimental-router.c
 module_experimental_router_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index f50d929..72fa8e6 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -196,6 +196,9 @@ ifelse(@HAVE_X11@, 1, [dnl
 #.endif
 ])dnl
 
+# Do some automatic routing
+load-module module-skoa-router
+
 ### Make some devices default
 #set-default-sink output
 #set-default-source input
diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in
index e881a12..800b4b2 100755
--- a/src/daemon/system.pa.in
+++ b/src/daemon/system.pa.in
@@ -69,3 +69,6 @@ load-module module-suspend-on-idle
 
 ### Enable positioned event sounds
 load-module module-position-event-sounds
+
+# Do some automatic routing
+load-module module-skoa-router
diff --git a/src/modules/module-skoa-router.c b/src/modules/module-skoa-router.c
new file mode 100644
index 0000000..d532e54
--- /dev/null
+++ b/src/modules/module-skoa-router.c
@@ -0,0 +1,167 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "module-skoa-router-symdef.h"
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/module.h>
+
+PA_MODULE_AUTHOR("Tanu Kaskinen");
+PA_MODULE_DESCRIPTION(_("The current default router module"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+struct userdata {
+    pa_hook_slot *card_set_profile_slot;
+};
+
+static pa_hook_result_t card_set_profile_cb(void *hook_data, void *call_data, void *userdata) {
+    pa_card_set_profile_hook_data *data = call_data;
+    pa_card_profile *profile;
+    pa_card *card;
+    pa_device_prototype *prototype;
+    void *state;
+    pa_sink *sink;
+    pa_queue *sink_inputs = NULL;
+    pa_source *source;
+    pa_queue *source_outputs = NULL;
+
+    pa_assert(data);
+
+    /* 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. */
+
+    profile = data->profile;
+    card = profile->card;
+
+    PA_HASHMAP_FOREACH(prototype, card->active_profile->sink_prototypes, state) {
+        if (!card->recreate_devices_on_profile_switch && pa_hashmap_get(profile->sink_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->device) {
+            sink = prototype->device;
+            sink_inputs = pa_sink_move_all_start(sink, sink_inputs);
+
+            if (!pa_queue_isempty(sink_inputs))
+                pa_log_debug("Started to move sink inputs away from sink %s.", sink->name);
+        }
+    }
+
+    PA_HASHMAP_FOREACH(prototype, card->active_profile->source_prototypes, state) {
+        if (!card->recreate_devices_on_profile_switch && pa_hashmap_get(profile->source_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->device) {
+            source = prototype->device;
+            source_outputs = pa_source_move_all_start(source, source_outputs);
+
+            if (!pa_queue_isempty(source_outputs))
+                pa_log_debug("Started to move source outputs away from source %s.", source->name);
+        }
+    }
+
+    data->ret = pa_card_set_profile(card, profile, data->save, true);
+    data->ret_valid = true;
+
+    if (data->ret >= 0 && (sink_inputs || source_outputs)) {
+        PA_HASHMAP_FOREACH(prototype, profile->sink_prototypes, state) {
+            if (prototype->device && sink_inputs && !pa_queue_isempty(sink_inputs)) {
+                sink = prototype->device;
+                pa_sink_move_all_finish(sink, sink_inputs, false);
+                pa_log_debug("Finished moving sink inputs to sink %s.", sink->name);
+                sink_inputs = NULL;
+            }
+
+            if (!sink_inputs)
+                break;
+        }
+
+        PA_HASHMAP_FOREACH(prototype, profile->source_prototypes, state) {
+            if (prototype->device && source_outputs && !pa_queue_isempty(source_outputs)) {
+                source = prototype->device;
+                pa_source_move_all_finish(source, source_outputs, false);
+                pa_log_debug("Finished moving source outputs to source %s.", source->name);
+                source_outputs = NULL;
+            }
+
+            if (!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 PA_HOOK_OK;
+}
+
+int pa__init(pa_module *module) {
+    struct userdata *u;
+
+    pa_assert(module);
+
+    module->userdata = u = pa_xnew0(struct userdata, 1);
+    u->card_set_profile_slot = pa_hook_connect(&module->core->hooks[PA_CORE_HOOK_CARD_SET_PROFILE], PA_HOOK_NORMAL,
+                                               card_set_profile_cb, NULL);
+
+    return 0;
+}
+
+void pa__done(pa_module *module) {
+    struct userdata *u;
+
+    pa_assert(module);
+
+    if (!(u = module->userdata))
+        return;
+
+    if (u->card_set_profile_slot)
+        pa_hook_slot_free(u->card_set_profile_slot);
+
+    pa_xfree(u);
+}
-- 
1.8.3.1



More information about the pulseaudio-discuss mailing list