mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
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:
parent
0a8ec0380b
commit
df66edd9ab
3 changed files with 379 additions and 0 deletions
|
|
@ -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/endpoint-link.c',
|
||||
'module-session-manager/client-session/session.c',
|
||||
'module-session-manager/endpoint.c',
|
||||
'module-session-manager/protocol-native.c',
|
||||
'module-session-manager/proxy-session-manager.c',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
int client_endpoint_factory_init(struct pw_impl_module *module);
|
||||
/* client-session.c */
|
||||
int client_session_factory_init(struct pw_impl_module *module);
|
||||
|
||||
int endpoint_factory_init(struct pw_impl_module *module);
|
||||
|
||||
/* protocol-native.c */
|
||||
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_session_factory_init(module);
|
||||
endpoint_factory_init(module);
|
||||
|
||||
pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
|
||||
|
||||
|
|
|
|||
374
src/modules/module-session-manager/endpoint.c
Normal file
374
src/modules/module-session-manager/endpoint.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue