discovery: add support for stream output (i.e. source-ouput in PA terms)
authorJanos Kovacs <jankovac503@gmail.com>
Tue, 14 Aug 2012 01:34:38 +0000 (04:34 +0300)
committerJanos Kovacs <jankovac503@gmail.com>
Tue, 14 Aug 2012 01:34:38 +0000 (04:34 +0300)
murphy/discover.c
murphy/discover.h
murphy/router.c
murphy/tracker.c
murphy/userdata.h
murphy/utils.c
murphy/utils.h

index 14f9419..65c5712 100644 (file)
@@ -79,8 +79,9 @@ typedef struct {
     uint32_t index;
 } stream_uncork_t;
 
-static const char combine_pattern[]  = "Simultaneous output on ";
-static const char loopback_pattern[] = "Loopback from ";
+static const char combine_pattern[]   = "Simultaneous output on ";
+static const char loopback_outpatrn[] = "Loopback from ";
+static const char loopback_inpatrn[]  = "Loopback to ";
 
 static void handle_alsa_card(struct userdata *, pa_card *);
 static void handle_bluetooth_card(struct userdata *, pa_card *);
@@ -104,6 +105,8 @@ static char *node_key(struct userdata *, mir_direction,
 static pa_sink *make_output_prerouting(struct userdata *, mir_node *,
                                        pa_channel_map *, const char *,
                                        mir_node **);
+static pa_source *make_input_prerouting(struct userdata *, mir_node *,
+                                        const char *, mir_node **);
 
 static mir_node_type get_stream_routing_class(pa_proplist *);
 
@@ -405,6 +408,7 @@ void pa_discover_add_sink(struct userdata *u, pa_sink *sink, pa_bool_t route)
         pa_log_debug("node for '%s' found (key %s). Updating with sink data",
                      node->paname, node->key);
         node->paidx = sink->index;
+        node->available = TRUE;
         pa_discover_add_node_to_ptr_hash(u, sink, node);
 
         if ((loopback_role = pa_classify_loopback_stream(node))) {
@@ -476,6 +480,7 @@ void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
         pa_log_debug("can't find node for sink (name '%s')", name);
     else {
         pa_log_debug("node found for '%s'. Reseting sink data", name);
+        schedule_source_cleanup(u, node);
         node->paidx = PA_IDXSET_INVALID;
         pa_hashmap_remove(discover->nodes.byptr, sink);
 
@@ -486,7 +491,7 @@ void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
                 node->available = FALSE;
             else {
                 if (!u->state.profile)
-                    schedule_deferred_routing(u);
+                    schedule_deferred_routing(u);                
             }
         }
         else {
@@ -528,6 +533,7 @@ void pa_discover_add_source(struct userdata *u, pa_source *source)
         pa_log_debug("node for '%s' found. Updating with source data",
                      node->amname);
         node->paidx = source->index;
+        node->available = TRUE;
         pa_discover_add_node_to_ptr_hash(u, source, node);
         if ((loopback_role = pa_classify_loopback_stream(node))) {
             if (!(ns = pa_utils_get_null_sink(u))) {
@@ -579,6 +585,7 @@ void pa_discover_remove_source(struct userdata *u, pa_source *source)
     pa_discover    *discover;
     mir_node       *node;
     char           *name;
+    mir_node_type   type;
 
     pa_assert(u);
     pa_assert(source);
@@ -593,6 +600,21 @@ void pa_discover_remove_source(struct userdata *u, pa_source *source)
         schedule_source_cleanup(u, node);
         node->paidx = PA_IDXSET_INVALID;
         pa_hashmap_remove(discover->nodes.byptr, source);
+
+        type = node->type;
+
+        if (source->card) {
+            if (type != mir_bluetooth_sco)
+                node->available = FALSE;
+            else {
+                if (!u->state.profile)
+                    schedule_deferred_routing(u);
+            }
+        }
+        else {
+            pa_log_info("currently we do not support statically "
+                        "loaded sources");
+        }
     }
 }
 
@@ -623,7 +645,7 @@ void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
             pa_log_debug("Seems to be a combine stream. Nothing to do ...");
             return;
         }
-        if (!strncmp(media, loopback_pattern, sizeof(loopback_pattern)-1)) {
+        if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
             pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
             return;
         }
@@ -631,7 +653,7 @@ void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
 
     name = pa_utils_get_sink_input_name(sinp);
 
-    pa_log_debug("registering stream '%s'", name);
+    pa_log_debug("registering input stream '%s'", name);
 
     if (!(type = pa_classify_guess_stream_node_type(pl))) {
         pa_log_debug("cant find stream class for '%s'. "
@@ -697,6 +719,7 @@ void pa_discover_preroute_sink_input(struct userdata *u,
     const char    *mnam;
     const char    *role;
     mir_node_type  type;
+    mir_node      *node;
     
     pa_assert(u);
     pa_assert(data);
@@ -709,8 +732,22 @@ void pa_discover_preroute_sink_input(struct userdata *u,
     if (pa_streq(mnam, "module-combine-sink"))
         type = mir_node_type_unknown;
     else {
-        if (pa_streq(mnam, "module-loopback"))
+        if (pa_streq(mnam, "module-loopback")) {
+
+            if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
+                pa_log_debug("can't find loopback node for sink-input");
+                return;
+            }
+
+            if (node->direction == mir_output) {
+                pa_log_debug("refuse to preroute loopback sink-input "
+                             "(current route: sink %u @ %p)", data->sink ?
+                             data->sink->index : PA_IDXSET_INVALID, sink);
+                return;
+            }
+
             data->sink = NULL;
+        }
         type = pa_classify_guess_stream_node_type(pl);
         pa_utils_set_stream_routing_properties(pl, type, data->sink);
     }
@@ -736,8 +773,10 @@ void pa_discover_preroute_sink_input(struct userdata *u,
             }
 #endif
 
-            if (!pa_sink_input_new_data_set_sink(data, sink, FALSE))
-                pa_log("can't set sink %d for new sink-input", sink->index);
+            if (pa_sink_input_new_data_set_sink(data, sink, FALSE))
+                pa_log_debug("set sink %u for new sink-input", sink->index);
+            else 
+                pa_log("can't set sink %u for new sink-input", sink->index);
         }
     }
 }
@@ -771,10 +810,10 @@ void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
     if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
         pa_log_debug("New stream is a combine stream. Nothing to do ...");
         return;
-    } else if (!strncmp(media, loopback_pattern, sizeof(loopback_pattern)-1)) {
+    } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
         pa_log_debug("New stream is a loopback output stream");
 
-        if ((node = pa_utils_get_node_from_stream(u, sinp))) {
+        if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
             if (node->direction == mir_input)
                 pa_log_debug("loopback stream node '%s' found", node->amname);
             else {
@@ -792,7 +831,7 @@ void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
     else {
         name = pa_utils_get_sink_input_name(sinp);
 
-        pa_log_debug("dealing with new stream '%s'", name);
+        pa_log_debug("dealing with new input stream '%s'", name);
         
         if ((type = get_stream_routing_class(pl)) == mir_node_type_unknown) {
             if (!(type = pa_classify_guess_stream_node_type(pl))) {
@@ -900,6 +939,298 @@ void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
 }
 
 
+void pa_discover_register_source_output(struct userdata  *u,
+                                        pa_source_output *sout)
+{
+    pa_core       *core;
+    pa_discover   *discover;
+    pa_proplist   *pl;
+    char          *name;
+    const char    *media;
+    mir_node_type  type;
+    mir_node       data;
+    mir_node      *node;
+    mir_node      *target;
+    char           key[256];
+    pa_source     *source;
+    const char    *role;
+
+    pa_assert(u);
+    pa_assert(sout);
+    pa_assert_se((core = u->core));
+    pa_assert_se((discover = u->discover));
+    pa_assert_se((pl = sout->proplist));
+    
+    if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
+        if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
+            pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
+            return;
+        }
+    }
+
+    name = pa_utils_get_source_output_name(sout);
+
+    pa_log_debug("registering output stream '%s'", name);
+
+    if (!(type = pa_classify_guess_stream_node_type(pl))) {
+        pa_log_debug("cant find stream class for '%s'. "
+                     "Leaving it alone", name);
+        return;
+    }
+
+    pa_utils_set_stream_routing_properties(pl, type, NULL);
+
+    snprintf(key, sizeof(key), "stream_output.%d", sout->index);
+
+    memset(&data, 0, sizeof(data));
+    data.key       = key;
+    data.direction = mir_output;
+    data.implement = mir_stream;
+    data.channels  = sout->channel_map.channels;
+    data.type      = type;
+    data.visible   = TRUE;
+    data.available = TRUE;
+    data.amname    = name;
+    data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
+    data.amid      = AM_ID_INVALID;
+    data.paname    = name;
+    data.paidx     = sout->index;
+
+    /*
+     * here we can't guess whether the application requested an explicit
+     * route by sepcifying the target source @ stream creation time.
+     *
+     * the brute force solution: we make a default route for this stream
+     * possibly overwiriting the orginal app request :(
+     */
+    role   = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
+    source = make_input_prerouting(u, &data, role, &target);
+
+    node = create_node(u, &data, NULL);
+    pa_assert(node);
+    pa_discover_add_node_to_ptr_hash(u, sout, node);
+
+    if (source && target) {
+        pa_log_debug("move stream to source %u (%s)",
+                     source->index, source->name);
+
+        if (pa_source_output_move_to(sout, source, FALSE) < 0)
+            pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
+        else {
+            pa_log_debug("register route '%s' => '%s'",
+                         node->amname, target->amname);
+            /* FIXME: and actually do it ... */
+        }
+    }
+}
+
+void pa_discover_preroute_source_output(struct userdata *u,
+                                        pa_source_output_new_data *data)
+{
+    pa_core       *core;
+    pa_module     *m;
+    pa_proplist   *pl;
+    pa_discover   *discover;
+    mir_node       fake;
+    pa_source     *source;
+    const char    *mnam;
+    const char    *role;
+    mir_node_type  type;
+    mir_node      *node;
+    
+    pa_assert(u);
+    pa_assert(data);
+    pa_assert_se((core = u->core));
+    pa_assert_se((discover = u->discover));
+    pa_assert_se((pl = data->proplist));
+
+    mnam = (m = data->module) ? m->name : "";
+
+    if (pa_streq(mnam, "module-loopback")) {
+        if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
+            pa_log_debug("can't find loopback node for source-output");
+            return;
+        }
+
+        if (node->direction == mir_input) {
+            pa_log_debug("refuse to preroute loopback source-output "
+                         "(current route: source %u @ %p)", data->source ?
+                         data->source->index : PA_IDXSET_INVALID, source);
+            return;
+        }
+
+        data->source = NULL;
+    }
+    type = pa_classify_guess_stream_node_type(pl);
+    pa_utils_set_stream_routing_properties(pl, type, data->source);
+
+    if (!data->source) {
+        memset(&fake, 0, sizeof(fake));
+        fake.direction = mir_output;
+        fake.implement = mir_stream;
+        fake.channels  = data->channel_map.channels;
+        fake.type      = type;
+        fake.visible   = TRUE;
+        fake.available = TRUE;
+        fake.amname    = "<preroute source-output>";
+
+        role   = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
+        source = make_input_prerouting(u, &fake, role, NULL);
+
+        if (source) {
+            if (pa_source_output_new_data_set_source(data, source, FALSE))
+                pa_log_debug("set source %u for new source-output");
+            else {
+                pa_log("can't set source %u for new source-output",
+                       source->index);
+            }
+        }
+    }
+}
+
+
+void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
+{
+    pa_core        *core;
+    pa_source      *s;
+    pa_proplist    *pl;
+    pa_discover    *discover;
+    mir_node        data;
+    mir_node       *node;
+    mir_node       *snod;
+    char           *name;
+    const char     *media;
+    mir_node_type   type;
+    char            key[256];
+    pa_bool_t       created;
+
+    pa_assert(u);
+    pa_assert(sout);
+    pa_assert_se((core = u->core));
+    pa_assert_se((discover = u->discover));
+    pa_assert_se((pl = sout->proplist));
+
+    if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
+        media = "<unknown>";
+
+    if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
+        pa_log_debug("New stream is a loopback input stream");
+
+        if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
+            if (node->direction == mir_output)
+                pa_log_debug("loopback stream node '%s' found", node->amname);
+            else {
+                pa_log_debug("ignoring it");
+                return;
+            }
+        }
+        else {
+            pa_log_debug("can't find node for the loopback stream");
+            return;
+        }
+    }
+    else {
+        name = pa_utils_get_source_output_name(sout);
+
+        pa_log_debug("dealing with new output stream '%s'", name);
+        
+        if ((type = get_stream_routing_class(pl)) == mir_node_type_unknown) {
+            if (!(type = pa_classify_guess_stream_node_type(pl))) {
+                pa_log_debug("cant find stream class for '%s'. "
+                             "Leaving it alone", name);
+                return;
+            }
+            
+            pa_utils_set_stream_routing_properties(pl, type, NULL);
+            
+            /* if needed, make some post-routing here */
+        }
+        
+        /* we need to add this to main hashmap as that is used for loop
+           through on all nodes. */
+        snprintf(key, sizeof(key), "stream_output.%d", sout->index);
+        
+        memset(&data, 0, sizeof(data));
+        data.key       = key;
+        data.direction = mir_output;
+        data.implement = mir_stream;
+        data.channels  = sout->channel_map.channels;
+        data.type      = type;
+        data.visible   = TRUE;
+        data.available = TRUE;
+        data.amname    = name;
+        data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
+        data.amid      = AM_ID_INVALID;
+        data.paname    = name;
+        data.paidx     = sout->index;
+        
+        node = create_node(u, &data, &created);
+
+        pa_assert(node);
+
+        if (!created) {
+            pa_log("%s: confused with stream. '%s' did exists",
+                   __FILE__, node->amname);
+            return;
+        }
+
+        pa_discover_add_node_to_ptr_hash(u, sout, node);        
+    }
+
+    if ((s = sout->source))
+        pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
+
+    if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
+        pa_log_debug("can't figure out where this stream is routed");
+    else {
+        pa_log_debug("register route '%s' => '%s'",
+                     snod->amname, node->amname);
+        /* FIXME: and actually do it ... */
+
+    }
+}
+
+
+void pa_discover_remove_source_output(struct userdata  *u,
+                                      pa_source_output *sout)
+{
+    pa_discover    *discover;
+    mir_node       *node;
+    mir_node       *srcnod;
+    char           *name;
+    pa_loopnode    *loop;
+
+    pa_assert(u);
+    pa_assert(sout);
+    pa_assert_se((discover = u->discover));
+
+    name = pa_utils_get_source_output_name(sout);
+
+    pa_log("source-output '%s' going to be destroyed", name);
+
+    if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
+        pa_log_debug("can't find node for source-output (name '%s')", name);
+    else {
+        pa_log_debug("node found for '%s'. After clearing routes "
+                     "it will be destroyed", name);
+
+        if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
+            pa_log_debug("can't figure out where this stream is routed");
+        else {
+            pa_log_debug("clear route '%s' => '%s'",
+                         node->amname, srcnod->amname);
+            
+            /* FIXME: and actually do it ... */
+            
+        }
+            
+        destroy_node(u, node);
+        
+        mir_router_make_routing(u);
+    }
+}
+
+
 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
 {
     pa_discover *discover;
@@ -1048,8 +1379,10 @@ static void handle_bluetooth_card(struct userdata *u, pa_card *card)
                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
                 pa_classify_node_by_card(&data, card, prof, NULL);
                 node = create_node(u, &data, NULL);
+#if 0
                 cd = mir_constrain_create(u, "profile", mir_constrain_profile,
                                           paname);
+#endif
                 mir_constrain_add_node(u, cd, node);
             }
 
