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);