diff options
author | Wim Taymans <wtaymans@redhat.com> | 2019-11-15 12:08:46 +0100 |
---|---|---|
committer | Wim Taymans <wtaymans@redhat.com> | 2019-11-29 13:34:07 +0100 |
commit | 3cd7d90d885950f82cf19ea79973dbf3091e4180 (patch) | |
tree | ee097d8fc1ac27a7e065b3eb4cfb882c50ddfb9c | |
parent | 9bc5d23ffecbc8a2d8c8450f7e25598ca2d450b8 (diff) |
media-session: link all stream portswork
Create a helper in the session manager to link all ports between
2 nodes.
Make the output endpoint call the input endpoint create_link, passing
the node or ports to link to.
Make the input endpoint complete the link by calling the session
helper to link all ports.
Remove the node policy, we're only using a policy for the endpoints
now.
-rw-r--r-- | src/examples/media-session/alsa-endpoint.c | 57 | ||||
-rw-r--r-- | src/examples/media-session/media-session.c | 82 | ||||
-rw-r--r-- | src/examples/media-session/media-session.h | 3 | ||||
-rw-r--r-- | src/examples/media-session/policy-ep.c | 52 | ||||
-rw-r--r-- | src/examples/media-session/policy.c | 1264 | ||||
-rw-r--r-- | src/examples/media-session/stream-monitor.c | 174 | ||||
-rw-r--r-- | src/examples/meson.build | 1 | ||||
-rw-r--r-- | src/extensions/session-manager/keys.h | 11 | ||||
-rw-r--r-- | src/modules/module-session-manager/client-endpoint.c | 4 |
9 files changed, 201 insertions, 1447 deletions
diff --git a/src/examples/media-session/alsa-endpoint.c b/src/examples/media-session/alsa-endpoint.c index 94f37aad..8a77fcd6 100644 --- a/src/examples/media-session/alsa-endpoint.c +++ b/src/examples/media-session/alsa-endpoint.c @@ -113,19 +113,10 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop char buf[1024]; struct spa_pod_builder b = { 0, }; struct spa_pod *param; + int res; pw_log_debug(NAME" %p: endpoint %p", impl, endpoint); - p = pw_properties_new_dict(props); - - if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { - pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->obj->info->id); - pw_properties_setf(p, PW_KEY_LINK_OUTPUT_PORT, "-1"); - } else { - pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->obj->info->id); - pw_properties_setf(p, PW_KEY_LINK_INPUT_PORT, "-1"); - } - if (!endpoint->active) { endpoint->format.info.raw.rate = 48000; @@ -147,15 +138,43 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop endpoint->active = true; } - sm_media_session_create_object(impl->session, - "link-factory", - PW_TYPE_INTERFACE_Link, - PW_VERSION_LINK_PROXY, - &p->dict, 0); + p = pw_properties_new_dict(props); + if (p == NULL) + return -errno; + + if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { + const char *str; + struct sm_object *obj; + + str = spa_dict_lookup(props, PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT); + if (str == NULL) { + pw_log_warn(NAME" %p: no target endpoint given", impl); + res = -EINVAL; + goto exit; + } + obj = sm_media_session_find_object(impl->session, atoi(str)); + if (obj == NULL || obj->type != PW_TYPE_INTERFACE_Endpoint) { + pw_log_warn(NAME" %p: could not find endpoint %s (%p)", impl, str, obj); + res = -EINVAL; + goto exit; + } + + pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->obj->info->id); + pw_properties_setf(p, PW_KEY_LINK_OUTPUT_PORT, "-1"); + pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)obj->proxy, &p->dict); + } else { + pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->obj->info->id); + pw_properties_setf(p, PW_KEY_LINK_INPUT_PORT, "-1"); + + sm_media_session_create_links(impl->session, &p->dict); + } + + res = 0; +exit: pw_properties_free(p); - return 0; + return res; } static const struct pw_client_endpoint_proxy_events client_endpoint_events = { @@ -305,14 +324,14 @@ static int setup_alsa_fallback_endpoint(struct alsa_object *obj) if ((str = pw_properties_get(n->props, PW_KEY_PRIORITY_SESSION)) != NULL) pw_properties_set(s->props, PW_KEY_PRIORITY_SESSION, str); if (n->direction == PW_DIRECTION_OUTPUT) - pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Playback"); + pw_properties_set(s->props, PW_KEY_ENDPOINT_STREAM_NAME, "Playback"); else - pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Capture"); + pw_properties_set(s->props, PW_KEY_ENDPOINT_STREAM_NAME, "Capture"); s->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; s->info.id = n->id; s->info.endpoint_id = endpoint->info.id; - s->info.name = (char*)pw_properties_get(s->props, PW_KEY_STREAM_NAME); + s->info.name = (char*)pw_properties_get(s->props, PW_KEY_ENDPOINT_STREAM_NAME); s->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS; s->info.props = &s->props->dict; diff --git a/src/examples/media-session/media-session.c b/src/examples/media-session/media-session.c index 1a60bcd1..8c368634 100644 --- a/src/examples/media-session/media-session.c +++ b/src/examples/media-session/media-session.c @@ -605,6 +605,88 @@ struct pw_proxy *sm_media_session_create_object(struct sm_media_session *sess, factory_name, type, version, props, user_data_size); } +int sm_media_session_create_links(struct sm_media_session *sess, + const struct spa_dict *dict) +{ + struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this); + struct sm_object *obj; + struct sm_node *outnode, *innode; + struct sm_port *outport, *inport; + struct pw_properties *props; + const char *str; + int res; + + sm_media_session_roundtrip(sess); + + /* find output node */ + if ((str = spa_dict_lookup(dict, PW_KEY_LINK_OUTPUT_NODE)) == NULL) { + res = -EINVAL; + pw_log_error(NAME" %p: no output node given", impl); + goto exit; + } + if ((obj = find_object(impl, atoi(str))) == NULL || + obj->type != PW_TYPE_INTERFACE_Node) { + res = -EINVAL; + pw_log_error(NAME" %p: can find output node %s", impl, str); + goto exit; + } + outnode = (struct sm_node*)obj; + + /* find input node */ + if ((str = spa_dict_lookup(dict, PW_KEY_LINK_INPUT_NODE)) == NULL) { + res = -EINVAL; + pw_log_error(NAME" %p: no input node given", impl); + goto exit; + } + if ((obj = find_object(impl, atoi(str))) == NULL || + obj->type != PW_TYPE_INTERFACE_Node) { + res = -EINVAL; + pw_log_error(NAME" %p: can find input node %s", impl, str); + goto exit; + } + innode = (struct sm_node*)obj; + + pw_log_debug(NAME" %p: linking %d -> %d", impl, outnode->obj.id, innode->obj.id); + + props = pw_properties_new(NULL, NULL); + pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", outnode->obj.id); + pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", innode->obj.id); + + for (outport = spa_list_first(&outnode->port_list, struct sm_port, link), + inport = spa_list_first(&innode->port_list, struct sm_port, link); + !spa_list_is_end(outport, &outnode->port_list, link) && + !spa_list_is_end(inport, &innode->port_list, link);) { + + pw_log_debug(NAME" %p: port %d:%d -> %d:%d", impl, + outport->direction, outport->obj.id, + inport->direction, inport->obj.id); + + if (outport->direction == PW_DIRECTION_OUTPUT && + inport->direction == PW_DIRECTION_INPUT) { + + pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", outport->obj.id); + pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", inport->obj.id); + + sm_media_session_create_object(sess, "link-factory", + PW_TYPE_INTERFACE_Link, + PW_VERSION_LINK_PROXY, + &props->dict, 0); + + outport = spa_list_next(outport, link); + inport = spa_list_next(inport, link); + } else { + if (outport->direction != PW_DIRECTION_OUTPUT) + outport = spa_list_next(outport, link); + if (inport->direction != PW_DIRECTION_INPUT) + inport = spa_list_next(inport, link); + } + } + pw_properties_free(props); + + res = 0; +exit: + return res; +} /** * Session implementation diff --git a/src/examples/media-session/media-session.h b/src/examples/media-session/media-session.h index d73fa990..9f88229e 100644 --- a/src/examples/media-session/media-session.h +++ b/src/examples/media-session/media-session.h @@ -168,6 +168,9 @@ struct pw_proxy *sm_media_session_create_object(struct sm_media_session *sess, const char *factory_name, uint32_t type, uint32_t version, const struct spa_dict *props, size_t user_data_size); +int sm_media_session_create_links(struct sm_media_session *sess, + const struct spa_dict *dict); + #ifdef __cplusplus } #endif diff --git a/src/examples/media-session/policy-ep.c b/src/examples/media-session/policy-ep.c index d1194f3f..833fa90b 100644 --- a/src/examples/media-session/policy-ep.c +++ b/src/examples/media-session/policy-ep.c @@ -43,6 +43,7 @@ #include "media-session.h" #define NAME "policy-ep" +#define SESSION_KEY "policy-endpoint" #define DEFAULT_CHANNELS 2 #define DEFAULT_SAMPLERATE 48000 @@ -108,7 +109,7 @@ handle_endpoint(struct impl *impl, struct sm_object *object) struct endpoint *ep; uint32_t client_id = SPA_ID_INVALID; - if (sm_object_get_data(object, "policy-endpoint") != NULL) + if (sm_object_get_data(object, SESSION_KEY) != NULL) return 0; if (object->props) { @@ -123,7 +124,7 @@ handle_endpoint(struct impl *impl, struct sm_object *object) if (media_class == NULL) return 0; - ep = sm_object_add_data(object, "policy-endpoint", sizeof(struct endpoint)); + ep = sm_object_add_data(object, SESSION_KEY, sizeof(struct endpoint)); ep->obj = (struct sm_endpoint*)object; ep->id = object->id; ep->impl = impl; @@ -183,27 +184,22 @@ handle_stream(struct impl *impl, struct sm_object *object) struct stream *s; struct endpoint *ep; - if (sm_object_get_data(object, "policy-endpoint") != NULL) + if (sm_object_get_data(object, SESSION_KEY) != NULL) return 0; if (stream->endpoint == NULL) return 0; - ep = sm_object_get_data(&stream->endpoint->obj, "policy-endpoint"); + ep = sm_object_get_data(&stream->endpoint->obj, SESSION_KEY); if (ep == NULL) return 0; - s = sm_object_add_data(object, "policy-endpoint", sizeof(struct stream)); + s = sm_object_add_data(object, SESSION_KEY, sizeof(struct stream)); s->obj = (struct sm_endpoint_stream*)object; s->id = object->id; s->impl = impl; s->endpoint = ep; - if (s->endpoint->type == ENDPOINT_TYPE_DEVICE) { - pw_endpoint_stream_proxy_enum_params((struct pw_endpoint_stream_proxy*)stream->obj.proxy, - 0, SPA_PARAM_EnumFormat, - 0, -1, NULL); - } return 0; } @@ -212,8 +208,6 @@ static void session_update(void *data, struct sm_object *object) struct impl *impl = data; int res; - pw_log_debug(NAME " %p: update global '%d'", impl, object->id); - switch (object->type) { case PW_TYPE_INTERFACE_Endpoint: res = handle_endpoint(impl, object); @@ -243,8 +237,10 @@ static void session_remove(void *data, struct sm_object *object) case PW_TYPE_INTERFACE_Endpoint: { struct endpoint *ep; - if ((ep = sm_object_get_data(object, "policy-endpoint")) != NULL) + if ((ep = sm_object_get_data(object, SESSION_KEY)) != NULL) { spa_list_remove(&ep->link); + free(ep->media); + } break; } default: @@ -323,24 +319,18 @@ static int link_endpoints(struct endpoint *endpoint, enum pw_direction direction pw_log_debug(NAME " %p: link endpoints %d %d %d", impl, max, endpoint->id, peer->id); - props = pw_properties_new(NULL, NULL); - if (endpoint->direction == PW_DIRECTION_OUTPUT) { - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->id); - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", -1); - pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", peer->id); - pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", -1); - pw_log_debug(NAME " %p: endpoint %d -> endpoint %d", impl, - endpoint->id, peer->id); - - } - else { - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", peer->id); - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", -1); - pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->id); - pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", -1); - pw_log_debug(NAME " %p: endpoint %d -> endpoint %d", impl, - peer->id, endpoint->id); + if (endpoint->direction == PW_DIRECTION_INPUT) { + struct endpoint *t = endpoint; + endpoint = peer; + peer = t; } + props = pw_properties_new(NULL, NULL); + pw_properties_setf(props, PW_KEY_ENDPOINT_LINK_OUTPUT_ENDPOINT, "%d", endpoint->id); + pw_properties_setf(props, PW_KEY_ENDPOINT_LINK_OUTPUT_STREAM, "%d", -1); + pw_properties_setf(props, PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT, "%d", peer->id); + pw_properties_setf(props, PW_KEY_ENDPOINT_LINK_INPUT_STREAM, "%d", -1); + pw_log_debug(NAME " %p: endpoint %d -> endpoint %d", impl, + endpoint->id, peer->id); pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)endpoint->obj->obj.proxy, &props->dict); @@ -487,7 +477,7 @@ static int rescan_endpoint(struct impl *impl, struct endpoint *ep) if ((obj = sm_media_session_find_object(impl->session, find.path_id)) != NULL) { if (obj->type == PW_TYPE_INTERFACE_Endpoint) { - peer = sm_object_get_data(obj, "policy-endpoint"); + peer = sm_object_get_data(obj, SESSION_KEY); goto do_link; } } diff --git a/src/examples/media-session/policy.c b/src/examples/media-session/policy.c deleted file mode 100644 index 52cd08cb..00000000 --- a/src/examples/media-session/policy.c +++ /dev/null @@ -1,1264 +0,0 @@ -/* PipeWire - * - * Copyright © 2019 Wim Taymans - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <math.h> -#include <time.h> - -#include "config.h" - -#include <spa/node/node.h> -#include <spa/utils/hook.h> -#include <spa/param/audio/format-utils.h> -#include <spa/param/props.h> -#include <spa/debug/pod.h> - -#include "pipewire/pipewire.h" -#include "pipewire/private.h" -#include "extensions/session-manager.h" - -#define NAME "policy" - -#define DEFAULT_CHANNELS 2 -#define DEFAULT_SAMPLERATE 48000 - -#define DEFAULT_IDLE_SECONDS 3 - -struct impl; - -struct impl { - struct timespec now; - - struct pw_core *core; - struct pw_remote *remote; - struct spa_hook remote_listener; - - struct pw_core_proxy *core_proxy; - struct spa_hook core_listener; - - struct pw_registry_proxy *registry_proxy; - struct spa_hook registry_listener; - - struct pw_map globals; - - struct spa_list client_list; - struct spa_list node_list; - struct spa_list session_list; - int seq; -}; - -struct object { - struct impl *impl; - uint32_t id; - uint32_t type; - struct pw_proxy *proxy; - struct spa_hook listener; -}; - -struct client { - struct object obj; - - struct spa_list l; - - struct spa_hook listener; - struct pw_client_info *info; -}; - -struct node { - struct object obj; - - struct spa_list l; - - struct spa_hook listener; - struct pw_node_info *info; - - uint32_t client_id; - - struct spa_list session_link; - struct session *session; - - struct node *peer; - - struct session *manager; - struct spa_list port_list; - - enum pw_direction direction; -#define NODE_TYPE_UNKNOWN 0 -#define NODE_TYPE_STREAM 1 -#define NODE_TYPE_DEVICE 2 - uint32_t type; - char *media; - - uint32_t media_type; - uint32_t media_subtype; - struct spa_audio_info_raw format; -}; - -struct port { - struct object obj; - - struct spa_list l; - enum pw_direction direction; - struct pw_port_info *info; - struct node *node; -#define PORT_FLAG_NONE 0 -#define PORT_FLAG_DSP (1<<0) -#define PORT_FLAG_SKIP (1<<1) - uint32_t flags; - - struct spa_hook listener; -}; - -struct link { - struct object obj; - struct port *out; - struct port *in; -}; - -struct session { - struct spa_list l; - - uint32_t id; - - struct impl *impl; - enum pw_direction direction; - int priority; - uint64_t plugged; - - struct node *node; - - struct spa_list node_list; - - struct spa_hook listener; - - struct spa_source *idle_timeout; - - bool starting; - bool enabled; - bool busy; - bool exclusive; - bool need_dsp; -}; - -static void add_object(struct impl *impl, struct object *obj) -{ - size_t size = pw_map_get_size(&impl->globals); - while (obj->id > size) - pw_map_insert_at(&impl->globals, size++, NULL); - pw_map_insert_at(&impl->globals, obj->id, obj); -} - -static void remove_object(struct impl *impl, struct object *obj) -{ - pw_map_insert_at(&impl->globals, obj->id, NULL); -} - -static void *find_object(struct impl *impl, uint32_t id) -{ - void *obj; - if ((obj = pw_map_lookup(&impl->globals, id)) != NULL) - return obj; - return NULL; -} - -static void schedule_rescan(struct impl *impl) -{ - if (impl->core_proxy) - impl->seq = pw_core_proxy_sync(impl->core_proxy, 0, impl->seq); -} - -static void remove_idle_timeout(struct session *sess) -{ - struct impl *impl = sess->impl; - struct pw_loop *main_loop = pw_core_get_main_loop(impl->core); - - if (sess->idle_timeout) { - pw_loop_destroy_source(main_loop, sess->idle_timeout); - sess->idle_timeout = NULL; - } -} - -static void idle_timeout(void *data, uint64_t expirations) -{ - struct session *sess = data; - struct impl *impl = sess->impl; - struct spa_command *cmd = &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Suspend); - - pw_log_debug(NAME " %p: session %d idle timeout", impl, sess->id); - - remove_idle_timeout(sess); - - pw_node_proxy_send_command((struct pw_node_proxy*)sess->node->obj.proxy, cmd); -} - -static void add_idle_timeout(struct session *sess) -{ - struct timespec value; - struct impl *impl = sess->impl; - struct pw_loop *main_loop = pw_core_get_main_loop(impl->core); - - if (sess->idle_timeout == NULL) - sess->idle_timeout = pw_loop_add_timer(main_loop, idle_timeout, sess); - - value.tv_sec = DEFAULT_IDLE_SECONDS; - value.tv_nsec = 0; - pw_loop_update_timer(main_loop, sess->idle_timeout, &value, NULL, false); -} - -static int on_node_idle(struct impl *impl, struct node *node) -{ - struct session *sess = node->manager; - - if (sess == NULL) - return 0; - - switch (node->type) { - case NODE_TYPE_DEVICE: - pw_log_debug(NAME" %p: device idle for session %d", impl, sess->id); - sess->busy = false; - sess->exclusive = false; - add_idle_timeout(sess); - break; - default: - break; - } - return 0; -} - -static int on_node_running(struct impl *impl, struct node *node) -{ - struct session *sess = node->manager; - - if (sess == NULL) - return 0; - - switch (node->type) { - case NODE_TYPE_DEVICE: - pw_log_debug(NAME" %p: device running or session %d", impl, sess->id); - remove_idle_timeout(sess); - break; - default: - break; - } - return 0; -} - -static void node_event_info(void *object, const struct pw_node_info *info) -{ - struct node *n = object; - struct impl *impl = n->obj.impl; - - pw_log_debug(NAME" %p: info for node %d type %d", impl, n->obj.id, n->type); - n->info = pw_node_info_update(n->info, info); - - if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) { - switch (info->state) { - case PW_NODE_STATE_IDLE: - on_node_idle(impl, n); - break; - case PW_NODE_STATE_RUNNING: - on_node_running(impl, n); - break; - case PW_NODE_STATE_SUSPENDED: - break; - default: - break; - } - } -} - -static void node_event_param(void *object, int seq, - uint32_t id, uint32_t index, uint32_t next, - const struct spa_pod *param) -{ - struct node *n = object; - struct impl *impl = n->obj.impl; - struct spa_audio_info_raw info = { 0, }; - - pw_log_debug(NAME" %p: param for node %d, %d", impl, n->obj.id, id); - - if (id != SPA_PARAM_EnumFormat) - goto error; - - if (spa_format_parse(param, &n->media_type, &n->media_subtype) < 0) - goto error; - - if (n->media_type != SPA_MEDIA_TYPE_audio || - n->media_subtype != SPA_MEDIA_SUBTYPE_raw) - return; - - spa_pod_object_fixate((struct spa_pod_object*)param); - if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) - spa_debug_pod(2, NULL, param); - - if (spa_format_audio_raw_parse(param, &info) < 0) - goto error; - - if (n->format.channels < info.channels) - n->format = info; - return; - - error: - pw_log_warn("unhandled param:"); - if (pw_log_level_enabled(SPA_LOG_LEVEL_WARN)) - spa_debug_pod(2, NULL, param); - return; -} - -static const struct pw_node_proxy_events node_events = { - PW_VERSION_NODE_PROXY_EVENTS, - .info = node_event_info, - .param = node_event_param, -}; - -static void remove_session(struct impl *impl, struct session *sess) -{ - struct node *n, *t; - - pw_log_debug(NAME " %p: remove session '%d'", impl, sess->id); - remove_idle_timeout(sess); - - spa_list_for_each_safe(n, t, &sess->node_list, session_link) { - n->session = NULL; - spa_list_remove(&n->session_link); - } - - spa_list_remove(&sess->l); - free(sess); -} - -static void node_proxy_destroy(void *data) -{ - struct node *n = data; - struct impl *impl = n->obj.impl; - struct port *p, *t; - - pw_log_debug(NAME " %p: proxy destroy node %d", impl, n->obj.id); - - spa_list_remove(&n->l); - - spa_list_for_each_safe(p, t, &n->port_list, l) { - spa_list_remove(&p->l); - p->node = NULL; - } - if (n->info) - pw_node_info_free(n->info); - free(n->media); - if (n->session) { - spa_list_remove(&n->session_link); - n->session = NULL; - } - if (n->manager) { - switch (n->type) { - case NODE_TYPE_DEVICE: - remove_session(impl, n->manager); - n->manager = NULL; - break; - } - } -} - -static const struct pw_proxy_events node_proxy_events = { - PW_VERSION_PROXY_EVENTS, - .destroy = node_proxy_destroy, -}; - -static int -handle_node(struct impl *impl, uint32_t id, - uint32_t type, const struct spa_dict *props) -{ - const char *str, *media_class; - bool need_dsp = false; - enum pw_direction direction; - struct pw_proxy *p; - struct node *node; - uint32_t client_id = SPA_ID_INVALID; - - if (props && (str = spa_dict_lookup(props, PW_KEY_CLIENT_ID)) != NULL) - client_id = atoi(str); - - p = pw_registry_proxy_bind(impl->registry_proxy, - id, type, PW_VERSION_NODE_PROXY, - sizeof(struct node)); - - node = pw_proxy_get_user_data(p); - node->obj.impl = impl; - node->obj.id = id; - node->obj.type = type; - node->obj.proxy = p; - node->client_id = client_id; - spa_list_init(&node->port_list); - pw_proxy_add_listener(p, &node->obj.listener, &node_proxy_events, node); - pw_proxy_add_object_listener(p, &node->listener, &node_events, node); - add_object(impl, &node->obj); - spa_list_append(&impl->node_list, &node->l); - node->type = NODE_TYPE_UNKNOWN; - - media_class = props ? spa_dict_lookup(props, PW_KEY_MEDIA_CLASS) : NULL; - - pw_log_debug(NAME" %p: node "PW_KEY_MEDIA_CLASS" %s", impl, media_class); - - if (media_class == NULL) - return 0; - - if (strstr(media_class, "Stream/") == media_class) { - media_class += strlen("Stream/"); - - if (strstr(media_class, "Output/") == media_class) { - direction = PW_DIRECTION_OUTPUT; - media_class += strlen("Output/"); - } - else if (strstr(media_class, "Input/") == media_class) { - direction = PW_DIRECTION_INPUT; - media_class += strlen("Input/"); - } - else - return 0; - - node->direction = direction; - node->type = NODE_TYPE_STREAM; - node->media = strdup(media_class); - pw_log_debug(NAME "%p: node %d is stream %s", impl, id, node->media); - - } - else { - struct session *sess; - - if (strstr(media_class, "Audio/") == media_class) { - need_dsp = true; - media_class += strlen("Audio/"); - } - else if (strstr(media_class, "Video/") == media_class) { - media_class += strlen("Video/"); - } - else - return 0; - - if (strcmp(media_class, "Sink") == 0) - direction = PW_DIRECTION_OUTPUT; - else if (strcmp(media_class, "Source") == 0) - direction = PW_DIRECTION_INPUT; - else - return 0; - - sess = calloc(1, sizeof(struct session)); - sess->impl = impl; - sess->direction = direction; - sess->id = id; - sess->need_dsp = need_dsp; - sess->enabled = false; - sess->starting = need_dsp; - sess->node = node; - if ((str = spa_dict_lookup(props, PW_KEY_NODE_PLUGGED)) != NULL) - sess->plugged = pw_properties_parse_uint64(str); - else - sess->plugged = SPA_TIMESPEC_TO_NSEC(&impl->now); - - if ((str = spa_dict_lookup(props, PW_KEY_PRIORITY_SESSION)) != NULL) - sess->priority = pw_properties_parse_int(str); - else - sess->priority = 0; - - spa_list_init(&sess->node_list); - spa_list_append(&impl->session_list, &sess->l); - - node->direction = direction; - node->type = NODE_TYPE_DEVICE; - node->manager = sess; - - pw_log_debug(NAME" %p: new session for device node %d %d prio:%d", impl, id, - need_dsp, sess->priority); - } - pw_node_proxy_enum_params((struct pw_node_proxy*)p, - 0, SPA_PARAM_EnumFormat, - 0, -1, NULL); - return 1; -} - -static void port_event_info(void *object, const struct pw_port_info *info) -{ - struct port *p = object; - pw_log_debug(NAME" %p: info for port %d", p->obj.impl, p->obj.id); - p->info = pw_port_info_update(p->info, info); -} - -static void port_event_param(void *object, int seq, - uint32_t id, uint32_t index, uint32_t next, - const struct spa_pod *param) -{ - struct port *p = object; - struct node *node = p->node; - struct spa_audio_info_raw info = { 0, }; - - pw_log_debug(NAME" %p: param for port %d", p->obj.impl, p->obj.id); - - if (node == NULL) - return; - - if (id != SPA_PARAM_EnumFormat) - return; - - if (node->manager) - node->manager->enabled = true; - - if (spa_format_parse(param, &node->media_type, &node->media_subtype) < 0) - return; - - if (node->media_type != SPA_MEDIA_TYPE_audio || - node->media_subtype != SPA_MEDIA_SUBTYPE_raw) - return; - - spa_pod_fixate((struct spa_pod*)param); - - if (spa_format_audio_raw_parse(param, &info) < 0) - return; - - if (info.channels > node->format.channels) - node->format = info; -} - -static const struct pw_port_proxy_events port_events = { - PW_VERSION_PORT_PROXY_EVENTS, - .info = port_event_info, - .param = port_event_param, -}; - -static void port_proxy_destroy(void *data) -{ - struct port *p = data; - - pw_log_debug(NAME " %p: proxy destroy port %d", p->obj.impl, p->obj.id); - - if (p->node) { - spa_list_remove(&p->l); - p->node = NULL; - } - if (p->info) - pw_port_info_free(p->info); -} - -static const struct pw_proxy_events port_proxy_events = { - PW_VERSION_PROXY_EVENTS, - .destroy = port_proxy_destroy, -}; - -static int -handle_port(struct impl *impl, uint32_t id, uint32_t type, - const struct spa_dict *props) -{ - struct port *port; - struct pw_proxy *p; - struct node *node; - const char *str; - uint32_t node_id; - - if (props == NULL || (str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL) - return -EINVAL; - - node_id = atoi(str); - - if ((node = find_object(impl, node_id)) == NULL) - return -ESRCH; - - if (props == NULL || (str = spa_dict_lookup(props, PW_KEY_PORT_DIRECTION)) == NULL) - return -EINVAL; - - p = pw_registry_proxy_bind(impl->registry_proxy, - id, type, PW_VERSION_PORT_PROXY, - sizeof(struct port)); - - port = pw_proxy_get_user_data(p); - port->obj.impl = impl; - port->obj.id = id; - port->obj.type = type; - port->obj.proxy = p; - port->node = node; - port->direction = strcmp(str, "out") ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT; - - if (props != NULL && (str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) != NULL) - port->flags |= PORT_FLAG_DSP; - - pw_proxy_add_listener(p, &port->obj.listener, &port_proxy_events, port); - pw_proxy_add_object_listener(p, &port->listener, &port_events, port); - add_object(impl, &port->obj); - - spa_list_append(&node->port_list, &port->l); - - pw_log_debug(NAME" %p: new port %d for node %d type %d %08x", impl, id, node_id, - node->type, port->flags); - - if (node->type == NODE_TYPE_DEVICE) { - pw_port_proxy_enum_params((struct pw_port_proxy*)p, - 0, SPA_PARAM_EnumFormat, - 0, -1, NULL); - } - - - return 0; -} - -static void client_event_info(void *object, const struct pw_client_info *info) -{ - struct client *c = object; - uint32_t i; - - pw_log_debug(NAME" %p: info for client %d", c->obj.impl, c->obj.id); - c->info = pw_client_info_update(c->info, info); - for (i = 0; i < info->props->n_items; i++) - pw_log_debug(NAME" %p: %s = %s", c, - info->props->items[i].key, - info->props->items[i].value); -} - -static const struct pw_client_proxy_events client_events = { - PW_VERSION_CLIENT_PROXY_EVENTS, - .info = client_event_info, -}; - -static void client_proxy_destroy(void *data) -{ - struct client *c = data; - - pw_log_debug(NAME " %p: proxy destroy client %d", c->obj.impl, c->obj.id); - - spa_list_remove(&c->l); - if (c->info) - pw_client_info_free(c->info); -} - -static const struct pw_proxy_events client_proxy_events = { - PW_VERSION_PROXY_EVENTS, - .destroy = client_proxy_destroy, -}; - -static int -handle_client(struct impl *impl, uint32_t id, - uint32_t type, const struct spa_dict *props) -{ - struct pw_proxy *p; - struct client *client; - struct pw_permission perms[2]; - const char *str; - - p = pw_registry_proxy_bind(impl->registry_proxy, - id, type, PW_VERSION_CLIENT_PROXY, - sizeof(struct client)); - - client = pw_proxy_get_user_data(p); - client->obj.impl = impl; - client->obj.id = id; - client->obj.type = type; - client->obj.proxy = p; - - pw_proxy_add_listener(p, &client->obj.listener, &client_proxy_events, client); - pw_proxy_add_object_listener(p, &client->listener, &client_events, client); - add_object(impl, &client->obj); - spa_list_append(&impl->client_list, &client->l); - - if (props == NULL) - return 0; - - str = spa_dict_lookup(props, PW_KEY_ACCESS); - if (str == NULL) - return 0; - - if (strcmp(str, "restricted") == 0) { - perms[0] = PW_PERMISSION_INIT(-1, PW_PERM_RWX); - pw_client_proxy_update_permissions((struct pw_client_proxy*)p, - 1, perms); - } - return 0; -} - -static void -registry_global(void *data,uint32_t id, - uint32_t permissions, uint32_t type, uint32_t version, - const struct spa_dict *props) -{ - struct impl *impl = data; - int res; - - pw_log_debug(NAME " %p: new global '%d' %d", impl, id, type); - - switch (type) { - case PW_TYPE_INTERFACE_Client: - res = handle_client(impl, id, type, props); - break; - - case PW_TYPE_INTERFACE_Node: - res = handle_node(impl, id, type, props); - break; - - case PW_TYPE_INTERFACE_Port: - res = handle_port(impl, id, type, props); - break; - - default: - res = 0; - break; - } - if (res < 0) { - pw_log_warn(NAME" %p: can't handle global %d", impl, id); - } - else - schedule_rescan(impl); -} - -static void -registry_global_remove(void *data, uint32_t id) -{ - struct impl *impl = data; - struct object *obj; - - pw_log_debug(NAME " %p: remove global '%d'", impl, id); - - if ((obj = find_object(impl, id)) == NULL) - return; - - switch (obj->type) { - case PW_TYPE_INTERFACE_Node: - { - struct node *node = (struct node*) obj, *n; - if (node->manager) - remove_session(impl, node->manager); - node->manager = NULL; - - spa_list_for_each(n, &impl->node_list, l) { - if (n->peer == node) - n->peer = NULL; - } - break; - } - default: - break; - } - remove_object(impl, obj); - schedule_rescan(impl); -} - -static const struct pw_registry_proxy_events registry_events = { - PW_VERSION_REGISTRY_PROXY_EVENTS, - .global = registry_global, - .global_remove = registry_global_remove, -}; - - -struct find_data { - struct impl *impl; - uint32_t path_id; - const char *media_class; - struct session *sess; - bool exclusive; - int priority; - uint64_t plugged; -}; - -static int find_session(void *data, struct session *sess) -{ - struct find_data *find = data; - struct impl *impl = find->impl; - const struct spa_dict *props; - const char *str; - int priority = 0; - uint64_t plugged = 0; - - pw_log_debug(NAME " %p: looking at session '%d' enabled:%d busy:%d exclusive:%d", - impl, sess->id, sess->enabled, sess->busy, sess->exclusive); - - if (!sess->enabled) - return 0; - - if (find->path_id != SPA_ID_INVALID && sess->id != find->path_id) - return 0; - - if (find->path_id == SPA_ID_INVALID) { - if ((props = sess->node->info->props) == NULL) - return 0; - - if ((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL) - return 0; - - if (strcmp(str, find->media_class) != 0) - return 0; - - plugged = sess->plugged; - priority = sess->priority; - } - - if ((find->exclusive && sess->busy) || sess->exclusive) { - pw_log_debug(NAME " %p: session '%d' in use", impl, sess->id); - return 0; - } - - pw_log_debug(NAME " %p: found session '%d' %"PRIu64" prio:%d", impl, - sess->id, plugged, priority); - - if (find->sess == NULL || - priority > find->priority || - (priority == find->priority && plugged > find->plugged)) { - pw_log_debug(NAME " %p: new best %d %" PRIu64, impl, priority, plugged); - find->sess = sess; - find->priority = priority; - find->plugged = plugged; - } - return 0; -} - -static int link_nodes(struct node *peer, enum pw_direction direction, struct node *node, int max) -{ - struct impl *impl = peer->obj.impl; - struct port *p; - - pw_log_debug(NAME " %p: link nodes %d %d %d", impl, max, node->obj.id, peer->obj.id); - - spa_list_for_each(p, &peer->port_list, l) { - struct pw_properties *props; - - pw_log_debug(NAME " %p: port %p: %d %d", impl, p, p->direction, p->flags); - - if (p->direction == direction) - continue; - if (p->flags & PORT_FLAG_SKIP) - continue; - - if (max-- == 0) - return 0; - - props = pw_properties_new(NULL, NULL); - if (p->direction == PW_DIRECTION_OUTPUT) { - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", node->obj.id); - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", -1); - pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", peer->obj.id); - pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", p->obj.id); - pw_log_debug(NAME " %p: node %d -> port %d:%d", impl, - node->obj.id, peer->obj.id, p->obj.id); - - } - else { - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", peer->obj.id); - pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", p->obj.id); - pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", node->obj.id); - pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", -1); - pw_log_debug(NAME " %p: port %d:%d -> node %d", impl, - peer->obj.id, p->obj.id, node->obj.id); - } - - pw_core_proxy_create_object(impl->core_proxy, - "link-factory", - PW_TYPE_INTERFACE_Link, - PW_VERSION_LINK_PROXY, - &props->dict, - 0); - - pw_properties_free(props); - } - node->peer = peer; - - return 0; -} - -static void stream_set_volume(struct impl *impl, struct node *node, float volume, bool mute) -{ - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - - pw_log_debug(NAME " %p: node %d set volume:%f mute:%d", impl, node->obj.id, volume, mute); - - pw_node_proxy_set_param((struct pw_node_proxy*)node->obj.proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, SPA_POD_Float(volume), - SPA_PROP_mute, SPA_POD_Bool(mute))); -} - -static int rescan_node(struct impl *impl, struct node *node) -{ - struct spa_dict *props; - const char *str, *media, *category, *role; - bool exclusive; - struct find_data find; - struct session *session; - struct pw_node_info *info; - struct node *peer; - enum pw_direction direction; - struct spa_pod_builder b = { 0, }; - struct spa_audio_info_raw audio_info = { 0, }; - struct spa_pod *param; - char buf[1024]; - int n_links = 0; - - if (node->type == NODE_TYPE_DEVICE) - return 0; - - if (node->session != NULL || node->peer != NULL) - return 0; - - if (node->info == NULL || node->info->props == NULL) { - pw_log_debug(NAME " %p: node %d has no properties", impl, node->obj.id); - return 0; - } - - info = node->info; - props = info->props; - - str = spa_dict_lookup(props, PW_KEY_NODE_AUTOCONNECT); - if (str == NULL || !pw_properties_parse_bool(str)) { - pw_log_debug(NAME" %p: node %d does not need autoconnect", impl, node->obj.id); - return 0; - } - - if ((media = spa_dict_lookup(props, PW_KEY_MEDIA_TYPE)) == NULL) - media = node->media; - if (media == NULL) { - pw_log_debug(NAME" %p: node %d has unknown media", impl, node->obj.id); - return 0; - } - - spa_zero(find); - - if ((category = spa_dict_lookup(props, PW_KEY_MEDIA_CATEGORY)) == NULL) { - pw_log_debug(NAME" %p: node %d find category from ports: %d %d", - impl, node->obj.id, info->n_input_ports, info->n_output_ports); - if (node->direction == PW_DIRECTION_INPUT || - (info->n_input_ports > 0 && info->n_output_ports == 0)) - category = "Capture"; - else if (node->direction == PW_DIRECTION_OUTPUT || - (info->n_output_ports > 0 && info->n_input_ports == 0)) - category = "Playback"; - else if (info->n_output_ports > 0 && info->n_input_ports > 0) - category = "Duplex"; - else { - pw_log_warn(NAME" %p: node %d can't determine category", - impl, node->obj.id); - return -EINVAL; - } - } - - if ((role = spa_dict_lookup(props, PW_KEY_MEDIA_ROLE)) == NULL) { - if (strcmp(media, "Audio") == 0) { - if (strcmp(category, "Duplex") == 0) - role = "Communication"; - else if (strcmp(category, "Capture") == 0) - role = "Production"; - else - role = "Music"; - } - else if (strcmp(media, "Video") == 0) { - if (strcmp(category, "Duplex") == 0) - role = "Communication"; - else if (strcmp(category, "Capture") == 0) - role = "Camera"; - else - role = "Video"; - } - } - - if ((str = spa_dict_lookup(props, PW_KEY_NODE_EXCLUSIVE)) != NULL) - exclusive = pw_properties_parse_bool(str); - else - exclusive = false; - - if (strcmp(media, "Audio") == 0) { - if (strcmp(category, "Playback") == 0) - find.media_class = "Audio/Sink"; - else if (strcmp(category, "Capture") == 0) - find.media_class = "Audio/Source"; - else { - pw_log_debug(NAME" %p: node %d unhandled category %s", - impl, node->obj.id, category); - return -EINVAL; - } - } - else if (strcmp(media, "Video") == 0) { - if (strcmp(category, "Capture") == 0) - find.media_class = "Video/Source"; - else { - pw_log_debug(NAME" %p: node %d unhandled category %s", - impl, node->obj.id, category); - return -EINVAL; - } - } - else { - pw_log_debug(NAME" %p: node %d unhandled media %s", - impl, node->obj.id, media); - return -EINVAL; - } - - if (strcmp(category, "Capture") == 0) - direction = PW_DIRECTION_OUTPUT; - else if (strcmp(category, "Playback") == 0) - direction = PW_DIRECTION_INPUT; - else { - pw_log_debug(NAME" %p: node %d unhandled category %s", - impl, node->obj.id, category); - return -EINVAL; - } - - str = spa_dict_lookup(props, PW_KEY_NODE_TARGET); - if (str != NULL) - find.path_id = atoi(str); - else - find.path_id = SPA_ID_INVALID; - - pw_log_info(NAME " %p: '%s' '%s' '%s' exclusive:%d target %d", impl, - media, category, role, exclusive, find.path_id); - - find.impl = impl; - find.exclusive = exclusive; - - spa_list_for_each(session, &impl->session_list, l) - find_session(&find, session); - - if (find.sess == NULL && find.path_id != SPA_ID_INVALID) { - pw_log_debug(NAME " %p: no session found for %d, try node", impl, node->obj.id); - - n_links = 1; - if ((peer = find_object(impl, find.path_id)) != NULL) { - if (peer->obj.type == PW_TYPE_INTERFACE_Node) { - if (peer->media_type == SPA_MEDIA_TYPE_audio) - goto do_link_profile; - else - goto do_link; - } - } - else { - str = spa_dict_lookup(props, PW_KEY_NODE_DONT_RECONNECT); - if (str != NULL && pw_properties_parse_bool(str)) { - pw_registry_proxy_destroy(impl->registry_proxy, node->obj.id); - return -ENOENT; - } - } - } - - if (find.sess == NULL) { - struct client *client; - - pw_log_warn(NAME " %p: no session found for %d", impl, node->obj.id); - - client = find_object(impl, node->client_id); - if (client && client->obj.type == PW_TYPE_INTERFACE_Client) { - pw_client_proxy_error((struct pw_client_proxy*)client->obj.proxy, - node->obj.id, -ENOENT, "no session available"); - } - return -ENOENT; - } - - session = find.sess; - - if (session->starting) { - pw_log_info(NAME " %p: session %d is starting", impl, session->id); - return 0; - } - - if (exclusive && session->busy) { - pw_log_warn(NAME" %p: session %d busy, can't get exclusive access", impl, session->id); - return -EBUSY; - } - peer = session->node; - session->exclusive = exclusive; - - pw_log_debug(NAME" %p: linking to session '%d'", impl, session->id); - - session->busy = true; - node->session = session; - spa_list_append(&session->node_list, &node->session_link); - - if (!exclusive && peer->media_type == SPA_MEDIA_TYPE_audio) { -do_link_profile: - audio_info = peer->format; - - if (direction == PW_DIRECTION_INPUT) - audio_info.channels = SPA_MIN(audio_info.channels, node->format.channels); - else - audio_info.channels = SPA_MAX(audio_info.channels, node->format.channels); - - pw_log_debug(NAME" %p: channels: %d -> %d", impl, - node->format.channels, audio_info.channels); - - audio_info.rate = DEFAULT_SAMPLERATE; - - spa_pod_builder_init(&b, buf, sizeof(buf)); - param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &audio_info); - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(pw_direction_reverse(direction)), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), - SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); - - if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) - spa_debug_pod(2, NULL, param); - - pw_node_proxy_set_param((struct pw_node_proxy*)node->obj.proxy, - SPA_PARAM_PortConfig, 0, param); - - stream_set_volume(impl, node, 1.0, false); - n_links = audio_info.channels; - } else { - n_links = audio_info.channels = 1; - } -do_link: - link_nodes(peer, direction, node, n_links); - - return 1; -} - -static void rescan_session(struct impl *impl, struct session *sess) -{ - struct node *node = sess->node; - struct spa_audio_info_raw info = { 0, }; - uint8_t buf[1024]; - struct spa_pod_builder b = { 0, }; - struct spa_pod *param; - - if (!sess->starting) - return; - - if (node->info->props == NULL) { - pw_log_debug(NAME " %p: node %p has no properties", impl, node); - return; - } - - if (node->media_type != SPA_MEDIA_TYPE_audio || - node->media_subtype != SPA_MEDIA_SUBTYPE_raw) { - pw_log_debug(NAME " %p: node %p has no media type", impl, node); - return; - } - - info = node->format; - info.rate = DEFAULT_SAMPLERATE; - - pw_log_debug(NAME" %p: setting profile for session %d %d", impl, sess->id, sess->direction); - - spa_pod_builder_init(&b, buf, sizeof(buf)); - param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info); - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(pw_direction_reverse(sess->direction)), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), - SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_Bool(true), - SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); - - pw_node_proxy_set_param((struct pw_node_proxy*)sess->node->obj.proxy, - SPA_PARAM_PortConfig, 0, param); - schedule_rescan(impl); - - sess->starting = false; -} - -static void do_rescan(struct impl *impl) -{ - struct session *sess; - struct node *node; - - clock_gettime(CLOCK_MONOTONIC, &impl->now); - pw_log_debug("media-session %p: do rescan", impl); - - spa_list_for_each(sess, &impl->session_list, l) - rescan_session(impl, sess); - spa_list_for_each(node, &impl->node_list, l) - rescan_node(impl, node); -} - -static void core_done(void *data, uint32_t id, int seq) -{ - struct impl *impl = data; - pw_log_debug("media-session %p: sync %u %d/%d", impl, id, seq, impl->seq); - if (impl->seq == seq) - do_rescan(impl); -} - -static const struct pw_core_proxy_events core_events = { - PW_VERSION_CORE_EVENTS, - .done = core_done -}; - -static void on_state_changed(void *_data, enum pw_remote_state old, enum pw_remote_state state, const char *error) -{ - struct impl *impl = _data; - - switch (state) { - case PW_REMOTE_STATE_ERROR: - pw_log_error(NAME" %p: remote error: %s", impl, error); - break; - - case PW_REMOTE_STATE_CONNECTED: - pw_log_info(NAME" %p: connected", impl); - impl->core_proxy = pw_remote_get_core_proxy(impl->remote); - pw_core_proxy_add_listener(impl->core_proxy, - &impl->core_listener, - &core_events, impl); - impl->registry_proxy = pw_core_proxy_get_registry(impl->core_proxy, - PW_VERSION_REGISTRY_PROXY, 0); - pw_registry_proxy_add_listener(impl->registry_proxy, - &impl->registry_listener, - ®istry_events, impl); - schedule_rescan(impl); - break; - - case PW_REMOTE_STATE_UNCONNECTED: - pw_log_info(NAME" %p: disconnected", impl); - impl->core_proxy = NULL; - impl->registry_proxy = NULL; - break; - - default: - printf("remote state: \"%s\"\n", pw_remote_state_as_string(state)); - break; - } -} - -static const struct pw_remote_events remote_events = { - PW_VERSION_REMOTE_EVENTS, - .state_changed = on_state_changed, -}; - -void *sm_policy_start(struct pw_remote *remote) -{ - struct impl *impl; - - impl = calloc(1, sizeof(struct impl)); - if (impl == NULL) - return NULL; - - impl->core = pw_remote_get_core(remote); - impl->remote = remote; - - pw_map_init(&impl->globals, 64, 64); - - spa_list_init(&impl->client_list); - spa_list_init(&impl->node_list); - spa_list_init(&impl->session_list); - - pw_remote_add_listener(impl->remote, &impl->remote_listener, &remote_events, impl); - - return impl; -} - -int sm_policy_stop(void *data) -{ - return 0; -} diff --git a/src/examples/media-session/stream-monitor.c b/src/examples/media-session/stream-monitor.c index 13d7e6bf..fefb1ca9 100644 --- a/src/examples/media-session/stream-monitor.c +++ b/src/examples/media-session/stream-monitor.c @@ -42,6 +42,7 @@ #include "media-session.h" #define NAME "stream-monitor" +#define SESSION_KEY "stream-monitor" #define DEFAULT_CHANNELS 2 #define DEFAULT_SAMPLERATE 48000 @@ -120,7 +121,13 @@ static int client_endpoint_set_session_id(void *object, uint32_t id) static int client_endpoint_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { - return -ENOTSUP; + struct client_endpoint *endpoint = object; + struct impl *impl = endpoint->impl; + struct node *node = endpoint->node; + + pw_log_debug(NAME " %p: node %d set param %d", impl, node->obj->obj.id, id); + return pw_node_proxy_set_param((struct pw_node_proxy*)node->obj->obj.proxy, + id, flags, param); } @@ -134,8 +141,6 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop { struct client_endpoint *endpoint = object; struct impl *impl = endpoint->impl; - const char *str; - struct sm_object *obj; struct node *node = endpoint->node; struct pw_properties *p; int res; @@ -152,22 +157,9 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->node->id); pw_properties_setf(p, PW_KEY_LINK_OUTPUT_PORT, "-1"); - str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_NODE); } else { pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->node->id); pw_properties_setf(p, PW_KEY_LINK_INPUT_PORT, "-1"); - str = spa_dict_lookup(props, PW_KEY_LINK_OUTPUT_NODE); - } - if (str == NULL) { - pw_log_warn(NAME" %p: no target endpoint given", impl); - res = -EINVAL; - goto exit; - } - obj = sm_media_session_find_object(impl->session, atoi(str)); - if (obj == NULL || obj->type != PW_TYPE_INTERFACE_Endpoint) { - pw_log_warn(NAME" %p: could not find object %s (%p)", impl, str, obj); - res = -EINVAL; - goto exit; } if (!endpoint->stream.active) { @@ -192,12 +184,29 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop pw_node_proxy_set_param((struct pw_node_proxy*)node->obj->obj.proxy, SPA_PARAM_PortConfig, 0, param); - endpoint->pending_config = pw_proxy_sync(node->obj->obj.proxy, 0); - endpoint->stream.active = true; } - pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)obj->proxy, &p->dict); + if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { + const char *str; + struct sm_object *obj; + + str = spa_dict_lookup(props, PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT); + if (str == NULL) { + pw_log_warn(NAME" %p: no target endpoint given", impl); + res = -EINVAL; + goto exit; + } + obj = sm_media_session_find_object(impl->session, atoi(str)); + if (obj == NULL || obj->type != PW_TYPE_INTERFACE_Endpoint) { + pw_log_warn(NAME" %p: could not find endpoint %s (%p)", impl, str, obj); + res = -EINVAL; + goto exit; + } + pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)obj->proxy, &p->dict); + } else { + sm_media_session_create_links(impl->session, &p->dict); + } res = 0; @@ -277,14 +286,14 @@ static struct client_endpoint *make_endpoint(struct node *node) if ((str = pw_properties_get(props, PW_KEY_MEDIA_CLASS)) != NULL) pw_properties_set(s->props, PW_KEY_MEDIA_CLASS, str); if (node->direction == PW_DIRECTION_OUTPUT) - pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Playback"); + pw_properties_set(s->props, PW_KEY_ENDPOINT_STREAM_NAME, "Playback"); else - pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Capture"); + pw_properties_set(s->props, PW_KEY_ENDPOINT_STREAM_NAME, "Capture"); s->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; s->info.id = 0; s->info.endpoint_id = endpoint->info.id; - s->info.name = (char*)pw_properties_get(s->props, PW_KEY_STREAM_NAME); + s->info.name = (char*)pw_properties_get(s->props, PW_KEY_ENDPOINT_STREAM_NAME); s->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS; s->info.props = &s->props->dict; @@ -349,46 +358,6 @@ static const struct pw_node_proxy_events node_events = { .param = node_event_param, }; -static void node_proxy_destroy(void *data) -{ - struct node *n = data; - struct impl *impl = n->impl; - - pw_log_debug(NAME " %p: proxy destroy node %d", impl, n->id); - - if (n->endpoint) - destroy_endpoint(n->endpoint); - free(n->media); -} - -static void node_proxy_done(void *data, int seq) -{ - struct node *n = data; - struct impl *impl = n->impl; - struct client_endpoint *endpoint = n->endpoint; - - if (endpoint == NULL) - return; - if (endpoint->pending_config != 0) { - pw_log_debug(NAME" %p: config complete", impl); - endpoint->pending_config = 0; - } -} - -static void node_proxy_error(void *data, int seq, int res, const char *message) -{ - struct node *n = data; - struct impl *impl = n->impl; - pw_log_error(NAME " %p: proxy seq:%d got error %d: %s", impl, seq, res, message); -} - -static const struct pw_proxy_events node_proxy_events = { - PW_VERSION_PROXY_EVENTS, - .destroy = node_proxy_destroy, - .done = node_proxy_done, - .error = node_proxy_error, -}; - static int handle_node(struct impl *impl, struct sm_object *obj) { @@ -396,7 +365,7 @@ handle_node(struct impl *impl, struct sm_object *obj) enum pw_direction direction; struct node *node; - if (sm_object_get_data(obj, "stream-monitor") != NULL) + if (sm_object_get_data(obj, SESSION_KEY) != NULL) return 0; media_class = obj->props ? pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS) : NULL; @@ -422,7 +391,7 @@ handle_node(struct impl *impl, struct sm_object *obj) else return 0; - node = sm_object_add_data(obj, "stream-monitor", sizeof(struct node)); + node = sm_object_add_data(obj, SESSION_KEY, sizeof(struct node)); node->obj = (struct sm_node*)obj; node->impl = impl; node->id = obj->id; @@ -430,7 +399,6 @@ handle_node(struct impl *impl, struct sm_object *obj) node->media = strdup(media_class); pw_log_debug(NAME "%p: node %d is stream %s", impl, node->id, node->media); - pw_proxy_add_listener(obj->proxy, &node->proxy_listener, &node_proxy_events, node); pw_proxy_add_object_listener(obj->proxy, &node->listener, &node_events, node); pw_node_proxy_enum_params((struct pw_node_proxy*)obj->proxy, @@ -439,73 +407,11 @@ handle_node(struct impl *impl, struct sm_object *obj) return 1; } -#if 0 -static void stream_set_volume(struct impl *impl, struct node *node, float volume, bool mute) -{ - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - - pw_log_debug(NAME " %p: node %d set volume:%f mute:%d", impl, node->obj.id, volume, mute); - - pw_node_proxy_set_param((struct pw_node_proxy*)node->obj.proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, SPA_POD_Float(volume), - SPA_PROP_mute, SPA_POD_Bool(mute))); -} - -static void rescan_session(struct impl *impl, struct session *sess) -{ - struct node *node = sess->node; - struct spa_audio_info_raw info = { 0, }; - uint8_t buf[1024]; - struct spa_pod_builder b = { 0, }; - struct spa_pod *param; - - if (!sess->starting) - return; - - if (node->info->props == NULL) { - pw_log_debug(NAME " %p: node %p has no properties", impl, node); - return; - } - - if (node->media_type != SPA_MEDIA_TYPE_audio || - node->media_subtype != SPA_MEDIA_SUBTYPE_raw) { - pw_log_debug(NAME " %p: node %p has no media type", impl, node); - return; - } - - info = node->format; - info.rate = DEFAULT_SAMPLERATE; - - pw_log_debug(NAME" %p: setting profile for session %d %d", impl, sess->id, sess->direction); - - spa_pod_builder_init(&b, buf, sizeof(buf)); - param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info); - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(pw_direction_reverse(sess->direction)), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), - SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_Bool(true), - SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); - - pw_node_proxy_set_param((struct pw_node_proxy*)sess->node->obj.proxy, - SPA_PARAM_PortConfig, 0, param); - schedule_rescan(impl); - - sess->starting = false; -} -#endif - static void session_update(void *data, struct sm_object *object) { struct impl *impl = data; int res; - pw_log_debug(NAME " %p: update object '%d' %d", impl, object->id, object->type); - switch (object->type) { case PW_TYPE_INTERFACE_Node: res = handle_node(impl, object); @@ -523,6 +429,20 @@ static void session_update(void *data, struct sm_object *object) static void session_remove(void *data, struct sm_object *object) { + switch (object->type) { + case PW_TYPE_INTERFACE_Node: + { + struct node *node; + if ((node = sm_object_get_data(object, SESSION_KEY)) != NULL) { + if (node->endpoint) + destroy_endpoint(node->endpoint); + free(node->media); + } + break; + } + default: + break; + } } static const struct sm_media_session_events session_events = { diff --git a/src/examples/meson.build b/src/examples/meson.build index f5b66452..8b045c93 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -48,7 +48,6 @@ executable('media-session', 'media-session/media-session.c', 'media-session/monitor.c', 'media-session/stream-monitor.c', - 'media-session/policy.c', 'media-session/policy-ep.c', c_args : [ '-D_GNU_SOURCE' ], install: false, diff --git a/src/extensions/session-manager/keys.h b/src/extensions/session-manager/keys.h index 19beb261..003d9574 100644 --- a/src/extensions/session-manager/keys.h +++ b/src/extensions/session-manager/keys.h @@ -39,9 +39,14 @@ extern "C" { #define PW_KEY_ENDPOINT_AUTOCONNECT "endpoint.autoconnect" /**< try to automatically connect this * endpoint. */ -#define PW_KEY_STREAM_ID "stream.id" /**< id of a stream */ -#define PW_KEY_STREAM_NAME "stream.name" /**< unique name of a stream */ -#define PW_KEY_STREAM_DESCRIPTION "stream.description" /**< description of a stream */ +#define PW_KEY_ENDPOINT_STREAM_ID "endpoint-stream.id" /**< id of a stream */ +#define PW_KEY_ENDPOINT_STREAM_NAME "endpoint-stream.name" /**< unique name of a stream */ +#define PW_KEY_ENDPOINT_STREAM_DESCRIPTION "endpoint-stream.description" /**< description of a stream */ + +#define PW_KEY_ENDPOINT_LINK_OUTPUT_ENDPOINT "endpoint-link.output.endpoint" /**< output endpoint of link */ +#define PW_KEY_ENDPOINT_LINK_OUTPUT_STREAM "endpoint-link.output.stream" /**< output stream of link */ +#define PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT "endpoint-link.input.endpoint" /**< input endpoint of link */ +#define PW_KEY_ENDPOINT_LINK_INPUT_STREAM "endpoint-link.inut.stream" /**< input stream of link */ #ifdef __cplusplus } diff --git a/src/modules/module-session-manager/client-endpoint.c b/src/modules/module-session-manager/client-endpoint.c index 713e776c..bb26c9dc 100644 --- a/src/modules/module-session-manager/client-endpoint.c +++ b/src/modules/module-session-manager/client-endpoint.c @@ -84,8 +84,8 @@ static int client_endpoint_stream_update(void *object, PW_KEY_CLIENT_ID, PW_KEY_ENDPOINT_ID, PW_KEY_PRIORITY_SESSION, - PW_KEY_STREAM_NAME, - PW_KEY_STREAM_DESCRIPTION, + PW_KEY_ENDPOINT_STREAM_NAME, + PW_KEY_ENDPOINT_STREAM_DESCRIPTION, NULL }; |