session-manager: implement 'endpoint' factory

This is modeled after the 'metadata' factory and provides
a way to use the pw_endpoint interface directly to implement
an endpoint object in a client.
This commit is contained in:
George Kiagiadakis 2020-03-24 17:17:24 +02:00 committed by Wim Taymans
parent 0a8ec0380b
commit df66edd9ab
3 changed files with 379 additions and 0 deletions

View file

@ -145,6 +145,7 @@ pipewire_module_session_manager = shared_library('pipewire-module-session-manage
'module-session-manager/client-session/client-session.c', 'module-session-manager/client-session/client-session.c',
'module-session-manager/client-session/endpoint-link.c', 'module-session-manager/client-session/endpoint-link.c',
'module-session-manager/client-session/session.c', 'module-session-manager/client-session/session.c',
'module-session-manager/endpoint.c',
'module-session-manager/protocol-native.c', 'module-session-manager/protocol-native.c',
'module-session-manager/proxy-session-manager.c', 'module-session-manager/proxy-session-manager.c',
], ],

View file

@ -31,6 +31,9 @@
int client_endpoint_factory_init(struct pw_impl_module *module); int client_endpoint_factory_init(struct pw_impl_module *module);
/* client-session.c */ /* client-session.c */
int client_session_factory_init(struct pw_impl_module *module); int client_session_factory_init(struct pw_impl_module *module);
int endpoint_factory_init(struct pw_impl_module *module);
/* protocol-native.c */ /* protocol-native.c */
int pw_protocol_native_ext_session_manager_init(struct pw_context *context); int pw_protocol_native_ext_session_manager_init(struct pw_context *context);
@ -51,6 +54,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
client_endpoint_factory_init(module); client_endpoint_factory_init(module);
client_session_factory_init(module); client_session_factory_init(module);
endpoint_factory_init(module);
pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));

View file

