mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05: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
				
			
		
							
								
								
									
										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