pipewire/src/modules/module-client-node/v0/transport.c

258 lines
7 KiB
C
Raw Normal View History

protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
/* PipeWire
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* 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 <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <spa/utils/ringbuffer.h>
#include <spa/node/io.h>
#include <pipewire/log.h>
#include <pipewire/private.h>
#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_context *context,
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
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(context->pool,
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
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;
}