diff --git a/pinos/client/stream.c b/pinos/client/stream.c index 3693868c0..b0899f96a 100644 --- a/pinos/client/stream.c +++ b/pinos/client/stream.c @@ -1048,7 +1048,7 @@ parse_control (PinosStream *stream, mid.ptr = NULL; mid.size = p.size; - g_debug ("add mem %u, fd %d, flags %d", p.mem_id, fd, p.flags); + g_debug ("add mem %u, fd %d, flags %d, size %zd", p.mem_id, fd, p.flags, p.size); g_array_append_val (priv->mem_ids, mid); break; } @@ -1098,9 +1098,7 @@ parse_control (PinosStream *stream, bid.buf = spa_buffer_deserialize (mid->ptr, p.buffers[i].offset); bid.id = bid.buf->id; - spa_debug_dump_mem (mid->ptr, 256); - - g_debug ("add buffer %d %zd", bid.id, p.buffers[i].offset); + g_debug ("add buffer %d %d %zd", mid->id, bid.id, p.buffers[i].offset); for (j = 0; j < bid.buf->n_datas; j++) { SpaData *d = &bid.buf->datas[j]; diff --git a/pinos/gst/gstpinossrc.c b/pinos/gst/gstpinossrc.c index e28ed95cd..4249fbb1c 100644 --- a/pinos/gst/gstpinossrc.c +++ b/pinos/gst/gstpinossrc.c @@ -721,7 +721,15 @@ on_format_notify (GObject *gobject, gst_caps_unref (caps); if (res) { - pinos_stream_finish_format (pinossrc->stream, SPA_RESULT_OK, NULL, 0); + SpaAllocParam *params[1]; + SpaAllocParamMetaEnable param_meta; + + params[0] = ¶m_meta.param; + param_meta.param.type = SPA_ALLOC_PARAM_TYPE_META_ENABLE; + param_meta.param.size = sizeof (param_meta); + param_meta.type = SPA_META_TYPE_HEADER; + + pinos_stream_finish_format (pinossrc->stream, SPA_RESULT_OK, params, 1); } else { pinos_stream_finish_format (pinossrc->stream, SPA_RESULT_INVALID_MEDIA_TYPE, NULL, 0); } diff --git a/pinos/server/link.c b/pinos/server/link.c index 5dfdfe147..bb5f4f7b9 100644 --- a/pinos/server/link.c +++ b/pinos/server/link.c @@ -28,6 +28,7 @@ #include "pinos/client/enumtypes.h" #include "pinos/server/link.h" +#include "pinos/server/utils.h" #include "pinos/dbus/org-pinos.h" @@ -51,7 +52,8 @@ struct _PinosLinkPrivate uint32_t async_busy; gboolean allocated; - SpaBuffer *buffers[MAX_BUFFERS]; + PinosMemblock buffer_mem; + SpaBuffer **buffers; unsigned int n_buffers; }; @@ -417,10 +419,10 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state) spa_debug_port_info (oinfo); spa_debug_port_info (iinfo); - if (!priv->allocated) { + if (priv->buffers == NULL) { SpaAllocParamBuffers *in_alloc, *out_alloc; guint max_buffers = MAX_BUFFERS; - SpaBufferAllocFlags flags = 0; + gboolean alloc_data = TRUE; max_buffers = MAX_BUFFERS; in_alloc = find_param (iinfo, SPA_ALLOC_PARAM_TYPE_BUFFERS); @@ -432,21 +434,49 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state) if ((in_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) || (out_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS)) - flags |= SPA_BUFFER_ALLOC_FLAG_NO_MEM; + alloc_data = FALSE; - priv->n_buffers = max_buffers; - if ((res = spa_buffer_alloc (flags, - oinfo->params, oinfo->n_params, - priv->buffers, - &priv->n_buffers)) < 0) { - g_set_error (&error, - PINOS_ERROR, - PINOS_ERROR_BUFFER_ALLOCATION, - "error buffer alloc: %d", res); - goto error; + if (this->output->allocated) { + out_flags = 0; + in_flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; + priv->n_buffers = this->output->n_buffers; + priv->buffers = this->output->buffers; + priv->allocated = FALSE; + g_debug ("reusing %d output buffers %p", priv->n_buffers, priv->buffers); + } else { + guint i; + size_t hdr_size; + void *p; + + spa_alloc_params_get_header_size (oinfo->params, oinfo->n_params, 1, &hdr_size); + + priv->n_buffers = max_buffers; + + pinos_memblock_alloc (PINOS_MEMBLOCK_FLAG_WITH_FD | + PINOS_MEMBLOCK_FLAG_MAP_READWRITE | + PINOS_MEMBLOCK_FLAG_SEAL, + priv->n_buffers * (sizeof (SpaBuffer*) + hdr_size), + &priv->buffer_mem); + + priv->buffers = p = priv->buffer_mem.ptr; + p = SPA_MEMBER (p, priv->n_buffers * sizeof (SpaBuffer*), void); + for (i = 0; i < priv->n_buffers; i++) + priv->buffers[i] = SPA_MEMBER (p, hdr_size * i, SpaBuffer); + + if ((res = spa_buffer_init_headers (oinfo->params, + oinfo->n_params, + 1, + priv->buffers, + priv->n_buffers)) < 0) { + g_set_error (&error, + PINOS_ERROR, + PINOS_ERROR_BUFFER_ALLOCATION, + "error buffer alloc: %d", res); + goto error; + } + g_debug ("allocated %d buffers %p", priv->n_buffers, priv->buffers); + priv->allocated = TRUE; } - g_debug ("allocated %d buffers %p", priv->n_buffers, priv->buffers); - priv->allocated = TRUE; if (in_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) { if ((res = spa_node_port_alloc_buffers (this->input->node->node, this->input->port, @@ -458,7 +488,10 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state) "error alloc input buffers: %d", res); goto error; } + this->input->buffers = priv->buffers; + this->input->n_buffers = priv->n_buffers; this->input->allocated = TRUE; + priv->allocated = FALSE; g_debug ("allocated %d buffers %p from input port", priv->n_buffers, priv->buffers); } else if (out_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) { @@ -471,10 +504,14 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state) "error alloc output buffers: %d", res); goto error; } + this->output->buffers = priv->buffers; + this->output->n_buffers = priv->n_buffers; this->output->allocated = TRUE; + priv->allocated = FALSE; g_debug ("allocated %d buffers %p from output port", priv->n_buffers, priv->buffers); } } + if (in_flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS) { g_debug ("using %d buffers %p on input port", priv->n_buffers, priv->buffers); if ((res = spa_node_port_use_buffers (this->input->node->node, this->input->port, @@ -485,6 +522,8 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state) "error use input buffers: %d", res); goto error; } + this->input->buffers = priv->buffers; + this->input->n_buffers = priv->n_buffers; this->input->allocated = FALSE; } else if (out_flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS) { @@ -497,21 +536,27 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state) "error use output buffers: %d", res); goto error; } + this->output->buffers = priv->buffers; + this->output->n_buffers = priv->n_buffers; this->output->allocated = FALSE; } else { g_set_error (&error, PINOS_ERROR, PINOS_ERROR_BUFFER_ALLOCATION, "no common buffer alloc found"); - goto error; - this->input->allocated = FALSE; - this->output->allocated = FALSE; + goto error; } return res; error: { + this->output->buffers = NULL; + this->output->n_buffers = 0; + this->output->allocated = FALSE; + this->input->buffers = NULL; + this->input->n_buffers = 0; + this->input->allocated = FALSE; pinos_link_report_error (this, error); return res; } @@ -624,11 +669,22 @@ on_property_notify (GObject *obj, static void on_node_remove (PinosNode *node, PinosLink *this) { + PinosLinkPrivate *priv = this->priv; + g_signal_handlers_disconnect_by_data (node, this); - if (node == this->input->node) + if (node == this->input->node) { + if (this->input->allocated) { + priv->buffers = NULL; + priv->n_buffers = 0; + } this->input = NULL; - else + } else { + if (this->output->allocated) { + priv->buffers = NULL; + priv->n_buffers = 0; + } this->output = NULL; + } pinos_link_update_state (this, PINOS_LINK_STATE_UNLINKED); } @@ -688,6 +744,9 @@ pinos_link_finalize (GObject * object) g_clear_object (&priv->iface); g_free (priv->object_path); + if (priv->allocated) + pinos_memblock_free (&priv->buffer_mem); + G_OBJECT_CLASS (pinos_link_parent_class)->finalize (object); } diff --git a/pinos/server/link.h b/pinos/server/link.h index cb3f1314d..cdf017958 100644 --- a/pinos/server/link.h +++ b/pinos/server/link.h @@ -44,6 +44,8 @@ typedef struct { PinosNode *node; uint32_t port; gboolean allocated; + SpaBuffer **buffers; + guint n_buffers; } PinosPort; /** diff --git a/pinos/server/memfd-wrappers.h b/pinos/server/memfd-wrappers.h new file mode 100644 index 000000000..8d6ac8b2a --- /dev/null +++ b/pinos/server/memfd-wrappers.h @@ -0,0 +1,59 @@ +/* Simple Plugin API + * 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 + +/* + * No glibc wrappers exist for memfd_create(2), so provide our own. + * + * Also define memfd fcntl sealing macros. While they are already + * defined in the kernel header file , that file as + * a whole conflicts with the original glibc header . + */ + +static inline int memfd_create(const char *name, unsigned int flags) { + return syscall(SYS_memfd_create, name, flags); +} + +/* memfd_create(2) flags */ + +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +/* fcntl() seals-related flags */ + +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) + +#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +#define F_SEAL_GROW 0x0004 /* prevent file from growing */ +#define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif diff --git a/pinos/server/meson.build b/pinos/server/meson.build index bba3711a6..c71220cc2 100644 --- a/pinos/server/meson.build +++ b/pinos/server/meson.build @@ -8,6 +8,7 @@ pinoscore_headers = [ 'node.h', 'node-factory.h', 'rt-loop.h', + 'utils.h', ] pinoscore_sources = [ @@ -20,6 +21,7 @@ pinoscore_sources = [ 'node.c', 'node-factory.c', 'rt-loop.c', + 'utils.c', ] libpinoscore_c_args = [ diff --git a/pinos/server/node.c b/pinos/server/node.c index c79b745cf..095693b0a 100644 --- a/pinos/server/node.c +++ b/pinos/server/node.c @@ -283,12 +283,16 @@ suspend_node (PinosNode *this) NodePort *p = walk->data; if ((res = spa_node_port_set_format (this->node, p->port.port, 0, NULL)) < 0) g_warning ("error unset format output: %d", res); + p->port.buffers = NULL; + p->port.n_buffers = 0; p->port.allocated = FALSE; } for (walk = priv->output_ports; walk; walk = g_list_next (walk)) { NodePort *p = walk->data; if ((res = spa_node_port_set_format (this->node, p->port.port, 0, NULL)) < 0) g_warning ("error unset format output: %d", res); + p->port.buffers = NULL; + p->port.n_buffers = 0; p->port.allocated = FALSE; } return res; diff --git a/pinos/server/utils.c b/pinos/server/utils.c new file mode 100644 index 000000000..64397e762 --- /dev/null +++ b/pinos/server/utils.c @@ -0,0 +1,91 @@ +/* Pinos + * 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 "memfd-wrappers.h" + +#include + +gboolean +pinos_memblock_alloc (PinosMemblockFlags flags, + gsize size, + PinosMemblock *mem) +{ + g_return_val_if_fail (mem != NULL, FALSE); + g_return_val_if_fail (size > 0, FALSE); + + mem->flags = flags; + mem->size = size; + + if (flags & PINOS_MEMBLOCK_FLAG_WITH_FD) { + mem->fd = memfd_create ("pinos-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING); + + if (ftruncate (mem->fd, size) < 0) { + g_warning ("Failed to truncate temporary file: %s", strerror (errno)); + close (mem->fd); + return FALSE; + } + if (flags & PINOS_MEMBLOCK_FLAG_SEAL) { + unsigned int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; + if (fcntl (mem->fd, F_ADD_SEALS, seals) == -1) { + g_warning ("Failed to add seals: %s", strerror (errno)); + } + } + if (flags & PINOS_MEMBLOCK_FLAG_MAP_READWRITE) { + int prot = 0; + + if (flags & PINOS_MEMBLOCK_FLAG_MAP_READ) + prot |= PROT_READ; + if (flags & PINOS_MEMBLOCK_FLAG_MAP_WRITE) + prot |= PROT_WRITE; + + mem->ptr = mmap (NULL, size, prot, MAP_SHARED, mem->fd, 0); + } else { + mem->ptr = NULL; + } + } else { + mem->ptr = g_malloc (size); + mem->fd = -1; + } + return TRUE; +} + +void +pinos_memblock_free (PinosMemblock *mem) +{ + g_return_if_fail (mem != NULL); + + if (mem->flags & PINOS_MEMBLOCK_FLAG_WITH_FD) { + if (mem->ptr) + munmap (mem->ptr, mem->size); + if (mem->fd != -1) + close (mem->fd); + } else { + g_free (mem->ptr); + } + mem->ptr = NULL; + mem->fd = -1; +} diff --git a/pinos/server/utils.h b/pinos/server/utils.h new file mode 100644 index 000000000..894312c87 --- /dev/null +++ b/pinos/server/utils.h @@ -0,0 +1,55 @@ +/* Pinos + * 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 __PINOS_UTILS_H__ +#define __PINOS_UTILS_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _PinosMemblock PinosMemblock; + +#include + +typedef enum { + PINOS_MEMBLOCK_FLAG_NONE = 0, + PINOS_MEMBLOCK_FLAG_WITH_FD = (1 << 0), + PINOS_MEMBLOCK_FLAG_MAP_READ = (1 << 1), + PINOS_MEMBLOCK_FLAG_MAP_WRITE = (1 << 2), + PINOS_MEMBLOCK_FLAG_SEAL = (1 << 3), +} PinosMemblockFlags; + +#define PINOS_MEMBLOCK_FLAG_MAP_READWRITE (PINOS_MEMBLOCK_FLAG_MAP_READ | PINOS_MEMBLOCK_FLAG_MAP_WRITE) + +struct _PinosMemblock { + PinosMemblockFlags flags; + int fd; + gpointer *ptr; + gsize size; +}; + +gboolean pinos_memblock_alloc (PinosMemblockFlags flags, + gsize size, + PinosMemblock *mem); +void pinos_memblock_free (PinosMemblock *mem); + +G_END_DECLS + +#endif /* __PINOS_UTILS_H__ */ diff --git a/spa/include/spa/buffer.h b/spa/include/spa/buffer.h index ed18e7f56..e9a1d9b09 100644 --- a/spa/include/spa/buffer.h +++ b/spa/include/spa/buffer.h @@ -164,21 +164,18 @@ size_t spa_buffer_get_size (const SpaBuffer *buffer); size_t spa_buffer_serialize (void *dest, const SpaBuffer *buffer); SpaBuffer * spa_buffer_deserialize (void *src, off_t offset); -/** - * SpaBufferAllocFlags: - * @SPA_BUFFER_ALLOC_FLAG_NONE: no flags - * @SPA_BUFFER_ALLOC_FLAG_NO_MEM: don't allocate memory - */ -typedef enum { - SPA_BUFFER_ALLOC_FLAG_NONE = 0, - SPA_BUFFER_ALLOC_FLAG_NO_MEM, -} SpaBufferAllocFlags; -SpaResult spa_buffer_alloc (SpaBufferAllocFlags flags, - SpaAllocParam **params, - unsigned int n_params, - SpaBuffer **buffers, - unsigned int *n_buffers); + +SpaResult spa_alloc_params_get_header_size (SpaAllocParam **params, + unsigned int n_params, + unsigned int n_datas, + size_t *size); + +SpaResult spa_buffer_init_headers (SpaAllocParam **params, + unsigned int n_params, + unsigned int n_datas, + SpaBuffer **buffers, + unsigned int n_buffers); #ifdef __cplusplus } /* extern "C" */ diff --git a/spa/include/spa/node.h b/spa/include/spa/node.h index 67187615c..2b8a2784d 100644 --- a/spa/include/spa/node.h +++ b/spa/include/spa/node.h @@ -346,7 +346,9 @@ struct _SpaNode { * the READY state or to CONFIGURE when @format is NULL. * * Returns: #SPA_RESULT_OK on success - * #SPA_RESULT_OK_RECHECK on success + * #SPA_RESULT_OK_RECHECK on success, the value of @format might have been + * changed depending on @flags and the final value can be found by + * doing SpaNode::get_format. * #SPA_RESULT_INVALID_ARGUMENTS when node is %NULL * #SPA_RESULT_INVALID_PORT when port_id is not valid * #SPA_RESULT_INVALID_MEDIA_TYPE when the media type is not valid @@ -411,8 +413,8 @@ struct _SpaNode { * on the buffers. * * Upon completion, this function might change the state of the - * node to PAUSED, when the node has enough buffers, or READY when - * @buffers are %NULL. + * node to PAUSED, when the node has enough buffers on all ports, or READY + * when @buffers are %NULL. * * Returns: #SPA_RESULT_OK on success * #SPA_RESULT_ASYNC the function is executed asynchronously @@ -432,6 +434,10 @@ struct _SpaNode { * * Tell the port to allocate memory for @buffers. * + * @params should contain an array of pointers to buffers. The data + * in the buffers should point to an array of at least 1 SPA_DATA_TYPE_INVALID + * data pointers that will be filled by this function. + * * For input ports, the buffers will be dequeued and ready to be filled * and pushed into the port. A notify should be configured so that you can * know when a buffer can be reused. @@ -439,7 +445,15 @@ struct _SpaNode { * For output ports, the buffers remain queued. port_reuse_buffer() should * be called when a buffer can be reused. * + * Upon completion, this function might change the state of the + * node to PAUSED, when the node has enough buffers on all ports. + * + * Once the port has allocated buffers, the memory of the buffers can be + * released again by calling SpaNode::port_use_buffers with %NULL. + * * Returns: #SPA_RESULT_OK on success + * #SPA_RESULT_ERROR when the node already has allocated buffers. + * #SPA_RESULT_ASYNC the function is executed asynchronously */ SpaResult (*port_alloc_buffers) (SpaNode *node, uint32_t port_id, diff --git a/spa/lib/buffer.c b/spa/lib/buffer.c index f95b771ab..6037d1e4b 100644 --- a/spa/lib/buffer.c +++ b/spa/lib/buffer.c @@ -25,62 +25,35 @@ #include #include -typedef struct { - SpaBuffer buffer; - SpaMeta metas[3]; - SpaMetaHeader header; - SpaMetaRingbuffer ringbuffer; - SpaMetaVideoCrop crop; - SpaData datas[4]; -} Buffer; +static const size_t header_sizes[] = { + 0, + sizeof (SpaMetaHeader), + sizeof (SpaMetaPointer), + sizeof (SpaMetaVideoCrop), + sizeof (SpaMetaRingbuffer), +}; SpaResult -spa_buffer_alloc (SpaBufferAllocFlags flags, - SpaAllocParam **params, - unsigned int n_params, - SpaBuffer **buffers, - unsigned int *n_buffers) +spa_alloc_params_get_header_size (SpaAllocParam **params, + unsigned int n_params, + unsigned int n_datas, + size_t *size) { - unsigned int i, nbufs; - size_t size = 0, stride = 0; - void *mem; - Buffer *bufs; - bool add_header = false; - int n_metas = 0; + unsigned int i, n_metas = 0; - nbufs = *n_buffers; - if (nbufs == 0) - return SPA_RESULT_ERROR; + *size = sizeof (SpaBuffer); for (i = 0; i < n_params; i++) { SpaAllocParam *p = params[i]; switch (p->type) { - case SPA_ALLOC_PARAM_TYPE_BUFFERS: - { - SpaAllocParamBuffers *b = (SpaAllocParamBuffers *) p; - - size = SPA_MAX (size, b->minsize); - break; - } case SPA_ALLOC_PARAM_TYPE_META_ENABLE: { SpaAllocParamMetaEnable *b = (SpaAllocParamMetaEnable *) p; - switch (b->type) { - case SPA_META_TYPE_HEADER: - if (!add_header) - n_metas++; - add_header = true; - break; - case SPA_META_TYPE_POINTER: - break; - case SPA_META_TYPE_VIDEO_CROP: - break; - case SPA_META_TYPE_RINGBUFFER: - break; - default: - break; + if (b->type > 0 && b->type < SPA_N_ELEMENTS (header_sizes)) { + n_metas++; + *size += header_sizes[b->type]; } break; } @@ -88,52 +61,71 @@ spa_buffer_alloc (SpaBufferAllocFlags flags, break; } } + *size += n_metas * sizeof (SpaMeta); + *size += n_datas * sizeof (SpaData); - *n_buffers = nbufs; + return SPA_RESULT_OK; +} - if (flags & SPA_BUFFER_ALLOC_FLAG_NO_MEM) - size = 0; +SpaResult +spa_buffer_init_headers (SpaAllocParam **params, + unsigned int n_params, + unsigned int n_datas, + SpaBuffer **buffers, + unsigned int n_buffers) +{ + unsigned int i; + int n_metas = 0; - mem = calloc (nbufs, sizeof (Buffer) + size); + for (i = 0; i < n_params; i++) { + SpaAllocParam *p = params[i]; - bufs = mem; - - for (i = 0; i < nbufs; i++) { - int mi = 0; - Buffer *b; - - b = &bufs[i]; - b->buffer.id = i; - - buffers[i] = &b->buffer; - - b->buffer.n_metas = n_metas; - b->buffer.metas = b->metas; - b->buffer.n_datas = 1; - b->buffer.datas = b->datas; - - if (add_header) { - b->header.flags = 0; - b->header.seq = 0; - b->header.pts = 0; - b->header.dts_offset = 0; - - b->metas[mi].type = SPA_META_TYPE_HEADER; - b->metas[mi].data = &b->header; - b->metas[mi].size = sizeof (b->header); - mi++; + switch (p->type) { + case SPA_ALLOC_PARAM_TYPE_META_ENABLE: + { + SpaAllocParamMetaEnable *b = (SpaAllocParamMetaEnable *) p; + if (b->type > 0 && b->type <= SPA_N_ELEMENTS (header_sizes)) + n_metas++; + } + default: + break; } + } - if (size == 0) { - b->datas[0].type = SPA_DATA_TYPE_INVALID; - b->datas[0].data = NULL; - } else { - b->datas[0].type = SPA_DATA_TYPE_MEMPTR; - b->datas[0].data = mem + sizeof (Buffer); + for (i = 0; i < n_buffers; i++) { + int mi = 0, j; + SpaBuffer *b; + void *p; + + b = buffers[i]; + b->id = i; + b->n_metas = n_metas; + b->metas = SPA_MEMBER (b, sizeof (SpaBuffer), SpaMeta); + b->n_datas = n_datas; + b->datas = SPA_MEMBER (b->metas, sizeof (SpaMeta) * n_metas, SpaData); + p = SPA_MEMBER (b->datas, sizeof (SpaData) * n_datas, void); + + for (j = 0, mi = 0; j < n_params; j++) { + SpaAllocParam *prm = params[j]; + + switch (prm->type) { + case SPA_ALLOC_PARAM_TYPE_META_ENABLE: + { + SpaAllocParamMetaEnable *pme = (SpaAllocParamMetaEnable *) prm; + + if (pme->type > 0 && pme->type <= SPA_N_ELEMENTS (header_sizes)) { + b->metas[mi].type = pme->type; + b->metas[mi].data = p; + b->metas[mi].size = header_sizes[pme->type]; + p = SPA_MEMBER (p, header_sizes[pme->type], void); + mi++; + } + break; + } + default: + break; + } } - b->datas[0].offset = size * i; - b->datas[0].size = size; - b->datas[0].stride = stride; } return SPA_RESULT_OK; } @@ -150,7 +142,7 @@ spa_buffer_get_size (const SpaBuffer *buffer) size = sizeof (SpaBuffer); for (i = 0; i < buffer->n_metas; i++) - size += buffer->metas[i].size * sizeof (SpaMeta); + size += sizeof (SpaMeta) + buffer->metas[i].size; for (i = 0; i < buffer->n_datas; i++) size += sizeof (SpaData); return size; diff --git a/spa/plugins/remote/proxy.c b/spa/plugins/remote/proxy.c index 97ff47ab3..1bb84d09e 100644 --- a/spa/plugins/remote/proxy.c +++ b/spa/plugins/remote/proxy.c @@ -51,8 +51,8 @@ typedef struct _ProxyBuffer ProxyBuffer; struct _ProxyBuffer { SpaBuffer *outbuf; - SpaBuffer buffer; + SpaMeta metas[4]; SpaData datas[4]; off_t offset; size_t size; @@ -172,6 +172,8 @@ clear_buffers (SpaProxy *this, SpaProxyPort *port) if (port->n_buffers) { fprintf (stderr, "proxy %p: clear buffers\n", this); + munmap (port->buffer_mem_ptr, port->buffer_mem_size); + close (port->buffer_mem_fd); port->n_buffers = 0; SPA_QUEUE_INIT (&port->ready); @@ -400,7 +402,7 @@ do_update_port (SpaProxy *this, if (pu->change_mask & SPA_CONTROL_CMD_PORT_UPDATE_PROPS) { } - if (pu->change_mask & SPA_CONTROL_CMD_PORT_UPDATE_INFO) { + if (pu->change_mask & SPA_CONTROL_CMD_PORT_UPDATE_INFO && pu->info) { port->info = *pu->info; } @@ -691,12 +693,17 @@ spa_proxy_node_port_use_buffers (SpaNode *node, b->outbuf = buffers[i]; memcpy (&b->buffer, buffers[i], sizeof (SpaBuffer)); b->buffer.datas = b->datas; + b->buffer.metas = b->metas; b->size = spa_buffer_get_size (buffers[i]); b->offset = size; size += b->size; + for (j = 0; j < buffers[i]->n_datas; j++) { + memcpy (&b->buffer.metas[j], &buffers[i]->metas[j], sizeof (SpaMeta)); + } + for (j = 0; j < buffers[i]->n_datas; j++) { SpaData *d = &buffers[i]->datas[j]; @@ -723,18 +730,32 @@ spa_proxy_node_port_use_buffers (SpaNode *node, if (ftruncate (port->buffer_mem_fd, size) < 0) { fprintf (stderr, "Failed to truncate temporary file: %s\n", strerror (errno)); close (port->buffer_mem_fd); + return SPA_RESULT_ERROR; } { unsigned int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; if (fcntl (port->buffer_mem_fd, F_ADD_SEALS, seals) == -1) { fprintf (stderr, "Failed to add seals: %s\n", strerror (errno)); - close (port->buffer_mem_fd); } } p = port->buffer_mem_ptr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, port->buffer_mem_fd, 0); - for (i = 0; i < n_buffers; i++) - p += spa_buffer_serialize (p, &port->buffers[i].buffer); + for (i = 0; i < n_buffers; i++) { + ProxyBuffer *b = &port->buffers[i]; + size_t len; + SpaBuffer *sb; + SpaMeta *sbm; + + len = spa_buffer_serialize (p, &b->buffer); + + sb = p; + sbm = SPA_MEMBER (sb, SPA_PTR_TO_INT (sb->metas), SpaMeta); + + for (j = 0; j < b->buffer.n_metas; j++) + b->metas[j].data = SPA_MEMBER (sb, SPA_PTR_TO_INT (sbm[j].data), void); + + p += len; + } am.port_id = port_id; am.mem_id = port->buffer_mem_id; @@ -837,6 +858,19 @@ spa_proxy_node_port_reuse_buffer (SpaNode *node, return res; } +static void +copy_meta (SpaProxy *this, SpaProxyPort *port, uint32_t buffer_id) +{ + ProxyBuffer *b = &port->buffers[buffer_id]; + unsigned int i; + + for (i = 0; i < b->outbuf->n_metas; i++) { + SpaMeta *sm = &b->outbuf->metas[i]; + SpaMeta *dm = &b->buffer.metas[i]; + memcpy (dm->data, sm->data, dm->size); + } +} + static SpaResult spa_proxy_node_port_push_input (SpaNode *node, unsigned int n_info, @@ -883,6 +917,8 @@ spa_proxy_node_port_push_input (SpaNode *node, continue; } + copy_meta (this, port, info[i].buffer_id); + pb.port_id = info[i].port_id; pb.buffer_id = info[i].buffer_id; spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_PROCESS_BUFFER, &pb); diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index 22c7f561b..069de19cd 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -78,7 +78,6 @@ struct _V4l2Format { typedef struct { bool export_buf; - bool have_buffers; bool started; bool next_fmtdesc; @@ -98,16 +97,17 @@ typedef struct { enum v4l2_buf_type type; enum v4l2_memory memtype; - struct v4l2_requestbuffers reqbuf; - V4l2Buffer buffers[MAX_BUFFERS]; - SpaQueue ready; + V4l2Buffer buffers[MAX_BUFFERS]; + unsigned int n_buffers; + SpaQueue ready; SpaPollFd fds[1]; SpaPollItem poll; SpaPortInfo info; - SpaAllocParam *params[1]; + SpaAllocParam *params[2]; SpaAllocParamBuffers param_buffers; + SpaAllocParamMetaEnable param_meta; SpaPortStatus status; int64_t last_ticks; @@ -228,7 +228,7 @@ spa_v4l2_source_node_send_command (SpaNode *node, if (state->current_format == NULL) return SPA_RESULT_NO_FORMAT; - if (!state->have_buffers) + if (state->n_buffers == 0) return SPA_RESULT_NO_BUFFERS; if ((res = spa_v4l2_start (this)) < 0) @@ -243,7 +243,7 @@ spa_v4l2_source_node_send_command (SpaNode *node, if (state->current_format == NULL) return SPA_RESULT_NO_FORMAT; - if (!state->have_buffers) + if (state->n_buffers == 0) return SPA_RESULT_NO_BUFFERS; if ((res = spa_v4l2_pause (this)) < 0) @@ -533,7 +533,7 @@ spa_v4l2_source_node_port_use_buffers (SpaNode *node, if (state->current_format == NULL) return SPA_RESULT_NO_FORMAT; - if (state->have_buffers) { + if (state->n_buffers) { if ((res = spa_v4l2_clear_buffers (this)) < 0) return res; } @@ -542,12 +542,12 @@ spa_v4l2_source_node_port_use_buffers (SpaNode *node, return res; } - if (state->have_buffers) + if (state->n_buffers) update_state (this, SPA_NODE_STATE_PAUSED); else update_state (this, SPA_NODE_STATE_READY); - return res; + return SPA_RESULT_OK; } static SpaResult @@ -577,7 +577,7 @@ spa_v4l2_source_node_port_alloc_buffers (SpaNode *node, res = spa_v4l2_alloc_buffers (this, params, n_params, buffers, n_buffers); - if (state->have_buffers) { + if (state->n_buffers) { if (state->started) update_state (this, SPA_NODE_STATE_STREAMING); else @@ -606,10 +606,10 @@ spa_v4l2_source_node_port_reuse_buffer (SpaNode *node, state = &this->state[port_id]; - if (!state->have_buffers) + if (state->n_buffers == 0) return SPA_RESULT_NO_BUFFERS; - if (buffer_id >= state->reqbuf.count) + if (buffer_id >= state->n_buffers) return SPA_RESULT_INVALID_BUFFER_ID; res = spa_v4l2_buffer_recycle (this, buffer_id); diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index 8db87da27..76a388233 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -92,12 +92,13 @@ static SpaResult spa_v4l2_clear_buffers (SpaV4l2Source *this) { SpaV4l2State *state = &this->state[0]; + struct v4l2_requestbuffers reqbuf; int i; - if (!state->have_buffers) + if (state->n_buffers == 0) return SPA_RESULT_OK; - for (i = 0; i < state->reqbuf.count; i++) { + for (i = 0; i < state->n_buffers; i++) { V4l2Buffer *b; b = &state->buffers[i]; @@ -105,9 +106,20 @@ spa_v4l2_clear_buffers (SpaV4l2Source *this) fprintf (stderr, "queueing outstanding buffer %p\n", b); spa_v4l2_buffer_recycle (this, i); } + if (state->export_buf) { + close (SPA_PTR_TO_INT (b->outbuf->datas[0].data)); + } } - state->have_buffers = false; + CLEAR(reqbuf); + reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + reqbuf.memory = state->memtype; + reqbuf.count = 0; + + if (xioctl (state->fd, VIDIOC_REQBUFS, &reqbuf) < 0) { + perror ("VIDIOC_REQBUFS"); + } + state->n_buffers = 0; return SPA_RESULT_OK; } @@ -120,7 +132,7 @@ spa_v4l2_close (SpaV4l2Source *this) if (!state->opened) return 0; - if (state->have_buffers) + if (state->n_buffers > 0) return 0; fprintf (stderr, "close\n"); @@ -786,7 +798,7 @@ spa_v4l2_set_format (SpaV4l2Source *this, V4l2Format *f, bool try_only) state->info.latency = (streamparm.parm.capture.timeperframe.numerator * SPA_NSEC_PER_SEC) / streamparm.parm.capture.timeperframe.denominator; - state->info.n_params = 1; + state->info.n_params = 2; state->info.params = state->params; state->params[0] = &state->param_buffers.param; state->param_buffers.param.type = SPA_ALLOC_PARAM_TYPE_BUFFERS; @@ -796,6 +808,11 @@ spa_v4l2_set_format (SpaV4l2Source *this, V4l2Format *f, bool try_only) state->param_buffers.min_buffers = 2; state->param_buffers.max_buffers = MAX_BUFFERS; state->param_buffers.align = 16; + state->params[1] = &state->param_meta.param; + state->param_meta.param.type = SPA_ALLOC_PARAM_TYPE_META_ENABLE; + state->param_meta.param.size = sizeof (state->param_meta); + state->param_meta.type = SPA_META_TYPE_HEADER; + state->info.features = NULL; return 0; @@ -873,6 +890,18 @@ v4l2_on_fd_events (SpaPollNotifyData *data) return 0; } +static void * +find_meta_data (SpaBuffer *b, SpaMetaType type) +{ + unsigned int i; + + for (i = 0; i < b->n_metas; i++) + if (b->metas[i].type == type) + return b->metas[i].data; + return NULL; +} + + static SpaResult spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffers) { @@ -896,7 +925,6 @@ spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffe fprintf (stderr, "can't allocate enough buffers\n"); return SPA_RESULT_ERROR; } - state->reqbuf = reqbuf; for (i = 0; i < reqbuf.count; i++) { V4l2Buffer *b; @@ -905,6 +933,7 @@ spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffe b = &state->buffers[i]; b->outbuf = buffers[i]; b->outstanding = true; + b->h = find_meta_data (b->outbuf, SPA_META_TYPE_HEADER); fprintf (stderr, "import buffer %p\n", buffers[i]); @@ -923,7 +952,7 @@ spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffe spa_v4l2_buffer_recycle (this, buffers[i]->id); } - state->have_buffers = true; + state->n_buffers = reqbuf.count; return SPA_RESULT_OK; } @@ -961,8 +990,6 @@ mmap_init (SpaV4l2Source *this, if (state->export_buf) fprintf (stderr, "using EXPBUF\n"); - state->reqbuf = reqbuf; - for (i = 0; i < reqbuf.count; i++) { V4l2Buffer *b; SpaData *d; @@ -975,6 +1002,7 @@ mmap_init (SpaV4l2Source *this, b = &state->buffers[i]; b->outbuf = buffers[i]; b->outstanding = true; + b->h = find_meta_data (b->outbuf, SPA_META_TYPE_HEADER); CLEAR (b->v4l2_buffer); b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -997,17 +1025,19 @@ mmap_init (SpaV4l2Source *this, CLEAR (expbuf); expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; expbuf.index = i; + expbuf.flags = O_CLOEXEC | O_RDONLY; if (xioctl (state->fd, VIDIOC_EXPBUF, &expbuf) < 0) { perror("VIDIOC_EXPBUF"); continue; } + fprintf (stderr, "expbuf %d\n", expbuf.fd); d[0].type = SPA_DATA_TYPE_FD; d[0].data = SPA_INT_TO_PTR (expbuf.fd); } else { d[0].type = SPA_DATA_TYPE_MEMPTR; d[0].data = mmap (NULL, b->v4l2_buffer.length, - PROT_READ | PROT_WRITE, + PROT_READ, MAP_SHARED, state->fd, b->v4l2_buffer.m.offset); @@ -1018,7 +1048,7 @@ mmap_init (SpaV4l2Source *this, } spa_v4l2_buffer_recycle (this, i); } - state->have_buffers = true; + state->n_buffers = reqbuf.count; return SPA_RESULT_OK; } @@ -1045,7 +1075,7 @@ spa_v4l2_alloc_buffers (SpaV4l2Source *this, SpaResult res; SpaV4l2State *state = &this->state[0]; - if (state->have_buffers) + if (state->n_buffers > 0) return SPA_RESULT_ERROR; if (state->cap.capabilities & V4L2_CAP_STREAMING) { @@ -1123,7 +1153,7 @@ spa_v4l2_pause (SpaV4l2Source *this) perror ("VIDIOC_STREAMOFF"); return SPA_RESULT_ERROR; } - for (i = 0; i < state->reqbuf.count; i++) { + for (i = 0; i < state->n_buffers; i++) { V4l2Buffer *b; b = &state->buffers[i];