+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);
+ }
+}
+
+