• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2     This file is part of PulseAudio.
3 
4     Copyright 2008 Colin Guthrie
5     Copyright 2017 Sebastian Dröge <sebastian@centricular.com>
6 
7     PulseAudio is free software; you can redistribute it and/or modify
8     it under the terms of the GNU Lesser General Public License as published
9     by the Free Software Foundation; either version 2.1 of the License,
10     or (at your option) any later version.
11 
12     PulseAudio is distributed in the hope that it will be useful, but
13     WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public License
18     along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <pulse/xmalloc.h>
26 
27 #include <pulsecore/core.h>
28 #include <pulsecore/core-util.h>
29 #include <pulsecore/i18n.h>
30 #include <pulsecore/source.h>
31 #include <pulsecore/modargs.h>
32 #include <pulsecore/log.h>
33 
34 PA_MODULE_AUTHOR("Sebastian Dröge");
35 PA_MODULE_DESCRIPTION(_("Always keeps at least one source loaded even if it's a null one"));
36 PA_MODULE_VERSION(PACKAGE_VERSION);
37 PA_MODULE_LOAD_ONCE(true);
38 PA_MODULE_USAGE(
39         "source_name=<name of source>");
40 
41 #define DEFAULT_SOURCE_NAME "auto_null"
42 
43 static const char* const valid_modargs[] = {
44     "source_name",
45     NULL,
46 };
47 
48 struct userdata {
49     uint32_t null_module;
50     bool ignore;
51     char *source_name;
52 };
53 
load_null_source_if_needed(pa_core * c,pa_source * source,struct userdata * u)54 static void load_null_source_if_needed(pa_core *c, pa_source *source, struct userdata* u) {
55     pa_source *target;
56     uint32_t idx;
57     char *t;
58     pa_module *m;
59 
60     pa_assert(c);
61     pa_assert(u);
62 
63     if (u->null_module != PA_INVALID_INDEX)
64         return; /* We've already got a null-source loaded */
65 
66     /* Loop through all sources and check to see if we have *any*
67      * sources. Ignore the source passed in (if it's not null), and
68      * don't count filter or monitor sources. */
69     PA_IDXSET_FOREACH(target, c->sources, idx)
70         if (!source || ((target != source) && !pa_source_is_filter(target) && target->monitor_of == NULL))
71             break;
72 
73     if (target)
74         return;
75 
76     pa_log_debug("Autoloading null-source as no other sources detected.");
77 
78     u->ignore = true;
79 
80     t = pa_sprintf_malloc("source_name=%s", u->source_name);
81     pa_module_load(&m, c, "module-null-source", t);
82     u->null_module = m ? m->index : PA_INVALID_INDEX;
83     pa_xfree(t);
84 
85     u->ignore = false;
86 
87     if (!m)
88         pa_log_warn("Unable to load module-null-source");
89 }
90 
put_hook_callback(pa_core * c,pa_source * source,void * userdata)91 static pa_hook_result_t put_hook_callback(pa_core *c, pa_source *source, void* userdata) {
92     struct userdata *u = userdata;
93 
94     pa_assert(c);
95     pa_assert(source);
96     pa_assert(u);
97 
98     /* This is us detecting ourselves on load... just ignore this. */
99     if (u->ignore)
100         return PA_HOOK_OK;
101 
102     /* There's no point in doing anything if the core is shut down anyway */
103     if (c->state == PA_CORE_SHUTDOWN)
104         return PA_HOOK_OK;
105 
106     /* Auto-loaded null-source not active, so ignoring newly detected source. */
107     if (u->null_module == PA_INVALID_INDEX)
108         return PA_HOOK_OK;
109 
110     /* This is us detecting ourselves on load in a different way... just ignore this too. */
111     if (source->module && source->module->index == u->null_module)
112         return PA_HOOK_OK;
113 
114     /* We don't count filter or monitor sources since they need a real source */
115     if (pa_source_is_filter(source) || source->monitor_of != NULL)
116         return PA_HOOK_OK;
117 
118     pa_log_info("A new source has been discovered. Unloading null-source.");
119 
120     pa_module_unload_request_by_index(c, u->null_module, true);
121     u->null_module = PA_INVALID_INDEX;
122 
123     return PA_HOOK_OK;
124 }
125 
unlink_hook_callback(pa_core * c,pa_source * source,void * userdata)126 static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) {
127     struct userdata *u = userdata;
128 
129     pa_assert(c);
130     pa_assert(source);
131     pa_assert(u);
132 
133     /* First check to see if it's our own null-source that's been removed... */
134     if (u->null_module != PA_INVALID_INDEX && source->module && source->module->index == u->null_module) {
135         pa_log_debug("Autoloaded null-source removed");
136         u->null_module = PA_INVALID_INDEX;
137         return PA_HOOK_OK;
138     }
139 
140     /* There's no point in doing anything if the core is shut down anyway */
141     if (c->state == PA_CORE_SHUTDOWN)
142         return PA_HOOK_OK;
143 
144     load_null_source_if_needed(c, source, u);
145 
146     return PA_HOOK_OK;
147 }
148 
pa__init(pa_module * m)149 int pa__init(pa_module*m) {
150     pa_modargs *ma = NULL;
151     struct userdata *u;
152 
153     pa_assert(m);
154 
155     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
156         pa_log("Failed to parse module arguments");
157         return -1;
158     }
159 
160     m->userdata = u = pa_xnew(struct userdata, 1);
161     u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
162     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u);
163     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u);
164     u->null_module = PA_INVALID_INDEX;
165     u->ignore = false;
166 
167     pa_modargs_free(ma);
168 
169     load_null_source_if_needed(m->core, NULL, u);
170 
171     return 0;
172 }
173 
pa__done(pa_module * m)174 void pa__done(pa_module*m) {
175     struct userdata *u;
176 
177     pa_assert(m);
178 
179     if (!(u = m->userdata))
180         return;
181 
182     if (u->null_module != PA_INVALID_INDEX && m->core->state != PA_CORE_SHUTDOWN)
183         pa_module_unload_request_by_index(m->core, u->null_module, true);
184 
185     pa_xfree(u->source_name);
186     pa_xfree(u);
187 }
188