diff --git a/doc/design.txt b/doc/design.txt index 14c5f02a2..c534bd0d3 100644 --- a/doc/design.txt +++ b/doc/design.txt @@ -370,3 +370,95 @@ Types: 1 = keyframe+full headers : the timestamp when the refresh should be, 0 for as soon as possible + + + +communication channel +--------------------- + + + +-----+ +----+ +----+ + | | | S | | | + | ----- ----- | + +-----+ +----+ +----+ + | + | + | + | + +----+ + | | + | C | + +----+ + + +1) Update config C->S INIT + + node-update + port-update + start-configure + +2) Set formats S->C CONFIGURE + + add-port + set-property + enumerate ports + enumerate formats + set-format + end-configure + +3) Buffer requirements update C->S BUFFER_REQS + + Update port status + start-alloc + +4) Start S->C ALLOC + + read port memory requirements + add_mem + add_buffer + pause/start + +5) Pause S->C PAUSED + + start/stop + +5) data transfer C->S STARTED + + need-input + add_mem + add_buffer + process_buffer + reuse_buffer + remove_buffer + remove_mem + have-output + pause/stop + +6) data transfer S->C + + add_mem + add_buffer + process_buffer + reuse_buffer + remove_buffer + remove_mem + pause/stop + +7) format change C->S + + port-update + start-configure + +8) format change S->C + + Send set-format change on ports + end-configure, back to 3 + +9) stop S->C ALLOC + + remove_buffer + remove_mem + +10) clear format S->C CONFIGURE + + format-change to NULL diff --git a/pinos/Makefile.am b/pinos/Makefile.am index b3c0424ab..e2d7c2403 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -40,8 +40,8 @@ AM_CFLAGS = $(GLIB_CFLAGS) $(GST_CFLAGS) AM_CXXFLAGS = $(AM_CFLAGS) SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS -AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so -AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so +AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS) -L$(top_srcdir)/spa/build/lib/ -lspa-lib +AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS) -L$(top_srcdir)/spa/build/lib/ -lspa-lib AM_LDFLAGS = $(NODELETE_LDFLAGS) FOREIGN_CFLAGS = -w @@ -161,9 +161,7 @@ pinos_monitor_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) # Client library # ################################### -pinosgstsource = gst/gstpinospay.h gst/gstpinospay.c \ - gst/gstpinosdepay.h gst/gstpinosdepay.c \ - gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c +pinosgstsource = gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c pinosinclude_HEADERS = \ client/buffer.h \ @@ -227,8 +225,8 @@ libpinoscore_@PINOS_MAJORMINOR@_la_SOURCES = \ libpinoscore_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) libpinoscore_@PINOS_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version -libpinoscore_@PINOS_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LTLIBICONV) \ - libpinos-@PINOS_MAJORMINOR@.la +libpinoscore_@PINOS_MAJORMINOR@_la_LIBADD = $(LIBLTDL) $(LTLIBICONV) \ + libpinos-@PINOS_MAJORMINOR@.la $(AM_LIBADD) ################################### # GStreamer Plugin # @@ -241,19 +239,19 @@ plugin_LTLIBRARIES = libgstpinos.la libgstpinos_la_SOURCES = \ gst/gstburstcache.c \ gst/gstpinos.c \ - gst/gstpinossocketsink.c \ - gst/gstpinosportsink.c \ - gst/gstpinosportsrc.c \ - gst/gstpinospay.c \ - gst/gstpinosdepay.c \ gst/gstpinosdeviceprovider.c \ gst/gstpinossrc.c \ gst/gstpinossink.c + #gst/gstpinosdepay.c + #gst/gstpinosportsrc.c + #gst/gstpinosportsink.c + #gst/gstpinossocketsink.c + #gst/gstpinospay.c libgstpinos_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GLIB_CFLAGS) -libgstpinos_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstpinos_la_LDFLAGS = $(AM_LDFLAGS) $(GST_PLUGIN_LDFLAGS) libgstpinos_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(GLIB_LIBS) $(LIBM) -lgstvideo-1.0 \ - libpinos-@PINOS_MAJORMINOR@.la libpinoscore-@PINOS_MAJORMINOR@.la + libpinos-@PINOS_MAJORMINOR@.la libpinoscore-@PINOS_MAJORMINOR@.la $(AM_LIBADD) libgstpinos_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) noinst_HEADERS = gst/gstburstcache.h gst/gstpinossrc.h \ diff --git a/pinos/client/buffer.c b/pinos/client/buffer.c index 441cf836a..75cd5f98f 100644 --- a/pinos/client/buffer.c +++ b/pinos/client/buffer.c @@ -21,6 +21,7 @@ #include +#if 0 #include "pinos/client/properties.h" #include "pinos/client/context.h" #include "pinos/client/buffer.h" @@ -1032,3 +1033,4 @@ pinos_buffer_builder_add_refresh_request (PinosBufferBuilder *builder, return TRUE; } +#endif diff --git a/pinos/client/buffer.h b/pinos/client/buffer.h index 313862dde..48d40e4a9 100644 --- a/pinos/client/buffer.h +++ b/pinos/client/buffer.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS typedef struct _PinosBuffer PinosBuffer; +#if 0 typedef struct _PinosBufferIter PinosBufferIter; typedef struct _PinosBufferBuilder PinosBufferBuilder; @@ -329,4 +330,5 @@ gboolean pinos_buffer_builder_add_refresh_request (PinosBufferBuilder PinosPacketRefreshRequest *payload); +#endif #endif /* __PINOS_BUFFER_H__ */ diff --git a/pinos/client/io.c b/pinos/client/io.c index e4564618a..adac35f90 100644 --- a/pinos/client/io.c +++ b/pinos/client/io.c @@ -26,6 +26,7 @@ #include "pinos/client/pinos.h" #include "pinos/client/private.h" +#if 0 gboolean pinos_io_read_buffer (int fd, PinosBuffer *buffer, @@ -177,3 +178,4 @@ send_error: return FALSE; } } +#endif diff --git a/pinos/client/private.h b/pinos/client/private.h index 08f4085de..7b11e01c6 100644 --- a/pinos/client/private.h +++ b/pinos/client/private.h @@ -57,6 +57,7 @@ GDBusProxy * pinos_subscribe_get_proxy_finish (PinosSubscribe *subsc GError **error); +#if 0 typedef struct { guint32 version; guint32 flags; @@ -92,3 +93,4 @@ gboolean pinos_io_read_buffer (int fd, gboolean pinos_io_write_buffer (int fd, PinosBuffer *buffer, GError **error); +#endif diff --git a/pinos/client/stream.c b/pinos/client/stream.c index ef458dee3..54f9204ab 100644 --- a/pinos/client/stream.c +++ b/pinos/client/stream.c @@ -31,10 +31,37 @@ #include "pinos/client/enumtypes.h" #include "pinos/client/private.h" +#include "spa/include/spa/control.h" #define MAX_BUFFER_SIZE 1024 #define MAX_FDS 16 +typedef struct { + bool cleanup; + uint32_t id; + int fd; +} MemId; + +static void +clear_mem_id (MemId *id) +{ + close (id->fd); + id->fd = -1; +} + +typedef struct { + bool cleanup; + uint32_t id; + SpaBuffer *buf; +} BufferId; + +static void +clear_buffer_id (BufferId *id) +{ + spa_buffer_unref (id->buf); + id->buf = NULL; +} + struct _PinosStreamPrivate { PinosContext *context; @@ -61,15 +88,18 @@ struct _PinosStreamPrivate GSource *socket_source; int fd; - PinosBuffer *buffer; - PinosBuffer recv_buffer; + SpaBuffer *buffer; + + SpaControl *control; + SpaControl recv_control; guint8 recv_data[MAX_BUFFER_SIZE]; int recv_fds[MAX_FDS]; guint8 send_data[MAX_BUFFER_SIZE]; int send_fds[MAX_FDS]; - GHashTable *mem_ids; + GArray *mem_ids; + GArray *buffer_ids; }; #define PINOS_STREAM_GET_PRIVATE(obj) \ @@ -260,9 +290,6 @@ pinos_stream_finalize (GObject * object) g_bytes_unref (priv->format); g_free (priv->path); - if (priv->possible_formats) - g_bytes_unref (priv->possible_formats); - g_clear_error (&priv->error); if (priv->properties) @@ -397,6 +424,10 @@ pinos_stream_init (PinosStream * stream) g_debug ("new stream %p", stream); priv->state = PINOS_STREAM_STATE_UNCONNECTED; + priv->mem_ids = g_array_sized_new (FALSE, FALSE, sizeof (MemId), 64); + g_array_set_clear_func (priv->mem_ids, (GDestroyNotify) clear_mem_id); + priv->buffer_ids = g_array_sized_new (FALSE, FALSE, sizeof (BufferId), 64); + g_array_set_clear_func (priv->buffer_ids, (GDestroyNotify) clear_buffer_id); } /** @@ -487,86 +518,224 @@ pinos_stream_get_error (PinosStream *stream) return stream->priv->error; } +static void +send_need_input (PinosStream *stream, uint32_t port) +{ + PinosStreamPrivate *priv = stream->priv; + SpaControlBuilder builder; + SpaControl control; + guint8 buffer[64]; + SpaControlCmdNeedInput ni; + + spa_control_builder_init_into (&builder, buffer, 64, NULL, 0); + ni.port = port; + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_NEED_INPUT, &ni); + spa_control_builder_end (&builder, &control); + + if (spa_control_write (&control, priv->fd) < 0) + g_warning ("stream %p: error writing control", stream); +} + +static BufferId * +find_buffer (PinosStream *stream, uint32_t id) +{ + PinosStreamPrivate *priv = stream->priv; + guint i; + for (i = 0; i < priv->buffer_ids->len; i++) { + BufferId *bid = &g_array_index (priv->buffer_ids, BufferId, i); + if (bid->id == id) + return bid; + } + return NULL; +} + +static MemId * +find_mem (PinosStream *stream, uint32_t id) +{ + PinosStreamPrivate *priv = stream->priv; + guint i; + for (i = 0; i < priv->mem_ids->len; i++) { + MemId *mid = &g_array_index (priv->mem_ids, MemId, i); + if (mid->id == id) + return mid; + } + return NULL; +} static gboolean -parse_buffer (PinosStream *stream, - PinosBuffer *pbuf) +parse_control (PinosStream *stream, + SpaControl *ctrl) { - PinosBufferIter it; + SpaControlIter it; PinosStreamPrivate *priv = stream->priv; - pinos_buffer_iter_init (&it, pbuf); - while (pinos_buffer_iter_next (&it)) { - PinosPacketType type = pinos_buffer_iter_get_type (&it); + spa_control_iter_init (&it, ctrl); + while (spa_control_iter_next (&it) == SPA_RESULT_OK) { + SpaControlCmd cmd = spa_control_iter_get_cmd (&it); - switch (type) { - case PINOS_PACKET_TYPE_ADD_MEM: - { - PinosPacketAddMem p; - int fd; - - if (!pinos_buffer_iter_parse_add_mem (&it, &p)) - break; - - fd = pinos_buffer_get_fd (pbuf, p.fd_index); - if (fd == -1) - break; - -// g_hash_table_insert (priv->mem_ids, GINT_TO_POINTER (p.id), NULL); + switch (cmd) { + case SPA_CONTROL_CMD_NODE_UPDATE: + case SPA_CONTROL_CMD_PORT_UPDATE: + case SPA_CONTROL_CMD_PORT_REMOVED: + case SPA_CONTROL_CMD_START_CONFIGURE: + case SPA_CONTROL_CMD_PORT_STATUS_CHANGE: + case SPA_CONTROL_CMD_START_ALLOC: + case SPA_CONTROL_CMD_NEED_INPUT: + case SPA_CONTROL_CMD_HAVE_OUTPUT: + g_warning ("got unexpected control %d", cmd); break; - } - case PINOS_PACKET_TYPE_REMOVE_MEM: - { - PinosPacketRemoveMem p; - if (!pinos_buffer_iter_parse_remove_mem (&it, &p)) - break; - -// g_hash_table_remove (priv->mem_ids, GINT_TO_POINTER (p.id)); + case SPA_CONTROL_CMD_ADD_PORT: + case SPA_CONTROL_CMD_REMOVE_PORT: + g_warning ("add/remove port not supported"); break; - } - case PINOS_PACKET_TYPE_FORMAT_CHANGE: - { - PinosPacketFormatChange p; - if (!pinos_buffer_iter_parse_format_change (&it, &p)) + case SPA_CONTROL_CMD_SET_FORMAT: + { + SpaControlCmdSetFormat p; + + if (spa_control_iter_parse_cmd (&it, &p) < 0) break; if (priv->format) g_bytes_unref (priv->format); - priv->format = g_bytes_new (p.format, strlen (p.format) + 1); + priv->format = g_bytes_new (p.str, strlen (p.str) + 1); g_object_notify (G_OBJECT (stream), "format"); break; } - case PINOS_PACKET_TYPE_STREAMING: + case SPA_CONTROL_CMD_SET_PROPERTY: + g_warning ("set property not implemented"); + break; + + case SPA_CONTROL_CMD_END_CONFIGURE: { + SpaControlBuilder builder; + SpaControl control; + guint8 buffer[1024]; + + /* FIXME send update port status */ + /* send start-alloc */ + spa_control_builder_init_into (&builder, buffer, 1024, NULL, 0); + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_START_ALLOC, NULL); + spa_control_builder_end (&builder, &control); + + if (spa_control_write (&control, priv->fd) < 0) + g_warning ("stream %p: error writing control", stream); + + break; + } + + case SPA_CONTROL_CMD_PAUSE: + break; + case SPA_CONTROL_CMD_START: + { + if (priv->direction == PINOS_DIRECTION_INPUT) + send_need_input (stream, 0); + stream_set_state (stream, PINOS_STREAM_STATE_STREAMING, NULL); break; } - case PINOS_PACKET_TYPE_STOPPED: + case SPA_CONTROL_CMD_STOP: { - unhandle_socket (stream); - - g_clear_pointer (&priv->format, g_bytes_unref); - g_object_notify (G_OBJECT (stream), "format"); - stream_set_state (stream, PINOS_STREAM_STATE_READY, NULL); break; } - case PINOS_PACKET_TYPE_HEADER: + + case SPA_CONTROL_CMD_ADD_MEM: { + SpaControlCmdAddMem p; + MemId mid; + int fd; + + if (spa_control_iter_parse_cmd (&it, &p) < 0) + break; + + fd = spa_control_get_fd (ctrl, p.fd_index, false); + if (fd == -1) + break; + + mid.cleanup = false; + mid.id = p.id; + mid.fd = fd; + g_array_append_val (priv->mem_ids, mid); break; } - case PINOS_PACKET_TYPE_PROCESS_MEM: + case SPA_CONTROL_CMD_REMOVE_MEM: { + SpaControlCmdRemoveMem p; + MemId *mid; + + if (spa_control_iter_parse_cmd (&it, &p) < 0) + break; + + if ((mid = find_mem (stream, p.id))) + mid->cleanup = true; break; } - default: - g_warning ("unhandled packet %d", type); + case SPA_CONTROL_CMD_ADD_BUFFER: + { + SpaControlCmdAddBuffer p; + BufferId bid; + + if (spa_control_iter_parse_cmd (&it, &p) < 0) + break; + + bid.cleanup = false; + bid.id = p.buffer->id; + bid.buf = p.buffer; + g_array_append_val (priv->buffer_ids, bid); + break; + } + case SPA_CONTROL_CMD_REMOVE_BUFFER: + { + SpaControlCmdRemoveBuffer p; + BufferId *bid; + + if (spa_control_iter_parse_cmd (&it, &p) < 0) + break; + + if ((bid = find_buffer (stream, p.id))) + bid->cleanup = true; + break; + } + case SPA_CONTROL_CMD_PROCESS_BUFFER: + { + SpaControlCmdProcessBuffer p; + unsigned int i; + BufferId *bid; + + if (spa_control_iter_parse_cmd (&it, &p) < 0) + break; + + if ((bid = find_buffer (stream, p.id))) { + SpaBuffer *b = bid->buf; + + for (i = 0; i < b->n_datas; i++) { + SpaData *d = &b->datas[i]; + + if (d->type == SPA_DATA_TYPE_MEMID) { + int id = *((int*)(d->ptr)); + MemId *mid; + + if ((mid = find_mem (stream, id))) { + d->type = SPA_DATA_TYPE_FD; + *((int *)(d->ptr)) = mid->fd; + } + } + } + priv->buffer = b; + } + break; + } + case SPA_CONTROL_CMD_REUSE_BUFFER: + break; + + case SPA_CONTROL_CMD_INVALID: + g_warning ("unhandled command %d", cmd); break; } } - pinos_buffer_iter_end (&it); + spa_control_iter_end (&it); return TRUE; } @@ -582,28 +751,41 @@ on_socket_condition (GSocket *socket, switch (condition) { case G_IO_IN: { - PinosBuffer *buffer = &priv->recv_buffer; - GError *error = NULL; + SpaControl *control = &priv->recv_control; + guint i; - if (!pinos_io_read_buffer (priv->fd, - buffer, - priv->recv_data, - MAX_BUFFER_SIZE, - priv->recv_fds, - MAX_FDS, - &error)) { - g_warning ("stream %p: failed to read buffer: %s", stream, error->message); - g_clear_error (&error); + if (spa_control_read (control, + priv->fd, + priv->recv_data, + MAX_BUFFER_SIZE, + priv->recv_fds, + MAX_FDS) < 0) { + g_warning ("stream %p: failed to read buffer", stream); return TRUE; } - parse_buffer (stream, buffer); + parse_control (stream, control); - priv->buffer = buffer; - g_signal_emit (stream, signals[SIGNAL_NEW_BUFFER], 0, NULL); - priv->buffer = NULL; - - g_assert (pinos_buffer_unref (buffer) == FALSE); + if (priv->buffer) { + g_signal_emit (stream, signals[SIGNAL_NEW_BUFFER], 0, NULL); + priv->buffer = NULL; + send_need_input (stream, 0); + } + for (i = 0; i < priv->mem_ids->len; i++) { + MemId *mid = &g_array_index (priv->mem_ids, MemId, i); + if (mid->cleanup) { + g_array_remove_index_fast (priv->mem_ids, i); + i--; + } + } + for (i = 0; i < priv->buffer_ids->len; i++) { + BufferId *bid = &g_array_index (priv->buffer_ids, BufferId, i); + if (bid->cleanup) { + g_array_remove_index_fast (priv->buffer_ids, i); + i--; + } + } + spa_control_clear (control); break; } @@ -851,29 +1033,39 @@ pinos_stream_connect (PinosStream *stream, return TRUE; } +static void +control_builder_init (PinosStream *stream, SpaControlBuilder *builder) +{ + PinosStreamPrivate *priv = stream->priv; + + spa_control_builder_init_into (builder, + priv->send_data, + MAX_BUFFER_SIZE, + priv->send_fds, + MAX_FDS); +} + static gboolean do_start (PinosStream *stream) { PinosStreamPrivate *priv = stream->priv; - PinosBufferBuilder builder; - PinosPacketFormatChange fc; - PinosBuffer pbuf; - GError *error = NULL; + SpaControlBuilder builder; + SpaControlCmdPortUpdate pu; + SpaControl control; handle_socket (stream, priv->fd); - pinos_stream_buffer_builder_init (stream, &builder); - fc.port = 0; - fc.id = 0; - fc.format = priv->format ? g_bytes_get_data (priv->format, NULL) : "ANY"; - pinos_buffer_builder_add_format_change (&builder, &fc); - pinos_buffer_builder_add_empty (&builder, PINOS_PACKET_TYPE_START); - pinos_buffer_builder_end (&builder, &pbuf); + control_builder_init (stream, &builder); + pu.port = 0; + pu.change_mask = 0; + + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_PORT_UPDATE, &pu); + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_START_CONFIGURE, NULL); + spa_control_builder_end (&builder, &control); + + if (spa_control_write (&control, priv->fd) < 0) + g_warning ("stream %p: failed to write control", stream); - if (!pinos_io_write_buffer (priv->fd, &pbuf, &error)) { - g_warning ("stream %p: failed to read buffer: %s", stream, error->message); - g_clear_error (&error); - } g_object_unref (stream); return FALSE; @@ -923,10 +1115,10 @@ pinos_stream_start (PinosStream *stream, static gboolean do_stop (PinosStream *stream) { - PinosBufferBuilder builder; + SpaControlBuilder builder; - pinos_stream_buffer_builder_init (stream, &builder); - pinos_buffer_builder_add_empty (&builder, PINOS_PACKET_TYPE_STOP); + control_builder_init (stream, &builder); + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_STOP, NULL); g_object_unref (stream); return FALSE; @@ -1048,9 +1240,9 @@ pinos_stream_disconnect (PinosStream *stream) * Get the current buffer from @stream. This function should be called from * the new-buffer signal callback. * - * Returns: a #PinosBuffer or %NULL when there is no buffer. + * Returns: a #SpaBuffer or %NULL when there is no buffer. */ -PinosBuffer * +SpaBuffer * pinos_stream_peek_buffer (PinosStream *stream) { PinosStreamPrivate *priv; @@ -1058,37 +1250,13 @@ pinos_stream_peek_buffer (PinosStream *stream) g_return_val_if_fail (PINOS_IS_STREAM (stream), NULL); priv = stream->priv; - return priv->buffer; -} - -/** - * pinos_stream_buffer_builder_init: - * @stream: a #PinosStream - * @builder: a #PinosBufferBuilder - * - * Get a #PinosBufferBuilder for @stream. - * - * Returns: a #PinosBuffer or %NULL when there is no buffer. - */ -void -pinos_stream_buffer_builder_init (PinosStream *stream, PinosBufferBuilder *builder) -{ - PinosStreamPrivate *priv; - - g_return_if_fail (PINOS_IS_STREAM (stream)); - priv = stream->priv; - - pinos_buffer_builder_init_into (builder, - priv->send_data, - MAX_BUFFER_SIZE, - priv->send_fds, - MAX_FDS); + return spa_buffer_ref (priv->buffer); } /** * pinos_stream_send_buffer: * @stream: a #PinosStream - * @buffer: a #PinosBuffer + * @buffer: a #SpaBuffer * * Send a buffer to @stream. * @@ -1102,20 +1270,17 @@ pinos_stream_buffer_builder_init (PinosStream *stream, PinosBufferBuilder *buil */ gboolean pinos_stream_send_buffer (PinosStream *stream, - PinosBuffer *buffer) + SpaBuffer *buffer) { - PinosStreamPrivate *priv; - GError *error = NULL; - g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE); g_return_val_if_fail (buffer != NULL, FALSE); - priv = stream->priv; - - if (!pinos_io_write_buffer (priv->fd, buffer, &error)) { +#if 0 + if (!spa_control_write (priv->fd, buffer, &error)) { g_warning ("stream %p: failed to write buffer: %s", stream, error->message); g_clear_error (&error); return FALSE; } +#endif return TRUE; } diff --git a/pinos/client/stream.h b/pinos/client/stream.h index b71a4f76c..80b72b76a 100644 --- a/pinos/client/stream.h +++ b/pinos/client/stream.h @@ -24,6 +24,7 @@ #include #include +#include G_BEGIN_DECLS @@ -105,12 +106,9 @@ gboolean pinos_stream_start (PinosStream *stream, PinosStreamMode mode); gboolean pinos_stream_stop (PinosStream *stream); -PinosBuffer * pinos_stream_peek_buffer (PinosStream *stream); -void pinos_stream_buffer_builder_init (PinosStream *stream, - PinosBufferBuilder *builder); +SpaBuffer * pinos_stream_peek_buffer (PinosStream *stream); gboolean pinos_stream_send_buffer (PinosStream *stream, - PinosBuffer *buffer); - + SpaBuffer *buffer); G_END_DECLS #endif /* __PINOS_STREAM_H__ */ diff --git a/pinos/gst/gstpinos.c b/pinos/gst/gstpinos.c index 23c833762..db4ef2f74 100644 --- a/pinos/gst/gstpinos.c +++ b/pinos/gst/gstpinos.c @@ -32,7 +32,7 @@ #include "config.h" #endif -#include "gstpinossocketsink.h" +//#include "gstpinossocketsink.h" #include "gstpinosportsink.h" #include "gstpinosportsrc.h" #include "gstpinossrc.h" @@ -46,20 +46,20 @@ GST_DEBUG_CATEGORY (pinos_debug); static gboolean plugin_init (GstPlugin * plugin) { - gst_element_register (plugin, "pinospay", GST_RANK_NONE, - GST_TYPE_PINOS_PAY); - gst_element_register (plugin, "pinosdepay", GST_RANK_NONE, - GST_TYPE_PINOS_DEPAY); +// gst_element_register (plugin, "pinospay", GST_RANK_NONE, + // GST_TYPE_PINOS_PAY); + // gst_element_register (plugin, "pinosdepay", GST_RANK_NONE, + // GST_TYPE_PINOS_DEPAY); gst_element_register (plugin, "pinossrc", GST_RANK_PRIMARY + 1, GST_TYPE_PINOS_SRC); gst_element_register (plugin, "pinossink", GST_RANK_NONE, GST_TYPE_PINOS_SINK); - gst_element_register (plugin, "pinossocketsink", GST_RANK_NONE, - GST_TYPE_PINOS_SOCKET_SINK); - gst_element_register (plugin, "pinosportsink", GST_RANK_NONE, - GST_TYPE_PINOS_PORT_SINK); - gst_element_register (plugin, "pinosportsrc", GST_RANK_NONE, - GST_TYPE_PINOS_PORT_SRC); + // gst_element_register (plugin, "pinossocketsink", GST_RANK_NONE, + // GST_TYPE_PINOS_SOCKET_SINK); + // gst_element_register (plugin, "pinosportsink", GST_RANK_NONE, + // GST_TYPE_PINOS_PORT_SINK); +// gst_element_register (plugin, "pinosportsrc", GST_RANK_NONE, +// GST_TYPE_PINOS_PORT_SRC); if (!gst_device_provider_register (plugin, "pinosdeviceprovider", GST_RANK_PRIMARY + 1, GST_TYPE_PINOS_DEVICE_PROVIDER)) diff --git a/pinos/gst/gstpinossink.c b/pinos/gst/gstpinossink.c index 2d1e63e8c..37e501d85 100644 --- a/pinos/gst/gstpinossink.c +++ b/pinos/gst/gstpinossink.c @@ -43,6 +43,8 @@ #include #include +#include + #include "gsttmpfileallocator.h" @@ -329,8 +331,7 @@ on_new_buffer (GObject *gobject, gpointer user_data) { GstPinosSink *pinossink = user_data; - PinosBuffer *pbuf; - PinosBufferIter it; + SpaBuffer *b; GST_LOG_OBJECT (pinossink, "got new buffer"); if (pinossink->stream == NULL) { @@ -338,11 +339,12 @@ on_new_buffer (GObject *gobject, return; } - if (!(pbuf = pinos_stream_peek_buffer (pinossink->stream))) { + if (!(b = pinos_stream_peek_buffer (pinossink->stream))) { g_warning ("failed to capture buffer"); return; } +#if 0 pinos_buffer_iter_init (&it, pbuf); while (pinos_buffer_iter_next (&it)) { switch (pinos_buffer_iter_get_type (&it)) { @@ -375,6 +377,7 @@ on_new_buffer (GObject *gobject, } } pinos_buffer_iter_end (&it); +#endif } static void @@ -483,18 +486,32 @@ start_error: } } +typedef struct { + SpaBuffer buffer; + SpaMeta metas[1]; + SpaMetaHeader header; + SpaData datas[1]; + GstMemory *mem; + GstPinosSink *pinossink; + int fd; +} SinkBuffer; + +static void +sink_buffer_free (void *user_data) +{ + SinkBuffer *b = user_data; + gst_memory_unref (b->mem); + gst_object_unref (b->pinossink); + g_slice_free (SinkBuffer, user_data); +} + static GstFlowReturn gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer) { GstPinosSink *pinossink; - PinosBuffer pbuf; - PinosBufferBuilder builder; + SinkBuffer *b; GstMemory *mem = NULL; GstClockTime pts, dts, base; - PinosPacketHeader hdr; - PinosPacketAddMem am; - PinosPacketProcessMem p; - PinosPacketRemoveMem rm; gsize size; gboolean tmpfile, res; @@ -505,6 +522,18 @@ gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer) base = GST_ELEMENT_CAST (bsink)->base_time; + size = gst_buffer_get_size (buffer); + + b = g_slice_new (SinkBuffer); + b->buffer.refcount = 0; + b->buffer.notify = sink_buffer_free; + b->buffer.id = pinos_fd_manager_get_id (pinossink->fdmanager); + b->buffer.size = size; + b->buffer.n_metas = 1; + b->buffer.metas = b->metas; + b->buffer.n_datas = 1; + b->buffer.datas = b->datas; + pts = GST_BUFFER_PTS (buffer); dts = GST_BUFFER_DTS (buffer); if (!GST_CLOCK_TIME_IS_VALID (pts)) @@ -512,12 +541,13 @@ gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer) else if (!GST_CLOCK_TIME_IS_VALID (dts)) dts = pts; - hdr.flags = 0; - hdr.seq = GST_BUFFER_OFFSET (buffer); - hdr.pts = GST_CLOCK_TIME_IS_VALID (pts) ? pts + base : base; - hdr.dts_offset = GST_CLOCK_TIME_IS_VALID (dts) && GST_CLOCK_TIME_IS_VALID (pts) ? pts - dts : 0; - - size = gst_buffer_get_size (buffer); + b->header.flags = 0; + b->header.seq = GST_BUFFER_OFFSET (buffer); + b->header.pts = GST_CLOCK_TIME_IS_VALID (pts) ? pts + base : base; + b->header.dts_offset = GST_CLOCK_TIME_IS_VALID (dts) && GST_CLOCK_TIME_IS_VALID (pts) ? pts - dts : 0; + b->metas[0].type = SPA_META_TYPE_HEADER; + b->metas[0].data = &b->header; + b->metas[0].size = sizeof (b->header); if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (gst_buffer_peek_memory (buffer, 0))) { @@ -541,35 +571,22 @@ gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer) if (pinos_stream_get_state (pinossink->stream) != PINOS_STREAM_STATE_STREAMING) goto streaming_error; - pinos_stream_buffer_builder_init (pinossink->stream, &builder); - pinos_buffer_builder_add_header (&builder, &hdr); + b->mem = mem; + b->fd = gst_fd_memory_get_fd (mem); + b->datas[0].type = SPA_DATA_TYPE_FD; + b->datas[0].ptr = &b->fd; + b->datas[0].ptr_type = mem->allocator->mem_type; + b->datas[0].offset = mem->offset; + b->datas[0].size = mem->size; + b->datas[0].stride = 0; - am.id = pinos_fd_manager_get_id (pinossink->fdmanager); - am.fd_index = pinos_buffer_builder_add_fd (&builder, gst_fd_memory_get_fd (mem)); - am.offset = 0; - am.size = mem->size + mem->offset; - p.port = 0; - p.id = am.id; - p.offset = mem->offset; - p.size = mem->size; - rm.id = am.id; - pinos_buffer_builder_add_add_mem (&builder, &am); - pinos_buffer_builder_add_process_mem (&builder, &p); - pinos_buffer_builder_add_remove_mem (&builder, &rm); - pinos_buffer_builder_end (&builder, &pbuf); + if (!(res = pinos_stream_send_buffer (pinossink->stream, &b->buffer))) + g_warning ("can't send buffer"); - GST_LOG ("sending fd mem %d %d", am.id, am.fd_index); - - res = pinos_stream_send_buffer (pinossink->stream, &pbuf); - pinos_buffer_steal_fds (&pbuf, NULL); - pinos_buffer_unref (&pbuf); pinos_main_loop_unlock (pinossink->loop); - if (res && !tmpfile) { - /* keep the memory around until we get the reuse mem message */ - g_hash_table_insert (pinossink->mem_ids, GINT_TO_POINTER (p.id), mem); - } else - gst_memory_unref (mem); + /* keep the memory around until we get the reuse mem message */ + g_hash_table_insert (pinossink->mem_ids, GINT_TO_POINTER (b->buffer.id), b); return GST_FLOW_OK; diff --git a/pinos/gst/gstpinossrc.c b/pinos/gst/gstpinossrc.c index 8cbdb5646..694d69ff5 100644 --- a/pinos/gst/gstpinossrc.c +++ b/pinos/gst/gstpinossrc.c @@ -44,6 +44,8 @@ #include #include +#include + static GQuark process_mem_data_quark; @@ -322,35 +324,16 @@ gst_pinos_src_src_fixate (GstBaseSrc * bsrc, GstCaps * caps) typedef struct { GstPinosSrc *src; - PinosPacketProcessMem p; + SpaBuffer *buffer; } ProcessMemData; static void process_mem_data_destroy (gpointer user_data) { ProcessMemData *data = user_data; - GstPinosSrc *pinossrc = data->src; - PinosBufferBuilder b; - PinosPacketReuseMem r; - PinosBuffer pbuf; - r.id = data->p.id; - - GST_DEBUG_OBJECT (pinossrc, "destroy %d", r.id); - - GST_OBJECT_LOCK (pinossrc); - if (pinossrc->stream_state == PINOS_STREAM_STATE_STREAMING) { - pinos_stream_buffer_builder_init (pinossrc->stream, &b); - pinos_buffer_builder_add_reuse_mem (&b, &r); - pinos_buffer_builder_end (&b, &pbuf); - - GST_DEBUG_OBJECT (pinossrc, "send release-fd for %d", r.id); - pinos_stream_send_buffer (pinossrc->stream, &pbuf); - pinos_buffer_unref (&pbuf); - } - GST_OBJECT_UNLOCK (pinossrc); - - gst_object_unref (pinossrc); + spa_buffer_unref (data->buffer); + gst_object_unref (data->src); g_slice_free (ProcessMemData, data); } @@ -359,113 +342,74 @@ on_new_buffer (GObject *gobject, gpointer user_data) { GstPinosSrc *pinossrc = user_data; - PinosBuffer *pbuf; - PinosBufferIter it; - GstBuffer *buf = NULL; + SpaBuffer *b; + GstBuffer *buf; + unsigned int i; + ProcessMemData data; GST_LOG_OBJECT (pinossrc, "got new buffer"); - if (!(pbuf = pinos_stream_peek_buffer (pinossrc->stream))) { + if (!(b = pinos_stream_peek_buffer (pinossrc->stream))) { g_warning ("failed to capture buffer"); return; } - pinos_buffer_iter_init (&it, pbuf); - while (pinos_buffer_iter_next (&it)) { - switch (pinos_buffer_iter_get_type (&it)) { - case PINOS_PACKET_TYPE_HEADER: + buf = gst_buffer_new (); + + data.src = gst_object_ref (pinossrc); + data.buffer = b; + gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), + process_mem_data_quark, + g_slice_dup (ProcessMemData, &data), + process_mem_data_destroy); + + for (i = 0; i < b->n_metas; i++) { + SpaMeta *m = &b->metas[i]; + + switch (m->type) { + case SPA_META_TYPE_HEADER: { - PinosPacketHeader hdr; + SpaMetaHeader *h = m->data; - if (!pinos_buffer_iter_parse_header (&it, &hdr)) - goto parse_failed; + GST_INFO ("pts %" G_GUINT64_FORMAT ", dts_offset %"G_GUINT64_FORMAT, h->pts, h->dts_offset); - if (buf == NULL) - buf = gst_buffer_new (); - - GST_INFO ("pts %" G_GUINT64_FORMAT ", dts_offset %"G_GUINT64_FORMAT, hdr.pts, hdr.dts_offset); - - if (GST_CLOCK_TIME_IS_VALID (hdr.pts)) { - GST_BUFFER_PTS (buf) = hdr.pts; - if (GST_BUFFER_PTS (buf) + hdr.dts_offset > 0) - GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) + hdr.dts_offset; + if (GST_CLOCK_TIME_IS_VALID (h->pts)) { + GST_BUFFER_PTS (buf) = h->pts; + if (GST_BUFFER_PTS (buf) + h->dts_offset > 0) + GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) + h->dts_offset; } - GST_BUFFER_OFFSET (buf) = hdr.seq; - break; - } - case PINOS_PACKET_TYPE_ADD_MEM: - { - GstMemory *fdmem = NULL; - PinosPacketAddMem p; - int fd; - - if (!pinos_buffer_iter_parse_add_mem (&it, &p)) - goto parse_failed; - - fd = pinos_buffer_get_fd (pbuf, p.fd_index); - if (fd == -1) - goto parse_failed; - - fdmem = gst_fd_allocator_alloc (pinossrc->fd_allocator, dup (fd), - p.offset + p.size, GST_FD_MEMORY_FLAG_NONE); - gst_memory_resize (fdmem, p.offset, p.size); - - g_hash_table_insert (pinossrc->mem_ids, GINT_TO_POINTER (p.id), fdmem); - break; - } - case PINOS_PACKET_TYPE_REMOVE_MEM: - { - PinosPacketRemoveMem p; - - if (!pinos_buffer_iter_parse_remove_mem (&it, &p)) - goto parse_failed; - - g_hash_table_remove (pinossrc->mem_ids, GINT_TO_POINTER (p.id)); - break; - } - case PINOS_PACKET_TYPE_PROCESS_MEM: - { - GstMemory *fdmem = NULL; - ProcessMemData data; - - if (!pinos_buffer_iter_parse_process_mem (&it, &data.p)) - goto parse_failed; - - GST_DEBUG ("got mem id %d", data.p.id); - if (!(fdmem = g_hash_table_lookup (pinossrc->mem_ids, GINT_TO_POINTER (data.p.id)))) - goto parse_failed; - - if (buf == NULL) - buf = gst_buffer_new (); - - fdmem = gst_memory_share (fdmem, data.p.offset, data.p.size); - gst_buffer_append_memory (buf, fdmem); - - data.src = gst_object_ref (pinossrc); - gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (fdmem), - process_mem_data_quark, - g_slice_dup (ProcessMemData, &data), - process_mem_data_destroy); - break; - } - case PINOS_PACKET_TYPE_FORMAT_CHANGE: - { - PinosPacketFormatChange change; - GstCaps *caps; - - if (!pinos_buffer_iter_parse_format_change (&it, &change)) - goto parse_failed; - GST_DEBUG ("got format change %d %s", change.id, change.format); - - caps = gst_caps_from_string (change.format); - gst_base_src_set_caps (GST_BASE_SRC (pinossrc), caps); - gst_caps_unref (caps); + GST_BUFFER_OFFSET (buf) = h->seq; + break; + } + default: + break; + } + } + for (i = 0; i < b->n_datas; i++) { + SpaData *d = &b->datas[i]; + + switch (d->type) { + case SPA_DATA_TYPE_MEMPTR: + { + gst_buffer_append_memory (buf, + gst_memory_new_wrapped (0, d->ptr, d->offset + d->size, d->offset, + d->size, NULL, NULL)); + break; + } + case SPA_DATA_TYPE_FD: + { + GstMemory *fdmem = NULL; + int fd = *((int *) d->ptr); + + fdmem = gst_fd_allocator_alloc (pinossrc->fd_allocator, dup (fd), + d->offset + d->size, GST_FD_MEMORY_FLAG_NONE); + gst_memory_resize (fdmem, d->offset, d->size); + gst_buffer_append_memory (buf, fdmem); break; } default: break; } } - pinos_buffer_iter_end (&it); if (buf) { g_queue_push_tail (&pinossrc->queue, buf); @@ -473,17 +417,6 @@ on_new_buffer (GObject *gobject, pinos_main_loop_signal (pinossrc->loop, FALSE); } return; - - /* ERRORS */ -parse_failed: - { - pinos_buffer_iter_end (&it); - pinos_buffer_unref (pbuf); - gst_buffer_unref (buf); - GST_ELEMENT_ERROR (pinossrc, RESOURCE, FAILED, ("buffer parse failure"), (NULL)); - pinos_main_loop_signal (pinossrc->loop, FALSE); - return; - } } static void @@ -740,6 +673,22 @@ connect_error: } } +static void +on_format_notify (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + GstPinosSrc *pinossrc = user_data; + GBytes *format; + GstCaps *caps; + + g_object_get (gobject, "format", &format, NULL); + + caps = gst_caps_from_string (g_bytes_get_data (format, NULL)); + gst_base_src_set_caps (GST_BASE_SRC (pinossrc), caps); + gst_caps_unref (caps); +} + static gboolean gst_pinos_src_unlock (GstBaseSrc * basesrc) { @@ -771,9 +720,6 @@ static gboolean gst_pinos_src_event (GstBaseSrc * src, GstEvent * event) { gboolean res = FALSE; - GstPinosSrc *pinossrc; - - pinossrc = GST_PINOS_SRC (src); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CUSTOM_UPSTREAM: @@ -781,13 +727,11 @@ gst_pinos_src_event (GstBaseSrc * src, GstEvent * event) GstClockTime running_time; gboolean all_headers; guint count; - PinosPacketRefreshRequest refresh; - PinosBufferBuilder b; - PinosBuffer pbuf; gst_video_event_parse_upstream_force_key_unit (event, &running_time, &all_headers, &count); +#if 0 pinos_buffer_builder_init (&b); refresh.last_id = 0; @@ -805,6 +749,7 @@ gst_pinos_src_event (GstBaseSrc * src, GstEvent * event) GST_OBJECT_UNLOCK (pinossrc); pinos_buffer_unref (&pbuf); +#endif res = TRUE; } else { res = GST_BASE_SRC_CLASS (parent_class)->event (src, event); @@ -1010,6 +955,7 @@ gst_pinos_src_open (GstPinosSrc * pinossrc) pinossrc->stream = pinos_stream_new (pinossrc->ctx, pinossrc->client_name, props); g_signal_connect (pinossrc->stream, "notify::state", (GCallback) on_stream_notify, pinossrc); + g_signal_connect (pinossrc->stream, "notify::format", (GCallback) on_format_notify, pinossrc); g_signal_connect (pinossrc->stream, "new-buffer", (GCallback) on_new_buffer, pinossrc); pinos_main_loop_unlock (pinossrc->loop); diff --git a/pinos/modules/spa/spa-alsa-sink.c b/pinos/modules/spa/spa-alsa-sink.c index 180bcbe1b..1b65145b5 100644 --- a/pinos/modules/spa/spa-alsa-sink.c +++ b/pinos/modules/spa/spa-alsa-sink.c @@ -434,110 +434,40 @@ free_mem_block (MemBlock *b) static gboolean on_received_buffer (PinosPort *port, - PinosBuffer *buffer, + SpaBuffer *buffer, GError **error, gpointer user_data) { PinosSpaAlsaSink *this = user_data; PinosSpaAlsaSinkPrivate *priv = this->priv; - PinosBuffer *pbuf = buffer; - PinosBufferIter it; + unsigned int i; - pinos_buffer_iter_init (&it, pbuf); - while (pinos_buffer_iter_next (&it)) { - switch (pinos_buffer_iter_get_type (&it)) { - case PINOS_PACKET_TYPE_HEADER: - { - PinosPacketHeader hdr; + for (i = 0; i < buffer->n_datas; i++) { + SpaData *d = &buffer->datas[i]; + PinosRingbufferArea areas[2]; + uint8_t *data; + size_t size, towrite, total; - if (!pinos_buffer_iter_parse_header (&it, &hdr)) - break; + if (d->type != SPA_DATA_TYPE_MEMPTR) + continue; - break; - } - case PINOS_PACKET_TYPE_ADD_MEM: - { - PinosPacketAddMem p; - MemBlock *b; - int fd; + size = d->size; + data = (guint8*)d->ptr + d->offset; - if (!pinos_buffer_iter_parse_add_mem (&it, &p)) - break; + pinos_ringbuffer_get_write_areas (priv->ringbuffer, areas); - fd = pinos_buffer_get_fd (pbuf, p.fd_index); - if (fd == -1) - break; + total = MIN (size, areas[0].len + areas[1].len); + g_debug ("total write %zd %zd", total, areas[0].len + areas[1].len); + towrite = MIN (size, areas[0].len); + memcpy (areas[0].data, data, towrite); + size -= towrite; + data += towrite; + towrite = MIN (size, areas[1].len); + memcpy (areas[1].data, data, towrite); - b = g_slice_new0 (MemBlock); - b->id = p.id; - b->type = p.type; - b->fd = fd; - b->data = mmap (NULL, p.size, PROT_READ, MAP_PRIVATE, fd, p.offset); - b->offset = p.offset; - b->size = p.size; - - g_hash_table_insert (priv->mem_ids, GINT_TO_POINTER (p.id), b); - break; - } - - case PINOS_PACKET_TYPE_REMOVE_MEM: - { - PinosPacketRemoveMem p; - - if (!pinos_buffer_iter_parse_remove_mem (&it, &p)) - break; - - g_hash_table_remove (priv->mem_ids, GINT_TO_POINTER (p.id)); - break; - } - - case PINOS_PACKET_TYPE_PROCESS_MEM: - { - PinosPacketProcessMem p; - MemBlock *b; - PinosRingbufferArea areas[2]; - uint8_t *data; - size_t size, towrite, total; - - if (!pinos_buffer_iter_parse_process_mem (&it, &p)) - break; - - if (!(b = g_hash_table_lookup (priv->mem_ids, GINT_TO_POINTER (p.id)))) - break; - - size = p.size; - data = (guint8*)b->data + p.offset; - - pinos_ringbuffer_get_write_areas (priv->ringbuffer, areas); - - total = MIN (size, areas[0].len + areas[1].len); - g_debug ("total write %zd %zd", total, areas[0].len + areas[1].len); - towrite = MIN (size, areas[0].len); - memcpy (areas[0].data, data, towrite); - size -= towrite; - data += towrite; - towrite = MIN (size, areas[1].len); - memcpy (areas[1].data, data, towrite); - - pinos_ringbuffer_write_advance (priv->ringbuffer, total); - - break; - } - case PINOS_PACKET_TYPE_FORMAT_CHANGE: - { - PinosPacketFormatChange change; - - if (!pinos_buffer_iter_parse_format_change (&it, &change)) - break; - g_debug ("got format change %d %s", change.id, change.format); - - break; - } - default: - break; - } + pinos_ringbuffer_write_advance (priv->ringbuffer, total); } - pinos_buffer_iter_end (&it); + spa_buffer_unref (buffer); return TRUE; } @@ -550,12 +480,11 @@ on_format_change (GObject *obj, SinkPortData *data = user_data; PinosNode *node = PINOS_NODE (data->sink); PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (node); - PinosSpaAlsaSinkPrivate *priv = sink->priv; GBytes *formats; g_object_get (obj, "format", &formats, NULL); if (formats) { - g_debug ("port %p: format change %s", obj, g_bytes_get_data (formats, NULL)); + g_debug ("port %p: format change %s", obj, (gchar*) g_bytes_get_data (formats, NULL)); negotiate_formats (sink); } } diff --git a/pinos/modules/spa/spa-v4l2-source.c b/pinos/modules/spa/spa-v4l2-source.c index 8d6507bf9..96f1161df 100644 --- a/pinos/modules/spa/spa-v4l2-source.c +++ b/pinos/modules/spa/spa-v4l2-source.c @@ -58,10 +58,9 @@ struct _PinosSpaV4l2SourcePrivate gboolean running; pthread_t thread; - const void *format; + GBytes *format; GList *ports; - PinosFdManager *fdmanager; }; enum { @@ -113,50 +112,6 @@ make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char return SPA_RESULT_ERROR; } -static void -send_format (PinosSpaV4l2Source *source, SourcePortData *data) -{ - PinosSpaV4l2SourcePrivate *priv = source->priv; - GError *error = NULL; - PinosBufferBuilder builder; - PinosBuffer pbuf; - PinosPacketFormatChange fc; - guint8 buf[1024]; - - pinos_buffer_builder_init_into (&builder, buf, 1024, NULL, 0); - fc.id = 0; - fc.format = priv->format; - pinos_buffer_builder_add_format_change (&builder, &fc); - pinos_buffer_builder_end (&builder, &pbuf); - - if (!pinos_port_send_buffer (PINOS_PORT (data->port), &pbuf, &error)) { - g_debug ("format update failed: %s", error->message); - g_clear_error (&error); - } - pinos_buffer_unref (&pbuf); - - data->have_format = TRUE; -} - -static int -tmpfile_create (PinosSpaV4l2Source * source, void *data, gsize size) -{ - char filename[] = "/dev/shm/tmpfilepay.XXXXXX"; - int fd; - - fd = mkostemp (filename, O_CLOEXEC); - if (fd == -1) { - g_debug ("Failed to create temporary file: %s", strerror (errno)); - return -1; - } - unlink (filename); - - if (write (fd, data, size) != (gssize) size) - g_debug ("Failed to write data: %s", strerror (errno)); - - return fd; -} - static void on_source_event (SpaHandle *handle, SpaEvent *event, void *user_data) { @@ -169,66 +124,27 @@ on_source_event (SpaHandle *handle, SpaEvent *event, void *user_data) SpaOutputInfo info[1] = { 0, }; SpaResult res; SpaBuffer *b; - PinosBuffer pbuf; - PinosBufferBuilder builder; - PinosPacketHeader hdr; - PinosPacketAddMem am; - PinosPacketProcessMem p; - PinosPacketRemoveMem rm; GList *walk; - gint fd; - guint8 buf[1024]; - gint fdbuf[8]; - gboolean do_close = FALSE; if ((res = priv->source_node->port_pull_output (priv->source, 1, info)) < 0) g_debug ("spa-v4l2-source %p: got pull error %d", source, res); b = info[0].buffer; - hdr.flags = 0; - hdr.seq = 0; - hdr.pts = -1; - hdr.dts_offset = 0; - - pinos_buffer_builder_init_into (&builder, buf, 1024, fdbuf, 8); - pinos_buffer_builder_add_header (&builder, &hdr); - - if (b->datas[0].type == SPA_DATA_TYPE_FD) { - fd = *((int *)b->datas[0].ptr); - } else { - fd = tmpfile_create (source, b->datas[0].ptr, b->size); - do_close = TRUE; - } - am.fd_index = pinos_buffer_builder_add_fd (&builder, fd); - am.id = pinos_fd_manager_get_id (priv->fdmanager); - am.offset = 0; - am.size = b->datas[0].size + b->datas[0].offset; - p.id = am.id; - p.offset = b->datas[0].offset; - p.size = b->datas[0].size; - rm.id = am.id; - pinos_buffer_builder_add_add_mem (&builder, &am); - pinos_buffer_builder_add_process_mem (&builder, &p); - pinos_buffer_builder_add_remove_mem (&builder, &rm); - pinos_buffer_builder_end (&builder, &pbuf); - for (walk = priv->ports; walk; walk = g_list_next (walk)) { SourcePortData *data = walk->data; GError *error = NULL; - if (!data->have_format) - send_format (source, data); + if (!data->have_format) { + g_object_set (data->port, "format", priv->format, NULL); + data->have_format = TRUE; + } - if (!pinos_port_send_buffer (PINOS_PORT (data->port), &pbuf, &error)) { + if (!pinos_port_send_buffer (data->port, b, &error)) { g_debug ("send failed: %s", error->message); g_clear_error (&error); } } - if (!do_close) - pinos_buffer_steal_fds (&pbuf, NULL); - pinos_buffer_unref (&pbuf); - spa_buffer_unref (b); break; } @@ -270,7 +186,7 @@ create_pipeline (PinosSpaV4l2Source *this) g_debug ("got get_props error %d", res); value.type = SPA_PROP_TYPE_STRING; - value.value = "/dev/video0"; + value.value = "/dev/video1"; value.size = strlen (value.value)+1; props->set_prop (props, spa_props_index_for_name (props, "device"), &value); @@ -290,6 +206,7 @@ negotiate_formats (PinosSpaV4l2Source *this) void *state = NULL; SpaFraction frac; SpaRectangle rect; + const gchar *str; if ((res = priv->source_node->port_enum_formats (priv->source, 0, &format, NULL, &state)) < 0) return res; @@ -323,11 +240,12 @@ negotiate_formats (PinosSpaV4l2Source *this) if ((res = priv->source_node->port_set_format (priv->source, 0, 0, format)) < 0) return res; - priv->format = "video/x-raw," - " format=(string)YUY2," - " width=(int)320," - " height=(int)240," - " framerate=(fraction)30/1"; + str = "video/x-raw," + " format=(string)YUY2," + " width=(int)320," + " height=(int)240," + " framerate=(fraction)30/1"; + priv->format = g_bytes_new_static (str, strlen (str)+1); return SPA_RESULT_OK; } @@ -487,26 +405,6 @@ on_deactivate (PinosPort *port, gpointer user_data) pinos_node_report_idle (PINOS_NODE (source)); } -static gboolean -on_received_buffer (PinosPort *port, - PinosBuffer *pbuf, - GError **error, - gpointer user_data) -{ - PinosBufferIter it; - - pinos_buffer_iter_init (&it, pbuf); - while (pinos_buffer_iter_next (&it)) { - switch (pinos_buffer_iter_get_type (&it)) { - default: - break; - } - } - pinos_buffer_iter_end (&it); - - return TRUE; -} - static void free_source_port_data (SourcePortData *data) { @@ -575,8 +473,6 @@ add_port (PinosNode *node, data->port = PINOS_NODE_CLASS (pinos_spa_v4l2_source_parent_class) ->add_port (node, direction, id, error); - pinos_port_set_received_buffer_cb (data->port, on_received_buffer, source, NULL); - g_debug ("connecting signals"); g_signal_connect (data->port, "activate", (GCallback) on_activate, data); g_signal_connect (data->port, "deactivate", (GCallback) on_deactivate, data); @@ -607,10 +503,7 @@ pinos_spa_v4l2_source_class_init (PinosSpaV4l2SourceClass * klass) static void pinos_spa_v4l2_source_init (PinosSpaV4l2Source * source) { - PinosSpaV4l2SourcePrivate *priv = source->priv = PINOS_SPA_V4L2_SOURCE_GET_PRIVATE (source); - - priv->fdmanager = pinos_fd_manager_get (PINOS_FD_MANAGER_DEFAULT); - + source->priv = PINOS_SPA_V4L2_SOURCE_GET_PRIVATE (source); } PinosNode * diff --git a/pinos/server/client-node.c b/pinos/server/client-node.c index 6f778571e..0e933e2bc 100644 --- a/pinos/server/client-node.c +++ b/pinos/server/client-node.c @@ -17,10 +17,15 @@ * Boston, MA 02110-1301, USA. */ +#define _GNU_SOURCE + #include #include #include #include +#include +#include + #include @@ -34,26 +39,19 @@ #include "pinos/dbus/org-pinos.h" +#include "spa/include/spa/control.h" + #define MAX_BUFFER_SIZE 1024 #define MAX_FDS 16 -typedef struct { - guint32 id; - guint32 type; - int fd; - guint64 offset; - guint64 size; - void *data; -} MemBlock; - struct _PinosClientNodePrivate { int fd; GSource *socket_source; GSocket *sockets[2]; - PinosBuffer recv_buffer; + SpaControl recv_control; guint8 recv_data[MAX_BUFFER_SIZE]; int recv_fds[MAX_FDS]; @@ -112,178 +110,118 @@ pinos_client_node_set_property (GObject *_object, } } -static void -free_mem_block (MemBlock *b) -{ - munmap (b->data, b->size); - g_slice_free (MemBlock, b); -} - static gboolean -parse_buffer (PinosClientNode *cnode, - PinosBuffer *pbuf) +parse_control (PinosClientNode *cnode, + SpaControl *ctrl) { PinosNode *node = PINOS_NODE (cnode); - PinosBufferIter it; PinosClientNodePrivate *priv = cnode->priv; - PinosPort *port; + SpaControlIter it; - pinos_buffer_iter_init (&it, pbuf); - while (pinos_buffer_iter_next (&it)) { - PinosPacketType type = pinos_buffer_iter_get_type (&it); + spa_control_iter_init (&it, ctrl); + while (spa_control_iter_next (&it) == SPA_RESULT_OK) { + SpaControlCmd cmd = spa_control_iter_get_cmd (&it); - switch (type) { - case PINOS_PACKET_TYPE_FORMAT_CHANGE: + switch (cmd) { + case SPA_CONTROL_CMD_ADD_PORT: + case SPA_CONTROL_CMD_REMOVE_PORT: + case SPA_CONTROL_CMD_SET_FORMAT: + case SPA_CONTROL_CMD_SET_PROPERTY: + case SPA_CONTROL_CMD_END_CONFIGURE: + case SPA_CONTROL_CMD_PAUSE: + case SPA_CONTROL_CMD_START: + case SPA_CONTROL_CMD_STOP: + g_warning ("client-node %p: got unexpected control %d", node, cmd); + break; + + case SPA_CONTROL_CMD_NODE_UPDATE: + case SPA_CONTROL_CMD_PORT_UPDATE: + case SPA_CONTROL_CMD_PORT_REMOVED: + g_warning ("client-node %p: command not implemented %d", node, cmd); + break; + + case SPA_CONTROL_CMD_START_CONFIGURE: { - PinosPacketFormatChange p; - GBytes *format; + SpaControlBuilder builder; + SpaControl control; + guint8 buffer[1024]; - if (!pinos_buffer_iter_parse_format_change (&it, &p)) - break; + /* set port format */ - g_debug ("format change port %d", p.port); + /* send end-configure */ + spa_control_builder_init_into (&builder, buffer, 1024, NULL, 0); + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_END_CONFIGURE, NULL); + spa_control_builder_end (&builder, &control); - if (!(port = pinos_node_find_port (node, p.port))) - break; + if (spa_control_write (&control, priv->fd) < 0) + g_warning ("client-node %p: error writing control", node); - format = g_bytes_new_static (p.format, strlen (p.format) + 1); - - g_object_set (port, "possible-formats", format, NULL); - g_object_set (port, "format", format, NULL); - g_debug ("client-node %p: format change %s", node, p.format); break; } - case PINOS_PACKET_TYPE_START: + case SPA_CONTROL_CMD_PORT_STATUS_CHANGE: { - GBytes *format; - PinosBufferBuilder builder; - PinosBuffer obuf; + g_warning ("client-node %p: command not implemented %d", node, cmd); + break; + } + case SPA_CONTROL_CMD_START_ALLOC: + { + SpaControlBuilder builder; + SpaControl control; guint8 buffer[1024]; - GError *error = NULL; GList *ports, *walk; - pinos_buffer_builder_init_into (&builder, buffer, 1024, NULL, 0); + /* FIXME read port memory requirements */ + /* FIXME add_mem */ + + /* send start */ + spa_control_builder_init_into (&builder, buffer, 1024, NULL, 0); + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_START, NULL); + spa_control_builder_end (&builder, &control); + + if (spa_control_write (&control, priv->fd) < 0) + g_warning ("client-node %p: error writing control", node); ports = pinos_node_get_ports (node); for (walk = ports; walk; walk = g_list_next (walk)) { - PinosPacketFormatChange fc; - - port = walk->data; + PinosPort *port = walk->data; pinos_port_activate (port); - - g_object_get (port, "format", &format, "id", &fc.port, NULL); - if (format == NULL) - break; - - fc.id = 0; - fc.format = g_bytes_get_data (format, NULL); - - g_debug ("client-node %p: port %u we are now streaming in format \"%s\"", - node, fc.port, fc.format); - - pinos_buffer_builder_add_format_change (&builder, &fc); - } - pinos_buffer_builder_add_empty (&builder, PINOS_PACKET_TYPE_STREAMING); - pinos_buffer_builder_end (&builder, &obuf); - - if (!pinos_io_write_buffer (priv->fd, &obuf, &error)) { - g_warning ("client-node %p: error writing buffer: %s", node, error->message); - g_clear_error (&error); - } - - break; - } - case PINOS_PACKET_TYPE_STOP: - { - PinosBufferBuilder builder; - PinosBuffer obuf; - guint8 buffer[1024]; - GError *error = NULL; - GList *ports, *walk; - - pinos_buffer_builder_init_into (&builder, buffer, 1024, NULL, 0); - - ports = pinos_node_get_ports (node); - for (walk = ports; walk; walk = g_list_next (walk)) { - port = walk->data; - pinos_port_deactivate (port); - } - pinos_buffer_builder_add_empty (&builder, PINOS_PACKET_TYPE_STOPPED); - pinos_buffer_builder_end (&builder, &obuf); - - if (!pinos_io_write_buffer (priv->fd, &obuf, &error)) { - g_warning ("client-node %p: error writing buffer: %s", node, error->message); - g_clear_error (&error); } break; } - case PINOS_PACKET_TYPE_ADD_MEM: - { - PinosPacketAddMem p; - MemBlock *b; - int fd; - - if (!pinos_buffer_iter_parse_add_mem (&it, &p)) - break; - - fd = pinos_buffer_get_fd (pbuf, p.fd_index); - if (fd == -1) - break; - - b = g_slice_new0 (MemBlock); - b->id = p.id; - b->type = p.type; - b->fd = fd; - b->data = mmap (NULL, p.size, PROT_READ, MAP_PRIVATE, fd, p.offset); - b->offset = p.offset; - b->size = p.size; - - g_hash_table_insert (priv->mem_ids, GINT_TO_POINTER (p.id), b); - break; - } - - case PINOS_PACKET_TYPE_REMOVE_MEM: - { - PinosPacketRemoveMem p; - - if (!pinos_buffer_iter_parse_remove_mem (&it, &p)) - break; - - g_hash_table_remove (priv->mem_ids, GINT_TO_POINTER (p.id)); - break; - } - case PINOS_PACKET_TYPE_PROCESS_MEM: - { - PinosPacketProcessMem p; - GError *error = NULL; - - if (!pinos_buffer_iter_parse_process_mem (&it, &p)) - break; - - if (!(port = pinos_node_find_port (node, p.port))) - break; - - if (!pinos_port_send_buffer (port, pbuf, &error)) { - g_warning ("client-node %p: port %p failed to receive buffer: %s", node, port, error->message); - g_clear_error (&error); - } - break; - } - case PINOS_PACKET_TYPE_HEADER: + case SPA_CONTROL_CMD_NEED_INPUT: { break; } - case PINOS_PACKET_TYPE_REUSE_MEM: + case SPA_CONTROL_CMD_HAVE_OUTPUT: { break; } + + case SPA_CONTROL_CMD_ADD_MEM: + break; + case SPA_CONTROL_CMD_REMOVE_MEM: + break; + case SPA_CONTROL_CMD_ADD_BUFFER: + break; + case SPA_CONTROL_CMD_REMOVE_BUFFER: + break; + + case SPA_CONTROL_CMD_PROCESS_BUFFER: + { + break; + } + case SPA_CONTROL_CMD_REUSE_BUFFER: + { + break; + } + default: - g_warning ("unhandled packet %d", type); + g_warning ("client-node %p: command unhandled %d", node, cmd); break; } } - pinos_buffer_iter_end (&it); + spa_control_iter_end (&it); return TRUE; } @@ -299,22 +237,19 @@ on_socket_condition (GSocket *socket, switch (condition) { case G_IO_IN: { - PinosBuffer *buffer = &priv->recv_buffer; - GError *error = NULL; + SpaControl *control = &priv->recv_control; - if (!pinos_io_read_buffer (priv->fd, - buffer, - priv->recv_data, - MAX_BUFFER_SIZE, - priv->recv_fds, - MAX_FDS, - &error)) { - g_warning ("client-node %p: failed to read buffer: %s", node, error->message); - g_clear_error (&error); + if (spa_control_read (control, + priv->fd, + priv->recv_data, + MAX_BUFFER_SIZE, + priv->recv_fds, + MAX_FDS) < 0) { + g_warning ("client-node %p: failed to read buffer", node); return TRUE; } - parse_buffer (node, buffer); + parse_control (node, control); #if 0 if (!pinos_port_receive_buffer (priv->port, buffer, &error)) { @@ -322,7 +257,7 @@ on_socket_condition (GSocket *socket, g_clear_error (&error); } #endif - g_assert (pinos_buffer_unref (buffer) == FALSE); + spa_control_clear (control); break; } @@ -416,16 +351,153 @@ create_failed: } } -static gboolean -on_received_buffer (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data) +static void +on_format_change (GObject *obj, + GParamSpec *pspec, + gpointer user_data) { PinosClientNode *node = user_data; PinosClientNodePrivate *priv = node->priv; + GBytes *format; + SpaControl control; + SpaControlBuilder builder; + SpaControlCmdSetFormat sf; + guint8 buf[1024]; - if (!pinos_io_write_buffer (priv->fd, buffer, error)) { - g_warning ("client-node %p: error writing buffer: %s", node, (*error)->message); - return FALSE; + g_object_get (obj, "format", &format, NULL); + if (format == NULL) + return ; + + g_debug ("port %p: format change %s", obj, (gchar*)g_bytes_get_data (format, NULL)); + + spa_control_builder_init_into (&builder, buf, 1024, NULL, 0); + sf.port = 0; + sf.format = NULL; + sf.str = g_bytes_get_data (format, NULL); + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_SET_FORMAT, &sf); + spa_control_builder_end (&builder, &control); + + if (spa_control_write (&control, priv->fd)) + g_warning ("client-node %p: error writing control", node); +} + + +static int +tmpfile_create (void *data, gsize size) +{ + char filename[] = "/dev/shm/tmpfilepay.XXXXXX"; + int fd; + + fd = mkostemp (filename, O_CLOEXEC); + if (fd == -1) { + g_debug ("Failed to create temporary file: %s", strerror (errno)); + return -1; } + unlink (filename); + + if (write (fd, data, size) != (gssize) size) + g_debug ("Failed to write data: %s", strerror (errno)); + + return fd; +} + +typedef struct { + SpaBuffer buffer; + SpaData datas[16]; + int idx[16]; + SpaBuffer *orig; +} MyBuffer; + +static gboolean +on_received_buffer (PinosPort *port, SpaBuffer *buffer, GError **error, gpointer user_data) +{ + PinosClientNode *node = user_data; + PinosClientNodePrivate *priv = node->priv; + SpaControl control; + SpaControlBuilder builder; + guint8 buf[1024]; + int fds[16]; + SpaControlCmdAddBuffer ab; + SpaControlCmdProcessBuffer pb; + SpaControlCmdRemoveBuffer rb; + bool tmpfile = false; + + if (pinos_port_get_direction (port) == PINOS_DIRECTION_OUTPUT) { + /* FIXME, does not happen */ + spa_control_builder_init_into (&builder, buf, 1024, NULL, 0); + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_HAVE_OUTPUT, NULL); + spa_control_builder_end (&builder, &control); + + if (spa_control_write (&control, priv->fd)) { + g_warning ("client-node %p: error writing control", node); + return FALSE; + } + } else { + unsigned int i; + MyBuffer b; + + spa_control_builder_init_into (&builder, buf, 1024, fds, 16); + + b.buffer.refcount = 1; + b.buffer.notify = NULL; + b.buffer.id = buffer->id; + b.buffer.size = buffer->size; + b.buffer.n_metas = buffer->n_metas; + b.buffer.metas = buffer->metas; + b.buffer.n_datas = buffer->n_datas; + b.buffer.datas = b.datas; + + for (i = 0; i < buffer->n_datas; i++) { + SpaData *d = &buffer->datas[i]; + int fd; + SpaControlCmdAddMem am; + + if (d->type == SPA_DATA_TYPE_FD) { + fd = *((int *)d->ptr); + } else { + fd = tmpfile_create (d->ptr, d->size + d->offset); + tmpfile = true; + } + am.port = 0; + am.id = i; + am.type = 0; + am.fd_index = spa_control_builder_add_fd (&builder, fd, tmpfile ? true : false); + am.offset = 0; + am.size = d->offset + d->size; + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_ADD_MEM, &am); + + b.idx[i] = i; + b.datas[i].type = SPA_DATA_TYPE_MEMID; + b.datas[i].ptr_type = NULL; + b.datas[i].ptr = &b.idx[i]; + b.datas[i].offset = d->offset; + b.datas[i].size = d->size; + b.datas[i].stride = d->stride; + } + ab.port = 0; + ab.buffer = &b.buffer; + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_ADD_BUFFER, &ab); + pb.port = 0; + pb.id = b.buffer.id; + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_PROCESS_BUFFER, &pb); + rb.port = 0; + rb.id = b.buffer.id; + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_REMOVE_BUFFER, &rb); + + for (i = 0; i < buffer->n_datas; i++) { + SpaControlCmdRemoveMem rm; + rm.port = 0; + rm.id = i; + spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_REMOVE_MEM, &rm); + } + spa_control_builder_end (&builder, &control); + + if (spa_control_write (&control, priv->fd)) + g_warning ("client-node %p: error writing control", node); + + spa_control_clear (&control); + } + return TRUE; } @@ -441,6 +513,8 @@ add_port (PinosNode *node, if (port) { pinos_port_set_received_buffer_cb (port, on_received_buffer, node, NULL); + + g_signal_connect (port, "notify::format", (GCallback) on_format_change, node); } return port; } @@ -506,8 +580,7 @@ pinos_client_node_class_init (PinosClientNodeClass * klass) static void pinos_client_node_init (PinosClientNode * node) { - PinosClientNodePrivate *priv = node->priv = PINOS_CLIENT_NODE_GET_PRIVATE (node); + node->priv = PINOS_CLIENT_NODE_GET_PRIVATE (node); g_debug ("client-node %p: new", node); - priv->mem_ids = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_mem_block); } diff --git a/pinos/server/client-node.h b/pinos/server/client-node.h index 1f64ca965..53b653759 100644 --- a/pinos/server/client-node.h +++ b/pinos/server/client-node.h @@ -22,6 +22,8 @@ #include +#include + G_BEGIN_DECLS #define PINOS_TYPE_CLIENT_NODE (pinos_client_node_get_type ()) diff --git a/pinos/server/daemon.c b/pinos/server/daemon.c index b152e1615..a56c22881 100644 --- a/pinos/server/daemon.c +++ b/pinos/server/daemon.c @@ -250,7 +250,7 @@ handle_create_client_node (PinosDaemon1 *interface, continue; } - pinos_client_add_object (client, G_OBJECT (target)); +// pinos_client_add_object (client, G_OBJECT (target)); link = pinos_link_new (daemon, port, target, NULL); pinos_client_add_object (client, G_OBJECT (link)); diff --git a/pinos/server/link.c b/pinos/server/link.c index 818a775a0..3af9272aa 100644 --- a/pinos/server/link.c +++ b/pinos/server/link.c @@ -69,7 +69,7 @@ enum static guint signals[LAST_SIGNAL] = { 0 }; static gboolean -on_output_send (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data) +on_output_send (PinosPort *port, SpaBuffer *buffer, GError **error, gpointer user_data) { PinosLink *link = user_data; PinosLinkPrivate *priv = link->priv; @@ -78,7 +78,7 @@ on_output_send (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer u } static gboolean -on_input_send (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data) +on_input_send (PinosPort *port, SpaBuffer *buffer, GError **error, gpointer user_data) { PinosLink *link = user_data; PinosLinkPrivate *priv = link->priv; @@ -245,7 +245,7 @@ on_format_change (GObject *obj, GBytes *formats; g_object_get (priv->output, "format", &formats, NULL); - g_debug ("port %p: format change %s", priv->output, g_bytes_get_data (formats, NULL)); + g_debug ("port %p: format change %s", priv->output, (gchar*)g_bytes_get_data (formats, NULL)); g_object_set (priv->input, "format", formats, NULL); } diff --git a/pinos/server/port.c b/pinos/server/port.c index a725ec824..31dcf5ebf 100644 --- a/pinos/server/port.c +++ b/pinos/server/port.c @@ -597,34 +597,6 @@ pinos_port_filter_formats (PinosPort *port, return pinos_format_filter (priv->possible_formats, filter, error); } -static void -parse_control_buffer (PinosPort *port, PinosBuffer *buffer) -{ - PinosPortPrivate *priv = port->priv; - PinosBufferIter it; - - pinos_buffer_iter_init (&it, buffer); - while (pinos_buffer_iter_next (&it)) { - switch (pinos_buffer_iter_get_type (&it)) { - case PINOS_PACKET_TYPE_FORMAT_CHANGE: - { - PinosPacketFormatChange change; - - if (!pinos_buffer_iter_parse_format_change (&it, &change)) - continue; - - if (priv->format) - g_bytes_unref (priv->format); - priv->format = g_bytes_new (change.format, strlen (change.format) + 1); - g_object_notify (G_OBJECT (port), "format"); - break; - } - default: - break; - } - } -} - void pinos_port_activate (PinosPort *port) { @@ -668,15 +640,13 @@ pinos_port_deactivate (PinosPort *port) */ gboolean pinos_port_receive_buffer (PinosPort *port, - PinosBuffer *buffer, + SpaBuffer *buffer, GError **error) { gboolean res = TRUE; PinosPortPrivate *priv = port->priv; PINOS_DEBUG_TRANSPORT ("port %p: receive buffer %p", port, buffer); - if (pinos_buffer_get_flags (buffer) & PINOS_BUFFER_FLAG_CONTROL) - parse_control_buffer (port, buffer); if (priv->received_buffer_cb) res = priv->received_buffer_cb (port, buffer, error, priv->received_buffer_data); @@ -687,7 +657,7 @@ pinos_port_receive_buffer (PinosPort *port, /** * pinos_port_send_buffer: * @port: a #PinosPort - * @buffer: a #PinosBuffer + * @buffer: a #SpaBuffer * @error: a #GError or %NULL * * Send @buffer out on @port. @@ -696,7 +666,7 @@ pinos_port_receive_buffer (PinosPort *port, */ gboolean pinos_port_send_buffer (PinosPort *port, - PinosBuffer *buffer, + SpaBuffer *buffer, GError **error) { gboolean res = TRUE; @@ -706,8 +676,6 @@ pinos_port_send_buffer (PinosPort *port, g_return_val_if_fail (PINOS_IS_PORT (port), FALSE); PINOS_DEBUG_TRANSPORT ("port %p: send buffer %p", port, buffer); - if (pinos_buffer_get_flags (buffer) & PINOS_BUFFER_FLAG_CONTROL) - parse_control_buffer (port, buffer); priv = port->priv; diff --git a/pinos/server/port.h b/pinos/server/port.h index fb30b6d17..85be6c75e 100644 --- a/pinos/server/port.h +++ b/pinos/server/port.h @@ -31,6 +31,7 @@ typedef struct _PinosPortPrivate PinosPortPrivate; #include #include #include +#include #define PINOS_TYPE_PORT (pinos_port_get_type ()) #define PINOS_IS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_PORT)) @@ -65,7 +66,7 @@ struct _PinosPortClass { GTask *task); }; -typedef gboolean (*PinosBufferCallback) (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data); +typedef gboolean (*PinosBufferCallback) (PinosPort *port, SpaBuffer *buffer, GError **error, gpointer user_data); /* normal GObject stuff */ GType pinos_port_get_type (void); @@ -99,10 +100,10 @@ void pinos_port_activate (PinosPort *port); void pinos_port_deactivate (PinosPort *port); gboolean pinos_port_send_buffer (PinosPort *port, - PinosBuffer *buffer, + SpaBuffer *buffer, GError **error); gboolean pinos_port_receive_buffer (PinosPort *port, - PinosBuffer *buffer, + SpaBuffer *buffer, GError **error); G_END_DECLS diff --git a/spa/include/spa/buffer.h b/spa/include/spa/buffer.h index 8eff18f50..12b7e6092 100644 --- a/spa/include/spa/buffer.h +++ b/spa/include/spa/buffer.h @@ -101,10 +101,11 @@ typedef struct { /** * SpaDataType: * @SPA_DATA_TYPE_INVALID: invalid data type, is ignored - * @SPA_DATA_TYPE_MEMPTR: data and size point to memory accassible by the + * @SPA_DATA_TYPE_MEMPTR: data and size point to memory accessible by the * CPU. * @SPA_DATA_TYPE_FD: data points to an int file descriptor that can be * mmapped. + * @SPA_DATA_TYPE_MEMID: data points to the id of the memory block to use * @SPA_DATA_TYPE_POINTER: data points to some other datastructure, the * type can be found in ptr_type */ @@ -112,6 +113,7 @@ typedef enum { SPA_DATA_TYPE_INVALID = 0, SPA_DATA_TYPE_MEMPTR, SPA_DATA_TYPE_FD, + SPA_DATA_TYPE_MEMID, SPA_DATA_TYPE_POINTER, } SpaDataType; @@ -138,6 +140,7 @@ typedef struct { * SpaBuffer: * @refcount: reference counter * @notify: called when the refcount reaches 0 + * @id: buffer id * @size: total size of the buffer data * @n_metas: number of metadata * @metas: array of @n_metas metadata @@ -147,6 +150,7 @@ typedef struct { struct _SpaBuffer { volatile int refcount; SpaNotify notify; + uint32_t id; size_t size; unsigned int n_metas; SpaMeta *metas; diff --git a/spa/include/spa/control.h b/spa/include/spa/control.h new file mode 100644 index 000000000..eb547bba8 --- /dev/null +++ b/spa/include/spa/control.h @@ -0,0 +1,273 @@ +/* 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. + */ + +#ifndef __SPA_CONTROL_H__ +#define __SPA_CONTROL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _SpaControl SpaControl; +typedef struct _SpaControlIter SpaControlIter; +typedef struct _SpaControlBuilder SpaControlBuilder; + +#define SPA_CONTROL_VERSION 0 + +#include +#include +#include +#include + +struct _SpaControl { + size_t x[16]; +}; + +SpaResult spa_control_init_data (SpaControl *control, + void *data, + size_t size, + int *fds, + unsigned int n_fds); + +SpaResult spa_control_clear (SpaControl *control); + +uint32_t spa_control_get_version (SpaControl *control); +int spa_control_get_fd (SpaControl *control, + unsigned int index, + bool close); + +typedef enum { + SPA_CONTROL_CMD_INVALID = 0, + /* client to server */ + SPA_CONTROL_CMD_NODE_UPDATE = 1, + SPA_CONTROL_CMD_PORT_UPDATE = 2, + SPA_CONTROL_CMD_PORT_REMOVED = 3, + + SPA_CONTROL_CMD_START_CONFIGURE = 4, + + SPA_CONTROL_CMD_PORT_STATUS_CHANGE = 5, + SPA_CONTROL_CMD_START_ALLOC = 6, + + SPA_CONTROL_CMD_NEED_INPUT = 7, + SPA_CONTROL_CMD_HAVE_OUTPUT = 8, + + /* server to client */ + SPA_CONTROL_CMD_ADD_PORT = 32, + SPA_CONTROL_CMD_REMOVE_PORT = 33, + + SPA_CONTROL_CMD_SET_FORMAT = 34, + SPA_CONTROL_CMD_SET_PROPERTY = 35, + SPA_CONTROL_CMD_END_CONFIGURE = 36, + + SPA_CONTROL_CMD_PAUSE = 37, + SPA_CONTROL_CMD_START = 38, + SPA_CONTROL_CMD_STOP = 39, + + /* both */ + SPA_CONTROL_CMD_ADD_MEM = 64, + SPA_CONTROL_CMD_REMOVE_MEM = 65, + + SPA_CONTROL_CMD_ADD_BUFFER = 66, + SPA_CONTROL_CMD_REMOVE_BUFFER = 67, + SPA_CONTROL_CMD_PROCESS_BUFFER = 68, + SPA_CONTROL_CMD_REUSE_BUFFER = 69, + +} SpaControlCmd; + +/* SPA_CONTROL_CMD_NODE_UPDATE */ +typedef struct { + uint32_t change_mask; + uint32_t max_input_ports; + uint32_t max_output_ports; + const SpaProps *props; +} SpaControlCmdNodeUpdate; + +/* SPA_CONTROL_CMD_PORT_UPDATE */ +typedef struct { + uint32_t port; + uint32_t change_mask; + uint32_t direction; + uint32_t n_possible_formats; + const SpaFormat **possible_formats; + const SpaProps *props; + const SpaPortInfo *info; +} SpaControlCmdPortUpdate; + +/* SPA_CONTROL_CMD_PORT_REMOVED */ +typedef struct { + uint32_t port; +} SpaControlCmdPortRemoved; + +/* SPA_CONTROL_CMD_START_CONFIGURE */ + +/* SPA_CONTROL_CMD_PORT_STATUS_CHANGE */ + +/* SPA_CONTROL_CMD_START_ALLOC */ + +/* SPA_CONTROL_CMD_NEED_INPUT */ +typedef struct { + uint32_t port; +} SpaControlCmdNeedInput; + +/* SPA_CONTROL_CMD_HAVE_OUTPUT */ +typedef struct { + uint32_t port; +} SpaControlCmdHaveOutput; + + +/* SPA_CONTROL_CMD_ADD_PORT */ +typedef struct { + uint32_t port; + uint32_t direction; +} SpaControlCmdAddPort; + +/* SPA_CONTROL_CMD_REMOVE_PORT */ +typedef struct { + uint32_t port; +} SpaControlCmdRemovePort; + + +/* SPA_CONTROL_CMD_SET_FORMAT */ +typedef struct { + uint32_t port; + const SpaFormat *format; + const char *str; +} SpaControlCmdSetFormat; + +/* SPA_CONTROL_CMD_SET_PROPERTY */ +typedef struct { + uint32_t port; + uint32_t id; + uint32_t size; + void *value; +} SpaControlCmdSetProperty; + +/* SPA_CONTROL_CMD_END_CONFIGURE */ + +/* SPA_CONTROL_CMD_PAUSE */ +/* SPA_CONTROL_CMD_START */ +/* SPA_CONTROL_CMD_STOP */ + + +/* SPA_CONTROL_CMD_ADD_MEM */ +typedef struct { + uint32_t port; + uint32_t id; + uint32_t type; + uint32_t fd_index; + uint64_t offset; + uint64_t size; +} SpaControlCmdAddMem; + +/* SPA_CONTROL_CMD_REMOVE_MEM */ +typedef struct { + uint32_t port; + uint32_t id; +} SpaControlCmdRemoveMem; + +/* SPA_CONTROL_CMD_ADD_BUFFER */ +typedef struct { + uint32_t port; + SpaBuffer *buffer; +} SpaControlCmdAddBuffer; + +/* SPA_CONTROL_CMD_REMOVE_BUFFER */ +typedef struct { + uint32_t port; + uint32_t id; +} SpaControlCmdRemoveBuffer; + +/* SPA_CONTROL_CMD_PROCESS_BUFFER */ +typedef struct { + uint32_t port; + uint32_t id; +} SpaControlCmdProcessBuffer; + +/* SPA_CONTROL_CMD_REUSE_BUFFER */ +typedef struct { + uint32_t port; + uint32_t id; +} SpaControlCmdReuseBuffer; + + + + +struct _SpaControlIter { + /*< private >*/ + size_t x[16]; +}; + +SpaResult spa_control_iter_init_full (SpaControlIter *iter, + SpaControl *control, + uint32_t version); +#define spa_control_iter_init(i,b) spa_control_iter_init_full(i,b, SPA_CONTROL_VERSION); + +SpaResult spa_control_iter_next (SpaControlIter *iter); +SpaResult spa_control_iter_end (SpaControlIter *iter); + +SpaControlCmd spa_control_iter_get_cmd (SpaControlIter *iter); +void * spa_control_iter_get_data (SpaControlIter *iter, size_t *size); + +SpaResult spa_control_iter_parse_cmd (SpaControlIter *iter, + void *command); + +/** + * SpaControlBuilder: + */ +struct _SpaControlBuilder { + /*< private >*/ + size_t x[16]; +}; + +SpaResult spa_control_builder_init_full (SpaControlBuilder *builder, + uint32_t version, + void *data, + size_t max_data, + int *fds, + unsigned int max_fds); +#define spa_control_builder_init_into(b,d,md,f,mf) spa_control_builder_init_full(b, SPA_CONTROL_VERSION,d,md,f,mf); +#define spa_control_builder_init(b) spa_control_builder_init_into(b, NULL, 0, NULL, 0); + +SpaResult spa_control_builder_clear (SpaControlBuilder *builder); +SpaResult spa_control_builder_end (SpaControlBuilder *builder, + SpaControl *control); + +int spa_control_builder_add_fd (SpaControlBuilder *builder, + int fd, + bool close); + +SpaResult spa_control_builder_add_cmd (SpaControlBuilder *builder, + SpaControlCmd cmd, + void *command); + +/* IO */ +SpaResult spa_control_read (SpaControl *control, + int fd, + void *data, + size_t max_data, + int *fds, + unsigned int max_fds); +SpaResult spa_control_write (SpaControl *control, + int fd); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPA_CONTROL_H__ */ diff --git a/spa/include/spa/debug.h b/spa/include/spa/debug.h new file mode 100644 index 000000000..5c83f3d4f --- /dev/null +++ b/spa/include/spa/debug.h @@ -0,0 +1,38 @@ +/* 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. + */ + +#ifndef __SPA_DEBUG_H__ +#define __SPA_DEBUG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +SpaResult spa_debug_port_info (const SpaPortInfo *info); +SpaResult spa_debug_dump_mem (void *data, size_t size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* __SPA_DEBUG_H__ */ diff --git a/spa/include/spa/event.h b/spa/include/spa/event.h index 2b9c32d74..d5733e5b5 100644 --- a/spa/include/spa/event.h +++ b/spa/include/spa/event.h @@ -45,6 +45,7 @@ typedef struct _SpaEvent SpaEvent; * @SPA_EVENT_TYPE_MARKER: emited when MARK command completed * @SPA_EVENT_TYPE_ERROR: emited when error occured * @SPA_EVENT_TYPE_BUFFERING: emited when buffering is in progress + * @SPA_EVENT_TYPE_REQUEST_REFRESH: emited when a keyframe refresh is needed */ typedef enum { SPA_EVENT_TYPE_INVALID = 0, @@ -60,6 +61,7 @@ typedef enum { SPA_EVENT_TYPE_MARKER, SPA_EVENT_TYPE_ERROR, SPA_EVENT_TYPE_BUFFERING, + SPA_EVENT_TYPE_REQUEST_REFRESH, } SpaEventType; struct _SpaEvent { diff --git a/spa/include/spa/format.h b/spa/include/spa/format.h index d415fe832..3f88ad94d 100644 --- a/spa/include/spa/format.h +++ b/spa/include/spa/format.h @@ -33,11 +33,26 @@ typedef enum { SPA_MEDIA_TYPE_INVALID = 0, SPA_MEDIA_TYPE_AUDIO = 1, SPA_MEDIA_TYPE_VIDEO = 2, + SPA_MEDIA_TYPE_IMAGE = 3, } SpaMediaType; typedef enum { SPA_MEDIA_SUBTYPE_INVALID = 0, SPA_MEDIA_SUBTYPE_RAW = 1, + SPA_MEDIA_SUBTYPE_H264 = 2, + SPA_MEDIA_SUBTYPE_MJPG = 3, + SPA_MEDIA_SUBTYPE_DV = 4, + SPA_MEDIA_SUBTYPE_MPEGTS = 5, + SPA_MEDIA_SUBTYPE_H263 = 6, + SPA_MEDIA_SUBTYPE_MPEG1 = 7, + SPA_MEDIA_SUBTYPE_MPEG2 = 8, + SPA_MEDIA_SUBTYPE_MPEG4 = 9, + SPA_MEDIA_SUBTYPE_XVID = 10, + SPA_MEDIA_SUBTYPE_VC1 = 11, + SPA_MEDIA_SUBTYPE_VP8 = 12, + SPA_MEDIA_SUBTYPE_VP9 = 13, + SPA_MEDIA_SUBTYPE_JPEG = 14, + SPA_MEDIA_SUBTYPE_BAYER = 15, } SpaMediaSubType; struct _SpaFormat { diff --git a/spa/include/spa/props.h b/spa/include/spa/props.h index f3c81f608..892696136 100644 --- a/spa/include/spa/props.h +++ b/spa/include/spa/props.h @@ -244,7 +244,6 @@ SpaResult spa_props_copy (const SpaProps *src, SpaProps *dest); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/spa/include/spa/video/format.h b/spa/include/spa/video/format.h index f25c6006b..1aaa8fe29 100644 --- a/spa/include/spa/video/format.h +++ b/spa/include/spa/video/format.h @@ -57,6 +57,10 @@ SpaResult spa_video_raw_format_init (SpaVideoRawFormat *format); SpaResult spa_video_raw_format_parse (const SpaFormat *format, SpaVideoRawFormat *rawformat); +SpaResult spa_video_raw_fill_default_info (SpaVideoRawInfo *info); +SpaResult spa_video_raw_fill_prop_info (SpaPropInfo *info, + SpaPropIdVideo id, + size_t offset); #ifdef __cplusplus } /* extern "C" */ diff --git a/spa/lib/audio-raw.c b/spa/lib/audio-raw.c index 79d9368f1..e75672314 100644 --- a/spa/lib/audio-raw.c +++ b/spa/lib/audio-raw.c @@ -235,7 +235,7 @@ spa_audio_raw_format_parse (const SpaFormat *format, spa_audio_raw_format_init (rawformat); props = &format->props; - if ((res = props->get_prop (props, SPA_PROP_ID_AUDIO_RAW_INFO, &value)) < 0) + if ((res = props->get_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_RAW_INFO), &value)) < 0) goto fallback; if (value.type != SPA_PROP_TYPE_POINTER || value.size != sizeof (SpaAudioRawInfo)) diff --git a/spa/lib/control.c b/spa/lib/control.c new file mode 100644 index 000000000..f7ae76002 --- /dev/null +++ b/spa/lib/control.c @@ -0,0 +1,1048 @@ +/* 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 +#include +#include +#include +#include +#include + +#include + +#if 0 +#define SPA_DEBUG_CONTROL(format,args...) fprintf(stderr,format,##args) +#else +#define SPA_DEBUG_CONTROL(format,args...) +#endif + +typedef struct { + uint32_t version; + uint32_t flags; + uint32_t length; +} SpaStackHeader; + +typedef struct { + void *data; + size_t size; + size_t max_size; + void *free_data; + int *fds; + int n_fds; + int max_fds; + void *free_fds; + size_t magic; +} SpaStackControl; + +#define SSC(c) ((SpaStackControl *) (c)) +#define SSC_MAGIC ((size_t) 5493683301u) +#define is_valid_control(c) (c != NULL && \ + SSC(c)->magic == SSC_MAGIC) + +/** + * spa_control_init_data: + * @control: a #SpaControl + * @data: data + * @size: size of @data + * @fds: file descriptors + * @n_fds: number of file descriptors + * + * Initialize @control with @data and @size and @fds and @n_fds. + * The memory pointer to by @data and @fds becomes property of @control + * and should not be freed or modified until all references to the control + * are gone. + */ +SpaResult +spa_control_init_data (SpaControl *control, + void *data, + size_t size, + int *fds, + unsigned int n_fds) +{ + SpaStackControl *sc = SSC (control); + + SPA_DEBUG_CONTROL ("control %p: init", control); + + sc->magic = SSC_MAGIC; + sc->data = data; + sc->size = size; + sc->max_size = size; + sc->free_data = NULL; + sc->fds = fds; + sc->n_fds = n_fds; + sc->max_fds = n_fds; + sc->free_fds = NULL; + + return SPA_RESULT_OK; +} + +/** + * spa_control_get_version + * @control: a #SpaControl + * + * Get the control version + * + * Returns: the control version. + */ +uint32_t +spa_control_get_version (SpaControl *control) +{ + SpaStackControl *sc = SSC (control); + SpaStackHeader *hdr; + + if (!is_valid_control (control)) + return -1; + + hdr = sc->data; + return hdr->version; +} + +/** + * spa_control_get_fd: + * @control: a #SpaControl + * @index: an index + * @steal: steal the fd + * + * Get the file descriptor at @index in @control. + * + * Returns: a file descriptor at @index in @control. The file descriptor + * is not duplicated in any way. -1 is returned on error. + */ +int +spa_control_get_fd (SpaControl *control, + unsigned int index, + bool close) +{ + SpaStackControl *sc = SSC (control); + int fd; + + if (!is_valid_control (control)) + return -1; + + if (sc->fds == NULL || sc->n_fds < index) + return -1; + + fd = sc->fds[index]; + if (fd < 0) + fd = -fd; + sc->fds[index] = close ? fd : -fd; + + return fd; +} + +SpaResult +spa_control_clear (SpaControl *control) +{ + SpaStackControl *sc = SSC (control); + int i; + + if (!is_valid_control (control)) + return SPA_RESULT_INVALID_ARGUMENTS; + + sc->magic = 0; + free (sc->free_data); + for (i = 0; i < sc->n_fds; i++) { + if (sc->fds[i] > 0) { + SPA_DEBUG_CONTROL ("%p: close %d %d", control, i, sc->fds[i]); + if (close (sc->fds[i]) < 0) + perror ("close"); + } + } + free (sc->free_fds); + sc->n_fds = 0; + + return SPA_RESULT_OK; +} + + +/** + * SpaControlIter: + * + * #SpaControlIter is an opaque data structure and can only be accessed + * using the following functions. + */ +struct stack_iter { + size_t magic; + uint32_t version; + SpaStackControl *control; + size_t offset; + + SpaControlCmd cmd; + size_t size; + void *data; +}; + +#define SCSI(i) ((struct stack_iter *) (i)) +#define SCSI_MAGIC ((size_t) 6739527471u) +#define is_valid_iter(i) (i != NULL && \ + SCSI(i)->magic == SCSI_MAGIC) + +/** + * spa_control_iter_init: + * @iter: a #SpaControlIter + * @control: a #SpaControl + * + * Initialize @iter to iterate the packets in @control. + */ +SpaResult +spa_control_iter_init_full (SpaControlIter *iter, + SpaControl *control, + uint32_t version) +{ + struct stack_iter *si = SCSI (iter); + + if (iter == NULL || !is_valid_control (control)) + return SPA_RESULT_INVALID_ARGUMENTS; + + si->magic = SCSI_MAGIC; + si->version = version; + si->control = SSC (control); + si->offset = 0; + si->cmd = SPA_CONTROL_CMD_INVALID; + si->size = sizeof (SpaStackHeader); + si->data = NULL; + + return SPA_RESULT_OK; +} + +static bool +read_length (uint8_t * data, unsigned int size, size_t * length, size_t * skip) +{ + size_t len, offset; + uint8_t b; + + /* start reading the length, we need this to skip to the data later */ + len = offset = 0; + do { + if (offset >= size) + return false; + + b = data[offset++]; + len = (len << 7) | (b & 0x7f); + } while (b & 0x80); + + /* check remaining control size */ + if (size - offset < len) + return false; + + *length = len; + *skip = offset; + + return true; +} + +/** + * spa_control_iter_next: + * @iter: a #SpaControlIter + * + * Move to the next packet in @iter. + * + * Returns: %SPA_RESULT_OK if more packets are available. + */ +SpaResult +spa_control_iter_next (SpaControlIter *iter) +{ + struct stack_iter *si = SCSI (iter); + size_t len, size, skip; + uint8_t *data; + + if (!is_valid_iter (iter)) + return SPA_RESULT_INVALID_ARGUMENTS; + + /* move to next packet */ + si->offset += si->size; + + /* now read packet */ + data = si->control->data; + size = si->control->size; + if (si->offset >= size) + return SPA_RESULT_ERROR; + + data += si->offset; + size -= si->offset; + + if (size < 1) + return SPA_RESULT_ERROR; + + si->cmd = *data; + + data++; + size--; + + if (!read_length (data, size, &len, &skip)) + return SPA_RESULT_ERROR; + + si->size = len; + si->data = data + skip; + si->offset += 1 + skip; + + return SPA_RESULT_OK; +} + +/** + * spa_control_iter_done: + * @iter: a #SpaControlIter + * + * End iterations on @iter. + */ +SpaResult +spa_control_iter_end (SpaControlIter *iter) +{ + struct stack_iter *si = SCSI (iter); + + if (!is_valid_iter (iter)) + return SPA_RESULT_INVALID_ARGUMENTS; + + si->magic = 0; + + return SPA_RESULT_OK; +} + +SpaControlCmd +spa_control_iter_get_cmd (SpaControlIter *iter) +{ + struct stack_iter *si = SCSI (iter); + + if (!is_valid_iter (iter)) + return SPA_CONTROL_CMD_INVALID; + + return si->cmd; +} + +void * +spa_control_iter_get_data (SpaControlIter *iter, size_t *size) +{ + struct stack_iter *si = SCSI (iter); + + if (!is_valid_iter (iter)) + return NULL; + + if (size) + *size = si->size; + + return si->data; +} + +typedef struct { + SpaBuffer buffer; + SpaMeta metas[16]; + SpaData datas[16]; + int mem[16]; +} MyBuffer; + +static SpaResult +parse_add_buffer (struct stack_iter *si, + SpaControlCmdAddBuffer *command) +{ + MyBuffer *b; + uint32_t *p = si->data; + unsigned int i; + + command->port = *p++; + b = malloc (sizeof (MyBuffer)); + b->buffer.refcount = 1; + b->buffer.notify = free; + b->buffer.id = *(uint32_t *)p++; + b->buffer.size = *(uint32_t *)p++; + b->buffer.n_metas = *(uint32_t *)p++; + b->buffer.metas = b->metas; + b->buffer.n_datas = *(uint32_t *)p++; + b->buffer.datas = b->datas; + + for (i = 0; i < b->buffer.n_metas; i++) { + SpaMeta *m = &b->metas[i]; + m->type = *p++; + m->size = *p++; + m->data = p; + p = p + m->size / 4; + } + for (i = 0; i < b->buffer.n_datas; i++) { + SpaData *d = &b->datas[i]; + d->type = SPA_DATA_TYPE_MEMID; + d->ptr_type = NULL; + b->mem[i] = *p++; + d->ptr = &b->mem[i]; + d->offset = *p++; + d->size = *p++; + d->stride = *p++; + } + command->buffer = &b->buffer; + + return SPA_RESULT_OK; +} + +SpaResult +spa_control_iter_parse_cmd (SpaControlIter *iter, + void *command) +{ + struct stack_iter *si = SCSI (iter); + SpaResult res = SPA_RESULT_OK; + + if (!is_valid_iter (iter)) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (si->cmd) { + /* C -> S */ + case SPA_CONTROL_CMD_NODE_UPDATE: + case SPA_CONTROL_CMD_PORT_UPDATE: + fprintf (stderr, "implement iter of %d\n", si->cmd); + break; + + case SPA_CONTROL_CMD_PORT_REMOVED: + if (si->size < sizeof (SpaControlCmdPortRemoved)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdPortRemoved)); + break; + + case SPA_CONTROL_CMD_START_CONFIGURE: + case SPA_CONTROL_CMD_PORT_STATUS_CHANGE: + case SPA_CONTROL_CMD_START_ALLOC: + break; + + case SPA_CONTROL_CMD_NEED_INPUT: + if (si->size < sizeof (SpaControlCmdNeedInput)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdNeedInput)); + break; + + case SPA_CONTROL_CMD_HAVE_OUTPUT: + if (si->size < sizeof (SpaControlCmdHaveOutput)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdHaveOutput)); + break; + + /* S -> C */ + case SPA_CONTROL_CMD_ADD_PORT: + if (si->size < sizeof (SpaControlCmdAddPort)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdAddPort)); + break; + + case SPA_CONTROL_CMD_REMOVE_PORT: + if (si->size < sizeof (SpaControlCmdRemovePort)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdRemovePort)); + break; + + case SPA_CONTROL_CMD_SET_FORMAT: + { + SpaControlCmdSetFormat *cmd; + uint32_t *p = si->data; + + cmd = command; + cmd->port = *p++; + cmd->str = (char *)p; + break; + } + + case SPA_CONTROL_CMD_SET_PROPERTY: + fprintf (stderr, "implement iter of %d\n", si->cmd); + break; + + case SPA_CONTROL_CMD_END_CONFIGURE: + case SPA_CONTROL_CMD_PAUSE: + case SPA_CONTROL_CMD_START: + case SPA_CONTROL_CMD_STOP: + break; + + /* bidirectional */ + case SPA_CONTROL_CMD_ADD_MEM: + if (si->size < sizeof (SpaControlCmdAddMem)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdAddMem)); + break; + + case SPA_CONTROL_CMD_REMOVE_MEM: + if (si->size < sizeof (SpaControlCmdRemoveMem)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdRemoveMem)); + break; + + case SPA_CONTROL_CMD_ADD_BUFFER: + res = parse_add_buffer (si, command); + break; + + case SPA_CONTROL_CMD_REMOVE_BUFFER: + if (si->size < sizeof (SpaControlCmdRemoveBuffer)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdRemoveBuffer)); + break; + + case SPA_CONTROL_CMD_PROCESS_BUFFER: + if (si->size < sizeof (SpaControlCmdProcessBuffer)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdProcessBuffer)); + break; + + case SPA_CONTROL_CMD_REUSE_BUFFER: + if (si->size < sizeof (SpaControlCmdReuseBuffer)) + return SPA_RESULT_ERROR; + memcpy (command, si->data, sizeof (SpaControlCmdReuseBuffer)); + break; + + case SPA_CONTROL_CMD_INVALID: + return SPA_RESULT_ERROR; + } + return res; +} + + +struct stack_builder { + size_t magic; + + SpaStackHeader *sh; + SpaStackControl control; + + SpaControlCmd cmd; + size_t offset; +}; + +#define SCSB(b) ((struct stack_builder *) (b)) +#define SCSB_MAGIC ((size_t) 8103647428u) +#define is_valid_builder(b) (b != NULL && \ + SCSB(b)->magic == SCSB_MAGIC) + + +/** + * spa_control_builder_init_full: + * @builder: a #SpaControlBuilder + * @version: a version + * @data: data to build into or %NULL to allocate + * @max_data: allocated size of @data + * @fds: memory for fds + * @max_fds: maximum number of fds in @fds + * + * Initialize a stack allocated @builder and set the @version. + */ +SpaResult +spa_control_builder_init_full (SpaControlBuilder *builder, + uint32_t version, + void *data, + size_t max_data, + int *fds, + unsigned int max_fds) +{ + struct stack_builder *sb = SCSB (builder); + SpaStackHeader *sh; + + if (builder == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + sb->magic = SCSB_MAGIC; + + if (max_data < sizeof (SpaStackHeader) || data == NULL) { + sb->control.max_size = sizeof (SpaStackHeader) + 128; + sb->control.data = malloc (sb->control.max_size); + sb->control.free_data = sb->control.data; + fprintf (stderr, "builder %p: realloc control memory %zd -> %zd\n", + builder, max_data, sb->control.max_size); + } else { + sb->control.max_size = max_data; + sb->control.data = data; + sb->control.free_data = NULL; + } + sb->control.size = sizeof (SpaStackHeader); + + sb->control.fds = fds; + sb->control.max_fds = max_fds; + sb->control.n_fds = 0; + sb->control.free_fds = NULL; + + sh = sb->sh = sb->control.data; + sh->version = version; + sh->flags = 0; + sh->length = 0; + + sb->cmd = 0; + sb->offset = 0; + + return SPA_RESULT_OK; +} + +/** + * spa_control_builder_clear: + * @builder: a #SpaControlBuilder + * + * Clear the memory used by @builder. This can be used to abort building the + * control. + * + * @builder becomes invalid after this function and can be reused with + * spa_control_builder_init() + */ +SpaResult +spa_control_builder_clear (SpaControlBuilder *builder) +{ + struct stack_builder *sb = SCSB (builder); + + if (!is_valid_builder (builder)) + return SPA_RESULT_INVALID_ARGUMENTS; + + sb->magic = 0; + free (sb->control.free_data); + free (sb->control.free_fds); + + return SPA_RESULT_OK; +} + +/** + * spa_control_builder_end: + * @builder: a #SpaControlBuilder + * @control: a #SpaControl + * + * Ends the building process and fills @control with the constructed + * #SpaControl. + * + * @builder becomes invalid after this function and can be reused with + * spa_control_builder_init() + */ +SpaResult +spa_control_builder_end (SpaControlBuilder *builder, + SpaControl *control) +{ + struct stack_builder *sb = SCSB (builder); + SpaStackControl *sc = SSC (control); + + if (!is_valid_builder (builder) || control == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + sb->magic = 0; + sb->sh->length = sb->control.size - sizeof (SpaStackHeader); + + sc->magic = SSC_MAGIC; + sc->data = sb->control.data; + sc->size = sb->control.size; + sc->max_size = sb->control.max_size; + sc->free_data = sb->control.free_data; + + sc->fds = sb->control.fds; + sc->n_fds = sb->control.n_fds; + sc->max_fds = sb->control.max_fds; + sc->free_fds = sb->control.free_fds; + + SPA_DEBUG_CONTROL ("builder %p: control %p init", builder, control); + + return SPA_RESULT_OK; +} + +/** + * spa_control_builder_add_fd: + * @builder: a #SpaControlBuilder + * @fd: a valid fd + * + * Add the file descriptor @fd to @builder. + * + * Returns: the index of the file descriptor in @builder. + */ +int +spa_control_builder_add_fd (SpaControlBuilder *builder, + int fd, + bool close) +{ + struct stack_builder *sb = SCSB (builder); + int index; + + if (!is_valid_builder (builder) || fd < 0) + return -1; + + if (sb->control.n_fds >= sb->control.max_fds) { + int new_size = sb->control.max_fds + 8; + fprintf (stderr, "builder %p: realloc control fds %d -> %d\n", + builder, sb->control.max_fds, new_size); + sb->control.max_fds = new_size; + sb->control.free_fds = realloc (sb->control.free_fds, new_size * sizeof (int)); + sb->control.fds = sb->control.free_fds; + } + index = sb->control.n_fds; + sb->control.fds[index] = close ? fd : -fd; + sb->control.n_fds++; + + return index; +} +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +static void * +builder_ensure_size (struct stack_builder *sb, size_t size) +{ + if (sb->control.size + size > sb->control.max_size) { + size_t new_size = sb->control.size + MAX (size, 1024); + fprintf (stderr, "builder %p: realloc control memory %zd -> %zd\n", + sb, sb->control.max_size, new_size); + sb->control.max_size = new_size; + sb->control.free_data = realloc (sb->control.free_data, new_size); + sb->sh = sb->control.data = sb->control.free_data; + } + return (uint8_t *) sb->control.data + sb->control.size; +} + +static void * +builder_add_cmd (struct stack_builder *sb, SpaControlCmd cmd, size_t size) +{ + uint8_t *p; + unsigned int plen; + + plen = 1; + while (size >> (7 * plen)) + plen++; + + /* 1 for cmd, plen for size and size for payload */ + p = builder_ensure_size (sb, 1 + plen + size); + + sb->cmd = cmd; + sb->offset = sb->control.size; + sb->control.size += 1 + plen + size; + + *p++ = cmd; + /* write length */ + while (plen) { + plen--; + *p++ = ((plen > 0) ? 0x80 : 0) | ((size >> (7 * plen)) & 0x7f); + } + return p; +} + +static SpaResult +build_add_buffer (struct stack_builder *sb, + SpaControlCmdAddBuffer *command) +{ + unsigned int i; + size_t size; + SpaBuffer *b = command->buffer; + uint32_t *p; + + /* port + id + size + n_metas + n_datas */ + size = 4 + 4 + 4 + 4 + 4; + + for (i = 0; i < b->n_metas; i++) { + SpaMeta *m = &b->metas[i]; + /* type + size + data */ + size += 4 + 4 + m->size; + } + for (i = 0; i < b->n_datas; i++) { + SpaData *d = &b->datas[i]; + if (d->type != SPA_DATA_TYPE_MEMID) + continue; + /* memidx + offset + size + stride */ + size += 4 + 4 + 4 + 4; + } + p = builder_add_cmd (sb, SPA_CONTROL_CMD_ADD_BUFFER, size); + + *p++ = command->port; + *p++ = b->id; + *p++ = b->size; + *p++ = b->n_metas; + *p++ = b->n_datas; + + for (i = 0; i < b->n_metas; i++) { + SpaMeta *m = &b->metas[i]; + + *p++ = m->type; + *p++ = m->size; + memcpy (p, m->data, m->size); + p = p + m->size / 4; + } + for (i = 0; i < b->n_datas; i++) { + SpaData *d = &b->datas[i]; + if (d->type != SPA_DATA_TYPE_MEMID) + continue; + *p++ = *((uint32_t*)(d->ptr)); + *p++ = d->offset; + *p++ = d->size; + *p++ = d->stride; + } + return SPA_RESULT_OK; +} + +/** + * spa_control_builder_add_cmd: + * @builder: a #SpaControlBuilder + * @cmd: a #SpaControlCmd + * @command: a command + * + * Add a @cmd to @builder with data from @command. + * + * Returns: %TRUE on success. + */ +SpaResult +spa_control_builder_add_cmd (SpaControlBuilder *builder, + SpaControlCmd cmd, + void *command) +{ + struct stack_builder *sb = SCSB (builder); + void *p; + SpaResult res = SPA_RESULT_OK; + + if (!is_valid_builder (builder)) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (cmd) { + /* C -> S */ + case SPA_CONTROL_CMD_NODE_UPDATE: + case SPA_CONTROL_CMD_PORT_UPDATE: + fprintf (stderr, "implement builder of %d\n", cmd); + break; + + case SPA_CONTROL_CMD_PORT_REMOVED: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdPortRemoved)); + memcpy (p, command, sizeof (SpaControlCmdPortRemoved)); + break; + + case SPA_CONTROL_CMD_START_CONFIGURE: + p = builder_add_cmd (sb, cmd, 0); + break; + + case SPA_CONTROL_CMD_PORT_STATUS_CHANGE: + case SPA_CONTROL_CMD_START_ALLOC: + p = builder_add_cmd (sb, cmd, 0); + break; + + case SPA_CONTROL_CMD_NEED_INPUT: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdNeedInput)); + memcpy (p, command, sizeof (SpaControlCmdNeedInput)); + break; + + case SPA_CONTROL_CMD_HAVE_OUTPUT: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdHaveOutput)); + memcpy (p, command, sizeof (SpaControlCmdHaveOutput)); + break; + + /* S -> C */ + case SPA_CONTROL_CMD_ADD_PORT: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdAddPort)); + memcpy (p, command, sizeof (SpaControlCmdAddPort)); + break; + + case SPA_CONTROL_CMD_REMOVE_PORT: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdRemovePort)); + memcpy (p, command, sizeof (SpaControlCmdRemovePort)); + break; + + case SPA_CONTROL_CMD_SET_FORMAT: + { + size_t len, slen; + SpaControlCmdSetFormat *sf = command; + uint32_t *p; + + slen = strlen (sf->str)+1; + /* port + string */ + len = 4 + slen; + + p = builder_add_cmd (sb, cmd, len); + *p++ = sf->port; + memcpy ((char*)p, sf->str, slen); + break; + } + + case SPA_CONTROL_CMD_SET_PROPERTY: + fprintf (stderr, "implement builder of %d\n", cmd); + break; + + case SPA_CONTROL_CMD_END_CONFIGURE: + p = builder_add_cmd (sb, cmd, 0); + break; + + case SPA_CONTROL_CMD_PAUSE: + case SPA_CONTROL_CMD_START: + case SPA_CONTROL_CMD_STOP: + p = builder_add_cmd (sb, cmd, 0); + break; + + /* bidirectional */ + case SPA_CONTROL_CMD_ADD_MEM: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdAddMem)); + memcpy (p, command, sizeof (SpaControlCmdAddMem)); + break; + + case SPA_CONTROL_CMD_REMOVE_MEM: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdRemoveMem)); + memcpy (p, command, sizeof (SpaControlCmdRemoveMem)); + break; + + case SPA_CONTROL_CMD_ADD_BUFFER: + res = build_add_buffer (sb, (SpaControlCmdAddBuffer *) command); + break; + + case SPA_CONTROL_CMD_REMOVE_BUFFER: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdRemoveBuffer)); + memcpy (p, command, sizeof (SpaControlCmdRemoveBuffer)); + break; + + case SPA_CONTROL_CMD_PROCESS_BUFFER: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdProcessBuffer)); + memcpy (p, command, sizeof (SpaControlCmdProcessBuffer)); + break; + + case SPA_CONTROL_CMD_REUSE_BUFFER: + p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdReuseBuffer)); + memcpy (p, command, sizeof (SpaControlCmdReuseBuffer)); + break; + + default: + case SPA_CONTROL_CMD_INVALID: + return SPA_RESULT_INVALID_ARGUMENTS; + } + return res; +} + + +SpaResult +spa_control_read (SpaControl *control, + int fd, + void *data, + size_t max_data, + int *fds, + unsigned int max_fds) +{ + ssize_t len; + SpaStackHeader *hdr; + SpaStackControl *sc = (SpaStackControl *) control; + size_t need; + struct cmsghdr *cmsg; + struct msghdr msg = {0}; + struct iovec iov[1]; + char cmsgbuf[CMSG_SPACE (max_fds * sizeof (int))]; + + sc->data = data; + sc->max_size = max_data; + sc->size = 0; + sc->free_data = NULL; + sc->fds = fds; + sc->max_fds = max_fds; + sc->n_fds = 0; + sc->free_fds = NULL; + hdr = sc->data; + + /* read header and control messages first */ + iov[0].iov_base = hdr; + iov[0].iov_len = sizeof (SpaStackHeader);; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof (cmsgbuf); + msg.msg_flags = MSG_CMSG_CLOEXEC; + + while (true) { + len = recvmsg (fd, &msg, msg.msg_flags); + if (len < 0) { + if (errno == EINTR) + continue; + else + goto recv_error; + } + break; + } + if (len != sizeof (SpaStackHeader)) + return SPA_RESULT_ERROR; + + /* now we know the total length */ + need = sizeof (SpaStackHeader) + hdr->length; + + if (sc->max_size < need) { + fprintf (stderr, "control: realloc receive memory %zd -> %zd\n", sc->max_size, need); + sc->max_size = need; + hdr = sc->data = sc->free_data = realloc (sc->free_data, need); + } + sc->size = need; + + if (hdr->length > 0) { + /* read data */ + while (true) { + len = recv (fd, (uint8_t *)sc->data + sizeof (SpaStackHeader), hdr->length, 0); + if (len < 0) { + if (errno == EINTR) + continue; + else + goto recv_error; + } + break; + } + if (len != hdr->length) + return SPA_RESULT_ERROR; + } + + /* handle control messages */ + for (cmsg = CMSG_FIRSTHDR (&msg); cmsg != NULL; cmsg = CMSG_NXTHDR (&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) + continue; + + sc->n_fds = (cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg)) / sizeof (int); + memcpy (sc->fds, CMSG_DATA (cmsg), sc->n_fds * sizeof (int)); + } + sc->magic = SSC_MAGIC; + + return SPA_RESULT_OK; + + /* ERRORS */ +recv_error: + { + fprintf (stderr, "could not recvmsg: %s\n", strerror (errno)); + return SPA_RESULT_ERROR; + } +} + + +SpaResult +spa_control_write (SpaControl *control, + int fd) +{ + SpaStackControl *sc = (SpaStackControl *) control; + ssize_t len; + struct msghdr msg = {0}; + struct iovec iov[1]; + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE (sc->n_fds * sizeof (int))]; + int fds_len = sc->n_fds * sizeof (int), *cm, i; + + iov[0].iov_base = sc->data; + iov[0].iov_len = sc->size; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = CMSG_SPACE (fds_len); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN (fds_len); + cm = (int*)CMSG_DATA (cmsg); + for (i = 0; i < fds_len; i++) + cm[i] = sc->fds[i] > 0 ? sc->fds[i] : -sc->fds[i]; + msg.msg_controllen = cmsg->cmsg_len; + + while (true) { + len = sendmsg (fd, &msg, 0); + if (len < 0) { + if (errno == EINTR) + continue; + else + goto send_error; + } + break; + } + if (len != (ssize_t) sc->size) + return SPA_RESULT_ERROR; + + return SPA_RESULT_OK; + + /* ERRORS */ +send_error: + { + fprintf (stderr, "could not sendmsg: %s\n", strerror (errno)); + return SPA_RESULT_ERROR; + } +} diff --git a/spa/lib/debug.c b/spa/lib/debug.c index a8960070f..e66bde1f4 100644 --- a/spa/lib/debug.c +++ b/spa/lib/debug.c @@ -78,3 +78,20 @@ spa_debug_port_info (const SpaPortInfo *info) } return SPA_RESULT_OK; } + +SpaResult +spa_debug_dump_mem (void *mem, size_t size) +{ + uint8_t *t = mem; + int i; + + if (mem == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + for (i = 0; i < size; i++) { + printf ("%02x ", t[i]); + if (i % 16 == 8 || i == size - 1) + printf ("\n"); + } + return SPA_RESULT_OK; +} diff --git a/spa/lib/meson.build b/spa/lib/meson.build index a155845b9..34285873a 100644 --- a/spa/lib/meson.build +++ b/spa/lib/meson.build @@ -1,4 +1,5 @@ spalib_sources = ['audio-raw.c', + 'control.c', 'debug.c', 'props.c', 'ringbuffer.c', @@ -8,3 +9,7 @@ spalib = shared_library('spa-lib', spalib_sources, include_directories : inc, install : true) +spalibs = static_library('spa-lib', + spalib_sources, + include_directories : inc, + install : true) diff --git a/spa/lib/video-raw.c b/spa/lib/video-raw.c index f359cc87b..8ccfd9a60 100644 --- a/spa/lib/video-raw.c +++ b/spa/lib/video-raw.c @@ -111,11 +111,11 @@ static const uint32_t format_values[] = { static const SpaPropRangeInfo format_range[] = { { "ENCODED,", "ENCODED", sizeof (uint32_t), &format_values[1] }, - { "S16LE", "S16LE", sizeof (uint32_t), &format_values[2] }, - { "S16BE", "S16BE", sizeof (uint32_t), &format_values[3] }, - { "U16LE", "U16LE", sizeof (uint32_t), &format_values[4] }, - { "U16BE", "U16BE", sizeof (uint32_t), &format_values[5] }, - { "S24_32LE", "S24_32LE", sizeof (uint32_t), &format_values[6] }, + { "I420", "I420", sizeof (uint32_t), &format_values[2] }, + { "YV12", "YV12", sizeof (uint32_t), &format_values[3] }, + { "YUY2", "YUY2", sizeof (uint32_t), &format_values[4] }, + { "UYVY", "UYVY", sizeof (uint32_t), &format_values[5] }, + { "AYUV", "AYUV", sizeof (uint32_t), &format_values[6] }, { "S24_32BE", "S24_32BE", sizeof (uint32_t), &format_values[7] }, { "U24_32LE", "U24_32LE", sizeof (uint32_t), &format_values[8] }, { "U24_32BE", "U24_32BE", sizeof (uint32_t), &format_values[9] }, @@ -509,7 +509,7 @@ spa_video_raw_format_parse (const SpaFormat *format, spa_video_raw_format_init (rawformat); props = &format->props; - if ((res = props->get_prop (props, SPA_PROP_ID_VIDEO_RAW_INFO, &value)) < 0) + if ((res = props->get_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_RAW_INFO), &value)) < 0) goto fallback; if (value.type != SPA_PROP_TYPE_POINTER || value.size != sizeof (SpaVideoRawInfo)) @@ -524,3 +524,37 @@ fallback: return res; } + +SpaResult +spa_video_raw_fill_default_info (SpaVideoRawInfo *info) +{ + if (info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + memcpy (info, &default_info, sizeof (SpaVideoRawInfo)); + + return SPA_RESULT_OK; +} + + +SpaResult +spa_video_raw_fill_prop_info (SpaPropInfo *info, + SpaPropIdVideo id, + size_t offset) +{ + unsigned int i; + + if (info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + for (i = 0; i < SPA_N_ELEMENTS (raw_format_prop_info); i++) { + if (raw_format_prop_info[i].id == id) { + memcpy (info, &raw_format_prop_info[i], sizeof (SpaPropInfo)); + info->offset = offset; + info->mask_offset = 0; + info->unset_mask = 0; + return SPA_RESULT_OK; + } + } + return SPA_RESULT_INVALID_PROPERTY_INDEX; +} diff --git a/spa/plugins/alsa/alsa.c b/spa/plugins/alsa/alsa.c index 805536e7a..06db5f750 100644 --- a/spa/plugins/alsa/alsa.c +++ b/spa/plugins/alsa/alsa.c @@ -40,6 +40,7 @@ spa_enum_handle_factory (const SpaHandleFactory **factory, default: return SPA_RESULT_ENUM_END; } - *(int*)state = index++; + *(int*)state = ++index; + return SPA_RESULT_OK; } diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c index f0b0c7ab4..121b54de6 100644 --- a/spa/plugins/audiomixer/audiomixer.c +++ b/spa/plugins/audiomixer/audiomixer.c @@ -324,7 +324,7 @@ spa_audiomixer_node_port_enum_formats (SpaHandle *handle, return SPA_RESULT_ENUM_END; } *format = &this->query_format.format; - *(int*)state = index++; + *(int*)state = ++index; return SPA_RESULT_OK; } diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index de08c4ed7..312f9e312 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -61,6 +61,24 @@ struct _V4l2Buffer { int dmafd; }; +typedef struct _V4l2Format V4l2Format; + +struct _V4l2Format { + SpaFormat fmt; + uint32_t unset_mask; + SpaVideoFormat format; + SpaRectangle size; + SpaFraction framerate; + SpaVideoInterlaceMode interlace_mode; + SpaVideoColorRange color_range; + SpaVideoColorMatrix color_matrix; + SpaVideoTransferFunction transfer_function; + SpaVideoColorPrimaries color_primaries; + SpaPropInfo infos[16]; + SpaPropRangeInfo ranges[16]; + SpaFraction framerates[16]; +}; + typedef struct { bool export_buf; bool have_buffers; @@ -69,11 +87,10 @@ typedef struct { struct v4l2_fmtdesc fmtdesc; bool next_frmsize; struct v4l2_frmsizeenum frmsize; - bool next_frmival; struct v4l2_frmivalenum frmival; void *cookie; - SpaVideoRawFormat raw_format[2]; + V4l2Format format[2]; SpaFormat *current_format; int fd; @@ -309,6 +326,33 @@ spa_v4l2_source_node_remove_port (SpaHandle *handle, return SPA_RESULT_NOT_IMPLEMENTED; } +static void +spa_v4l2_format_init (V4l2Format *f) +{ + f->fmt.props.n_prop_info = 3; + f->fmt.props.prop_info = f->infos; + f->fmt.props.set_prop = spa_props_generic_set_prop; + f->fmt.props.get_prop = spa_props_generic_get_prop; + + spa_video_raw_fill_prop_info (&f->infos[0], + SPA_PROP_ID_VIDEO_FORMAT, + offsetof (V4l2Format, format)); + f->infos[0].mask_offset = offsetof (V4l2Format, unset_mask); + f->infos[0].unset_mask = 1 << 0; + + spa_video_raw_fill_prop_info (&f->infos[1], + SPA_PROP_ID_VIDEO_SIZE, + offsetof (V4l2Format, size)); + f->infos[1].mask_offset = offsetof (V4l2Format, unset_mask); + f->infos[1].unset_mask = 1 << 1; + + spa_video_raw_fill_prop_info (&f->infos[2], + SPA_PROP_ID_VIDEO_FRAMERATE, + offsetof (V4l2Format, framerate)); + f->infos[2].mask_offset = offsetof (V4l2Format, unset_mask); + f->infos[2].unset_mask = 1 << 2; +} + static SpaResult spa_v4l2_source_node_port_enum_formats (SpaHandle *handle, uint32_t port_id, @@ -339,7 +383,7 @@ spa_v4l2_source_node_port_set_format (SpaHandle *handle, SpaV4l2Source *this = (SpaV4l2Source *) handle; SpaV4l2State *state; SpaResult res; - SpaFormat *f, *tf; + V4l2Format *f, *tf; size_t fs; if (handle == NULL || format == NULL) @@ -355,25 +399,22 @@ spa_v4l2_source_node_port_set_format (SpaHandle *handle, return SPA_RESULT_OK; } - if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { - if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { - if ((res = spa_video_raw_format_parse (format, &state->raw_format[0]) < 0)) - return res; + f = &state->format[0]; + tf = &state->format[1]; + fs = sizeof (V4l2Format); - f = &state->raw_format[0].format; - tf = &state->raw_format[1].format; - fs = sizeof (SpaVideoRawFormat); - } else - return SPA_RESULT_INVALID_MEDIA_TYPE; - } else - return SPA_RESULT_INVALID_MEDIA_TYPE; + spa_v4l2_format_init (f); + f->fmt.media_type = format->media_type; + f->fmt.media_subtype = format->media_subtype; + if ((res = spa_props_copy (&format->props, &f->fmt.props) < 0)) + return res; if (spa_v4l2_set_format (this, f, flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY) < 0) return SPA_RESULT_INVALID_MEDIA_TYPE; if (!(flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY)) { memcpy (tf, f, fs); - state->current_format = tf; + state->current_format = &tf->fmt; } return SPA_RESULT_OK; diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index dd77309d8..5eb7ede44 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -66,201 +66,157 @@ spa_v4l2_open (SpaV4l2Source *this) return 0; } -static SpaVideoFormat -fourcc_to_video_format (uint32_t fourcc) -{ - SpaVideoFormat format; - - switch (fourcc) { - case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ - format = SPA_VIDEO_FORMAT_GRAY8; - break; - case V4L2_PIX_FMT_Y16: - format = SPA_VIDEO_FORMAT_GRAY16_LE; - break; - case V4L2_PIX_FMT_Y16_BE: - format = SPA_VIDEO_FORMAT_GRAY16_BE; - break; - case V4L2_PIX_FMT_XRGB555: - case V4L2_PIX_FMT_RGB555: - format = SPA_VIDEO_FORMAT_RGB15; - break; - case V4L2_PIX_FMT_XRGB555X: - case V4L2_PIX_FMT_RGB555X: - format = SPA_VIDEO_FORMAT_BGR15; - break; - case V4L2_PIX_FMT_RGB565: - format = SPA_VIDEO_FORMAT_RGB16; - break; - case V4L2_PIX_FMT_RGB24: - format = SPA_VIDEO_FORMAT_RGB; - break; - case V4L2_PIX_FMT_BGR24: - format = SPA_VIDEO_FORMAT_BGR; - break; - case V4L2_PIX_FMT_XRGB32: - case V4L2_PIX_FMT_RGB32: - format = SPA_VIDEO_FORMAT_xRGB; - break; - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_BGR32: - format = SPA_VIDEO_FORMAT_BGRx; - break; - case V4L2_PIX_FMT_ABGR32: - format = SPA_VIDEO_FORMAT_BGRA; - break; - case V4L2_PIX_FMT_ARGB32: - format = SPA_VIDEO_FORMAT_ARGB; - break; - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV12M: - format = SPA_VIDEO_FORMAT_NV12; - break; - case V4L2_PIX_FMT_NV12MT: - format = SPA_VIDEO_FORMAT_NV12_64Z32; - break; - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV21M: - format = SPA_VIDEO_FORMAT_NV21; - break; - case V4L2_PIX_FMT_YVU410: - format = SPA_VIDEO_FORMAT_YVU9; - break; - case V4L2_PIX_FMT_YUV410: - format = SPA_VIDEO_FORMAT_YUV9; - break; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YUV420M: - format = SPA_VIDEO_FORMAT_I420; - break; - case V4L2_PIX_FMT_YUYV: - format = SPA_VIDEO_FORMAT_YUY2; - break; - case V4L2_PIX_FMT_YVU420: - format = SPA_VIDEO_FORMAT_YV12; - break; - case V4L2_PIX_FMT_UYVY: - format = SPA_VIDEO_FORMAT_UYVY; - break; - case V4L2_PIX_FMT_YUV411P: - format = SPA_VIDEO_FORMAT_Y41B; - break; - case V4L2_PIX_FMT_YUV422P: - format = SPA_VIDEO_FORMAT_Y42B; - break; - case V4L2_PIX_FMT_YVYU: - format = SPA_VIDEO_FORMAT_YVYU; - break; - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV16M: - format = SPA_VIDEO_FORMAT_NV16; - break; - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_NV61M: - format = SPA_VIDEO_FORMAT_NV61; - break; - case V4L2_PIX_FMT_NV24: - format = SPA_VIDEO_FORMAT_NV24; - break; - default: - format = SPA_VIDEO_FORMAT_UNKNOWN; - break; - } - return format; -} - -static uint32_t -video_format_to_fourcc (SpaVideoFormat format) -{ +typedef struct { uint32_t fourcc; + SpaVideoFormat format; + SpaMediaType media_type; + SpaMediaSubType media_subtype; +} FormatInfo; - switch (format) { - case SPA_VIDEO_FORMAT_I420: - fourcc = V4L2_PIX_FMT_YUV420; - break; - case SPA_VIDEO_FORMAT_YUY2: - fourcc = V4L2_PIX_FMT_YUYV; - break; - case SPA_VIDEO_FORMAT_UYVY: - fourcc = V4L2_PIX_FMT_UYVY; - break; - case SPA_VIDEO_FORMAT_YV12: - fourcc = V4L2_PIX_FMT_YVU420; - break; - case SPA_VIDEO_FORMAT_Y41B: - fourcc = V4L2_PIX_FMT_YUV411P; - break; - case SPA_VIDEO_FORMAT_Y42B: - fourcc = V4L2_PIX_FMT_YUV422P; - break; - case SPA_VIDEO_FORMAT_NV12: - fourcc = V4L2_PIX_FMT_NV12; - break; - case SPA_VIDEO_FORMAT_NV12_64Z32: - fourcc = V4L2_PIX_FMT_NV12MT; - break; - case SPA_VIDEO_FORMAT_NV21: - fourcc = V4L2_PIX_FMT_NV21; - break; - case SPA_VIDEO_FORMAT_NV16: - fourcc = V4L2_PIX_FMT_NV16; - break; - case SPA_VIDEO_FORMAT_NV61: - fourcc = V4L2_PIX_FMT_NV61; - break; - case SPA_VIDEO_FORMAT_NV24: - fourcc = V4L2_PIX_FMT_NV24; - break; - case SPA_VIDEO_FORMAT_YVYU: - fourcc = V4L2_PIX_FMT_YVYU; - break; - case SPA_VIDEO_FORMAT_RGB15: - fourcc = V4L2_PIX_FMT_RGB555; - break; - case SPA_VIDEO_FORMAT_RGB16: - fourcc = V4L2_PIX_FMT_RGB565; - break; - case SPA_VIDEO_FORMAT_RGB: - fourcc = V4L2_PIX_FMT_RGB24; - break; - case SPA_VIDEO_FORMAT_BGR: - fourcc = V4L2_PIX_FMT_BGR24; - break; - case SPA_VIDEO_FORMAT_xRGB: - fourcc = V4L2_PIX_FMT_RGB32; - break; - case SPA_VIDEO_FORMAT_ARGB: - fourcc = V4L2_PIX_FMT_RGB32; - break; - case SPA_VIDEO_FORMAT_BGRx: - fourcc = V4L2_PIX_FMT_BGR32; - break; - case SPA_VIDEO_FORMAT_BGRA: - fourcc = V4L2_PIX_FMT_BGR32; - break; - case SPA_VIDEO_FORMAT_GRAY8: - fourcc = V4L2_PIX_FMT_GREY; - break; - case SPA_VIDEO_FORMAT_GRAY16_LE: - fourcc = V4L2_PIX_FMT_Y16; - break; - case SPA_VIDEO_FORMAT_GRAY16_BE: - fourcc = V4L2_PIX_FMT_Y16_BE; - break; - default: - fourcc = 0; - break; +static const FormatInfo format_info[] = +{ + /* RGB formats */ + { V4L2_PIX_FMT_RGB332, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_ARGB555, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_XRGB555, SPA_VIDEO_FORMAT_RGB15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_ARGB555X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_XRGB555X, SPA_VIDEO_FORMAT_BGR15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_RGB565, SPA_VIDEO_FORMAT_RGB16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_RGB565X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_BGR666, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_BGR24, SPA_VIDEO_FORMAT_BGR, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_RGB24, SPA_VIDEO_FORMAT_RGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_ABGR32, SPA_VIDEO_FORMAT_BGRA, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_XBGR32, SPA_VIDEO_FORMAT_BGRx, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_ARGB32, SPA_VIDEO_FORMAT_ARGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_XRGB32, SPA_VIDEO_FORMAT_xRGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + + /* Deprecated Packed RGB Image Formats (alpha ambiguity) */ + { V4L2_PIX_FMT_RGB444, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_RGB555, SPA_VIDEO_FORMAT_RGB15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_RGB555X, SPA_VIDEO_FORMAT_BGR15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_BGR32, SPA_VIDEO_FORMAT_BGRx, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_RGB32, SPA_VIDEO_FORMAT_xRGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + + /* Grey formats */ + { V4L2_PIX_FMT_GREY, SPA_VIDEO_FORMAT_GRAY8, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y4, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y6, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y10, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y12, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y16, SPA_VIDEO_FORMAT_GRAY16_LE, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y16_BE, SPA_VIDEO_FORMAT_GRAY16_BE, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y10BPACK, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + + /* Palette formats */ + { V4L2_PIX_FMT_PAL8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + + /* Chrominance formats */ + { V4L2_PIX_FMT_UV8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + + /* Luminance+Chrominance formats */ + { V4L2_PIX_FMT_YVU410, SPA_VIDEO_FORMAT_YVU9, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YVU420, SPA_VIDEO_FORMAT_YV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YVU420M, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUYV, SPA_VIDEO_FORMAT_YUY2, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YYUV, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YVYU, SPA_VIDEO_FORMAT_YVYU, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_UYVY, SPA_VIDEO_FORMAT_UYVY, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_VYUY, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV422P, SPA_VIDEO_FORMAT_Y42B, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV411P, SPA_VIDEO_FORMAT_Y41B, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_Y41P, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV444, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV555, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV565, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV32, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV410, SPA_VIDEO_FORMAT_YUV9, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV420, SPA_VIDEO_FORMAT_I420, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_YUV420M, SPA_VIDEO_FORMAT_I420, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_HI240, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_HM12, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_M420, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + + /* two planes -- one Y, one Cr + Cb interleaved */ + { V4L2_PIX_FMT_NV12, SPA_VIDEO_FORMAT_NV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV12M, SPA_VIDEO_FORMAT_NV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV12MT, SPA_VIDEO_FORMAT_NV12_64Z32, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV12MT_16X16, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV21, SPA_VIDEO_FORMAT_NV21, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV21M, SPA_VIDEO_FORMAT_NV21, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV16, SPA_VIDEO_FORMAT_NV16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV16M, SPA_VIDEO_FORMAT_NV16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV61, SPA_VIDEO_FORMAT_NV61, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV61M, SPA_VIDEO_FORMAT_NV61, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV24, SPA_VIDEO_FORMAT_NV24, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + { V4L2_PIX_FMT_NV42, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + + /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ + { V4L2_PIX_FMT_SBGGR8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER }, + { V4L2_PIX_FMT_SGBRG8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER }, + { V4L2_PIX_FMT_SGRBG8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER }, + { V4L2_PIX_FMT_SRGGB8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER }, + + /* compressed formats */ + {V4L2_PIX_FMT_MJPEG, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MJPG }, + {V4L2_PIX_FMT_JPEG, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_IMAGE, SPA_MEDIA_SUBTYPE_JPEG }, + {V4L2_PIX_FMT_PJPG, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + {V4L2_PIX_FMT_DV, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_DV }, + {V4L2_PIX_FMT_MPEG, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEGTS }, + {V4L2_PIX_FMT_H264, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H264 }, + {V4L2_PIX_FMT_H264_NO_SC, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H264 }, + {V4L2_PIX_FMT_H264_MVC, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H264 }, + {V4L2_PIX_FMT_H263, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H263 }, + {V4L2_PIX_FMT_MPEG1, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEG1 }, + {V4L2_PIX_FMT_MPEG2, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEG2 }, + {V4L2_PIX_FMT_MPEG4, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEG4 }, + {V4L2_PIX_FMT_XVID, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_XVID }, + {V4L2_PIX_FMT_VC1_ANNEX_G, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_VC1 }, + {V4L2_PIX_FMT_VC1_ANNEX_L, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_VC1 }, + {V4L2_PIX_FMT_VP8, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_VP8 }, + + /* Vendor-specific formats */ + {V4L2_PIX_FMT_WNVA, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + {V4L2_PIX_FMT_SN9C10X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + {V4L2_PIX_FMT_PWC1, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, + {V4L2_PIX_FMT_PWC2, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW }, +}; + +static const FormatInfo * +fourcc_to_format_info (uint32_t fourcc) +{ + int i; + + for (i = 0; i < SPA_N_ELEMENTS (format_info); i++) { + if (format_info[i].fourcc == fourcc) + return &format_info[i]; } - return fourcc; + return NULL; } +#if 0 +static const FormatInfo * +video_format_to_format_info (SpaVideoFormat format) +{ + int i; + + for (i = 0; i < SPA_N_ELEMENTS (format_info); i++) { + if (format_info[i].format == format) + return &format_info[i]; + } + return NULL; +} +#endif + #define FOURCC_ARGS(f) (f)&0x7f,((f)>>8)&0x7f,((f)>>16)&0x7f,((f)>>24)&0x7f static SpaResult spa_v4l2_enum_format (SpaV4l2Source *this, SpaFormat **format, void **cookie) { SpaV4l2State *state = &this->state[0]; - int res; + int res, i, pi; + V4l2Format *fmt; + const FormatInfo *info; if (spa_v4l2_open (this) < 0) return SPA_RESULT_ERROR; @@ -272,13 +228,9 @@ spa_v4l2_enum_format (SpaV4l2Source *this, SpaFormat **format, void **cookie) state->fmtdesc.index = 0; state->fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; state->next_fmtdesc = true; - CLEAR (state->frmsize); state->next_frmsize = true; - CLEAR (state->frmival); - state->next_frmival = true; - *cookie = state; } @@ -295,6 +247,13 @@ again: state->frmsize.pixel_format = state->fmtdesc.pixelformat; state->next_frmsize = true; } + + if (!(info = fourcc_to_format_info (state->fmtdesc.pixelformat))) { + state->fmtdesc.index++; + state->next_fmtdesc = true; + goto again; + } + if (state->next_frmsize) { if ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMESIZES, &state->frmsize)) < 0) { if (errno == EINVAL) { @@ -312,68 +271,112 @@ again: state->frmival.pixel_format = state->frmsize.pixel_format; state->frmival.width = state->frmsize.discrete.width; state->frmival.height = state->frmsize.discrete.height; - state->next_frmival = true; } } - if (state->next_frmival) { + + fmt = &state->format[0]; + fmt->fmt.media_type = info->media_type; + fmt->fmt.media_subtype = info->media_subtype; + fmt->fmt.props.prop_info = fmt->infos; + fmt->fmt.props.n_prop_info = pi = 0; + fmt->fmt.props.set_prop = spa_props_generic_set_prop; + fmt->fmt.props.get_prop = spa_props_generic_get_prop; + fmt->unset_mask = 0; + + if (info->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { + spa_video_raw_fill_prop_info (&fmt->infos[pi], + SPA_PROP_ID_VIDEO_FORMAT, + offsetof (V4l2Format, format)); + fmt->infos[pi].mask_offset = offsetof (V4l2Format, unset_mask); + fmt->format = info->format; + pi = ++fmt->fmt.props.n_prop_info; + } + + spa_video_raw_fill_prop_info (&fmt->infos[pi], + SPA_PROP_ID_VIDEO_SIZE, + offsetof (V4l2Format, size)); + fmt->infos[pi].mask_offset = offsetof (V4l2Format, unset_mask); + fmt->size.width = state->frmsize.discrete.width; + fmt->size.height = state->frmsize.discrete.height; + pi = ++fmt->fmt.props.n_prop_info; + + spa_video_raw_fill_prop_info (&fmt->infos[pi], + SPA_PROP_ID_VIDEO_FRAMERATE, + offsetof (V4l2Format, framerate)); + fmt->infos[pi].mask_offset = offsetof (V4l2Format, unset_mask); + fmt->infos[pi].range_type = SPA_PROP_RANGE_TYPE_ENUM; + fmt->infos[pi].range_values = fmt->ranges; + fmt->infos[pi].n_range_values = 0; + i = state->frmival.index = 0; + + while (true) { if ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMEINTERVALS, &state->frmival)) < 0) { if (errno == EINVAL) { state->frmsize.index++; state->next_frmsize = true; - goto again; + break; } perror ("VIDIOC_ENUM_FRAMEINTERVALS"); return SPA_RESULT_ENUM_END; } - state->frmival.index++; + + fmt->ranges[i].name = NULL; + fmt->ranges[i].description = NULL; + fmt->ranges[i].size = sizeof (SpaFraction); + fmt->framerates[i].num = state->frmival.discrete.numerator; + fmt->framerates[i].denom = state->frmival.discrete.denominator; + fmt->ranges[i].value = &fmt->framerates[i]; + + i = ++state->frmival.index; } + fmt->infos[pi].n_range_values = i; + fmt->infos[pi].unset_mask = 1 << i; + fmt->unset_mask |= fmt->infos[pi].unset_mask; + pi = ++fmt->fmt.props.n_prop_info; - spa_video_raw_format_init (&state->raw_format[0]); - state->raw_format[0].info.format = fourcc_to_video_format (state->fmtdesc.pixelformat); - state->raw_format[0].info.size.width = state->frmsize.discrete.width; - state->raw_format[0].info.size.height = state->frmsize.discrete.height; - state->raw_format[0].info.framerate.num = state->frmival.discrete.numerator; - state->raw_format[0].info.framerate.denom = state->frmival.discrete.denominator; - state->raw_format[0].unset_mask &= ~((1<<0)|(1<<1)|(1<<2)); - - *format = &state->raw_format[0].format; + *format = &state->format[0].fmt; return SPA_RESULT_OK; } static int -spa_v4l2_set_format (SpaV4l2Source *this, SpaFormat *format, bool try_only) +spa_v4l2_set_format (SpaV4l2Source *this, V4l2Format *f, bool try_only) { SpaV4l2State *state = &this->state[0]; int cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT; struct v4l2_format reqfmt, fmt; struct v4l2_streamparm streamparm; + const FormatInfo *info = NULL; + int i; CLEAR (fmt); CLEAR (streamparm); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { - if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { - SpaVideoRawFormat *f = (SpaVideoRawFormat *) format; - - fmt.fmt.pix.pixelformat = video_format_to_fourcc (f->info.format); - fmt.fmt.pix.width = f->info.size.width; - fmt.fmt.pix.height = f->info.size.height; - fmt.fmt.pix.field = V4L2_FIELD_ANY; - streamparm.parm.capture.timeperframe.numerator = f->info.framerate.denom; - streamparm.parm.capture.timeperframe.denominator = f->info.framerate.num; - - fprintf (stderr, "set %08x %dx%d %d/%d\n", fmt.fmt.pix.pixelformat, - fmt.fmt.pix.width, fmt.fmt.pix.height, f->info.framerate.denom, - f->info.framerate.num); - - } else - return -1; - } else + for (i = 0; i < SPA_N_ELEMENTS (format_info); i++) { + if (format_info[i].media_type == f->fmt.media_type && + format_info[i].media_subtype == f->fmt.media_subtype && + format_info[i].format == f->format) { + info = &format_info[i]; + break; + } + } + if (info == NULL) return -1; + fmt.fmt.pix.pixelformat = info->fourcc; + fmt.fmt.pix.field = V4L2_FIELD_ANY; + fmt.fmt.pix.width = f->size.width; + fmt.fmt.pix.height = f->size.height; + streamparm.parm.capture.timeperframe.numerator = f->framerate.denom; + streamparm.parm.capture.timeperframe.denominator = f->framerate.num; + + fprintf (stderr, "set %08x %dx%d %d/%d\n", fmt.fmt.pix.pixelformat, + fmt.fmt.pix.width, fmt.fmt.pix.height, + streamparm.parm.capture.timeperframe.denominator, + streamparm.parm.capture.timeperframe.numerator); + reqfmt = fmt; if (spa_v4l2_open (this) < 0) @@ -531,6 +534,7 @@ spa_v4l2_import_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_bu b->source = this; b->buffer.refcount = 0; b->buffer.notify = v4l2_buffer_free; + b->buffer.id = i; b->buffer.size = buffers[i]->size; b->buffer.n_metas = buffers[i]->n_metas; b->buffer.metas = buffers[i]->metas; @@ -600,6 +604,7 @@ mmap_init (SpaV4l2Source *this) b->source = this; b->buffer.refcount = 0; b->buffer.notify = v4l2_buffer_free; + b->buffer.id = i; b->buffer.size = buf.length; b->buffer.n_metas = 1; b->buffer.metas = b->metas; diff --git a/spa/tests/test-v4l2.c b/spa/tests/test-v4l2.c index 14d758ca8..66989deea 100644 --- a/spa/tests/test-v4l2.c +++ b/spa/tests/test-v4l2.c @@ -269,50 +269,53 @@ alloc_buffers (AppData *data) } #endif +typedef struct { + SpaFormat fmt; + SpaPropInfo infos[3]; + SpaVideoFormat format; + SpaRectangle size; + SpaFraction framerate; +} VideoFormat; + static SpaResult negotiate_formats (AppData *data) { SpaResult res; - SpaFormat *format; - SpaProps *props; - uint32_t val; - SpaFraction frac; - SpaPropValue value; const SpaPortInfo *info; - SpaRectangle size; + VideoFormat f; + +#if 0 void *state = NULL; if ((res = data->source_node->port_enum_formats (data->source, 0, &format, NULL, &state)) < 0) return res; +#else + f.fmt.media_type = SPA_MEDIA_TYPE_VIDEO; + f.fmt.media_subtype = SPA_MEDIA_SUBTYPE_RAW; + f.fmt.props.n_prop_info = 3; + f.fmt.props.prop_info = f.infos; + f.fmt.props.set_prop = spa_props_generic_set_prop; + f.fmt.props.get_prop = spa_props_generic_get_prop; - props = &format->props; + spa_video_raw_fill_prop_info (&f.infos[0], + SPA_PROP_ID_VIDEO_FORMAT, + offsetof (VideoFormat, format)); + f.format = SPA_VIDEO_FORMAT_YUY2; - value.type = SPA_PROP_TYPE_UINT32; - value.size = sizeof (uint32_t); - value.value = &val; + spa_video_raw_fill_prop_info (&f.infos[1], + SPA_PROP_ID_VIDEO_SIZE, + offsetof (VideoFormat, size)); + f.size.width = 320; + f.size.height = 240; - val = SPA_VIDEO_FORMAT_YUY2; - if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FORMAT), &value)) < 0) - return res; + spa_video_raw_fill_prop_info (&f.infos[2], + SPA_PROP_ID_VIDEO_FRAMERATE, + offsetof (VideoFormat, framerate)); + f.framerate.num = 25; + f.framerate.denom = 1; +#endif - value.type = SPA_PROP_TYPE_RECTANGLE; - value.size = sizeof (SpaRectangle); - value.value = &size; - size.width = 320; - size.height = 240; - if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_SIZE), &value)) < 0) - return res; - - value.type = SPA_PROP_TYPE_FRACTION; - value.size = sizeof (SpaFraction); - value.value = &frac; - frac.num = 25; - frac.denom = 1; - - if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FRAMERATE), &value)) < 0) - return res; - - if ((res = data->source_node->port_set_format (data->source, 0, false, format)) < 0) + if ((res = data->source_node->port_set_format (data->source, 0, false, &f.fmt)) < 0) return res; if ((res = data->source_node->port_get_info (data->source, 0, &info)) < 0) diff --git a/spa/tools/spa-inspect.c b/spa/tools/spa-inspect.c index c633bb7d3..c83879927 100644 --- a/spa/tools/spa-inspect.c +++ b/spa/tools/spa-inspect.c @@ -38,6 +38,8 @@ struct media_subtype_name { } media_subtype_names[] = { { "unknown" }, { "raw" }, + { "h264" }, + { "mjpg" }, }; struct prop_type_name { @@ -64,8 +66,25 @@ struct prop_type_name { }; static void -print_value (SpaPropType type, int size, const void *value) +print_value (const SpaPropInfo *info, int size, const void *value) { + SpaPropType type = info->type; + bool enum_string = false; + + if (info->range_type == SPA_PROP_RANGE_TYPE_ENUM) { + int i; + + for (i = 0; i < info->n_range_values; i++) { + if (memcmp (info->range_values[i].value, value, size) == 0) { + if (info->range_values[i].name) { + type = SPA_PROP_TYPE_STRING; + value = info->range_values[i].name; + enum_string = true; + } + } + } + } + switch (type) { case SPA_PROP_TYPE_INVALID: printf ("invalid"); @@ -104,7 +123,10 @@ print_value (SpaPropType type, int size, const void *value) printf ("%g", *(double *)value); break; case SPA_PROP_TYPE_STRING: - printf ("\"%s\"", (char *)value); + if (enum_string) + printf ("%s", (char *)value); + else + printf ("\"%s\"", (char *)value); break; case SPA_PROP_TYPE_RECTANGLE: { @@ -157,7 +179,7 @@ print_props (const SpaProps *props, int print_ranges) printf ("Default: "); if (info->default_value) - print_value (info->type, info->default_size, info->default_value); + print_value (info, info->default_size, info->default_value); else printf ("None"); @@ -165,7 +187,7 @@ print_props (const SpaProps *props, int print_ranges) printf (". Current: "); if (res == SPA_RESULT_OK) - print_value (info->type, value.size, value.value); + print_value (info, value.size, value.value); else if (res == SPA_RESULT_PROPERTY_UNSET) printf ("Unset"); else @@ -199,7 +221,7 @@ print_props (const SpaProps *props, int print_ranges) for (j = 0; j < info->n_range_values; j++) { const SpaPropRangeInfo *rinfo = &info->range_values[j]; printf ("%-23.23s ", ""); - print_value (info->type, rinfo->size, rinfo->value); + print_value (info, rinfo->size, rinfo->value); printf ("\t: %-12s - %s \n", rinfo->name, rinfo->description); } } @@ -219,7 +241,7 @@ print_format (const SpaFormat *format) const SpaProps *props = &format->props; int i; - printf (" %-10s %s/%s\n", "", media_type_names[format->media_type].name, + printf ("%-6s %s/%s\n", "", media_type_names[format->media_type].name, media_subtype_names[format->media_subtype].name); for (i = 0; i < props->n_prop_info; i++) { @@ -234,9 +256,34 @@ print_format (const SpaFormat *format) printf (" %20s : (%s) ", info->name, prop_type_names[info->type].name); if (res == SPA_RESULT_OK) { - print_value (info->type, value.size, value.value); + print_value (info, value.size, value.value); } else if (res == SPA_RESULT_PROPERTY_UNSET) { - printf ("Unset"); + int j; + const char *ssep, *esep, *sep; + + switch (info->range_type) { + case SPA_PROP_RANGE_TYPE_MIN_MAX: + case SPA_PROP_RANGE_TYPE_STEP: + ssep = "[ "; + sep = ", "; + esep = " ]"; + break; + default: + case SPA_PROP_RANGE_TYPE_ENUM: + case SPA_PROP_RANGE_TYPE_FLAGS: + ssep = "{ "; + sep = ", "; + esep = " }"; + break; + } + + printf (ssep); + for (j = 0; j < info->n_range_values; j++) { + const SpaPropRangeInfo *rinfo = &info->range_values[j]; + print_value (info, rinfo->size, rinfo->value); + printf ("%s", j + 1 < info->n_range_values ? sep : ""); + } + printf (esep); } else { printf ("*Error*"); }