From 082463efd0c0794d1d41f12c1b3d8797235e56fd Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Oct 2019 22:52:25 +0200 Subject: [PATCH] protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node. --- src/extensions/client-node.h | 2 +- src/modules/meson.build | 31 +- src/modules/module-client-node.c | 9 +- .../module-client-node/protocol-native.c | 2 +- .../module-client-node/v0/client-node.c | 1343 +++++++++++++++++ .../module-client-node/v0/client-node.h | 52 + .../module-client-node/v0/ext-client-node.h | 407 +++++ .../module-client-node/v0/protocol-native.c | 517 +++++++ src/modules/module-client-node/v0/transport.c | 257 ++++ src/modules/module-client-node/v0/transport.h | 54 + src/modules/module-protocol-native.c | 75 +- .../module-protocol-native/connection.c | 83 +- .../module-protocol-native/connection.h | 2 + .../module-protocol-native/protocol-native.c | 6 +- .../module-protocol-native/v0/interfaces.h | 532 +++++++ .../v0/protocol-native.c | 1186 +++++++++++++++ .../module-protocol-native/v0/typemap.h | 261 ++++ src/pipewire/core.c | 14 +- src/pipewire/interfaces.h | 18 +- src/pipewire/private.h | 9 + src/pipewire/protocol.c | 4 +- src/pipewire/protocol.h | 2 +- src/pipewire/proxy.c | 13 +- src/pipewire/proxy.h | 1 + src/pipewire/remote.c | 7 +- src/pipewire/resource.c | 4 +- 26 files changed, 4814 insertions(+), 77 deletions(-) create mode 100644 src/modules/module-client-node/v0/client-node.c create mode 100644 src/modules/module-client-node/v0/client-node.h create mode 100644 src/modules/module-client-node/v0/ext-client-node.h create mode 100644 src/modules/module-client-node/v0/protocol-native.c create mode 100644 src/modules/module-client-node/v0/transport.c create mode 100644 src/modules/module-client-node/v0/transport.h create mode 100644 src/modules/module-protocol-native/v0/interfaces.h create mode 100644 src/modules/module-protocol-native/v0/protocol-native.c create mode 100644 src/modules/module-protocol-native/v0/typemap.h diff --git a/src/extensions/client-node.h b/src/extensions/client-node.h index 0857a666c..a8c60e347 100644 --- a/src/extensions/client-node.h +++ b/src/extensions/client-node.h @@ -34,7 +34,7 @@ extern "C" { struct pw_client_node_proxy { struct spa_interface iface; }; -#define PW_VERSION_CLIENT_NODE 0 +#define PW_VERSION_CLIENT_NODE 3 #define PW_EXTENSION_MODULE_CLIENT_NODE PIPEWIRE_MODULE_PREFIX "module-client-node" diff --git a/src/modules/meson.build b/src/modules/meson.build index bec6f5583..1466d45fe 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -23,19 +23,6 @@ pipewire_module_rtkit = shared_library('pipewire-module-rtkit', [ 'module-rtkit. ) endif -pipewire_module_client_node = shared_library('pipewire-module-client-node', - [ 'module-client-node.c', - 'module-client-node/remote-node.c', - 'module-client-node/client-node.c', - 'module-client-node/protocol-native.c', - 'spa/spa-node.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_client_node = shared_library('pipewire-module-client-device', [ 'module-client-device.c', 'module-client-device/resource-device.c', @@ -68,6 +55,7 @@ pipewire_module_protocol_native = shared_library('pipewire-module-protocol-nativ 'module-protocol-native/local-socket.c', 'module-protocol-native/portal-screencast.c', 'module-protocol-native/protocol-native.c', + 'module-protocol-native/v0/protocol-native.c', 'module-protocol-native/connection.c' ], c_args : pipewire_module_c_args, include_directories : [configinc, spa_inc], @@ -76,6 +64,23 @@ pipewire_module_protocol_native = shared_library('pipewire-module-protocol-nativ dependencies : pipewire_module_protocol_native_deps, ) +pipewire_module_client_node = shared_library('pipewire-module-client-node', + [ 'module-client-node.c', + 'module-client-node/remote-node.c', + 'module-client-node/client-node.c', + 'module-client-node/protocol-native.c', + 'module-client-node/v0/client-node.c', + 'module-client-node/v0/transport.c', + 'module-client-node/v0/protocol-native.c', + 'spa/spa-node.c', ], + c_args : pipewire_module_c_args, + include_directories : [configinc, spa_inc], + link_with : pipewire_module_protocol_native, + install : true, + install_dir : modules_install_dir, + dependencies : [mathlib, dl_lib, pipewire_dep], +) + test('pw-test-protocol-native', executable('pw-test-protocol-native', [ 'module-protocol-native/test-connection.c', diff --git a/src/modules/module-client-node.c b/src/modules/module-client-node.c index 30f03a267..0f70aca8f 100644 --- a/src/modules/module-client-node.c +++ b/src/modules/module-client-node.c @@ -31,6 +31,7 @@ #include +#include "module-client-node/v0/client-node.h" #include "module-client-node/client-node.h" #define NAME "client-node" @@ -47,6 +48,7 @@ struct pw_proxy *pw_remote_spa_node_export(struct pw_remote *remote, uint32_t type, struct pw_properties *props, void *object, size_t user_data_size); struct pw_protocol *pw_protocol_native_ext_client_node_init(struct pw_core *core); +struct pw_protocol *pw_protocol_native_ext_client_node0_init(struct pw_core *core); struct factory_data { struct pw_factory *this; @@ -76,7 +78,11 @@ static void *create_object(void *_data, goto error_resource; } - result = pw_client_node_new(node_resource, properties, true); + if (version == 0) { + result = pw_client_node0_new(node_resource, properties); + } else { + result = pw_client_node_new(node_resource, properties, true); + } if (result == NULL) { res = -errno; goto error_node; @@ -167,6 +173,7 @@ int pipewire__module_init(struct pw_module *module, const char *args) data); pw_protocol_native_ext_client_node_init(core); + pw_protocol_native_ext_client_node0_init(core); data->export_node.type = PW_TYPE_INTERFACE_Node; data->export_node.func = pw_remote_node_export; diff --git a/src/modules/module-client-node/protocol-native.c b/src/modules/module-client-node/protocol-native.c index 8b5d1ac41..c2b4b109a 100644 --- a/src/modules/module-client-node/protocol-native.c +++ b/src/modules/module-client-node/protocol-native.c @@ -97,7 +97,7 @@ client_node_marshal_get_node(void *object, uint32_t version, size_t user_data_si struct pw_proxy *res; uint32_t new_id; - res = pw_proxy_new(object, PW_TYPE_INTERFACE_Node, user_data_size); + res = pw_proxy_new(object, PW_TYPE_INTERFACE_Node, version, user_data_size); if (res == NULL) return NULL; diff --git a/src/modules/module-client-node/v0/client-node.c b/src/modules/module-client-node/v0/client-node.c new file mode 100644 index 000000000..cce161110 --- /dev/null +++ b/src/modules/module-client-node/v0/client-node.c @@ -0,0 +1,1343 @@ +/* PipeWire + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pipewire/pipewire.h" +#include "pipewire/interfaces.h" +#include "pipewire/private.h" + +#include "pipewire/core.h" +#include "modules/spa/spa-node.h" +#include "client-node.h" +#include "transport.h" + +#define NAME "client-node0" + +/** \cond */ + +#define MAX_INPUTS 64 +#define MAX_OUTPUTS 64 + +#define MAX_BUFFERS 64 + +#define CHECK_IN_PORT_ID(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_INPUTS) +#define CHECK_OUT_PORT_ID(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < MAX_OUTPUTS) +#define CHECK_PORT_ID(this,d,p) (CHECK_IN_PORT_ID(this,d,p) || CHECK_OUT_PORT_ID(this,d,p)) +#define CHECK_FREE_IN_PORT(this,d,p) (CHECK_IN_PORT_ID(this,d,p) && !(this)->in_ports[p].valid) +#define CHECK_FREE_OUT_PORT(this,d,p) (CHECK_OUT_PORT_ID(this,d,p) && !(this)->out_ports[p].valid) +#define CHECK_FREE_PORT(this,d,p) (CHECK_FREE_IN_PORT (this,d,p) || CHECK_FREE_OUT_PORT (this,d,p)) +#define CHECK_IN_PORT(this,d,p) (CHECK_IN_PORT_ID(this,d,p) && (this)->in_ports[p].valid) +#define CHECK_OUT_PORT(this,d,p) (CHECK_OUT_PORT_ID(this,d,p) && (this)->out_ports[p].valid) +#define CHECK_PORT(this,d,p) (CHECK_IN_PORT (this,d,p) || CHECK_OUT_PORT (this,d,p)) + +#define GET_IN_PORT(this,p) (&this->in_ports[p]) +#define GET_OUT_PORT(this,p) (&this->out_ports[p]) +#define GET_PORT(this,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p)) + +#define CHECK_PORT_BUFFER(this,b,p) (b < p->n_buffers) + +struct mem { + uint32_t id; + int ref; + int fd; + uint32_t type; + uint32_t flags; +}; + +struct buffer { + struct spa_buffer *outbuf; + struct spa_buffer buffer; + struct spa_meta metas[4]; + struct spa_data datas[4]; + bool outstanding; + uint32_t memid; +}; + +struct port { + uint32_t id; + enum spa_direction direction; + + bool valid; + struct spa_port_info info; + struct pw_properties *properties; + + bool have_format; + uint32_t n_params; + struct spa_pod **params; + struct spa_io_buffers *io; + + uint32_t n_buffers; + struct buffer buffers[MAX_BUFFERS]; +}; + +struct node { + struct spa_node node; + + struct impl *impl; + + struct spa_log *log; + struct spa_loop *data_loop; + struct spa_system *data_system; + + struct spa_hook_list hooks; + struct spa_callbacks callbacks; + + struct pw_resource *resource; + + struct spa_source data_source; + int writefd; + + struct spa_node_info info; + + uint32_t n_inputs; + uint32_t n_outputs; + struct port in_ports[MAX_INPUTS]; + struct port out_ports[MAX_OUTPUTS]; + + uint32_t n_params; + struct spa_pod **params; + + uint32_t seq; + uint32_t init_pending; +}; + +struct impl { + struct pw_client_node0 this; + + bool client_reuse; + + struct pw_core *core; + + struct node node; + + struct pw_client_node0_transport *transport; + + struct spa_hook node_listener; + struct spa_hook resource_listener; + struct spa_hook object_listener; + + struct pw_array mems; + + int fds[2]; + int other_fds[2]; + + uint32_t input_ready; + bool out_pending; +}; + +/** \endcond */ + +static struct mem *ensure_mem(struct impl *impl, int fd, uint32_t type, uint32_t flags) +{ + struct mem *m, *f = NULL; + + pw_array_for_each(m, &impl->mems) { + if (m->ref <= 0) + f = m; + else if (m->fd == fd) + goto found; + } + + if (f == NULL) { + m = pw_array_add(&impl->mems, sizeof(struct mem)); + m->id = pw_array_get_len(&impl->mems, struct mem) - 1; + m->ref = 0; + } + else { + m = f; + } + m->fd = fd; + m->type = type; + m->flags = flags; + + pw_client_node0_resource_add_mem(impl->node.resource, + m->id, + type, + m->fd, + m->flags); + found: + m->ref++; + return m; +} + + +static int clear_buffers(struct node *this, struct port *port) +{ + uint32_t i, j; + struct impl *impl = this->impl; + + for (i = 0; i < port->n_buffers; i++) { + struct buffer *b = &port->buffers[i]; + struct mem *m; + + spa_log_debug(this->log, "node %p: clear buffer %d", this, i); + + for (j = 0; j < b->buffer.n_datas; j++) { + struct spa_data *d = &b->datas[j]; + + if (d->type == SPA_DATA_DmaBuf || + d->type == SPA_DATA_MemFd) { + uint32_t id; + + id = SPA_PTR_TO_UINT32(b->buffer.datas[j].data); + m = pw_array_get_unchecked(&impl->mems, id, struct mem); + m->ref--; + } + } + m = pw_array_get_unchecked(&impl->mems, b->memid, struct mem); + m->ref--; + } + port->n_buffers = 0; + return 0; +} + +static void emit_port_info(struct node *this, struct port *port) +{ + spa_node_emit_port_info(&this->hooks, + port->direction, port->id, &port->info); +} + +static int impl_node_add_listener(void *object, + struct spa_hook *listener, + const struct spa_node_events *events, + void *data) +{ + struct node *this = object; + struct spa_hook_list save; + uint32_t i; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_hook_list_isolate(&this->hooks, &save, listener, events, data); + + for (i = 0; i < MAX_INPUTS; i++) { + if (this->in_ports[i].valid) + emit_port_info(this, &this->in_ports[i]); + } + for (i = 0; i < MAX_OUTPUTS; i++) { + if (this->out_ports[i].valid) + emit_port_info(this, &this->out_ports[i]); + } + spa_hook_list_join(&this->hooks, &save); + + return 0; +} + +static int impl_node_enum_params(void *object, int seq, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + struct node *this = object; + uint8_t buffer[1024]; + struct spa_pod_builder b = { 0 }; + struct spa_result_node_params result; + uint32_t count = 0; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(num != 0, -EINVAL); + + result.id = id; + result.next = start; + + while (true) { + struct spa_pod *param; + + result.index = result.next++; + if (result.index >= this->n_params) + break; + + param = this->params[result.index]; + + if (param == NULL || !spa_pod_is_object_id(param, id)) + continue; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + if (spa_pod_filter(&b, &result.param, param, filter) != 0) + continue; + + pw_log_debug(NAME " %p: %d param %u", this, seq, result.index); + spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + + if (++count == num) + break; + } + return 0; +} + +static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct node *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + if (this->resource == NULL) + return -EIO; + + pw_client_node0_resource_set_param(this->resource, this->seq, id, flags, param); + + return SPA_RESULT_RETURN_ASYNC(this->seq++); +} + +static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) +{ + return -ENOTSUP; +} + +static inline void do_flush(struct node *this) +{ + uint64_t cmd = 1; + if (write(this->writefd, &cmd, 8) != 8) + spa_log_warn(this->log, "node %p: error flushing : %s", this, strerror(errno)); + +} + +static int impl_node_send_command(void *object, const struct spa_command *command) +{ + struct node *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(command != NULL, -EINVAL); + + if (this->resource == NULL) + return -EIO; + + pw_client_node0_resource_command(this->resource, this->seq, command); + return SPA_RESULT_RETURN_ASYNC(this->seq++); +} + +static int +impl_node_set_callbacks(void *object, + const struct spa_node_callbacks *callbacks, + void *data) +{ + struct node *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); + + return 0; +} + +static int +impl_node_sync(void *object, int seq) +{ + struct node *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + pw_log_debug(NAME " %p: sync %p", this, this->resource); + + if (this->resource == NULL) + return -EIO; + + this->init_pending = SPA_RESULT_RETURN_ASYNC(this->seq++); + + return this->init_pending; +} + +extern uint32_t pw_protocol_native0_type_from_v2(struct pw_client *client, uint32_t type); +extern uint32_t pw_protocol_native0_type_to_v2(struct pw_client *client, uint32_t type); + +extern struct spa_pod *pw_protocol_native0_pod_from_v2(struct pw_client *client, const struct spa_pod *pod); +extern int pw_protocol_native0_pod_to_v2(struct pw_client *client, const struct spa_pod *pod, + struct spa_pod_builder *b); + +static void +do_update_port(struct node *this, + enum spa_direction direction, + uint32_t port_id, + uint32_t change_mask, + uint32_t n_params, + const struct spa_pod **params, + const struct spa_port_info *info) +{ + struct port *port; + + port = GET_PORT(this, direction, port_id); + + if (!port->valid) { + spa_log_info(this->log, "node %p: adding port %d", this, port_id); + port->id = port_id; + port->direction = direction; + port->have_format = false; + port->valid = true; + + if (direction == SPA_DIRECTION_INPUT) + this->n_inputs++; + else + this->n_outputs++; + } + + if (change_mask & PW_CLIENT_NODE0_PORT_UPDATE_PARAMS) { + uint32_t i; + + port->have_format = false; + + spa_log_info(this->log, "node %p: port %u update %d params", this, port_id, n_params); + for (i = 0; i < port->n_params; i++) + free(port->params[i]); + port->n_params = n_params; + port->params = realloc(port->params, port->n_params * sizeof(struct spa_pod *)); + + for (i = 0; i < port->n_params; i++) { + port->params[i] = params[i] ? + pw_protocol_native0_pod_from_v2(this->resource->client, params[i]) : NULL; + + if (port->params[i] && spa_pod_is_object_id(port->params[i], SPA_PARAM_Format)) + port->have_format = true; + } + } + + if (change_mask & PW_CLIENT_NODE0_PORT_UPDATE_INFO) { + if (port->properties) + pw_properties_free(port->properties); + port->properties = NULL; + port->info.props = NULL; + port->info.n_params = 0; + port->info.params = NULL; + + if (info) { + port->info = *info; + if (info->props) { + port->properties = pw_properties_new_dict(info->props); + port->info.props = &port->properties->dict; + } + } + spa_node_emit_port_info(&this->hooks, direction, port_id, info); + } +} + +static void +clear_port(struct node *this, + struct port *port, enum spa_direction direction, uint32_t port_id) +{ + do_update_port(this, + direction, + port_id, + PW_CLIENT_NODE0_PORT_UPDATE_PARAMS | + PW_CLIENT_NODE0_PORT_UPDATE_INFO, 0, NULL, NULL); + clear_buffers(this, port); +} + +static void do_uninit_port(struct node *this, enum spa_direction direction, uint32_t port_id) +{ + struct port *port; + + spa_log_info(this->log, "node %p: removing port %d", this, port_id); + + if (direction == SPA_DIRECTION_INPUT) { + port = GET_IN_PORT(this, port_id); + this->n_inputs--; + } else { + port = GET_OUT_PORT(this, port_id); + this->n_outputs--; + } + clear_port(this, port, direction, port_id); + port->valid = false; + spa_node_emit_port_info(&this->hooks, direction, port_id, NULL); +} + +static int +impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, + const struct spa_dict *props) +{ + struct node *this = object; + struct port *port; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_FREE_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + clear_port(this, port, direction, port_id); + + return 0; +} + +static int +impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) +{ + struct node *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + do_uninit_port(this, direction, port_id); + + return 0; +} + +static int +impl_node_port_enum_params(void *object, int seq, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + struct node *this = object; + struct port *port; + uint8_t buffer[1024]; + struct spa_pod_builder b = { 0 }; + struct spa_result_node_params result; + uint32_t count = 0; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(num != 0, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + + pw_log_debug(NAME " %p: %d port %d.%d %u %u %u", this, seq, + direction, port_id, id, start, num); + + result.id = id; + result.next = start; + + while (true) { + struct spa_pod *param; + + result.index = result.next++; + if (result.index >= port->n_params) + break; + + param = port->params[result.index]; + + if (param == NULL || !spa_pod_is_object_id(param, id)) + continue; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + if (spa_pod_filter(&b, &result.param, param, filter) < 0) + continue; + + pw_log_debug(NAME " %p: %d param %u", this, seq, result.index); + spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + + if (++count == num) + break; + } + return 0; +} + +static int +impl_node_port_set_param(void *object, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct node *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + if (this->resource == NULL) + return -EIO; + + pw_client_node0_resource_port_set_param(this->resource, + this->seq, + direction, port_id, + id, flags, + param); + return SPA_RESULT_RETURN_ASYNC(this->seq++); +} + +static int +impl_node_port_set_io(void *object, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, + void *data, size_t size) +{ + struct node *this = object; + struct impl *impl; + struct pw_memblock *mem; + struct mem *m; + uint32_t memid, mem_offset, mem_size; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + impl = this->impl; + + spa_log_info(this->log, "node %p: port %d.%d set io %d %p", this, + direction, port_id, id, data); + + if (id == SPA_IO_Buffers) { + struct port *port = GET_PORT(this, direction, port_id); + port->io = data; + } + + if (this->resource == NULL) + return -EIO; + + + if (data) { + if ((mem = pw_mempool_find_ptr(impl->core->pool, data)) == NULL) + return -EINVAL; + + mem_offset = SPA_PTRDIFF(data, mem->map->ptr); + mem_size = mem->size; + if (mem_size - mem_offset < size) + return -EINVAL; + + mem_offset += mem->map->offset; + m = ensure_mem(impl, mem->fd, SPA_DATA_MemFd, mem->flags); + memid = m->id; + } + else { + memid = SPA_ID_INVALID; + mem_offset = mem_size = 0; + } + + pw_client_node0_resource_port_set_io(this->resource, + this->seq, + direction, port_id, + id, + memid, + mem_offset, mem_size); + return SPA_RESULT_RETURN_ASYNC(this->seq++); +} + +static int +impl_node_port_use_buffers(void *object, + enum spa_direction direction, + uint32_t port_id, + uint32_t flags, + struct spa_buffer **buffers, + uint32_t n_buffers) +{ + struct node *this = object; + struct impl *impl; + struct port *port; + uint32_t i, j; + struct pw_client_node0_buffer *mb; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + impl = this->impl; + spa_log_info(this->log, "node %p: use buffers %p %u", this, buffers, n_buffers); + + port = GET_PORT(this, direction, port_id); + + if (!port->have_format) + return -EIO; + + clear_buffers(this, port); + + if (n_buffers > 0) { + mb = alloca(n_buffers * sizeof(struct pw_client_node0_buffer)); + } else { + mb = NULL; + } + + port->n_buffers = n_buffers; + + if (this->resource == NULL) + return -EIO; + + for (i = 0; i < n_buffers; i++) { + struct buffer *b = &port->buffers[i]; + struct pw_memblock *mem; + struct mem *m; + size_t data_size, size; + void *baseptr; + + b->outbuf = buffers[i]; + memcpy(&b->buffer, buffers[i], sizeof(struct spa_buffer)); + b->buffer.datas = b->datas; + b->buffer.metas = b->metas; + + if (buffers[i]->n_metas > 0) + baseptr = buffers[i]->metas[0].data; + else if (buffers[i]->n_datas > 0) + baseptr = buffers[i]->datas[0].chunk; + else + return -EINVAL; + + if ((mem = pw_mempool_find_ptr(impl->core->pool, baseptr)) == NULL) + return -EINVAL; + + data_size = 0; + for (j = 0; j < buffers[i]->n_metas; j++) { + data_size += buffers[i]->metas[j].size; + } + for (j = 0; j < buffers[i]->n_datas; j++) { + struct spa_data *d = buffers[i]->datas; + data_size += sizeof(struct spa_chunk); + if (d->type == SPA_DATA_MemPtr) + data_size += d->maxsize; + } + + m = ensure_mem(impl, mem->fd, SPA_DATA_MemFd, mem->flags); + b->memid = m->id; + + mb[i].buffer = &b->buffer; + mb[i].mem_id = b->memid; + mb[i].offset = SPA_PTRDIFF(baseptr, SPA_MEMBER(mem->map->ptr, mem->map->offset, void)); + mb[i].size = data_size; + + for (j = 0; j < buffers[i]->n_metas; j++) + memcpy(&b->buffer.metas[j], &buffers[i]->metas[j], sizeof(struct spa_meta)); + b->buffer.n_metas = j; + + size = 0; + for (j = 0; j < buffers[i]->n_datas; j++) { + struct spa_data *d = &buffers[i]->datas[j]; + + memcpy(&b->buffer.datas[j], d, sizeof(struct spa_data)); + + if (d->type == SPA_DATA_DmaBuf || + d->type == SPA_DATA_MemFd) { + m = ensure_mem(impl, d->fd, d->type, d->flags); + b->buffer.datas[j].data = SPA_UINT32_TO_PTR(m->id); + } else if (d->type == SPA_DATA_MemPtr) { + b->buffer.datas[j].data = SPA_INT_TO_PTR(size); + size += d->maxsize; + } else { + b->buffer.datas[j].type = SPA_ID_INVALID; + b->buffer.datas[j].data = 0; + spa_log_error(this->log, "invalid memory type %d", d->type); + } + } + } + + pw_client_node0_resource_port_use_buffers(this->resource, + this->seq, + direction, port_id, + n_buffers, mb); + + return SPA_RESULT_RETURN_ASYNC(this->seq++); +} + +static int +impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) +{ + struct node *this = object; + struct impl *impl; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_OUT_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); + + impl = this->impl; + + spa_log_trace(this->log, "reuse buffer %d", buffer_id); + + pw_client_node0_transport_add_message(impl->transport, (struct pw_client_node0_message *) + &PW_CLIENT_NODE0_MESSAGE_PORT_REUSE_BUFFER_INIT(port_id, buffer_id)); + do_flush(this); + + return 0; +} + +static int impl_node_process_input(struct spa_node *node) +{ + struct node *this = SPA_CONTAINER_OF(node, struct node, node); + struct impl *impl = this->impl; +// bool client_reuse = impl->client_reuse; + uint32_t i; + int res; + + if (impl->input_ready == 0) { + /* the client is not ready to receive our buffers, recycle them */ + pw_log_trace("node not ready, recycle buffers"); + for (i = 0; i < MAX_INPUTS; i++) { + struct port *p = &this->in_ports[i]; + struct spa_io_buffers *io = p->io; + + if (!p->valid || io == NULL) + continue; + + io->status = SPA_STATUS_NEED_DATA; + } + res = SPA_STATUS_NEED_DATA; + } + else { + for (i = 0; i < MAX_INPUTS; i++) { + struct port *p = &this->in_ports[i]; + struct spa_io_buffers *io = p->io; + + if (!p->valid || io == NULL) + continue; + + pw_log_trace("set io status to %d %d", io->status, io->buffer_id); + impl->transport->inputs[p->id] = *io; + + /* explicitly recycle buffers when the client is not going to do it */ +// if (!client_reuse && (pp = p->peer)) +// spa_node_port_reuse_buffer(pp->node->implementation, +// pp->port_id, io->buffer_id); + } + pw_client_node0_transport_add_message(impl->transport, + &PW_CLIENT_NODE0_MESSAGE_INIT(PW_CLIENT_NODE0_MESSAGE_PROCESS_INPUT)); + do_flush(this); + + impl->input_ready--; + res = SPA_STATUS_OK; + } + return res; +} + +static int impl_node_process_output(struct spa_node *node) +{ + struct node *this; + struct impl *impl; + uint32_t i; + + this = SPA_CONTAINER_OF(node, struct node, node); + impl = this->impl; + + if (impl->out_pending) + goto done; + + impl->out_pending = true; + + for (i = 0; i < MAX_OUTPUTS; i++) { + struct port *p = &this->out_ports[i]; + struct spa_io_buffers *io = p->io; + + if (!p->valid || io == NULL) + continue; + + impl->transport->outputs[p->id] = *io; + + pw_log_trace("%d %d -> %d %d", io->status, io->buffer_id, + impl->transport->outputs[p->id].status, + impl->transport->outputs[p->id].buffer_id); + } + + done: + pw_client_node0_transport_add_message(impl->transport, + &PW_CLIENT_NODE0_MESSAGE_INIT(PW_CLIENT_NODE0_MESSAGE_PROCESS_OUTPUT)); + do_flush(this); + + return SPA_STATUS_OK; +} + +static int impl_node_process(void *object) +{ + struct node *this = object; + struct impl *impl = this->impl; + struct pw_node *n = impl->this.node; + + return impl_node_process_input(n->node); +} + +static int handle_node_message(struct node *this, struct pw_client_node0_message *message) +{ + struct impl *impl = SPA_CONTAINER_OF(this, struct impl, node); + uint32_t i; + + switch (PW_CLIENT_NODE0_MESSAGE_TYPE(message)) { + case PW_CLIENT_NODE0_MESSAGE_HAVE_OUTPUT: + for (i = 0; i < MAX_OUTPUTS; i++) { + struct port *p = &this->out_ports[i]; + struct spa_io_buffers *io = p->io; + if (!p->valid || io == NULL) + continue; + *io = impl->transport->outputs[p->id]; + pw_log_trace("have output %d %d", io->status, io->buffer_id); + } + impl->out_pending = false; + spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA); + break; + + case PW_CLIENT_NODE0_MESSAGE_NEED_INPUT: + for (i = 0; i < MAX_INPUTS; i++) { + struct port *p = &this->in_ports[i]; + struct spa_io_buffers *io = p->io; + if (!p->valid || io == NULL) + continue; + pw_log_trace("need input %d %d", i, p->id); + *io = impl->transport->inputs[p->id]; + pw_log_trace("need input %d %d", io->status, io->buffer_id); + } + impl->input_ready++; + spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA); + break; + + case PW_CLIENT_NODE0_MESSAGE_PORT_REUSE_BUFFER: + if (impl->client_reuse) { + struct pw_client_node0_message_port_reuse_buffer *p = + (struct pw_client_node0_message_port_reuse_buffer *) message; + spa_node_call_reuse_buffer(&this->callbacks, p->body.port_id.value, + p->body.buffer_id.value); + } + break; + + default: + pw_log_warn("unhandled message %d", PW_CLIENT_NODE0_MESSAGE_TYPE(message)); + return -ENOTSUP; + } + return 0; +} + +static void setup_transport(struct impl *impl) +{ + struct node *this = &impl->node; + uint32_t max_inputs = 0, max_outputs = 0, n_inputs = 0, n_outputs = 0; + + n_inputs = this->n_inputs; + max_inputs = this->info.max_input_ports == 0 ? this->n_inputs : this->info.max_input_ports; + n_outputs = this->n_outputs; + max_outputs = this->info.max_output_ports == 0 ? this->n_outputs : this->info.max_output_ports; + + impl->transport = pw_client_node0_transport_new(impl->core, max_inputs, max_outputs); + impl->transport->area->n_input_ports = n_inputs; + impl->transport->area->n_output_ports = n_outputs; +} + +static void +client_node0_done(void *data, int seq, int res) +{ + struct impl *impl = data; + struct node *this = &impl->node; + + if (seq == 0 && res == 0 && impl->transport == NULL) + setup_transport(impl); + + pw_log_debug("seq:%d res:%d pending:%d", seq, res, this->init_pending); + spa_node_emit_result(&this->hooks, seq, res, 0, NULL); + + if (this->init_pending != SPA_ID_INVALID) { + spa_node_emit_result(&this->hooks, this->init_pending, res, 0, NULL); + this->init_pending = SPA_ID_INVALID; + } +} + +static void +client_node0_update(void *data, + uint32_t change_mask, + uint32_t max_input_ports, + uint32_t max_output_ports, + uint32_t n_params, + const struct spa_pod **params) +{ + struct impl *impl = data; + struct node *this = &impl->node; + + if (change_mask & PW_CLIENT_NODE0_UPDATE_MAX_INPUTS) + this->info.max_input_ports = max_input_ports; + if (change_mask & PW_CLIENT_NODE0_UPDATE_MAX_OUTPUTS) + this->info.max_output_ports = max_output_ports; + if (change_mask & PW_CLIENT_NODE0_UPDATE_PARAMS) { + uint32_t i; + spa_log_info(this->log, "node %p: update %d params", this, n_params); + + for (i = 0; i < this->n_params; i++) + free(this->params[i]); + this->n_params = n_params; + this->params = realloc(this->params, this->n_params * sizeof(struct spa_pod *)); + + for (i = 0; i < this->n_params; i++) + this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; + } + if (change_mask & (PW_CLIENT_NODE0_UPDATE_MAX_INPUTS | PW_CLIENT_NODE0_UPDATE_MAX_OUTPUTS)) { + spa_node_emit_info(&this->hooks, &this->info); + } + + spa_log_info(this->log, "node %p: got node update max_in %u, max_out %u", this, + this->info.max_input_ports, this->info.max_output_ports); +} + +static void +client_node0_port_update(void *data, + enum spa_direction direction, + uint32_t port_id, + uint32_t change_mask, + uint32_t n_params, + const struct spa_pod **params, + const struct spa_port_info *info) +{ + struct impl *impl = data; + struct node *this = &impl->node; + bool remove; + + spa_log_info(this->log, "node %p: got port update", this); + if (!CHECK_PORT_ID(this, direction, port_id)) + return; + + remove = (change_mask == 0); + + if (remove) { + do_uninit_port(this, direction, port_id); + } else { + do_update_port(this, + direction, + port_id, + change_mask, + n_params, params, info); + } +} + +static void client_node0_set_active(void *data, bool active) +{ + struct impl *impl = data; + pw_node_set_active(impl->this.node, active); +} + +static void client_node0_event(void *data, struct spa_event *event) +{ + struct impl *impl = data; + struct node *this = &impl->node; + spa_node_emit_event(&this->hooks, event); +} + +static struct pw_client_node0_proxy_methods client_node0_methods = { + PW_VERSION_CLIENT_NODE0_PROXY_METHODS, + .done = client_node0_done, + .update = client_node0_update, + .port_update = client_node0_port_update, + .set_active = client_node0_set_active, + .event = client_node0_event, +}; + +static void node_on_data_fd_events(struct spa_source *source) +{ + struct node *this = source->data; + struct impl *impl = this->impl; + + if (source->rmask & (SPA_IO_ERR | SPA_IO_HUP)) { + spa_log_warn(this->log, "node %p: got error", this); + return; + } + + if (source->rmask & SPA_IO_IN) { + struct pw_client_node0_message message; + uint64_t cmd; + + if (read(this->data_source.fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) + spa_log_warn(this->log, "node %p: error reading message: %s", + this, strerror(errno)); + + while (pw_client_node0_transport_next_message(impl->transport, &message) == 1) { + struct pw_client_node0_message *msg = alloca(SPA_POD_SIZE(&message)); + pw_client_node0_transport_parse_message(impl->transport, msg); + handle_node_message(this, msg); + } + } +} + +static const struct spa_node_methods impl_node = { + SPA_VERSION_NODE_METHODS, + .add_listener = impl_node_add_listener, + .set_callbacks = impl_node_set_callbacks, + .sync = impl_node_sync, + .enum_params = impl_node_enum_params, + .set_param = impl_node_set_param, + .set_io = impl_node_set_io, + .send_command = impl_node_send_command, + .add_port = impl_node_add_port, + .remove_port = impl_node_remove_port, + .port_enum_params = impl_node_port_enum_params, + .port_set_param = impl_node_port_set_param, + .port_use_buffers = impl_node_port_use_buffers, + .port_set_io = impl_node_port_set_io, + .port_reuse_buffer = impl_node_port_reuse_buffer, + .process = impl_node_process, +}; + +static int +node_init(struct node *this, + struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + uint32_t i; + + for (i = 0; i < n_support; i++) { + switch (support[i].type) { + case SPA_TYPE_INTERFACE_Log: + this->log = support[i].data; + break; + case SPA_TYPE_INTERFACE_DataLoop: + this->data_loop = support[i].data; + break; + case SPA_TYPE_INTERFACE_DataSystem: + this->data_system = support[i].data; + break; + } + } + if (this->data_loop == NULL) { + spa_log_error(this->log, "a data-loop is needed"); + return -EINVAL; + } + + this->node.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Node, + SPA_VERSION_NODE, + &impl_node, this); + spa_hook_list_init(&this->hooks); + + this->data_source.func = node_on_data_fd_events; + this->data_source.data = this; + this->data_source.fd = -1; + this->data_source.mask = SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP; + this->data_source.rmask = 0; + + this->seq = 1; + this->init_pending = SPA_ID_INVALID; + + return SPA_RESULT_RETURN_ASYNC(this->seq++); +} + +static int node_clear(struct node *this) +{ + uint32_t i; + + for (i = 0; i < MAX_INPUTS; i++) { + if (this->in_ports[i].valid) + clear_port(this, &this->in_ports[i], SPA_DIRECTION_INPUT, i); + } + for (i = 0; i < MAX_OUTPUTS; i++) { + if (this->out_ports[i].valid) + clear_port(this, &this->out_ports[i], SPA_DIRECTION_OUTPUT, i); + } + + return 0; +} + +static int do_remove_source(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct spa_source *source = user_data; + spa_loop_remove_source(loop, source); + return 0; +} + +static void client_node0_resource_destroy(void *data) +{ + struct impl *impl = data; + struct pw_client_node0 *this = &impl->this; + struct node *node = &impl->node; + + pw_log_debug("client-node %p: destroy", impl); + + impl->node.resource = this->resource = NULL; + spa_hook_remove(&impl->resource_listener); + spa_hook_remove(&impl->object_listener); + + if (node->data_source.fd != -1) { + spa_loop_invoke(node->data_loop, + do_remove_source, + SPA_ID_INVALID, + NULL, + 0, + true, + &node->data_source); + } + pw_node_destroy(this->node); +} + +static void node_initialized(void *data) +{ + struct impl *impl = data; + struct pw_client_node0 *this = &impl->this; + struct pw_node *node = this->node; + + if (this->resource == NULL) + return; + + impl->fds[0] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + impl->fds[1] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + impl->node.data_source.fd = impl->fds[0]; + impl->node.writefd = impl->fds[1]; + impl->other_fds[0] = impl->fds[1]; + impl->other_fds[1] = impl->fds[0]; + + spa_loop_add_source(impl->node.data_loop, &impl->node.data_source); + pw_log_debug("client-node %p: transport fd %d %d", node, impl->fds[0], impl->fds[1]); + + pw_client_node0_resource_transport(this->resource, + pw_global_get_id(pw_node_get_global(node)), + impl->other_fds[0], + impl->other_fds[1], + impl->transport); +} + +static void node_free(void *data) +{ + struct impl *impl = data; + + pw_log_debug("client-node %p: free", &impl->this); + node_clear(&impl->node); + + if (impl->transport) + pw_client_node0_transport_destroy(impl->transport); + + spa_hook_remove(&impl->node_listener); + + pw_array_clear(&impl->mems); + + if (impl->fds[0] != -1) + close(impl->fds[0]); + if (impl->fds[1] != -1) + close(impl->fds[1]); + free(impl); +} + +static const struct pw_node_events node_events = { + PW_VERSION_NODE_EVENTS, + .free = node_free, + .initialized = node_initialized, +}; + +static const struct pw_resource_events resource_events = { + PW_VERSION_RESOURCE_EVENTS, + .destroy = client_node0_resource_destroy, +}; + +static void convert_properties(struct pw_properties *properties) +{ + struct { + const char *from, *to; + } props[] = { + { "pipewire.autoconnect", PW_KEY_NODE_AUTOCONNECT, } + }; + uint32_t i; + const char *str; + + for(i = 0; i < SPA_N_ELEMENTS(props); i++) { + if ((str = pw_properties_get(properties, props[i].from)) != NULL) { + pw_properties_set(properties, props[i].to, str); + pw_properties_set(properties, props[i].from, NULL); + } + } +} + +/** Create a new client node + * \param client an owner \ref pw_client + * \param id an id + * \param name a name + * \param properties extra properties + * \return a newly allocated client node + * + * Create a new \ref pw_node. + * + * \memberof pw_client_node + */ +struct pw_client_node0 *pw_client_node0_new(struct pw_resource *resource, + struct pw_properties *properties) +{ + struct impl *impl; + struct pw_client_node0 *this; + struct pw_client *client = pw_resource_get_client(resource); + struct pw_core *core = pw_client_get_core(client); + const struct spa_support *support; + uint32_t n_support; + const char *name; + const char *str; + int res; + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + return NULL; + + this = &impl->this; + + if (properties == NULL) + properties = pw_properties_new(NULL, NULL); + if (properties == NULL) { + res = -errno; + goto error_exit_free; + } + convert_properties(properties); + + pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", client->global->id); + + impl->core = core; + impl->fds[0] = impl->fds[1] = -1; + pw_log_debug("client-node %p: new", impl); + + support = pw_core_get_support(impl->core, &n_support); + + node_init(&impl->node, NULL, support, n_support); + impl->node.impl = impl; + + pw_array_init(&impl->mems, 64); + + if ((name = pw_properties_get(properties, "node.name")) == NULL) + name = "client-node"; + pw_properties_set(properties, PW_KEY_MEDIA_TYPE, "Video"); + pw_properties_set(properties, SPA_KEY_MEDIA_CLASS, "Stream/Duplex/Video"); + + impl->node.resource = resource; + this->resource = resource; + this->node = pw_spa_node_new(core, + PW_SPA_NODE_FLAG_ASYNC, + &impl->node.node, + NULL, + properties, 0); + if (this->node == NULL) { + res = -errno; + goto error_no_node; + } + + str = pw_properties_get(properties, "pipewire.client.reuse"); + impl->client_reuse = str && pw_properties_parse_bool(str); + + pw_resource_add_listener(this->resource, + &impl->resource_listener, + &resource_events, + impl); + pw_resource_add_object_listener(this->resource, + &impl->object_listener, + &client_node0_methods, + impl); + + + pw_node_add_listener(this->node, &impl->node_listener, &node_events, impl); + + return this; + +error_no_node: + pw_resource_destroy(this->resource); + node_clear(&impl->node); +error_exit_free: + free(impl); + errno = -res; + return NULL; +} + +/** Destroy a client node + * \param node the client node to destroy + * \memberof pw_client_node + */ +void pw_client_node0_destroy(struct pw_client_node0 *node) +{ + pw_resource_destroy(node->resource); +} diff --git a/src/modules/module-client-node/v0/client-node.h b/src/modules/module-client-node/v0/client-node.h new file mode 100644 index 000000000..42a3b2891 --- /dev/null +++ b/src/modules/module-client-node/v0/client-node.h @@ -0,0 +1,52 @@ +/* PipeWire + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PIPEWIRE_CLIENT_NODE0_H +#define PIPEWIRE_CLIENT_NODE0_H + +#include + +#include "ext-client-node.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \class pw_client_node0 + * + * PipeWire client node interface + */ +struct pw_client_node0 { + struct pw_node *node; + + struct pw_resource *resource; +}; + +struct pw_client_node0 * +pw_client_node0_new(struct pw_resource *resource, + struct pw_properties *properties); + +void +pw_client_node0_destroy(struct pw_client_node0 *node); + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_CLIENT_NODE0_H */ diff --git a/src/modules/module-client-node/v0/ext-client-node.h b/src/modules/module-client-node/v0/ext-client-node.h new file mode 100644 index 000000000..7f4a36634 --- /dev/null +++ b/src/modules/module-client-node/v0/ext-client-node.h @@ -0,0 +1,407 @@ +/* PipeWire + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PIPEWIRE_EXT_CLIENT_NODE0_H__ +#define __PIPEWIRE_EXT_CLIENT_NODE0_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +#define PW_VERSION_CLIENT_NODE0 0 + +struct pw_client_node0_message; + +/** Shared structure between client and server \memberof pw_client_node */ +struct pw_client_node0_area { + uint32_t max_input_ports; /**< max input ports of the node */ + uint32_t n_input_ports; /**< number of input ports of the node */ + uint32_t max_output_ports; /**< max output ports of the node */ + uint32_t n_output_ports; /**< number of output ports of the node */ +}; + +/** \class pw_client_node0_transport + * + * \brief Transport object + * + * The transport object contains shared data and ringbuffers to exchange + * events and data between the server and the client in a low-latency and + * lockfree way. + */ +struct pw_client_node0_transport { + struct pw_client_node0_area *area; /**< the transport area */ + struct spa_io_buffers *inputs; /**< array of buffer input io */ + struct spa_io_buffers *outputs; /**< array of buffer output io */ + void *input_data; /**< input memory for ringbuffer */ + struct spa_ringbuffer *input_buffer; /**< ringbuffer for input memory */ + void *output_data; /**< output memory for ringbuffer */ + struct spa_ringbuffer *output_buffer; /**< ringbuffer for output memory */ + + /** Destroy a transport + * \param trans a transport to destroy + * \memberof pw_client_node0_transport + */ + void (*destroy) (struct pw_client_node0_transport *trans); + + /** Add a message to the transport + * \param trans the transport to send the message on + * \param message the message to add + * \return 0 on success, < 0 on error + * + * Write \a message to the shared ringbuffer. + */ + int (*add_message) (struct pw_client_node0_transport *trans, struct pw_client_node0_message *message); + + /** Get next message from a transport + * \param trans the transport to get the message of + * \param[out] message the message to read + * \return < 0 on error, 1 when a message is available, + * 0 when no more messages are available. + * + * Get the skeleton next message from \a trans into \a message. This function will + * only read the head and object body of the message. + * + * After the complete size of the message has been calculated, you should call + * \ref parse_message() to read the complete message contents. + */ + int (*next_message) (struct pw_client_node0_transport *trans, struct pw_client_node0_message *message); + + /** Parse the complete message on transport + * \param trans the transport to read from + * \param[out] message memory that can hold the complete message + * \return 0 on success, < 0 on error + * + * Use this function after \ref next_message(). + */ + int (*parse_message) (struct pw_client_node0_transport *trans, void *message); +}; + +#define pw_client_node0_transport_destroy(t) ((t)->destroy((t))) +#define pw_client_node0_transport_add_message(t,m) ((t)->add_message((t), (m))) +#define pw_client_node0_transport_next_message(t,m) ((t)->next_message((t), (m))) +#define pw_client_node0_transport_parse_message(t,m) ((t)->parse_message((t), (m))) + +enum pw_client_node0_message_type { + PW_CLIENT_NODE0_MESSAGE_HAVE_OUTPUT, /*< signal that the node has output */ + PW_CLIENT_NODE0_MESSAGE_NEED_INPUT, /*< signal that the node needs input */ + PW_CLIENT_NODE0_MESSAGE_PROCESS_INPUT, /*< instruct the node to process input */ + PW_CLIENT_NODE0_MESSAGE_PROCESS_OUTPUT, /*< instruct the node output is processed */ + PW_CLIENT_NODE0_MESSAGE_PORT_REUSE_BUFFER, /*< reuse a buffer */ +}; + +struct pw_client_node0_message_body { + struct spa_pod_int type SPA_ALIGNED(8); /*< one of enum pw_client_node0_message_type */ +}; + +struct pw_client_node0_message { + struct spa_pod_struct pod; + struct pw_client_node0_message_body body; +}; + +struct pw_client_node0_message_port_reuse_buffer_body { + struct spa_pod_int type SPA_ALIGNED(8); /*< PW_CLIENT_NODE0_MESSAGE_PORT_REUSE_BUFFER */ + struct spa_pod_int port_id SPA_ALIGNED(8); /*< port id */ + struct spa_pod_int buffer_id SPA_ALIGNED(8); /*< buffer id to reuse */ +}; + +struct pw_client_node0_message_port_reuse_buffer { + struct spa_pod_struct pod; + struct pw_client_node0_message_port_reuse_buffer_body body; +}; + +#define PW_CLIENT_NODE0_MESSAGE_TYPE(message) (((struct pw_client_node0_message*)(message))->body.type.value) + +#define PW_CLIENT_NODE0_MESSAGE_INIT(message) (struct pw_client_node0_message) \ + { { { sizeof(struct pw_client_node0_message_body), SPA_TYPE_Struct } }, \ + { SPA_POD_INIT_Int(message) } } + +#define PW_CLIENT_NODE0_MESSAGE_INIT_FULL(type,size,message,...) (type) \ + { { { size, SPA_TYPE_Struct } }, \ + { SPA_POD_INIT_Int(message), ##__VA_ARGS__ } } \ + +#define PW_CLIENT_NODE0_MESSAGE_PORT_REUSE_BUFFER_INIT(port_id,buffer_id) \ + PW_CLIENT_NODE0_MESSAGE_INIT_FULL(struct pw_client_node0_message_port_reuse_buffer, \ + sizeof(struct pw_client_node0_message_port_reuse_buffer_body), \ + PW_CLIENT_NODE0_MESSAGE_PORT_REUSE_BUFFER, \ + SPA_POD_INIT_Int(port_id), \ + SPA_POD_INIT_Int(buffer_id)) + +/** information about a buffer */ +struct pw_client_node0_buffer { + uint32_t mem_id; /**< the memory id for the metadata */ + uint32_t offset; /**< offset in memory */ + uint32_t size; /**< size in memory */ + struct spa_buffer *buffer; /**< buffer describing metadata and buffer memory */ +}; + +#define PW_CLIENT_NODE0_PROXY_METHOD_DONE 0 +#define PW_CLIENT_NODE0_PROXY_METHOD_UPDATE 1 +#define PW_CLIENT_NODE0_PROXY_METHOD_PORT_UPDATE 2 +#define PW_CLIENT_NODE0_PROXY_METHOD_SET_ACTIVE 3 +#define PW_CLIENT_NODE0_PROXY_METHOD_EVENT 4 +#define PW_CLIENT_NODE0_PROXY_METHOD_DESTROY 5 +#define PW_CLIENT_NODE0_PROXY_METHOD_NUM 6 + +/** \ref pw_client_node methods */ +struct pw_client_node0_proxy_methods { +#define PW_VERSION_CLIENT_NODE0_PROXY_METHODS 0 + uint32_t version; + + /** Complete an async operation */ + void (*done) (void *object, int seq, int res); + + /** + * Update the node ports and properties + * + * Update the maximum number of ports and the params of the + * client node. + * \param change_mask bitfield with changed parameters + * \param max_input_ports new max input ports + * \param max_output_ports new max output ports + * \param params new params + */ + void (*update) (void *object, +#define PW_CLIENT_NODE0_UPDATE_MAX_INPUTS (1 << 0) +#define PW_CLIENT_NODE0_UPDATE_MAX_OUTPUTS (1 << 1) +#define PW_CLIENT_NODE0_UPDATE_PARAMS (1 << 2) + uint32_t change_mask, + uint32_t max_input_ports, + uint32_t max_output_ports, + uint32_t n_params, + const struct spa_pod **params); + + /** + * Update a node port + * + * Update the information of one port of a node. + * \param direction the direction of the port + * \param port_id the port id to update + * \param change_mask a bitfield of changed items + * \param n_params number of port parameters + * \param params array of port parameters + * \param info port information + */ + void (*port_update) (void *object, + enum spa_direction direction, + uint32_t port_id, +#define PW_CLIENT_NODE0_PORT_UPDATE_PARAMS (1 << 0) +#define PW_CLIENT_NODE0_PORT_UPDATE_INFO (1 << 1) + uint32_t change_mask, + uint32_t n_params, + const struct spa_pod **params, + const struct spa_port_info *info); + /** + * Activate or deactivate the node + */ + void (*set_active) (void *object, bool active); + /** + * Send an event to the node + * \param event the event to send + */ + void (*event) (void *object, struct spa_event *event); + /** + * Destroy the client_node + */ + void (*destroy) (void *object); +}; + +#define PW_CLIENT_NODE0_PROXY_EVENT_ADD_MEM 0 +#define PW_CLIENT_NODE0_PROXY_EVENT_TRANSPORT 1 +#define PW_CLIENT_NODE0_PROXY_EVENT_SET_PARAM 2 +#define PW_CLIENT_NODE0_PROXY_EVENT_EVENT 3 +#define PW_CLIENT_NODE0_PROXY_EVENT_COMMAND 4 +#define PW_CLIENT_NODE0_PROXY_EVENT_ADD_PORT 5 +#define PW_CLIENT_NODE0_PROXY_EVENT_REMOVE_PORT 6 +#define PW_CLIENT_NODE0_PROXY_EVENT_PORT_SET_PARAM 7 +#define PW_CLIENT_NODE0_PROXY_EVENT_PORT_USE_BUFFERS 8 +#define PW_CLIENT_NODE0_PROXY_EVENT_PORT_COMMAND 9 +#define PW_CLIENT_NODE0_PROXY_EVENT_PORT_SET_IO 10 +#define PW_CLIENT_NODE0_PROXY_EVENT_NUM 11 + +/** \ref pw_client_node events */ +struct pw_client_node0_proxy_events { +#define PW_VERSION_CLIENT_NODE0_PROXY_EVENTS 0 + uint32_t version; + /** + * Memory was added to a node + * + * \param mem_id the id of the memory + * \param type the memory type + * \param memfd the fd of the memory + * \param flags flags for the \a memfd + */ + void (*add_mem) (void *object, + uint32_t mem_id, + uint32_t type, + int memfd, + uint32_t flags); + /** + * Notify of a new transport area + * + * The transport area is used to exchange real-time commands between + * the client and the server. + * + * \param node_id the node id created for this client node + * \param readfd fd for signal data can be read + * \param writefd fd for signal data can be written + * \param transport the shared transport area + */ + void (*transport) (void *object, + uint32_t node_id, + int readfd, + int writefd, + struct pw_client_node0_transport *transport); + /** + * Notify of a property change + * + * When the server configures the properties on the node + * this event is sent + * + * \param seq a sequence number + * \param id the id of the parameter + * \param flags parameter flags + * \param param the param to set + */ + void (*set_param) (void *object, uint32_t seq, + uint32_t id, uint32_t flags, + const struct spa_pod *param); + /** + * Receive an event from the client node + * \param event the received event */ + void (*event) (void *object, const struct spa_event *event); + /** + * Notify of a new node command + * + * \param seq a sequence number + * \param command the command + */ + void (*command) (void *object, uint32_t seq, const struct spa_command *command); + /** + * A new port was added to the node + * + * The server can at any time add a port to the node when there + * are free ports available. + * + * \param seq a sequence number + * \param direction the direction of the port + * \param port_id the new port id + */ + void (*add_port) (void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id); + /** + * A port was removed from the node + * + * \param seq a sequence number + * \param direction a port direction + * \param port_id the remove port id + */ + void (*remove_port) (void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id); + /** + * A parameter was configured on the port + * + * \param seq a sequence number + * \param direction a port direction + * \param port_id the port id + * \param id the id of the parameter + * \param flags flags used when setting the param + * \param param the new param + */ + void (*port_set_param) (void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, uint32_t flags, + const struct spa_pod *param); + /** + * Notify the port of buffers + * + * \param seq a sequence number + * \param direction a port direction + * \param port_id the port id + * \param n_buffer the number of buffers + * \param buffers and array of buffer descriptions + */ + void (*port_use_buffers) (void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t n_buffers, + struct pw_client_node0_buffer *buffers); + /** + * Notify of a new port command + * + * \param direction a port direction + * \param port_id the port id + * \param command the command + */ + void (*port_command) (void *object, + enum spa_direction direction, + uint32_t port_id, + const struct spa_command *command); + + /** + * Configure the io area with \a id of \a port_id. + * + * \param seq a sequence number + * \param direction the direction of the port + * \param port_id the port id + * \param id the id of the io area to set + * \param mem_id the id of the memory to use + * \param offset offset of io area in memory + * \param size size of the io area + */ + void (*port_set_io) (void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, + uint32_t mem_id, + uint32_t offset, + uint32_t size); +}; +#define pw_client_node0_resource(r,m,v,...) pw_resource_call(r, struct pw_client_node0_proxy_events, m, v, ##__VA_ARGS__) + +#define pw_client_node0_resource_add_mem(r,...) pw_client_node0_resource(r,add_mem,0,__VA_ARGS__) +#define pw_client_node0_resource_transport(r,...) pw_client_node0_resource(r,transport,0,__VA_ARGS__) +#define pw_client_node0_resource_set_param(r,...) pw_client_node0_resource(r,set_param,0,__VA_ARGS__) +#define pw_client_node0_resource_event(r,...) pw_client_node0_resource(r,event,0,__VA_ARGS__) +#define pw_client_node0_resource_command(r,...) pw_client_node0_resource(r,command,0,__VA_ARGS__) +#define pw_client_node0_resource_add_port(r,...) pw_client_node0_resource(r,add_port,0,__VA_ARGS__) +#define pw_client_node0_resource_remove_port(r,...) pw_client_node0_resource(r,remove_port,0,__VA_ARGS__) +#define pw_client_node0_resource_port_set_param(r,...) pw_client_node0_resource(r,port_set_param,0,__VA_ARGS__) +#define pw_client_node0_resource_port_use_buffers(r,...) pw_client_node0_resource(r,port_use_buffers,0,__VA_ARGS__) +#define pw_client_node0_resource_port_command(r,...) pw_client_node0_resource(r,port_command,0,__VA_ARGS__) +#define pw_client_node0_resource_port_set_io(r,...) pw_client_node0_resource(r,port_set_io,0,__VA_ARGS__) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __PIPEWIRE_EXT_CLIENT_NODE0_H__ */ diff --git a/src/modules/module-client-node/v0/protocol-native.c b/src/modules/module-client-node/v0/protocol-native.c new file mode 100644 index 000000000..51a2c7d34 --- /dev/null +++ b/src/modules/module-client-node/v0/protocol-native.c @@ -0,0 +1,517 @@ +/* PipeWire + * Copyright (C) 2017 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include "pipewire/pipewire.h" +#include "pipewire/interfaces.h" +#include "pipewire/protocol.h" +#include "pipewire/client.h" +#include "pipewire/private.h" + +#include "extensions/protocol-native.h" + +#include "ext-client-node.h" + +#include "transport.h" + +#define PW_PROTOCOL_NATIVE_FLAG_REMAP (1<<0) + +extern uint32_t pw_protocol_native0_find_type(struct pw_client *client, const char *type); +extern int pw_protocol_native0_pod_to_v2(struct pw_client *client, const struct spa_pod *pod, + struct spa_pod_builder *b); +extern uint32_t pw_protocol_native0_type_to_v2(struct pw_client *client, + const struct spa_type_info *info, uint32_t type); + +static void +client_node_marshal_add_mem(void *object, + uint32_t mem_id, + uint32_t type, + int memfd, uint32_t flags) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + const char *typename; + + switch (type) { + case SPA_DATA_MemFd: + typename = "Spa:Enum:DataType:Fd:MemFd"; + break; + case SPA_DATA_DmaBuf: + typename = "Spa:Enum:DataType:Fd:DmaBuf"; + break; + default: + case SPA_DATA_MemPtr: + return; + + } + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_ADD_MEM, NULL); + + spa_pod_builder_add_struct(b, + "i", mem_id, + "I", pw_protocol_native0_find_type(resource->client, typename), + "i", pw_protocol_native_add_resource_fd(resource, memfd), + "i", flags); + + pw_protocol_native_end_resource(resource, b); +} + +static void client_node_marshal_transport(void *object, uint32_t node_id, int readfd, int writefd, + struct pw_client_node0_transport *transport) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + struct pw_client_node0_transport_info info; + + pw_client_node0_transport_get_info(transport, &info); + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_TRANSPORT, NULL); + + spa_pod_builder_add_struct(b, + "i", node_id, + "i", pw_protocol_native_add_resource_fd(resource, readfd), + "i", pw_protocol_native_add_resource_fd(resource, writefd), + "i", pw_protocol_native_add_resource_fd(resource, info.memfd), + "i", info.offset, + "i", info.size); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_set_param(void *object, uint32_t seq, uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_SET_PARAM, NULL); + + spa_pod_builder_add_struct(b, + "i", seq, + "I", id, + "i", flags, + "P", param); + + pw_protocol_native_end_resource(resource, b); +} + +static void client_node_marshal_event_event(void *object, const struct spa_event *event) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_EVENT, NULL); + + spa_pod_builder_add_struct(b, "P", event); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_command(void *object, uint32_t seq, const struct spa_command *command) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_builder *b; + struct spa_pod_frame f; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_COMMAND, NULL); + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, "i", seq, NULL); + pw_protocol_native0_pod_to_v2(client, (struct spa_pod *)command, b); + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_add_port(void *object, + uint32_t seq, enum spa_direction direction, uint32_t port_id) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_ADD_PORT, NULL); + + spa_pod_builder_add_struct(b, + "i", seq, + "i", direction, + "i", port_id); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_remove_port(void *object, + uint32_t seq, enum spa_direction direction, uint32_t port_id) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_REMOVE_PORT, NULL); + + spa_pod_builder_add_struct(b, + "i", seq, + "i", direction, + "i", port_id); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_port_set_param(void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, + uint32_t flags, + const struct spa_pod *param) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_builder *b; + struct spa_pod_frame f; + const char *typename; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_PORT_SET_PARAM, NULL); + + switch (id) { + case SPA_PARAM_Props: + typename = "Spa:Enum:ParamId:Props"; + break; + case SPA_PARAM_Format: + typename = "Spa:Enum:ParamId:Format"; + break; + default: + return; + } + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", seq, + "i", direction, + "i", port_id, + "I", pw_protocol_native0_find_type(client, typename), + "i", flags, NULL); + pw_protocol_native0_pod_to_v2(client, param, b); + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_port_use_buffers(void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t n_buffers, struct pw_client_node0_buffer *buffers) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, j; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_PORT_USE_BUFFERS, NULL); + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", seq, + "i", direction, + "i", port_id, + "i", n_buffers, NULL); + + for (i = 0; i < n_buffers; i++) { + struct spa_buffer *buf = buffers[i].buffer; + + spa_pod_builder_add(b, + "i", buffers[i].mem_id, + "i", buffers[i].offset, + "i", buffers[i].size, + "i", i, + "i", buf->n_metas, NULL); + + for (j = 0; j < buf->n_metas; j++) { + struct spa_meta *m = &buf->metas[j]; + spa_pod_builder_add(b, + "I", pw_protocol_native0_type_to_v2(client, spa_type_meta_type, m->type), + "i", m->size, NULL); + } + spa_pod_builder_add(b, "i", buf->n_datas, NULL); + for (j = 0; j < buf->n_datas; j++) { + struct spa_data *d = &buf->datas[j]; + spa_pod_builder_add(b, + "I", pw_protocol_native0_type_to_v2(client, spa_type_data_type, d->type), + "i", SPA_PTR_TO_UINT32(d->data), + "i", d->flags, + "i", d->mapoffset, + "i", d->maxsize, NULL); + } + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_port_command(void *object, + uint32_t direction, + uint32_t port_id, + const struct spa_command *command) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_builder *b; + struct spa_pod_frame f; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_PORT_COMMAND, NULL); + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", direction, + "i", port_id, NULL); + pw_protocol_native0_pod_to_v2(client, (struct spa_pod *)command, b); + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void +client_node_marshal_port_set_io(void *object, + uint32_t seq, + uint32_t direction, + uint32_t port_id, + uint32_t id, + uint32_t memid, + uint32_t offset, + uint32_t size) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_NODE0_PROXY_EVENT_PORT_SET_IO, NULL); + + spa_pod_builder_add_struct(b, + "i", seq, + "i", direction, + "i", port_id, + "I", id, + "i", memid, + "i", offset, + "i", size); + + pw_protocol_native_end_resource(resource, b); +} + + +static int client_node_demarshal_done(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t seq, res; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "i", &seq, + "i", &res) < 0) + return -EINVAL; + + return pw_resource_notify(resource, struct pw_client_node0_proxy_methods, done, 0, seq, res); +} + +static int client_node_demarshal_update(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + struct spa_pod_frame f; + uint32_t change_mask, max_input_ports, max_output_ports, n_params; + const struct spa_pod **params; + uint32_t i; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_push_struct(&prs, &f) < 0 || + spa_pod_parser_get(&prs, + "i", &change_mask, + "i", &max_input_ports, + "i", &max_output_ports, + "i", &n_params, NULL) < 0) + return -EINVAL; + + params = alloca(n_params * sizeof(struct spa_pod *)); + for (i = 0; i < n_params; i++) + if (spa_pod_parser_get(&prs, "O", ¶ms[i], NULL) < 0) + return -EINVAL; + + return pw_resource_notify(resource, struct pw_client_node0_proxy_methods, update, 0, change_mask, + max_input_ports, + max_output_ports, + n_params, + params); +} + +static int client_node_demarshal_port_update(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + struct spa_pod_frame f[2]; + uint32_t i, direction, port_id, change_mask, n_params; + const struct spa_pod **params = NULL; + struct spa_port_info info = { 0 }, *infop = NULL; + struct spa_dict props; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 || + spa_pod_parser_get(&prs, + "i", &direction, + "i", &port_id, + "i", &change_mask, + "i", &n_params, NULL) < 0) + return -EINVAL; + + params = alloca(n_params * sizeof(struct spa_pod *)); + for (i = 0; i < n_params; i++) + if (spa_pod_parser_get(&prs, "O", ¶ms[i], NULL) < 0) + return -EINVAL; + + + if (spa_pod_parser_push_struct(&prs, &f[1]) >= 0) { + infop = &info; + + if (spa_pod_parser_get(&prs, + "i", &info.flags, + "i", &info.rate, + "i", &props.n_items, NULL) < 0) + return -EINVAL; + + 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(&prs, + "s", &props.items[i].key, + "s", &props.items[i].value, + NULL) < 0) + return -EINVAL; + } + } + } + + return pw_resource_notify(resource, struct pw_client_node0_proxy_methods, port_update, 0, direction, + port_id, + change_mask, + n_params, + params, infop); +} + +static int client_node_demarshal_set_active(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + int active; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "b", &active) < 0) + return -EINVAL; + + return pw_resource_notify(resource, struct pw_client_node0_proxy_methods, set_active, 0, active); +} + +static int client_node_demarshal_event_method(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, + "O", &event) < 0) + return -EINVAL; + + return pw_resource_notify(resource, struct pw_client_node0_proxy_methods, event, 0, event); +} + +static int client_node_demarshal_destroy(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + int res; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, NULL) < 0) + return -EINVAL; + + res = pw_resource_notify(resource, struct pw_client_node0_proxy_methods, destroy, 0); + pw_resource_destroy(resource); + return res; +} + +static const struct pw_protocol_native_demarshal pw_protocol_native_client_node_method_demarshal[] = { + { &client_node_demarshal_done, 0, 0 }, + { &client_node_demarshal_update, 0, PW_PROTOCOL_NATIVE_FLAG_REMAP }, + { &client_node_demarshal_port_update, 0, PW_PROTOCOL_NATIVE_FLAG_REMAP }, + { &client_node_demarshal_set_active, 0, 0 }, + { &client_node_demarshal_event_method, 0, PW_PROTOCOL_NATIVE_FLAG_REMAP }, + { &client_node_demarshal_destroy, 0, 0 }, +}; + +static const struct pw_client_node0_proxy_events pw_protocol_native_client_node_event_marshal = { + PW_VERSION_CLIENT_NODE0_PROXY_EVENTS, + &client_node_marshal_add_mem, + &client_node_marshal_transport, + &client_node_marshal_set_param, + &client_node_marshal_event_event, + &client_node_marshal_command, + &client_node_marshal_add_port, + &client_node_marshal_remove_port, + &client_node_marshal_port_set_param, + &client_node_marshal_port_use_buffers, + &client_node_marshal_port_command, + &client_node_marshal_port_set_io, +}; + +static const struct pw_protocol_marshal pw_protocol_native_client_node_marshal = { + PW_TYPE_INTERFACE_ClientNode, + PW_VERSION_CLIENT_NODE0, + PW_CLIENT_NODE0_PROXY_METHOD_NUM, + PW_CLIENT_NODE0_PROXY_EVENT_NUM, + NULL, + &pw_protocol_native_client_node_method_demarshal, + &pw_protocol_native_client_node_event_marshal, + NULL, +}; + +struct pw_protocol *pw_protocol_native_ext_client_node0_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_node_marshal); + + return protocol; +} diff --git a/src/modules/module-client-node/v0/transport.c b/src/modules/module-client-node/v0/transport.c new file mode 100644 index 000000000..97897e255 --- /dev/null +++ b/src/modules/module-client-node/v0/transport.c @@ -0,0 +1,257 @@ +/* PipeWire + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "ext-client-node.h" + +#include "transport.h" + +/** \cond */ + +#define INPUT_BUFFER_SIZE (1<<12) +#define OUTPUT_BUFFER_SIZE (1<<12) + +struct transport { + struct pw_client_node0_transport trans; + + struct pw_memblock *mem; + size_t offset; + + struct pw_client_node0_message current; + uint32_t current_index; +}; +/** \endcond */ + +static size_t area_get_size(struct pw_client_node0_area *area) +{ + size_t size; + size = sizeof(struct pw_client_node0_area); + size += area->max_input_ports * sizeof(struct spa_io_buffers); + size += area->max_output_ports * sizeof(struct spa_io_buffers); + size += sizeof(struct spa_ringbuffer); + size += INPUT_BUFFER_SIZE; + size += sizeof(struct spa_ringbuffer); + size += OUTPUT_BUFFER_SIZE; + return size; +} + +static void transport_setup_area(void *p, struct pw_client_node0_transport *trans) +{ + struct pw_client_node0_area *a; + + trans->area = a = p; + p = SPA_MEMBER(p, sizeof(struct pw_client_node0_area), struct spa_io_buffers); + + trans->inputs = p; + p = SPA_MEMBER(p, a->max_input_ports * sizeof(struct spa_io_buffers), void); + + trans->outputs = p; + p = SPA_MEMBER(p, a->max_output_ports * sizeof(struct spa_io_buffers), void); + + trans->input_buffer = p; + p = SPA_MEMBER(p, sizeof(struct spa_ringbuffer), void); + + trans->input_data = p; + p = SPA_MEMBER(p, INPUT_BUFFER_SIZE, void); + + trans->output_buffer = p; + p = SPA_MEMBER(p, sizeof(struct spa_ringbuffer), void); + + trans->output_data = p; + p = SPA_MEMBER(p, OUTPUT_BUFFER_SIZE, void); +} + +static void transport_reset_area(struct pw_client_node0_transport *trans) +{ + uint32_t i; + struct pw_client_node0_area *a = trans->area; + + for (i = 0; i < a->max_input_ports; i++) { + trans->inputs[i].status = SPA_STATUS_OK; + trans->inputs[i].buffer_id = SPA_ID_INVALID; + } + for (i = 0; i < a->max_output_ports; i++) { + trans->outputs[i].status = SPA_STATUS_OK; + trans->outputs[i].buffer_id = SPA_ID_INVALID; + } + spa_ringbuffer_init(trans->input_buffer); + spa_ringbuffer_init(trans->output_buffer); +} + +static void destroy(struct pw_client_node0_transport *trans) +{ + struct transport *impl = (struct transport *) trans; + + pw_log_debug("transport %p: destroy", trans); + + pw_memblock_free(impl->mem); + free(impl); +} + +static int add_message(struct pw_client_node0_transport *trans, struct pw_client_node0_message *message) +{ + struct transport *impl = (struct transport *) trans; + int32_t filled, avail; + uint32_t size, index; + + if (impl == NULL || message == NULL) + return -EINVAL; + + filled = spa_ringbuffer_get_write_index(trans->output_buffer, &index); + avail = OUTPUT_BUFFER_SIZE - filled; + size = SPA_POD_SIZE(message); + if (avail < (int)size) + return -ENOSPC; + + spa_ringbuffer_write_data(trans->output_buffer, + trans->output_data, OUTPUT_BUFFER_SIZE, + index & (OUTPUT_BUFFER_SIZE - 1), message, size); + spa_ringbuffer_write_update(trans->output_buffer, index + size); + + return 0; +} + +static int next_message(struct pw_client_node0_transport *trans, struct pw_client_node0_message *message) +{ + struct transport *impl = (struct transport *) trans; + int32_t avail; + + if (impl == NULL || message == NULL) + return -EINVAL; + + avail = spa_ringbuffer_get_read_index(trans->input_buffer, &impl->current_index); + if (avail < (int) sizeof(struct pw_client_node0_message)) + return 0; + + spa_ringbuffer_read_data(trans->input_buffer, + trans->input_data, INPUT_BUFFER_SIZE, + impl->current_index & (INPUT_BUFFER_SIZE - 1), + &impl->current, sizeof(struct pw_client_node0_message)); + + if (avail < (int) SPA_POD_SIZE(&impl->current)) + return 0; + + *message = impl->current; + + return 1; +} + +static int parse_message(struct pw_client_node0_transport *trans, void *message) +{ + struct transport *impl = (struct transport *) trans; + uint32_t size; + + if (impl == NULL || message == NULL) + return -EINVAL; + + size = SPA_POD_SIZE(&impl->current); + + spa_ringbuffer_read_data(trans->input_buffer, + trans->input_data, INPUT_BUFFER_SIZE, + impl->current_index & (INPUT_BUFFER_SIZE - 1), message, size); + spa_ringbuffer_read_update(trans->input_buffer, impl->current_index + size); + + return 0; +} + +/** Create a new transport + * \param max_input_ports maximum number of input_ports + * \param max_output_ports maximum number of output_ports + * \return a newly allocated \ref pw_client_node0_transport + * \memberof pw_client_node0_transport + */ +struct pw_client_node0_transport * +pw_client_node0_transport_new(struct pw_core *core, + uint32_t max_input_ports, uint32_t max_output_ports) +{ + struct transport *impl; + struct pw_client_node0_transport *trans; + struct pw_client_node0_area area = { 0 }; + + area.max_input_ports = max_input_ports; + area.n_input_ports = 0; + area.max_output_ports = max_output_ports; + area.n_output_ports = 0; + + impl = calloc(1, sizeof(struct transport)); + if (impl == NULL) + return NULL; + + pw_log_debug("transport %p: new %d %d", impl, max_input_ports, max_output_ports); + + trans = &impl->trans; + impl->offset = 0; + + impl->mem = pw_mempool_alloc(core->pool, + PW_MEMBLOCK_FLAG_READWRITE | + PW_MEMBLOCK_FLAG_MAP | + PW_MEMBLOCK_FLAG_SEAL, + SPA_DATA_MemFd, area_get_size(&area)); + if (impl->mem == NULL) + return NULL; + + memcpy(impl->mem->map->ptr, &area, sizeof(struct pw_client_node0_area)); + transport_setup_area(impl->mem->map->ptr, trans); + transport_reset_area(trans); + + trans->destroy = destroy; + trans->add_message = add_message; + trans->next_message = next_message; + trans->parse_message = parse_message; + + return trans; +} + +struct pw_client_node0_transport * +pw_client_node0_transport_new_from_info(struct pw_client_node0_transport_info *info) +{ + errno = ENOTSUP; + return NULL; +} + +/** Get transport info + * \param trans the transport to get info of + * \param[out] info transport info + * \return 0 on success + * + * Fill \a info with the transport info of \a trans. This information can be + * passed to the client to set up the shared transport. + * + * \memberof pw_client_node0_transport + */ +int pw_client_node0_transport_get_info(struct pw_client_node0_transport *trans, + struct pw_client_node0_transport_info *info) +{ + struct transport *impl = (struct transport *) trans; + + info->memfd = impl->mem->fd; + info->offset = impl->offset; + info->size = impl->mem->size; + + return 0; +} diff --git a/src/modules/module-client-node/v0/transport.h b/src/modules/module-client-node/v0/transport.h new file mode 100644 index 000000000..e3d68a88f --- /dev/null +++ b/src/modules/module-client-node/v0/transport.h @@ -0,0 +1,54 @@ +/* PipeWire + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PIPEWIRE_CLIENT_NODE0_TRANSPORT_H__ +#define __PIPEWIRE_CLIENT_NODE0_TRANSPORT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + +/** information about the transport region \memberof pw_client_node */ +struct pw_client_node0_transport_info { + int memfd; /**< the memfd of the transport area */ + uint32_t offset; /**< offset to map \a memfd at */ + uint32_t size; /**< size of memfd mapping */ +}; + +struct pw_client_node0_transport * +pw_client_node0_transport_new(struct pw_core *core, uint32_t max_input_ports, uint32_t max_output_ports); + +struct pw_client_node0_transport * +pw_client_node0_transport_new_from_info(struct pw_client_node0_transport_info *info); + +int +pw_client_node0_transport_get_info(struct pw_client_node0_transport *trans, + struct pw_client_node0_transport_info *info); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __PIPEWIRE_CLIENT_NODE0_TRANSPORT_H__ */ diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index d328815fc..b1f86e5b2 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -44,6 +44,8 @@ #include #include + +#include "pipewire/map.h" #include "pipewire/private.h" #include "modules/module-protocol-native/connection.h" @@ -67,6 +69,7 @@ static bool debug_messages = 0; #define LOCK_SUFFIXLEN 5 void pw_protocol_native_init(struct pw_protocol *protocol); +void pw_protocol_native0_init(struct pw_protocol *protocol); struct protocol_data { struct pw_module *module; @@ -102,10 +105,15 @@ struct server { struct client_data { struct pw_client *client; struct spa_hook client_listener; + struct spa_source *source; struct pw_protocol_native_connection *connection; + struct spa_hook conn_listener; + unsigned int busy:1; unsigned int need_flush:1; + + struct protocol_compat_v2 compat_v2; }; static void @@ -127,9 +135,20 @@ process_messages(struct client_data *data) const struct pw_protocol_marshal *marshal; uint32_t permissions, required; - if (pw_protocol_native_connection_get_next(conn, &msg) != 1) + res = pw_protocol_native_connection_get_next(conn, &msg); + if (res < 0) { + if (res == -EAGAIN) + break; + goto error; + } + if (res == 0) break; + if (client->core_resource == NULL) { + res = -EPROTO; + goto error; + } + client->recv_seq = msg->seq; pw_log_trace(NAME" %p: got message %d from %u", client->protocol, @@ -193,6 +212,10 @@ invalid_message: spa_debug_pod(0, NULL, (struct spa_pod *)msg->data); pw_client_destroy(client); goto done; +error: + pw_log_error(NAME" %p: client error (%s)", client->protocol, spa_strerror(res)); + pw_client_destroy(client); + goto done; } static void @@ -260,6 +283,8 @@ static void client_free(void *data) pw_loop_destroy_source(client->protocol->core->main_loop, this->source); if (this->connection) pw_protocol_native_connection_destroy(this->connection); + + pw_map_clear(&this->compat_v2.types); } static const struct pw_client_events client_events = { @@ -268,6 +293,32 @@ static const struct pw_client_events client_events = { .busy_changed = client_busy_changed, }; +static void on_start(void *data, uint32_t version) +{ + struct client_data *this = data; + struct pw_client *client = this->client; + struct pw_core *core = client->core; + + pw_log_debug("version %d", version); + + if (pw_global_bind(pw_core_get_global(core), client, + PW_PERM_RWX, version, 0) < 0) + return; + + if (version == 0) + client->compat_v2 = &this->compat_v2; + + if (pw_client_register(client, NULL) < 0) + return; + + return; +} + +static const struct pw_protocol_native_connection_events server_conn_events = { + PW_VERSION_PROTOCOL_NATIVE_CONNECTION_EVENTS, + .start = on_start, +}; + static struct pw_client *client_new(struct server *s, int fd) { struct client_data *this; @@ -323,18 +374,15 @@ static struct pw_client *client_new(struct server *s, int fd) if (this->connection == NULL) goto cleanup_client; + pw_map_init(&this->compat_v2.types, 0, 32); + + pw_protocol_native_connection_add_listener(this->connection, + &this->conn_listener, + &server_conn_events, + this); + pw_client_add_listener(client, &this->client_listener, &client_events, this); - if (pw_global_bind(pw_core_get_global(core), client, - PW_PERM_RWX, PW_VERSION_CORE_PROXY, 0) < 0) - goto cleanup_client; - - if (pw_client_register(client, NULL) < 0) - goto cleanup_client; - - if (pw_global_bind(pw_client_get_global(client), client, - PW_PERM_RWX, PW_VERSION_CLIENT_PROXY, 1) < 0) - goto cleanup_client; return client; @@ -632,7 +680,7 @@ static void on_need_flush(void *data) } } -static const struct pw_protocol_native_connection_events conn_events = { +static const struct pw_protocol_native_connection_events client_conn_events = { PW_VERSION_PROTOCOL_NATIVE_CONNECTION_EVENTS, .need_flush = on_need_flush, }; @@ -662,7 +710,7 @@ static int impl_connect_fd(struct pw_protocol_client *client, int fd, bool do_cl pw_protocol_native_connection_add_listener(impl->connection, &impl->conn_listener, - &conn_events, + &client_conn_events, impl); return 0; @@ -957,6 +1005,7 @@ int pipewire__module_init(struct pw_module *module, const char *args) this->extension = &protocol_ext_impl; pw_protocol_native_init(this); + pw_protocol_native0_init(this); pw_log_debug(NAME" %p: new %d", this, debug_messages); diff --git a/src/modules/module-protocol-native/connection.c b/src/modules/module-protocol-native/connection.c index 116457e39..c3260fd5d 100644 --- a/src/modules/module-protocol-native/connection.c +++ b/src/modules/module-protocol-native/connection.c @@ -58,6 +58,7 @@ struct buffer { struct pw_protocol_native_message msg; bool update; + bool first; }; struct impl { @@ -66,6 +67,9 @@ struct impl { struct buffer in, out; struct spa_pod_builder builder; + + uint32_t version; + size_t hdr_size; }; /** \endcond */ @@ -240,11 +244,15 @@ struct pw_protocol_native_connection *pw_protocol_native_connection_new(struct p this->fd = fd; spa_hook_list_init(&this->listener_list); + impl->hdr_size = HDR_SIZE; + impl->version = 3; + impl->out.buffer_data = calloc(1, MAX_BUFFER_SIZE); impl->out.buffer_maxsize = MAX_BUFFER_SIZE; impl->in.buffer_data = calloc(1, MAX_BUFFER_SIZE); impl->in.buffer_maxsize = MAX_BUFFER_SIZE; impl->in.update = true; + impl->in.first = true; if (impl->out.buffer_data == NULL || impl->in.buffer_data == NULL) goto no_mem; @@ -279,6 +287,7 @@ void pw_protocol_native_connection_destroy(struct pw_protocol_native_connection static int prepare_packet(struct pw_protocol_native_connection *conn, struct buffer *buf) { + struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this); uint8_t *data; size_t size, len; uint32_t *p; @@ -286,18 +295,39 @@ static int prepare_packet(struct pw_protocol_native_connection *conn, struct buf data = buf->buffer_data + buf->offset; size = buf->buffer_size - buf->offset; - if (size < HDR_SIZE) - return HDR_SIZE; + if (size < impl->hdr_size) + return impl->hdr_size; p = (uint32_t *) data; - data += HDR_SIZE; - size -= HDR_SIZE; buf->msg.id = p[0]; buf->msg.opcode = p[1] >> 24; len = p[1] & 0xffffff; - buf->msg.seq = p[2]; - buf->msg.n_fds = p[3]; + + if (buf->first) { + buf->first = false; + if (p[2] != 0) { + pw_log_warn("old version detected"); + impl->version = 0; + impl->hdr_size = 8; + } else { + impl->version = 3; + impl->hdr_size = 16; + } + spa_hook_list_call(&conn->listener_list, + struct pw_protocol_native_connection_events, + start, 0, impl->version); + } + + if (impl->version >= 3) { + buf->msg.seq = p[2]; + buf->msg.n_fds = p[3]; + } else { + buf->msg.seq = 0; + buf->msg.n_fds = 0; + } + data += impl->hdr_size; + size -= impl->hdr_size; buf->msg.fds = &buf->fds[buf->fds_offset]; if (size < len) @@ -306,7 +336,7 @@ static int prepare_packet(struct pw_protocol_native_connection *conn, struct buf buf->msg.size = len; buf->msg.data = data; - buf->offset += HDR_SIZE + len; + buf->offset += impl->hdr_size + len; buf->fds_offset += buf->msg.n_fds; if (buf->offset >= buf->buffer_size) @@ -334,14 +364,16 @@ pw_protocol_native_connection_get_next(struct pw_protocol_native_connection *con const struct pw_protocol_native_message **msg) { struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this); - size_t len; - int res; + int len, res; struct buffer *buf; buf = &impl->in; while (1) { - if ((len = prepare_packet(conn, buf)) == 0) + len = prepare_packet(conn, buf); + if (len < 0) + return len; + if (len == 0) break; if (connection_ensure_size(conn, buf, len) == NULL) @@ -359,10 +391,10 @@ static inline void *begin_write(struct pw_protocol_native_connection *conn, uint uint32_t *p; struct buffer *buf = &impl->out; /* header and size for payload */ - if ((p = connection_ensure_size(conn, buf, HDR_SIZE + size)) == NULL) + if ((p = connection_ensure_size(conn, buf, impl->hdr_size + size)) == NULL) return NULL; - return SPA_MEMBER(p, HDR_SIZE, void); + return SPA_MEMBER(p, impl->hdr_size, void); } static int builder_overflow(void *data, uint32_t size) @@ -393,8 +425,14 @@ pw_protocol_native_connection_begin(struct pw_protocol_native_connection *conn, buf->msg.opcode = opcode; impl->builder = SPA_POD_BUILDER_INIT(NULL, 0); spa_pod_builder_set_callbacks(&impl->builder, &builder_callbacks, impl); - buf->msg.n_fds = 0; - buf->msg.fds = &buf->fds[buf->n_fds]; + if (impl->version >= 3) { + buf->msg.n_fds = 0; + buf->msg.fds = &buf->fds[buf->n_fds]; + } else { + buf->msg.n_fds = buf->n_fds; + buf->msg.fds = &buf->fds[0]; + } + buf->msg.seq = buf->seq; if (msg) *msg = &buf->msg; @@ -410,21 +448,26 @@ pw_protocol_native_connection_end(struct pw_protocol_native_connection *conn, struct buffer *buf = &impl->out; int res; - if ((p = connection_ensure_size(conn, buf, HDR_SIZE + size)) == NULL) + if ((p = connection_ensure_size(conn, buf, impl->hdr_size + size)) == NULL) return -errno; p[0] = buf->msg.id; p[1] = (buf->msg.opcode << 24) | (size & 0xffffff); - p[2] = buf->msg.seq; - p[3] = buf->msg.n_fds; + if (impl->version >= 3) { + p[2] = buf->msg.seq; + p[3] = buf->msg.n_fds; + } - buf->buffer_size += HDR_SIZE + size; - buf->n_fds += buf->msg.n_fds; + buf->buffer_size += impl->hdr_size + size; + if (impl->version >= 3) + buf->n_fds += buf->msg.n_fds; + else + buf->n_fds = buf->msg.n_fds; if (debug_messages) { fprintf(stderr, ">>>>>>>>> out: id:%d op:%d size:%d seq:%d\n", buf->msg.id, buf->msg.opcode, size, buf->msg.seq); - spa_debug_pod(0, NULL, SPA_MEMBER(p, HDR_SIZE, struct spa_pod)); + spa_debug_pod(0, NULL, SPA_MEMBER(p, impl->hdr_size, struct spa_pod)); } buf->seq = (buf->seq + 1) & SPA_ASYNC_SEQ_MASK; diff --git a/src/modules/module-protocol-native/connection.h b/src/modules/module-protocol-native/connection.h index 039e35502..c4eb476b0 100644 --- a/src/modules/module-protocol-native/connection.h +++ b/src/modules/module-protocol-native/connection.h @@ -43,6 +43,8 @@ struct pw_protocol_native_connection_events { void (*error) (void *data, int error); void (*need_flush) (void *data); + + void (*start) (void *data, uint32_t version); }; /** \class pw_protocol_native_connection diff --git a/src/modules/module-protocol-native/protocol-native.c b/src/modules/module-protocol-native/protocol-native.c index 1d7503a51..8c55bf563 100644 --- a/src/modules/module-protocol-native/protocol-native.c +++ b/src/modules/module-protocol-native/protocol-native.c @@ -108,7 +108,7 @@ static struct pw_registry_proxy * core_method_marshal_get_registry(void *object, struct pw_proxy *res; uint32_t new_id; - res = pw_proxy_new(object, PW_TYPE_INTERFACE_Registry, user_data_size); + res = pw_proxy_new(object, PW_TYPE_INTERFACE_Registry, version, user_data_size); if (res == NULL) return NULL; @@ -200,7 +200,7 @@ core_method_marshal_create_object(void *object, struct pw_proxy *res; uint32_t new_id; - res = pw_proxy_new(object, type, user_data_size); + res = pw_proxy_new(object, type, version, user_data_size); if (res == NULL) return NULL; @@ -1833,7 +1833,7 @@ static void * registry_marshal_bind(void *object, uint32_t id, struct pw_proxy *res; uint32_t new_id; - res = pw_proxy_new(object, type, user_data_size); + res = pw_proxy_new(object, type, version, user_data_size); if (res == NULL) return NULL; diff --git a/src/modules/module-protocol-native/v0/interfaces.h b/src/modules/module-protocol-native/v0/interfaces.h new file mode 100644 index 000000000..8622a1a64 --- /dev/null +++ b/src/modules/module-protocol-native/v0/interfaces.h @@ -0,0 +1,532 @@ +/* PipeWire + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PIPEWIRE_INTERFACES_V0_H +#define PIPEWIRE_INTERFACES_V0_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include + +/** Core */ + +#define PW_VERSION_CORE_V0 0 + +#define PW_CORE_PROXY_V0_METHOD_HELLO 0 +#define PW_CORE_PROXY_V0_METHOD_UPDATE_TYPES 1 +#define PW_CORE_PROXY_V0_METHOD_SYNC 2 +#define PW_CORE_PROXY_V0_METHOD_GET_REGISTRY 3 +#define PW_CORE_PROXY_V0_METHOD_CLIENT_UPDATE 4 +#define PW_CORE_PROXY_V0_METHOD_PERMISSIONS 5 +#define PW_CORE_PROXY_V0_METHOD_CREATE_OBJECT 6 +#define PW_CORE_PROXY_V0_METHOD_DESTROY 7 +#define PW_CORE_PROXY_V0_METHOD_NUM 8 + +#if 0 +/** + * Key to update default permissions of globals without specific + * permissions. value is "[r][w][x]" */ +#define PW_CORE_PROXY_PERMISSIONS_DEFAULT "permissions.default" + +/** + * Key to update specific permissions of a global. If the global + * did not have specific permissions, it will first be assigned + * the default permissions before it is updated. + * Value is ":[r][w][x]"*/ +#define PW_CORE_PROXY_PERMISSIONS_GLOBAL "permissions.global" + +/** + * Key to update specific permissions of all existing globals. + * This is equivalent to using \ref PW_CORE_PROXY_PERMISSIONS_GLOBAL + * on each global id individually that did not have specific + * permissions. + * Value is "[r][w][x]" */ +#define PW_CORE_PROXY_PERMISSIONS_EXISTING "permissions.existing" + +#define PW_LINK_OUTPUT_NODE_ID "link.output_node.id" +#define PW_LINK_OUTPUT_PORT_ID "link.output_port.id" +#define PW_LINK_INPUT_NODE_ID "link.input_node.id" +#define PW_LINK_INPUT_PORT_ID "link.input_port.id" +#endif + +/** + * \struct pw_core_proxy_v0_methods + * \brief Core methods + * + * The core global object. This is a singleton object used for + * creating new objects in the remote PipeWire intance. It is + * also used for internal features. + */ +struct pw_core_proxy_v0_methods { +#define PW_VERSION_CORE_PROXY_V0_METHODS 0 + uint32_t version; + /** + * Start a conversation with the server. This will send + * the core info and server types. + * + * All the existing resources for the client (except the core + * resource) will be destroyed. + */ + void (*hello) (void *object); + /** + * Update the type map + * + * Send a type map update to the PipeWire server. The server uses this + * information to keep a mapping between client types and the server types. + * \param first_id the id of the first type + * \param types the types as a string + * \param n_types the number of types + */ + void (*update_types) (void *object, + uint32_t first_id, + const char **types, + uint32_t n_types); + /** + * Do server roundtrip + * + * Ask the server to emit the 'done' event with \a id. + * Since methods are handled in-order and events are delivered + * in-order, this can be used as a barrier to ensure all previous + * methods and the resulting events have been handled. + * \param seq the sequence number passed to the done event + */ + void (*sync) (void *object, uint32_t seq); + /** + * Get the registry object + * + * Create a registry object that allows the client to list and bind + * the global objects available from the PipeWire server + * \param version the client proxy id + * \param id the client proxy id + */ + void (*get_registry) (void *object, uint32_t version, uint32_t new_id); + /** + * Update the client properties + * \param props the new client properties + */ + void (*client_update) (void *object, const struct spa_dict *props); + /** + * Manage the permissions of the global objects + * + * Update the permissions of the global objects using the + * dictionary with properties. + * + * Globals can use the default permissions or can have specific + * permissions assigned to them. + * + * \param id the global id to change + * \param props dictionary with permission properties + */ + void (*permissions) (void *object, const struct spa_dict *props); + /** + * Create a new object on the PipeWire server from a factory. + * Use a \a factory_name of "client-node" to create a + * \ref pw_client_node. + * + * \param factory_name the factory name to use + * \param type the interface to bind to + * \param version the version of the interface + * \param props extra properties + * \param new_id the client proxy id + */ + void (*create_object) (void *object, + const char *factory_name, + uint32_t type, + uint32_t version, + const struct spa_dict *props, + uint32_t new_id); + + /** + * Destroy an object id + * + * \param id the object id to destroy + */ + void (*destroy) (void *object, uint32_t id); +}; + +#define PW_CORE_PROXY_V0_EVENT_UPDATE_TYPES 0 +#define PW_CORE_PROXY_V0_EVENT_DONE 1 +#define PW_CORE_PROXY_V0_EVENT_ERROR 2 +#define PW_CORE_PROXY_V0_EVENT_REMOVE_ID 3 +#define PW_CORE_PROXY_V0_EVENT_INFO 4 +#define PW_CORE_PROXY_V0_EVENT_NUM 5 + +/** \struct pw_core_proxy_v0_events + * \brief Core events + * \ingroup pw_core_interface The pw_core interface + */ +struct pw_core_proxy_v0_events { +#define PW_VERSION_CORE_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Update the type map + * + * Send a type map update to the client. The client uses this + * information to keep a mapping between server types and the client types. + * \param first_id the id of the first type + * \param types the types as a string + * \param n_types the number of \a types + */ + void (*update_types) (void *object, + uint32_t first_id, + const char **types, + uint32_t n_types); + /** + * Emit a done event + * + * The done event is emited as a result of a sync method with the + * same sequence number. + * \param seq the sequence number passed to the sync method call + */ + void (*done) (void *object, uint32_t seq); + /** + * Fatal error event + * + * The error event is sent out when a fatal (non-recoverable) + * error has occurred. The id argument is the object where + * the error occurred, most often in response to a request to that + * object. The message is a brief description of the error, + * for (debugging) convenience. + * \param id object where the error occurred + * \param res error code + * \param error error description + */ + void (*error) (void *object, uint32_t id, int res, const char *error, ...); + /** + * Remove an object ID + * + * This event is used internally by the object ID management + * logic. When a client deletes an object, the server will send + * this event to acknowledge that it has seen the delete request. + * When the client receives this event, it will know that it can + * safely reuse the object ID. + * \param id deleted object ID + */ + void (*remove_id) (void *object, uint32_t id); + /** + * Notify new core info + * + * \param info new core info + */ + void (*info) (void *object, struct pw_core_info *info); +}; + +#define pw_core_resource_v0_update_types(r,...) pw_resource_notify(r,struct pw_core_proxy_v0_events,update_types,__VA_ARGS__) +#define pw_core_resource_v0_done(r,...) pw_resource_notify(r,struct pw_core_proxy_v0_events,done,__VA_ARGS__) +#define pw_core_resource_v0_error(r,...) pw_resource_notify(r,struct pw_core_proxy_v0_events,error,__VA_ARGS__) +#define pw_core_resource_v0_remove_id(r,...) pw_resource_notify(r,struct pw_core_proxy_v0_events,remove_id,__VA_ARGS__) +#define pw_core_resource_v0_info(r,...) pw_resource_notify(r,struct pw_core_proxy_v0_events,info,__VA_ARGS__) + + +#define PW_VERSION_REGISTRY_V0 0 + +/** \page page_registry Registry + * + * \section page_registry_overview Overview + * + * The registry object is a singleton object that keeps track of + * global objects on the PipeWire instance. See also \ref page_global. + * + * Global objects typically represent an actual object in PipeWire + * (for example, a module or node) or they are singleton + * objects such as the core. + * + * When a client creates a registry object, the registry object + * will emit a global event for each global currently in the + * registry. Globals come and go as a result of device hotplugs or + * reconfiguration or other events, and the registry will send out + * global and global_remove events to keep the client up to date + * with the changes. To mark the end of the initial burst of + * events, the client can use the pw_core.sync methosd immediately + * after calling pw_core.get_registry. + * + * A client can bind to a global object by using the bind + * request. This creates a client-side proxy that lets the object + * emit events to the client and lets the client invoke methods on + * the object. See \ref page_proxy + * + * Clients can also change the permissions of the global objects that + * it can see. This is interesting when you want to configure a + * pipewire session before handing it to another application. You + * can, for example, hide certain existing or new objects or limit + * the access permissions on an object. + */ +#define PW_REGISTRY_PROXY_V0_METHOD_BIND 0 +#define PW_REGISTRY_PROXY_V0_METHOD_NUM 1 + +/** Registry methods */ +struct pw_registry_proxy_v0_methods { +#define PW_VERSION_REGISTRY_PROXY_V0_METHODS 0 + uint32_t version; + /** + * Bind to a global object + * + * Bind to the global object with \a id and use the client proxy + * with new_id as the proxy. After this call, methods can be + * send to the remote global object and events can be received + * + * \param id the global id to bind to + * \param type the interface type to bind to + * \param version the interface version to use + * \param new_id the client proxy to use + */ + void (*bind) (void *object, uint32_t id, uint32_t type, uint32_t version, uint32_t new_id); +}; + +#define PW_REGISTRY_PROXY_V0_EVENT_GLOBAL 0 +#define PW_REGISTRY_PROXY_V0_EVENT_GLOBAL_REMOVE 1 +#define PW_REGISTRY_PROXY_V0_EVENT_NUM 2 + +/** Registry events */ +struct pw_registry_proxy_v0_events { +#define PW_VERSION_REGISTRY_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Notify of a new global object + * + * The registry emits this event when a new global object is + * available. + * + * \param id the global object id + * \param parent_id the parent global id + * \param permissions the permissions of the object + * \param type the type of the interface + * \param version the version of the interface + * \param props extra properties of the global + */ + void (*global) (void *object, uint32_t id, uint32_t parent_id, + uint32_t permissions, uint32_t type, uint32_t version, + const struct spa_dict *props); + /** + * Notify of a global object removal + * + * Emited when a global object was removed from the registry. + * If the client has any bindings to the global, it should destroy + * those. + * + * \param id the id of the global that was removed + */ + void (*global_remove) (void *object, uint32_t id); +}; + +#define pw_registry_resource_v0_global(r,...) pw_resource_notify(r,struct pw_registry_proxy_v0_events,global,__VA_ARGS__) +#define pw_registry_resource_v0_global_remove(r,...) pw_resource_notify(r,struct pw_registry_proxy_v0_events,global_remove,__VA_ARGS__) + + +#define PW_VERSION_MODULE_V0 0 + +#define PW_MODULE_PROXY_V0_EVENT_INFO 0 +#define PW_MODULE_PROXY_V0_EVENT_NUM 1 + +/** Module events */ +struct pw_module_proxy_v0_events { +#define PW_VERSION_MODULE_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Notify module info + * + * \param info info about the module + */ + void (*info) (void *object, struct pw_module_info *info); +}; + +#define pw_module_resource_v0_info(r,...) pw_resource_notify(r,struct pw_module_proxy_v0_events,info,__VA_ARGS__) + +#define PW_VERSION_NODE_V0 0 + +#define PW_NODE_PROXY_V0_EVENT_INFO 0 +#define PW_NODE_PROXY_V0_EVENT_PARAM 1 +#define PW_NODE_PROXY_V0_EVENT_NUM 2 + +/** Node events */ +struct pw_node_proxy_v0_events { +#define PW_VERSION_NODE_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Notify node info + * + * \param info info about the node + */ + void (*info) (void *object, struct pw_node_info *info); + /** + * Notify a node param + * + * Event emited as a result of the enum_params method. + * + * \param id the param id + * \param index the param index + * \param next the param index of the next param + * \param param the parameter + */ + void (*param) (void *object, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param); +}; + +#define pw_node_resource_v0_info(r,...) pw_resource_notify(r,struct pw_node_proxy_v0_events,info,__VA_ARGS__) +#define pw_node_resource_v0_param(r,...) pw_resource_notify(r,struct pw_node_proxy_v0_events,param,__VA_ARGS__) + +#define PW_NODE_PROXY_V0_METHOD_ENUM_PARAMS 0 +#define PW_NODE_PROXY_V0_METHOD_NUM 1 + +/** Node methods */ +struct pw_node_proxy_v0_methods { +#define PW_VERSION_NODE_PROXY_V0_METHODS 0 + uint32_t version; + /** + * Enumerate node parameters + * + * Start enumeration of node parameters. For each param, a + * param event will be emited. + * + * \param id the parameter id to enum or SPA_ID_INVALID for all + * \param start the start index or 0 for the first param + * \param num the maximum number of params to retrieve + * \param filter a param filter or NULL + */ + void (*enum_params) (void *object, uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter); +}; + +#define PW_VERSION_PORT_V0 0 + +#define PW_PORT_PROXY_V0_EVENT_INFO 0 +#define PW_PORT_PROXY_V0_EVENT_PARAM 1 +#define PW_PORT_PROXY_V0_EVENT_NUM 2 + +/** Port events */ +struct pw_port_proxy_v0_events { +#define PW_VERSION_PORT_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Notify port info + * + * \param info info about the port + */ + void (*info) (void *object, struct pw_port_info *info); + /** + * Notify a port param + * + * Event emited as a result of the enum_params method. + * + * \param id the param id + * \param index the param index + * \param next the param index of the next param + * \param param the parameter + */ + void (*param) (void *object, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param); +}; + +#define pw_port_resource_v0_info(r,...) pw_resource_notify(r,struct pw_port_proxy_v0_events,info,__VA_ARGS__) +#define pw_port_resource_v0_param(r,...) pw_resource_notify(r,struct pw_port_proxy_v0_events,param,__VA_ARGS__) + +#define PW_PORT_PROXY_V0_METHOD_ENUM_PARAMS 0 +#define PW_PORT_PROXY_V0_METHOD_NUM 1 + +/** Port methods */ +struct pw_port_proxy_v0_methods { +#define PW_VERSION_PORT_PROXY_V0_METHODS 0 + uint32_t version; + /** + * Enumerate port parameters + * + * Start enumeration of port parameters. For each param, a + * param event will be emited. + * + * \param id the parameter id to enumerate + * \param start the start index or 0 for the first param + * \param num the maximum number of params to retrieve + * \param filter a param filter or NULL + */ + void (*enum_params) (void *object, uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter); +}; + +#define PW_VERSION_FACTORY_V0 0 + +#define PW_FACTORY_PROXY_V0_EVENT_INFO 0 +#define PW_FACTORY_PROXY_V0_EVENT_NUM 1 + +/** Factory events */ +struct pw_factory_proxy_v0_events { +#define PW_VERSION_FACTORY_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Notify factory info + * + * \param info info about the factory + */ + void (*info) (void *object, struct pw_factory_info *info); +}; + +#define pw_factory_resource_v0_info(r,...) pw_resource_notify(r,struct pw_factory_proxy_v0_events,info,__VA_ARGS__) + +#define PW_VERSION_CLIENT_V0 0 + +#define PW_CLIENT_PROXY_V0_EVENT_INFO 0 +#define PW_CLIENT_PROXY_V0_EVENT_NUM 1 + +/** Client events */ +struct pw_client_proxy_v0_events { +#define PW_VERSION_CLIENT_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Notify client info + * + * \param info info about the client + */ + void (*info) (void *object, struct pw_client_info *info); +}; + +#define pw_client_resource_v0_info(r,...) pw_resource_notify(r,struct pw_client_proxy_v0_events,info,__VA_ARGS__) + + +#define PW_VERSION_LINK_V0 0 + +#define PW_LINK_PROXY_V0_EVENT_INFO 0 +#define PW_LINK_PROXY_V0_EVENT_NUM 1 + +/** Link events */ +struct pw_link_proxy_v0_events { +#define PW_VERSION_LINK_PROXY_V0_EVENTS 0 + uint32_t version; + /** + * Notify link info + * + * \param info info about the link + */ + void (*info) (void *object, struct pw_link_info *info); +}; + +#define pw_link_resource_v0_info(r,...) pw_resource_notify(r,struct pw_link_proxy_v0_events,info,__VA_ARGS__) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PIPEWIRE_INTERFACES_V0_H */ diff --git a/src/modules/module-protocol-native/v0/protocol-native.c b/src/modules/module-protocol-native/v0/protocol-native.c new file mode 100644 index 000000000..5e59d36bd --- /dev/null +++ b/src/modules/module-protocol-native/v0/protocol-native.c @@ -0,0 +1,1186 @@ +/* PipeWire + * Copyright (C) 2017 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "spa/pod/parser.h" +#include "spa/debug/pod.h" + +#include "pipewire/pipewire.h" +#include "pipewire/private.h" +#include "pipewire/protocol.h" +#include "pipewire/resource.h" +#include "extensions/protocol-native.h" + +#include "interfaces.h" +#include "typemap.h" + +#include "../connection.h" + +#define PW_PROTOCOL_NATIVE_FLAG_REMAP (1<<0) + +SPA_EXPORT +uint32_t pw_protocol_native0_find_type(struct pw_client *client, const char *type) +{ + uint32_t i; + for (i = 0; i < SPA_N_ELEMENTS(type_map); i++) { + if (!strcmp(type_map[i].type, type)) + return i; + } + return SPA_ID_INVALID; +} + +static void +update_types_server(struct pw_resource *resource) +{ + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i; + + b = pw_protocol_native_begin_resource(resource, PW_CORE_PROXY_V0_EVENT_UPDATE_TYPES, NULL); + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", 0, + "i", SPA_N_ELEMENTS(type_map), NULL); + + for (i = 0; i < SPA_N_ELEMENTS(type_map); i++) { + spa_pod_builder_add(b, "s", type_map[i].type, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + + +static void core_marshal_info(void *object, const struct pw_core_info *info) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + uint32_t i, n_items; + struct spa_pod_frame f; + struct pw_protocol_native_message *msg; + + b = pw_protocol_native_begin_resource(resource, PW_CORE_PROXY_V0_EVENT_INFO, &msg); + + if (msg->seq == 0) { + update_types_server(resource); + b = pw_protocol_native_begin_resource(resource, PW_CORE_PROXY_V0_EVENT_INFO, &msg); + } + + n_items = info->props ? info->props->n_items : 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", info->id, + "l", info->change_mask, + "s", info->user_name, + "s", info->host_name, + "s", info->version, + "s", info->name, + "i", info->cookie, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void core_marshal_done(void *object, uint32_t id, int seq) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CORE_PROXY_V0_EVENT_DONE, NULL); + + spa_pod_builder_add_struct(b, "i", seq); + + pw_protocol_native_end_resource(resource, b); +} + +static void core_marshal_error(void *object, uint32_t id, int seq, int res, const char *error) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CORE_PROXY_V0_EVENT_ERROR, NULL); + + spa_pod_builder_add_struct(b, + "i", id, + "i", res, + "s", error); + + pw_protocol_native_end_resource(resource, b); +} + +static void core_marshal_remove_id(void *object, uint32_t id) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_CORE_PROXY_V0_EVENT_REMOVE_ID, NULL); + + spa_pod_builder_add_struct(b, "i", id); + + pw_protocol_native_end_resource(resource, b); +} + +static int core_demarshal_client_update(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_dict props; + struct spa_pod_parser prs; + struct spa_pod_frame f; + uint32_t i; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_push_struct(&prs, &f) < 0 || + spa_pod_parser_get(&prs, + "i", &props.n_items, NULL) < 0) + return -EINVAL; + + props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); + for (i = 0; i < props.n_items; i++) { + if (spa_pod_parser_get(&prs, + "s", &props.items[i].key, + "s", &props.items[i].value, + NULL) < 0) + return -EINVAL; + } + pw_client_update_properties(resource->client, &props); + return 0; +} + +static int core_demarshal_permissions(void *object, const struct pw_protocol_native_message *msg) +{ + //struct pw_resource *resource = object; + struct spa_dict props; + struct spa_pod_parser prs; + struct spa_pod_frame f; + uint32_t i; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_push_struct(&prs, &f) < 0 || + spa_pod_parser_get(&prs, "i", &props.n_items, NULL) < 0) + return -EINVAL; + + props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); + for (i = 0; i < props.n_items; i++) { + if (spa_pod_parser_get(&prs, + "s", &props.items[i].key, + "s", &props.items[i].value, + NULL) < 0) + return -EINVAL; + } + /* FIXME */ + //return pw_resource_notify(resource, struct pw_client_proxy_methods, update_permissions, 0, + // n_permissions, permissions); + return 0; +} + +static int core_demarshal_hello(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + void *ptr; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "P", &ptr) < 0) + return -EINVAL; + + return pw_resource_notify(resource, struct pw_core_proxy_methods, hello, 0, 2); +} + +static int core_demarshal_sync(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t seq; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, "i", &seq) < 0) + return -EINVAL; + + return pw_resource_notify(resource, struct pw_core_proxy_methods, sync, 0, 0, seq); +} + +static int core_demarshal_get_registry(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + int32_t version, new_id; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "i", &version, + "i", &new_id) < 0) + return -EINVAL; + + return pw_resource_notify(resource, struct pw_core_proxy_methods, get_registry, 0, version, new_id); +} + +SPA_EXPORT +uint32_t pw_protocol_native0_type_from_v2(struct pw_client *client, uint32_t type) +{ + void *t; + uint32_t index; + struct protocol_compat_v2 *compat_v2 = client->compat_v2; + + if ((t = pw_map_lookup(&compat_v2->types, type)) == NULL) + return SPA_ID_INVALID; + + index = PW_MAP_PTR_TO_ID(t); + if (index >= SPA_N_ELEMENTS(type_map)) + return SPA_ID_INVALID; + + return type_map[index].id; +} + +SPA_EXPORT +uint32_t pw_protocol_native0_type_to_v2(struct pw_client *client, + const struct spa_type_info *info, uint32_t type) +{ + uint32_t i; + const char *name; + + /** find full name of type in type_info */ + if ((name = spa_debug_type_find_name(info, type)) == NULL) + return SPA_ID_INVALID; + + /* match name to type table and return index */ + for (i = 0; i < SPA_N_ELEMENTS(type_map); i++) { + if (type_map[i].name != NULL && !strcmp(type_map[i].name, name)) + return i; + } + return SPA_ID_INVALID; +} + +struct spa_pod_prop_body0 { + uint32_t key; +#define SPA_POD_PROP0_RANGE_NONE 0 /**< no range */ +#define SPA_POD_PROP0_RANGE_MIN_MAX 1 /**< property has range */ +#define SPA_POD_PROP0_RANGE_STEP 2 /**< property has range with step */ +#define SPA_POD_PROP0_RANGE_ENUM 3 /**< property has enumeration */ +#define SPA_POD_PROP0_RANGE_FLAGS 4 /**< property has flags */ +#define SPA_POD_PROP0_RANGE_MASK 0xf /**< mask to select range type */ +#define SPA_POD_PROP0_FLAG_UNSET (1 << 4) /**< property value is unset */ +#define SPA_POD_PROP0_FLAG_OPTIONAL (1 << 5) /**< property value is optional */ +#define SPA_POD_PROP0_FLAG_READONLY (1 << 6) /**< property is readonly */ +#define SPA_POD_PROP0_FLAG_DEPRECATED (1 << 7) /**< property is deprecated */ +#define SPA_POD_PROP0_FLAG_INFO (1 << 8) /**< property is informational and is not + * used when filtering */ + uint32_t flags; + struct spa_pod value; + /* array with elements of value.size follows, + * first element is value/default, rest are alternatives */ +}; + +/* v2 iterates object as containing spa_pod */ +#define SPA_POD_OBJECT_BODY_FOREACH0(body, size, iter) \ + for ((iter) = SPA_MEMBER((body), sizeof(struct spa_pod_object_body), struct spa_pod); \ + spa_pod_is_inside(body, size, iter); \ + (iter) = spa_pod_next(iter)) + +#define SPA_POD_PROP_ALTERNATIVE_FOREACH0(body, _size, iter) \ + for ((iter) = SPA_MEMBER((body), (body)->value.size + \ + sizeof(struct spa_pod_prop_body0), __typeof__(*iter)); \ + (iter) <= SPA_MEMBER((body), (_size)-(body)->value.size, __typeof__(*iter)); \ + (iter) = SPA_MEMBER((iter), (body)->value.size, __typeof__(*iter))) + +static int remap_from_v2(uint32_t type, void *body, uint32_t size, struct pw_client *client, + struct spa_pod_builder *builder) +{ + int res; + + switch (type) { + case SPA_TYPE_Id: + spa_pod_builder_id(builder, pw_protocol_native0_type_from_v2(client, *(int32_t*) body)); + break; + + /** choice was props in v2 */ + case SPA_TYPE_Choice: + { + struct spa_pod_prop_body0 *b = body; + struct spa_pod_frame f; + void *alt; + uint32_t key = pw_protocol_native0_type_from_v2(client, b->key); + enum spa_choice_type type; + + spa_pod_builder_prop(builder, key, 0); + + switch (b->flags & SPA_POD_PROP0_RANGE_MASK) { + default: + case SPA_POD_PROP0_RANGE_NONE: + type = SPA_CHOICE_None; + break; + case SPA_POD_PROP0_RANGE_MIN_MAX: + type = SPA_CHOICE_Range; + break; + case SPA_POD_PROP0_RANGE_STEP: + type = SPA_CHOICE_Step; + break; + case SPA_POD_PROP0_RANGE_ENUM: + type = SPA_CHOICE_Enum; + break; + case SPA_POD_PROP0_RANGE_FLAGS: + type = SPA_CHOICE_Flags; + break; + } + if (!SPA_FLAG_IS_SET(b->flags, SPA_POD_PROP0_FLAG_UNSET)) + type = SPA_CHOICE_None; + + spa_pod_builder_push_choice(builder, &f, type, 0); + + if (b->value.type == SPA_TYPE_Id) { + uint32_t id; + if ((res = spa_pod_get_id(&b->value, &id)) < 0) + return res; + spa_pod_builder_id(builder, pw_protocol_native0_type_from_v2(client, id)); + SPA_POD_PROP_ALTERNATIVE_FOREACH0(b, size, alt) + if ((res = remap_from_v2(b->value.type, alt, b->value.size, client, builder)) < 0) + return res; + } else { + spa_pod_builder_raw(builder, &b->value, size - sizeof(struct spa_pod)); + } + + spa_pod_builder_pop(builder, &f); + + break; + } + case SPA_TYPE_Object: + { + struct spa_pod_object_body *b = body; + struct spa_pod *p; + struct spa_pod_frame f; + uint32_t type, count = 0; + + /* type and id are switched */ + type = pw_protocol_native0_type_from_v2(client, b->id), + spa_pod_builder_push_object(builder, &f, type, + pw_protocol_native0_type_from_v2(client, b->type)); + + /* object contained pods in v2 */ + SPA_POD_OBJECT_BODY_FOREACH0(b, size, p) { + if (type == SPA_TYPE_OBJECT_Format && count < 2) { + uint32_t id; + if (spa_pod_get_id(p, &id) < 0) + continue; + id = pw_protocol_native0_type_from_v2(client, id); + + if (count == 0) { + spa_pod_builder_prop(builder, SPA_FORMAT_mediaType, 0); + spa_pod_builder_id(builder, id); + } + if (count == 1) { + spa_pod_builder_prop(builder, SPA_FORMAT_mediaSubtype, 0); + spa_pod_builder_id(builder, id); + } + count++; + continue; + } + if ((res = remap_from_v2(p->type, + SPA_POD_BODY(p), + p->size, + client, builder)) < 0) + return res; + } + spa_pod_builder_pop(builder, &f); + break; + } + case SPA_TYPE_Struct: + { + struct spa_pod *b = body, *p; + struct spa_pod_frame f; + + spa_pod_builder_push_struct(builder, &f); + SPA_POD_FOREACH(b, size, p) + if ((res = remap_from_v2(p->type, SPA_POD_BODY(p), p->size, client, builder)) < 0) + return res; + spa_pod_builder_pop(builder, &f); + break; + } + default: + break; + } + return 0; +} + +static int remap_to_v2(struct pw_client *client, const struct spa_type_info *info, + uint32_t type, void *body, uint32_t size, + struct spa_pod_builder *builder) +{ + int res; + + switch (type) { + case SPA_TYPE_Id: + spa_pod_builder_id(builder, pw_protocol_native0_type_to_v2(client, info, *(int32_t*) body)); + break; + + case SPA_TYPE_Object: + { + struct spa_pod_object_body *b = body; + struct spa_pod_prop *p; + struct spa_pod_frame f[2]; + uint32_t type; + const struct spa_type_info *ti, *ii; + + ti = spa_debug_type_find(info, b->type); + ii = ti ? spa_debug_type_find(ti->values, 0) : NULL; + + pw_log_debug("type:%d id:%d", b->type, b->id); + + if (b->type == SPA_TYPE_COMMAND_Node) { + spa_pod_builder_push_object(builder, &f[0], 0, + pw_protocol_native0_type_to_v2(client, ii ? ii->values : NULL, b->id)); + } else { + ii = ii ? spa_debug_type_find(ii->values, b->id) : NULL; + /* type and id are switched */ + type = pw_protocol_native0_type_to_v2(client, info, b->type), + spa_pod_builder_push_object(builder, &f[0], + pw_protocol_native0_type_to_v2(client, ii ? ii->values : NULL, b->id), type); + } + + + info = ti ? ti->values : info; + + SPA_POD_OBJECT_BODY_FOREACH(b, size, p) { + uint32_t key, flags; + uint32_t n_vals, choice; + struct spa_pod *values; + + ii = spa_debug_type_find(info, p->key); + + values = spa_pod_get_values(&p->value, &n_vals, &choice); + + if (b->type == SPA_TYPE_OBJECT_Format && + (p->key == SPA_FORMAT_mediaType || + p->key == SPA_FORMAT_mediaSubtype)) { + uint32_t val; + + if (spa_pod_get_id(values, &val) < 0) + continue; + spa_pod_builder_id(builder, + pw_protocol_native0_type_to_v2(client, ii ? ii->values : NULL, val)); + continue; + } + + flags = 0; + switch(choice) { + case SPA_CHOICE_None: + flags |= SPA_POD_PROP0_RANGE_NONE; + break; + case SPA_CHOICE_Range: + flags |= SPA_POD_PROP0_RANGE_MIN_MAX | SPA_POD_PROP0_FLAG_UNSET; + break; + case SPA_CHOICE_Step: + flags |= SPA_POD_PROP0_RANGE_STEP | SPA_POD_PROP0_FLAG_UNSET; + break; + case SPA_CHOICE_Enum: + flags |= SPA_POD_PROP0_RANGE_ENUM | SPA_POD_PROP0_FLAG_UNSET; + break; + case SPA_CHOICE_Flags: + flags |= SPA_POD_PROP0_RANGE_FLAGS | SPA_POD_PROP0_FLAG_UNSET; + break; + } + + key = pw_protocol_native0_type_to_v2(client, info, p->key); + + spa_pod_builder_push_choice(builder, &f[1], key, flags); + + if (values->type == SPA_TYPE_Id) { + uint32_t i, *id = SPA_POD_BODY(values); + + for (i = 0; i < n_vals; i++) { + spa_pod_builder_id(builder, + pw_protocol_native0_type_to_v2(client, ii ? ii->values : NULL, id[i])); + } + + } else { + spa_pod_builder_raw(builder, values, sizeof(struct spa_pod) + n_vals * values->size); + } + spa_pod_builder_pop(builder, &f[1]); + } + spa_pod_builder_pop(builder, &f[0]); + break; + } + case SPA_TYPE_Struct: + { + struct spa_pod *b = body, *p; + struct spa_pod_frame f; + + spa_pod_builder_push_struct(builder, &f); + SPA_POD_FOREACH(b, size, p) + if ((res = remap_to_v2(client, info, p->type, SPA_POD_BODY(p), p->size, builder)) < 0) + return res; + spa_pod_builder_pop(builder, &f); + break; + } + default: + break; + } + return 0; +} + + + + +SPA_EXPORT +struct spa_pod * pw_protocol_native0_pod_from_v2(struct pw_client *client, const struct spa_pod *pod) +{ + uint8_t buffer[4096]; + struct spa_pod *copy; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, 4096); + int res; + + if (pod == NULL) + return NULL; + + if ((res = remap_from_v2(SPA_POD_TYPE(pod), + SPA_POD_BODY(pod), + SPA_POD_BODY_SIZE(pod), + client, &b)) < 0) { + errno = -res; + return NULL; + } + copy = spa_pod_copy(b.data); + spa_debug_pod(0, NULL, copy); + return copy; +} + +SPA_EXPORT +int pw_protocol_native0_pod_to_v2(struct pw_client *client, const struct spa_pod *pod, + struct spa_pod_builder *b) +{ + int res; + + if (pod == NULL) { + spa_pod_builder_none(b); + return 0; + } + + if ((res = remap_to_v2(client, pw_type_info(), + SPA_POD_TYPE(pod), + SPA_POD_BODY(pod), + SPA_POD_BODY_SIZE(pod), + b)) < 0) { + return -res; + } + return 0; +} + +static int core_demarshal_create_object(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct pw_client *client = pw_resource_get_client(resource); + struct spa_pod_parser prs; + struct spa_pod_frame f; + uint32_t version, type, new_id, i; + const char *factory_name; + struct spa_dict props; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_push_struct(&prs, &f) < 0 || + spa_pod_parser_get(&prs, + "s", &factory_name, + "I", &type, + "i", &version, + "i", &props.n_items, NULL) < 0) + return -EINVAL; + + props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); + for (i = 0; i < props.n_items; i++) { + if (spa_pod_parser_get(&prs, + "s", &props.items[i].key, + "s", &props.items[i].value, NULL) < 0) + return -EINVAL; + } + if (spa_pod_parser_get(&prs, "i", &new_id, NULL) < 0) + return -EINVAL; + + type = pw_protocol_native0_type_from_v2(client, type); + + return pw_resource_notify(resource, struct pw_core_proxy_methods, create_object, 0, factory_name, + type, version, + &props, new_id); +} + +static int core_demarshal_destroy(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object, *r; + struct pw_client *client = pw_resource_get_client(resource); + struct spa_pod_parser prs; + uint32_t id; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "i", &id, NULL) < 0) + return -EINVAL; + + pw_log_debug("client %p: destroy resource %u", client, id); + + if ((r = pw_client_find_resource(client, id)) == NULL) + goto no_resource; + + return pw_resource_notify(resource, struct pw_core_proxy_methods, destroy, 0, r); + +no_resource: + pw_log_error("client %p: unknown resouce %u op:%u", client, id, msg->opcode); + pw_resource_error(resource, -EINVAL, "unknown resource %d op:%u", id, msg->opcode); + return 0; +} + +static int core_demarshal_update_types_server(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct pw_client *client = pw_resource_get_client(resource); + struct protocol_compat_v2 *compat_v2 = client->compat_v2; + struct spa_pod_parser prs; + uint32_t first_id, n_types; + struct spa_pod_frame f; + const char **types; + uint32_t i; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_push_struct(&prs, &f) < 0 || + spa_pod_parser_get(&prs, + "i", &first_id, + "i", &n_types, + NULL) < 0) + return -EINVAL; + + types = alloca(n_types * sizeof(char *)); + for (i = 0; i < n_types; i++) { + if (spa_pod_parser_get(&prs, "s", &types[i], NULL) < 0) + return -EINVAL; + } + + for (i = 0; i < n_types; i++, first_id++) { + int type_id = pw_protocol_native0_find_type(client, types[i]); + if (pw_map_insert_at(&compat_v2->types, first_id, PW_MAP_ID_TO_PTR(type_id)) < 0) + pw_log_error("can't add type %d->%d for client", first_id, type_id); + } + return 0; +} + +static void registry_marshal_global(void *object, uint32_t id, uint32_t permissions, + uint32_t type, uint32_t version, const struct spa_dict *props) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, n_items, parent_id; + + b = pw_protocol_native_begin_resource(resource, PW_REGISTRY_PROXY_V0_EVENT_GLOBAL, NULL); + + n_items = props ? props->n_items : 0; + + type = pw_protocol_native0_type_to_v2(client, pw_type_info(), type); + parent_id = 0; + version = 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", id, + "i", parent_id, + "i", permissions, + "I", type, + "i", version, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", props->items[i].key, + "s", props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void registry_marshal_global_remove(void *object, uint32_t id) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_REGISTRY_PROXY_V0_EVENT_GLOBAL_REMOVE, NULL); + + spa_pod_builder_add_struct(b, "i", id); + + pw_protocol_native_end_resource(resource, b); +} + +static int registry_demarshal_bind(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t id, version, type, new_id; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "i", &id, + "I", &type, + "i", &version, + "i", &new_id) < 0) + return -EINVAL; + + type = pw_protocol_native0_type_from_v2(resource->client, type); + + return pw_resource_notify(resource, struct pw_registry_proxy_methods, bind, 0, id, type, version, new_id); +} + +static void module_marshal_info(void *object, const struct pw_module_info *info) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, n_items; + + b = pw_protocol_native_begin_resource(resource, PW_MODULE_PROXY_V0_EVENT_INFO, NULL); + + n_items = info->props ? info->props->n_items : 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", info->id, + "l", info->change_mask, + "s", info->name, + "s", info->filename, + "s", info->args, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void factory_marshal_info(void *object, const struct pw_factory_info *info) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, n_items, type, version; + + b = pw_protocol_native_begin_resource(resource, PW_FACTORY_PROXY_V0_EVENT_INFO, NULL); + + n_items = info->props ? info->props->n_items : 0; + + type = pw_protocol_native0_type_to_v2(client, pw_type_info(), info->type); + version = 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", info->id, + "l", info->change_mask, + "s", info->name, + "I", type, + "i", version, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void node_marshal_info(void *object, const struct pw_node_info *info) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, n_items; + + b = pw_protocol_native_begin_resource(resource, PW_NODE_PROXY_V0_EVENT_INFO, NULL); + + n_items = info->props ? info->props->n_items : 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", info->id, + "l", info->change_mask, + "s", "node.name", + "i", info->max_input_ports, + "i", info->n_input_ports, + "i", info->max_output_ports, + "i", info->n_output_ports, + "i", info->state, + "s", info->error, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void node_marshal_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_NODE_PROXY_V0_EVENT_PARAM, NULL); + + spa_pod_builder_add_struct(b, "I", id, "i", index, "i", next, "P", param); + + pw_protocol_native_end_resource(resource, b); +} + +static int node_demarshal_enum_params(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_parser prs; + uint32_t id, index, num; + struct spa_pod *filter; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "I", &id, + "i", &index, + "i", &num, + "P", &filter) < 0) + return -EINVAL; + + id = pw_protocol_native0_type_from_v2(client, id); + filter = NULL; + + return pw_resource_notify(resource, struct pw_node_proxy_methods, enum_params, 0, + 0, id, index, num, filter); +} + +static void port_marshal_info(void *object, const struct pw_port_info *info) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, n_items; + + b = pw_protocol_native_begin_resource(resource, PW_PORT_PROXY_V0_EVENT_INFO, NULL); + + n_items = info->props ? info->props->n_items : 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", info->id, + "l", info->change_mask, + "s", "port.name", + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void port_marshal_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_PORT_PROXY_V0_EVENT_PARAM, NULL); + + id = pw_protocol_native0_type_to_v2(client, pw_type_info(), id), + + spa_pod_builder_add_struct(b, "I", id, "i", index, "i", next, "P", param); + + pw_protocol_native_end_resource(resource, b); +} + +static int port_demarshal_enum_params(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct pw_client *client = resource->client; + struct spa_pod_parser prs; + uint32_t id, index, num; + struct spa_pod *filter; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, + "I", &id, + "i", &index, + "i", &num, + "P", &filter) < 0) + return -EINVAL; + + id = pw_protocol_native0_type_from_v2(client, id); + filter = NULL; + + return pw_resource_notify(resource, struct pw_port_proxy_methods, enum_params, 0, + 0, id, index, num, filter); +} + +static void client_marshal_info(void *object, const struct pw_client_info *info) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, n_items; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_PROXY_V0_EVENT_INFO, NULL); + + n_items = info->props ? info->props->n_items : 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", info->id, + "l", info->change_mask, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static void client_marshal_permissions(void *object, uint32_t index, uint32_t n_permissions, + const struct pw_permission *permissions) +{ +} + + +static void link_marshal_info(void *object, const struct pw_link_info *info) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + struct spa_pod_frame f; + uint32_t i, n_items; + + b = pw_protocol_native_begin_resource(resource, PW_LINK_PROXY_V0_EVENT_INFO, NULL); + + n_items = info->props ? info->props->n_items : 0; + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + "i", info->id, + "l", info->change_mask, + "i", info->output_node_id, + "i", info->output_port_id, + "i", info->input_node_id, + "i", info->input_port_id, + "P", info->format, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + +static const struct pw_protocol_native_demarshal pw_protocol_native_core_method_demarshal[PW_CORE_PROXY_V0_METHOD_NUM] = { + [PW_CORE_PROXY_V0_METHOD_HELLO] = { &core_demarshal_hello, 0, }, + [PW_CORE_PROXY_V0_METHOD_UPDATE_TYPES] = { &core_demarshal_update_types_server, 0, }, + [PW_CORE_PROXY_V0_METHOD_SYNC] = { &core_demarshal_sync, 0, }, + [PW_CORE_PROXY_V0_METHOD_GET_REGISTRY] = { &core_demarshal_get_registry, 0, }, + [PW_CORE_PROXY_V0_METHOD_CLIENT_UPDATE] = { &core_demarshal_client_update, 0, }, + [PW_CORE_PROXY_V0_METHOD_PERMISSIONS] = { &core_demarshal_permissions, 0, }, + [PW_CORE_PROXY_V0_METHOD_CREATE_OBJECT] = { &core_demarshal_create_object, 0, PW_PROTOCOL_NATIVE_FLAG_REMAP, }, + [PW_CORE_PROXY_V0_METHOD_DESTROY] = { &core_demarshal_destroy, 0, } +}; + +static const struct pw_core_proxy_events pw_protocol_native_core_event_marshal = { + PW_VERSION_CORE_PROXY_EVENTS, + .info = &core_marshal_info, + .done = &core_marshal_done, + .error = &core_marshal_error, + .remove_id = &core_marshal_remove_id, +}; + +static const struct pw_protocol_marshal pw_protocol_native_core_marshal = { + PW_TYPE_INTERFACE_Core, + PW_VERSION_CORE_V0, + PW_CORE_PROXY_V0_METHOD_NUM, + PW_CORE_PROXY_EVENT_NUM, + NULL, + pw_protocol_native_core_method_demarshal, + &pw_protocol_native_core_event_marshal, + NULL +}; + +static const struct pw_protocol_native_demarshal pw_protocol_native_registry_method_demarshal[] = { + [PW_REGISTRY_PROXY_V0_METHOD_BIND] = { ®istry_demarshal_bind, 0, PW_PROTOCOL_NATIVE_FLAG_REMAP, }, +}; + +static const struct pw_registry_proxy_events pw_protocol_native_registry_event_marshal = { + PW_VERSION_REGISTRY_PROXY_EVENTS, + .global = ®istry_marshal_global, + .global_remove = ®istry_marshal_global_remove, +}; + +static const struct pw_protocol_marshal pw_protocol_native_registry_marshal = { + PW_TYPE_INTERFACE_Registry, + PW_VERSION_REGISTRY_V0, + PW_REGISTRY_PROXY_V0_METHOD_NUM, + PW_REGISTRY_PROXY_EVENT_NUM, + NULL, + pw_protocol_native_registry_method_demarshal, + &pw_protocol_native_registry_event_marshal, + NULL +}; + +static const struct pw_module_proxy_events pw_protocol_native_module_event_marshal = { + PW_VERSION_MODULE_PROXY_EVENTS, + .info = &module_marshal_info, +}; + +static const struct pw_protocol_marshal pw_protocol_native_module_marshal = { + PW_TYPE_INTERFACE_Module, + PW_VERSION_MODULE_V0, + 0, + PW_MODULE_PROXY_EVENT_NUM, + NULL, NULL, + &pw_protocol_native_module_event_marshal, + NULL +}; + +static const struct pw_factory_proxy_events pw_protocol_native_factory_event_marshal = { + PW_VERSION_FACTORY_PROXY_EVENTS, + .info = &factory_marshal_info, +}; + +static const struct pw_protocol_marshal pw_protocol_native_factory_marshal = { + PW_TYPE_INTERFACE_Factory, + PW_VERSION_FACTORY_V0, + 0, + PW_FACTORY_PROXY_EVENT_NUM, + NULL, NULL, + &pw_protocol_native_factory_event_marshal, + NULL, +}; + +static const struct pw_protocol_native_demarshal pw_protocol_native_node_method_demarshal[] = { + [PW_NODE_PROXY_V0_METHOD_ENUM_PARAMS] = { &node_demarshal_enum_params, 0, PW_PROTOCOL_NATIVE_FLAG_REMAP, }, +}; + +static const struct pw_node_proxy_events pw_protocol_native_node_event_marshal = { + PW_VERSION_NODE_PROXY_EVENTS, + .info = &node_marshal_info, + .param = &node_marshal_param, +}; + +static const struct pw_protocol_marshal pw_protocol_native_node_marshal = { + PW_TYPE_INTERFACE_Node, + PW_VERSION_NODE_V0, + PW_NODE_PROXY_V0_METHOD_NUM, + PW_NODE_PROXY_EVENT_NUM, + NULL, + pw_protocol_native_node_method_demarshal, + &pw_protocol_native_node_event_marshal, + NULL +}; + + +static const struct pw_protocol_native_demarshal pw_protocol_native_port_method_demarshal[] = { + [PW_PORT_PROXY_V0_METHOD_ENUM_PARAMS] = { &port_demarshal_enum_params, 0, PW_PROTOCOL_NATIVE_FLAG_REMAP, }, +}; + +static const struct pw_port_proxy_events pw_protocol_native_port_event_marshal = { + PW_VERSION_PORT_PROXY_EVENTS, + .info = &port_marshal_info, + .param = &port_marshal_param, +}; + +static const struct pw_protocol_marshal pw_protocol_native_port_marshal = { + PW_TYPE_INTERFACE_Port, + PW_VERSION_PORT_V0, + PW_PORT_PROXY_V0_METHOD_NUM, + PW_PORT_PROXY_EVENT_NUM, + NULL, + pw_protocol_native_port_method_demarshal, + &pw_protocol_native_port_event_marshal, + NULL +}; + +static const struct pw_client_proxy_events pw_protocol_native_client_event_marshal = { + PW_VERSION_CLIENT_PROXY_EVENTS, + .info = &client_marshal_info, + .permissions = &client_marshal_permissions, +}; + +static const struct pw_protocol_marshal pw_protocol_native_client_marshal = { + PW_TYPE_INTERFACE_Client, + PW_VERSION_CLIENT_V0, + 0, + PW_CLIENT_PROXY_EVENT_NUM, + NULL, NULL, + &pw_protocol_native_client_event_marshal, + NULL, +}; + +static const struct pw_link_proxy_events pw_protocol_native_link_event_marshal = { + PW_VERSION_LINK_PROXY_EVENTS, + .info = &link_marshal_info, +}; + +static const struct pw_protocol_marshal pw_protocol_native_link_marshal = { + PW_TYPE_INTERFACE_Link, + PW_VERSION_LINK_V0, + 0, + PW_LINK_PROXY_EVENT_NUM, + NULL, NULL, + &pw_protocol_native_link_event_marshal, + NULL +}; + +void pw_protocol_native0_init(struct pw_protocol *protocol) +{ + pw_protocol_add_marshal(protocol, &pw_protocol_native_core_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_registry_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_module_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_node_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_port_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_factory_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_client_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_link_marshal); +} diff --git a/src/modules/module-protocol-native/v0/typemap.h b/src/modules/module-protocol-native/v0/typemap.h new file mode 100644 index 000000000..6620fbd44 --- /dev/null +++ b/src/modules/module-protocol-native/v0/typemap.h @@ -0,0 +1,261 @@ + +const struct type_info { + const char *type; + const char *name; + uint32_t id; +} type_map[] = { + { "Spa:Interface:TypeMap", SPA_TYPE_INFO_INTERFACE_BASE, 0, }, + { "Spa:Interface:Log", SPA_TYPE_INFO_INTERFACE_BASE "Log", SPA_TYPE_INTERFACE_Log, }, + { "Spa:Interface:Loop", SPA_TYPE_INFO_INTERFACE_BASE "Loop", SPA_TYPE_INTERFACE_Loop, }, + { "Spa:Interface:LoopControl", SPA_TYPE_INFO_INTERFACE_BASE "LoopControl", SPA_TYPE_INTERFACE_LoopControl, }, + { "Spa:Interface:LoopUtils", SPA_TYPE_INFO_INTERFACE_BASE "LoopUtils", SPA_TYPE_INTERFACE_LoopUtils, }, + { "PipeWire:Interface:Core", PW_TYPE_INFO_INTERFACE_BASE "Core", PW_TYPE_INTERFACE_Core, }, + { "PipeWire:Interface:Registry", PW_TYPE_INFO_INTERFACE_BASE "Registry", PW_TYPE_INTERFACE_Registry, }, + { "PipeWire:Interface:Node", PW_TYPE_INFO_INTERFACE_BASE "Node", PW_TYPE_INTERFACE_Node, }, + { "PipeWire:Interface:Port", PW_TYPE_INFO_INTERFACE_BASE "Port", PW_TYPE_INTERFACE_Port,}, + { "PipeWire:Interface:Factory", PW_TYPE_INFO_INTERFACE_BASE "Factory", PW_TYPE_INTERFACE_Factory, }, + { "PipeWire:Interface:Link", PW_TYPE_INFO_INTERFACE_BASE "Link", PW_TYPE_INTERFACE_Link, }, + { "PipeWire:Interface:Client", PW_TYPE_INFO_INTERFACE_BASE "Client", PW_TYPE_INTERFACE_Client, }, + { "PipeWire:Interface:Module", PW_TYPE_INFO_INTERFACE_BASE "Module", PW_TYPE_INTERFACE_Module, }, + { "PipeWire:Interface:ClientNode", PW_TYPE_INFO_INTERFACE_BASE "ClientNode", PW_TYPE_INTERFACE_ClientNode, }, + { "PipeWire:Interface:Device", PW_TYPE_INFO_INTERFACE_BASE "Device", PW_TYPE_INTERFACE_Device, }, + { "Spa:Interface:Node", SPA_TYPE_INFO_INTERFACE_BASE "Node", SPA_TYPE_INTERFACE_Node, }, + { "Spa:Interface:Clock", }, + { "Spa:Interface:Monitor", }, + { "Spa:Interface:Device", SPA_TYPE_INFO_INTERFACE_BASE "Device", SPA_TYPE_INTERFACE_Device, }, + { "Spa:POD:Object:Param:Format", SPA_TYPE_INFO_Format, SPA_TYPE_OBJECT_Format, }, + { "Spa:POD:Object:Param:Props", SPA_TYPE_INFO_Props, SPA_TYPE_OBJECT_Props, }, + { "Spa:Pointer:IO:Buffers", }, + { "Spa:Pointer:IO:Control:Range", }, + { "Spa:Pointer:IO:Prop", }, + { "Spa:Enum:ParamId:List", }, + { "Spa:POD:Object:Param:List", }, + { "Spa:POD:Object:Param:List:id", }, + { "Spa:Enum:ParamId:PropInfo", SPA_TYPE_INFO_PARAM_ID_BASE "PropInfo", SPA_PARAM_PropInfo, }, + { "Spa:POD:Object:Param:PropInfo", SPA_TYPE_INFO_PROP_INFO_BASE, SPA_PROP_INFO_START, }, + { "Spa:POD:Object:Param:PropInfo:id", SPA_TYPE_INFO_PROP_INFO_BASE "id", SPA_PROP_INFO_id, }, + { "Spa:POD:Object:Param:PropInfo:name", SPA_TYPE_INFO_PROP_INFO_BASE "name", SPA_PROP_INFO_name, }, + { "Spa:POD:Object:Param:PropInfo:type", SPA_TYPE_INFO_PROP_INFO_BASE "typ", SPA_PROP_INFO_type, }, + { "Spa:POD:Object:Param:PropInfo:labels", SPA_TYPE_INFO_PROP_INFO_BASE "labels", SPA_PROP_INFO_labels, }, + { "Spa:Enum:ParamId:Props", SPA_TYPE_INFO_PARAM_ID_BASE "Props", SPA_PARAM_Props, }, + { "Spa:Enum:ParamId:EnumFormat", SPA_TYPE_INFO_PARAM_ID_BASE "EnumFormat", SPA_PARAM_EnumFormat,}, + { "Spa:Enum:ParamId:Format", SPA_TYPE_INFO_PARAM_ID_BASE "Format", SPA_PARAM_Format, }, + { "Spa:Enum:ParamId:Buffers", SPA_TYPE_INFO_PARAM_ID_BASE "Buffers", SPA_PARAM_Buffers }, + { "Spa:Enum:ParamId:Meta", SPA_TYPE_INFO_PARAM_ID_BASE "Meta", SPA_PARAM_Meta, }, + { "Spa:Pointer:Meta:Header", SPA_TYPE_INFO_META_BASE "Header", SPA_META_Header, }, + { "Spa:Pointer:Meta:VideoCrop", SPA_TYPE_INFO_META_REGION_BASE "VideoCrop", SPA_META_VideoCrop, }, + { "Spa:Pointer:Meta:VideoDamage", SPA_TYPE_INFO_META_REGION_ARRAY_BASE "VideoDamage", SPA_META_VideoDamage, }, + { "Spa:Pointer:Meta:Bitmap", SPA_TYPE_INFO_META_BASE "Bitmap", SPA_META_Bitmap, }, + { "Spa:Pointer:Meta:Cursor", SPA_TYPE_INFO_META_BASE "Cursor", SPA_META_Cursor, }, + { "Spa:Enum:DataType:MemPtr", SPA_TYPE_INFO_DATA_BASE "MemPtr", SPA_DATA_MemPtr, }, + { "Spa:Enum:DataType:Fd:MemFd", SPA_TYPE_INFO_DATA_FD_BASE "MemFd", SPA_DATA_MemFd, }, + { "Spa:Enum:DataType:Fd:DmaBuf", SPA_TYPE_INFO_DATA_FD_BASE "DmaBuf", SPA_DATA_DmaBuf, }, + { "Spa:POD:Object:Event:Node:Error", }, + { "Spa:POD:Object:Event:Node:Buffering", }, + { "Spa:POD:Object:Event:Node:RequestRefresh", }, + { "Spa:POD:Object:Event:Node:RequestClockUpdate", }, + { "Spa:POD:Object:Command:Node", SPA_TYPE_INFO_COMMAND_BASE "Node", SPA_TYPE_COMMAND_Node,}, + { "Spa:POD:Object:Command:Node:Suspend", SPA_TYPE_INFO_NODE_COMMAND_BASE "Suspend", SPA_NODE_COMMAND_Suspend,}, + { "Spa:POD:Object:Command:Node:Pause", SPA_TYPE_INFO_NODE_COMMAND_BASE "Pause", SPA_NODE_COMMAND_Pause, }, + { "Spa:POD:Object:Command:Node:Start", SPA_TYPE_INFO_NODE_COMMAND_BASE "Start", SPA_NODE_COMMAND_Start, }, + { "Spa:POD:Object:Command:Node:Enable", SPA_TYPE_INFO_NODE_COMMAND_BASE "Enable", SPA_NODE_COMMAND_Enable, }, + { "Spa:POD:Object:Command:Node:Disable", SPA_TYPE_INFO_NODE_COMMAND_BASE "Disable", SPA_NODE_COMMAND_Disable, }, + { "Spa:POD:Object:Command:Node:Flush", SPA_TYPE_INFO_NODE_COMMAND_BASE "Flush", SPA_NODE_COMMAND_Flush, }, + { "Spa:POD:Object:Command:Node:Drain", SPA_TYPE_INFO_NODE_COMMAND_BASE "Drain", SPA_NODE_COMMAND_Drain, }, + { "Spa:POD:Object:Command:Node:Marker", SPA_TYPE_INFO_NODE_COMMAND_BASE "Marker", SPA_NODE_COMMAND_Marker, }, + { "Spa:POD:Object:Command:Node:ClockUpdate", }, + { "Spa:POD:Object:Event:Monitor:Added", }, + { "Spa:POD:Object:Event:Monitor:Removed", }, + { "Spa:POD:Object:Event:Monitor:Changed", }, + { "Spa:POD:Object:MonitorItem", }, + { "Spa:POD:Object:MonitorItem:id", }, + { "Spa:POD:Object:MonitorItem:flags", }, + { "Spa:POD:Object:MonitorItem:state", }, + { "Spa:POD:Object:MonitorItem:name", }, + { "Spa:POD:Object:MonitorItem:class", }, + { "Spa:POD:Object:MonitorItem:info", }, + { "Spa:POD:Object:MonitorItem:factory", }, + { "Spa:POD:Object:Param:Buffers", SPA_TYPE_INFO_PARAM_Buffers, SPA_TYPE_OBJECT_ParamBuffers, }, + { "Spa:POD:Object:Param:Buffers:size", SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "size", SPA_PARAM_BUFFERS_size, }, + { "Spa:POD:Object:Param:Buffers:stride", SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "stride", SPA_PARAM_BUFFERS_stride, }, + { "Spa:POD:Object:Param:Buffers:buffers", SPA_TYPE_INFO_PARAM_BUFFERS_BASE "buffers", SPA_PARAM_BUFFERS_buffers, }, + { "Spa:POD:Object:Param:Buffers:align",SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "align", SPA_PARAM_BUFFERS_align, }, + { "Spa:POD:Object:Param:Meta", SPA_TYPE_INFO_PARAM_Meta, SPA_TYPE_OBJECT_ParamMeta, }, + { "Spa:POD:Object:Param:Meta:type", SPA_TYPE_INFO_PARAM_META_BASE "type", SPA_PARAM_META_type, }, + { "Spa:POD:Object:Param:Meta:size", SPA_TYPE_INFO_PARAM_META_BASE "size", SPA_PARAM_META_size, }, + { "Spa:POD:Object:Param:IO:id", }, + { "Spa:POD:Object:Param:IO:size", }, + { "Spa:Enum:ParamId:IO:Buffers", }, + { "Spa:POD:Object:Param:IO:Buffers", }, + { "Spa:Enum:ParamId:IO:Control", }, + { "Spa:POD:Object:Param:IO:Control", }, + { "Spa:Enum:ParamId:IO:Props:In", }, + { "Spa:Enum:ParamId:IO:Props:Out", }, + { "Spa:POD:Object:Param:IO:Prop", }, + { "Spa:Interface:DBus", SPA_TYPE_INFO_INTERFACE_BASE "DBus", SPA_TYPE_INTERFACE_DBus, }, + { "Spa:Enum:MediaType:audio", SPA_TYPE_INFO_MEDIA_TYPE_BASE "audio", SPA_MEDIA_TYPE_audio, }, + { "Spa:Enum:MediaType:video", SPA_TYPE_INFO_MEDIA_TYPE_BASE "video", SPA_MEDIA_TYPE_video, }, + { "Spa:Enum:MediaType:image", SPA_TYPE_INFO_MEDIA_TYPE_BASE "image", SPA_MEDIA_TYPE_image, }, + { "Spa:Enum:MediaType:binary", SPA_TYPE_INFO_MEDIA_TYPE_BASE "binary", SPA_MEDIA_TYPE_binary, }, + { "Spa:Enum:MediaType:stream", SPA_TYPE_INFO_MEDIA_TYPE_BASE "stream", SPA_MEDIA_TYPE_stream, }, + { "Spa:Enum:MediaSubtype:raw", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:h264", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h264", SPA_MEDIA_SUBTYPE_h264, }, + { "Spa:Enum:MediaSubtype:mjpg", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mjpg", SPA_MEDIA_SUBTYPE_mjpg, }, + { "Spa:Enum:MediaSubtype:dv", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "dv", SPA_MEDIA_SUBTYPE_dv, }, + { "Spa:Enum:MediaSubtype:mpegts", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpegts", SPA_MEDIA_SUBTYPE_mpegts, }, + { "Spa:Enum:MediaSubtype:h263", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h263", SPA_MEDIA_SUBTYPE_h263, }, + { "Spa:Enum:MediaSubtype:mpeg1", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg1", SPA_MEDIA_SUBTYPE_mpeg1, }, + { "Spa:Enum:MediaSubtype:mpeg2", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg2", SPA_MEDIA_SUBTYPE_mpeg2, }, + { "Spa:Enum:MediaSubtype:mpeg4", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg4", SPA_MEDIA_SUBTYPE_mpeg4, }, + { "Spa:Enum:MediaSubtype:xvid", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:vc1", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:vp8", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:vp9", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:jpeg", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:bayer", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:mp3", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:aac", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:vorbis", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:wma", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:ra", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:sbc", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:adpcm", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:g723", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:g726", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:g729", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:amr", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:gsm", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:Enum:MediaSubtype:midi", SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", SPA_MEDIA_SUBTYPE_raw, }, + { "Spa:POD:Object:Param:Format:Video:format", SPA_TYPE_INFO_FORMAT_VIDEO_BASE "format", SPA_FORMAT_VIDEO_format,}, + { "Spa:POD:Object:Param:Format:Video:size", SPA_TYPE_INFO_FORMAT_VIDEO_BASE "size", SPA_FORMAT_VIDEO_size,}, + { "Spa:POD:Object:Param:Format:Video:framerate", SPA_TYPE_INFO_FORMAT_VIDEO_BASE "framerate", SPA_FORMAT_VIDEO_framerate}, + { "Spa:POD:Object:Param:Format:Video:max-framerate", }, + { "Spa:POD:Object:Param:Format:Video:views", }, + { "Spa:POD:Object:Param:Format:Video:interlace-mode", }, + { "Spa:POD:Object:Param:Format:Video:pixel-aspect-ratio", }, + { "Spa:POD:Object:Param:Format:Video:multiview-mode", }, + { "Spa:POD:Object:Param:Format:Video:multiview-flags", }, + { "Spa:POD:Object:Param:Format:Video:chroma-site", }, + { "Spa:POD:Object:Param:Format:Video:color-range", }, + { "Spa:POD:Object:Param:Format:Video:color-matrix", }, + { "Spa:POD:Object:Param:Format:Video:transfer-function", }, + { "Spa:POD:Object:Param:Format:Video:color-primaries", }, + { "Spa:POD:Object:Param:Format:Video:profile", }, + { "Spa:POD:Object:Param:Format:Video:level", }, + { "Spa:POD:Object:Param:Format:Video:stream-format", }, + { "Spa:POD:Object:Param:Format:Video:alignment", }, + { "Spa:POD:Object:Param:Format:Audio:format", SPA_TYPE_INFO_FORMAT_AUDIO_BASE "format", SPA_FORMAT_AUDIO_format,}, + { "Spa:POD:Object:Param:Format:Audio:flags", }, + { "Spa:POD:Object:Param:Format:Audio:layout", }, + { "Spa:POD:Object:Param:Format:Audio:rate", }, + { "Spa:POD:Object:Param:Format:Audio:channels", }, + { "Spa:POD:Object:Param:Format:Audio:channel-mask", }, + { "Spa:Enum:VideoFormat:encoded", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "encoded", SPA_VIDEO_FORMAT_ENCODED,}, + { "Spa:Enum:VideoFormat:I420", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:YV12", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YV12", SPA_VIDEO_FORMAT_YV12,}, + { "Spa:Enum:VideoFormat:YUY2", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUY2", SPA_VIDEO_FORMAT_YUY2,}, + { "Spa:Enum:VideoFormat:UYVY", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UYVY", SPA_VIDEO_FORMAT_UYVY,}, + { "Spa:Enum:VideoFormat:AYUV", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "AYUV", SPA_VIDEO_FORMAT_AYUV,}, + { "Spa:Enum:VideoFormat:RGBx", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBx", SPA_VIDEO_FORMAT_RGBx,}, + { "Spa:Enum:VideoFormat:BGRx", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRx", SPA_VIDEO_FORMAT_BGRx,}, + { "Spa:Enum:VideoFormat:xRGB", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:xBGR", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:RGBA", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:BGRA", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:ARGB", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:ABGR", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:RGB", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:BGR", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:Y41B", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:Y42B", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:YVYU", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:Y444", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:v210", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:v216", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:NV12", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:NV21", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GRAY8", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GRAY16_BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GRAY16_LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:v308", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:RGB16", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:BGR16", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:RGB15", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:BGR15", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:UYVP", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:A420", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:RGB8P", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:YUV9", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:YVU9", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:IYU1", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:ARGB64", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:AYUV64", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:r210", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I420_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I420_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I422_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I422_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:Y444_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:Y444_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBR", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBR_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBR_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:NV16", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:NV24", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:NV12_64Z32", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:A420_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:A420_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:A422_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:A422_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:A444_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:A444_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:NV61", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:P010_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:P010_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:IYU2", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:VYUY", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBRA", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBRA_10BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBRA_10LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBR_12BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBR_12LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBRA_12BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:GBRA_12LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I420_12BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I420_12LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I422_12BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:I422_12LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:Y444_12BE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:VideoFormat:Y444_12LE", SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", SPA_VIDEO_FORMAT_I420,}, + { "Spa:Enum:AudioFormat:ENCODED", }, + { "Spa:Enum:AudioFormat:S8", }, + { "Spa:Enum:AudioFormat:U8", }, + { "Spa:Enum:AudioFormat:S16LE", }, + { "Spa:Enum:AudioFormat:U16LE", }, + { "Spa:Enum:AudioFormat:S24_32LE", }, + { "Spa:Enum:AudioFormat:U24_32LE", }, + { "Spa:Enum:AudioFormat:S32LE", }, + { "Spa:Enum:AudioFormat:U32LE", }, + { "Spa:Enum:AudioFormat:S24LE", }, + { "Spa:Enum:AudioFormat:U24LE", }, + { "Spa:Enum:AudioFormat:S20LE", }, + { "Spa:Enum:AudioFormat:U20LE", }, + { "Spa:Enum:AudioFormat:S18LE", }, + { "Spa:Enum:AudioFormat:U18LE", }, + { "Spa:Enum:AudioFormat:F32LE", }, + { "Spa:Enum:AudioFormat:F64LE", }, + { "Spa:Enum:AudioFormat:S16BE", }, + { "Spa:Enum:AudioFormat:U16BE", }, + { "Spa:Enum:AudioFormat:S24_32BE", }, + { "Spa:Enum:AudioFormat:U24_32BE", }, + { "Spa:Enum:AudioFormat:S32BE", }, + { "Spa:Enum:AudioFormat:U32BE", }, + { "Spa:Enum:AudioFormat:S24BE", }, + { "Spa:Enum:AudioFormat:U24BE", }, + { "Spa:Enum:AudioFormat:S20BE", }, + { "Spa:Enum:AudioFormat:U20BE", }, + { "Spa:Enum:AudioFormat:S18BE", }, + { "Spa:Enum:AudioFormat:U18BE", }, + { "Spa:Enum:AudioFormat:F32BE", }, + { "Spa:Enum:AudioFormat:F64BE", }, +}; diff --git a/src/pipewire/core.c b/src/pipewire/core.c index 31c7d72ed..ead2487f1 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -172,8 +172,7 @@ static int destroy_resource(void *object, void *data) struct pw_client *client = resource->client; if (resource && - resource != client->core_resource && - resource != client->client_resource) { + resource != client->core_resource) { resource->removed = true; pw_resource_destroy(resource); } @@ -185,11 +184,20 @@ static int core_hello(void *object, uint32_t version) struct pw_resource *resource = object; struct pw_client *client = resource->client; struct pw_core *this = resource->core; + int res; pw_log_debug(NAME" %p: hello %d from resource %p", this, version, resource); - this->info.change_mask = PW_CORE_CHANGE_MASK_ALL; pw_map_for_each(&client->objects, destroy_resource, client); + + this->info.change_mask = PW_CORE_CHANGE_MASK_ALL; pw_core_resource_info(resource, &this->info); + + if (version >= 3) { + if ((res = pw_global_bind(client->global, client, + PW_PERM_RWX, PW_VERSION_CLIENT_PROXY, 1)) < 0) + return res; + } + return 0; } diff --git a/src/pipewire/interfaces.h b/src/pipewire/interfaces.h index 448275b03..5c5042ed4 100644 --- a/src/pipewire/interfaces.h +++ b/src/pipewire/interfaces.h @@ -41,23 +41,23 @@ extern "C" { #include #include -#define PW_VERSION_CORE_PROXY 0 +#define PW_VERSION_CORE_PROXY 3 struct pw_core_proxy { struct spa_interface iface; }; -#define PW_VERSION_REGISTRY_PROXY 0 +#define PW_VERSION_REGISTRY_PROXY 3 struct pw_registry_proxy { struct spa_interface iface; }; -#define PW_VERSION_MODULE_PROXY 0 +#define PW_VERSION_MODULE_PROXY 3 struct pw_module_proxy { struct spa_interface iface; }; -#define PW_VERSION_DEVICE_PROXY 0 +#define PW_VERSION_DEVICE_PROXY 3 struct pw_device_proxy { struct spa_interface iface; }; -#define PW_VERSION_NODE_PROXY 0 +#define PW_VERSION_NODE_PROXY 3 struct pw_node_proxy { struct spa_interface iface; }; -#define PW_VERSION_PORT_PROXY 0 +#define PW_VERSION_PORT_PROXY 3 struct pw_port_proxy { struct spa_interface iface; }; -#define PW_VERSION_FACTORY_PROXY 0 +#define PW_VERSION_FACTORY_PROXY 3 struct pw_factory_proxy { struct spa_interface iface; }; -#define PW_VERSION_CLIENT_PROXY 0 +#define PW_VERSION_CLIENT_PROXY 3 struct pw_client_proxy { struct spa_interface iface; }; -#define PW_VERSION_LINK_PROXY 0 +#define PW_VERSION_LINK_PROXY 3 struct pw_link_proxy { struct spa_interface iface; }; /** diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 381c796c0..cd6732171 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -87,6 +87,11 @@ typedef uint32_t (*pw_permission_func_t) (struct pw_global *global, #define pw_client_emit_resource_removed(o,r) pw_client_emit(o, resource_removed, 0, r) #define pw_client_emit_busy_changed(o,b) pw_client_emit(o, busy_changed, 0, b) +struct protocol_compat_v2 { + /* v2 typemap */ + struct pw_map types; +}; + struct pw_client { struct pw_core *core; /**< core object */ struct spa_list link; /**< link in core object client list */ @@ -119,6 +124,9 @@ struct pw_client { unsigned int registered:1; unsigned int ucred_valid:1; /**< if the ucred member is valid */ unsigned int busy:1; + + /* v2 compatibility data */ + void *compat_v2; }; #define pw_global_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_global_events, m, v, ##__VA_ARGS__) @@ -689,6 +697,7 @@ struct pw_proxy { struct pw_remote *remote; /**< the owner remote of this proxy */ uint32_t id; /**< client side id */ + uint32_t version; /**< client side version */ int refcount; unsigned int zombie:1; /**< proxy is removed locally and waiting to * be removed from server */ diff --git a/src/pipewire/protocol.c b/src/pipewire/protocol.c index 1ee56ade6..658c426ed 100644 --- a/src/pipewire/protocol.c +++ b/src/pipewire/protocol.c @@ -152,12 +152,12 @@ pw_protocol_add_marshal(struct pw_protocol *protocol, SPA_EXPORT const struct pw_protocol_marshal * -pw_protocol_get_marshal(struct pw_protocol *protocol, uint32_t type) +pw_protocol_get_marshal(struct pw_protocol *protocol, uint32_t type, uint32_t version) { struct marshal *impl; spa_list_for_each(impl, &protocol->marshal_list, link) { - if (impl->marshal->type == type) + if (impl->marshal->type == type && impl->marshal->version == version) return impl->marshal; } return NULL; diff --git a/src/pipewire/protocol.h b/src/pipewire/protocol.h index 809ddcb70..041aa566e 100644 --- a/src/pipewire/protocol.h +++ b/src/pipewire/protocol.h @@ -132,7 +132,7 @@ int pw_protocol_add_marshal(struct pw_protocol *protocol, const struct pw_protocol_marshal *marshal); const struct pw_protocol_marshal * -pw_protocol_get_marshal(struct pw_protocol *protocol, uint32_t type); +pw_protocol_get_marshal(struct pw_protocol *protocol, uint32_t type, uint32_t version); struct pw_protocol * pw_core_find_protocol(struct pw_core *core, const char *name); diff --git a/src/pipewire/proxy.c b/src/pipewire/proxy.c index 537a93e27..2aa457f66 100644 --- a/src/pipewire/proxy.c +++ b/src/pipewire/proxy.c @@ -56,7 +56,7 @@ struct proxy { */ SPA_EXPORT struct pw_proxy *pw_proxy_new(struct pw_proxy *factory, - uint32_t type, + uint32_t type, uint32_t version, size_t user_data_size) { struct proxy *impl; @@ -71,10 +71,13 @@ struct pw_proxy *pw_proxy_new(struct pw_proxy *factory, this = &impl->this; this->remote = remote; this->refcount = 1; + this->version = version; - this->marshal = pw_protocol_get_marshal(remote->conn->protocol, type); + this->marshal = pw_protocol_get_marshal(remote->conn->protocol, type, version); if (this->marshal == NULL) { - pw_log_error(NAME" %p: no marshal for type %d", this, type); + pw_log_error(NAME" %p: no marshal for type %s/%d", this, + spa_debug_type_find_name(pw_type_info(), type), + version); res = -EPROTO; goto error_clean; } @@ -97,9 +100,9 @@ struct pw_proxy *pw_proxy_new(struct pw_proxy *factory, this->marshal->version, this->marshal->method_marshal, this); - pw_log_debug(NAME" %p: new %u %s remote %p, marshal %p", + pw_log_debug(NAME" %p: new %u type %s/%d remote:%p, marshal:%p", this, this->id, - spa_debug_type_find_name(pw_type_info(), type), + spa_debug_type_find_name(pw_type_info(), type), version, remote, this->marshal); return this; diff --git a/src/pipewire/proxy.h b/src/pipewire/proxy.h index efd367f3b..8d8b249be 100644 --- a/src/pipewire/proxy.h +++ b/src/pipewire/proxy.h @@ -123,6 +123,7 @@ struct pw_proxy_events { struct pw_proxy * pw_proxy_new(struct pw_proxy *factory, /**< factory */ uint32_t type, /**< interface type */ + uint32_t version, /**< interface version */ size_t user_data_size /**< size of user data */); /** Add an event listener to proxy */ diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c index fe62582ce..8c8f9430e 100644 --- a/src/pipewire/remote.c +++ b/src/pipewire/remote.c @@ -377,9 +377,10 @@ do_connect(struct spa_loop *loop, struct pw_proxy dummy, *core_proxy; int res; + spa_zero(dummy); dummy.remote = remote; - core_proxy = pw_proxy_new(&dummy, PW_TYPE_INTERFACE_Core, 0); + core_proxy = pw_proxy_new(&dummy, PW_TYPE_INTERFACE_Core, PW_VERSION_CORE_PROXY, 0); if (core_proxy == NULL) { res = -errno; goto error_disconnect; @@ -387,7 +388,7 @@ do_connect(struct spa_loop *loop, remote->core_proxy = (struct pw_core_proxy*)core_proxy; remote->client_proxy = (struct pw_client_proxy*)pw_proxy_new(&dummy, - PW_TYPE_INTERFACE_Client, 0); + PW_TYPE_INTERFACE_Client, PW_VERSION_CLIENT_PROXY, 0); if (remote->client_proxy == NULL) { res = -errno; goto error_clean_core_proxy; @@ -396,8 +397,8 @@ do_connect(struct spa_loop *loop, pw_core_proxy_add_listener(remote->core_proxy, &impl->core_listener, &core_events, remote); pw_proxy_add_listener(core_proxy, &impl->core_proxy_listener, &core_proxy_events, remote); - pw_client_proxy_update_properties(remote->client_proxy, &remote->properties->dict); pw_core_proxy_hello(remote->core_proxy, PW_VERSION_CORE_PROXY); + pw_client_proxy_update_properties(remote->client_proxy, &remote->properties->dict); pw_remote_update_state(remote, PW_REMOTE_STATE_CONNECTED, NULL); return 0; diff --git a/src/pipewire/resource.c b/src/pipewire/resource.c index 9dd06c42b..61a116f75 100644 --- a/src/pipewire/resource.c +++ b/src/pipewire/resource.c @@ -66,7 +66,7 @@ struct pw_resource *pw_resource_new(struct pw_client *client, spa_hook_list_init(&this->listener_list); spa_hook_list_init(&this->object_listener_list); - this->marshal = pw_protocol_get_marshal(client->protocol, type); + this->marshal = pw_protocol_get_marshal(client->protocol, type, version); if (this->marshal == NULL) { pw_log_error(NAME" %p: no marshal for type %s/%d", this, spa_debug_type_find_name(pw_type_info(), type), @@ -97,7 +97,7 @@ struct pw_resource *pw_resource_new(struct pw_client *client, this->marshal->version, this->marshal->event_marshal, this); - pw_log_debug(NAME" %p: new %u %s/%d client %p marshal %p", + pw_log_debug(NAME" %p: new %u type %s/%d client:%p marshal:%p", this, id, spa_debug_type_find_name(pw_type_info(), type), version, client, this->marshal);