filter-apply: Add ability to pass parameters to a filter module 03/125103/2
authorKimJeongYeon <jeongyeon.kim@samsung.com>
Thu, 13 Apr 2017 23:06:36 +0000 (08:06 +0900)
committeryoungseok lee <youngseok7.lee@samsung.com>
Fri, 26 May 2017 05:34:57 +0000 (05:34 +0000)
Currently passing parameters to a filter loaded by module-filter-apply is
not possible.

To enable passing parameters to a filter this patch uses an additional property
filter.apply.{MODULE_NAME}.parameters. This way, filters like virtual-surround-sink
and ladspa-sink are fully supported. For example:
paplay file.wav --property=filter.apply=ladspa-sink \
                --property=filter.apply.ladspa-sink.parameters="plugin=ladspa \
                  label=ladspa_stereo control=0"

This patch based on:
https://cgit.freedesktop.org/pulseaudio/pulseaudio/commit/?id=caabff2728d9d588664f8a0ea2f8441804c8b91a

Note for tizen:
Proplist 'filter.apply.extra.parameters' and 'filter.apply.extra.group' are removed.
Also, their names are modified as below.
1) 'filter.apply.extra.group' to 'filter.apply.{MODULE_NAME}.group'
2) 'filter.apply.extra.parameters' to 'filter.apply.{MODULE_NAME}.parameters'
For example:
paplay file1.wav --property=filter.apply=ladspa-sink \
                 --property=filter.apply.ladspa-sink.group=group1 \
                 --property=filter.apply.ladspa-sink.parameters="plugin=ladspa1 \
                   label=ladspa_stereo control=0"
paplay file2.wav --property=filter.apply=ladspa-sink \
                 --property=filter.apply.ladspa-sink.group=group2 \
                 --property=filter.apply.ladspa-sink.parameters="plugin=ladspa2 \
                   label=ladspa_stereo control=0"

Signed-off-by: KimJeongYeon <jeongyeon.kim@samsung.com>
Change-Id: Ia1c85aca2be1f7f4328674146e5caa5e78f5e536

src/modules/module-filter-apply.c
src/pulse/proplist.h

index bf3fbaa..ce26d14 100644 (file)
@@ -39,7 +39,9 @@
 
 #include "module-filter-apply-symdef.h"
 
-#define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving"
+#define PA_PROP_FILTER_APPLY_GROUP      PA_PROP_FILTER_APPLY".%s.group"
+#define PA_PROP_FILTER_APPLY_PARAMETERS PA_PROP_FILTER_APPLY".%s.parameters"
+#define PA_PROP_FILTER_APPLY_MOVING     "filter.apply.moving"
 
 PA_MODULE_AUTHOR("Colin Guthrie");
 PA_MODULE_DESCRIPTION("Load filter sinks automatically when needed");
