For example, a normal stream tried to attach to filter sink(or source), which filter loaded and managed by filter-apply. But, the stream become to attach to the ***master sink(or source)*** of filter module due to restoring operation. It seems should to be attached to the filter sink(or source) properly. Signed-off-by: KimJeongYeon <jeongyeon.kim at samsung.com> --- src/modules/module-filter-apply.c | 103 +++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 7 deletions(-) diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index 364d68b..07d1c52 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -54,6 +54,12 @@ static const char* const valid_modargs[] = { #define DEFAULT_AUTOCLEAN true #define HOUSEKEEPING_INTERVAL (10 * PA_USEC_PER_SEC) +typedef enum _process_cmd_type { + PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PUT, + PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_MOVE_FINISH, + PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PROPLIST, +} process_cmd_type_t; + struct filter { char *name; uint32_t module_index; @@ -72,6 +78,7 @@ struct userdata { pa_hashmap *mdm_ignored_inputs, *mdm_ignored_outputs; bool autoclean; pa_time_event *housekeeping_time_event; + bool skip_prop_change; }; static unsigned filter_hash(const void *p) { @@ -410,7 +417,77 @@ 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 bool make_sure_filter_apply(struct userdata *u, pa_object *o, process_cmd_type_t cmd, bool is_sink_input) { + pa_sink *sink = NULL; + pa_source *source = NULL; + pa_proplist *pl; + void *state; + struct filter *filter; + bool is_attached = false; + + if (is_sink_input) { + sink = PA_SINK_INPUT(o)->sink; + pl = PA_SINK_INPUT(o)->proplist; + } else { + source = PA_SOURCE_OUTPUT(o)->source; + pl = PA_SOURCE_OUTPUT(o)->proplist; + } + + /* Make sure to exist of 'filter.apply' property of stream that puts or + * moves to filter according to request. Append the property if not exists. + * No action required if 'filter.apply' property exists. */ + PA_HASHMAP_FOREACH(filter, u->filters, state) { + if ((is_sink_input && sink == filter->sink) || (!is_sink_input && source == filter->source)) { + is_attached = true; + + switch (cmd) { + case PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PUT: + case PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_MOVE_FINISH: + if (!pa_proplist_gets(pl, PA_PROP_FILTER_APPLY)) + pa_proplist_sets(pl, PA_PROP_FILTER_APPLY, filter->name); + break; + case PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PROPLIST: + if (pa_proplist_gets(pl, PA_PROP_FILTER_APPLY) || u->skip_prop_change) + goto do_nothing; + /* Else, will be restore to master sink or source. */ + break; + } + break; + } + } + + /* Make sure to remove 'filter.apply' property of stream that moved away + * from filter. Remove the property if exists. + * No action required if 'filter.apply' property not exists. */ + if (!is_attached) { + switch (cmd) { + case PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PUT: + if (!pa_proplist_gets(pl, PA_PROP_FILTER_APPLY)) + goto do_nothing; + /* Else, will be apply filter. */ + break; + case PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_MOVE_FINISH: + if (pa_proplist_gets(pl, PA_PROP_FILTER_APPLY)) { + pa_proplist_unset(pl, PA_PROP_FILTER_APPLY); + if (pa_hashmap_size(u->filters) > 0) + trigger_housekeeping(u); + } + goto do_nothing; + case PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PROPLIST: + if (!pa_proplist_gets(pl, PA_PROP_FILTER_APPLY) || u->skip_prop_change) + goto do_nothing; + /* Else, will be apply filter. */ + break; + } + } + + return true; + +do_nothing: + return false; +} + +static pa_hook_result_t process(struct userdata *u, pa_object *o, process_cmd_type_t cmd, bool is_sink_input) { const char *want; bool done_something = false; pa_sink *sink = NULL; @@ -435,6 +512,9 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i if ((is_sink_input && !sink) || (!is_sink_input && !source)) goto done; + if (!make_sure_filter_apply(u, o, cmd, is_sink_input)) + goto done; + /* If the stream doesn't what any filter, then let it be. */ if ((want = should_filter(o, is_sink_input))) { /* We need to ensure the SI is playing on a sink of this type @@ -515,7 +595,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), PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PUT, true); } static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -525,17 +605,19 @@ static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input * if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING)) return PA_HOOK_OK; + u->skip_prop_change = true; /* 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); + u->skip_prop_change = false; - return process(u, PA_OBJECT(i), true); + return process(u, PA_OBJECT(i), PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_MOVE_FINISH, true); } 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), PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PROPLIST, true); } static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -547,7 +629,9 @@ static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, st if (pa_hashmap_size(u->filters) > 0) trigger_housekeeping(u); + u->skip_prop_change = true; pa_hashmap_remove(u->mdm_ignored_inputs, i); + u->skip_prop_change = false; return PA_HOOK_OK; } @@ -592,7 +676,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), PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PUT, false); } static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, struct userdata *u) { @@ -602,17 +686,19 @@ static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_ou if (pa_proplist_gets(o->proplist, PA_PROP_FILTER_APPLY_MOVING)) return PA_HOOK_OK; + u->skip_prop_change = true; /* 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); + u->skip_prop_change = false; - return process(u, PA_OBJECT(o), false); + return process(u, PA_OBJECT(o), PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_MOVE_FINISH, 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), PROCESS_CMD_SINK_INPUT_SOURCE_OUTPUT_PROPLIST, false); } static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, struct userdata *u) { @@ -624,7 +710,9 @@ static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output if (pa_hashmap_size(u->filters) > 0) trigger_housekeeping(u); + u->skip_prop_change = true; pa_hashmap_remove(u->mdm_ignored_outputs, o); + u->skip_prop_change = false; return PA_HOOK_OK; } @@ -699,6 +787,7 @@ int pa__init(pa_module *m) { u->filters = pa_hashmap_new(filter_hash, filter_compare); u->mdm_ignored_inputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_input, NULL); u->mdm_ignored_outputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_output, NULL); + u->skip_prop_change = false; pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u); pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u); -- 2.7.4