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