@@ -58,6 +60,7 @@ static const char* const valid_modargs[] = {
 struct filter {
     char *name;
     char *group;
+    char *parameters;
     uint32_t module_index;
     pa_sink *sink;
     pa_sink *sink_master;
@@ -106,7 +109,7 @@ static int filter_compare(const void *a, const void *b) {
     return 0;
 }
 
-static struct filter *filter_new(const char *name, const char *group, pa_sink *sink, pa_source *source) {
+static struct filter *filter_new(const char *name, const char *group, const char *parameters, pa_sink *sink, pa_source *source) {
     struct filter *f;
 
     pa_assert(sink || source);
@@ -114,6 +117,7 @@ static struct filter *filter_new(const char *name, const char *group, pa_sink *s
     f = pa_xnew(struct filter, 1);
     f->name = pa_xstrdup(name);
     f->group = pa_xstrdup(group);
+    f->parameters = pa_xstrdup(parameters);
     f->sink_master = sink;
     f->source_master = source;
     f->module_index = PA_INVALID_INDEX;
@@ -127,11 +131,12 @@ static void filter_free(struct filter *f) {
     if (f) {
         pa_xfree(f->name);
         pa_xfree(f->group);
+        pa_xfree(f->parameters);
         pa_xfree(f);
     }
 }
 
-static const char* should_filter(pa_object *o, bool is_sink_input) {
+static const char* get_filter_name(pa_object *o, bool is_sink_input) {
     const char *apply;
     pa_proplist *pl;
 
@@ -151,8 +156,9 @@ static const char* should_filter(pa_object *o, bool is_sink_input) {
     return NULL;
 }
 
-static const char* should_filter_parameters(pa_object *o, bool is_sink_input) {
+static const char* get_filter_parameters(pa_object *o, const char *want, bool is_sink_input) {
     const char *parameters;
+    char *prop_parameters;
     pa_proplist *pl;
 
     if (is_sink_input)
@@ -160,15 +166,16 @@ static const char* should_filter_parameters(pa_object *o, bool is_sink_input) {
     else
         pl = PA_SOURCE_OUTPUT(o)->proplist;
 
-    /* If the stream needs parameters, append them to module. */
-    if ((parameters = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY_EXTRA_PARAMETERS)) && !pa_streq(parameters, ""))
-        return parameters;
+    prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, want);
+    parameters = pa_proplist_gets(pl, prop_parameters);
+    pa_xfree(prop_parameters);
 
-    return NULL;
+    return parameters;
 }
 
-static const char* should_filter_group(pa_object *o, bool is_sink_input) {
+static const char* get_filter_group(pa_object *o, const char *want, bool is_sink_input) {
     const char *group;
+    char *prop_group;
     pa_proplist *pl;
 
     if (is_sink_input)
@@ -176,14 +183,14 @@ static const char* should_filter_group(pa_object *o, bool is_sink_input) {
     else
         pl = PA_SOURCE_OUTPUT(o)->proplist;
 
-    /* If the stream needs group of filters, allow multiple instances. */
-    if ((group = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY_EXTRA_GROUP)) && !pa_streq(group, ""))
-        return group;
+    prop_group = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_GROUP, want);
+    group = pa_proplist_gets(pl, prop_group);
+    pa_xfree(prop_group);
 
-    return NULL;
+    return group;
 }
 
-static bool is_duplex_filter(struct filter *filter) {
+static bool should_group_filter(struct filter *filter) {
     return pa_streq(filter->name, "echo-cancel");
 }
 
@@ -363,7 +370,7 @@ static void move_object_for_filter(pa_object *o, struct filter* filter, bool res
 static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore,
         bool is_sink_input) {
 
-    if (!is_duplex_filter(filter))
+    if (!should_group_filter(filter))
         move_object_for_filter(o, filter, restore, is_sink_input);
     else {
         pa_source_output *so;
@@ -397,7 +404,7 @@ static void move_objects_for_filter(struct userdata *u, pa_object *o, struct fil
 
 /* Note that we assume a filter will provide at most one sink and at most one
  * source (and at least one of either). */
-static void find_filters_for_module(struct userdata *u, pa_module *m, const char *name, const char *group) {
+static void find_filters_for_module(struct userdata *u, pa_module *m, const char *name, const char *group, const char *parameters) {
     uint32_t idx;
     pa_sink *sink;
     pa_source *source;
@@ -407,7 +414,7 @@ static void find_filters_for_module(struct userdata *u, pa_module *m, const char
         if (sink->module == m) {
             pa_assert(sink->input_to_master != NULL);
 
-            fltr = filter_new(name, group, sink->input_to_master->sink, NULL);
+            fltr = filter_new(name, group, parameters, sink->input_to_master->sink, NULL);
             fltr->module_index = m->index;
             fltr->sink = sink;
 
@@ -420,7 +427,7 @@ static void find_filters_for_module(struct userdata *u, pa_module *m, const char
             pa_assert(source->output_from_master != NULL);
 
             if (!fltr) {
-                fltr = filter_new(name, group, NULL, source->output_from_master->source);
+                fltr = filter_new(name, group, parameters, NULL, source->output_from_master->source);
                 fltr->module_index = m->index;
                 fltr->source = source;
             } else {
@@ -450,6 +457,8 @@ static bool can_unload_module(struct userdata *u, uint32_t idx) {
 
 static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input) {
     const char *want;
+    const char *group;
+    const char *parameters;
     bool done_something = false;
     pa_sink *sink = NULL;
     pa_source *source = NULL;
@@ -472,11 +481,9 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
         return PA_HOOK_OK;
 
     /* If the stream doesn't what any filter, then let it be. */
-    if ((want = should_filter(o, is_sink_input))) {
+    if ((want = get_filter_name(o, is_sink_input))) {
         char *module_name;
         struct filter *fltr, *filter;
-        const char *parameters;
-        const char *group;
 
         /* We need to ensure the SI is playing on a sink of this type
          * attached to the sink it's "officially" playing on */
@@ -491,17 +498,18 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
             return PA_HOOK_OK;
         }
 
-        /* Some filter modules might require parameters by default.
-         * (e.g. 'plugin', 'label', 'control' of module-ladspa-sink) */
-        parameters = should_filter_parameters(o, is_sink_input);
-
         /* Some applications may want group of filters. (optional) */
-        group = should_filter_group(o, is_sink_input);
+        group = get_filter_group(o, want, is_sink_input);
+
+        /* Some filter modules might require parameters by default.
+         * (e.g 'plugin', 'label', 'control' of module-ladspa-sink) */
+        parameters = get_filter_parameters(o, want, is_sink_input);
 
-        fltr = filter_new(want, group, sink, source);
+        fltr = filter_new(want, group, parameters, sink, source);
 
-        if (is_duplex_filter(fltr) && !find_paired_master(u, fltr, o, is_sink_input)) {
+        if (should_group_filter(fltr) && !find_paired_master(u, fltr, o, is_sink_input)) {
             pa_log_debug("Want group filtering but don't have enough streams.");
+            pa_xfree(module_name);
             filter_free(fltr);
             return PA_HOOK_OK;
         }
@@ -520,7 +528,7 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
             pa_log_debug("Loading %s with arguments '%s'", module_name, args);
 
             if ((m = pa_module_load(u->core, module_name, args))) {
-                find_filters_for_module(u, m, want, group);
+                find_filters_for_module(u, m, want, group, parameters);
                 filter = pa_hashmap_get(u->filters, fltr);
                 done_something = true;
             }
index 5240485..88548b8 100644 (file)
@@ -71,13 +71,7 @@ PA_C_DECL_BEGIN
 /** For streams: the name of a filter that is desired, e.g.\ "echo-cancel" or "equalizer-sink". Differs from PA_PROP_FILTER_WANT in that it forces PulseAudio to apply the filter, regardless of whether PulseAudio thinks it makes sense to do so or not. If this is set, PA_PROP_FILTER_WANT is ignored. In other words, you almost certainly do not want to use this. \since 1.0 */
 #define PA_PROP_FILTER_APPLY                   "filter.apply"
 
-/** For streams: some modules require extra parameters, e.g.\ "plugin=ladspa label=ladspa_stereo control=0" in case of "ladspa-sink" filter */
-#define PA_PROP_FILTER_APPLY_EXTRA_PARAMETERS  "filter.apply.extra.parameters"
-
-/** For streams: some applications may want to classify sub filter groups and they can be used as multiple instances. (e.g. load multiple plugin instances of "ladspa-sink" filter) */
-#define PA_PROP_FILTER_APPLY_EXTRA_GROUP       "filter.apply.extra.group"
-
-/** For streams: the name of a filter that should specifically be suppressed (i.e.\ overrides PA_PROP_FILTER_WANT). Useful for the times that PA_PROP_FILTER_WANT is automatically added (e.g. echo-cancellation for phone streams when $VOIP_APP does it's own, internal AEC) \since 1.0 */
+/** For streams: the name of a filter that should specifically suppressed (i.e.\ overrides PA_PROP_FILTER_WANT). Useful for the times that PA_PROP_FILTER_WANT is automatically added (e.g. echo-cancellation for phone streams when $VOIP_APP does it's own, internal AEC) \since 1.0 */
 #define PA_PROP_FILTER_SUPPRESS                "filter.suppress"
 
 #ifdef __TIZEN__