• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2     This file is part of PulseAudio.
3 
4     Copyright 2013 bct electronic GmbH
5     Contributor: Stefan Huber <s.huber@bct-electronic.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 <stdio.h>
26 
27 #include <pulse/xmalloc.h>
28 
29 #include <pulsecore/i18n.h>
30 #include <pulsecore/macro.h>
31 #include <pulsecore/namereg.h>
32 #include <pulsecore/module.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/modargs.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/rtpoll.h>
37 #include <pulsecore/sample-util.h>
38 #include <pulsecore/ltdl-helper.h>
39 #include <pulsecore/mix.h>
40 
41 PA_MODULE_AUTHOR("Stefan Huber");
42 PA_MODULE_DESCRIPTION("Virtual channel remapping source");
43 PA_MODULE_VERSION(PACKAGE_VERSION);
44 PA_MODULE_LOAD_ONCE(false);
45 PA_MODULE_USAGE(
46         "source_name=<name for the source> "
47         "source_properties=<properties for the source> "
48         "master=<name of source to filter> "
49         "master_channel_map=<channel map> "
50         "format=<sample format> "
51         "rate=<sample rate> "
52         "channels=<number of channels> "
53         "channel_map=<channel map> "
54         "resample_method=<resampler> "
55         "remix=<remix channels?>");
56 
57 struct userdata {
58     pa_module *module;
59 
60     pa_source *source;
61     pa_source_output *source_output;
62 
63     bool auto_desc;
64 };
65 
66 static const char* const valid_modargs[] = {
67     "source_name",
68     "source_properties",
69     "master",
70     "master_channel_map",
71     "format",
72     "rate",
73     "channels",
74     "channel_map",
75     "resample_method",
76     "remix",
77     NULL
78 };
79 
80 /* Called from I/O thread context */
source_process_msg_cb(pa_msgobject * o,int code,void * data,int64_t offset,pa_memchunk * chunk)81 static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
82     struct userdata *u = PA_SOURCE(o)->userdata;
83 
84     switch (code) {
85 
86         case PA_SOURCE_MESSAGE_GET_LATENCY:
87 
88             /* The source is _put() before the source output is, so let's
89              * make sure we don't access it in that time. Also, the
90              * source output is first shut down, the source second. */
91             if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
92                 !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
93                 *((int64_t*) data) = 0;
94                 return 0;
95             }
96 
97             *((int64_t*) data) =
98 
99                 /* Get the latency of the master source */
100                 pa_source_get_latency_within_thread(u->source_output->source, true) +
101                 /* Add the latency internal to our source output on top */
102                 pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec);
103 
104             return 0;
105     }
106 
107     return pa_source_process_msg(o, code, data, offset, chunk);
108 }
109 
110 /* Called from main context */
source_set_state_in_main_thread_cb(pa_source * s,pa_source_state_t state,pa_suspend_cause_t suspend_cause)111 static int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause) {
112     struct userdata *u;
113 
114     pa_source_assert_ref(s);
115     pa_assert_se(u = s->userdata);
116 
117     if (!PA_SOURCE_IS_LINKED(state) ||
118         !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state))
119         return 0;
120 
121     pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED);
122     return 0;
123 }
124 
125 /* Called from I/O thread context */
source_update_requested_latency_cb(pa_source * s)126 static void source_update_requested_latency_cb(pa_source *s) {
127     struct userdata *u;
128 
129     pa_source_assert_ref(s);
130     pa_assert_se(u = s->userdata);
131 
132     if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
133         !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state))
134         return;
135 
136     pa_log_debug("Source update requested latency.");
137 
138     /* Just hand this one over to the master source */
139     pa_source_output_set_requested_latency_within_thread(
140             u->source_output,
141             pa_source_get_requested_latency_within_thread(s));
142 }
143 
144 /* Called from output thread context */
source_output_push_cb(pa_source_output * o,const pa_memchunk * chunk)145 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
146     struct userdata *u;
147 
148     pa_source_output_assert_ref(o);
149     pa_source_output_assert_io_context(o);
150     pa_assert_se(u = o->userdata);
151 
152     if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state))
153         return;
154 
155     if (!PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
156         pa_log("push when no link?");
157         return;
158     }
159 
160     pa_source_post(u->source, chunk);
161 }
162 
163 /* Called from output thread context */
source_output_process_rewind_cb(pa_source_output * o,size_t nbytes)164 static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
165     struct userdata *u;
166 
167     pa_source_output_assert_ref(o);
168     pa_source_output_assert_io_context(o);
169     pa_assert_se(u = o->userdata);
170 
171     /* If the source is not yet linked, there is nothing to rewind */
172     if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
173         pa_source_process_rewind(u->source, nbytes);
174 }
175 
176 /* Called from output thread context */
source_output_update_max_rewind_cb(pa_source_output * o,size_t nbytes)177 static void source_output_update_max_rewind_cb(pa_source_output *o, size_t nbytes) {
178     struct userdata *u;
179 
180     pa_source_output_assert_ref(o);
181     pa_source_output_assert_io_context(o);
182     pa_assert_se(u = o->userdata);
183 
184     pa_source_set_max_rewind_within_thread(u->source, nbytes);
185 }
186 
187 /* Called from output thread context */
source_output_detach_cb(pa_source_output * o)188 static void source_output_detach_cb(pa_source_output *o) {
189     struct userdata *u;
190 
191     pa_source_output_assert_ref(o);
192     pa_source_output_assert_io_context(o);
193     pa_assert_se(u = o->userdata);
194 
195     if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
196         pa_source_detach_within_thread(u->source);
197 
198     pa_source_set_rtpoll(u->source, NULL);
199 }
200 
201 /* Called from output thread context */
source_output_attach_cb(pa_source_output * o)202 static void source_output_attach_cb(pa_source_output *o) {
203     struct userdata *u;
204 
205     pa_source_output_assert_ref(o);
206     pa_source_output_assert_io_context(o);
207     pa_assert_se(u = o->userdata);
208 
209     pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll);
210     pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
211     pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
212     pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
213 
214     if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
215         pa_source_attach_within_thread(u->source);
216 }
217 
218 /* Called from main thread */
source_output_kill_cb(pa_source_output * o)219 static void source_output_kill_cb(pa_source_output *o) {
220     struct userdata *u;
221 
222     pa_source_output_assert_ref(o);
223     pa_assert_ctl_context();
224     pa_assert_se(u = o->userdata);
225 
226     /* The order here matters! We first kill the source so that streams
227      * can properly be moved away while the source output is still connected
228      * to the master. */
229     pa_source_output_cork(u->source_output, true);
230     pa_source_unlink(u->source);
231     pa_source_output_unlink(u->source_output);
232 
233     pa_source_output_unref(u->source_output);
234     u->source_output = NULL;
235 
236     pa_source_unref(u->source);
237     u->source = NULL;
238 
239     pa_module_unload_request(u->module, true);
240 }
241 
242 /* Called from output thread context except when cork() is called without valid source. */
source_output_state_change_cb(pa_source_output * o,pa_source_output_state_t state)243 static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
244     struct userdata *u;
245 
246     pa_source_output_assert_ref(o);
247     pa_assert_se(u = o->userdata);
248 
249     pa_log_debug("Source output %d state %d.", o->index, state);
250 }
251 
252 /* Called from main thread */
source_output_moving_cb(pa_source_output * o,pa_source * dest)253 static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
254     struct userdata *u;
255     uint32_t idx;
256     pa_source_output *output;
257 
258     pa_source_output_assert_ref(o);
259     pa_assert_ctl_context();
260     pa_assert_se(u = o->userdata);
261 
262     if (dest) {
263         pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
264         pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
265     } else
266         pa_source_set_asyncmsgq(u->source, NULL);
267 
268     /* Propagate asyncmsq change to attached virtual sources */
269     PA_IDXSET_FOREACH(output, u->source->outputs, idx) {
270         if (output->destination_source && output->moving)
271             output->moving(output, u->source);
272     }
273 
274     if (u->auto_desc && dest) {
275         const char *k;
276         pa_proplist *pl;
277 
278         pl = pa_proplist_new();
279         k = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
280         pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : dest->name);
281 
282         pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl);
283         pa_proplist_free(pl);
284     }
285 }
286 
pa__init(pa_module * m)287 int pa__init(pa_module*m) {
288     struct userdata *u;
289     pa_sample_spec ss;
290     pa_resample_method_t resample_method = PA_RESAMPLER_INVALID;
291     pa_channel_map source_map, stream_map;
292     pa_modargs *ma;
293     pa_source *master;
294     pa_source_output_new_data source_output_data;
295     pa_source_new_data source_data;
296     bool remix = true;
297 
298     pa_assert(m);
299 
300     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
301         pa_log("Failed to parse module arguments.");
302         goto fail;
303     }
304 
305     if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SOURCE))) {
306         pa_log("Master source not found.");
307         goto fail;
308     }
309 
310     ss = master->sample_spec;
311     source_map = master->channel_map;
312     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &source_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
313         pa_log("Invalid sample format specification or channel map.");
314         goto fail;
315     }
316 
317     stream_map = source_map;
318     if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
319         pa_log("Invalid master channel map.");
320         goto fail;
321     }
322 
323     if (stream_map.channels != ss.channels) {
324         pa_log("Number of channels doesn't match.");
325         goto fail;
326     }
327 
328     if (pa_channel_map_equal(&stream_map, &master->channel_map))
329         pa_log_warn("No remapping configured, proceeding nonetheless!");
330 
331     if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
332         pa_log("Invalid boolean remix parameter.");
333         goto fail;
334     }
335 
336     if (pa_modargs_get_resample_method(ma, &resample_method) < 0) {
337         pa_log("Invalid resampling method");
338         goto fail;
339     }
340 
341     u = pa_xnew0(struct userdata, 1);
342     u->module = m;
343     m->userdata = u;
344 
345     /* Create source */
346     pa_source_new_data_init(&source_data);
347     source_data.driver = __FILE__;
348     source_data.module = m;
349     if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
350         source_data.name = pa_sprintf_malloc("%s.remapped", master->name);
351     pa_source_new_data_set_sample_spec(&source_data, &ss);
352     pa_source_new_data_set_channel_map(&source_data, &source_map);
353     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
354     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
355 
356     if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
357         pa_log("Invalid properties.");
358         pa_source_new_data_done(&source_data);
359         goto fail;
360     }
361 
362     if ((u->auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
363         const char *k;
364 
365         k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
366         pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
367     }
368 
369     u->source = pa_source_new(m->core, &source_data, master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY));
370     pa_source_new_data_done(&source_data);
371 
372     if (!u->source) {
373         pa_log("Failed to create source.");
374         goto fail;
375     }
376 
377     u->source->parent.process_msg = source_process_msg_cb;
378     u->source->set_state_in_main_thread = source_set_state_in_main_thread_cb;
379     u->source->update_requested_latency = source_update_requested_latency_cb;
380 
381     u->source->userdata = u;
382 
383     pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
384 
385     /* Create source output */
386     pa_source_output_new_data_init(&source_output_data);
387     source_output_data.driver = __FILE__;
388     source_output_data.module = m;
389     pa_source_output_new_data_set_source(&source_output_data, master, false, true);
390     source_output_data.destination_source = u->source;
391 
392     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
393     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
394     pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
395     pa_source_output_new_data_set_channel_map(&source_output_data, &stream_map);
396     source_output_data.flags = (remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX) | PA_SOURCE_OUTPUT_START_CORKED;
397     source_output_data.resample_method = resample_method;
398 
399     pa_source_output_new(&u->source_output, m->core, &source_output_data);
400     pa_source_output_new_data_done(&source_output_data);
401 
402     if (!u->source_output)
403         goto fail;
404 
405     u->source_output->push = source_output_push_cb;
406     u->source_output->process_rewind = source_output_process_rewind_cb;
407     u->source_output->update_max_rewind = source_output_update_max_rewind_cb;
408     u->source_output->kill = source_output_kill_cb;
409     u->source_output->attach = source_output_attach_cb;
410     u->source_output->detach = source_output_detach_cb;
411     u->source_output->state_change = source_output_state_change_cb;
412     u->source_output->moving = source_output_moving_cb;
413     u->source_output->userdata = u;
414 
415     u->source->output_from_master = u->source_output;
416 
417     /* The order here is important. The output must be put first,
418      * otherwise streams might attach to the source before the
419      * source output is attached to the master. */
420     pa_source_output_put(u->source_output);
421     pa_source_put(u->source);
422     pa_source_output_cork(u->source_output, false);
423 
424     pa_modargs_free(ma);
425 
426     return 0;
427 
428 fail:
429     if (ma)
430         pa_modargs_free(ma);
431 
432     pa__done(m);
433 
434     return -1;
435 }
436 
pa__get_n_used(pa_module * m)437 int pa__get_n_used(pa_module *m) {
438     struct userdata *u;
439 
440     pa_assert(m);
441     pa_assert_se(u = m->userdata);
442 
443     return pa_source_linked_by(u->source);
444 }
445 
pa__done(pa_module * m)446 void pa__done(pa_module*m) {
447     struct userdata *u;
448 
449     pa_assert(m);
450 
451     if (!(u = m->userdata))
452         return;
453 
454     /* See comments in source_output_kill_cb() above regarding
455      * destruction order! */
456 
457     if (u->source_output)
458         pa_source_output_cork(u->source_output, true);
459 
460     if (u->source)
461         pa_source_unlink(u->source);
462 
463     if (u->source_output) {
464         pa_source_output_unlink(u->source_output);
465         pa_source_output_unref(u->source_output);
466     }
467 
468     if (u->source)
469         pa_source_unref(u->source);
470 
471     pa_xfree(u);
472 }
473