media-session: add media session helpers

Move all the media-session object monitoring into one place and
provide an API to get to the session objects.

Make API to add module specific info to objects.

Add methods to export and create objects in the session. This should
make it possible to link proxy to implementation and avoid a server
roundtrip in some cases.
This commit is contained in:
Wim Taymans 2019-11-14 18:35:29 +01:00
parent 3f3dfbc67e
commit 161cf46898
9 changed files with 1140 additions and 1054 deletions

View file

@ -40,6 +40,8 @@
#include "pipewire/private.h"
#include "extensions/session-manager.h"
#include "media-session.h"
#define NAME "policy-ep"
#define DEFAULT_CHANNELS 2
@ -47,72 +49,32 @@
#define DEFAULT_IDLE_SECONDS 3
struct impl;
struct impl {
struct timespec now;
struct sm_media_session *session;
struct spa_hook listener;
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 endpoint_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 session {
struct object obj;
struct spa_list l;
struct spa_hook listener;
struct pw_session_info *info;
};
struct endpoint {
struct object obj;
struct sm_endpoint *obj;
struct spa_list l;
uint32_t id;
struct impl *impl;
struct spa_hook listener;
struct pw_endpoint_info *info;
struct spa_list link; /**< link in impl endpoint_list */
enum pw_direction direction;
struct endpoint *peer;
struct session *session;
uint32_t client_id;
int32_t priority;
struct spa_list stream_list;
enum pw_direction direction;
#define ENDPOINT_TYPE_UNKNOWN 0
#define ENDPOINT_TYPE_STREAM 1
#define ENDPOINT_TYPE_DEVICE 2
@ -130,161 +92,46 @@ struct endpoint {
};
struct stream {
struct object obj;
struct sm_endpoint_stream *obj;
uint32_t id;
struct impl *impl;
struct spa_list l;
enum pw_direction direction;
struct pw_endpoint_stream_info *info;
struct endpoint *endpoint;
#define STREAM_FLAG_NONE 0
#define STREAM_FLAG_DSP (1<<0)
#define STREAM_FLAG_SKIP (1<<1)
uint32_t flags;
struct spa_hook listener;
};
struct link {
struct object obj;
struct stream *out;
struct stream *in;
};
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 endpoint_event_info(void *object, const struct pw_endpoint_info *update)
{
struct endpoint *e = object;
struct impl *impl = e->obj.impl;
struct pw_endpoint_info *info = e->info;
const char *str;
pw_log_debug(NAME" %p: info for endpoint %d type %d", impl, e->obj.id, e->type);
if (info == NULL && update) {
info = e->info = calloc(1, sizeof(*info));
info->id = update->id;
info->name = update->name ? strdup(update->name) : NULL;
info->media_class = update->media_class ? strdup(update->media_class) : NULL;
info->direction = update->direction;
info->flags = update->flags;
}
info->change_mask = update->change_mask;
if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) {
info->session_id = update->session_id;
e->session = find_object(impl, info->session_id);
}
if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
if (info->props)
pw_properties_free ((struct pw_properties *)info->props);
info->props = (struct spa_dict *) pw_properties_new_dict (update->props);
if ((str = spa_dict_lookup(info->props, PW_KEY_PRIORITY_SESSION)) != NULL)
e->priority = pw_properties_parse_int(str);
}
e->enabled = true;
}
static void endpoint_event_param(void *object, int seq,
uint32_t id, uint32_t index, uint32_t next,
const struct spa_pod *param)
{
struct endpoint *e = object;
struct impl *impl = e->obj.impl;
pw_log_debug(NAME" %p: param for endpoint %d, %d", impl, e->obj.id, id);
}
static const struct pw_endpoint_proxy_events endpoint_events = {
PW_VERSION_ENDPOINT_PROXY_EVENTS,
.info = endpoint_event_info,
.param = endpoint_event_param,
};
static void endpoint_proxy_destroy(void *data)
{
struct endpoint *e = data;
struct impl *impl = e->obj.impl;
struct stream *s, *t;
pw_log_debug(NAME " %p: proxy destroy endpoint %d", impl, e->obj.id);
spa_list_remove(&e->l);
spa_list_for_each_safe(s, t, &e->stream_list, l) {
spa_list_remove(&s->l);
s->endpoint = NULL;
}
free(e->media);
}
static const struct pw_proxy_events endpoint_proxy_events = {
PW_VERSION_PROXY_EVENTS,
.destroy = endpoint_proxy_destroy,
};
static int
handle_endpoint(struct impl *impl, uint32_t id,
uint32_t type, const struct spa_dict *props)
handle_endpoint(struct impl *impl, struct sm_object *object)
{
const char *str, *media_class;
enum pw_direction direction;
struct pw_proxy *p;
struct endpoint *ep;
uint32_t client_id = SPA_ID_INVALID;
if (props) {
if ((str = spa_dict_lookup(props, PW_KEY_CLIENT_ID)) != NULL)
if (sm_object_get_data(object, "policy-endpoint") != NULL)
return 0;
if (object->props) {
if ((str = pw_properties_get(object->props, PW_KEY_CLIENT_ID)) != NULL)
client_id = atoi(str);
}
p = pw_registry_proxy_bind(impl->registry_proxy,
id, type, PW_VERSION_ENDPOINT_PROXY,
sizeof(struct endpoint));
ep = pw_proxy_get_user_data(p);
ep->obj.impl = impl;
ep->obj.id = id;
ep->obj.type = type;
ep->obj.proxy = p;
ep->client_id = client_id;
spa_list_init(&ep->stream_list);
pw_proxy_add_listener(p, &ep->obj.listener, &endpoint_proxy_events, ep);
pw_proxy_add_object_listener(p, &ep->listener, &endpoint_events, ep);
add_object(impl, &ep->obj);
spa_list_append(&impl->endpoint_list, &ep->l);
ep->type = ENDPOINT_TYPE_UNKNOWN;
media_class = props ? spa_dict_lookup(props, PW_KEY_MEDIA_CLASS) : NULL;
media_class = object->props ? pw_properties_get(object->props, PW_KEY_MEDIA_CLASS) : NULL;
pw_log_debug(NAME" %p: endpoint "PW_KEY_MEDIA_CLASS" %s", impl, media_class);
if (media_class == NULL)
return 0;
ep = sm_object_add_data(object, "policy-endpoint", sizeof(struct endpoint));
ep->obj = (struct sm_endpoint*)object;
ep->id = object->id;
ep->impl = impl;
ep->client_id = client_id;
ep->type = ENDPOINT_TYPE_UNKNOWN;
ep->enabled = true;
spa_list_append(&impl->endpoint_list, &ep->link);
if (strstr(media_class, "Stream/") == media_class) {
media_class += strlen("Stream/");
@ -302,7 +149,7 @@ handle_endpoint(struct impl *impl, uint32_t id,
ep->direction = direction;
ep->type = ENDPOINT_TYPE_STREAM;
ep->media = strdup(media_class);
pw_log_debug(NAME "%p: endpoint %d is stream %s", impl, id, ep->media);
pw_log_debug(NAME "%p: endpoint %d is stream %s", impl, object->id, ep->media);
}
else {
if (strstr(media_class, "Audio/") == media_class) {
@ -324,267 +171,56 @@ handle_endpoint(struct impl *impl, uint32_t id,
ep->direction = direction;
ep->type = ENDPOINT_TYPE_DEVICE;
pw_log_debug(NAME" %p: endpoint %d prio:%d", impl, id, ep->priority);
pw_log_debug(NAME" %p: endpoint %d prio:%d", impl, object->id, ep->priority);
}
return 1;
}
static void stream_event_info(void *object, const struct pw_endpoint_stream_info *info)
{
struct stream *s = object;
pw_log_debug(NAME" %p: info for stream %d", s->obj.impl, s->obj.id);
}
static void stream_event_param(void *object, int seq,
uint32_t id, uint32_t index, uint32_t next,
const struct spa_pod *param)
{
struct stream *s = object;
struct endpoint *ep = s->endpoint;
struct spa_audio_info_raw info = { 0, };
pw_log_debug(NAME" %p: param for stream %d", s->obj.impl, s->obj.id);
if (ep == NULL)
return;
if (id != SPA_PARAM_EnumFormat)
return;
if (spa_format_parse(param, &ep->media_type, &ep->media_subtype) < 0)
return;
if (ep->media_type != SPA_MEDIA_TYPE_audio ||
ep->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 > ep->format.channels)
ep->format = info;
}
static const struct pw_endpoint_stream_proxy_events stream_events = {
PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS,
.info = stream_event_info,
.param = stream_event_param,
};
static void stream_proxy_destroy(void *data)
{
struct stream *s = data;
pw_log_debug(NAME " %p: proxy destroy stream %d", s->obj.impl, s->obj.id);
if (s->endpoint) {
spa_list_remove(&s->l);
s->endpoint = NULL;
}
}
static const struct pw_proxy_events stream_proxy_events = {
PW_VERSION_PROXY_EVENTS,
.destroy = stream_proxy_destroy,
};
static int
handle_stream(struct impl *impl, uint32_t id, uint32_t type,
const struct spa_dict *props)
handle_stream(struct impl *impl, struct sm_object *object)
{
struct sm_endpoint_stream *stream = (struct sm_endpoint_stream*)object;
struct stream *s;
struct pw_proxy *p;
struct endpoint *ep;
const char *str;
uint32_t endpoint_id;
if (props == NULL || (str = spa_dict_lookup(props, PW_KEY_ENDPOINT_ID)) == NULL)
return -EINVAL;
if (sm_object_get_data(object, "policy-endpoint") != NULL)
return 0;
endpoint_id = atoi(str);
if (stream->endpoint == NULL)
return 0;
if ((ep = find_object(impl, endpoint_id)) == NULL)
return -ESRCH;
ep = sm_object_get_data(&stream->endpoint->obj, "policy-endpoint");
if (ep == NULL)
return 0;
p = pw_registry_proxy_bind(impl->registry_proxy,
id, type, PW_VERSION_ENDPOINT_STREAM_PROXY,
sizeof(struct stream));
s = pw_proxy_get_user_data(p);
s->obj.impl = impl;
s->obj.id = id;
s->obj.type = type;
s->obj.proxy = p;
s = sm_object_add_data(object, "policy-endpoint", sizeof(struct stream));
s->obj = (struct sm_endpoint_stream*)object;
s->id = object->id;
s->impl = impl;
s->endpoint = ep;
s->direction = ep->direction;
pw_proxy_add_listener(p, &s->obj.listener, &stream_proxy_events, s);
pw_proxy_add_object_listener(p, &s->listener, &stream_events, s);
add_object(impl, &s->obj);
spa_list_append(&ep->stream_list, &s->l);
pw_log_debug(NAME" %p: new stream %d for endpoint %d type %d %08x", impl, id, endpoint_id,
ep->type, s->flags);
if (ep->type == ENDPOINT_TYPE_DEVICE) {
pw_endpoint_stream_proxy_enum_params((struct pw_endpoint_stream_proxy*)p,
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;
}
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 session_event_info(void *object, const struct pw_session_info *info)
{
struct session *c = object;
pw_log_debug(NAME" %p: info for session %d", c->obj.impl, c->obj.id);
}
static const struct pw_session_proxy_events session_events = {
PW_VERSION_SESSION_PROXY_EVENTS,
.info = session_event_info,
};
static void session_proxy_destroy(void *data)
{
struct session *c = data;
pw_log_debug(NAME " %p: proxy destroy session %d", c->obj.impl, c->obj.id);
spa_list_remove(&c->l);
}
static const struct pw_proxy_events session_proxy_events = {
PW_VERSION_PROXY_EVENTS,
.destroy = session_proxy_destroy,
};
static int
handle_session(struct impl *impl, uint32_t id,
uint32_t type, const struct spa_dict *props)
{
struct pw_proxy *p;
struct session *session;
p = pw_registry_proxy_bind(impl->registry_proxy,
id, type, PW_VERSION_SESSION_PROXY,
sizeof(struct session));
session = pw_proxy_get_user_data(p);
session->obj.impl = impl;
session->obj.id = id;
session->obj.type = type;
session->obj.proxy = p;
pw_proxy_add_listener(p, &session->obj.listener, &session_proxy_events, session);
pw_proxy_add_object_listener(p, &session->listener, &session_events, session);
add_object(impl, &session->obj);
spa_list_append(&impl->session_list, &session->l);
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)
static void session_update(void *data, struct sm_object *object)
{
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_Session:
res = handle_session(impl, id, type, props);
break;
pw_log_debug(NAME " %p: update global '%d'", impl, object->id);
switch (object->type) {
case PW_TYPE_INTERFACE_Endpoint:
res = handle_endpoint(impl, id, type, props);
res = handle_endpoint(impl, object);
break;
case PW_TYPE_INTERFACE_EndpointStream:
res = handle_stream(impl, id, type, props);
res = handle_stream(impl, object);
break;
default:
@ -592,34 +228,32 @@ registry_global(void *data,uint32_t id,
break;
}
if (res < 0) {
pw_log_warn(NAME" %p: can't handle global %d", impl, id);
pw_log_warn(NAME" %p: can't handle global %d", impl, object->id);
}
else
schedule_rescan(impl);
sm_media_session_schedule_rescan(impl->session);
}
static void
registry_global_remove(void *data, uint32_t id)
static void session_remove(void *data, struct sm_object *object)
{
struct impl *impl = data;
struct object *obj;
pw_log_debug(NAME " %p: remove global '%d'", impl, object->id);
pw_log_debug(NAME " %p: remove global '%d'", impl, id);
switch (object->type) {
case PW_TYPE_INTERFACE_Endpoint:
{
struct endpoint *ep;
if ((ep = sm_object_get_data(object, "policy-endpoint")) != NULL)
spa_list_remove(&ep->link);
break;
}
default:
break;
}
if ((obj = find_object(impl, id)) == NULL)
return;
remove_object(impl, obj);
schedule_rescan(impl);
sm_media_session_schedule_rescan(impl->session);
}
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;
@ -640,17 +274,17 @@ static int find_endpoint(void *data, struct endpoint *endpoint)
uint64_t plugged = 0;
pw_log_debug(NAME " %p: looking at endpoint '%d' enabled:%d busy:%d exclusive:%d",
impl, endpoint->obj.id, endpoint->enabled, endpoint->busy, endpoint->exclusive);
impl, endpoint->id, endpoint->enabled, endpoint->busy, endpoint->exclusive);
if (!endpoint->enabled)
return 0;
if (find->path_id != SPA_ID_INVALID && endpoint->obj.id != find->path_id)
if (find->path_id != SPA_ID_INVALID && endpoint->id != find->path_id)
return 0;
if (find->path_id == SPA_ID_INVALID) {
if (endpoint->info == NULL ||
(props = endpoint->info->props) == NULL)
if (endpoint->obj->info == NULL ||
(props = endpoint->obj->info->props) == NULL)
return 0;
if ((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL)
@ -664,12 +298,12 @@ static int find_endpoint(void *data, struct endpoint *endpoint)
}
if ((find->exclusive && endpoint->busy) || endpoint->exclusive) {
pw_log_debug(NAME " %p: endpoint '%d' in use", impl, endpoint->obj.id);
pw_log_debug(NAME " %p: endpoint '%d' in use", impl, endpoint->id);
return 0;
}
pw_log_debug(NAME " %p: found endpoint '%d' %"PRIu64" prio:%d", impl,
endpoint->obj.id, plugged, priority);
endpoint->id, plugged, priority);
if (find->endpoint == NULL ||
priority > find->priority ||
@ -684,53 +318,35 @@ static int find_endpoint(void *data, struct endpoint *endpoint)
static int link_endpoints(struct endpoint *endpoint, enum pw_direction direction, struct endpoint *peer, int max)
{
struct impl *impl = peer->obj.impl;
struct stream *s;
struct impl *impl = peer->impl;
struct pw_properties *props;
pw_log_debug(NAME " %p: link endpoints %d %d %d", impl, max, endpoint->obj.id, peer->obj.id);
pw_log_debug(NAME " %p: link endpoints %d %d %d", impl, max, endpoint->id, peer->id);
if (endpoint->session == NULL) {
pw_log_debug(NAME " %p: endpoint has no session", impl);
return -EINVAL;
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);
}
spa_list_for_each(s, &endpoint->stream_list, l) {
struct pw_properties *props;
pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)endpoint->obj->obj.proxy,
&props->dict);
pw_log_debug(NAME " %p: stream %p: %d %d", impl, s, s->direction, s->flags);
pw_properties_free(props);
if (s->direction == direction)
continue;
if (s->flags & STREAM_FLAG_SKIP)
continue;
if (max-- == 0)
return 0;
props = pw_properties_new(NULL, NULL);
if (s->direction == PW_DIRECTION_OUTPUT) {
pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->obj.id);
pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", s->obj.id);
pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", peer->obj.id);
pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", -1);
pw_log_debug(NAME " %p: stream %d:%d -> endpoint %d", impl,
endpoint->obj.id, s->obj.id, peer->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", -1);
pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->obj.id);
pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", s->obj.id);
pw_log_debug(NAME " %p: endpoint %d -> stream %d:%d", impl,
peer->obj.id, endpoint->obj.id, s->obj.id);
}
pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)endpoint->obj.proxy,
&props->dict);
pw_properties_free(props);
}
endpoint->peer = peer;
peer->peer = endpoint;
@ -750,27 +366,27 @@ static int rescan_endpoint(struct impl *impl, struct endpoint *ep)
if (ep->type == ENDPOINT_TYPE_DEVICE)
return 0;
if (ep->info == NULL || ep->info->props == NULL) {
pw_log_debug(NAME " %p: endpoint %d has no properties", impl, ep->obj.id);
if (ep->obj->info == NULL || ep->obj->info->props == NULL) {
pw_log_debug(NAME " %p: endpoint %d has no properties", impl, ep->id);
return 0;
}
if (ep->peer != NULL)
return 0;
info = ep->info;
info = ep->obj->info;
props = info->props;
str = spa_dict_lookup(props, PW_KEY_ENDPOINT_AUTOCONNECT);
if (str == NULL || !pw_properties_parse_bool(str)) {
pw_log_debug(NAME" %p: endpoint %d does not need autoconnect", impl, ep->obj.id);
pw_log_debug(NAME" %p: endpoint %d does not need autoconnect", impl, ep->id);
return 0;
}
if ((media = spa_dict_lookup(props, PW_KEY_MEDIA_TYPE)) == NULL)
media = ep->media;
if (media == NULL) {
pw_log_debug(NAME" %p: endpoint %d has unknown media", impl, ep->obj.id);
pw_log_debug(NAME" %p: endpoint %d has unknown media", impl, ep->id);
return 0;
}
@ -778,14 +394,14 @@ static int rescan_endpoint(struct impl *impl, struct endpoint *ep)
if ((category = spa_dict_lookup(props, PW_KEY_MEDIA_CATEGORY)) == NULL) {
pw_log_debug(NAME" %p: endpoint %d find category",
impl, ep->obj.id);
impl, ep->id);
if (ep->direction == PW_DIRECTION_INPUT) {
category = "Capture";
} else if (ep->direction == PW_DIRECTION_OUTPUT) {
category = "Playback";
} else {
pw_log_warn(NAME" %p: endpoint %d can't determine category",
impl, ep->obj.id);
impl, ep->id);
return -EINVAL;
}
}
@ -821,7 +437,7 @@ static int rescan_endpoint(struct impl *impl, struct endpoint *ep)
find.media_class = "Audio/Source";
else {
pw_log_debug(NAME" %p: endpoint %d unhandled category %s",
impl, ep->obj.id, category);
impl, ep->id, category);
return -EINVAL;
}
}
@ -830,13 +446,13 @@ static int rescan_endpoint(struct impl *impl, struct endpoint *ep)
find.media_class = "Video/Source";
else {
pw_log_debug(NAME" %p: endpoint %d unhandled category %s",
impl, ep->obj.id, category);
impl, ep->id, category);
return -EINVAL;
}
}
else {
pw_log_debug(NAME" %p: endpoint %d unhandled media %s",
impl, ep->obj.id, media);
impl, ep->id, media);
return -EINVAL;
}
@ -846,7 +462,7 @@ static int rescan_endpoint(struct impl *impl, struct endpoint *ep)
direction = PW_DIRECTION_INPUT;
else {
pw_log_debug(NAME" %p: endpoint %d unhandled category %s",
impl, ep->obj.id, category);
impl, ep->id, category);
return -EINVAL;
}
@ -862,46 +478,49 @@ static int rescan_endpoint(struct impl *impl, struct endpoint *ep)
find.impl = impl;
find.exclusive = exclusive;
spa_list_for_each(peer, &impl->endpoint_list, l)
spa_list_for_each(peer, &impl->endpoint_list, link)
find_endpoint(&find, peer);
if (find.endpoint == NULL && find.path_id != SPA_ID_INVALID) {
pw_log_debug(NAME " %p: no endpoint found for %d, try endpoint", impl, ep->obj.id);
struct sm_object *obj;
pw_log_debug(NAME " %p: no endpoint found for %d, try endpoint", impl, ep->id);
if ((peer = find_object(impl, find.path_id)) != NULL) {
if (peer->obj.type == PW_TYPE_INTERFACE_Endpoint)
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");
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, ep->obj.id);
// pw_registry_proxy_destroy(impl->registry_proxy, ep->id);
return -ENOENT;
}
}
}
if (find.endpoint == NULL) {
struct client *client;
struct sm_object *obj;
pw_log_warn(NAME " %p: no endpoint found for %d", impl, ep->obj.id);
pw_log_warn(NAME " %p: no endpoint found for %d", impl, ep->id);
client = find_object(impl, ep->client_id);
if (client && client->obj.type == PW_TYPE_INTERFACE_Client) {
pw_client_proxy_error((struct pw_client_proxy*)client->obj.proxy,
ep->obj.id, -ENOENT, "no endpoint available");
obj = sm_media_session_find_object(impl->session, ep->client_id);
if (obj && obj->type == PW_TYPE_INTERFACE_Client) {
pw_client_proxy_error((struct pw_client_proxy*)obj->proxy,
ep->id, -ENOENT, "no endpoint available");
}
return -ENOENT;
}
peer = find.endpoint;
if (exclusive && peer->busy) {
pw_log_warn(NAME" %p: endpoint %d busy, can't get exclusive access", impl, peer->obj.id);
pw_log_warn(NAME" %p: endpoint %d busy, can't get exclusive access", impl, peer->id);
return -EBUSY;
}
peer->exclusive = exclusive;
pw_log_debug(NAME" %p: linking to endpoint '%d'", impl, peer->obj.id);
pw_log_debug(NAME" %p: linking to endpoint '%d'", impl, peer->id);
peer->busy = true;
@ -911,71 +530,26 @@ do_link:
return 1;
}
static void do_rescan(struct impl *impl)
static void session_rescan(void *data, int seq)
{
struct impl *impl = data;
struct endpoint *ep;
clock_gettime(CLOCK_MONOTONIC, &impl->now);
pw_log_debug("media-session %p: do rescan", impl);
pw_log_debug(NAME" %p: rescan", impl);
spa_list_for_each(ep, &impl->endpoint_list, l)
spa_list_for_each(ep, &impl->endpoint_list, link)
rescan_endpoint(impl, ep);
}
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 const struct sm_media_session_events session_events = {
SM_VERSION_MEDIA_SESSION_EVENTS,
.update = session_update,
.remove = session_remove,
.rescan = session_rescan,
};
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,
&registry_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,
};
int sm_policy_ep_start(struct pw_remote *remote)
int sm_policy_ep_start(struct sm_media_session *session)
{
struct impl *impl;
@ -983,16 +557,12 @@ int sm_policy_ep_start(struct pw_remote *remote)
if (impl == NULL)
return -errno;
impl->core = pw_remote_get_core(remote);
impl->remote = remote;
impl->session = session;
impl->core = session->core;
pw_map_init(&impl->globals, 64, 64);
spa_list_init(&impl->client_list);
spa_list_init(&impl->session_list);
spa_list_init(&impl->endpoint_list);
pw_remote_add_listener(impl->remote, &impl->remote_listener, &remote_events, impl);
sm_media_session_add_listener(impl->session, &impl->listener, &session_events, impl);
return 0;
}