routing: set correct routing class when reinstating a default stream
[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-float-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
84     pa_assert(core);
85
86     if (!resampler)
87         resampler = DEFAULT_RESAMPLER;
88
89     if (!(sink = pa_idxset_get_by_index(core->sinks, sink_index))) {
90         pa_log_debug("can't find the primary sink (index %u) for multiplexer",
91                      sink_index);
92         return NULL;
93     }
94
95     snprintf(args, sizeof(args), "slaves=\"%s\" resample_method=\"%s\" "
96              "channels=%u", sink->name, resampler, chmap->channels);
97
98     if (!(module = pa_module_load(core, modnam, args))) {
99         pa_log("failed to load module '%s %s'. can't multiplex", modnam, args);
100         return NULL;
101     }
102
103     pa_assert_se((u = module->userdata));
104     pa_assert(u->sink);
105
106     u->no_reattach = TRUE;
107
108     mux = pa_xnew0(pa_muxnode, 1);
109     mux->module_index = module->index;
110     mux->sink_index = u->sink->index;
111     mux->defstream_index = PA_IDXSET_INVALID;
112
113     PA_LLIST_PREPEND(pa_muxnode, multiplex->muxnodes, mux);
114
115     if (!(o = pa_idxset_first(u->outputs, &idx)))
116         pa_log("can't find default multiplexer stream");
117     else {
118         if ((sinp = o->sink_input)) {
119             if (media_role)
120                 pa_proplist_sets(sinp->proplist,PA_PROP_MEDIA_ROLE,media_role);
121             pa_utils_set_stream_routing_properties(sinp->proplist, type, NULL);
122             mux->defstream_index = sinp->index;
123         }
124     }
125
126     pa_log_debug("multiplexer succesfully loaded");
127
128     return mux;
129 }
130
131
132 void pa_multiplex_destroy(pa_multiplex *multiplex,
133                           pa_core      *core,
134                           pa_muxnode   *mux)
135 {
136     pa_assert(multiplex);
137     pa_assert(core);
138
139     if (mux) {
140         PA_LLIST_REMOVE(pa_muxnode, multiplex->muxnodes, mux);
141         pa_module_unload_by_index(core, mux->module_index, FALSE);
142         pa_xfree(mux);
143     }
144 }
145
146
147 pa_muxnode *pa_multiplex_find(pa_multiplex *multiplex, uint32_t sink_index)
148 {
149     pa_muxnode *mux;
150
151     if (sink_index != PA_IDXSET_INVALID) {
152         PA_LLIST_FOREACH(mux, multiplex->muxnodes) {
153             if (sink_index == mux->sink_index) {
154                 pa_log_debug("muxnode found for sink %u", sink_index);
155                 return mux;
156             }
157         }
158     }
159
160     pa_log_debug("can't find muxnode for sink %u", sink_index);
161
162     return NULL;
163 }
164
165
166 pa_bool_t pa_multiplex_add_default_route(pa_core    *core,
167                                          pa_muxnode *mux,
168                                          pa_sink    *sink,
169                                          int         type)
170 {
171     pa_module *module;
172     pa_sink_input *sinp;
173     struct userdata *u;         /* combine's userdata! */
174
175     pa_assert(core);
176     pa_assert(mux);
177     pa_assert(sink);
178
179     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
180         pa_log("module %u is gone", mux->module_index);
181     else {
182         pa_assert_se((u = module->userdata));
183
184         if (sink == u->sink) {
185             pa_log("%s: mux %d refuses to make a loopback to itself",
186                    __FILE__, mux->module_index);
187         }
188         else {
189             pa_log_debug("adding default route to mux %u", mux->module_index);
190
191             if (!(sinp = u->add_slave(u, sink))) {
192                 pa_log("failed to add new slave to mux %u", mux->module_index);
193                 return FALSE;
194             }
195
196             copy_media_role_property(u->sink, sinp);
197             pa_utils_set_stream_routing_properties(sinp->proplist, type, NULL);
198             mux->defstream_index = sinp->index;
199
200             return TRUE;
201         }
202     }
203
204     return FALSE;
205 }
206
207 pa_bool_t pa_multiplex_remove_default_route(pa_core *core,
208                                             pa_muxnode *mux,
209                                             pa_bool_t transfer_to_explicit)
210 {
211     pa_module       *module;
212     pa_sink_input   *sinp;
213     uint32_t         idx;
214     struct userdata *u;         /* combine's userdata! */
215
216     pa_assert(core);
217     pa_assert(mux);
218
219     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
220         pa_log("module %u is gone", mux->module_index);
221     else if ((idx = mux->defstream_index) == PA_IDXSET_INVALID)
222         pa_log_debug("mux %u do not have default stream", mux->module_index);
223     else if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, idx)))
224         pa_log("can't remove default route: sink-input %u is gone", idx);
225     else {
226         pa_assert_se((u = module->userdata));
227         mux->defstream_index = PA_IDXSET_INVALID;
228
229         if (transfer_to_explicit) {
230             pa_log_debug("converting default route sink-input.%u -> sink.%u "
231                          "to explicit", sinp->index, sinp->sink->index);
232             pa_utils_set_stream_routing_method_property(sinp->proplist, TRUE);
233             return TRUE;
234         }
235         else {
236             u->remove_slave(u, sinp, NULL);
237         }
238     }
239
240     return FALSE;
241 }
242
243 pa_bool_t pa_multiplex_change_default_route(pa_core    *core,
244                                             pa_muxnode *mux,
245                                             pa_sink    *sink)
246 {
247     pa_module       *module;
248     pa_sink_input   *sinp;
249     uint32_t         idx;
250     struct userdata *u;         /* combine's userdata! */
251
252     pa_assert(core);
253     pa_assert(mux);
254     pa_assert(sink);
255
256     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
257         pa_log("module %u is gone", mux->module_index);
258     else if ((idx = mux->defstream_index) == PA_IDXSET_INVALID)
259         pa_log_debug("mux %u do not have default stream", mux->module_index);
260     else if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, idx)))
261         pa_log("can't remove default route: sink-input %u is gone", idx);
262     else {
263         pa_assert_se((u = module->userdata));
264         if (!u->move_slave(u, sinp, sink))
265             pa_log_debug("failed to move default stream on mux %u", mux->module_index);
266         else {
267             pa_log_debug("default stream was successfully moved on mux %u",
268                          mux->module_index);
269             return TRUE;
270         }
271     }
272
273     return FALSE;
274 }
275
276
277 pa_bool_t pa_multiplex_add_explicit_route(pa_core    *core,
278                                           pa_muxnode *mux,
279                                           pa_sink    *sink,
280                                           int         type)
281 {
282     pa_module *module;
283     pa_sink_input *sinp;
284     struct userdata *u;         /* combine's userdata! */
285
286     pa_assert(core);
287     pa_assert(mux);
288     pa_assert(sink);
289
290     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
291         pa_log("module %u is gone", mux->module_index);
292     else {
293         pa_assert_se((u = module->userdata));
294
295         if (sink == u->sink) {
296             pa_log("%s: mux %d refuses to make a loopback to itself",
297                    __FILE__, mux->module_index);
298         }
299         else {
300             pa_log_debug("adding explicit route to mux %u", mux->module_index);
301
302             if (!(sinp = u->add_slave(u, sink))) {
303                 pa_log("failed to add new slave to mux %u", mux->module_index);
304                 return FALSE;
305             }
306
307             copy_media_role_property(u->sink, sinp);
308             pa_utils_set_stream_routing_properties(sinp->proplist, type, sink);
309
310             return TRUE;
311         }
312     }
313
314     return FALSE;
315 }
316
317
318 pa_bool_t pa_multiplex_remove_explicit_route(pa_core    *core,
319                                              pa_muxnode *mux,
320                                              pa_sink    *sink)
321 {
322     pa_module *module;
323     struct userdata *u;         /* combine's userdata! */
324
325     pa_assert(core);
326     pa_assert(mux);
327     pa_assert(sink);
328
329     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
330         pa_log("module %u is gone", mux->module_index);
331     else {
332         pa_assert_se((u = module->userdata));
333
334         u->remove_slave(u, NULL, sink);
335
336         pa_log_debug("link to sink.%u removed", sink->index);
337
338         return TRUE;
339     }
340
341     return FALSE;
342 }
343
344
345 pa_bool_t pa_multiplex_duplicate_route(pa_core       *core,
346                                        pa_muxnode    *mux,
347                                        pa_sink_input *sinp,
348                                        pa_sink       *sink)
349 {
350     pa_module       *module;
351     struct userdata *u;   /* combine's userdata! */
352     struct output   *o;
353     uint32_t         idx;
354     pa_sink_input   *i;
355
356     pa_assert(core);
357     pa_assert(mux);
358     pa_assert(sink);
359
360     pa_log_debug("check for duplicate route on mux %u",
361                  mux->module_index);
362
363     if (!(module = pa_idxset_get_by_index(core->modules,mux->module_index)))
364         pa_log("module %u is gone", mux->module_index);
365     else {
366         pa_assert_se((u = module->userdata));
367
368         PA_IDXSET_FOREACH(o, u->outputs, idx) {
369             if (!(i = o->sink_input))
370                 continue;
371             if (sinp && i == sinp)
372                 continue;
373             if (i->sink == sink) {
374                 pa_log_debug("route sink-input.%u -> sink.%u is a duplicate",
375                              i->index, sink->index);
376                 return TRUE;
377             }
378         }
379
380         if (!sinp)
381             pa_log_debug("no duplicate route found to sink.%u", sink->index);
382         else {
383             pa_log_debug("no duplicate found for route sink-input.%u -> "
384                          "sink.%u", sinp->index, sink->index);
385         }
386     }
387         
388     return FALSE;
389 }
390
391 int pa_multiplex_no_of_routes(pa_core *core, pa_muxnode *mux)
392 {
393     pa_module       *module;
394     struct userdata *u;   /* combine's userdata! */
395
396     pa_assert(core);
397     pa_assert(mux);
398
399     if (!(module = pa_idxset_get_by_index(core->modules,mux->module_index))) {
400         pa_log("module %u is gone", mux->module_index);
401         return -1;
402     }
403
404     pa_assert_se((u = module->userdata));
405
406     return (int)pa_idxset_size(u->outputs);
407 }
408
409
410 int pa_multiplex_print(pa_muxnode *mux, char *buf, int len)
411 {
412     char *p, *e;
413
414     pa_assert(buf);
415     pa_assert(len > 0);
416
417     e = (p = buf) + len;
418
419     if (!mux)
420         p += snprintf(p, e-p, "<not set>");
421     else {
422         p += snprintf(p, e-p, "module %u, sink %u, default stream %u",
423                       mux->module_index, mux->sink_index,mux->defstream_index);
424     }
425     
426     return p - buf;
427 }
428
429 static void copy_media_role_property(pa_sink *sink, pa_sink_input *to)
430 {
431     uint32_t index;
432     pa_sink_input *from;
433     const char *role;
434
435     pa_assert(to);
436
437     if (sink && (from = pa_idxset_first(sink->inputs, &index))) {
438         pa_assert(from->proplist);
439         pa_assert(to->proplist);
440
441         if ((role = pa_proplist_gets(from->proplist, PA_PROP_MEDIA_ROLE)) &&
442             pa_proplist_sets(to->proplist, PA_PROP_MEDIA_ROLE, role) == 0)
443         {
444             pa_log_debug("set media.role=\"%s\" on sink_input.%d",
445                          role, to->index);
446             return;
447         }
448     }
449
450     pa_log_debug("failed to set media.role on sink_input.%d", to->index);
451 }
452
453
454 /*
455  * Local Variables:
456  * c-basic-offset: 4
457  * indent-tabs-mode: nil
458  * End:
459  *
460  */
461