mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
client-device: add client device
Make it possible to implement a device out-of-process
This commit is contained in:
parent
504b2da72b
commit
67a1d317c4
7 changed files with 1292 additions and 0 deletions
|
|
@ -37,6 +37,18 @@ pipewire_module_client_node = shared_library('pipewire-module-client-node',
|
|||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_client_node = shared_library('pipewire-module-client-device',
|
||||
[ 'module-client-device.c',
|
||||
'module-client-device/resource-device.c',
|
||||
'module-client-device/proxy-device.c',
|
||||
'module-client-device/protocol-native.c', ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_link_factory = shared_library('pipewire-module-link-factory',
|
||||
[ 'module-link-factory.c' ],
|
||||
c_args : pipewire_module_c_args,
|
||||
|
|
|
|||
160
src/modules/module-client-device.c
Normal file
160
src/modules/module-client-device.c
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/* 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 <dlfcn.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "module-client-device/client-device.h"
|
||||
|
||||
static const struct spa_dict_item module_props[] = {
|
||||
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
|
||||
{ PW_KEY_MODULE_DESCRIPTION, "Allow clients to create and control remote devices" },
|
||||
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
|
||||
};
|
||||
|
||||
struct pw_proxy *pw_remote_spa_device_export(struct pw_remote *remote,
|
||||
uint32_t type, struct pw_properties *props, void *object);
|
||||
|
||||
struct pw_protocol *pw_protocol_native_ext_client_device_init(struct pw_core *core);
|
||||
|
||||
struct factory_data {
|
||||
struct pw_factory *this;
|
||||
struct pw_properties *properties;
|
||||
|
||||
struct pw_module *module;
|
||||
struct spa_hook module_listener;
|
||||
|
||||
struct pw_export_type export_spadevice;
|
||||
};
|
||||
|
||||
static void *create_object(void *_data,
|
||||
struct pw_resource *resource,
|
||||
uint32_t type,
|
||||
uint32_t version,
|
||||
struct pw_properties *properties,
|
||||
uint32_t new_id)
|
||||
{
|
||||
void *result;
|
||||
struct pw_resource *device_resource;
|
||||
struct pw_global *parent;
|
||||
struct pw_client *client = pw_resource_get_client(resource);
|
||||
|
||||
device_resource = pw_resource_new(client, new_id, PW_PERM_RWX, type, version, 0);
|
||||
if (device_resource == NULL)
|
||||
goto no_mem;
|
||||
|
||||
parent = pw_client_get_global(client);
|
||||
|
||||
result = pw_client_device_new(device_resource, parent, properties);
|
||||
if (result == NULL)
|
||||
goto no_mem;
|
||||
|
||||
return result;
|
||||
|
||||
no_mem:
|
||||
pw_log_error("can't create device");
|
||||
pw_resource_error(resource, -ENOMEM, "can't create device: no memory");
|
||||
goto done;
|
||||
done:
|
||||
if (properties)
|
||||
pw_properties_free(properties);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct pw_factory_implementation impl_factory = {
|
||||
PW_VERSION_FACTORY_IMPLEMENTATION,
|
||||
.create_object = create_object,
|
||||
};
|
||||
|
||||
static void module_destroy(void *data)
|
||||
{
|
||||
struct factory_data *d = data;
|
||||
|
||||
spa_hook_remove(&d->module_listener);
|
||||
|
||||
if (d->properties)
|
||||
pw_properties_free(d->properties);
|
||||
|
||||
spa_list_remove(&d->export_spadevice.link);
|
||||
|
||||
pw_factory_destroy(d->this);
|
||||
}
|
||||
|
||||
static const struct pw_module_events module_events = {
|
||||
PW_VERSION_MODULE_EVENTS,
|
||||
.destroy = module_destroy,
|
||||
};
|
||||
|
||||
static int module_init(struct pw_module *module, struct pw_properties *properties)
|
||||
{
|
||||
struct pw_core *core = pw_module_get_core(module);
|
||||
struct pw_factory *factory;
|
||||
struct factory_data *data;
|
||||
|
||||
factory = pw_factory_new(core,
|
||||
"client-device",
|
||||
SPA_TYPE_INTERFACE_Device,
|
||||
SPA_VERSION_DEVICE,
|
||||
NULL,
|
||||
sizeof(*data));
|
||||
if (factory == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
data = pw_factory_get_user_data(factory);
|
||||
data->this = factory;
|
||||
data->module = module;
|
||||
data->properties = properties;
|
||||
|
||||
pw_log_debug("module %p: new", module);
|
||||
|
||||
pw_factory_set_implementation(factory,
|
||||
&impl_factory,
|
||||
data);
|
||||
|
||||
pw_protocol_native_ext_client_device_init(core);
|
||||
|
||||
pw_factory_register(factory, NULL, pw_module_get_global(module), NULL);
|
||||
|
||||
data->export_spadevice.type = SPA_TYPE_INTERFACE_Device;
|
||||
data->export_spadevice.func = pw_remote_spa_device_export;
|
||||
pw_core_register_export_type(core, &data->export_spadevice);
|
||||
|
||||
pw_module_add_listener(module, &data->module_listener, &module_events, data);
|
||||
|
||||
pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPA_EXPORT
|
||||
int pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
return module_init(module, NULL);
|
||||
}
|
||||
520
src/modules/module-client-device/protocol-native.c
Normal file
520
src/modules/module-client-device/protocol-native.c
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
/* 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 <errno.h>
|
||||
|
||||
#include <spa/pod/parser.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include <extensions/protocol-native.h>
|
||||
|
||||
#if 0
|
||||
static void push_dict(struct spa_pod_builder *b, const struct spa_dict *dict)
|
||||
{
|
||||
uint32_t i, n_items;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
n_items = dict ? dict->n_items : 0;
|
||||
|
||||
spa_pod_builder_push_struct(b, &f);
|
||||
spa_pod_builder_int(b, n_items);
|
||||
for (i = 0; i < n_items; i++) {
|
||||
spa_pod_builder_string(b, dict->items[i].key);
|
||||
spa_pod_builder_string(b, dict->items[i].value);
|
||||
}
|
||||
spa_pod_builder_pop(b, &f);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int device_marshal_add_listener(void *object,
|
||||
struct spa_hook *listener,
|
||||
const struct spa_device_events *events,
|
||||
void *data)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int device_demarshal_add_listener(void *object,
|
||||
const struct pw_protocol_native_message *msg)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int device_marshal_enum_params(void *object, int seq,
|
||||
uint32_t id, uint32_t index, uint32_t max,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
struct pw_protocol_native_message *msg;
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f[2];
|
||||
|
||||
b = pw_protocol_native_begin_resource(resource, SPA_DEVICE_METHOD_ENUM_PARAMS, &msg);
|
||||
|
||||
spa_pod_builder_push_struct(b, &f[0]);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)),
|
||||
SPA_POD_Id(id),
|
||||
SPA_POD_Int(index),
|
||||
SPA_POD_Int(max),
|
||||
SPA_POD_Pod(filter), NULL);
|
||||
spa_pod_builder_pop(b, &f[0]);
|
||||
|
||||
return pw_protocol_native_end_resource(resource, b);
|
||||
}
|
||||
|
||||
static int device_demarshal_enum_params(void *object, const struct pw_protocol_native_message *msg)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_parser prs;
|
||||
uint32_t id, index, max;
|
||||
int seq;
|
||||
struct spa_pod *filter;
|
||||
|
||||
spa_pod_parser_init(&prs, msg->data, msg->size);
|
||||
if (spa_pod_parser_get_struct(&prs,
|
||||
SPA_POD_Int(&seq),
|
||||
SPA_POD_Id(&id),
|
||||
SPA_POD_Int(&index),
|
||||
SPA_POD_Int(&max),
|
||||
SPA_POD_Pod(&filter)) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pw_proxy_notify(proxy, struct spa_device_methods, enum_params, 0,
|
||||
seq, id, index, max, filter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_marshal_set_param(void *object,
|
||||
uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f[2];
|
||||
|
||||
b = pw_protocol_native_begin_resource(resource, SPA_DEVICE_METHOD_SET_PARAM, NULL);
|
||||
|
||||
spa_pod_builder_push_struct(b, &f[0]);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Id(id),
|
||||
SPA_POD_Int(flags),
|
||||
SPA_POD_Pod(param), NULL);
|
||||
spa_pod_builder_pop(b, &f[0]);
|
||||
|
||||
return pw_protocol_native_end_resource(resource, b);
|
||||
}
|
||||
|
||||
static int device_demarshal_set_param(void *object, const struct pw_protocol_native_message *msg)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_parser prs;
|
||||
uint32_t id, flags;
|
||||
struct spa_pod *param;
|
||||
|
||||
spa_pod_parser_init(&prs, msg->data, msg->size);
|
||||
if (spa_pod_parser_get_struct(&prs,
|
||||
SPA_POD_Id(&id),
|
||||
SPA_POD_Int(&flags),
|
||||
SPA_POD_Pod(¶m)) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pw_proxy_notify(proxy, struct spa_device_methods, set_param, 0,
|
||||
id, flags, param);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_marshal_info(void *object,
|
||||
const struct spa_device_info *info)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f[2];
|
||||
uint32_t i, n_items;
|
||||
|
||||
b = pw_protocol_native_begin_proxy(proxy, SPA_DEVICE_EVENT_INFO, NULL);
|
||||
|
||||
spa_pod_builder_push_struct(b, &f[0]);
|
||||
if (info) {
|
||||
uint64_t change_mask = info->change_mask;
|
||||
|
||||
change_mask &= SPA_DEVICE_CHANGE_MASK_FLAGS |
|
||||
SPA_DEVICE_CHANGE_MASK_PROPS |
|
||||
SPA_DEVICE_CHANGE_MASK_PARAMS;
|
||||
|
||||
n_items = info->props ? info->props->n_items : 0;
|
||||
|
||||
spa_pod_builder_push_struct(b, &f[1]);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Long(change_mask),
|
||||
SPA_POD_Long(info->flags),
|
||||
SPA_POD_Int(n_items), NULL);
|
||||
for (i = 0; i < n_items; i++) {
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_String(info->props->items[i].key),
|
||||
SPA_POD_String(info->props->items[i].value), NULL);
|
||||
}
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Int(info->n_params), NULL);
|
||||
for (i = 0; i < info->n_params; i++) {
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Id(info->params[i].id),
|
||||
SPA_POD_Int(info->params[i].flags), NULL);
|
||||
}
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
} else {
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Pod(NULL), NULL);
|
||||
}
|
||||
spa_pod_builder_pop(b, &f[0]);
|
||||
|
||||
pw_protocol_native_end_proxy(proxy, b);
|
||||
}
|
||||
|
||||
static int device_demarshal_info(void *object,
|
||||
const struct pw_protocol_native_message *msg)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_parser prs;
|
||||
struct spa_pod *ipod;
|
||||
struct spa_device_info info = SPA_DEVICE_INFO_INIT(), *infop;
|
||||
struct spa_dict props;
|
||||
uint32_t i;
|
||||
|
||||
spa_pod_parser_init(&prs, msg->data, msg->size);
|
||||
|
||||
if (spa_pod_parser_get_struct(&prs,
|
||||
SPA_POD_PodStruct(&ipod)) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (ipod) {
|
||||
struct spa_pod_parser p2;
|
||||
struct spa_pod_frame f2;
|
||||
infop = &info;
|
||||
|
||||
spa_pod_parser_pod(&p2, ipod);
|
||||
if (spa_pod_parser_push_struct(&p2, &f2) < 0 ||
|
||||
spa_pod_parser_get(&p2,
|
||||
SPA_POD_Long(&info.change_mask),
|
||||
SPA_POD_Long(&info.flags),
|
||||
SPA_POD_Int(&props.n_items), NULL) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
info.change_mask &= SPA_DEVICE_CHANGE_MASK_FLAGS |
|
||||
SPA_DEVICE_CHANGE_MASK_PROPS |
|
||||
SPA_DEVICE_CHANGE_MASK_PARAMS;
|
||||
|
||||
if (props.n_items > 0) {
|
||||
info.props = &props;
|
||||
|
||||
props.items = alloca(props.n_items * sizeof(struct spa_dict_item));
|
||||
for (i = 0; i < props.n_items; i++) {
|
||||
if (spa_pod_parser_get(&p2,
|
||||
SPA_POD_String(&props.items[i].key),
|
||||
SPA_POD_String(&props.items[i].value), NULL) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (spa_pod_parser_get(&p2,
|
||||
SPA_POD_Int(&info.n_params), NULL) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (info.n_params > 0) {
|
||||
info.params = alloca(info.n_params * sizeof(struct spa_param_info));
|
||||
for (i = 0; i < info.n_params; i++) {
|
||||
if (spa_pod_parser_get(&p2,
|
||||
SPA_POD_Id(&info.params[i].id),
|
||||
SPA_POD_Int(&info.params[i].flags), NULL) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
infop = NULL;
|
||||
}
|
||||
pw_resource_do(resource, struct spa_device_events, info, 0, infop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_marshal_result(void *object,
|
||||
int seq, int res, uint32_t type, const void *result)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f[2];
|
||||
|
||||
b = pw_protocol_native_begin_proxy(proxy, SPA_DEVICE_EVENT_RESULT, NULL);
|
||||
spa_pod_builder_push_struct(b, &f[0]);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Int(seq),
|
||||
SPA_POD_Int(res),
|
||||
SPA_POD_Id(type),
|
||||
NULL);
|
||||
|
||||
switch (type) {
|
||||
case SPA_RESULT_TYPE_DEVICE_PARAMS:
|
||||
{
|
||||
const struct spa_result_device_params *r = result;
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Id(r->id),
|
||||
SPA_POD_Int(r->index),
|
||||
SPA_POD_Int(r->next),
|
||||
SPA_POD_Pod(r->param),
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
spa_pod_builder_pop(b, &f[0]);
|
||||
|
||||
pw_protocol_native_end_proxy(proxy, b);
|
||||
}
|
||||
|
||||
static int device_demarshal_result(void *object,
|
||||
const struct pw_protocol_native_message *msg)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_parser prs;
|
||||
struct spa_pod_frame f[1];
|
||||
int seq, res;
|
||||
uint32_t type;
|
||||
const void *result;
|
||||
struct spa_result_device_params params;
|
||||
|
||||
spa_pod_parser_init(&prs, msg->data, msg->size);
|
||||
if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
|
||||
spa_pod_parser_get(&prs,
|
||||
SPA_POD_Int(&seq),
|
||||
SPA_POD_Int(&res),
|
||||
SPA_POD_Id(&type),
|
||||
NULL) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (type) {
|
||||
case SPA_RESULT_TYPE_DEVICE_PARAMS:
|
||||
if (spa_pod_parser_get(&prs,
|
||||
SPA_POD_Id(¶ms.id),
|
||||
SPA_POD_Int(¶ms.index),
|
||||
SPA_POD_Int(¶ms.next),
|
||||
SPA_POD_PodObject(¶ms.param),
|
||||
NULL) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
result = ¶ms;
|
||||
break;
|
||||
|
||||
default:
|
||||
result = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
pw_resource_do(resource, struct spa_device_events, result, 0, seq, res, type, result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_marshal_event(void *object, const struct spa_event *event)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f[2];
|
||||
|
||||
b = pw_protocol_native_begin_proxy(proxy, SPA_DEVICE_EVENT_RESULT, NULL);
|
||||
spa_pod_builder_push_struct(b, &f[0]);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Pod(event),
|
||||
NULL);
|
||||
spa_pod_builder_pop(b, &f[0]);
|
||||
|
||||
pw_protocol_native_end_proxy(proxy, b);
|
||||
}
|
||||
|
||||
static int device_demarshal_event(void *object,
|
||||
const struct pw_protocol_native_message *msg)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_parser prs;
|
||||
struct spa_event *event;
|
||||
|
||||
spa_pod_parser_init(&prs, msg->data, msg->size);
|
||||
if (spa_pod_parser_get_struct(&prs,
|
||||
SPA_POD_PodObject(&event)) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pw_resource_do(resource, struct spa_device_events, event, 0, event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_marshal_object_info(void *object, uint32_t id,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f[2];
|
||||
uint32_t i, n_items;
|
||||
|
||||
b = pw_protocol_native_begin_proxy(proxy, SPA_DEVICE_EVENT_OBJECT_INFO, NULL);
|
||||
spa_pod_builder_push_struct(b, &f[0]);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Int(id),
|
||||
NULL);
|
||||
if (info) {
|
||||
uint64_t change_mask = info->change_mask;
|
||||
|
||||
change_mask &= SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS |
|
||||
SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
|
||||
|
||||
n_items = info->props ? info->props->n_items : 0;
|
||||
|
||||
spa_pod_builder_push_struct(b, &f[1]);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Id(info->type),
|
||||
SPA_POD_Long(change_mask),
|
||||
SPA_POD_Long(info->flags),
|
||||
SPA_POD_Int(n_items), NULL);
|
||||
for (i = 0; i < n_items; i++) {
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_String(info->props->items[i].key),
|
||||
SPA_POD_String(info->props->items[i].value), NULL);
|
||||
}
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
} else {
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_Pod(NULL), NULL);
|
||||
}
|
||||
spa_pod_builder_pop(b, &f[0]);
|
||||
|
||||
pw_protocol_native_end_proxy(proxy, b);
|
||||
}
|
||||
|
||||
static int device_demarshal_object_info(void *object,
|
||||
const struct pw_protocol_native_message *msg)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_parser prs;
|
||||
struct spa_device_object_info info = SPA_DEVICE_OBJECT_INFO_INIT(), *infop;
|
||||
struct spa_pod *ipod;
|
||||
struct spa_dict props;
|
||||
uint32_t i, id;
|
||||
|
||||
spa_pod_parser_init(&prs, msg->data, msg->size);
|
||||
if (spa_pod_parser_get_struct(&prs,
|
||||
SPA_POD_Int(&id),
|
||||
SPA_POD_PodStruct(&ipod)) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (ipod) {
|
||||
struct spa_pod_parser p2;
|
||||
struct spa_pod_frame f2;
|
||||
infop = &info;
|
||||
|
||||
spa_pod_parser_pod(&p2, ipod);
|
||||
if (spa_pod_parser_push_struct(&p2, &f2) < 0 ||
|
||||
spa_pod_parser_get(&p2,
|
||||
SPA_POD_Id(&info.type),
|
||||
SPA_POD_Long(&info.change_mask),
|
||||
SPA_POD_Long(&info.flags),
|
||||
SPA_POD_Int(&props.n_items), NULL) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
info.change_mask &= SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS |
|
||||
SPA_DEVICE_CHANGE_MASK_PROPS;
|
||||
|
||||
if (props.n_items > 0) {
|
||||
info.props = &props;
|
||||
|
||||
props.items = alloca(props.n_items * sizeof(struct spa_dict_item));
|
||||
for (i = 0; i < props.n_items; i++) {
|
||||
if (spa_pod_parser_get(&p2,
|
||||
SPA_POD_String(&props.items[i].key),
|
||||
SPA_POD_String(&props.items[i].value), NULL) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
infop = NULL;
|
||||
}
|
||||
|
||||
pw_resource_do(resource, struct spa_device_events, object_info, 0, id, infop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spa_device_methods pw_protocol_native_device_method_marshal = {
|
||||
SPA_VERSION_DEVICE_METHODS,
|
||||
.add_listener = &device_marshal_add_listener,
|
||||
.enum_params = &device_marshal_enum_params,
|
||||
.set_param = &device_marshal_set_param
|
||||
};
|
||||
|
||||
static const struct pw_protocol_native_demarshal
|
||||
pw_protocol_native_device_method_demarshal[SPA_DEVICE_METHOD_NUM] =
|
||||
{
|
||||
[SPA_DEVICE_METHOD_ADD_LISTENER] = { &device_demarshal_add_listener, 0 },
|
||||
[SPA_DEVICE_METHOD_ENUM_PARAMS] = { &device_demarshal_enum_params, 0 },
|
||||
[SPA_DEVICE_METHOD_SET_PARAM] = { &device_demarshal_set_param, 0 },
|
||||
};
|
||||
|
||||
static const struct spa_device_events pw_protocol_native_device_event_marshal = {
|
||||
SPA_VERSION_DEVICE_EVENTS,
|
||||
.info = &device_marshal_info,
|
||||
.result = &device_marshal_result,
|
||||
.event = &device_marshal_event,
|
||||
.object_info = &device_marshal_object_info,
|
||||
};
|
||||
|
||||
static const struct pw_protocol_native_demarshal
|
||||
pw_protocol_native_device_event_demarshal[SPA_DEVICE_EVENT_NUM] =
|
||||
{
|
||||
[SPA_DEVICE_EVENT_INFO] = { &device_demarshal_info, 0 },
|
||||
[SPA_DEVICE_EVENT_RESULT] = { &device_demarshal_result, 0 },
|
||||
[SPA_DEVICE_EVENT_EVENT] = { &device_demarshal_event, 0 },
|
||||
[SPA_DEVICE_EVENT_OBJECT_INFO] = { &device_demarshal_object_info, 0 },
|
||||
};
|
||||
|
||||
static const struct pw_protocol_marshal pw_protocol_native_client_device_marshal = {
|
||||
SPA_TYPE_INTERFACE_Device,
|
||||
SPA_VERSION_DEVICE,
|
||||
SPA_DEVICE_EVENT_NUM,
|
||||
SPA_DEVICE_METHOD_NUM,
|
||||
&pw_protocol_native_device_event_marshal,
|
||||
pw_protocol_native_device_event_demarshal,
|
||||
&pw_protocol_native_device_method_marshal,
|
||||
pw_protocol_native_device_method_demarshal,
|
||||
};
|
||||
|
||||
struct pw_protocol *pw_protocol_native_ext_client_device_init(struct pw_core *core)
|
||||
{
|
||||
struct pw_protocol *protocol;
|
||||
|
||||
protocol = pw_core_find_protocol(core, PW_TYPE_INFO_PROTOCOL_Native);
|
||||
|
||||
if (protocol == NULL)
|
||||
return NULL;
|
||||
|
||||
pw_protocol_add_marshal(protocol, &pw_protocol_native_client_device_marshal);
|
||||
|
||||
return protocol;
|
||||
}
|
||||
160
src/modules/module-client-device/proxy-device.c
Normal file
160
src/modules/module-client-device/proxy-device.c
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/* 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 <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/monitor/device.h>
|
||||
|
||||
#include "pipewire/pipewire.h"
|
||||
#include "pipewire/private.h"
|
||||
|
||||
struct device_data {
|
||||
struct pw_remote *remote;
|
||||
struct pw_core *core;
|
||||
|
||||
struct spa_device *device;
|
||||
struct spa_hook device_listener;
|
||||
struct spa_hook device_methods;
|
||||
|
||||
struct pw_proxy *proxy;
|
||||
struct spa_hook proxy_listener;
|
||||
};
|
||||
|
||||
#define pw_device_proxy_event(o,method,version,...) \
|
||||
({ \
|
||||
int _res = -ENOTSUP; \
|
||||
struct spa_interface *_p = o; \
|
||||
spa_interface_call_res(&_p->iface, \
|
||||
struct pw_core_proxy_methods, _res, \
|
||||
method, version, ##__VA_ARGS__); \
|
||||
_res; \
|
||||
})
|
||||
|
||||
static void device_event_info(void *_data, const struct spa_device_info *info)
|
||||
{
|
||||
struct device_data *data = _data;
|
||||
struct spa_interface *iface = (struct spa_interface*)data->proxy;
|
||||
pw_log_debug("%p", data);
|
||||
spa_interface_call(iface, struct spa_device_events, info, 0, info);
|
||||
}
|
||||
|
||||
static void device_event_result(void *_data, int seq, int res, uint32_t type, const void *result)
|
||||
{
|
||||
struct device_data *data = _data;
|
||||
struct spa_interface *iface = (struct spa_interface*)data->proxy;
|
||||
pw_log_debug("%p", data);
|
||||
spa_interface_call(iface, struct spa_device_events, result, 0, seq, res, type, result);
|
||||
}
|
||||
|
||||
static void device_event_event(void *_data, const struct spa_event *event)
|
||||
{
|
||||
struct device_data *data = _data;
|
||||
struct spa_interface *iface = (struct spa_interface*)data->proxy;
|
||||
pw_log_debug("%p", data);
|
||||
spa_interface_call(iface, struct spa_device_events, event, 0, event);
|
||||
}
|
||||
|
||||
static void device_event_object_info(void *_data, uint32_t id,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
struct device_data *data = _data;
|
||||
struct spa_interface *iface = (struct spa_interface*)data->proxy;
|
||||
pw_log_debug("%p", data);
|
||||
spa_interface_call(iface, struct spa_device_events, object_info, 0, id, info);
|
||||
}
|
||||
|
||||
static const struct spa_device_events device_events = {
|
||||
SPA_VERSION_DEVICE_EVENTS,
|
||||
.info = device_event_info,
|
||||
.result = device_event_result,
|
||||
.event = device_event_event,
|
||||
.object_info = device_event_object_info,
|
||||
};
|
||||
|
||||
static int device_method_enum_params(void *object, int seq,
|
||||
uint32_t id, uint32_t index, uint32_t max,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
struct device_data *data = object;
|
||||
pw_log_debug("%p", data);
|
||||
return spa_device_enum_params(data->device, seq, id, index, max, filter);
|
||||
}
|
||||
|
||||
static int device_method_set_param(void *object,
|
||||
uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
struct device_data *data = object;
|
||||
pw_log_debug("%p", data);
|
||||
return spa_device_set_param(data->device, id, flags, param);
|
||||
}
|
||||
|
||||
static const struct spa_device_methods device_methods = {
|
||||
SPA_VERSION_DEVICE_METHODS,
|
||||
.enum_params = device_method_enum_params,
|
||||
.set_param = device_method_set_param,
|
||||
};
|
||||
|
||||
static void device_proxy_destroy(void *_data)
|
||||
{
|
||||
struct device_data *data = _data;
|
||||
spa_hook_remove(&data->device_listener);
|
||||
}
|
||||
|
||||
static const struct pw_proxy_events proxy_events = {
|
||||
PW_VERSION_PROXY_EVENTS,
|
||||
.destroy = device_proxy_destroy,
|
||||
};
|
||||
|
||||
struct pw_proxy *pw_remote_spa_device_export(struct pw_remote *remote,
|
||||
uint32_t type, struct pw_properties *props, void *object)
|
||||
{
|
||||
struct spa_device *device = object;
|
||||
struct pw_proxy *proxy;
|
||||
struct device_data *data;
|
||||
|
||||
proxy = pw_core_proxy_create_object(remote->core_proxy,
|
||||
"client-device",
|
||||
SPA_TYPE_INTERFACE_Device,
|
||||
SPA_VERSION_DEVICE,
|
||||
&props->dict,
|
||||
sizeof(struct device_data));
|
||||
if (proxy == NULL)
|
||||
return NULL;
|
||||
|
||||
data = pw_proxy_get_user_data(proxy);
|
||||
data->remote = remote;
|
||||
data->device = device;
|
||||
data->core = pw_remote_get_core(remote);
|
||||
data->proxy = proxy;
|
||||
|
||||
pw_proxy_add_listener(proxy, &data->proxy_listener, &proxy_events, data);
|
||||
pw_proxy_add_proxy_listener(proxy, &data->device_methods, &device_methods, data);
|
||||
spa_device_add_listener(device, &data->device_listener, &device_events, data);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
219
src/modules/module-client-device/resource-device.c
Normal file
219
src/modules/module-client-device/resource-device.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/* PipeWire
|
||||
*
|
||||
* Copyright © 2018 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 <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include <spa/monitor/device.h>
|
||||
#include <spa/monitor/utils.h>
|
||||
#include <spa/pod/filter.h>
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/debug/types.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include "pipewire/private.h"
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct pw_device *device;
|
||||
|
||||
struct spa_device impl;
|
||||
struct spa_hook_list hooks;
|
||||
|
||||
struct pw_resource *resource;
|
||||
struct spa_hook resource_listener;
|
||||
|
||||
struct pw_global *parent;
|
||||
unsigned int registered:1;
|
||||
};
|
||||
|
||||
#define pw_device_resource(r,m,v,...) \
|
||||
pw_resource_notify_res(r,struct spa_device_methods,m,v,__VA_ARGS__)
|
||||
|
||||
#define pw_device_resource_enum_params(r,...) \
|
||||
pw_device_resource(r,enum_params,0,__VA_ARGS__)
|
||||
#define pw_device_resource_set_param(r,...) \
|
||||
pw_device_resource(r,set_param,0,__VA_ARGS__)
|
||||
|
||||
static int device_add_listener(void *object,
|
||||
struct spa_hook *listener,
|
||||
const struct spa_device_events *events,
|
||||
void *data)
|
||||
{
|
||||
struct impl *impl = object;
|
||||
struct spa_hook_list save;
|
||||
|
||||
spa_hook_list_isolate(&impl->hooks, &save, listener, events, data);
|
||||
pw_log_debug("client-device %p: add listener", impl);
|
||||
|
||||
spa_hook_list_join(&impl->hooks, &save);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_sync(void *object, int seq)
|
||||
{
|
||||
struct impl *impl = object;
|
||||
|
||||
pw_log_debug("client-device %p: sync %d", impl, seq);
|
||||
|
||||
return pw_resource_ping(impl->resource, seq);
|
||||
}
|
||||
|
||||
static int device_enum_params(void *object, int seq,
|
||||
uint32_t id, uint32_t index, uint32_t max,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
struct impl *impl = object;
|
||||
pw_log_debug("client-device %p: enum params", impl);
|
||||
return pw_device_resource_enum_params(impl->resource, seq, id, index, max, filter);
|
||||
}
|
||||
|
||||
static int device_set_param(void *object,
|
||||
uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
struct impl *impl = object;
|
||||
pw_log_debug("client-device %p: set param", impl);
|
||||
return pw_device_resource_set_param(impl->resource, id, flags, param);
|
||||
}
|
||||
|
||||
static const struct spa_device_methods impl_device = {
|
||||
SPA_VERSION_DEVICE_METHODS,
|
||||
.add_listener = device_add_listener,
|
||||
.sync = device_sync,
|
||||
.enum_params = device_enum_params,
|
||||
.set_param = device_set_param,
|
||||
};
|
||||
|
||||
static void device_info(void *data, const struct spa_device_info *info)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
spa_device_emit_info(&impl->hooks, info);
|
||||
|
||||
if (!impl->registered) {
|
||||
pw_device_register(impl->device, impl->resource->client, impl->parent, NULL);
|
||||
impl->registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void device_result(void *data, int seq, int res, uint32_t type, const void *result)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
pw_log_debug("client-device %p: result %d %d %u", impl, seq, res, type);
|
||||
spa_device_emit_result(&impl->hooks, seq, res, type, result);
|
||||
}
|
||||
|
||||
static void device_event(void *data, const struct spa_event *event)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
spa_device_emit_event(&impl->hooks, event);
|
||||
}
|
||||
|
||||
static void device_object_info(void *data, uint32_t id,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
spa_device_emit_object_info(&impl->hooks, id, info);
|
||||
}
|
||||
|
||||
static const struct spa_device_events device_events = {
|
||||
SPA_VERSION_DEVICE_EVENTS,
|
||||
.info = device_info,
|
||||
.result = device_result,
|
||||
.event = device_event,
|
||||
.object_info = device_object_info,
|
||||
};
|
||||
|
||||
static void device_resource_destroy(void *data)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
struct pw_device *device = impl->device;
|
||||
|
||||
pw_log_debug("client-device %p: destroy", impl);
|
||||
|
||||
impl->resource = NULL;
|
||||
spa_hook_remove(&impl->resource_listener);
|
||||
|
||||
pw_device_destroy(device);
|
||||
}
|
||||
|
||||
static void device_resource_pong(void *data, int seq)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
spa_device_emit_result(&impl->hooks, seq, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static const struct pw_resource_events resource_events = {
|
||||
PW_VERSION_RESOURCE_EVENTS,
|
||||
.destroy = device_resource_destroy,
|
||||
.pong = device_resource_pong,
|
||||
};
|
||||
|
||||
struct pw_device *pw_client_device_new(struct pw_resource *resource,
|
||||
struct pw_global *parent,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_device *device;
|
||||
struct pw_client *client = pw_resource_get_client(resource);
|
||||
struct pw_core *core = pw_client_get_core(client);
|
||||
const char *name;
|
||||
|
||||
if ((name = pw_properties_get(properties, PW_KEY_DEVICE_NAME)) == NULL)
|
||||
name = "client-device";
|
||||
|
||||
device = pw_device_new(core, name, properties, sizeof(struct impl));
|
||||
if (device == NULL)
|
||||
return NULL;
|
||||
|
||||
impl = pw_device_get_user_data(device);
|
||||
impl->device = device;
|
||||
impl->core = core;
|
||||
impl->resource = resource;
|
||||
impl->parent = parent;
|
||||
|
||||
impl->impl.iface = SPA_INTERFACE_INIT(
|
||||
SPA_TYPE_INTERFACE_Device,
|
||||
SPA_VERSION_DEVICE,
|
||||
&impl_device, impl);
|
||||
spa_hook_list_init(&impl->hooks);
|
||||
pw_device_set_implementation(device, &impl->impl);
|
||||
|
||||
pw_resource_add_listener(impl->resource,
|
||||
&impl->resource_listener,
|
||||
&resource_events,
|
||||
impl);
|
||||
pw_resource_set_implementation(impl->resource,
|
||||
&device_events,
|
||||
impl);
|
||||
|
||||
|
||||
return device;
|
||||
}
|
||||
|
|
@ -38,3 +38,12 @@ pipewire_module_spa_node_factory = shared_library('pipewire-module-spa-node-fact
|
|||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_spa_device_factory = shared_library('pipewire-module-spa-device-factory',
|
||||
[ 'module-device-factory.c', 'spa-device.c' ],
|
||||
c_args : pipewire_module_spa_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
|
|
|||
212
src/modules/spa/module-device-factory.c
Normal file
212
src/modules/spa/module-device-factory.c
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/* PipeWire
|
||||
*
|
||||
* Copyright © 2018 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 <dlfcn.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/pipewire.h"
|
||||
|
||||
#include "spa-device.h"
|
||||
|
||||
static const struct spa_dict_item module_props[] = {
|
||||
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
|
||||
{ PW_KEY_MODULE_DESCRIPTION, "Provide a factory to make SPA devices" },
|
||||
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
|
||||
};
|
||||
|
||||
struct factory_data {
|
||||
struct pw_core *core;
|
||||
struct pw_factory *this;
|
||||
struct pw_properties *properties;
|
||||
|
||||
struct spa_hook factory_listener;
|
||||
struct spa_hook module_listener;
|
||||
|
||||
struct spa_list device_list;
|
||||
};
|
||||
|
||||
struct device_data {
|
||||
struct spa_list link;
|
||||
struct pw_device *device;
|
||||
struct spa_hook device_listener;
|
||||
};
|
||||
|
||||
static void device_destroy(void *data)
|
||||
{
|
||||
struct device_data *nd = data;
|
||||
spa_list_remove(&nd->link);
|
||||
spa_hook_remove(&nd->device_listener);
|
||||
}
|
||||
|
||||
static const struct pw_device_events device_events = {
|
||||
PW_VERSION_DEVICE_EVENTS,
|
||||
.destroy = device_destroy,
|
||||
};
|
||||
|
||||
static void *create_object(void *_data,
|
||||
struct pw_resource *resource,
|
||||
uint32_t type,
|
||||
uint32_t version,
|
||||
struct pw_properties *properties,
|
||||
uint32_t new_id)
|
||||
{
|
||||
struct factory_data *data = _data;
|
||||
struct pw_device *device;
|
||||
const char *lib, *factory_name, *name;
|
||||
struct device_data *nd;
|
||||
|
||||
if (properties == NULL)
|
||||
goto no_properties;
|
||||
|
||||
lib = pw_properties_get(properties, "spa.library.name");
|
||||
factory_name = pw_properties_get(properties, "spa.factory.name");
|
||||
name = pw_properties_get(properties, "name");
|
||||
|
||||
if (lib == NULL || factory_name == NULL)
|
||||
goto no_properties;
|
||||
|
||||
if (name == NULL)
|
||||
name = "spa-device";
|
||||
|
||||
device = pw_spa_device_load(data->core,
|
||||
NULL,
|
||||
pw_factory_get_global(data->this),
|
||||
lib,
|
||||
factory_name,
|
||||
name,
|
||||
0,
|
||||
properties,
|
||||
sizeof(struct device_data));
|
||||
if (device == NULL)
|
||||
goto no_mem;
|
||||
|
||||
nd = pw_spa_device_get_user_data(device);
|
||||
nd->device = device;
|
||||
spa_list_append(&data->device_list, &nd->link);
|
||||
|
||||
pw_device_add_listener(device, &nd->device_listener, &device_events, nd);
|
||||
|
||||
if (resource)
|
||||
pw_global_bind(pw_device_get_global(device),
|
||||
pw_resource_get_client(resource),
|
||||
PW_PERM_RWX,
|
||||
version, new_id);
|
||||
|
||||
return device;
|
||||
|
||||
no_properties:
|
||||
pw_log_error("needed properties: spa.library.name=<library-name> spa.factory.name=<factory-name>");
|
||||
if (resource) {
|
||||
pw_resource_error(resource, -EINVAL,
|
||||
"needed properties: "
|
||||
"spa.library.name=<library-name> "
|
||||
"spa.factory.name=<factory-name>");
|
||||
}
|
||||
return NULL;
|
||||
no_mem:
|
||||
pw_log_error("can't create device: no memory");
|
||||
if (resource) {
|
||||
pw_resource_error(resource, -ENOMEM, "no memory");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct pw_factory_implementation factory_impl = {
|
||||
PW_VERSION_FACTORY_IMPLEMENTATION,
|
||||
.create_object = create_object,
|
||||
};
|
||||
|
||||
static void factory_destroy(void *_data)
|
||||
{
|
||||
struct factory_data *data = _data;
|
||||
struct device_data *nd;
|
||||
|
||||
spa_hook_remove(&data->module_listener);
|
||||
|
||||
spa_list_consume(nd, &data->device_list, link)
|
||||
pw_device_destroy(nd->device);
|
||||
|
||||
if (data->properties)
|
||||
pw_properties_free(data->properties);
|
||||
}
|
||||
|
||||
static const struct pw_factory_events factory_events = {
|
||||
PW_VERSION_FACTORY_IMPLEMENTATION,
|
||||
.destroy = factory_destroy,
|
||||
};
|
||||
|
||||
static void module_destroy(void *_data)
|
||||
{
|
||||
struct factory_data *data = _data;
|
||||
pw_factory_destroy(data->this);
|
||||
}
|
||||
|
||||
static const struct pw_module_events module_events = {
|
||||
PW_VERSION_MODULE_EVENTS,
|
||||
.destroy = module_destroy,
|
||||
};
|
||||
|
||||
static int module_init(struct pw_module *module, struct pw_properties *properties)
|
||||
{
|
||||
struct pw_core *core = pw_module_get_core(module);
|
||||
struct pw_factory *factory;
|
||||
struct factory_data *data;
|
||||
|
||||
factory = pw_factory_new(core,
|
||||
"spa-device-factory",
|
||||
PW_TYPE_INTERFACE_Device,
|
||||
PW_VERSION_DEVICE_PROXY,
|
||||
NULL,
|
||||
sizeof(*data));
|
||||
if (factory == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
data = pw_factory_get_user_data(factory);
|
||||
data->this = factory;
|
||||
data->core = core;
|
||||
data->properties = properties;
|
||||
spa_list_init(&data->device_list);
|
||||
|
||||
pw_factory_add_listener(factory, &data->factory_listener, &factory_events, data);
|
||||
pw_factory_set_implementation(factory, &factory_impl, data);
|
||||
|
||||
pw_log_debug("module %p: new", module);
|
||||
pw_module_add_listener(module, &data->module_listener, &module_events, data);
|
||||
|
||||
pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
|
||||
|
||||
pw_factory_register(factory, NULL, pw_module_get_global(module), NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPA_EXPORT
|
||||
int pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
return module_init(module, NULL);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue