finer granularity of amname generation + audio manager registration filtering
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / multiplex.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU Lesser General Public License,
7  * version 2.1, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <strings.h>
23
24 #include <pulsecore/pulsecore-config.h>
25
26 #include <pulse/def.h>
27 #include <pulsecore/thread.h>
28 #include <pulsecore/strlist.h>
29 #include <pulsecore/time-smoother.h>
30 #include <pulsecore/sink.h>
31 #include <pulsecore/sink-input.h>
32
33 #include <combine/userdata.h>
34
35 #include "multiplex.h"
36 #include "utils.h"
37
38 #ifndef DEFAULT_RESAMPLER
39 #define DEFAULT_RESAMPLER "speex-fixed-3"
40 #endif
41
42
43 static void copy_media_role_property(pa_sink *, pa_sink_input *);
44
45
46 pa_multiplex *pa_multiplex_init(void)
47 {
48     pa_multiplex *multiplex = pa_xnew0(pa_multiplex, 1);
49
50     return multiplex;
51 }
52
53
54 void pa_multiplex_done(pa_multiplex *multiplex, pa_core *core)
55 {
56     pa_muxnode *mux, *n;
57
58     PA_LLIST_FOREACH_SAFE(mux,n, multiplex->muxnodes) {
59         pa_module_unload_by_index(core, mux->module_index, false);
60     }
61 }
62
63
64
65 pa_muxnode *pa_multiplex_create(pa_multiplex   *multiplex,
66                                 pa_core        *core,
67                                 uint32_t        sink_index,
68                                 pa_channel_map *chmap,
69                                 const char     *resampler,
70                                 const char     *media_role,
71                                 int             type)
72 {
73     static char *modnam = "module-combine-sink";
74
75     struct userdata *u;         /* combine's userdata! */
76     struct output   *o;
77     pa_muxnode      *mux;
78     pa_sink         *sink;
79     pa_sink_input   *sinp;
80     pa_module       *module;
81     char             args[512];
82     uint32_t         idx;
83     uint32_t         channels;
84
85     pa_assert(core);
86
87     if (!resampler)
88         resampler = DEFAULT_RESAMPLER;
89
90     if (!(sink = pa_idxset_get_by_index(core->sinks, sink_index))) {
91         pa_log_debug("can't find the primary sink (index %u) for multiplexer",
92                      sink_index);
93         return NULL;
94     }
95
96     channels = chmap->channels ? chmap->channels : sink->channel_map.channels;
97
98     snprintf(args, sizeof(args), "slaves=\"%s\" resample_method=\"%s\" "
99              "channels=%u", sink->name, resampler, channels);
100
101     if (!(module = pa_module_load(core, modnam, args))) {
102         pa_log("failed to load module '%s %s'. can't multiplex", modnam, args);
103         return NULL;
104     }
105
106     pa_assert_se((u = module->userdata));
107     pa_assert(u->sink);
108
109     u->no_reattach = true;
110
111     mux = pa_xnew0(pa_muxnode, 1);
112     mux->module_index = module->index;
113     mux->sink_index = u->sink->index;
114     mux->defstream_index = PA_IDXSET_INVALID;
115
116     PA_LLIST_PREPEND(pa_muxnode, multiplex->muxnodes, mux);
117
118     if (!(o = pa_idxset_first(u->outputs, &idx)))
119         pa_log("can't find default multiplexer stream");
120     else {
121         if ((sinp = o->sink_input)) {
122             if (media_role)
123                 pa_proplist_sets(sinp->proplist,PA_PROP_MEDIA_ROLE,media_role);
124             pa_utils_set_stream_routing_properties(sinp->proplist, type, NULL);
125             mux->defstream_index = sinp->index;
126         }
127     }
128
129     pa_log_debug("multiplexer succesfully loaded");
130
131     return mux;
132 }
133
134
135 void pa_multiplex_destroy(pa_multiplex *multiplex,
136                           pa_core      *core,
137                           pa_muxnode   *mux)
138 {
139     pa_assert(multiplex);
140     pa_assert(core);
141
142     if (mux) {
143         pa_module_unload_by_index(core, mux->module_index, false);
144         PA_LLIST_REMOVE(pa_muxnode, multiplex->muxnodes, mux);
145         pa_xfree(mux);
146     }
147 }
148
149 pa_muxnode *pa_multiplex_find_by_sink(pa_multiplex *multiplex,
150                                       uint32_t sink_index)
151 {
152     pa_muxnode *mux;
153
154     if (sink_index != PA_IDXSET_INVALID) {
155         PA_LLIST_FOREACH(mux, multiplex->muxnodes) {
156             if (sink_index == mux->sink_index) {
157                 pa_log_debug("muxnode found for sink %u", sink_index);
158                 return mux;
159             }
160         }
161     }
162
163     pa_log_debug("can't find muxnode for sink %u", sink_index);
164
165     return NULL;
166 }
167
168 pa_muxnode *pa_multiplex_find_by_module(pa_multiplex *multiplex,
169                                         pa_module    *module)
170 {
171     uint32_t module_index;
172     pa_muxnode *mux;
173
174     pa_assert(multiplex);
175
176     if (module) {
177         module_index = module->index;
178
179         PA_LLIST_FOREACH(mux, multiplex->muxnodes) {
180             if (mux->module_index != PA_IDXSET_INVALID && module_index == mux->module_index)
181                 return mux;
182         }
183     }
184     
185     return NULL;
186 }
187
188 bool pa_multiplex_sink_input_remove(pa_multiplex  *multiplex,
189                                          pa_sink_input *sinp)
190 {
191     pa_muxnode *mux;
192     char *name;
193
194     pa_assert(multiplex);
195     pa_assert(sinp);
196
197     if ((mux = pa_multiplex_find_by_module(multiplex, sinp->module))) {
198         name = pa_utils_get_sink_input_name(sinp);
199
200         pa_log_debug("multiplex (module %u) found for sink-input "
201                      "(name %s)", mux->module_index, name);
202
203         if (sinp->index == mux->defstream_index) {
204             pa_log_debug("reseting default route on multiplex (module %u)",
205                          mux->module_index);
206             mux->defstream_index = PA_IDXSET_INVALID;
207         }
208         else {
209             pa_log_debug("reseting explicit route on multiplex (module %u)",
210                          mux->module_index);
211         }
212
213         return true;
214     }
215
216     return false;
217 }
218
219 bool pa_multiplex_add_default_route(pa_core    *core,
220                                          pa_muxnode *mux,
221                                          pa_sink    *sink,
222                                          int         type)
223 {
224     pa_module *module;
225     pa_sink_input *sinp;
226     struct userdata *u;         /* combine's userdata! */
227
228     pa_assert(core);
229     pa_assert(mux);
230     pa_assert(sink);
231
232     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
233         pa_log_debug("module %u is gone", mux->module_index);
234     else {
235         pa_assert_se((u = module->userdata));
236
237         if (sink == u->sink) {
238             pa_log("%s: mux %d refuses to make a loopback to itself",
239                    __FILE__, mux->module_index);
240         }
241         else {
242             pa_log_debug("adding default route to mux %u", mux->module_index);
243
244             if (!(sinp = u->add_slave(u, sink))) {
245                 pa_log("failed to add new slave to mux %u", mux->module_index);
246                 return false;
247             }
248
249             copy_media_role_property(u->sink, sinp);
250             pa_utils_set_stream_routing_properties(sinp->proplist, type, NULL);
251             mux->defstream_index = sinp->index;
252
253             return true;
254         }
255     }
256
257     return false;
258 }
259
260 bool pa_multiplex_remove_default_route(pa_core *core,
261                                             pa_muxnode *mux,
262                                             bool transfer_to_explicit)
263 {
264     pa_module       *module;
265     pa_sink_input   *sinp;
266     uint32_t         idx;
267     struct userdata *u;         /* combine's userdata! */
268
269     pa_assert(core);
270     pa_assert(mux);
271
272     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
273         pa_log_debug("module %u is gone", mux->module_index);
274     else if ((idx = mux->defstream_index) == PA_IDXSET_INVALID)
275         pa_log_debug("mux %u do not have default stream", mux->module_index);
276     else if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, idx)))
277         pa_log("can't remove default route: sink-input %u is gone", idx);
278     else {
279         pa_assert_se((u = module->userdata));
280         mux->defstream_index = PA_IDXSET_INVALID;
281
282         if (transfer_to_explicit) {
283             pa_log_debug("converting default route sink-input.%u -> sink.%u "
284                          "to explicit", sinp->index, sinp->sink->index);
285             pa_utils_set_stream_routing_method_property(sinp->proplist, true);
286             return true;
287         }
288         else {
289             u->remove_slave(u, sinp, NULL);
290         }
291     }
292
293     return false;
294 }
295
296 bool pa_multiplex_change_default_route(pa_core    *core,
297                                             pa_muxnode *mux,
298                                             pa_sink    *sink)
299 {
300     pa_module       *module;
301     pa_sink_input   *sinp;
302     uint32_t         idx;
303     struct userdata *u;         /* combine's userdata! */
304
305     pa_assert(core);
306     pa_assert(mux);
307     pa_assert(sink);
308
309     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
310         pa_log_debug("module %u is gone", mux->module_index);
311     else if ((idx = mux->defstream_index) == PA_IDXSET_INVALID)
312         pa_log_debug("mux %u do not have default stream", mux->module_index);
313     else if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, idx)))
314         pa_log("can't remove default route: sink-input %u is gone", idx);
315     else {
316         pa_assert_se((u = module->userdata));
317         if (u->move_slave(u, sinp, sink) < 0)
318             pa_log_debug("failed to move default stream on mux %u", mux->module_index);
319         else {
320             pa_log_debug("default stream was successfully moved on mux %u",
321                          mux->module_index);
322             return true;
323         }
324     }
325
326     return false;
327 }
328
329
330 bool pa_multiplex_add_explicit_route(pa_core    *core,
331                                           pa_muxnode *mux,
332                                           pa_sink    *sink,
333                                           int         type)
334 {
335     pa_module *module;
336     pa_sink_input *sinp;
337     struct userdata *u;         /* combine's userdata! */
338
339     pa_assert(core);
340     pa_assert(mux);
341     pa_assert(sink);
342
343     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
344         pa_log_debug("module %u is gone", mux->module_index);
345     else {
346         pa_assert_se((u = module->userdata));
347
348         if (sink == u->sink) {
349             pa_log("%s: mux %d refuses to make a loopback to itself",
350                    __FILE__, mux->module_index);
351         }
352         else {
353             pa_log_debug("adding explicit route to mux %u", mux->module_index);
354
355             if (!(sinp = u->add_slave(u, sink))) {
356                 pa_log("failed to add new slave to mux %u", mux->module_index);
357                 return false;
358             }
359
360             copy_media_role_property(u->sink, sinp);
361             pa_utils_set_stream_routing_properties(sinp->proplist, type, sink);
362
363             return true;
364         }
365     }
366
367     return false;
368 }
369
370
371 bool pa_multiplex_remove_explicit_route(pa_core    *core,
372                                              pa_muxnode *mux,
373                                              pa_sink    *sink)
374 {
375     pa_module *module;
376     struct userdata *u;         /* combine's userdata! */
377
378     pa_assert(core);
379     pa_assert(mux);
380     pa_assert(sink);
381
382     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
383         pa_log_debug("module %u is gone", mux->module_index);
384     else {
385         pa_assert_se((u = module->userdata));
386
387         u->remove_slave(u, NULL, sink);
388
389         pa_log_debug("link to sink.%u removed", sink->index);
390
391         return true;
392     }
393
394     return false;
395 }
396
397
398 bool pa_multiplex_duplicate_route(pa_core       *core,
399                                        pa_muxnode    *mux,
400                                        pa_sink_input *sinp,
401                                        pa_sink       *sink)
402 {
403     pa_module       *module;
404     struct userdata *u;   /* combine's userdata! */
405     struct output   *o;
406     uint32_t         idx;
407     pa_sink_input   *i;
408
409     pa_assert(core);
410     pa_assert(mux);
411     pa_assert(sink);
412
413     pa_log_debug("check for duplicate route on mux %u",
414                  mux->module_index);
415
416     if (!(module = pa_idxset_get_by_index(core->modules,mux->module_index)))
417         pa_log_debug("module %u is gone", mux->module_index);
418     else {
419         pa_assert_se((u = module->userdata));
420
421         PA_IDXSET_FOREACH(o, u->outputs, idx) {
422             if (!(i = o->sink_input))
423                 continue;
424             if (sinp && i == sinp)
425                 continue;
426             if (i->sink == sink) {
427                 pa_log_debug("route sink-input.%u -> sink.%u is a duplicate",
428                              i->index, sink->index);
429                 return true;
430             }
431         }
432
433         if (!sinp)
434             pa_log_debug("no duplicate route found to sink.%u", sink->index);
435         else {
436             pa_log_debug("no duplicate found for route sink-input.%u -> "
437                          "sink.%u", sinp->index, sink->index);
438         }
439     }
440         
441     return false;
442 }
443
444 int pa_multiplex_no_of_routes(pa_core *core, pa_muxnode *mux)
445 {
446     pa_module       *module;
447     struct userdata *u;   /* combine's userdata! */
448
449     pa_assert(core);
450     pa_assert(mux);
451
452     if (!(module = pa_idxset_get_by_index(core->modules,mux->module_index))) {
453         pa_log_debug("module %u is gone", mux->module_index);
454         return -1;
455     }
456
457     pa_assert_se((u = module->userdata));
458
459     return (int)pa_idxset_size(u->outputs);
460 }
461
462
463 int pa_multiplex_print(pa_muxnode *mux, char *buf, int len)
464 {
465     char *p, *e;
466
467     pa_assert(buf);
468     pa_assert(len > 0);
469
470     e = (p = buf) + len;
471
472     if (!mux)
473         p += snprintf(p, e-p, "<not set>");
474     else {
475         p += snprintf(p, e-p, "module %u, sink %u, default stream %u",
476                       mux->module_index, mux->sink_index,mux->defstream_index);
477     }
478     
479     return p - buf;
480 }
481
482 static void copy_media_role_property(pa_sink *sink, pa_sink_input *to)
483 {
484     uint32_t index;
485     pa_sink_input *from;
486     const char *role;
487
488     pa_assert(to);
489
490     if (sink && (from = pa_idxset_first(sink->inputs, &index))) {
491         pa_assert(from->proplist);
492         pa_assert(to->proplist);
493
494         if ((role = pa_proplist_gets(from->proplist, PA_PROP_MEDIA_ROLE)) &&
495             pa_proplist_sets(to->proplist, PA_PROP_MEDIA_ROLE, role) == 0)
496         {
497             pa_log_debug("set media.role=\"%s\" on sink_input.%d",
498                          role, to->index);
499             return;
500         }
501     }
502
503     pa_log_debug("failed to set media.role on sink_input.%d", to->index);
504 }
505
506
507 /*
508  * Local Variables:
509  * c-basic-offset: 4
510  * indent-tabs-mode: nil
511  * End:
512  *
513  */
514