diff options
| author | Arun Raghavan <arun.raghavan@collabora.co.uk> | 2011-04-22 17:33:03 +0530 | 
|---|---|---|
| committer | Colin Guthrie <colin@mageia.org> | 2011-04-23 18:23:38 +0100 | 
| commit | 87570523f84817b19486ab9302314091424bbad8 (patch) | |
| tree | 213a82f0ea050da77067a7e42a1cd6f61e74a684 /src | |
| parent | ceca42f706c7d193cbda4e201d84f62790f0a465 (diff) | |
filters: Handle filters on sources as well
This makes the core code in the filter-* modules generic enough to be
used on sources or sinks. We need special handling for modules that
introduce more than one sink (for now echo-cancel only).
Diffstat (limited to 'src')
| -rw-r--r-- | src/modules/module-filter-apply.c | 295 | ||||
| -rw-r--r-- | src/modules/module-filter-heuristics.c | 65 | 
2 files changed, 283 insertions, 77 deletions
diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index faa918bf..79558f2e 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -55,9 +55,10 @@ static const char* const valid_modargs[] = {  struct filter {      char *name; -    pa_sink* parent_sink;      uint32_t module_index; -    pa_sink* sink; +    pa_bool_t is_sink; +    pa_object *parent_obj;      /* source or sink that the filter is connected to */ +    pa_object *obj;             /* source or sink of the filter */  };  struct userdata { @@ -68,7 +69,12 @@ struct userdata {          *sink_input_move_finish_slot,          *sink_input_proplist_slot,          *sink_input_unlink_slot, -        *sink_unlink_slot; +        *sink_unlink_slot, +        *source_output_put_slot, +        *source_output_move_finish_slot, +        *source_output_proplist_slot, +        *source_output_unlink_slot, +        *source_unlink_slot;      pa_bool_t autoclean;      pa_time_event *housekeeping_time_event;  }; @@ -76,16 +82,17 @@ struct userdata {  static unsigned filter_hash(const void *p) {      const struct filter *f = p; -    return -        (unsigned) f->parent_sink->index + -        pa_idxset_string_hash_func(f->name); +    if (f->is_sink) +        return (unsigned) (PA_SINK(f->parent_obj)->index + pa_idxset_string_hash_func(f->name)); +    else +        return (unsigned) ((PA_SOURCE(f->parent_obj)->index << 16) + pa_idxset_string_hash_func(f->name));  }  static int filter_compare(const void *a, const void *b) {      const struct filter *fa = a, *fb = b;      int r; -    if (fa->parent_sink != fb->parent_sink) +    if (fa->parent_obj != fb->parent_obj)          return 1;      if ((r = strcmp(fa->name, fb->name)))          return r; @@ -93,14 +100,15 @@ static int filter_compare(const void *a, const void *b) {      return 0;  } -static struct filter *filter_new(const char *name, pa_sink* parent_sink) { +static struct filter *filter_new(const char *name, pa_object* parent_obj, pa_bool_t is_sink) {      struct filter *f;      f = pa_xnew(struct filter, 1);      f->name = pa_xstrdup(name); -    pa_assert_se(f->parent_sink = parent_sink); +    pa_assert_se(f->parent_obj = parent_obj); +    f->is_sink = is_sink;      f->module_index = PA_INVALID_INDEX; -    f->sink = NULL; +    f->obj = NULL;      return f;  } @@ -111,12 +119,18 @@ static void filter_free(struct filter *f) {      pa_xfree(f);  } -static const char* should_filter(pa_sink_input *i) { +static const char* should_filter(pa_object *o, pa_bool_t is_sink_input) {      const char *apply; +    pa_proplist *pl; + +    if (is_sink_input) +        pl = PA_SINK_INPUT(o)->proplist; +    else +        pl = PA_SOURCE_OUTPUT(o)->proplist;      /* If the stream doesn't what any filter, then let it be. */ -    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 ((apply = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY)) && !pa_streq(apply, "")) { +        const char* suppress = pa_proplist_gets(pl, PA_PROP_FILTER_SUPPRESS);          if (!suppress || !pa_streq(suppress, apply))              return apply; @@ -125,6 +139,14 @@ static const char* should_filter(pa_sink_input *i) {      return NULL;  } +static pa_bool_t nothing_attached(pa_object *obj, pa_bool_t is_sink) +{ +    if (is_sink) +        return pa_idxset_isempty(PA_SINK(obj)->inputs); +    else +        return pa_idxset_isempty(PA_SOURCE(obj)->outputs); +} +  static void housekeeping_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {      struct userdata *u = userdata;      struct filter *filter; @@ -139,10 +161,10 @@ static void housekeeping_time_callback(pa_mainloop_api*a, pa_time_event* e, cons      u->housekeeping_time_event = NULL;      PA_HASHMAP_FOREACH(filter, u->filters, state) { -        if (filter->sink && pa_idxset_size(filter->sink->inputs) == 0) { +        if (filter->obj && nothing_attached(filter->obj, filter->is_sink)) {              uint32_t idx; -            pa_log_debug("Detected filter %s as no longer used on sink %s. Unloading.", filter->name, filter->sink->name); +            pa_log_debug("Detected filter %s as no longer used. Unloading.", filter->name);              idx = filter->module_index;              pa_hashmap_remove(u->filters, filter);              filter_free(filter); @@ -165,98 +187,157 @@ static void trigger_housekeeping(struct userdata *u) {      u->housekeeping_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + HOUSEKEEPING_INTERVAL, housekeeping_time_callback, u);  } -static void move_input_for_filter(pa_sink_input *i, struct filter* filter, pa_bool_t restore) { -    pa_sink *sink; +static int do_move(pa_object *obj, pa_object *parent, pa_bool_t restore, pa_bool_t is_input) { +    if (is_input) +        return pa_sink_input_move_to(PA_SINK_INPUT(obj), PA_SINK(parent), restore); +    else +        return pa_source_output_move_to(PA_SOURCE_OUTPUT(obj), PA_SOURCE(parent), restore); +} -    pa_assert(i); +static void move_object_for_filter(pa_object *o, struct filter* filter, pa_bool_t restore, pa_bool_t is_sink_input) { +    pa_object *parent; +    pa_proplist *pl; +    const char *name; + +    pa_assert(o);      pa_assert(filter); -    pa_assert_se(sink = (restore ? filter->parent_sink : filter->sink)); +    pa_assert_se(parent = (restore ? filter->parent_obj : filter->obj)); -    pa_proplist_sets(i->proplist, PA_PROP_FILTER_APPLY_MOVING, "1"); +    if (is_sink_input) { +        pl = PA_SINK_INPUT(o)->proplist; +        name = PA_SINK(parent)->name; +    } else { +        pl = PA_SOURCE_OUTPUT(o)->proplist; +        name = PA_SOURCE(parent)->name; +    } -    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); +    pa_proplist_sets(pl, PA_PROP_FILTER_APPLY_MOVING, "1"); + +    if (do_move(o, parent, FALSE, is_sink_input) < 0) +        pa_log_info("Failed to move %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output", +                    pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), 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_log_info("Sucessfully moved %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output", +                    pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), name); -    pa_proplist_unset(i->proplist, PA_PROP_FILTER_APPLY_MOVING); +    pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_MOVING);  } -static pa_hook_result_t process(struct userdata *u, pa_sink_input *i) { +static void find_filters_for_module(struct userdata *u, pa_module *m, const char *name) { +    uint32_t idx; +    pa_sink *sink; +    pa_source *source; +    struct filter *fltr; + +    PA_IDXSET_FOREACH(sink, u->core->sinks, idx) { +        if (sink->module == m) { +            pa_assert(sink->input_to_master != NULL); + +            fltr = filter_new(name, PA_OBJECT(sink->input_to_master->sink), TRUE); +            fltr->module_index = m->index; +            fltr->obj = PA_OBJECT(sink); + +            pa_hashmap_put(u->filters, fltr, fltr); +        } +    } + +    PA_IDXSET_FOREACH(source, u->core->sources, idx) { +        if (source->module == m && !source->monitor_of) { +            pa_assert(source->output_from_master != NULL); + +            fltr = filter_new(name, PA_OBJECT(source->output_from_master->source), FALSE); +            fltr->module_index = m->index; +            fltr->obj = PA_OBJECT(source); + +            pa_hashmap_put(u->filters, fltr, fltr); +        } +    } +} + +static pa_bool_t can_unload_module(struct userdata *u, uint32_t idx) { +    void *state; +    struct filter *filter; + +    /* Check if any other struct filters point to the same module */ +    PA_HASHMAP_FOREACH(filter, u->filters, state) { +        if (filter->module_index == idx && !nothing_attached(filter->obj, pa_sink_isinstance(filter->obj))) +            return FALSE; +    } + +    return TRUE; +} + +static pa_hook_result_t process(struct userdata *u, pa_object *o, pa_bool_t is_sink_input) {      const char *want;      pa_bool_t done_something = FALSE; -    pa_assert(u); -    pa_sink_input_assert_ref(i); +    pa_object *parent; /* source/sink of the given source-output/sink-input */ +    const char *parent_name; +    pa_module *module; + +    if (is_sink_input) { +        parent = PA_OBJECT(PA_SINK_INPUT(o)->sink); +        parent_name = PA_SINK_INPUT(o)->sink->name; +        module = PA_SINK_INPUT(o)->sink->module; +    } else { +        parent = PA_OBJECT(PA_SOURCE_OUTPUT(o)->source); +        parent_name = PA_SOURCE_OUTPUT(o)->source->name; +        module = PA_SOURCE_OUTPUT(o)->source->module; +    }      /* If there is no sink yet, we can't do much */ -    if (!i->sink) +    if (!parent)          return PA_HOOK_OK;      /* If the stream doesn't what any filter, then let it be. */ -    if ((want = should_filter(i))) { +    if ((want = should_filter(o, is_sink_input))) {          char *module_name;          struct filter *fltr, *filter;          /* We need to ensure the SI is playing on a sink of this type           * attached to the sink it's "officially" playing on */ -        if (!i->sink->module) +        if (!module)              return PA_HOOK_OK;          module_name = pa_sprintf_malloc("module-%s", want); -        if (pa_streq(i->sink->module->name, module_name)) { +        if (pa_streq(module->name, module_name)) {              pa_log_debug("Stream appears to be playing on an appropriate sink already. Ignoring.");              pa_xfree(module_name);              return PA_HOOK_OK;          } -        fltr = filter_new(want, i->sink); +        fltr = filter_new(want, parent, is_sink_input);          if (!(filter = pa_hashmap_get(u->filters, fltr))) {              char *args;              pa_module *m; -            args = pa_sprintf_malloc("sink_master=%s", i->sink->name); +            args = pa_sprintf_malloc("%s_master=%s", is_sink_input ? "sink" : "source", parent_name);              pa_log_debug("Loading %s with arguments '%s'", module_name, args);              if ((m = pa_module_load(u->core, module_name, args))) { -                uint32_t idx; -                pa_sink *sink; - -                fltr->module_index = m->index; -                /* We cannot use the SINK_PUT hook here to detect our sink as it'll -                 * be called during the module load so we wont yet have put the filter -                 * in our hashmap to compare... so we have to search for it */ -                PA_IDXSET_FOREACH(sink, u->core->sinks, idx) { -                    if (sink->module == m) { -                        fltr->sink = sink; -                        break; -                    } -                } -                pa_hashmap_put(u->filters, fltr, fltr); -                filter = fltr; -                fltr = NULL; +                find_filters_for_module(u, m, want); +                filter = pa_hashmap_get(u->filters, fltr);                  done_something = TRUE;              }              pa_xfree(args);          } +          pa_xfree(fltr);          if (!filter) { -            pa_log("Unable to load %s for sink <%s>", module_name, i->sink->name); +            pa_log("Unable to load %s for <%s>", module_name, parent_name);              pa_xfree(module_name);              return PA_HOOK_OK;          }          pa_xfree(module_name); -        if (filter->sink) { +        if (filter->obj) {              /* We can move the sink_input now as the know the destination.               * If this isn't true, we will do it later when the sink appears. */ -            move_input_for_filter(i, filter, FALSE); +            move_object_for_filter(o, filter, FALSE, is_sink_input);              done_something = TRUE;          }      } else { @@ -266,8 +347,8 @@ static pa_hook_result_t process(struct userdata *u, pa_sink_input *i) {          /* 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 (i->sink == filter->sink) { -                move_input_for_filter(i, filter, TRUE); +            if (parent == filter->obj) { +                move_object_for_filter(o, filter, TRUE, is_sink_input);                  done_something = TRUE;                  break;              } @@ -284,7 +365,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, i); +    return process(u, PA_OBJECT(i), TRUE);  }  static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -294,14 +375,14 @@ 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; -    return process(u, i); +    return process(u, PA_OBJECT(i), 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, i); +    return process(u, PA_OBJECT(i), TRUE);  }  static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -327,29 +408,102 @@ static pa_hook_result_t sink_unlink_cb(pa_core *core, pa_sink *sink, struct user      /* If either the parent or the sink we've loaded disappears,       * we should remove it from our hashmap */      PA_HASHMAP_FOREACH(filter, u->filters, state) { -        if (filter->parent_sink == sink || filter->sink == sink) { +        if (filter->parent_obj == PA_OBJECT(sink) || filter->obj == PA_OBJECT(sink)) {              uint32_t idx;              /* Attempt to rescue any streams to the parent sink as this is likely               * the best course of action (as opposed to a generic rescue via               * module-rescue-streams */ -            if (filter->sink == sink) { +            if (filter->obj == PA_OBJECT(sink)) {                  pa_sink_input *i;                  PA_IDXSET_FOREACH(i, sink->inputs, idx) -                    move_input_for_filter(i, filter, TRUE); +                    move_object_for_filter(PA_OBJECT(i), filter, TRUE, TRUE);              }              idx = filter->module_index;              pa_hashmap_remove(u->filters, filter);              filter_free(filter); -            pa_module_unload_request_by_index(u->core, idx, TRUE); + +            if (can_unload_module(u, idx)) +                pa_module_unload_request_by_index(u->core, idx, TRUE);          }      }      return PA_HOOK_OK;  } +static pa_hook_result_t source_output_put_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); +} + +static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, struct userdata *u) { +    pa_core_assert_ref(core); +    pa_source_output_assert_ref(o); + +    if (pa_proplist_gets(o->proplist, PA_PROP_FILTER_APPLY_MOVING)) +        return PA_HOOK_OK; + +    return process(u, PA_OBJECT(o), 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); +} + +static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, struct userdata *u) { +    pa_core_assert_ref(core); +    pa_source_output_assert_ref(o); + +    pa_assert(u); + +    if (pa_hashmap_size(u->filters) > 0) +        trigger_housekeeping(u); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t source_unlink_cb(pa_core *core, pa_source *source, struct userdata *u) { +    void *state; +    struct filter *filter = NULL; + +    pa_core_assert_ref(core); +    pa_source_assert_ref(source); +    pa_assert(u); + +    /* If either the parent or the source we've loaded disappears, +     * we should remove it from our hashmap */ +    PA_HASHMAP_FOREACH(filter, u->filters, state) { +        if (filter->parent_obj == PA_OBJECT(source) || filter->obj == PA_OBJECT(source)) { +            uint32_t idx; + +            /* Attempt to rescue any streams to the parent source as this is likely +             * the best course of action (as opposed to a generic rescue via +             * module-rescue-streams */ +            if (filter->obj == PA_OBJECT(source)) { +                pa_source_output *o; + +                PA_IDXSET_FOREACH(o, source->outputs, idx) +                    move_object_for_filter(PA_OBJECT(o), filter, TRUE, FALSE); +            } + +            idx = filter->module_index; +            pa_hashmap_remove(u->filters, filter); +            filter_free(filter); + +            if (can_unload_module(u, idx)) +                pa_module_unload_request_by_index(u->core, idx, TRUE); +        } +    } + +    return PA_HOOK_OK; +}  int pa__init(pa_module *m) {      pa_modargs *ma = NULL; @@ -379,6 +533,11 @@ int pa__init(pa_module *m) {      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); +    u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_output_put_cb, u); +    u->source_output_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) source_output_move_finish_cb, u); +    u->source_output_proplist_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) source_output_proplist_cb, u); +    u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_output_unlink_cb, u); +    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_cb, u);      pa_modargs_free(ma); @@ -411,6 +570,16 @@ void pa__done(pa_module *m) {          pa_hook_slot_free(u->sink_input_unlink_slot);      if (u->sink_unlink_slot)          pa_hook_slot_free(u->sink_unlink_slot); +    if (u->source_output_put_slot) +        pa_hook_slot_free(u->source_output_put_slot); +    if (u->source_output_move_finish_slot) +        pa_hook_slot_free(u->source_output_move_finish_slot); +    if (u->source_output_proplist_slot) +        pa_hook_slot_free(u->source_output_proplist_slot); +    if (u->source_output_unlink_slot) +        pa_hook_slot_free(u->source_output_unlink_slot); +    if (u->source_unlink_slot) +        pa_hook_slot_free(u->source_unlink_slot);      if (u->housekeeping_time_event)          u->core->mainloop->time_free(u->housekeeping_time_event); diff --git a/src/modules/module-filter-heuristics.c b/src/modules/module-filter-heuristics.c index 20a48355..4fba2913 100644 --- a/src/modules/module-filter-heuristics.c +++ b/src/modules/module-filter-heuristics.c @@ -49,30 +49,41 @@ struct userdata {      pa_core *core;      pa_hook_slot          *sink_input_put_slot, -        *sink_input_move_finish_slot; +        *sink_input_move_finish_slot, +        *source_output_put_slot, +        *source_output_move_finish_slot;  }; -static pa_hook_result_t process(struct userdata *u, pa_sink_input *i) { -    const char *want, *sink_role, *si_role; +static pa_hook_result_t process(struct userdata *u, pa_object *o, pa_bool_t is_sink_input) { +    const char *want, *int_role, *stream_role; +    pa_proplist *pl, *parent_pl; + +    if (is_sink_input) { +        pl = PA_SINK_INPUT(o)->proplist; +        parent_pl = PA_SINK_INPUT(o)->sink->proplist; +    } else { +        pl = PA_SOURCE_OUTPUT(o)->proplist; +        parent_pl = PA_SOURCE_OUTPUT(o)->source->proplist; +    }      /* 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)) +    if (!pa_proplist_gets(pl, PA_PROP_FILTER_HEURISTICS) && pa_proplist_gets(pl, PA_PROP_FILTER_APPLY))          return PA_HOOK_OK; -    want = pa_proplist_gets(i->proplist, PA_PROP_FILTER_WANT); +    want = pa_proplist_gets(pl, 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")) +        if ((stream_role = pa_proplist_gets(pl, PA_PROP_MEDIA_ROLE)) && pa_streq(stream_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 ((int_role = pa_proplist_gets(parent_pl, PA_PROP_DEVICE_INTENDED_ROLES)) && strstr(int_role, "phone")) { +        const char *apply = pa_proplist_gets(pl, 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); +            pa_proplist_unset(pl, PA_PROP_FILTER_APPLY); +            pa_proplist_unset(pl, PA_PROP_FILTER_HEURISTICS);          }          return PA_HOOK_OK; @@ -80,8 +91,8 @@ static pa_hook_result_t process(struct userdata *u, pa_sink_input *i) {      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"); +        pa_proplist_sets(pl, PA_PROP_FILTER_APPLY, want); +        pa_proplist_sets(pl, PA_PROP_FILTER_HEURISTICS, "1");      }      return PA_HOOK_OK; @@ -92,7 +103,7 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struc      pa_sink_input_assert_ref(i);      pa_assert(u); -    return process(u, i); +    return process(u, PA_OBJECT(i), TRUE);  }  static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { @@ -104,7 +115,27 @@ 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; -    return process(u, i); +    return process(u, PA_OBJECT(i), TRUE); +} + +static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *i, struct userdata *u) { +    pa_core_assert_ref(core); +    pa_source_output_assert_ref(i); +    pa_assert(u); + +    return process(u, PA_OBJECT(i), FALSE); +} + +static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *i, struct userdata *u) { +    pa_core_assert_ref(core); +    pa_source_output_assert_ref(i); +    pa_assert(u); + +    /* module-filter-apply triggered this move, ignore */ +    if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING)) +        return PA_HOOK_OK; + +    return process(u, PA_OBJECT(i), FALSE);  }  int pa__init(pa_module *m) { @@ -124,6 +155,8 @@ int pa__init(pa_module *m) {      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); +    u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE-1, (pa_hook_cb_t) source_output_put_cb, u); +    u->source_output_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_LATE-1, (pa_hook_cb_t) source_output_move_finish_cb, u);      pa_modargs_free(ma); @@ -152,6 +185,10 @@ void pa__done(pa_module *m) {          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->source_output_put_slot) +        pa_hook_slot_free(u->source_output_put_slot); +    if (u->source_output_move_finish_slot) +        pa_hook_slot_free(u->source_output_move_finish_slot);      pa_xfree(u);  | 