@ -0,0 +1,374 @@
/* PipeWire
*
* Copyright © 2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* 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 <pipewire/impl.h>
#include <extensions/session-manager.h>
#include <spa/utils/result.h>
#define NAME "endpoint"
struct pw_proxy *pw_core_endpoint_export(struct pw_core *core,
const char *type, const struct spa_dict *props, void *object,
size_t user_data_size);
struct impl
{
struct pw_global *global;
struct spa_hook global_listener;
union {
struct pw_endpoint *endpoint;
struct pw_resource *resource;
};
struct spa_hook resource_listener;
};
struct resource_data
{
struct impl *impl;
struct pw_resource *resource;
struct spa_hook resource_listener;
struct spa_hook object_listener;
struct spa_hook endpoint_listener;
};
struct factory_data
{
struct pw_impl_factory *this;
struct pw_impl_module *module;
struct spa_hook module_listener;
struct pw_export_type export;
};
static int method_subscribe_params(void *object, uint32_t *ids, uint32_t n_ids)
{
struct resource_data *d = object;
struct impl *impl = d->impl;
pw_endpoint_subscribe_params(impl->endpoint, ids, n_ids);
return 0;
}
static int method_enum_params(void *object, int seq,
uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter)
{
struct resource_data *d = object;
struct impl *impl = d->impl;
pw_endpoint_enum_params(impl->endpoint, seq, id, start, num, filter);
return 0;
}
static int method_set_param(void *object, uint32_t id, uint32_t flags,
const struct spa_pod *param)
{
struct resource_data *d = object;
struct impl *impl = d->impl;
pw_endpoint_set_param(impl->endpoint, id, flags, param);
return 0;
}
static int method_create_link(void *object, const struct spa_dict *props)
{
struct resource_data *d = object;
struct impl *impl = d->impl;
pw_endpoint_create_link(impl->endpoint, props);
return 0;
}
static const struct pw_endpoint_methods endpoint_methods = {
PW_VERSION_ENDPOINT_METHODS,
.subscribe_params = method_subscribe_params,
.enum_params = method_enum_params,
.set_param = method_set_param,
.create_link = method_create_link,
};
#define pw_endpoint_resource(r,m,v,...) \
pw_resource_call(r,struct pw_endpoint_events,m,v,__VA_ARGS__)
#define pw_endpoint_resource_info(r,...) \
pw_endpoint_resource(r,info,0,__VA_ARGS__)
#define pw_endpoint_resource_param(r,...) \
pw_endpoint_resource(r,param,0,__VA_ARGS__)
static void event_info(void *object, const struct pw_endpoint_info *info)
{
struct resource_data *d = object;
pw_endpoint_resource_info(d->resource, info);
}
static void event_param(void *object, int seq,
uint32_t id, uint32_t index, uint32_t next,
const struct spa_pod *param)
{
struct resource_data *d = object;
pw_endpoint_resource_param (d->resource, seq, id, index, next, param);
}
static const struct pw_endpoint_events endpoint_events = {
PW_VERSION_ENDPOINT_EVENTS,
.info = event_info,
.param = event_param,
};
static void global_unbind(void *data)
{
struct resource_data *d = data;
if (d->resource)
spa_hook_remove(&d->endpoint_listener);
}
static const struct pw_resource_events resource_events = {
PW_VERSION_RESOURCE_EVENTS,
.destroy = global_unbind,
};
static int global_bind(void *_data, struct pw_impl_client *client,
uint32_t permissions, uint32_t version, uint32_t id)
{
struct impl *impl = _data;
struct pw_resource *resource;
struct resource_data *data;
resource = pw_resource_new(client, id, permissions,
PW_TYPE_INTERFACE_Endpoint,
version, sizeof(*data));
if (resource == NULL)
return -errno;
data = pw_resource_get_user_data(resource);
data->impl = impl;
data->resource = resource;
pw_global_add_resource(impl->global, resource);
/* listen for when the resource goes away */
pw_resource_add_listener(resource,
&data->resource_listener,
&resource_events, data);
/* resource methods -> implemention */
pw_resource_add_object_listener(resource,
&data->object_listener,
&endpoint_methods, data);
/* implementation events -> resource */
pw_endpoint_add_listener(impl->endpoint,
&data->endpoint_listener,
&endpoint_events, data);
return 0;
}
static void global_destroy(void *data)
{
struct impl *impl = data;
spa_hook_remove(&impl->global_listener);
impl->global = NULL;
if (impl->resource)
pw_resource_destroy(impl->resource);
}
static const struct pw_global_events global_events = {
PW_VERSION_GLOBAL_EVENTS,
.destroy = global_destroy,
};
static void global_resource_destroy(void *data)
{
struct impl *impl = data;
spa_hook_remove(&impl->resource_listener);
impl->resource = NULL;
if (impl->global)
pw_global_destroy(impl->global);
}
static const struct pw_resource_events global_resource_events = {
PW_VERSION_RESOURCE_EVENTS,
.destroy = global_resource_destroy,
};
static void *endpoint_new(struct pw_context *context,
struct pw_resource *resource,
struct pw_properties *properties)
{
struct impl *impl;
impl = calloc(1, sizeof(*impl));
if (impl == NULL) {
pw_properties_free(properties);
return NULL;
}
impl->global = pw_global_new(context,
PW_TYPE_INTERFACE_Endpoint,
PW_VERSION_ENDPOINT,
properties,
global_bind, impl);
if (impl->global == NULL) {
free(impl);
return NULL;
}
impl->resource = resource;
pw_global_add_listener(impl->global,
&impl->global_listener,
&global_events, impl);
pw_resource_add_listener(resource,
&impl->resource_listener,
&global_resource_events, impl);
pw_resource_set_bound_id(resource, pw_global_get_id (impl->global));
pw_global_register(impl->global);
return impl;
}
static void *create_object(void *data,
struct pw_resource *resource,
const char *type,
uint32_t version,
struct pw_properties *properties,
uint32_t new_id)
{
struct factory_data *d = data;
struct pw_resource *impl_resource;
struct pw_impl_client *client = pw_resource_get_client(resource);
void *result;
int res;
impl_resource = pw_resource_new(client, new_id, PW_PERM_RWX, type, version, 0);
if (impl_resource == NULL) {
res = -errno;
goto error_resource;
}
pw_resource_install_marshal(impl_resource, true);
if (properties == NULL)
properties = pw_properties_new(NULL, NULL);
if (properties == NULL) {
res = -ENOMEM;
goto error_endpoint;
}
pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
pw_impl_client_get_info(client)->id);
pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d",
pw_impl_factory_get_info(d->this)->id);
result = endpoint_new(pw_impl_client_get_context(client), impl_resource, properties);
if (result == NULL) {
res = -errno;
goto error_endpoint;
}
return result;
error_resource:
pw_log_error("can't create resource: %s", spa_strerror(res));
pw_resource_errorf_id(resource, new_id, res, "can't create resource: %s", spa_strerror(res));
goto error_exit;
error_endpoint:
pw_log_error("can't create endpoint: %s", spa_strerror(res));
pw_resource_errorf_id(resource, new_id, res, "can't create endpoint: %s", spa_strerror(res));
goto error_exit_free;
error_exit_free:
pw_resource_remove(impl_resource);
error_exit:
errno = -res;
return NULL;
}
static const struct pw_impl_factory_implementation impl_factory = {
PW_VERSION_IMPL_FACTORY_IMPLEMENTATION,
.create_object = create_object,
};
static void module_destroy(void *data)
{
struct factory_data *d = data;
spa_hook_remove(&d->module_listener);
spa_list_remove(&d->export.link);
pw_impl_factory_destroy(d->this);
}
static void module_registered(void *data)
{
struct factory_data *d = data;
struct pw_impl_module *module = d->module;
struct pw_impl_factory *factory = d->this;
struct spa_dict_item items[1];
char id[16];
int res;
snprintf(id, sizeof(id), "%d", pw_impl_module_get_info(module)->id);
items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id);
pw_impl_factory_update_properties(factory, &SPA_DICT_INIT(items, 1));
if ((res = pw_impl_factory_register(factory, NULL)) < 0) {
pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res));
}
}
static const struct pw_impl_module_events module_events = {
PW_VERSION_IMPL_MODULE_EVENTS,
.destroy = module_destroy,
.registered = module_registered,
};
int endpoint_factory_init(struct pw_impl_module *module)
{
struct pw_context *context = pw_impl_module_get_context(module);
struct pw_impl_factory *factory;
struct factory_data *data;
factory = pw_context_create_factory(context,
"endpoint",
PW_TYPE_INTERFACE_Endpoint,
PW_VERSION_ENDPOINT,
NULL,
sizeof(*data));
if (factory == NULL)
return -errno;
data = pw_impl_factory_get_user_data(factory);
data->this = factory;
data->module = module;
pw_impl_factory_set_implementation(factory, &impl_factory, data);
data->export.type = PW_TYPE_INTERFACE_Endpoint;
data->export.func = pw_core_endpoint_export;
pw_context_register_export_type(context, &data->export);
pw_impl_module_add_listener(module, &data->module_listener, &module_events, data);
return 0;
}