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