1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2011 Colin Guthrie
5 
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <pulse/timeval.h>
25 #include <pulse/rtclock.h>
26 #include <pulse/xmalloc.h>
27 
28 #include <pulsecore/core.h>
29 #include <pulsecore/core-util.h>
30 #include <pulsecore/i18n.h>
31 #include <pulsecore/macro.h>
32 #include <pulsecore/hashmap.h>
33 #include <pulsecore/hook-list.h>
34 #include <pulsecore/sink-input.h>
35 #include <pulsecore/modargs.h>
36 #include <pulsecore/proplist-util.h>
37 
38 #define PA_PROP_FILTER_APPLY_PARAMETERS PA_PROP_FILTER_APPLY".%s.parameters"
39 #define PA_PROP_FILTER_APPLY_MOVING     "filter.apply.moving"
40 #define PA_PROP_FILTER_APPLY_SET_BY_MFA "filter.apply.set_by_mfa"
41 #define PA_PROP_MDM_AUTO_FILTERED       "module-device-manager.auto_filtered"
42 
43 PA_MODULE_AUTHOR("Colin Guthrie");
44 PA_MODULE_DESCRIPTION("Load filter sinks automatically when needed");
45 PA_MODULE_VERSION(PACKAGE_VERSION);
46 PA_MODULE_LOAD_ONCE(true);
47 PA_MODULE_USAGE(_("autoclean=<automatically unload unused filters?>"));
48 
49 static const char* const valid_modargs[] = {
50     "autoclean",
51     NULL
52 };
53 
54 #define DEFAULT_AUTOCLEAN true
55 #define HOUSEKEEPING_INTERVAL (10 * PA_USEC_PER_SEC)
56 
57 struct filter {
58     char *name;
59     char *parameters;
60     uint32_t module_index;
61     pa_sink *sink;
62     pa_sink *sink_master;
63     pa_source *source;
64     pa_source *source_master;
65 };
66 
67 struct userdata {
68     pa_core *core;
69     pa_hashmap *filters;
70     /* Keep track of streams we're managing PA_PROP_MDM_AUTO_FILTERED on, we're
71      * only maintaining membership, so key and value are just the
72      * pa_sink_input/pa_source_output. */
73     pa_hashmap *mdm_ignored_inputs, *mdm_ignored_outputs;
74     bool autoclean;
75     pa_time_event *housekeeping_time_event;
76 };
77 
filter_hash(const void *p)78 static unsigned filter_hash(const void *p) {
79     const struct filter *f = p;
80 
81     if (f->sink_master && !f->source_master)
82         return (unsigned) (f->sink_master->index + pa_idxset_string_hash_func(f->name));
83     else if (!f->sink_master && f->source_master)
84         return (unsigned) ((f->source_master->index << 16) + pa_idxset_string_hash_func(f->name));
85     else
86         return (unsigned) (f->sink_master->index + (f->source_master->index << 16) + pa_idxset_string_hash_func(f->name));
87 }
88 
filter_compare(const void *a, const void *b)89 static int filter_compare(const void *a, const void *b) {
90     const struct filter *fa = a, *fb = b;
91     int r;
92 
93     if (fa->sink_master != fb->sink_master || fa->source_master != fb->source_master)
94         return 1;
95     if ((r = strcmp(fa->name, fb->name)))
96         return r;
97 
98     return 0;
99 }
100 
filter_new(const char *name, const char *parameters, pa_sink *sink, pa_source *source)101 static struct filter *filter_new(const char *name, const char *parameters, pa_sink *sink, pa_source *source) {
102     struct filter *f;
103 
104     pa_assert(sink || source);
105 
106     f = pa_xnew(struct filter, 1);
107     f->name = pa_xstrdup(name);
108     f->parameters = pa_xstrdup(parameters);
109     f->sink_master = sink;
110     f->source_master = source;
111     f->module_index = PA_INVALID_INDEX;
112     f->sink = NULL;
113     f->source = NULL;
114 
115     return f;
116 }
117 
filter_free(struct filter *f)118 static void filter_free(struct filter *f) {
119     if (f) {
120         pa_xfree(f->name);
121         pa_xfree(f->parameters);
122         pa_xfree(f);
123     }
124 }
125 
get_filter_name(pa_object *o, bool is_sink_input)126 static const char* get_filter_name(pa_object *o, bool is_sink_input) {
127     const char *apply;
128     pa_proplist *pl;
129 
130     if (is_sink_input)
131         pl = PA_SINK_INPUT(o)->proplist;
132     else
133         pl = PA_SOURCE_OUTPUT(o)->proplist;
134 
135     /* If the stream doesn't want any filter, then let it be. */
136     if ((apply = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY)) && !pa_streq(apply, "")) {
137         const char* suppress = pa_proplist_gets(pl, PA_PROP_FILTER_SUPPRESS);
138 
139         if (!suppress || !pa_streq(suppress, apply))
140             return apply;
141     }
142 
143     return NULL;
144 }
145 
get_filter_parameters(pa_object *o, const char *want, bool is_sink_input)146 static const char* get_filter_parameters(pa_object *o, const char *want, bool is_sink_input) {
147     const char *parameters;
148     char *prop_parameters;
149     pa_proplist *pl, *device_pl;
150 
151     if (is_sink_input) {
152         pl = PA_SINK_INPUT(o)->proplist;
153         device_pl = PA_SINK_INPUT(o)->sink->proplist;
154     } else {
155         pl = PA_SOURCE_OUTPUT(o)->proplist;
156         device_pl = PA_SOURCE_OUTPUT(o)->source->proplist;
157     }
158 
159     prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, want);
160     parameters = pa_proplist_gets(pl, prop_parameters);
161     if (!parameters)
162         parameters = pa_proplist_gets(device_pl, prop_parameters);
163     pa_xfree(prop_parameters);
164 
165     return parameters;
166 }
167 
168 /* This function is used to set or unset the filter related stream properties. This is necessary
169  * if a stream does not have filter.apply set and is manually moved to a filter sink or source.
170  * In this case, the properties must be temporarily set and removed when the stream is moved away
171  * from the filter. */
set_filter_properties(pa_proplist *pl, struct filter *filter, bool set_properties)172 static void set_filter_properties(pa_proplist *pl, struct filter *filter, bool set_properties) {
173     char *prop_parameters;
174 
175     if (set_properties) {
176         pa_assert(filter);
177 
178         pa_proplist_sets(pl, PA_PROP_FILTER_APPLY, filter->name);
179 
180         if (filter->parameters) {
181             prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, filter->name);
182             pa_proplist_sets(pl, prop_parameters, filter->parameters);
183             pa_xfree(prop_parameters);
184         }
185 
186         pa_proplist_sets(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA, "1");
187 
188     } else {
189         const char *old_filter_name = NULL;
190 
191         if (filter)
192             old_filter_name = filter->name;
193         else
194             old_filter_name = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY);
195 
196         /* If the filter name cannot be determined, properties cannot be removed. */
197         if (!old_filter_name)
198             return;
199 
200         prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, old_filter_name);
201         pa_proplist_unset(pl, prop_parameters);
202         pa_xfree(prop_parameters);
203 
204         pa_proplist_unset(pl, PA_PROP_FILTER_APPLY);
205         pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA);
206     }
207 }
208 
get_filter_for_object(struct userdata *u, pa_object *o, bool is_sink_input)209 static struct filter* get_filter_for_object(struct userdata *u, pa_object *o, bool is_sink_input) {
210     pa_sink *sink = NULL;
211     pa_source *source = NULL;
212     struct filter *filter = NULL;
213     void *state;
214 
215     if (is_sink_input)
216         sink = PA_SINK_INPUT(o)->sink;
217     else
218         source = PA_SOURCE_OUTPUT(o)->source;
219 
220     PA_HASHMAP_FOREACH(filter, u->filters, state) {
221         if ((is_sink_input && sink == filter->sink) || (!is_sink_input && source == filter->source)) {
222             return filter;
223         }
224     }
225 
226     return NULL;
227 }
228 
should_group_filter(struct filter *filter)229 static bool should_group_filter(struct filter *filter) {
230     return pa_streq(filter->name, "echo-cancel");
231 }
232 
get_group(pa_object *o, bool is_sink_input)233 static char* get_group(pa_object *o, bool is_sink_input) {
234     pa_proplist *pl;
235 
236     if (is_sink_input)
237         pl = PA_SINK_INPUT(o)->proplist;
238     else
239         pl = PA_SOURCE_OUTPUT(o)->proplist;
240 
241     /* There's a bit of cleverness here -- the second argument ensures that we
242      * only group streams that require the same filter */
243     return pa_proplist_get_stream_group(pl, pa_proplist_gets(pl, PA_PROP_FILTER_APPLY), NULL);
244 }
245 
246 /* For filters that apply on a source-output/sink-input pair, this finds the
247  * master sink if we know the master source, or vice versa. It does this by
248  * looking up streams that belong to the same stream group as the original
249  * object. The idea is that streams from the sam group are always routed
250  * together. */
find_paired_master(struct userdata *u, struct filter *filter, pa_object *o, bool is_sink_input)251 static bool find_paired_master(struct userdata *u, struct filter *filter, pa_object *o, bool is_sink_input) {
252     char *group;
253 
254     if ((group = get_group(o, is_sink_input))) {
255         uint32_t idx;
256         char *g;
257         char *module_name = pa_sprintf_malloc("module-%s", filter->name);
258 
259         if (is_sink_input) {
260             pa_source_output *so;
261 
262             PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
263                 g = get_group(PA_OBJECT(so), false);
264 
265                 if (pa_streq(g, group)) {
266                     if (pa_streq(module_name, so->source->module->name)) {
267                         /* Make sure we are not routing to the monitor source
268                          * of the same filter */
269                         if (so->source->monitor_of) {
270                             pa_xfree(g);
271                             continue;
272                         }
273                         /* Make sure we're not routing to another instance of
274                          * the same filter. */
275                         filter->source_master = so->source->output_from_master->source;
276                     } else {
277                         filter->source_master = so->source;
278                     }
279 
280                     pa_xfree(g);
281                     break;
282                 }
283 
284                 pa_xfree (g);
285             }
286         } else {
287             pa_sink_input *si;
288 
289             PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
290                 g = get_group(PA_OBJECT(si), true);
291 
292                 if (pa_streq(g, group)) {
293                     if (pa_streq(module_name, si->sink->module->name)) {
294                         /* Make sure we're not routing to another instance of
295                          * the same filter. */
296                         filter->sink_master = si->sink->input_to_master->sink;
297                     } else {
298                         filter->sink_master = si->sink;
299                     }
300 
301                     pa_xfree(g);
302                     break;
303                 }
304 
305                 pa_xfree(g);
306             }
307         }
308 
309         pa_xfree(group);
310         pa_xfree(module_name);
311 
312         if (!filter->sink_master || !filter->source_master)
313             return false;
314     }
315 
316     return true;
317 }
318 
nothing_attached(struct filter *f)319 static bool nothing_attached(struct filter *f) {
320     bool no_si = true, no_so = true;
321 
322     if (f->sink)
323         no_si = pa_idxset_isempty(f->sink->inputs);
324     if (f->source)
325         no_so = pa_idxset_isempty(f->source->outputs);
326 
327     return no_si && no_so;
328 }
329 
housekeeping_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata)330 static void housekeeping_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
331     struct userdata *u = userdata;
332     struct filter *filter;
333     void *state;
334 
335     pa_assert(a);
336     pa_assert(e);
337     pa_assert(u);
338 
339     pa_assert(e == u->housekeeping_time_event);
340     u->core->mainloop->time_free(u->housekeeping_time_event);
341     u->housekeeping_time_event = NULL;
342 
343     PA_HASHMAP_FOREACH(filter, u->filters, state) {
344         if (nothing_attached(filter)) {
345             uint32_t idx;
346 
347             pa_log_debug("Detected filter %s as no longer used. Unloading.", filter->name);
348             idx = filter->module_index;
349             pa_hashmap_remove(u->filters, filter);
350             filter_free(filter);
351             pa_module_unload_request_by_index(u->core, idx, true);
352         }
353     }
354 
355     pa_log_info("Housekeeping Done.");
356 }
357 
trigger_housekeeping(struct userdata *u)358 static void trigger_housekeeping(struct userdata *u) {
359     pa_assert(u);
360 
361     if (!u->autoclean)
362         return;
363 
364     if (u->housekeeping_time_event)
365         return;
366 
367     u->housekeeping_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + HOUSEKEEPING_INTERVAL, housekeeping_time_callback, u);
368 }
369 
do_move(struct userdata *u, pa_object *obj, pa_object *parent, bool is_input)370 static int do_move(struct userdata *u, pa_object *obj, pa_object *parent, bool is_input) {
371     /* Keep track of objects that we've marked for module-device-manager to ignore */
372     pa_hashmap_put(is_input ? u->mdm_ignored_inputs : u->mdm_ignored_outputs, obj, obj);
373 
374     if (is_input) {
375         pa_sink_input_set_property(PA_SINK_INPUT(obj), PA_PROP_MDM_AUTO_FILTERED, "1");
376         return pa_sink_input_move_to(PA_SINK_INPUT(obj), PA_SINK(parent), false);
377     } else {
378         pa_source_output_set_property(PA_SOURCE_OUTPUT(obj), PA_PROP_MDM_AUTO_FILTERED, "1");
379         return pa_source_output_move_to(PA_SOURCE_OUTPUT(obj), PA_SOURCE(parent), false);
380     }
381 }
382 
move_object_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore, bool is_sink_input)383 static void move_object_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore, bool is_sink_input) {
384     pa_object *parent;
385     pa_proplist *pl;
386     const char *name;
387 
388     pa_assert(o);
389     pa_assert(filter);
390 
391     if (is_sink_input) {
392         pl = PA_SINK_INPUT(o)->proplist;
393         parent = PA_OBJECT(restore ? filter->sink_master : filter->sink);
394         if (!parent)
395             return;
396         name = PA_SINK(parent)->name;
397     } else {
398         pl = PA_SOURCE_OUTPUT(o)->proplist;
399         parent = PA_OBJECT(restore ? filter->source_master : filter->source);
400         if (!parent)
401             return;
402         name = PA_SOURCE(parent)->name;
403     }
404 
405     pa_proplist_sets(pl, PA_PROP_FILTER_APPLY_MOVING, "1");
406 
407     if (do_move(u, o, parent, is_sink_input) < 0)
408         pa_log_info("Failed to move %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output",
409                     pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), name);
410     else
411         pa_log_info("Successfully moved %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output",
412                     pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), name);
413 
414     pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_MOVING);
415 }
416 
move_objects_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore, bool is_sink_input)417 static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter *filter, bool restore,
418         bool is_sink_input) {
419 
420     if (!should_group_filter(filter))
421         move_object_for_filter(u, o, filter, restore, is_sink_input);
422     else {
423         pa_source_output *so;
424         pa_sink_input *si;
425         char *g, *group;
426         uint32_t idx;
427 
428         group = get_group(o, is_sink_input);
429 
430         PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
431             g = get_group(PA_OBJECT(so), false);
432 
433             if (pa_streq(g, group))
434                 move_object_for_filter(u, PA_OBJECT(so), filter, restore, false);
435 
436             pa_xfree(g);
437         }
438 
439         PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
440             g = get_group(PA_OBJECT(si), true);
441 
442             if (pa_streq(g, group))
443                 move_object_for_filter(u, PA_OBJECT(si), filter, restore, true);
444 
445             pa_xfree(g);
446         }
447 
448         pa_xfree(group);
449     }
450 }
451 
452 /* Note that we assume a filter will provide at most one sink and at most one
453  * source (and at least one of either). */
find_filters_for_module(struct userdata *u, pa_module *m, const char *name, const char *parameters)454 static void find_filters_for_module(struct userdata *u, pa_module *m, const char *name, const char *parameters) {
455     uint32_t idx;
456     pa_sink *sink;
457     pa_source *source;
458     struct filter *fltr = NULL;
459 
460     PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
461         if (sink->module == m) {
462             pa_assert(pa_sink_is_filter(sink));
463 
464             fltr = filter_new(name, parameters, sink->input_to_master->sink, NULL);
465             fltr->module_index = m->index;
466             fltr->sink = sink;
467 
468             break;
469         }
470     }
471 
472     PA_IDXSET_FOREACH(source, u->core->sources, idx) {
473         if (source->module == m && !source->monitor_of) {
474             pa_assert(pa_source_is_filter(source));
475 
476             if (!fltr) {
477                 fltr = filter_new(name, parameters, NULL, source->output_from_master->source);
478                 fltr->module_index = m->index;
479                 fltr->source = source;
480             } else {
481                 fltr->source = source;
482                 fltr->source_master = source->output_from_master->source;
483             }
484 
485             break;
486         }
487     }
488 
489     pa_hashmap_put(u->filters, fltr, fltr);
490 }
491 
can_unload_module(struct userdata *u, uint32_t idx)492 static bool can_unload_module(struct userdata *u, uint32_t idx) {
493     void *state;
494     struct filter *filter;
495 
496     /* Check if any other struct filters point to the same module */
497     PA_HASHMAP_FOREACH(filter, u->filters, state) {
498         if (filter->module_index == idx && !nothing_attached(filter))
499             return false;
500     }
501 
502     return true;
503 }
504 
process(struct userdata *u, pa_object *o, bool is_sink_input, bool is_property_change)505 static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input, bool is_property_change) {
506     const char *want;
507     const char *parameters;
508     bool done_something = false;
509     pa_sink *sink = NULL;
510     pa_source *source = NULL;
511     pa_module *module = NULL;
512     char *module_name = NULL;
513     struct filter *fltr = NULL, *filter = NULL;
514     pa_proplist *pl;
515 
516     if (is_sink_input) {
517         if ((sink = PA_SINK_INPUT(o)->sink))
518             module = sink->module;
519         pl = PA_SINK_INPUT(o)->proplist;
520     } else {
521         if ((source = PA_SOURCE_OUTPUT(o)->source))
522             module = source->module;
523         pl = PA_SOURCE_OUTPUT(o)->proplist;
524     }
525 
526     /* If there is no sink/source yet, we can't do much */
527     if ((is_sink_input && !sink) || (!is_sink_input && !source))
528         goto done;
529 
530     /* If the stream doesn't want any filter, then let it be. */
531     if ((want = get_filter_name(o, is_sink_input))) {
532         /* We need to ensure the SI is playing on a sink of this type
533          * attached to the sink it's "officially" playing on */
534 
535         if (!module)
536             goto done;
537 
538         module_name = pa_sprintf_malloc("module-%s", want);
539         if (pa_streq(module->name, module_name)) {
540             pa_log_debug("Stream appears to be playing on an appropriate sink already. Ignoring.");
541             goto done;
542         }
543 
544         /* If the stream originally did not have the filter.apply property set and is
545          * manually moved away from the filter, remove the filter properties from the
546          * stream */
547         if (pa_proplist_gets(pl, PA_PROP_FILTER_APPLY_SET_BY_MFA)) {
548 
549             set_filter_properties(pl, NULL, false);
550 
551             /* If the new sink/source is also a filter, the stream has been moved from
552              * one filter to another, so add the properties for the new filter. */
553             if ((filter = get_filter_for_object(u, o, is_sink_input)))
554                 set_filter_properties(pl, filter, true);
555 
556             done_something = true;
557             goto done;
558         }
559 
560         /* The stream needs be moved to a filter. */
561 
562         /* Some filter modules might require parameters by default.
563          * (e.g 'plugin', 'label', 'control' of module-ladspa-sink) */
564         parameters = get_filter_parameters(o, want, is_sink_input);
565 
566         fltr = filter_new(want, parameters, sink, source);
567 
568         if (should_group_filter(fltr) && !find_paired_master(u, fltr, o, is_sink_input)) {
569             pa_log_debug("Want group filtering but don't have enough streams.");
570             goto done;
571         }
572 
573         if (!(filter = pa_hashmap_get(u->filters, fltr))) {
574             char *args;
575             pa_module *m;
576 
577             args = pa_sprintf_malloc("autoloaded=1 %s%s %s%s %s",
578                     fltr->sink_master ? "sink_master=" : "",
579                     fltr->sink_master ? fltr->sink_master->name : "",
580                     fltr->source_master ? "source_master=" : "",
581                     fltr->source_master ? fltr->source_master->name : "",
582                     fltr->parameters ? fltr->parameters : "");
583 
584             pa_log_debug("Loading %s with arguments '%s'", module_name, args);
585 
586             if (pa_module_load(&m, u->core, module_name, args) >= 0) {
587                 find_filters_for_module(u, m, want, parameters);
588                 filter = pa_hashmap_get(u->filters, fltr);
589                 done_something = true;
590             }
591             pa_xfree(args);
592         }
593 
594         if (!filter) {
595             pa_log("Unable to load %s", module_name);
596             goto done;
597         }
598 
599         /* We can move the stream now as we know the destination. If this
600          * isn't true, we will do it later when the sink appears. */
601         if ((is_sink_input && filter->sink) || (!is_sink_input && filter->source)) {
602             move_objects_for_filter(u, o, filter, false, is_sink_input);
603             done_something = true;
604         }
605     } else {
606         /* The filter.apply property is not set. If the stream is nevertheless using a
607          * filter sink/source, it either has been moved to the filter manually or the
608          * user just removed the filter.apply property. */
609 
610         if ((filter = get_filter_for_object(u, o, is_sink_input))) {
611             if (is_property_change) {
612                 /* 'filter.apply' has been manually unset. Do restore. */
613                 move_objects_for_filter(u, o, filter, true, is_sink_input);
614                 set_filter_properties(pl, filter, false);
615                 done_something = true;
616             } else {
617                 /* Stream has been manually moved to a filter sink/source
618                  * without 'filter.apply' set. Leave sink as it is. */
619                 set_filter_properties(pl, filter, true);
620             }
621         }
622     }
623 
624 done:
625     if (done_something)
626         trigger_housekeeping(u);
627 
628     pa_xfree(module_name);
629     filter_free(fltr);
630 
631     return PA_HOOK_OK;
632 }
633 
sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u)634 static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
635     pa_core_assert_ref(core);
636     pa_sink_input_assert_ref(i);
637 
638     return process(u, PA_OBJECT(i), true, false);
639 }
640 
sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u)641 static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
642     pa_core_assert_ref(core);
643     pa_sink_input_assert_ref(i);
644 
645     if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING))
646         return PA_HOOK_OK;
647 
648     /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */
649     pa_hashmap_remove(u->mdm_ignored_inputs, i);
650 
651     return process(u, PA_OBJECT(i), true, false);
652 }
653 
sink_input_proplist_cb(pa_core *core, pa_sink_input *i, struct userdata *u)654 static pa_hook_result_t sink_input_proplist_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
655     pa_core_assert_ref(core);
656     pa_sink_input_assert_ref(i);
657 
658     /* Eliminate nested and redundant hook event that is triggered by
659        pa_sink_input_set_property() in do_move(). */
660     if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING))
661         return PA_HOOK_OK;
662 
663     return process(u, PA_OBJECT(i), true, true);
664 }
665 
sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u)666 static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
667     pa_core_assert_ref(core);
668     pa_sink_input_assert_ref(i);
669 
670     pa_assert(u);
671 
672     if (pa_hashmap_size(u->filters) > 0)
673         trigger_housekeeping(u);
674 
675     pa_hashmap_remove(u->mdm_ignored_inputs, i);
676 
677     return PA_HOOK_OK;
678 }
679 
sink_unlink_cb(pa_core *core, pa_sink *sink, struct userdata *u)680 static pa_hook_result_t sink_unlink_cb(pa_core *core, pa_sink *sink, struct userdata *u) {
681     void *state;
682     struct filter *filter = NULL;
683 
684     pa_core_assert_ref(core);
685     pa_sink_assert_ref(sink);
686     pa_assert(u);
687 
688     /* If either the parent or the sink we've loaded disappears,
689      * we should remove it from our hashmap */
690     PA_HASHMAP_FOREACH(filter, u->filters, state) {
691         if (filter->sink_master == sink || filter->sink == sink) {
692             uint32_t idx;
693 
694             /* Attempt to rescue any streams to the parent sink as this is likely
695              * the best course of action */
696             if (filter->sink == sink) {
697                 pa_sink_input *i;
698 
699                 PA_IDXSET_FOREACH(i, sink->inputs, idx)
700                     move_objects_for_filter(u, PA_OBJECT(i), filter, true, true);
701             }
702 
703             idx = filter->module_index;
704             pa_hashmap_remove(u->filters, filter);
705             filter_free(filter);
706 
707             if (can_unload_module(u, idx))
708                 pa_module_unload_request_by_index(u->core, idx, true);
709         }
710     }
711 
712     return PA_HOOK_OK;
713 }
714 
source_output_put_cb(pa_core *core, pa_source_output *o, struct userdata *u)715 static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
716     pa_core_assert_ref(core);
717     pa_source_output_assert_ref(o);
718 
719     return process(u, PA_OBJECT(o), false, false);
720 }
721 
source_output_move_finish_cb(pa_core *core, pa_source_output *o, struct userdata *u)722 static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
723     pa_core_assert_ref(core);
724     pa_source_output_assert_ref(o);
725 
726     if (pa_proplist_gets(o->proplist, PA_PROP_FILTER_APPLY_MOVING))
727         return PA_HOOK_OK;
728 
729     /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */
730     pa_hashmap_remove(u->mdm_ignored_outputs, o);
731 
732     return process(u, PA_OBJECT(o), false, false);
733 }
734 
source_output_proplist_cb(pa_core *core, pa_source_output *o, struct userdata *u)735 static pa_hook_result_t source_output_proplist_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
736     pa_core_assert_ref(core);
737     pa_source_output_assert_ref(o);
738 
739     /* Eliminate nested and redundant hook event that is triggered by
740        pa_source_output_set_property() in do_move(). */
741     if (pa_proplist_gets(o->proplist, PA_PROP_FILTER_APPLY_MOVING))
742         return PA_HOOK_OK;
743 
744     return process(u, PA_OBJECT(o), false, true);
745 }
746 
source_output_unlink_cb(pa_core *core, pa_source_output *o, struct userdata *u)747 static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
748     pa_core_assert_ref(core);
749     pa_source_output_assert_ref(o);
750 
751     pa_assert(u);
752 
753     if (pa_hashmap_size(u->filters) > 0)
754         trigger_housekeeping(u);
755 
756     pa_hashmap_remove(u->mdm_ignored_outputs, o);
757 
758     return PA_HOOK_OK;
759 }
760 
source_unlink_cb(pa_core *core, pa_source *source, struct userdata *u)761 static pa_hook_result_t source_unlink_cb(pa_core *core, pa_source *source, struct userdata *u) {
762     void *state;
763     struct filter *filter = NULL;
764 
765     pa_core_assert_ref(core);
766     pa_source_assert_ref(source);
767     pa_assert(u);
768 
769     /* If either the parent or the source we've loaded disappears,
770      * we should remove it from our hashmap */
771     PA_HASHMAP_FOREACH(filter, u->filters, state) {
772         if (filter->source_master == source || filter->source == source) {
773             uint32_t idx;
774 
775             /* Attempt to rescue any streams to the parent source as this is likely
776              * the best course of action */
777             if (filter->source == source) {
778                 pa_source_output *o;
779 
780                 PA_IDXSET_FOREACH(o, source->outputs, idx)
781                     move_objects_for_filter(u, PA_OBJECT(o), filter, true, false);
782             }
783 
784             idx = filter->module_index;
785             pa_hashmap_remove(u->filters, filter);
786             filter_free(filter);
787 
788             if (can_unload_module(u, idx))
789                 pa_module_unload_request_by_index(u->core, idx, true);
790         }
791     }
792 
793     return PA_HOOK_OK;
794 }
795 
unset_mdm_ignore_input(pa_sink_input *i)796 static void unset_mdm_ignore_input(pa_sink_input *i)
797 {
798     pa_sink_input_set_property(i, PA_PROP_MDM_AUTO_FILTERED, NULL);
799 }
800 
unset_mdm_ignore_output(pa_source_output *o)801 static void unset_mdm_ignore_output(pa_source_output *o)
802 {
803     pa_source_output_set_property(o, PA_PROP_MDM_AUTO_FILTERED, NULL);
804 }
805 
pa__init(pa_module *m)806 int pa__init(pa_module *m) {
807     pa_modargs *ma = NULL;
808     struct userdata *u;
809 
810     pa_assert(m);
811 
812     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
813         pa_log("Failed to parse module arguments");
814         goto fail;
815     }
816 
817     m->userdata = u = pa_xnew0(struct userdata, 1);
818 
819     u->core = m->core;
820 
821     u->autoclean = DEFAULT_AUTOCLEAN;
822     if (pa_modargs_get_value_boolean(ma, "autoclean", &u->autoclean) < 0) {
823         pa_log("Failed to parse autoclean value");
824         goto fail;
825     }
826 
827     u->filters = pa_hashmap_new(filter_hash, filter_compare);
828     u->mdm_ignored_inputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_input, NULL);
829     u->mdm_ignored_outputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_output, NULL);
830 
831     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);
832     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);
833     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_proplist_cb, u);
834     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
835     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE-1, (pa_hook_cb_t) sink_unlink_cb, u);
836     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_output_put_cb, u);
837     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) source_output_move_finish_cb, u);
838     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) source_output_proplist_cb, u);
839     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_output_unlink_cb, u);
840     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE-1, (pa_hook_cb_t) source_unlink_cb, u);
841 
842     pa_modargs_free(ma);
843 
844     return 0;
845 
846 fail:
847     pa__done(m);
848 
849     if (ma)
850         pa_modargs_free(ma);
851 
852     return -1;
853 }
854 
pa__done(pa_module *m)855 void pa__done(pa_module *m) {
856     struct userdata* u;
857 
858     pa_assert(m);
859 
860     if (!(u = m->userdata))
861         return;
862 
863     if (u->housekeeping_time_event)
864         u->core->mainloop->time_free(u->housekeeping_time_event);
865 
866     if (u->filters) {
867         struct filter *f;
868 
869         while ((f = pa_hashmap_steal_first(u->filters))) {
870             pa_module_unload_request_by_index(u->core, f->module_index, true);
871             filter_free(f);
872         }
873 
874         pa_hashmap_free(u->filters);
875     }
876 
877     if (u->mdm_ignored_inputs)
878         pa_hashmap_free(u->mdm_ignored_inputs);
879 
880     if (u->mdm_ignored_outputs)
881         pa_hashmap_free(u->mdm_ignored_outputs);
882 
883     pa_xfree(u);
884 }
885