@@ -1062,6 +1395,10 @@ static void handle_bluetooth_card(struct userdata *u, pa_card *card)
                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
                 pa_classify_node_by_card(&data, card, prof, NULL);
                 node = create_node(u, &data, NULL);
+#if 0
+                cd = mir_constrain_create(u, "profile", mir_constrain_profile,
+                                          paname);
+#endif
                 mir_constrain_add_node(u, cd, node);
             }
         }
@@ -1506,7 +1843,7 @@ static pa_sink *make_output_prerouting(struct userdata *u,
         pa_log("can't route to default '%s': no sink", target->amname);
     else {
         if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
-            pa_log("can't route to default '%s': sink is gone",target->amname);
+            pa_log("no route to default '%s': sink is gone", target->amname);
         else {
             if (pa_classify_multiplex_stream(data)) {
                 data->mux = pa_multiplex_create(u->multiplex, core,
@@ -1528,6 +1865,36 @@ static pa_sink *make_output_prerouting(struct userdata *u,
 }
 
 
+static pa_source *make_input_prerouting(struct userdata *u,
+                                        mir_node        *data,
+                                        const char      *media_role,
+                                        mir_node       **target_ret)
+{
+    pa_core    *core;
+    mir_node   *target;
+    pa_source  *source = NULL;
+
+    pa_assert(u);
+    pa_assert(data);
+    pa_assert_se((core = u->core));
+        
+    target = mir_router_make_prerouting(u, data);
+
+    if (!target)
+        pa_log("there is no default route for the stream '%s'", data->amname);
+    else if (target->paidx == PA_IDXSET_INVALID)
+        pa_log("can't route to default '%s': no source", target->amname);
+    else {
+        if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
+            pa_log("no route to default '%s': source is gone",target->amname);
+    }
+
+    if (target_ret)
+        *target_ret = target;
+
+    return source;
+}
+
 static mir_node_type get_stream_routing_class(pa_proplist *pl)
 {
     const char    *clid;
index 6dce2e9..e31462a 100644 (file)
@@ -94,6 +94,12 @@ void pa_discover_preroute_sink_input(struct userdata *,
 void pa_discover_add_sink_input(struct userdata *, pa_sink_input *);
 void pa_discover_remove_sink_input(struct userdata *, pa_sink_input *);
 
+void pa_discover_register_source_output(struct userdata *, pa_source_output *);
+void pa_discover_preroute_source_output(struct userdata *,
+                                        pa_source_output_new_data *);
+void pa_discover_add_source_output(struct userdata *, pa_source_output *);
+void pa_discover_remove_source_output(struct userdata *, pa_source_output *);
+
 
 mir_node *pa_discover_find_node_by_key(struct userdata *, const char *);
 mir_node *pa_discover_find_node_by_ptr(struct userdata *, void *);
index 0e13dde..3dea21b 100644 (file)
@@ -251,7 +251,7 @@ pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
                      "router group not found", clnam, direction, rtgrpnam);
     }
 
-    router->classmap.output[class] = rtg;
+    classmap[class] = rtg;
 
     pa_log_debug("class '%s' assigned to %s routing group '%s'",
                  clnam, direction, rtgrpnam);
@@ -436,7 +436,6 @@ mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
     pa_assert(u);
     pa_assert_se((router = u->router));
     pa_assert_se((data->implement == mir_stream));
-    pa_assert_se((data->direction == mir_input));
 
     priority = node_priority(u, data);
     done = FALSE;
@@ -552,6 +551,8 @@ pa_bool_t mir_router_phone_accept(struct userdata *u, mir_rtgroup *rtg,
             class != mir_jack             &&
             class != mir_hdmi             &&
             class != mir_spdif            &&
+            class != mir_bluetooth_source &&
+            class != mir_bluetooth_sink   &&
             class != mir_bluetooth_carkit   )
         {
             return TRUE;
index 50d44e8..72d6157 100644 (file)
@@ -57,13 +57,20 @@ struct pa_sink_input_hooks {
     pa_hook_slot    *unlink;
 };
 
+struct pa_source_output_hooks {
+    pa_hook_slot    *neew;
+    pa_hook_slot    *put;
+    pa_hook_slot    *unlink;
+};
+
 
 struct pa_tracker {
-    pa_card_hooks       card;
-    pa_port_hooks       port;
-    pa_sink_hooks       sink;
-    pa_source_hooks     source;
-    pa_sink_input_hooks sink_input;
+    pa_card_hooks           card;
+    pa_port_hooks           port;
+    pa_sink_hooks           sink;
+    pa_source_hooks         source;
+    pa_sink_input_hooks     sink_input;
+    pa_source_output_hooks  source_output;
 };
 
 
@@ -85,17 +92,22 @@ static pa_hook_result_t sink_input_new(void *, void *, void *);
 static pa_hook_result_t sink_input_put(void *, void *, void *);
 static pa_hook_result_t sink_input_unlink(void *, void *, void *);
 
+static pa_hook_result_t source_output_new(void *, void *, void *);
+static pa_hook_result_t source_output_put(void *, void *, void *);
+static pa_hook_result_t source_output_unlink(void *, void *, void *);
+
 
 pa_tracker *pa_tracker_init(struct userdata *u)
 {
-    pa_core             *core;
-    pa_hook             *hooks;
-    pa_tracker          *tracker;
-    pa_card_hooks       *card;
-    pa_port_hooks       *port;
-    pa_sink_hooks       *sink;
-    pa_source_hooks     *source;
-    pa_sink_input_hooks *sinp;
+    pa_core                *core;
+    pa_hook                *hooks;
+    pa_tracker             *tracker;
+    pa_card_hooks          *card;
+    pa_port_hooks          *port;
+    pa_sink_hooks          *sink;
+    pa_source_hooks        *source;
+    pa_sink_input_hooks    *sinp;
+    pa_source_output_hooks *sout;
 
     pa_assert(u);
     pa_assert_se((core = u->core));
@@ -107,6 +119,7 @@ pa_tracker *pa_tracker_init(struct userdata *u)
     sink   = &tracker->sink;
     source = &tracker->source;
     sinp   = &tracker->sink_input;
+    sout   = &tracker->source_output;
 
     /* card */
     card->put     = pa_hook_connect(
@@ -166,6 +179,20 @@ pa_tracker *pa_tracker_init(struct userdata *u)
                        PA_HOOK_LATE, sink_input_unlink, u
                    );
     
+    /* source-output */
+    sout->neew   = pa_hook_connect(
+                       hooks + PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+                       PA_HOOK_EARLY, source_output_new, u
+                   );
+    sout->put    = pa_hook_connect(
+                       hooks + PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
+                       PA_HOOK_LATE, source_output_put, u
+                   );
+    sout->unlink = pa_hook_connect(
+                       hooks + PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
+                       PA_HOOK_LATE, source_output_unlink, u
+                   );
+    
     return tracker;
 }
 
@@ -211,12 +238,13 @@ void pa_tracker_done(struct userdata *u)
 
 void pa_tracker_synchronize(struct userdata *u)
 {
-    pa_core       *core;
-    pa_card       *card;
-    pa_sink       *sink;
-    pa_source     *source;
-    pa_sink_input *sinp;
-    uint32_t       index;
+    pa_core          *core;
+    pa_card          *card;
+    pa_sink          *sink;
+    pa_source        *source;
+    pa_sink_input    *sinp;
+    pa_source_output *sout;
+    uint32_t          index;
 
     pa_assert(u);
     pa_assert_se((core = u->core));
@@ -234,7 +262,7 @@ void pa_tracker_synchronize(struct userdata *u)
         pa_discover_add_source(u, source);
     }
 
-    /* Hmm... we should first collect all sink-inputs, assign
+    /* Hmm... we should first collect all sink-inputs & source-outputs, assign
        priority to them, sort them, and call pa_discover_register_sink_input()
        in reverse priority order. Until than we may experience sound leaks
        unnecessary profile changes etc ... */
@@ -243,6 +271,10 @@ void pa_tracker_synchronize(struct userdata *u)
         pa_discover_register_sink_input(u, sinp);
     }
 
+    PA_IDXSET_FOREACH(sout, core->source_outputs, index) {
+        pa_discover_register_source_output(u, sout);
+    }
+
     mir_router_make_routing(u);
 }
 
@@ -458,6 +490,53 @@ static pa_hook_result_t sink_input_unlink(void *hook_data,
 }
 
 
+static pa_hook_result_t source_output_new(void *hook_data,
+                                          void *call_data,
+                                          void *slot_data)
+{
+    pa_source_output_new_data *data = (pa_source_output_new_data *)call_data;
+    struct userdata *u = (struct userdata *)slot_data;
+
+    pa_assert(u);
+    pa_assert(data);
+
+    pa_discover_preroute_source_output(u, data);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put(void *hook_data,
+                                          void *call_data,
+                                          void *slot_data)
+{
+    pa_source_output *sout = (pa_source_output *)call_data;
+    struct userdata *u = (struct userdata *)slot_data;
+
+    pa_assert(u);
+    pa_assert(sout);
+
+    pa_discover_add_source_output(u, sout);
+
+    return PA_HOOK_OK;
+}
+
+
+static pa_hook_result_t source_output_unlink(void *hook_data,
+                                             void *call_data,
+                                             void *slot_data)
+{
+    struct pa_source_output *sout = (pa_source_output *)call_data;
+    struct userdata *u = (struct userdata *)slot_data;
+
+    pa_assert(u);
+    pa_assert(sout);
+
+    pa_discover_remove_source_output(u, sout);
+
+    return PA_HOOK_OK;
+}
+
+
 /*
  * Local Variables:
  * c-basic-offset: 4
index eab55c8..eb2c78d 100644 (file)
@@ -56,6 +56,8 @@ typedef struct pa_port_hooks            pa_port_hooks;
 typedef struct pa_sink_hooks            pa_sink_hooks;
 typedef struct pa_source_hooks          pa_source_hooks;
 typedef struct pa_sink_input_hooks      pa_sink_input_hooks;
+typedef struct pa_source_output_hooks   pa_source_output_hooks;
+
 
 typedef enum   mir_direction            mir_direction;
 typedef enum   mir_implement            mir_implement;
index 5316cf1..b7d3da0 100644 (file)
@@ -37,6 +37,7 @@
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
 #include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
 
 #include "userdata.h"
 #include "utils.h"
@@ -57,7 +58,7 @@ struct pa_null_sink {
 
 static uint32_t stamp;
 
-static char *sink_input_name(pa_proplist *);
+static char *stream_name(pa_proplist *);
 
 
 pa_null_sink *pa_utils_create_null_sink(struct userdata *u, const char *name)
@@ -159,7 +160,7 @@ char *pa_utils_get_sink_input_name(pa_sink_input *sinp)
 {
     char *name;
 
-    if (sinp && (name = sink_input_name(sinp->proplist)))
+    if (sinp && (name = stream_name(sinp->proplist)))
         return name;
     
     return "<unknown>";
@@ -169,7 +170,28 @@ char *pa_utils_get_sink_input_name_from_data(pa_sink_input_new_data *data)
 {
     char *name;
 
-    if (data && (name = sink_input_name(data->proplist)))
+    if (data && (name = stream_name(data->proplist)))
+        return name;
+    
+    return "<unknown>";
+}
+
+
+char *pa_utils_get_source_output_name(pa_source_output *sout)
+{
+    char *name;
+
+    if (sout && (name = stream_name(sout->proplist)))
+        return name;
+    
+    return "<unknown>";
+}
+
+char *pa_utils_get_source_output_name_from_data(pa_source_output_new_data*data)
+{
+    char *name;
+
+    if (data && (name = stream_name(data->proplist)))
         return name;
     
     return "<unknown>";
@@ -178,7 +200,7 @@ char *pa_utils_get_sink_input_name_from_data(pa_sink_input_new_data *data)
 
 void pa_utils_set_stream_routing_properties(pa_proplist *pl,
                                             int          styp,
-                                            pa_sink     *sink)
+                                            void        *target)
 {
     const char    *clnam;
     const char    *method;
@@ -189,13 +211,13 @@ void pa_utils_set_stream_routing_properties(pa_proplist *pl,
     
     snprintf(clid, sizeof(clid), "%d", styp);
     clnam  = mir_node_type_str(styp);
-    method = sink ? PA_ROUTING_EXPLICIT : PA_ROUTING_DEFAULT;
+    method = target ? PA_ROUTING_EXPLICIT : PA_ROUTING_DEFAULT;
 
     if (pa_proplist_sets(pl, PA_PROP_ROUTING_CLASS_NAME, clnam ) < 0 ||
         pa_proplist_sets(pl, PA_PROP_ROUTING_CLASS_ID  , clid  ) < 0 ||
         pa_proplist_sets(pl, PA_PROP_ROUTING_METHOD    , method) < 0  )
     {
-        pa_log("failed to set some property on sink-input");
+        pa_log("failed to set some stream property");
     }
 }
 
@@ -243,30 +265,91 @@ int pa_utils_get_stream_class(pa_proplist *pl)
     return (int)clid;
 }
 
-mir_node *pa_utils_get_node_from_stream(struct userdata *u,pa_sink_input *sinp)
+mir_node *pa_utils_get_node_from_stream(struct userdata *u,
+                                        mir_direction    type,
+                                        void            *ptr)
 {
-    mir_node *node;
-    const char *index_str;
-    uint32_t index = PA_IDXSET_INVALID;
-    char *e;
+    pa_sink_input    *sinp;
+    pa_source_output *sout;
+    pa_proplist      *pl;
+    mir_node         *node;
+    const char       *index_str;
+    uint32_t          index = PA_IDXSET_INVALID;
+    char             *e;
+    char              name[256];
 
     pa_assert(u);
-    pa_assert(sinp);
+    pa_assert(ptr);
+    pa_assert(type == mir_input || type == mir_output);
+
+    if (type == mir_input) {
+        sinp = (pa_sink_input *)ptr;
+        pl = sinp->proplist;
+        snprintf(name, sizeof(name), "sink-input.%u", sinp->index);
+    }
+    else {
+        sout = (pa_source_output *)ptr;
+        pl = sout->proplist;
+        snprintf(name, sizeof(name), "source-output.%u", sout->index);
+    }
+    
+
+    if ((index_str = pa_proplist_gets(pl, PA_PROP_NODE_INDEX))) {
+        index = strtoul(index_str, &e, 10);
+        if (e != index_str && *e == '\0') {
+            if ((node = mir_node_find_by_index(u, index)))
+                return node;
+
+            pa_log_debug("can't find find node for %s", name);
+        }
+    }
+
+    return NULL;
+}
+
+mir_node *pa_utils_get_node_from_data(struct userdata *u,
+                                      mir_direction    type,
+                                      void            *ptr)
+{
+    pa_sink_input_new_data *sinp;
+    pa_source_output_new_data *sout;
+    pa_proplist  *pl;
+    mir_node     *node;
+    const char   *index_str;
+    uint32_t      index = PA_IDXSET_INVALID;
+    char         *e;
+    char          name[256];
+
+    pa_assert(u);
+    pa_assert(ptr);
+    pa_assert(type == mir_input || type == mir_output);
+
+    if (type == mir_input) {
+        sinp = (pa_sink_input_new_data *)ptr;
+        pl = sinp->proplist;
+        snprintf(name, sizeof(name), "sink-input");
+    }
+    else {
+        sout = (pa_source_output_new_data *)ptr;
+        pl = sout->proplist;
+        snprintf(name, sizeof(name), "source-output");
+    }
+    
 
-    if ((index_str = pa_proplist_gets(sinp->proplist, PA_PROP_NODE_INDEX))) {
+    if ((index_str = pa_proplist_gets(pl, PA_PROP_NODE_INDEX))) {
         index = strtoul(index_str, &e, 10);
         if (e != index_str && *e == '\0') {
             if ((node = mir_node_find_by_index(u, index)))
                 return node;
 
-            pa_log_debug("can't find find node for sink-input.%u",sinp->index);
+            pa_log_debug("can't find find node for %s", name);
         }
     }
 
     return NULL;
 }
 
-static char *sink_input_name(pa_proplist *pl)
+static char *stream_name(pa_proplist *pl)
 {
     const char  *appnam;
     const char  *binnam;
index b2fd42d..73a8872 100644 (file)
@@ -35,14 +35,17 @@ char *pa_utils_get_sink_name(pa_sink *);
 char *pa_utils_get_source_name(pa_source *);
 char *pa_utils_get_sink_input_name(pa_sink_input *);
 char *pa_utils_get_sink_input_name_from_data(pa_sink_input_new_data *);
+char *pa_utils_get_source_output_name(pa_source_output *);
+char *pa_utils_get_source_output_name_from_data(pa_source_output_new_data *);
 
-void  pa_utils_set_stream_routing_properties(pa_proplist *, int, pa_sink *);
+void  pa_utils_set_stream_routing_properties(pa_proplist *, int, void *);
 void  pa_utils_set_stream_routing_method_property(pa_proplist *, pa_bool_t);
 pa_bool_t pa_utils_stream_has_default_route(pa_proplist *);
 int   pa_utils_get_stream_class(pa_proplist *);
 
 #ifdef foouserdatafoo  /* argh ... */
-mir_node *pa_utils_get_node_from_stream(struct userdata *, pa_sink_input *);
+mir_node *pa_utils_get_node_from_stream(struct userdata *,mir_direction,void*);
+mir_node *pa_utils_get_node_from_data(struct userdata *, mir_direction,void *);
 #endif
 
 const char *pa_utils_file_path(const char *, char *, size_t);