From ceca42f706c7d193cbda4e201d84f62790f0a465 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Thu, 21 Apr 2011 12:52:27 +0530 Subject: filters: Handle stream moves properly This makes sure that we handle streams moving between sinks properly. To do this, we change the way the filter.* properties are handled a little bit. Firstly, this splits up the "filter.apply" property into two properties - "filter.want" and "filter.apply". "filter.apply" acts as before - it bypasses module-filter-heuristics and directly tells module-filter-apply what filters are to be applied. "filter.want" is used to tell module-filter-heuristics what filters the client wants. The module then decides whether to actually apply the filter or not (for now, this makes sure we don't apply echo-cancellation even if requested on phone sinks (where it is assumed AEC is taken care of or is not required). Next, we also make sure that we track whether the client set "filter.apply" or module-filter-heuristics did - and in the latter case, we recalculate "filter.apply" and then have module-filter-apply apply the filter if required. This introduces some evil in the form of causing the move_finish callback to possibly trigger another move, but we protect for this case (with a property) to be doubly sure of not causing an infinite loop. --- src/modules/module-filter-apply.c | 27 ++++++++++++--- src/modules/module-filter-heuristics.c | 63 ++++++++++++++++++++++++++++------ 2 files changed, 75 insertions(+), 15 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index c8989711..faa918bf 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -37,6 +37,7 @@ #include "module-filter-apply-symdef.h" +#define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving" PA_MODULE_AUTHOR("Colin Guthrie"); PA_MODULE_DESCRIPTION("Load filter sinks automatically when needed"); @@ -64,6 +65,7 @@ struct userdata { pa_hashmap *filters; pa_hook_slot *sink_input_put_slot, + *sink_input_move_finish_slot, *sink_input_proplist_slot, *sink_input_unlink_slot, *sink_unlink_slot; @@ -110,14 +112,14 @@ static void filter_free(struct filter *f) { } static const char* should_filter(pa_sink_input *i) { - const char *want; + const char *apply; /* If the stream doesn't what any filter, then let it be. */ - if ((want = pa_proplist_gets(i->proplist, PA_PROP_FILTER_WANT)) && !pa_streq(want, "")) { + if ((apply = pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY)) && !pa_streq(apply, "")) { const char* suppress = pa_proplist_gets(i->proplist, PA_PROP_FILTER_SUPPRESS); - if (!suppress || !pa_streq(suppress, want)) - return want; + if (!suppress || !pa_streq(suppress, apply)) + return apply; } return NULL; @@ -171,12 +173,16 @@ static void move_input_for_filter(pa_sink_input *i, struct filter* filter, pa_bo pa_assert_se(sink = (restore ? filter->parent_sink : filter->sink)); + pa_proplist_sets(i->proplist, PA_PROP_FILTER_APPLY_MOVING, "1"); + if (pa_sink_input_move_to(i, sink, FALSE) < 0) pa_log_info("Failed to move sink input %u \"%s\" to <%s>.", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name); else pa_log_info("Sucessfully moved sink input %u \"%s\" to <%s>.", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name); + + pa_proplist_unset(i->proplist, PA_PROP_FILTER_APPLY_MOVING); } static pa_hook_result_t process(struct userdata *u, pa_sink_input *i) { @@ -281,6 +287,16 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struc return process(u, i); } +static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + + if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING)) + return PA_HOOK_OK; + + return process(u, i); +} + 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); @@ -359,6 +375,7 @@ int pa__init(pa_module *m) { u->filters = pa_hashmap_new(filter_hash, filter_compare); u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u); + u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u); u->sink_input_proplist_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_proplist_cb, u); u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u); u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_cb, u); @@ -386,6 +403,8 @@ void pa__done(pa_module *m) { if (u->sink_input_put_slot) pa_hook_slot_free(u->sink_input_put_slot); + if (u->sink_input_move_finish_slot) + pa_hook_slot_free(u->sink_input_move_finish_slot); if (u->sink_input_proplist_slot) pa_hook_slot_free(u->sink_input_proplist_slot); if (u->sink_input_unlink_slot) diff --git a/src/modules/module-filter-heuristics.c b/src/modules/module-filter-heuristics.c index a385ff24..20a48355 100644 --- a/src/modules/module-filter-heuristics.c +++ b/src/modules/module-filter-heuristics.c @@ -33,6 +33,8 @@ #include "module-filter-heuristics-symdef.h" +#define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving" +#define PA_PROP_FILTER_HEURISTICS "filter.heuristics" PA_MODULE_AUTHOR("Colin Guthrie"); PA_MODULE_DESCRIPTION("Detect when various filters are desirable"); @@ -46,27 +48,63 @@ static const char* const valid_modargs[] = { struct userdata { pa_core *core; pa_hook_slot - *sink_input_put_slot; + *sink_input_put_slot, + *sink_input_move_finish_slot; }; -static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { - const char *sink_role, *si_role; +static pa_hook_result_t process(struct userdata *u, pa_sink_input *i) { + const char *want, *sink_role, *si_role; + + /* If the stream already specifies what it must have, then let it be. */ + if (!pa_proplist_gets(i->proplist, PA_PROP_FILTER_HEURISTICS) && pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY)) + return PA_HOOK_OK; + + want = pa_proplist_gets(i->proplist, PA_PROP_FILTER_WANT); + if (!want) { + /* This is a phone stream, maybe we want echo cancellation */ + if ((si_role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)) && pa_streq(si_role, "phone")) + want = "echo-cancel"; + } + + /* On phone sinks, make sure we're not applying echo cancellation */ + if ((sink_role = pa_proplist_gets(i->sink->proplist, PA_PROP_DEVICE_INTENDED_ROLES)) && strstr(sink_role, "phone")) { + const char *apply = pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY); + + if (apply && pa_streq(apply, "echo-cancel")) { + pa_proplist_unset(i->proplist, PA_PROP_FILTER_APPLY); + pa_proplist_unset(i->proplist, PA_PROP_FILTER_HEURISTICS); + } + + return PA_HOOK_OK; + } + + if (want) { + /* There's a filter that we want, ask module-filter-apply to apply it, and remember that we're managing filter.apply */ + pa_proplist_sets(i->proplist, PA_PROP_FILTER_APPLY, want); + pa_proplist_sets(i->proplist, PA_PROP_FILTER_HEURISTICS, "1"); + } + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { pa_core_assert_ref(core); pa_sink_input_assert_ref(i); pa_assert(u); - /* If the stream already specifies what it wants, then let it be. */ - if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_WANT)) - return PA_HOOK_OK; + return process(u, i); +} - if ((sink_role = pa_proplist_gets(i->sink->proplist, PA_PROP_DEVICE_INTENDED_ROLES)) && strstr(sink_role, "phone")) - return PA_HOOK_OK; +static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + pa_assert(u); - if ((si_role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)) && pa_streq(si_role, "phone")) - pa_proplist_sets(i->proplist, PA_PROP_FILTER_WANT, "echo-cancel"); + /* module-filter-apply triggered this move, ignore */ + if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING)) + return PA_HOOK_OK; - return PA_HOOK_OK; + return process(u, i); } int pa__init(pa_module *m) { @@ -85,6 +123,7 @@ int pa__init(pa_module *m) { u->core = m->core; u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE-1, (pa_hook_cb_t) sink_input_put_cb, u); + u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE-1, (pa_hook_cb_t) sink_input_move_finish_cb, u); pa_modargs_free(ma); @@ -111,6 +150,8 @@ void pa__done(pa_module *m) { if (u->sink_input_put_slot) pa_hook_slot_free(u->sink_input_put_slot); + if (u->sink_input_move_finish_slot) + pa_hook_slot_free(u->sink_input_move_finish_slot); pa_xfree(u); -- cgit