device-manager reroutes all streams whenever a new device appears. When filter-apply has loaded a filter for some stream, the filter device may not be what device-manager considers the best device for the stream, which means that when an unrelated device appears, device-manager may break the filtering that filter-apply had set up. This patch changes filter-apply so that it saves the filter device name to the stream proplist when it sets up a filter. device-manager can then check the proplist when it does rerouting, and skip the rerouting for streams that have a filter applied to them. The proplist isn't cleaned up when the stream moves away from the filter device, so before doing any decisions based on the filter_device property, it should be checked that the stream is currently routed to the filter device. It seemed simpler to do it this way compared to setting up stream move monitoring in filter-apply and removing the property when the stream moves away from the filter device. --- src/modules/module-device-manager.c | 14 ++++++++++ src/modules/module-filter-apply.c | 53 +++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 1a0a53e..76363f3 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -649,6 +649,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha } static void route_sink_input(struct userdata *u, pa_sink_input *si) { + const char *filter_device; const char *role; uint32_t role_index, device_index; pa_sink *sink; @@ -663,6 +664,12 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) { if (!si->sink) return; + /* If module-filter-apply has loaded a filter for the stream, let's not + * break the filtering. */ + filter_device = pa_proplist_gets(si->proplist, "module-filter-apply.filter_device"); + if (filter_device && pa_streq(filter_device, si->sink->name)) + return; + /* It might happen that a stream and a sink are set up at the same time, in which case we want to make sure we don't interfere with that */ @@ -707,6 +714,7 @@ static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_si } static void route_source_output(struct userdata *u, pa_source_output *so) { + const char *filter_device; const char *role; uint32_t role_index, device_index; pa_source *source; @@ -724,6 +732,12 @@ static void route_source_output(struct userdata *u, pa_source_output *so) { if (!so->source) return; + /* If module-filter-apply has loaded a filter for the stream, let's not + * break the filtering. */ + filter_device = pa_proplist_gets(so->proplist, "module-filter-apply.filter_device"); + if (filter_device && pa_streq(filter_device, so->source->name)) + return; + /* It might happen that a stream and a source are set up at the same time, in which case we want to make sure we don't interfere with that */ diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index 7f4a2b7..f5ff0a3 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -271,10 +271,59 @@ static void trigger_housekeeping(struct userdata *u) { } static int do_move(pa_object *obj, pa_object *parent, bool restore, bool is_input) { - if (is_input) + if (is_input) { + if (!restore) { + char *old_value; + + if (pa_proplist_contains(PA_SINK_INPUT(obj)->proplist, "module-filter-apply.filter_device")) { + old_value = pa_xstrdup(pa_proplist_gets(PA_SINK_INPUT(obj)->proplist, "module-filter-apply.filter_device")); + if (!old_value) + old_value = pa_xstrdup("(data)"); + } else + old_value = pa_xstrdup("(unset)"); + + if (!pa_streq(PA_SINK(parent)->name, old_value)) { + pa_proplist *pl; + + pl = pa_proplist_new(); + pa_proplist_sets(pl, "module-filter-apply.filter_device", PA_SINK(parent)->name); + pa_sink_input_update_proplist(PA_SINK_INPUT(obj), PA_UPDATE_REPLACE, pl); + pa_proplist_free(pl); + pa_log_debug("Sink input %u: proplist[module-filter-apply.filter_device]: %s -> %s", + PA_SINK_INPUT(obj)->index, old_value, PA_SINK(parent)->name); + } + + pa_xfree(old_value); + } + return pa_sink_input_move_to(PA_SINK_INPUT(obj), PA_SINK(parent), restore); - else + } else { + if (!restore) { + char *old_value; + + if (pa_proplist_contains(PA_SOURCE_OUTPUT(obj)->proplist, "module-filter-apply.filter_device")) { + old_value = pa_xstrdup(pa_proplist_gets(PA_SOURCE_OUTPUT(obj)->proplist, "module-filter-apply.filter_device")); + if (!old_value) + old_value = pa_xstrdup("(data)"); + } else + old_value = pa_xstrdup("(unset)"); + + if (!pa_streq(PA_SOURCE(parent)->name, old_value)) { + pa_proplist *pl; + + pl = pa_proplist_new(); + pa_proplist_sets(pl, "module-filter-apply.filter_device", PA_SOURCE(parent)->name); + pa_source_output_update_proplist(PA_SOURCE_OUTPUT(obj), PA_UPDATE_REPLACE, pl); + pa_proplist_free(pl); + pa_log_debug("Source output %u: proplist[module-filter-apply.filter_device]: %s -> %s", + PA_SOURCE_OUTPUT(obj)->index, old_value, PA_SOURCE(parent)->name); + } + + pa_xfree(old_value); + } + return pa_source_output_move_to(PA_SOURCE_OUTPUT(obj), PA_SOURCE(parent), restore); + } } static void move_object_for_filter(pa_object *o, struct filter* filter, bool restore, bool is_sink_input) { -- 2.7.0