For example, a normal stream tried to attach to filter sink or source, which filter loaded and managed by filter-apply. But, the stream goes to filter's ***master sink or source*** due to unexpected restoring operation. It should attached to filter sink or source properly. Also, includes further fix according to Georg's comment, [1] If a stream that had filter.apply set initially is moved to another sink/source, then the filter should be applied again (a new filter, since the master sink/source has changed). If a stream that did not have filter.apply set initially is moved away, the property should be removed and no new filter applied. Also, a property list change might add or remove the filter.apply property. If it is added, we want that the filter is applied. Your patch does nothing and assumes that the stream is already filtered, even if the stream is not on a filter sink. [1] https://lists.freedesktop.org/archives/pulseaudio-discuss/2017-April/027980.html Signed-off-by: KimJeongYeon <jeongyeon.kim at samsung.com> --- src/modules/module-filter-apply.c | 118 +++++++++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 21 deletions(-) diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index e91fb37..8e1bb24 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -39,6 +39,7 @@ #define PA_PROP_FILTER_APPLY_PARAMETERS PA_PROP_FILTER_APPLY".%s.parameters" #define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving" +#define PA_PROP_FILTER_APPLY_SET_BY_MFA "filter.apply.set_by_mfa" #define PA_PROP_MDM_AUTO_FILTERED "module-device-manager.auto_filtered" PA_MODULE_AUTHOR("Colin Guthrie"); @@ -161,6 +162,63 @@ static const char* get_filter_parameters(pa_object *o, const char *want, bool is return parameters; } +static void set_filter_properties(pa_proplist *pl, struct filter *filter, bool set_properties) { + char *prop_parameters; + + if (set_properties) { + pa_assert(filter); + + pa_proplist_sets(pl, PA_PROP_FILTER_APPLY, filter->name); + + if (filter->parameters) { + prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, filter->name); + pa_proplist_sets(pl, prop_parameters, filter->parameters); + pa_xfree(prop_parameters); + } + + pa_proplist_sets(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA, "1"); + } else { + const char *old_filter_name = NULL; + + if (filter) + old_filter_name = filter->name; + else + old_filter_name = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY); + + if (!old_filter_name) { + /* Filter name cannot be determined, nothing to do. */ + return; + } + + prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, old_filter_name); + pa_proplist_unset(pl, prop_parameters); + pa_xfree(prop_parameters); + + pa_proplist_unset(pl, PA_PROP_FILTER_APPLY); + pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA); + } +} + +static struct filter* get_filter_for_object(struct userdata *u, pa_object *o, bool is_sink_input) { + pa_sink *sink = NULL; + pa_source *source = NULL; + struct filter *filter = NULL; + void *state; + + if (is_sink_input) + sink = PA_SINK_INPUT(o)->sink; + else + source = PA_SOURCE_OUTPUT(o)->source; + + PA_HASHMAP_FOREACH(filter, u->filters, state) { + if ((is_sink_input && sink == filter->sink) || (!is_sink_input && source == filter->source)) { + return filter; + } + } + + return NULL; +} + static bool should_group_filter(struct filter *filter) { return pa_streq(filter->name, "echo-cancel"); } @@ -309,7 +367,7 @@ static int do_move(struct userdata *u, pa_object *obj, pa_object *parent, bool i } } -static void move_object_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore, bool is_sink_input) { +static void move_object_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore, bool is_sink_input) { pa_object *parent; pa_proplist *pl; const char *name; @@ -343,7 +401,7 @@ static void move_object_for_filter(struct userdata *u, pa_object *o, struct filt pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_MOVING); } -static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore, +static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore, bool is_sink_input) { if (!should_group_filter(filter)) @@ -431,7 +489,7 @@ static bool can_unload_module(struct userdata *u, uint32_t idx) { return true; } -static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input) { +static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input, bool is_property_change) { const char *want; const char *parameters; bool done_something = false; @@ -440,17 +498,16 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i pa_module *module = NULL; char *module_name = NULL; struct filter *fltr = NULL, *filter = NULL; + pa_proplist *pl; if (is_sink_input) { - sink = PA_SINK_INPUT(o)->sink; - - if (sink) + if ((sink = PA_SINK_INPUT(o)->sink)) module = sink->module; + pl = PA_SINK_INPUT(o)->proplist; } else { - source = PA_SOURCE_OUTPUT(o)->source; - - if (source) + if ((source = PA_SOURCE_OUTPUT(o)->source)) module = source->module; + pl = PA_SOURCE_OUTPUT(o)->proplist; } /* If there is no sink/source yet, we can't do much */ @@ -471,6 +528,21 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i goto done; } + if (pa_proplist_gets(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA)) { + /* The stream has been manually moved away from the filter. + * Keep sink/source as is. */ + + set_filter_properties(pl, NULL, false); + if ((filter = get_filter_for_object(u, o, is_sink_input))) { + /* Change 'filter.apply'. */ + set_filter_properties(pl, filter, true); + } + + done_something = true; + goto done; + } + /* Else, normal case. The stream should be moved to a filter. */ + /* 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); @@ -515,23 +587,27 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i done_something = true; } } else { - void *state; - /* We do not want to filter... but are we already filtered? * This can happen if an input's proplist changes */ - PA_HASHMAP_FOREACH(filter, u->filters, state) { - if ((is_sink_input && sink == filter->sink) || (!is_sink_input && source == filter->source)) { + + if ((filter = get_filter_for_object(u, o, is_sink_input))) { + if (is_property_change) { + /* 'filter.apply' has been manually unset. Do restore. */ move_objects_for_filter(u, o, filter, true, is_sink_input); + set_filter_properties(pl, filter, false); done_something = true; - break; + } else { + /* Stream has been manually moved to a filter sink/source + * without 'filter.apply' set. Leave sink as it is. */ + set_filter_properties(pl, filter, true); } } } +done: if (done_something) trigger_housekeeping(u); -done: pa_xfree(module_name); filter_free(fltr); @@ -542,7 +618,7 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struc pa_core_assert_ref(core); pa_sink_input_assert_ref(i); - return process(u, PA_OBJECT(i), true); + return process(u, PA_OBJECT(i), true, false); } static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -555,14 +631,14 @@ static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input * /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */ pa_hashmap_remove(u->mdm_ignored_inputs, i); - return process(u, PA_OBJECT(i), true); + return process(u, PA_OBJECT(i), true, false); } static pa_hook_result_t sink_input_proplist_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { pa_core_assert_ref(core); pa_sink_input_assert_ref(i); - return process(u, PA_OBJECT(i), true); + return process(u, PA_OBJECT(i), true, true); } static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -619,7 +695,7 @@ static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, pa_core_assert_ref(core); pa_source_output_assert_ref(o); - return process(u, PA_OBJECT(o), false); + return process(u, PA_OBJECT(o), false, false); } static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, struct userdata *u) { @@ -632,14 +708,14 @@ static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_ou /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */ pa_hashmap_remove(u->mdm_ignored_outputs, o); - return process(u, PA_OBJECT(o), false); + return process(u, PA_OBJECT(o), false, false); } static pa_hook_result_t source_output_proplist_cb(pa_core *core, pa_source_output *o, struct userdata *u) { pa_core_assert_ref(core); pa_source_output_assert_ref(o); - return process(u, PA_OBJECT(o), false); + return process(u, PA_OBJECT(o), false, true); } static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, struct userdata *u) { -- 2.7.4