]> code.delx.au - pulseaudio/blob - src/modules/module-remap-source.c
modules: add module-remap-source
[pulseaudio] / src / modules / module-remap-source.c
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
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2.1 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29
30 #include <pulse/xmalloc.h>
31
32 #include <pulsecore/i18n.h>
33 #include <pulsecore/macro.h>
34 #include <pulsecore/namereg.h>
35 #include <pulsecore/module.h>
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/modargs.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/rtpoll.h>
40 #include <pulsecore/sample-util.h>
41 #include <pulsecore/ltdl-helper.h>
42 #include <pulsecore/mix.h>
43
44 #include "module-remap-source-symdef.h"
45
46 PA_MODULE_AUTHOR("Stefan Huber");
47 PA_MODULE_DESCRIPTION("Virtual channel remapping source");
48 PA_MODULE_VERSION(PACKAGE_VERSION);
49 PA_MODULE_LOAD_ONCE(false);
50 PA_MODULE_USAGE(
51 "source_name=<name for the source> "
52 "source_properties=<properties for the source> "
53 "master=<name of source to filter> "
54 "master_channel_map=<channel map> "
55 "format=<sample format> "
56 "rate=<sample rate> "
57 "channels=<number of channels> "
58 "channel_map=<channel map> "
59 "remix=<remix channels?>");
60
61 struct userdata {
62 pa_module *module;
63
64 pa_source *source;
65 pa_source_output *source_output;
66
67 bool auto_desc;
68 };
69
70 static const char* const valid_modargs[] = {
71 "source_name",
72 "source_properties",
73 "master",
74 "master_channel_map",
75 "format",
76 "rate",
77 "channels",
78 "channel_map",
79 "remix",
80 NULL
81 };
82
83 /* Called from I/O thread context */
84 static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
85 struct userdata *u = PA_SOURCE(o)->userdata;
86
87 switch (code) {
88
89 case PA_SOURCE_MESSAGE_GET_LATENCY:
90
91 /* The source is _put() before the source output is, so let's
92 * make sure we don't access it in that time. Also, the
93 * source output is first shut down, the source second. */
94 if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
95 !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
96 *((pa_usec_t*) data) = 0;
97 return 0;
98 }
99
100 *((pa_usec_t*) data) =
101
102 /* Get the latency of the master source */
103 pa_source_get_latency_within_thread(u->source_output->source) +
104 /* Add the latency internal to our source output on top */
105 pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec);
106
107 return 0;
108 }
109
110 return pa_source_process_msg(o, code, data, offset, chunk);
111 }
112
113 /* Called from main context */
114 static int source_set_state_cb(pa_source *s, pa_source_state_t state) {
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(pa_source_output_get_state(u->source_output)))
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 */
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 */
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_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
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 */
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 pa_source_process_rewind(u->source, nbytes);
172 }
173
174 /* Called from output thread context */
175 static void source_output_detach_cb(pa_source_output *o) {
176 struct userdata *u;
177
178 pa_source_output_assert_ref(o);
179 pa_source_output_assert_io_context(o);
180 pa_assert_se(u = o->userdata);
181
182 pa_source_detach_within_thread(u->source);
183
184 pa_source_set_rtpoll(u->source, NULL);
185 }
186
187 /* Called from output thread context */
188 static void source_output_attach_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 pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll);
196 pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
197 pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
198 pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
199
200 pa_source_attach_within_thread(u->source);
201 }
202
203 /* Called from main thread */
204 static void source_output_kill_cb(pa_source_output *o) {
205 struct userdata *u;
206
207 pa_source_output_assert_ref(o);
208 pa_assert_ctl_context();
209 pa_assert_se(u = o->userdata);
210
211 /* The order here matters! We first kill the source output, followed
212 * by the source. That means the source callbacks must be protected
213 * against an unconnected source output! */
214 pa_source_output_unlink(u->source_output);
215 pa_source_unlink(u->source);
216
217 pa_source_output_unref(u->source_output);
218 u->source_output = NULL;
219
220 pa_source_unref(u->source);
221 u->source = NULL;
222
223 pa_module_unload_request(u->module, true);
224 }
225
226 /* Called from output thread context */
227 static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
228 struct userdata *u;
229
230 pa_source_output_assert_ref(o);
231 pa_source_output_assert_io_context(o);
232 pa_assert_se(u = o->userdata);
233
234 pa_log_debug("Source output %d state %d.", o->index, state);
235 }
236
237 /* Called from main thread */
238 static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
239 struct userdata *u;
240
241 pa_source_output_assert_ref(o);
242 pa_assert_ctl_context();
243 pa_assert_se(u = o->userdata);
244
245 if (dest) {
246 pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
247 pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
248 } else
249 pa_source_set_asyncmsgq(u->source, NULL);
250
251 if (u->auto_desc && dest) {
252 const char *k;
253 pa_proplist *pl;
254
255 pl = pa_proplist_new();
256 k = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
257 pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : dest->name);
258
259 pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl);
260 pa_proplist_free(pl);
261 }
262 }
263
264 int pa__init(pa_module*m) {
265 struct userdata *u;
266 pa_sample_spec ss;
267 pa_channel_map source_map, stream_map;
268 pa_modargs *ma;
269 pa_source *master;
270 pa_source_output_new_data source_output_data;
271 pa_source_new_data source_data;
272 bool remix = true;
273
274 pa_assert(m);
275
276 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
277 pa_log("Failed to parse module arguments.");
278 goto fail;
279 }
280
281 if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SOURCE))) {
282 pa_log("Master source not found.");
283 goto fail;
284 }
285
286 ss = master->sample_spec;
287 source_map = master->channel_map;
288 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &source_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
289 pa_log("Invalid sample format specification or channel map.");
290 goto fail;
291 }
292
293 stream_map = source_map;
294 if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
295 pa_log("Invalid master channel map.");
296 goto fail;
297 }
298
299 if (stream_map.channels != ss.channels) {
300 pa_log("Number of channels doesn't match.");
301 goto fail;
302 }
303
304 if (pa_channel_map_equal(&stream_map, &master->channel_map))
305 pa_log_warn("No remapping configured, proceeding nonetheless!");
306
307 if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
308 pa_log("Invalid boolean remix parameter.");
309 goto fail;
310 }
311
312 u = pa_xnew0(struct userdata, 1);
313 u->module = m;
314 m->userdata = u;
315
316 /* Create source */
317 pa_source_new_data_init(&source_data);
318 source_data.driver = __FILE__;
319 source_data.module = m;
320 if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
321 source_data.name = pa_sprintf_malloc("%s.remapped", master->name);
322 pa_source_new_data_set_sample_spec(&source_data, &ss);
323 pa_source_new_data_set_channel_map(&source_data, &source_map);
324 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
325 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
326
327 if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
328 pa_log("Invalid properties.");
329 pa_source_new_data_done(&source_data);
330 goto fail;
331 }
332
333 if ((u->auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
334 const char *k;
335
336 k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
337 pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
338 }
339
340 u->source = pa_source_new(m->core, &source_data, master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY));
341 pa_source_new_data_done(&source_data);
342
343 if (!u->source) {
344 pa_log("Failed to create source.");
345 goto fail;
346 }
347
348 u->source->parent.process_msg = source_process_msg_cb;
349 u->source->set_state = source_set_state_cb;
350 u->source->update_requested_latency = source_update_requested_latency_cb;
351
352 u->source->userdata = u;
353
354 pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
355
356 /* Create source output */
357 pa_source_output_new_data_init(&source_output_data);
358 source_output_data.driver = __FILE__;
359 source_output_data.module = m;
360 pa_source_output_new_data_set_source(&source_output_data, master, false);
361 source_output_data.destination_source = u->source;
362
363 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
364 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
365 pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
366 pa_source_output_new_data_set_channel_map(&source_output_data, &stream_map);
367 source_output_data.flags = remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX;
368
369 pa_source_output_new(&u->source_output, m->core, &source_output_data);
370 pa_source_output_new_data_done(&source_output_data);
371
372 if (!u->source_output)
373 goto fail;
374
375 u->source_output->push = source_output_push_cb;
376 u->source_output->process_rewind = source_output_process_rewind_cb;
377 u->source_output->kill = source_output_kill_cb;
378 u->source_output->attach = source_output_attach_cb;
379 u->source_output->detach = source_output_detach_cb;
380 u->source_output->state_change = source_output_state_change_cb;
381 u->source_output->moving = source_output_moving_cb;
382 u->source_output->userdata = u;
383
384 u->source->output_from_master = u->source_output;
385
386 pa_source_put(u->source);
387 pa_source_output_put(u->source_output);
388
389 pa_modargs_free(ma);
390
391 return 0;
392
393 fail:
394 if (ma)
395 pa_modargs_free(ma);
396
397 pa__done(m);
398
399 return -1;
400 }
401
402 int pa__get_n_used(pa_module *m) {
403 struct userdata *u;
404
405 pa_assert(m);
406 pa_assert_se(u = m->userdata);
407
408 return pa_source_linked_by(u->source);
409 }
410
411 void pa__done(pa_module*m) {
412 struct userdata *u;
413
414 pa_assert(m);
415
416 if (!(u = m->userdata))
417 return;
418
419 /* See comments in source_output_kill_cb() above regarding
420 * destruction order! */
421
422 if (u->source_output)
423 pa_source_output_unlink(u->source_output);
424
425 if (u->source)
426 pa_source_unlink(u->source);
427
428 if (u->source_output)
429 pa_source_output_unref(u->source_output);
430
431 if (u->source)
432 pa_source_unref(u->source);
433
434 pa_xfree(u);
435 }