Map labels to classes, map routing groups to audio adapters
[staging/agl-audio-plugin.git] / discover.c
1 /*
2  * module-agl-audio -- PulseAudio module for providing audio routing support
3  * (forked from "module-murphy-ivi" - https://github.com/otcshare )
4  * Copyright (c) 2012, Intel Corporation.
5  * Copyright (c) 2016, IoT.bzh
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms and conditions of the GNU Lesser General Public License,
9  * version 2.1, as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
19  * MA 02110-1301 USA.
20  *
21  */
22 #include <pulsecore/pulsecore-config.h> /* required for "core-util.h" */
23 #include <pulsecore/core-util.h>        /* requred for "pa_streq" */
24 #include <pulsecore/device-port.h>      /* required for "card.h" */
25 #include <pulsecore/card.h>             /* for "struct pa_card", "struct pa_card_profile" */
26
27 #include "discover.h"
28 #include "node.h"
29 #include "utils.h"
30 #include "classify.h"
31 #include "router.h"
32
33 #define MAX_CARD_TARGET 4    /* max number of managed sinks/sources per card */
34 #define MAX_NAME_LENGTH 256  /* max sink/source name length */
35
36 static void handle_alsa_card (struct userdata *, pa_card *);
37 static void handle_alsa_card_sinks_and_sources (struct userdata *, pa_card *, agl_node *, const char *);
38 static void get_sinks_and_sources_from_profile (pa_card_profile *, char **, char **, char *, int);
39 static char *get_sink_or_source_from_str (char **, int);
40 static void handle_card_ports (struct userdata *, agl_node *, pa_card *, pa_card_profile *);
41 static const char *node_key (struct userdata *, agl_direction,
42                              void *, pa_device_port *, char *, size_t);
43 static agl_node *create_node (struct userdata *, agl_node *, bool *);
44
45 struct agl_discover *agl_discover_init (struct userdata *u)
46 {
47         agl_discover *discover = pa_xnew0 (agl_discover, 1);
48         discover->chmin = 1;
49         discover->chmax = 2;
50         discover->selected = true;
51         discover->nodes.byname = pa_hashmap_new (pa_idxset_string_hash_func,
52                                                  pa_idxset_string_compare_func);
53         discover->nodes.byptr  = pa_hashmap_new (pa_idxset_trivial_hash_func,
54                                                  pa_idxset_trivial_compare_func);
55
56         return discover;
57 }
58
59 void agl_discover_done (struct userdata *u)
60 {
61         agl_discover *discover;
62         void *state;
63         agl_node *node;
64
65         if (u && (discover = u->discover)) {
66                 /*PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
67                         agl_node_destroy(u, node);
68                 }*/
69                 pa_hashmap_free (discover->nodes.byname);
70                 pa_hashmap_free (discover->nodes.byptr);
71                 pa_xfree (discover);
72                 u->discover = NULL;
73         }
74 }
75
76 void agl_discover_add_card (struct userdata *u, pa_card *card)
77 {
78         const char *bus;
79
80         pa_assert(u);
81         pa_assert(card);
82
83         if (!(bus = agl_utils_get_card_bus (card))) {
84                 pa_log_debug ("ignoring card '%s' due to lack of '%s' property",
85                               agl_utils_get_card_name (card), PA_PROP_DEVICE_BUS);
86                 return;
87         }
88
89         if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) {
90                 pa_log_debug ("adding card '%s' thanks to its '%s' property",
91                               agl_utils_get_card_name (card), PA_PROP_DEVICE_BUS);
92                 pa_log_debug ("card  type is '%s'", bus);
93                 handle_alsa_card (u, card);
94                 return;
95         }
96 /* this never happens, because "agl_utils_get_card_bus()" never returns "bluetooth",
97  * but have this here as a reminder */
98 #if 0
99         else if (pa_streq(bus, "bluetooth")) {
100                 handle_bluetooth_card(u, card);
101                 return;
102         }
103 #endif
104
105         pa_log_debug ("ignoring card '%s' due to unsupported bus type '%s'",
106                       agl_utils_get_card_name (card), bus);
107 }
108
109 void agl_discover_remove_card (struct userdata *u, pa_card *card)
110 {
111         const char  *bus;
112         agl_discover *discover;
113         agl_node    *node;
114         void        *state;
115
116         pa_assert (u);
117         pa_assert (card);
118         pa_assert_se (discover = u->discover);
119
120         if (!(bus = agl_utils_get_card_bus(card)))
121                 bus = "<unknown>";
122
123         /*PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
124         if (node->implement == agl_device &&
125             node->pacard.index == card->index)
126         {
127                 if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform"))
128                         agl_constrain_destroy (u, node->paname);
129
130                 destroy_node(u, node);
131         }*/
132
133   /* this never happens, because "agl_utils_get_card_bus()" never returns "bluetooth",
134   * but have this here as a reminder */
135 #if 0
136     if (pa_streq(bus, "bluetooth"))
137         agl_constrain_destroy(u, card->name);   
138 #endif
139 }
140
141 void agl_discover_add_sink (struct userdata *u, pa_sink *sink, bool route)
142 {
143         pa_core *core;
144         pa_module *module;
145         pa_card *card;
146         agl_discover *discover;
147         char kbf[256];
148         const char *key;
149         agl_node *node;
150         pa_source *null_source;
151
152         pa_assert (u);
153         pa_assert (sink);
154         pa_assert_se (core = u->core);
155         pa_assert_se (discover = u->discover);
156
157         module = sink->module;
158         card = sink->card;
159
160         if (card) {
161                  /* helper function verifying that sink direction is input/output */
162                 key = node_key (u, agl_output, sink, NULL, kbf, sizeof(kbf));
163                 if (!key) return;
164                 pa_log_debug ("Sink key: %s", key);
165                 node = agl_discover_find_node_by_key (u, key);
166                 if (!node) {    /* ALWAYS NULL, IF CALL "handle_card_ports" FROM "handle_alsa_card" !!! */
167                         if (u->state.profile)
168                                 pa_log_debug ("can't find node for sink (key '%s')", key);
169                         else {  /* how do we get here ? not in initial setup */
170                                 u->state.sink = sink->index;
171                                 pa_log_debug ("BUG ! Did you call handle_card_ports() ?");
172                         }
173                         return;
174                 }
175                 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
176                              node->paname, node->key);
177                 node->paidx = sink->index;
178                 node->available = true;
179                 agl_discover_add_node_to_ptr_hash (u, sink, node);
180
181 #if 0
182                 /* loopback part : it is a device node, use "module-loopback" to make its */
183                 if (node->implement == agl_device) {
184                         null_source = agl_utils_get_null_source (u);
185                         if (!null_source) {
186                                 pa_log ("Can't load loopback module: no initial null source");
187                                 return;
188                         }
189                 }
190 #endif
191         }
192 }
193
194
195 static void handle_alsa_card (struct userdata *u, pa_card *card)
196 {
197         agl_node data;
198         const char *cnam;       /* PulseAudio name */
199         const char *cid;        /* short PulseAudio name (with "alsa_name." stripped) */
200         const char *alsanam;    /* ALSA name */
201         const char *udd;        /* managed by udev ("1" = yes) */
202
203         memset (&data, 0, sizeof(data));
204         data.zone = agl_utils_get_zone (card->proplist, NULL);
205         data.visible = true;
206         data.amid = AM_ID_INVALID;
207         data.implement = agl_device;            /* this node is a physical device */
208         data.paidx = PA_IDXSET_INVALID;
209         data.stamp = agl_utils_get_stamp ();    /* each time incremented by one */
210
211         cnam = agl_utils_get_card_name (card);  /* PulseAudio name */
212         cid = cnam + 10;                        /* PulseAudio short name, with "alsa_card." prefix removed */
213         alsanam = pa_proplist_gets (card->proplist, "alsa.card_name"); /* ALSA name */
214         udd = pa_proplist_gets (card->proplist, "module-udev-detect.discovered");
215
216         data.amdescr = (char *)alsanam;
217         data.pacard.index = card->index;
218
219         pa_log_debug ("Sound card zone: %s", data.zone);
220         pa_log_debug ("Sound card stamp: %d", data.stamp);
221         pa_log_debug ("PulseAudio card name: %s", cnam);
222         pa_log_debug ("PulseAudio short card name: %s", cid);
223         pa_log_debug ("ALSA card name: %s", alsanam);
224         if (udd)
225                 pa_log_debug ("ALSA card detected by udev: %s", udd);
226
227         /* WITH THE TIZEN MODULE, ONLY UDEV-MANAGED CARDS ARE ACCEPTED
228          * NO MATTER, TREAT STATIC CARDS THE SAME WAY HERE.. */
229         /*if (!udd || (udd && !pa_streq(udd, "1"))
230                 pa_log_debug ("Card not accepted, not managed by udev\n");*/
231
232         handle_alsa_card_sinks_and_sources (u, card, &data, cid);
233 }
234                                               ;
235 static void handle_alsa_card_sinks_and_sources (struct userdata *u, pa_card *card, agl_node *data, const char *cardid)
236 {
237         agl_discover *discover; /* discovery restrictions (max channels...) */
238         pa_card_profile *prof;
239         void *state;
240         char *sinks[MAX_CARD_TARGET+1];   /* discovered sinks array */
241         char *sources[MAX_CARD_TARGET+1]; /* discovered sources array */
242         char namebuf[MAX_NAME_LENGTH+1]; /* discovered sink/source name buf.*/
243         const char *alsanam;
244         char paname[MAX_NAME_LENGTH+1];
245         char amname[MAX_NAME_LENGTH+1];
246         int i, j, k;
247
248         pa_assert (card);
249         pa_assert (card->profiles);
250         pa_assert_se (discover = u->discover);
251
252         alsanam = pa_proplist_gets (card->proplist, "alsa.card_name");
253         data->paname = paname;
254         data->amname = amname;
255         data->amdescr = (char *)alsanam;
256         data->pacard.index = card->index;
257
258         PA_HASHMAP_FOREACH(prof, card->profiles, state) {
259                  /* TODO : skip selected profile here */
260
261                  /* skip profiles withoutqx sinks/sources */
262                 if (!prof->n_sinks && !prof->n_sources)
263                         continue;
264                  /* skip profiles with too few/many channels */
265                 if (prof->n_sinks &&
266                     (prof->max_sink_channels < discover->chmin ||
267                      prof->max_sink_channels  > discover->chmax))
268                         continue;
269                 if (prof->n_sources &&
270                     (prof->max_source_channels < discover->chmin ||
271                      prof->max_source_channels  > discover->chmax))
272                         continue;
273
274                  /* VALID PROFILE, STORE IT */
275                 pa_log_debug ("Discovered valid profile: %s", prof->name);
276                 data->pacard.profile = prof->name;
277                  /* NOW FILLING SINKS/SOURCE ARRAYS WITH PROFILE DATA */
278                 get_sinks_and_sources_from_profile (prof, sinks, sources, namebuf, sizeof(namebuf));
279
280                  /* OUTPUT DIRECTION, SINKS */
281                 data->direction = agl_output;
282                 data->channels = prof->max_sink_channels;
283                 for (i = 0; sinks[i]; i++) {
284                         pa_log_debug ("Discovered valid sink #%s on card %s", sinks[i], cardid);
285                         snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sinks[i]);
286                         handle_card_ports(u, data, card, prof);
287                 }
288
289                  /* INPUT DIRECTION, SOURCES */
290                 data->direction = agl_input;
291                 data->channels = prof->max_source_channels;
292                 for (i = 0; sources[i]; i++) {
293                         pa_log_debug ("Discovered valid source #%s on card %s", sources[i], cardid);
294                         snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sinks[i]);
295                         handle_card_ports(u, data, card, prof);
296                 }
297         }
298 }
299
300 static void get_sinks_and_sources_from_profile (pa_card_profile *prof, char **sinks, char **sources, char *buf, int buflen)
301 {
302         char *p = buf;
303         int i = 0;
304         int j = 0;
305
306         pa_assert (prof->name);
307
308         strncpy (buf, prof->name, (size_t)buflen);
309         buf[buflen-1] = '\0';
310
311         memset (sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
312         memset (sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
313
314         do {
315                 if (!strncmp (p, "output:", 7)) {
316                         if (i >= MAX_CARD_TARGET) {
317                                 pa_log_debug ("number of outputs exeeds the maximum %d in "
318                                               "profile name '%s'", MAX_CARD_TARGET, prof->name);
319                                 return;
320                         }
321                         sinks[i++] = get_sink_or_source_from_str (&p, 7);
322                 } else if (!strncmp (p, "input:", 6)) {
323                         if (j >= MAX_CARD_TARGET) {
324                                 pa_log_debug ("number of inputs exeeds the maximum %d in "
325                                               "profile name '%s'", MAX_CARD_TARGET, prof->name);
326                                 return;
327                         }
328                         sources[j++] = get_sink_or_source_from_str (&p, 6);
329                 } else {
330                         pa_log ("%s: failed to parse profile name '%s'",
331                                 __FILE__, prof->name);
332                         return;
333                 }
334         } while (*p);
335 }
336
337 static char *get_sink_or_source_from_str (char **string_ptr, int offs)
338 {
339         char c, *name, *end;
340
341         name = *string_ptr + offs;
342
343         for (end = name;  (c = *end);   end++) {
344                 if (c == '+') {
345                         *end++ = '\0';
346                         break;
347                 }
348         }
349
350         *string_ptr = end;
351
352         return name;
353 }
354
355 static void handle_card_ports (struct userdata *u, agl_node *data, pa_card *card, pa_card_profile *prof) {
356         agl_node *node = NULL;
357         pa_device_port *port;
358         void *state;
359         bool created;
360         bool have_ports = false;
361         char key[MAX_NAME_LENGTH+1];
362         const char *amname = data->amname;
363
364         pa_assert (u);
365         pa_assert (data);
366         pa_assert (card);
367         pa_assert (prof);
368
369         if (card->ports) {
370                 PA_HASHMAP_FOREACH (port, card->ports, state) {
371                         if (port->profiles &&
372                             pa_hashmap_get (port->profiles, prof->name) &&
373                             ((port->direction == PA_DIRECTION_INPUT && data->direction == agl_input)||
374                             (port->direction == PA_DIRECTION_OUTPUT && data->direction == agl_output))) {
375                                 have_ports = true;
376                                 snprintf (key, sizeof(key), "%s@%s", data->paname, port->name);
377
378                                 data->key = key;
379                                 data->available = (port->available != PA_AVAILABLE_NO);
380                                 data->type = 0;
381                                 data->amname = amname;
382                                 data->paport = port->name;
383
384                                 printf ("Key : %s\n", key);
385
386                                  /* this is needed to fill the "port->type" field */
387                                 //agl_classify_node_by_card (data, card, prof, port);
388
389                                  /* this is needed to complete the "pa_discover_add_sink" first pass */
390                                 node = create_node (u, data, &created);
391                         }
392                 }
393         }
394
395         amname = "";
396         data->amname = amname;
397 }
398
399 static const char *node_key (struct userdata *u, agl_direction direction,
400                              void *data, pa_device_port *port, char *buf, size_t len) {
401         char *type;                     /* "sink" or "source" */
402         const char *name;               /* sink or source name */
403         pa_card *card;
404         pa_card_profile *profile;
405         const char *profile_name;
406         const char *bus;
407         char *key = NULL;
408
409         pa_assert (u);
410         pa_assert (data);
411         pa_assert (buf);
412         pa_assert (direction == agl_input || direction == agl_output);
413
414         if (direction == agl_output) {
415                 pa_sink *sink = data;
416                 type = pa_xstrdup ("sink");
417                 name = agl_utils_get_sink_name (sink);
418                 card = sink->card;
419                 if (!port)
420                         port = sink->active_port;
421         } else {
422                 pa_source *source = data;
423                 type = pa_xstrdup ("source");
424                 name = agl_utils_get_source_name (source);
425                 card = source->card;
426                 if (!port)
427                         port = source->active_port;
428         }
429
430         pa_log_debug ("Node type (sink/source): %s", type);
431         pa_log_debug ("Node name: %s", name);
432
433         if (!card)
434                 return NULL;
435
436         pa_assert_se (profile = card->active_profile);
437         if (!u->state.profile) {
438                 pa_log_debug ("profile is now '%s'", profile->name);
439                 profile_name = profile->name;
440         } else {
441                 pa_log_debug ("state.profile is not null. '%s' supresses '%s'",
442                               u->state.profile, profile->name);
443                 profile_name = u->state.profile;
444         }
445
446         if (!(bus = agl_utils_get_card_bus (card))) {
447                 pa_log_debug ("ignoring card '%s' due to lack of '%s' property",
448                               agl_utils_get_card_name (card), PA_PROP_DEVICE_BUS);
449                 return NULL;
450         }
451
452         if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) {
453                 if (!port)
454                         key = (char *)name;
455                 else {
456                         key = buf;
457                         snprintf (buf, len, "%s@%s", name, port->name);
458                 }
459         }
460         /* we do not handle Bluetooth yet, and the function never returns it */
461         /*else if (pa_streq(bus, "bluetooth")) {
462         }*/
463         
464         return (const char *)key;
465 }
466
467 agl_node *agl_discover_find_node_by_key (struct userdata *u, const char *key)
468 {
469         agl_discover *discover;
470         agl_node *node;
471
472         pa_assert (u);
473         pa_assert_se (discover = u->discover);
474
475         if (key)
476                 node = pa_hashmap_get (discover->nodes.byname, key);
477         else
478                 node = NULL;
479
480         return node;
481 }
482
483 void agl_discover_add_node_to_ptr_hash (struct userdata *u, void *ptr, agl_node *node)
484 {
485         agl_discover *discover;
486
487         pa_assert (u);
488         pa_assert (ptr);
489         pa_assert (node);
490         pa_assert_se (discover = u->discover);
491
492         pa_hashmap_put (discover->nodes.byptr, ptr, node);
493 }
494
495 static agl_node *create_node (struct userdata *u, agl_node *data, bool *created_ret)
496 {
497         agl_discover *discover;
498         agl_node *node;
499         bool created;
500
501         pa_assert (u);
502         pa_assert (data);
503         pa_assert (data->key);
504         pa_assert (data->paname);
505         pa_assert_se (discover = u->discover);
506
507         if ((node = pa_hashmap_get (discover->nodes.byname, data->key))) {
508                 pa_log_debug ("No need to create this node");
509                 created = false;
510         } else {
511                 pa_log_debug ("Creating new node");
512
513                 node = agl_node_create (u, data);
514                 pa_hashmap_put (discover->nodes.byname, node->key, node);
515
516                  /* TODO: registering the new node to the Optional router daemon */
517                 /* if (node->available)
518                         pa_audiomgr_register_node (u, node); */
519
520                 created = true;
521         }
522
523         if (created_ret)
524                 *created_ret = created;
525
526         return node;
527 }
528
529 void agl_discover_add_source (struct userdata *u, pa_source *source)
530 {
531         static agl_nodeset_resdef def_resdef = {0, {0, 0}};
532
533         pa_core *core;
534         pa_module *module;
535         pa_card *card;
536         agl_discover *discover;
537         const char *key;
538         char kbf[256];
539         agl_node *node;
540
541         pa_assert (u);
542         pa_assert (source);
543         pa_assert_se (core = u->core);
544         pa_assert_se (discover = u->discover);
545
546         module = source->module;
547         card = source->card;
548
549         if (card) {
550                  /* helper function verifying that sink direction is input/output */
551                 key = node_key (u, agl_input, source, NULL, kbf, sizeof(kbf));
552                 if (!key) return;
553                 pa_log_debug ("Source key: %s", key);
554                 node = agl_discover_find_node_by_key (u, key);
555                 if (!node) {    /* VERIFY IF THIS WORKS */
556                         if (u->state.profile)
557                                 pa_log_debug ("can't find node for source (key '%s')", key);
558                         else {  /* how do we get here ? not in initial setup */
559                                 u->state.source = source->index;
560                                 pa_log_debug ("BUG !");
561                         }
562                         return;
563                 }
564                 pa_log_debug("node for '%s' found (key %s). Updating with source data",
565                              node->paname, node->key);
566                 node->paidx = source->index;
567                 node->available = true;
568                 agl_discover_add_node_to_ptr_hash (u, source, node);
569         }
570 }
571
572 bool agl_discover_preroute_sink_input(struct userdata *u, pa_sink_input_new_data *data)
573 {
574         pa_core *core;
575         pa_sink *sink;
576         pa_source *source;
577         agl_discover *discover;
578         agl_nodeset *nodeset;
579         pa_proplist *pl;
580         agl_node *node;
581
582         pa_assert (u);
583         pa_assert (data);
584         pa_assert_se (core = u->core);
585         pa_assert_se (discover = u->discover);
586         pa_assert_se (nodeset = u->nodeset);
587         pa_assert_se (pl = data->proplist);
588
589          /* is this a valid sink input ? */
590         if (!data->client)
591                 return true;
592
593          /* is there an existing matching node ? */
594         node = agl_node_get_from_data (u, agl_input, data);
595
596         if (!node) {
597                  /* create node */
598                 node = agl_node_create (u, NULL);
599                 node->direction = agl_input;
600                 node->implement = agl_stream;
601                 node->type = agl_classify_guess_stream_node_type (u, pl);
602                 node->visible = true;
603                 node->available = true;
604                 node->ignore = true; /* gets ignored initially */
605                 node->paname = pa_proplist_gets (pl, PA_PROP_APPLICATION_NAME);
606                 node->client = data->client;
607                  /* add to global nodeset */
608                 pa_idxset_put (nodeset->nodes, node, &node->index);
609         }
610
611          /* create NULL sink */
612         if (!node->nullsink)
613                 node->nullsink = agl_utils_create_null_sink (u, u->nsnam);
614         if (!node->nullsink)
615                 return false;
616
617          /* redirect sink input to NULL sink */
618         sink = agl_utils_get_null_sink (u, node->nullsink);
619
620         if (pa_sink_input_new_data_set_sink (data, sink, false))
621                 pa_log_debug ("set sink %u for new sink-input", sink->index);
622         else
623                 pa_log ("can't set sink %u for new sink-input", sink->index);
624
625         return true;
626 }
627
628 void agl_discover_register_sink_input (struct userdata *u, pa_sink_input *sinp)
629 {
630         pa_core *core;
631         pa_proplist *pl, *client_proplist;
632         agl_discover *discover;
633         const char *media;
634         const char *name;
635         const char *role;
636         agl_node_type type;
637         agl_node node_data, *node;
638         char key[256];
639         pa_sink *sink;
640
641         pa_assert (u);
642         pa_assert (sinp);
643         pa_assert_se (core = u->core);
644         pa_assert_se (discover = u->discover);
645         pa_assert_se (pl = sinp->proplist);
646
647         media = pa_proplist_gets (sinp->proplist, PA_PROP_MEDIA_NAME);
648         if (media) {
649                 /* special treatment for combine/loopback streams */
650                 pa_log_debug ("Stream may me combine/loopback, in this case we should ignore it, but use it for now");
651                 /* return; */
652         }
653
654         name = agl_utils_get_sink_input_name (sinp);
655         client_proplist = sinp->client ? sinp->client->proplist : NULL;
656
657         pa_log_debug ("registering input stream '%s'", name);
658
659         /* we could autodetect sink type by using: 
660          *      - PA_PROP_APPLICATION_PROCESS_ID;
661          *      - PA_PROP_APPLICATION_PROCESS_BINARY;
662          *      - PA_PROP_APPLICATION_NAME;
663          *      - ...
664         but let us assume "agl_player" for now */
665         type = agl_player;
666
667         /* this sets our routing properties on the sink, which are :
668          *      #define PA_PROP_ROUTING_CLASS_NAME "routing.class.name"
669          *      #define PA_PROP_ROUTING_CLASS_ID   "routing.class.id"
670          *      #define PA_PROP_ROUTING_METHOD     "routing.method" */
671         agl_utils_set_stream_routing_properties (pl, type, NULL);
672
673         /* we now create a new node for this sink */
674         memset (&node_data, 0, sizeof(node_data));
675         node_data.key = key;
676         node_data.direction = agl_input;
677         node_data.implement = agl_stream;
678         node_data.channels  = sinp->channel_map.channels;
679         node_data.type      = type;
680         node_data.zone      = agl_utils_get_zone (sinp->proplist, client_proplist);
681         node_data.visible   = true;
682         node_data.available = true;
683         node_data.amname    = pa_proplist_gets (pl, "resource.set.appid");
684         node_data.amdescr   = pa_proplist_gets (pl, PA_PROP_MEDIA_NAME);
685         node_data.amid      = AM_ID_INVALID;
686         node_data.paname    = (char *)name;
687         node_data.paidx     = sinp->index;
688         /*node_data.rset.id   = pa_utils_get_rsetid (pl, idbuf, sizeof(idbuf));*/
689
690         role = pa_proplist_gets (sinp->proplist, PA_PROP_MEDIA_ROLE);
691         /* MAIN PREROUTING DONE HERE ! (this was failing in the samples) */
692         /*sink = mir_router_make_prerouting (u, &data, &sinp->channel_map, role, &target);*/
693
694         node = create_node (u, &node_data, NULL);
695         pa_assert (node);
696         agl_discover_add_node_to_ptr_hash (u, sinp, node);
697 /*
698         if (sink && target) {
699                 pa_log_debug ("move stream to sink %u (%s)", sink->index, sink->name);
700
701                 if (pa_sink_input_move_to (sinp, sink, false) < 0)
702                         pa_log ("failed to route '%s' => '%s'",node->amname,target->amname);
703                 else
704                         pa_audiomgr_add_default_route (u, node, target);
705         }*/
706 }
707
708 void agl_discover_register_source_output (struct userdata *u, pa_source_output *sout)
709 {
710         pa_core *core;
711         pa_proplist *pl, *client_proplist;
712         agl_discover *discover;
713         const char *media;
714         const char *name;
715         const char *role;
716         agl_node_type type;
717         agl_node node_data, *node;
718         char key[256];
719         pa_source *source;
720
721         pa_assert (u);
722         pa_assert (sout);
723         pa_assert_se (core = u->core);
724         pa_assert_se (discover = u->discover);
725         pa_assert_se (pl = sout->proplist);
726
727         media = pa_proplist_gets (sout->proplist, PA_PROP_MEDIA_NAME);
728         if (media) {
729                 /* special treatment for loopback streams (not combine as with sinks !) */
730                 pa_log_debug ("Stream may me loopback, in this case we should ignore it, but use it for now");
731                 /* return; */
732         }
733
734          /* TODO : contrary to "agl_discover_register_sink_input", this function is always called
735           * even if we do not find PA_PROP_MEDIA_NAME (see above). */
736         //agl_utils_set_stream_routing_properties (pl, type, NULL);
737
738         name = agl_utils_get_source_output_name (sout);
739         client_proplist = sout->client ? sout->client->proplist : NULL;
740
741         pa_log_debug("registering output stream '%s'", name);
742 }
743
744 void agl_discover_add_sink_input (struct userdata *u, pa_sink_input *sinp) 
745 {
746         pa_core *core;
747         agl_node *node;
748
749         pa_assert (u);
750         pa_assert (sinp);
751         pa_assert_se (core = u->core);
752
753         if (!sinp->client)
754                 return;
755
756          /* is there an existing matching node ? */
757         node = agl_node_get_from_client (u, sinp->client);
758         if (!node) return;
759
760          /* start routing */
761         agl_router_register_node (u, node);
762 }
763
764 void agl_discover_remove_sink_input (struct userdata *u, pa_sink_input *sinp)
765 {
766         pa_core *core;
767         agl_node *node;
768
769         pa_assert (u);
770         pa_assert (sinp);
771         pa_assert_se (core = u->core);
772
773         if (!sinp->client)
774                 return;
775
776          /* is there an existing matching node ? */
777         node = agl_node_get_from_client (u, sinp->client);
778         if (!node) return;
779
780          /* stop routing */
781         agl_router_unregister_node (u, node);
782 }