Rework transport protocol

Remove the old PinosBuffer object and replace it with SpaControl, this
communication protocol is designed to make it possible to implement
remote nodes and so it is moved to Spa.
Move SpaBuffer into to API
Work on easier API to make formats, implement enumeration and support
for all formats in v4l2.
Improve format output in -inspect
This commit is contained in:
Wim Taymans 2016-07-28 21:19:20 +02:00
parent b795fb851f
commit 4cb90f3b86
37 changed files with 2658 additions and 1032 deletions

View file

@ -370,3 +370,95 @@ Types:
1 = keyframe+full headers 1 = keyframe+full headers
<PTS> : the timestamp when the refresh should be, <PTS> : the timestamp when the refresh should be,
0 for as soon as possible 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

View file

@ -40,8 +40,8 @@ AM_CFLAGS = $(GLIB_CFLAGS) $(GST_CFLAGS)
AM_CXXFLAGS = $(AM_CFLAGS) AM_CXXFLAGS = $(AM_CFLAGS)
SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS) #$(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) #$(top_srcdir)/spa/build/lib/libspa-lib.so AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS) -L$(top_srcdir)/spa/build/lib/ -lspa-lib
AM_LDFLAGS = $(NODELETE_LDFLAGS) AM_LDFLAGS = $(NODELETE_LDFLAGS)
FOREIGN_CFLAGS = -w FOREIGN_CFLAGS = -w
@ -161,9 +161,7 @@ pinos_monitor_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Client library # # Client library #
################################### ###################################
pinosgstsource = gst/gstpinospay.h gst/gstpinospay.c \ pinosgstsource = gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c
gst/gstpinosdepay.h gst/gstpinosdepay.c \
gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c
pinosinclude_HEADERS = \ pinosinclude_HEADERS = \
client/buffer.h \ 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_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
libpinoscore_@PINOS_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version libpinoscore_@PINOS_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
libpinoscore_@PINOS_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LTLIBICONV) \ libpinoscore_@PINOS_MAJORMINOR@_la_LIBADD = $(LIBLTDL) $(LTLIBICONV) \
libpinos-@PINOS_MAJORMINOR@.la libpinos-@PINOS_MAJORMINOR@.la $(AM_LIBADD)
################################### ###################################
# GStreamer Plugin # # GStreamer Plugin #
@ -241,19 +239,19 @@ plugin_LTLIBRARIES = libgstpinos.la
libgstpinos_la_SOURCES = \ libgstpinos_la_SOURCES = \
gst/gstburstcache.c \ gst/gstburstcache.c \
gst/gstpinos.c \ gst/gstpinos.c \
gst/gstpinossocketsink.c \
gst/gstpinosportsink.c \
gst/gstpinosportsrc.c \
gst/gstpinospay.c \
gst/gstpinosdepay.c \
gst/gstpinosdeviceprovider.c \ gst/gstpinosdeviceprovider.c \
gst/gstpinossrc.c \ gst/gstpinossrc.c \
gst/gstpinossink.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_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 \ 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) libgstpinos_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
noinst_HEADERS = gst/gstburstcache.h gst/gstpinossrc.h \ noinst_HEADERS = gst/gstburstcache.h gst/gstpinossrc.h \

View file

@ -21,6 +21,7 @@
#include <gio/gio.h> #include <gio/gio.h>
#if 0
#include "pinos/client/properties.h" #include "pinos/client/properties.h"
#include "pinos/client/context.h" #include "pinos/client/context.h"
#include "pinos/client/buffer.h" #include "pinos/client/buffer.h"
@ -1032,3 +1033,4 @@ pinos_buffer_builder_add_refresh_request (PinosBufferBuilder *builder,
return TRUE; return TRUE;
} }
#endif

View file

@ -26,6 +26,7 @@
G_BEGIN_DECLS G_BEGIN_DECLS
typedef struct _PinosBuffer PinosBuffer; typedef struct _PinosBuffer PinosBuffer;
#if 0
typedef struct _PinosBufferIter PinosBufferIter; typedef struct _PinosBufferIter PinosBufferIter;
typedef struct _PinosBufferBuilder PinosBufferBuilder; typedef struct _PinosBufferBuilder PinosBufferBuilder;
@ -329,4 +330,5 @@ gboolean pinos_buffer_builder_add_refresh_request (PinosBufferBuilder
PinosPacketRefreshRequest *payload); PinosPacketRefreshRequest *payload);
#endif
#endif /* __PINOS_BUFFER_H__ */ #endif /* __PINOS_BUFFER_H__ */

View file

@ -26,6 +26,7 @@
#include "pinos/client/pinos.h" #include "pinos/client/pinos.h"
#include "pinos/client/private.h" #include "pinos/client/private.h"
#if 0
gboolean gboolean
pinos_io_read_buffer (int fd, pinos_io_read_buffer (int fd,
PinosBuffer *buffer, PinosBuffer *buffer,
@ -177,3 +178,4 @@ send_error:
return FALSE; return FALSE;
} }
} }
#endif

View file

@ -57,6 +57,7 @@ GDBusProxy * pinos_subscribe_get_proxy_finish (PinosSubscribe *subsc
GError **error); GError **error);
#if 0
typedef struct { typedef struct {
guint32 version; guint32 version;
guint32 flags; guint32 flags;
@ -92,3 +93,4 @@ gboolean pinos_io_read_buffer (int fd,
gboolean pinos_io_write_buffer (int fd, gboolean pinos_io_write_buffer (int fd,
PinosBuffer *buffer, PinosBuffer *buffer,
GError **error); GError **error);
#endif

View file

@ -31,10 +31,37 @@
#include "pinos/client/enumtypes.h" #include "pinos/client/enumtypes.h"
#include "pinos/client/private.h" #include "pinos/client/private.h"
#include "spa/include/spa/control.h"
#define MAX_BUFFER_SIZE 1024 #define MAX_BUFFER_SIZE 1024
#define MAX_FDS 16 #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 struct _PinosStreamPrivate
{ {
PinosContext *context; PinosContext *context;
@ -61,15 +88,18 @@ struct _PinosStreamPrivate
GSource *socket_source; GSource *socket_source;
int fd; int fd;
PinosBuffer *buffer; SpaBuffer *buffer;
PinosBuffer recv_buffer;
SpaControl *control;
SpaControl recv_control;
guint8 recv_data[MAX_BUFFER_SIZE]; guint8 recv_data[MAX_BUFFER_SIZE];
int recv_fds[MAX_FDS]; int recv_fds[MAX_FDS];
guint8 send_data[MAX_BUFFER_SIZE]; guint8 send_data[MAX_BUFFER_SIZE];
int send_fds[MAX_FDS]; int send_fds[MAX_FDS];
GHashTable *mem_ids; GArray *mem_ids;
GArray *buffer_ids;
}; };
#define PINOS_STREAM_GET_PRIVATE(obj) \ #define PINOS_STREAM_GET_PRIVATE(obj) \
@ -260,9 +290,6 @@ pinos_stream_finalize (GObject * object)
g_bytes_unref (priv->format); g_bytes_unref (priv->format);
g_free (priv->path); g_free (priv->path);
if (priv->possible_formats)
g_bytes_unref (priv->possible_formats);
g_clear_error (&priv->error); g_clear_error (&priv->error);
if (priv->properties) if (priv->properties)
@ -397,6 +424,10 @@ pinos_stream_init (PinosStream * stream)
g_debug ("new stream %p", stream); g_debug ("new stream %p", stream);
priv->state = PINOS_STREAM_STATE_UNCONNECTED; 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; 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 static gboolean
parse_buffer (PinosStream *stream, parse_control (PinosStream *stream,
PinosBuffer *pbuf) SpaControl *ctrl)
{ {
PinosBufferIter it; SpaControlIter it;
PinosStreamPrivate *priv = stream->priv; PinosStreamPrivate *priv = stream->priv;
pinos_buffer_iter_init (&it, pbuf); spa_control_iter_init (&it, ctrl);
while (pinos_buffer_iter_next (&it)) { while (spa_control_iter_next (&it) == SPA_RESULT_OK) {
PinosPacketType type = pinos_buffer_iter_get_type (&it); SpaControlCmd cmd = spa_control_iter_get_cmd (&it);
switch (type) { switch (cmd) {
case PINOS_PACKET_TYPE_ADD_MEM: case SPA_CONTROL_CMD_NODE_UPDATE:
{ case SPA_CONTROL_CMD_PORT_UPDATE:
PinosPacketAddMem p; case SPA_CONTROL_CMD_PORT_REMOVED:
int fd; case SPA_CONTROL_CMD_START_CONFIGURE:
case SPA_CONTROL_CMD_PORT_STATUS_CHANGE:
if (!pinos_buffer_iter_parse_add_mem (&it, &p)) case SPA_CONTROL_CMD_START_ALLOC:
break; case SPA_CONTROL_CMD_NEED_INPUT:
case SPA_CONTROL_CMD_HAVE_OUTPUT:
fd = pinos_buffer_get_fd (pbuf, p.fd_index); g_warning ("got unexpected control %d", cmd);
if (fd == -1)
break;
// g_hash_table_insert (priv->mem_ids, GINT_TO_POINTER (p.id), NULL);
break; break;
}
case PINOS_PACKET_TYPE_REMOVE_MEM:
{
PinosPacketRemoveMem p;
if (!pinos_buffer_iter_parse_remove_mem (&it, &p)) case SPA_CONTROL_CMD_ADD_PORT:
break; case SPA_CONTROL_CMD_REMOVE_PORT:
g_warning ("add/remove port not supported");
// g_hash_table_remove (priv->mem_ids, GINT_TO_POINTER (p.id));
break; 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; break;
if (priv->format) if (priv->format)
g_bytes_unref (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"); g_object_notify (G_OBJECT (stream), "format");
break; 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); stream_set_state (stream, PINOS_STREAM_STATE_STREAMING, NULL);
break; 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); stream_set_state (stream, PINOS_STREAM_STATE_READY, NULL);
break; 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; 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; break;
} }
default: case SPA_CONTROL_CMD_ADD_BUFFER:
g_warning ("unhandled packet %d", type); {
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; break;
} }
} }
pinos_buffer_iter_end (&it); spa_control_iter_end (&it);
return TRUE; return TRUE;
} }
@ -582,28 +751,41 @@ on_socket_condition (GSocket *socket,
switch (condition) { switch (condition) {
case G_IO_IN: case G_IO_IN:
{ {
PinosBuffer *buffer = &priv->recv_buffer; SpaControl *control = &priv->recv_control;
GError *error = NULL; guint i;
if (!pinos_io_read_buffer (priv->fd, if (spa_control_read (control,
buffer, priv->fd,
priv->recv_data, priv->recv_data,
MAX_BUFFER_SIZE, MAX_BUFFER_SIZE,
priv->recv_fds, priv->recv_fds,
MAX_FDS, MAX_FDS) < 0) {
&error)) { g_warning ("stream %p: failed to read buffer", stream);
g_warning ("stream %p: failed to read buffer: %s", stream, error->message);
g_clear_error (&error);
return TRUE; return TRUE;
} }
parse_buffer (stream, buffer); parse_control (stream, control);
priv->buffer = buffer; if (priv->buffer) {
g_signal_emit (stream, signals[SIGNAL_NEW_BUFFER], 0, NULL); g_signal_emit (stream, signals[SIGNAL_NEW_BUFFER], 0, NULL);
priv->buffer = NULL; priv->buffer = NULL;
send_need_input (stream, 0);
g_assert (pinos_buffer_unref (buffer) == FALSE); }
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; break;
} }
@ -851,29 +1033,39 @@ pinos_stream_connect (PinosStream *stream,
return TRUE; 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 static gboolean
do_start (PinosStream *stream) do_start (PinosStream *stream)
{ {
PinosStreamPrivate *priv = stream->priv; PinosStreamPrivate *priv = stream->priv;
PinosBufferBuilder builder; SpaControlBuilder builder;
PinosPacketFormatChange fc; SpaControlCmdPortUpdate pu;
PinosBuffer pbuf; SpaControl control;
GError *error = NULL;
handle_socket (stream, priv->fd); handle_socket (stream, priv->fd);
pinos_stream_buffer_builder_init (stream, &builder); control_builder_init (stream, &builder);
fc.port = 0; pu.port = 0;
fc.id = 0; pu.change_mask = 0;
fc.format = priv->format ? g_bytes_get_data (priv->format, NULL) : "ANY";
pinos_buffer_builder_add_format_change (&builder, &fc); spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_PORT_UPDATE, &pu);
pinos_buffer_builder_add_empty (&builder, PINOS_PACKET_TYPE_START); spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_START_CONFIGURE, NULL);
pinos_buffer_builder_end (&builder, &pbuf); 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); g_object_unref (stream);
return FALSE; return FALSE;
@ -923,10 +1115,10 @@ pinos_stream_start (PinosStream *stream,
static gboolean static gboolean
do_stop (PinosStream *stream) do_stop (PinosStream *stream)
{ {
PinosBufferBuilder builder; SpaControlBuilder builder;
pinos_stream_buffer_builder_init (stream, &builder); control_builder_init (stream, &builder);
pinos_buffer_builder_add_empty (&builder, PINOS_PACKET_TYPE_STOP); spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_STOP, NULL);
g_object_unref (stream); g_object_unref (stream);
return FALSE; return FALSE;
@ -1048,9 +1240,9 @@ pinos_stream_disconnect (PinosStream *stream)
* Get the current buffer from @stream. This function should be called from * Get the current buffer from @stream. This function should be called from
* the new-buffer signal callback. * 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) pinos_stream_peek_buffer (PinosStream *stream)
{ {
PinosStreamPrivate *priv; PinosStreamPrivate *priv;
@ -1058,37 +1250,13 @@ pinos_stream_peek_buffer (PinosStream *stream)
g_return_val_if_fail (PINOS_IS_STREAM (stream), NULL); g_return_val_if_fail (PINOS_IS_STREAM (stream), NULL);
priv = stream->priv; priv = stream->priv;
return priv->buffer; return spa_buffer_ref (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);
} }
/** /**
* pinos_stream_send_buffer: * pinos_stream_send_buffer:
* @stream: a #PinosStream * @stream: a #PinosStream
* @buffer: a #PinosBuffer * @buffer: a #SpaBuffer
* *
* Send a buffer to @stream. * Send a buffer to @stream.
* *
@ -1102,20 +1270,17 @@ pinos_stream_buffer_builder_init (PinosStream *stream, PinosBufferBuilder *buil
*/ */
gboolean gboolean
pinos_stream_send_buffer (PinosStream *stream, 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 (PINOS_IS_STREAM (stream), FALSE);
g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (buffer != NULL, FALSE);
priv = stream->priv; #if 0
if (!spa_control_write (priv->fd, buffer, &error)) {
if (!pinos_io_write_buffer (priv->fd, buffer, &error)) {
g_warning ("stream %p: failed to write buffer: %s", stream, error->message); g_warning ("stream %p: failed to write buffer: %s", stream, error->message);
g_clear_error (&error); g_clear_error (&error);
return FALSE; return FALSE;
} }
#endif
return TRUE; return TRUE;
} }

View file

@ -24,6 +24,7 @@
#include <pinos/client/buffer.h> #include <pinos/client/buffer.h>
#include <pinos/client/context.h> #include <pinos/client/context.h>
#include <spa/include/spa/buffer.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -105,12 +106,9 @@ gboolean pinos_stream_start (PinosStream *stream,
PinosStreamMode mode); PinosStreamMode mode);
gboolean pinos_stream_stop (PinosStream *stream); gboolean pinos_stream_stop (PinosStream *stream);
PinosBuffer * pinos_stream_peek_buffer (PinosStream *stream); SpaBuffer * pinos_stream_peek_buffer (PinosStream *stream);
void pinos_stream_buffer_builder_init (PinosStream *stream,
PinosBufferBuilder *builder);
gboolean pinos_stream_send_buffer (PinosStream *stream, gboolean pinos_stream_send_buffer (PinosStream *stream,
PinosBuffer *buffer); SpaBuffer *buffer);
G_END_DECLS G_END_DECLS
#endif /* __PINOS_STREAM_H__ */ #endif /* __PINOS_STREAM_H__ */

View file

@ -32,7 +32,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "gstpinossocketsink.h" //#include "gstpinossocketsink.h"
#include "gstpinosportsink.h" #include "gstpinosportsink.h"
#include "gstpinosportsrc.h" #include "gstpinosportsrc.h"
#include "gstpinossrc.h" #include "gstpinossrc.h"
@ -46,20 +46,20 @@ GST_DEBUG_CATEGORY (pinos_debug);
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
{ {
gst_element_register (plugin, "pinospay", GST_RANK_NONE, // gst_element_register (plugin, "pinospay", GST_RANK_NONE,
GST_TYPE_PINOS_PAY); // GST_TYPE_PINOS_PAY);
gst_element_register (plugin, "pinosdepay", GST_RANK_NONE, // gst_element_register (plugin, "pinosdepay", GST_RANK_NONE,
GST_TYPE_PINOS_DEPAY); // GST_TYPE_PINOS_DEPAY);
gst_element_register (plugin, "pinossrc", GST_RANK_PRIMARY + 1, gst_element_register (plugin, "pinossrc", GST_RANK_PRIMARY + 1,
GST_TYPE_PINOS_SRC); GST_TYPE_PINOS_SRC);
gst_element_register (plugin, "pinossink", GST_RANK_NONE, gst_element_register (plugin, "pinossink", GST_RANK_NONE,
GST_TYPE_PINOS_SINK); GST_TYPE_PINOS_SINK);
gst_element_register (plugin, "pinossocketsink", GST_RANK_NONE, // gst_element_register (plugin, "pinossocketsink", GST_RANK_NONE,
GST_TYPE_PINOS_SOCKET_SINK); // GST_TYPE_PINOS_SOCKET_SINK);
gst_element_register (plugin, "pinosportsink", GST_RANK_NONE, // gst_element_register (plugin, "pinosportsink", GST_RANK_NONE,
GST_TYPE_PINOS_PORT_SINK); // GST_TYPE_PINOS_PORT_SINK);
gst_element_register (plugin, "pinosportsrc", GST_RANK_NONE, // gst_element_register (plugin, "pinosportsrc", GST_RANK_NONE,
GST_TYPE_PINOS_PORT_SRC); // GST_TYPE_PINOS_PORT_SRC);
if (!gst_device_provider_register (plugin, "pinosdeviceprovider", if (!gst_device_provider_register (plugin, "pinosdeviceprovider",
GST_RANK_PRIMARY + 1, GST_TYPE_PINOS_DEVICE_PROVIDER)) GST_RANK_PRIMARY + 1, GST_TYPE_PINOS_DEVICE_PROVIDER))

View file

@ -43,6 +43,8 @@
#include <gst/allocators/gstfdmemory.h> #include <gst/allocators/gstfdmemory.h>
#include <gst/video/video.h> #include <gst/video/video.h>
#include <spa/include/spa/buffer.h>
#include "gsttmpfileallocator.h" #include "gsttmpfileallocator.h"
@ -329,8 +331,7 @@ on_new_buffer (GObject *gobject,
gpointer user_data) gpointer user_data)
{ {
GstPinosSink *pinossink = user_data; GstPinosSink *pinossink = user_data;
PinosBuffer *pbuf; SpaBuffer *b;
PinosBufferIter it;
GST_LOG_OBJECT (pinossink, "got new buffer"); GST_LOG_OBJECT (pinossink, "got new buffer");
if (pinossink->stream == NULL) { if (pinossink->stream == NULL) {
@ -338,11 +339,12 @@ on_new_buffer (GObject *gobject,
return; return;
} }
if (!(pbuf = pinos_stream_peek_buffer (pinossink->stream))) { if (!(b = pinos_stream_peek_buffer (pinossink->stream))) {
g_warning ("failed to capture buffer"); g_warning ("failed to capture buffer");
return; return;
} }
#if 0
pinos_buffer_iter_init (&it, pbuf); pinos_buffer_iter_init (&it, pbuf);
while (pinos_buffer_iter_next (&it)) { while (pinos_buffer_iter_next (&it)) {
switch (pinos_buffer_iter_get_type (&it)) { switch (pinos_buffer_iter_get_type (&it)) {
@ -375,6 +377,7 @@ on_new_buffer (GObject *gobject,
} }
} }
pinos_buffer_iter_end (&it); pinos_buffer_iter_end (&it);
#endif
} }
static void 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 static GstFlowReturn
gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer) gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
{ {
GstPinosSink *pinossink; GstPinosSink *pinossink;
PinosBuffer pbuf; SinkBuffer *b;
PinosBufferBuilder builder;
GstMemory *mem = NULL; GstMemory *mem = NULL;
GstClockTime pts, dts, base; GstClockTime pts, dts, base;
PinosPacketHeader hdr;
PinosPacketAddMem am;
PinosPacketProcessMem p;
PinosPacketRemoveMem rm;
gsize size; gsize size;
gboolean tmpfile, res; gboolean tmpfile, res;
@ -505,6 +522,18 @@ gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
base = GST_ELEMENT_CAST (bsink)->base_time; 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); pts = GST_BUFFER_PTS (buffer);
dts = GST_BUFFER_DTS (buffer); dts = GST_BUFFER_DTS (buffer);
if (!GST_CLOCK_TIME_IS_VALID (pts)) 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)) else if (!GST_CLOCK_TIME_IS_VALID (dts))
dts = pts; dts = pts;
hdr.flags = 0; b->header.flags = 0;
hdr.seq = GST_BUFFER_OFFSET (buffer); b->header.seq = GST_BUFFER_OFFSET (buffer);
hdr.pts = GST_CLOCK_TIME_IS_VALID (pts) ? pts + base : base; b->header.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; 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;
size = gst_buffer_get_size (buffer); b->metas[0].data = &b->header;
b->metas[0].size = sizeof (b->header);
if (gst_buffer_n_memory (buffer) == 1 if (gst_buffer_n_memory (buffer) == 1
&& gst_is_fd_memory (gst_buffer_peek_memory (buffer, 0))) { && 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) if (pinos_stream_get_state (pinossink->stream) != PINOS_STREAM_STATE_STREAMING)
goto streaming_error; goto streaming_error;
pinos_stream_buffer_builder_init (pinossink->stream, &builder); b->mem = mem;
pinos_buffer_builder_add_header (&builder, &hdr); 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); if (!(res = pinos_stream_send_buffer (pinossink->stream, &b->buffer)))
am.fd_index = pinos_buffer_builder_add_fd (&builder, gst_fd_memory_get_fd (mem)); g_warning ("can't send buffer");
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);
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); pinos_main_loop_unlock (pinossink->loop);
if (res && !tmpfile) { /* keep the memory around until we get the reuse mem message */
/* 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);
g_hash_table_insert (pinossink->mem_ids, GINT_TO_POINTER (p.id), mem);
} else
gst_memory_unref (mem);
return GST_FLOW_OK; return GST_FLOW_OK;

View file

@ -44,6 +44,8 @@
#include <gst/allocators/gstfdmemory.h> #include <gst/allocators/gstfdmemory.h>
#include <gst/video/video.h> #include <gst/video/video.h>
#include <spa/include/spa/buffer.h>
static GQuark process_mem_data_quark; static GQuark process_mem_data_quark;
@ -322,35 +324,16 @@ gst_pinos_src_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
typedef struct { typedef struct {
GstPinosSrc *src; GstPinosSrc *src;
PinosPacketProcessMem p; SpaBuffer *buffer;
} ProcessMemData; } ProcessMemData;
static void static void
process_mem_data_destroy (gpointer user_data) process_mem_data_destroy (gpointer user_data)
{ {
ProcessMemData *data = user_data; ProcessMemData *data = user_data;
GstPinosSrc *pinossrc = data->src;
PinosBufferBuilder b;
PinosPacketReuseMem r;
PinosBuffer pbuf;
r.id = data->p.id; spa_buffer_unref (data->buffer);
gst_object_unref (data->src);
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);
g_slice_free (ProcessMemData, data); g_slice_free (ProcessMemData, data);
} }
@ -359,113 +342,74 @@ on_new_buffer (GObject *gobject,
gpointer user_data) gpointer user_data)
{ {
GstPinosSrc *pinossrc = user_data; GstPinosSrc *pinossrc = user_data;
PinosBuffer *pbuf; SpaBuffer *b;
PinosBufferIter it; GstBuffer *buf;
GstBuffer *buf = NULL; unsigned int i;
ProcessMemData data;
GST_LOG_OBJECT (pinossrc, "got new buffer"); 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"); g_warning ("failed to capture buffer");
return; return;
} }
pinos_buffer_iter_init (&it, pbuf); buf = gst_buffer_new ();
while (pinos_buffer_iter_next (&it)) {
switch (pinos_buffer_iter_get_type (&it)) { data.src = gst_object_ref (pinossrc);
case PINOS_PACKET_TYPE_HEADER: 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)) GST_INFO ("pts %" G_GUINT64_FORMAT ", dts_offset %"G_GUINT64_FORMAT, h->pts, h->dts_offset);
goto parse_failed;
if (buf == NULL) if (GST_CLOCK_TIME_IS_VALID (h->pts)) {
buf = gst_buffer_new (); GST_BUFFER_PTS (buf) = h->pts;
if (GST_BUFFER_PTS (buf) + h->dts_offset > 0)
GST_INFO ("pts %" G_GUINT64_FORMAT ", dts_offset %"G_GUINT64_FORMAT, hdr.pts, hdr.dts_offset); GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) + h->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;
} }
GST_BUFFER_OFFSET (buf) = hdr.seq; GST_BUFFER_OFFSET (buf) = h->seq;
break; break;
} }
case PINOS_PACKET_TYPE_ADD_MEM: default:
{ break;
GstMemory *fdmem = NULL; }
PinosPacketAddMem p; }
int fd; for (i = 0; i < b->n_datas; i++) {
SpaData *d = &b->datas[i];
if (!pinos_buffer_iter_parse_add_mem (&it, &p))
goto parse_failed; switch (d->type) {
case SPA_DATA_TYPE_MEMPTR:
fd = pinos_buffer_get_fd (pbuf, p.fd_index); {
if (fd == -1) gst_buffer_append_memory (buf,
goto parse_failed; gst_memory_new_wrapped (0, d->ptr, d->offset + d->size, d->offset,
d->size, NULL, NULL));
fdmem = gst_fd_allocator_alloc (pinossrc->fd_allocator, dup (fd), break;
p.offset + p.size, GST_FD_MEMORY_FLAG_NONE); }
gst_memory_resize (fdmem, p.offset, p.size); case SPA_DATA_TYPE_FD:
{
g_hash_table_insert (pinossrc->mem_ids, GINT_TO_POINTER (p.id), fdmem); GstMemory *fdmem = NULL;
break; int fd = *((int *) d->ptr);
}
case PINOS_PACKET_TYPE_REMOVE_MEM: fdmem = gst_fd_allocator_alloc (pinossrc->fd_allocator, dup (fd),
{ d->offset + d->size, GST_FD_MEMORY_FLAG_NONE);
PinosPacketRemoveMem p; gst_memory_resize (fdmem, d->offset, d->size);
gst_buffer_append_memory (buf, fdmem);
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);
break; break;
} }
default: default:
break; break;
} }
} }
pinos_buffer_iter_end (&it);
if (buf) { if (buf) {
g_queue_push_tail (&pinossrc->queue, buf); g_queue_push_tail (&pinossrc->queue, buf);
@ -473,17 +417,6 @@ on_new_buffer (GObject *gobject,
pinos_main_loop_signal (pinossrc->loop, FALSE); pinos_main_loop_signal (pinossrc->loop, FALSE);
} }
return; 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 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 static gboolean
gst_pinos_src_unlock (GstBaseSrc * basesrc) gst_pinos_src_unlock (GstBaseSrc * basesrc)
{ {
@ -771,9 +720,6 @@ static gboolean
gst_pinos_src_event (GstBaseSrc * src, GstEvent * event) gst_pinos_src_event (GstBaseSrc * src, GstEvent * event)
{ {
gboolean res = FALSE; gboolean res = FALSE;
GstPinosSrc *pinossrc;
pinossrc = GST_PINOS_SRC (src);
switch (GST_EVENT_TYPE (event)) { switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CUSTOM_UPSTREAM: case GST_EVENT_CUSTOM_UPSTREAM:
@ -781,13 +727,11 @@ gst_pinos_src_event (GstBaseSrc * src, GstEvent * event)
GstClockTime running_time; GstClockTime running_time;
gboolean all_headers; gboolean all_headers;
guint count; guint count;
PinosPacketRefreshRequest refresh;
PinosBufferBuilder b;
PinosBuffer pbuf;
gst_video_event_parse_upstream_force_key_unit (event, gst_video_event_parse_upstream_force_key_unit (event,
&running_time, &all_headers, &count); &running_time, &all_headers, &count);
#if 0
pinos_buffer_builder_init (&b); pinos_buffer_builder_init (&b);
refresh.last_id = 0; refresh.last_id = 0;
@ -805,6 +749,7 @@ gst_pinos_src_event (GstBaseSrc * src, GstEvent * event)
GST_OBJECT_UNLOCK (pinossrc); GST_OBJECT_UNLOCK (pinossrc);
pinos_buffer_unref (&pbuf); pinos_buffer_unref (&pbuf);
#endif
res = TRUE; res = TRUE;
} else { } else {
res = GST_BASE_SRC_CLASS (parent_class)->event (src, event); 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); 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::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); g_signal_connect (pinossrc->stream, "new-buffer", (GCallback) on_new_buffer, pinossrc);
pinos_main_loop_unlock (pinossrc->loop); pinos_main_loop_unlock (pinossrc->loop);

View file

@ -434,110 +434,40 @@ free_mem_block (MemBlock *b)
static gboolean static gboolean
on_received_buffer (PinosPort *port, on_received_buffer (PinosPort *port,
PinosBuffer *buffer, SpaBuffer *buffer,
GError **error, GError **error,
gpointer user_data) gpointer user_data)
{ {
PinosSpaAlsaSink *this = user_data; PinosSpaAlsaSink *this = user_data;
PinosSpaAlsaSinkPrivate *priv = this->priv; PinosSpaAlsaSinkPrivate *priv = this->priv;
PinosBuffer *pbuf = buffer; unsigned int i;
PinosBufferIter it;
pinos_buffer_iter_init (&it, pbuf); for (i = 0; i < buffer->n_datas; i++) {
while (pinos_buffer_iter_next (&it)) { SpaData *d = &buffer->datas[i];
switch (pinos_buffer_iter_get_type (&it)) { PinosRingbufferArea areas[2];
case PINOS_PACKET_TYPE_HEADER: uint8_t *data;
{ size_t size, towrite, total;
PinosPacketHeader hdr;
if (!pinos_buffer_iter_parse_header (&it, &hdr)) if (d->type != SPA_DATA_TYPE_MEMPTR)
break; continue;
break; size = d->size;
} data = (guint8*)d->ptr + d->offset;
case PINOS_PACKET_TYPE_ADD_MEM:
{
PinosPacketAddMem p;
MemBlock *b;
int fd;
if (!pinos_buffer_iter_parse_add_mem (&it, &p)) pinos_ringbuffer_get_write_areas (priv->ringbuffer, areas);
break;
fd = pinos_buffer_get_fd (pbuf, p.fd_index); total = MIN (size, areas[0].len + areas[1].len);
if (fd == -1) g_debug ("total write %zd %zd", total, areas[0].len + areas[1].len);
break; 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); pinos_ringbuffer_write_advance (priv->ringbuffer, total);
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_buffer_iter_end (&it); spa_buffer_unref (buffer);
return TRUE; return TRUE;
} }
@ -550,12 +480,11 @@ on_format_change (GObject *obj,
SinkPortData *data = user_data; SinkPortData *data = user_data;
PinosNode *node = PINOS_NODE (data->sink); PinosNode *node = PINOS_NODE (data->sink);
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (node); PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (node);
PinosSpaAlsaSinkPrivate *priv = sink->priv;
GBytes *formats; GBytes *formats;
g_object_get (obj, "format", &formats, NULL); g_object_get (obj, "format", &formats, NULL);
if (formats) { 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); negotiate_formats (sink);
} }
} }

View file

@ -58,10 +58,9 @@ struct _PinosSpaV4l2SourcePrivate
gboolean running; gboolean running;
pthread_t thread; pthread_t thread;
const void *format; GBytes *format;
GList *ports; GList *ports;
PinosFdManager *fdmanager;
}; };
enum { enum {
@ -113,50 +112,6 @@ make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char
return SPA_RESULT_ERROR; 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 static void
on_source_event (SpaHandle *handle, SpaEvent *event, void *user_data) 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, }; SpaOutputInfo info[1] = { 0, };
SpaResult res; SpaResult res;
SpaBuffer *b; SpaBuffer *b;
PinosBuffer pbuf;
PinosBufferBuilder builder;
PinosPacketHeader hdr;
PinosPacketAddMem am;
PinosPacketProcessMem p;
PinosPacketRemoveMem rm;
GList *walk; 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) 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); g_debug ("spa-v4l2-source %p: got pull error %d", source, res);
b = info[0].buffer; 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)) { for (walk = priv->ports; walk; walk = g_list_next (walk)) {
SourcePortData *data = walk->data; SourcePortData *data = walk->data;
GError *error = NULL; GError *error = NULL;
if (!data->have_format) if (!data->have_format) {
send_format (source, data); 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_debug ("send failed: %s", error->message);
g_clear_error (&error); g_clear_error (&error);
} }
} }
if (!do_close)
pinos_buffer_steal_fds (&pbuf, NULL);
pinos_buffer_unref (&pbuf);
spa_buffer_unref (b); spa_buffer_unref (b);
break; break;
} }
@ -270,7 +186,7 @@ create_pipeline (PinosSpaV4l2Source *this)
g_debug ("got get_props error %d", res); g_debug ("got get_props error %d", res);
value.type = SPA_PROP_TYPE_STRING; value.type = SPA_PROP_TYPE_STRING;
value.value = "/dev/video0"; value.value = "/dev/video1";
value.size = strlen (value.value)+1; value.size = strlen (value.value)+1;
props->set_prop (props, spa_props_index_for_name (props, "device"), &value); props->set_prop (props, spa_props_index_for_name (props, "device"), &value);
@ -290,6 +206,7 @@ negotiate_formats (PinosSpaV4l2Source *this)
void *state = NULL; void *state = NULL;
SpaFraction frac; SpaFraction frac;
SpaRectangle rect; SpaRectangle rect;
const gchar *str;
if ((res = priv->source_node->port_enum_formats (priv->source, 0, &format, NULL, &state)) < 0) if ((res = priv->source_node->port_enum_formats (priv->source, 0, &format, NULL, &state)) < 0)
return res; return res;
@ -323,11 +240,12 @@ negotiate_formats (PinosSpaV4l2Source *this)
if ((res = priv->source_node->port_set_format (priv->source, 0, 0, format)) < 0) if ((res = priv->source_node->port_set_format (priv->source, 0, 0, format)) < 0)
return res; return res;
priv->format = "video/x-raw," str = "video/x-raw,"
" format=(string)YUY2," " format=(string)YUY2,"
" width=(int)320," " width=(int)320,"
" height=(int)240," " height=(int)240,"
" framerate=(fraction)30/1"; " framerate=(fraction)30/1";
priv->format = g_bytes_new_static (str, strlen (str)+1);
return SPA_RESULT_OK; return SPA_RESULT_OK;
} }
@ -487,26 +405,6 @@ on_deactivate (PinosPort *port, gpointer user_data)
pinos_node_report_idle (PINOS_NODE (source)); 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 static void
free_source_port_data (SourcePortData *data) 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) data->port = PINOS_NODE_CLASS (pinos_spa_v4l2_source_parent_class)
->add_port (node, direction, id, error); ->add_port (node, direction, id, error);
pinos_port_set_received_buffer_cb (data->port, on_received_buffer, source, NULL);
g_debug ("connecting signals"); g_debug ("connecting signals");
g_signal_connect (data->port, "activate", (GCallback) on_activate, data); g_signal_connect (data->port, "activate", (GCallback) on_activate, data);
g_signal_connect (data->port, "deactivate", (GCallback) on_deactivate, 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 static void
pinos_spa_v4l2_source_init (PinosSpaV4l2Source * source) pinos_spa_v4l2_source_init (PinosSpaV4l2Source * source)
{ {
PinosSpaV4l2SourcePrivate *priv = source->priv = PINOS_SPA_V4L2_SOURCE_GET_PRIVATE (source); source->priv = PINOS_SPA_V4L2_SOURCE_GET_PRIVATE (source);
priv->fdmanager = pinos_fd_manager_get (PINOS_FD_MANAGER_DEFAULT);
} }
PinosNode * PinosNode *

View file

@ -17,10 +17,15 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#define _GNU_SOURCE
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <errno.h> #include <errno.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <gio/gunixfdlist.h> #include <gio/gunixfdlist.h>
@ -34,26 +39,19 @@
#include "pinos/dbus/org-pinos.h" #include "pinos/dbus/org-pinos.h"
#include "spa/include/spa/control.h"
#define MAX_BUFFER_SIZE 1024 #define MAX_BUFFER_SIZE 1024
#define MAX_FDS 16 #define MAX_FDS 16
typedef struct {
guint32 id;
guint32 type;
int fd;
guint64 offset;
guint64 size;
void *data;
} MemBlock;
struct _PinosClientNodePrivate struct _PinosClientNodePrivate
{ {
int fd; int fd;
GSource *socket_source; GSource *socket_source;
GSocket *sockets[2]; GSocket *sockets[2];
PinosBuffer recv_buffer; SpaControl recv_control;
guint8 recv_data[MAX_BUFFER_SIZE]; guint8 recv_data[MAX_BUFFER_SIZE];
int recv_fds[MAX_FDS]; 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 static gboolean
parse_buffer (PinosClientNode *cnode, parse_control (PinosClientNode *cnode,
PinosBuffer *pbuf) SpaControl *ctrl)
{ {
PinosNode *node = PINOS_NODE (cnode); PinosNode *node = PINOS_NODE (cnode);
PinosBufferIter it;
PinosClientNodePrivate *priv = cnode->priv; PinosClientNodePrivate *priv = cnode->priv;
PinosPort *port; SpaControlIter it;
pinos_buffer_iter_init (&it, pbuf); spa_control_iter_init (&it, ctrl);
while (pinos_buffer_iter_next (&it)) { while (spa_control_iter_next (&it) == SPA_RESULT_OK) {
PinosPacketType type = pinos_buffer_iter_get_type (&it); SpaControlCmd cmd = spa_control_iter_get_cmd (&it);
switch (type) { switch (cmd) {
case PINOS_PACKET_TYPE_FORMAT_CHANGE: 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; SpaControlBuilder builder;
GBytes *format; SpaControl control;
guint8 buffer[1024];
if (!pinos_buffer_iter_parse_format_change (&it, &p)) /* set port format */
break;
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))) if (spa_control_write (&control, priv->fd) < 0)
break; 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; break;
} }
case PINOS_PACKET_TYPE_START: case SPA_CONTROL_CMD_PORT_STATUS_CHANGE:
{ {
GBytes *format; g_warning ("client-node %p: command not implemented %d", node, cmd);
PinosBufferBuilder builder; break;
PinosBuffer obuf; }
case SPA_CONTROL_CMD_START_ALLOC:
{
SpaControlBuilder builder;
SpaControl control;
guint8 buffer[1024]; guint8 buffer[1024];
GError *error = NULL;
GList *ports, *walk; 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); ports = pinos_node_get_ports (node);
for (walk = ports; walk; walk = g_list_next (walk)) { for (walk = ports; walk; walk = g_list_next (walk)) {
PinosPacketFormatChange fc; PinosPort *port = walk->data;
port = walk->data;
pinos_port_activate (port); 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; break;
} }
case PINOS_PACKET_TYPE_ADD_MEM: case SPA_CONTROL_CMD_NEED_INPUT:
{
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:
{ {
break; break;
} }
case PINOS_PACKET_TYPE_REUSE_MEM: case SPA_CONTROL_CMD_HAVE_OUTPUT:
{ {
break; 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: default:
g_warning ("unhandled packet %d", type); g_warning ("client-node %p: command unhandled %d", node, cmd);
break; break;
} }
} }
pinos_buffer_iter_end (&it); spa_control_iter_end (&it);
return TRUE; return TRUE;
} }
@ -299,22 +237,19 @@ on_socket_condition (GSocket *socket,
switch (condition) { switch (condition) {
case G_IO_IN: case G_IO_IN:
{ {
PinosBuffer *buffer = &priv->recv_buffer; SpaControl *control = &priv->recv_control;
GError *error = NULL;
if (!pinos_io_read_buffer (priv->fd, if (spa_control_read (control,
buffer, priv->fd,
priv->recv_data, priv->recv_data,
MAX_BUFFER_SIZE, MAX_BUFFER_SIZE,
priv->recv_fds, priv->recv_fds,
MAX_FDS, MAX_FDS) < 0) {
&error)) { g_warning ("client-node %p: failed to read buffer", node);
g_warning ("client-node %p: failed to read buffer: %s", node, error->message);
g_clear_error (&error);
return TRUE; return TRUE;
} }
parse_buffer (node, buffer); parse_control (node, control);
#if 0 #if 0
if (!pinos_port_receive_buffer (priv->port, buffer, &error)) { if (!pinos_port_receive_buffer (priv->port, buffer, &error)) {
@ -322,7 +257,7 @@ on_socket_condition (GSocket *socket,
g_clear_error (&error); g_clear_error (&error);
} }
#endif #endif
g_assert (pinos_buffer_unref (buffer) == FALSE); spa_control_clear (control);
break; break;
} }
@ -416,16 +351,153 @@ create_failed:
} }
} }
static gboolean static void
on_received_buffer (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data) on_format_change (GObject *obj,
GParamSpec *pspec,
gpointer user_data)
{ {
PinosClientNode *node = user_data; PinosClientNode *node = user_data;
PinosClientNodePrivate *priv = node->priv; 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_object_get (obj, "format", &format, NULL);
g_warning ("client-node %p: error writing buffer: %s", node, (*error)->message); if (format == NULL)
return FALSE; 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; return TRUE;
} }
@ -441,6 +513,8 @@ add_port (PinosNode *node,
if (port) { if (port) {
pinos_port_set_received_buffer_cb (port, on_received_buffer, node, NULL); 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; return port;
} }
@ -506,8 +580,7 @@ pinos_client_node_class_init (PinosClientNodeClass * klass)
static void static void
pinos_client_node_init (PinosClientNode * node) 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); 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);
} }

View file

@ -22,6 +22,8 @@
#include <glib-object.h> #include <glib-object.h>
#include <pinos/server/node.h>
G_BEGIN_DECLS G_BEGIN_DECLS
#define PINOS_TYPE_CLIENT_NODE (pinos_client_node_get_type ()) #define PINOS_TYPE_CLIENT_NODE (pinos_client_node_get_type ())

View file

@ -250,7 +250,7 @@ handle_create_client_node (PinosDaemon1 *interface,
continue; 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); link = pinos_link_new (daemon, port, target, NULL);
pinos_client_add_object (client, G_OBJECT (link)); pinos_client_add_object (client, G_OBJECT (link));

View file

@ -69,7 +69,7 @@ enum
static guint signals[LAST_SIGNAL] = { 0 }; static guint signals[LAST_SIGNAL] = { 0 };
static gboolean 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; PinosLink *link = user_data;
PinosLinkPrivate *priv = link->priv; PinosLinkPrivate *priv = link->priv;
@ -78,7 +78,7 @@ on_output_send (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer u
} }
static gboolean 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; PinosLink *link = user_data;
PinosLinkPrivate *priv = link->priv; PinosLinkPrivate *priv = link->priv;
@ -245,7 +245,7 @@ on_format_change (GObject *obj,
GBytes *formats; GBytes *formats;
g_object_get (priv->output, "format", &formats, NULL); 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); g_object_set (priv->input, "format", formats, NULL);
} }

View file

@ -597,34 +597,6 @@ pinos_port_filter_formats (PinosPort *port,
return pinos_format_filter (priv->possible_formats, filter, error); 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 void
pinos_port_activate (PinosPort *port) pinos_port_activate (PinosPort *port)
{ {
@ -668,15 +640,13 @@ pinos_port_deactivate (PinosPort *port)
*/ */
gboolean gboolean
pinos_port_receive_buffer (PinosPort *port, pinos_port_receive_buffer (PinosPort *port,
PinosBuffer *buffer, SpaBuffer *buffer,
GError **error) GError **error)
{ {
gboolean res = TRUE; gboolean res = TRUE;
PinosPortPrivate *priv = port->priv; PinosPortPrivate *priv = port->priv;
PINOS_DEBUG_TRANSPORT ("port %p: receive buffer %p", port, buffer); 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) if (priv->received_buffer_cb)
res = priv->received_buffer_cb (port, buffer, error, priv->received_buffer_data); 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: * pinos_port_send_buffer:
* @port: a #PinosPort * @port: a #PinosPort
* @buffer: a #PinosBuffer * @buffer: a #SpaBuffer
* @error: a #GError or %NULL * @error: a #GError or %NULL
* *
* Send @buffer out on @port. * Send @buffer out on @port.
@ -696,7 +666,7 @@ pinos_port_receive_buffer (PinosPort *port,
*/ */
gboolean gboolean
pinos_port_send_buffer (PinosPort *port, pinos_port_send_buffer (PinosPort *port,
PinosBuffer *buffer, SpaBuffer *buffer,
GError **error) GError **error)
{ {
gboolean res = TRUE; gboolean res = TRUE;
@ -706,8 +676,6 @@ pinos_port_send_buffer (PinosPort *port,
g_return_val_if_fail (PINOS_IS_PORT (port), FALSE); g_return_val_if_fail (PINOS_IS_PORT (port), FALSE);
PINOS_DEBUG_TRANSPORT ("port %p: send buffer %p", port, buffer); 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; priv = port->priv;

View file

@ -31,6 +31,7 @@ typedef struct _PinosPortPrivate PinosPortPrivate;
#include <pinos/client/introspect.h> #include <pinos/client/introspect.h>
#include <pinos/client/buffer.h> #include <pinos/client/buffer.h>
#include <pinos/server/daemon.h> #include <pinos/server/daemon.h>
#include <spa/include/spa/buffer.h>
#define PINOS_TYPE_PORT (pinos_port_get_type ()) #define PINOS_TYPE_PORT (pinos_port_get_type ())
#define PINOS_IS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_PORT)) #define PINOS_IS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_PORT))
@ -65,7 +66,7 @@ struct _PinosPortClass {
GTask *task); 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 */ /* normal GObject stuff */
GType pinos_port_get_type (void); GType pinos_port_get_type (void);
@ -99,10 +100,10 @@ void pinos_port_activate (PinosPort *port);
void pinos_port_deactivate (PinosPort *port); void pinos_port_deactivate (PinosPort *port);
gboolean pinos_port_send_buffer (PinosPort *port, gboolean pinos_port_send_buffer (PinosPort *port,
PinosBuffer *buffer, SpaBuffer *buffer,
GError **error); GError **error);
gboolean pinos_port_receive_buffer (PinosPort *port, gboolean pinos_port_receive_buffer (PinosPort *port,
PinosBuffer *buffer, SpaBuffer *buffer,
GError **error); GError **error);
G_END_DECLS G_END_DECLS

View file

@ -101,10 +101,11 @@ typedef struct {
/** /**
* SpaDataType: * SpaDataType:
* @SPA_DATA_TYPE_INVALID: invalid data type, is ignored * @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. * CPU.
* @SPA_DATA_TYPE_FD: data points to an int file descriptor that can be * @SPA_DATA_TYPE_FD: data points to an int file descriptor that can be
* mmapped. * 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 * @SPA_DATA_TYPE_POINTER: data points to some other datastructure, the
* type can be found in ptr_type * type can be found in ptr_type
*/ */
@ -112,6 +113,7 @@ typedef enum {
SPA_DATA_TYPE_INVALID = 0, SPA_DATA_TYPE_INVALID = 0,
SPA_DATA_TYPE_MEMPTR, SPA_DATA_TYPE_MEMPTR,
SPA_DATA_TYPE_FD, SPA_DATA_TYPE_FD,
SPA_DATA_TYPE_MEMID,
SPA_DATA_TYPE_POINTER, SPA_DATA_TYPE_POINTER,
} SpaDataType; } SpaDataType;
@ -138,6 +140,7 @@ typedef struct {
* SpaBuffer: * SpaBuffer:
* @refcount: reference counter * @refcount: reference counter
* @notify: called when the refcount reaches 0 * @notify: called when the refcount reaches 0
* @id: buffer id
* @size: total size of the buffer data * @size: total size of the buffer data
* @n_metas: number of metadata * @n_metas: number of metadata
* @metas: array of @n_metas metadata * @metas: array of @n_metas metadata
@ -147,6 +150,7 @@ typedef struct {
struct _SpaBuffer { struct _SpaBuffer {
volatile int refcount; volatile int refcount;
SpaNotify notify; SpaNotify notify;
uint32_t id;
size_t size; size_t size;
unsigned int n_metas; unsigned int n_metas;
SpaMeta *metas; SpaMeta *metas;

273
spa/include/spa/control.h Normal file
View file

@ -0,0 +1,273 @@
/* Simple Plugin API
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#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 <spa/defs.h>
#include <spa/props.h>
#include <spa/format.h>
#include <spa/port.h>
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__ */

38
spa/include/spa/debug.h Normal file
View file

@ -0,0 +1,38 @@
/* Simple Plugin API
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __SPA_DEBUG_H__
#define __SPA_DEBUG_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <spa/defs.h>
#include <spa/port.h>
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__ */

View file

@ -45,6 +45,7 @@ typedef struct _SpaEvent SpaEvent;
* @SPA_EVENT_TYPE_MARKER: emited when MARK command completed * @SPA_EVENT_TYPE_MARKER: emited when MARK command completed
* @SPA_EVENT_TYPE_ERROR: emited when error occured * @SPA_EVENT_TYPE_ERROR: emited when error occured
* @SPA_EVENT_TYPE_BUFFERING: emited when buffering is in progress * @SPA_EVENT_TYPE_BUFFERING: emited when buffering is in progress
* @SPA_EVENT_TYPE_REQUEST_REFRESH: emited when a keyframe refresh is needed
*/ */
typedef enum { typedef enum {
SPA_EVENT_TYPE_INVALID = 0, SPA_EVENT_TYPE_INVALID = 0,
@ -60,6 +61,7 @@ typedef enum {
SPA_EVENT_TYPE_MARKER, SPA_EVENT_TYPE_MARKER,
SPA_EVENT_TYPE_ERROR, SPA_EVENT_TYPE_ERROR,
SPA_EVENT_TYPE_BUFFERING, SPA_EVENT_TYPE_BUFFERING,
SPA_EVENT_TYPE_REQUEST_REFRESH,
} SpaEventType; } SpaEventType;
struct _SpaEvent { struct _SpaEvent {

View file

@ -33,11 +33,26 @@ typedef enum {
SPA_MEDIA_TYPE_INVALID = 0, SPA_MEDIA_TYPE_INVALID = 0,
SPA_MEDIA_TYPE_AUDIO = 1, SPA_MEDIA_TYPE_AUDIO = 1,
SPA_MEDIA_TYPE_VIDEO = 2, SPA_MEDIA_TYPE_VIDEO = 2,
SPA_MEDIA_TYPE_IMAGE = 3,
} SpaMediaType; } SpaMediaType;
typedef enum { typedef enum {
SPA_MEDIA_SUBTYPE_INVALID = 0, SPA_MEDIA_SUBTYPE_INVALID = 0,
SPA_MEDIA_SUBTYPE_RAW = 1, 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; } SpaMediaSubType;
struct _SpaFormat { struct _SpaFormat {

View file

@ -244,7 +244,6 @@ SpaResult spa_props_copy (const SpaProps *src,
SpaProps *dest); SpaProps *dest);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
#endif #endif

View file

@ -57,6 +57,10 @@ SpaResult spa_video_raw_format_init (SpaVideoRawFormat *format);
SpaResult spa_video_raw_format_parse (const SpaFormat *format, SpaResult spa_video_raw_format_parse (const SpaFormat *format,
SpaVideoRawFormat *rawformat); 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 #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View file

@ -235,7 +235,7 @@ spa_audio_raw_format_parse (const SpaFormat *format,
spa_audio_raw_format_init (rawformat); spa_audio_raw_format_init (rawformat);
props = &format->props; 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; goto fallback;
if (value.type != SPA_PROP_TYPE_POINTER || value.size != sizeof (SpaAudioRawInfo)) if (value.type != SPA_PROP_TYPE_POINTER || value.size != sizeof (SpaAudioRawInfo))

1048
spa/lib/control.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -78,3 +78,20 @@ spa_debug_port_info (const SpaPortInfo *info)
} }
return SPA_RESULT_OK; 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;
}

View file

@ -1,4 +1,5 @@
spalib_sources = ['audio-raw.c', spalib_sources = ['audio-raw.c',
'control.c',
'debug.c', 'debug.c',
'props.c', 'props.c',
'ringbuffer.c', 'ringbuffer.c',
@ -8,3 +9,7 @@ spalib = shared_library('spa-lib',
spalib_sources, spalib_sources,
include_directories : inc, include_directories : inc,
install : true) install : true)
spalibs = static_library('spa-lib',
spalib_sources,
include_directories : inc,
install : true)

View file

@ -111,11 +111,11 @@ static const uint32_t format_values[] = {
static const SpaPropRangeInfo format_range[] = { static const SpaPropRangeInfo format_range[] = {
{ "ENCODED,", "ENCODED", sizeof (uint32_t), &format_values[1] }, { "ENCODED,", "ENCODED", sizeof (uint32_t), &format_values[1] },
{ "S16LE", "S16LE", sizeof (uint32_t), &format_values[2] }, { "I420", "I420", sizeof (uint32_t), &format_values[2] },
{ "S16BE", "S16BE", sizeof (uint32_t), &format_values[3] }, { "YV12", "YV12", sizeof (uint32_t), &format_values[3] },
{ "U16LE", "U16LE", sizeof (uint32_t), &format_values[4] }, { "YUY2", "YUY2", sizeof (uint32_t), &format_values[4] },
{ "U16BE", "U16BE", sizeof (uint32_t), &format_values[5] }, { "UYVY", "UYVY", sizeof (uint32_t), &format_values[5] },
{ "S24_32LE", "S24_32LE", sizeof (uint32_t), &format_values[6] }, { "AYUV", "AYUV", sizeof (uint32_t), &format_values[6] },
{ "S24_32BE", "S24_32BE", sizeof (uint32_t), &format_values[7] }, { "S24_32BE", "S24_32BE", sizeof (uint32_t), &format_values[7] },
{ "U24_32LE", "U24_32LE", sizeof (uint32_t), &format_values[8] }, { "U24_32LE", "U24_32LE", sizeof (uint32_t), &format_values[8] },
{ "U24_32BE", "U24_32BE", sizeof (uint32_t), &format_values[9] }, { "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); spa_video_raw_format_init (rawformat);
props = &format->props; 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; goto fallback;
if (value.type != SPA_PROP_TYPE_POINTER || value.size != sizeof (SpaVideoRawInfo)) if (value.type != SPA_PROP_TYPE_POINTER || value.size != sizeof (SpaVideoRawInfo))
@ -524,3 +524,37 @@ fallback:
return res; 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;
}

View file

@ -40,6 +40,7 @@ spa_enum_handle_factory (const SpaHandleFactory **factory,
default: default:
return SPA_RESULT_ENUM_END; return SPA_RESULT_ENUM_END;
} }
*(int*)state = index++; *(int*)state = ++index;
return SPA_RESULT_OK; return SPA_RESULT_OK;
} }

View file

@ -324,7 +324,7 @@ spa_audiomixer_node_port_enum_formats (SpaHandle *handle,
return SPA_RESULT_ENUM_END; return SPA_RESULT_ENUM_END;
} }
*format = &this->query_format.format; *format = &this->query_format.format;
*(int*)state = index++; *(int*)state = ++index;
return SPA_RESULT_OK; return SPA_RESULT_OK;
} }

View file

@ -61,6 +61,24 @@ struct _V4l2Buffer {
int dmafd; 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 { typedef struct {
bool export_buf; bool export_buf;
bool have_buffers; bool have_buffers;
@ -69,11 +87,10 @@ typedef struct {
struct v4l2_fmtdesc fmtdesc; struct v4l2_fmtdesc fmtdesc;
bool next_frmsize; bool next_frmsize;
struct v4l2_frmsizeenum frmsize; struct v4l2_frmsizeenum frmsize;
bool next_frmival;
struct v4l2_frmivalenum frmival; struct v4l2_frmivalenum frmival;
void *cookie; void *cookie;
SpaVideoRawFormat raw_format[2]; V4l2Format format[2];
SpaFormat *current_format; SpaFormat *current_format;
int fd; int fd;
@ -309,6 +326,33 @@ spa_v4l2_source_node_remove_port (SpaHandle *handle,
return SPA_RESULT_NOT_IMPLEMENTED; 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 static SpaResult
spa_v4l2_source_node_port_enum_formats (SpaHandle *handle, spa_v4l2_source_node_port_enum_formats (SpaHandle *handle,
uint32_t port_id, uint32_t port_id,
@ -339,7 +383,7 @@ spa_v4l2_source_node_port_set_format (SpaHandle *handle,
SpaV4l2Source *this = (SpaV4l2Source *) handle; SpaV4l2Source *this = (SpaV4l2Source *) handle;
SpaV4l2State *state; SpaV4l2State *state;
SpaResult res; SpaResult res;
SpaFormat *f, *tf; V4l2Format *f, *tf;
size_t fs; size_t fs;
if (handle == NULL || format == NULL) if (handle == NULL || format == NULL)
@ -355,25 +399,22 @@ spa_v4l2_source_node_port_set_format (SpaHandle *handle,
return SPA_RESULT_OK; return SPA_RESULT_OK;
} }
if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { f = &state->format[0];
if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { tf = &state->format[1];
if ((res = spa_video_raw_format_parse (format, &state->raw_format[0]) < 0)) fs = sizeof (V4l2Format);
return res;
f = &state->raw_format[0].format; spa_v4l2_format_init (f);
tf = &state->raw_format[1].format; f->fmt.media_type = format->media_type;
fs = sizeof (SpaVideoRawFormat); f->fmt.media_subtype = format->media_subtype;
} else if ((res = spa_props_copy (&format->props, &f->fmt.props) < 0))
return SPA_RESULT_INVALID_MEDIA_TYPE; return res;
} else
return SPA_RESULT_INVALID_MEDIA_TYPE;
if (spa_v4l2_set_format (this, f, flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY) < 0) if (spa_v4l2_set_format (this, f, flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY) < 0)
return SPA_RESULT_INVALID_MEDIA_TYPE; return SPA_RESULT_INVALID_MEDIA_TYPE;
if (!(flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY)) { if (!(flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY)) {
memcpy (tf, f, fs); memcpy (tf, f, fs);
state->current_format = tf; state->current_format = &tf->fmt;
} }
return SPA_RESULT_OK; return SPA_RESULT_OK;

View file

@ -66,201 +66,157 @@ spa_v4l2_open (SpaV4l2Source *this)
return 0; return 0;
} }
static SpaVideoFormat typedef struct {
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)
{
uint32_t fourcc; uint32_t fourcc;
SpaVideoFormat format;
SpaMediaType media_type;
SpaMediaSubType media_subtype;
} FormatInfo;
switch (format) { static const FormatInfo format_info[] =
case SPA_VIDEO_FORMAT_I420: {
fourcc = V4L2_PIX_FMT_YUV420; /* RGB formats */
break; { V4L2_PIX_FMT_RGB332, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_YUY2: { V4L2_PIX_FMT_ARGB555, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_YUYV; { V4L2_PIX_FMT_XRGB555, SPA_VIDEO_FORMAT_RGB15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_ARGB555X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_UYVY: { V4L2_PIX_FMT_XRGB555X, SPA_VIDEO_FORMAT_BGR15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_UYVY; { V4L2_PIX_FMT_RGB565, SPA_VIDEO_FORMAT_RGB16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_RGB565X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_YV12: { V4L2_PIX_FMT_BGR666, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_YVU420; { V4L2_PIX_FMT_BGR24, SPA_VIDEO_FORMAT_BGR, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_RGB24, SPA_VIDEO_FORMAT_RGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_Y41B: { V4L2_PIX_FMT_ABGR32, SPA_VIDEO_FORMAT_BGRA, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_YUV411P; { V4L2_PIX_FMT_XBGR32, SPA_VIDEO_FORMAT_BGRx, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_ARGB32, SPA_VIDEO_FORMAT_ARGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_Y42B: { V4L2_PIX_FMT_XRGB32, SPA_VIDEO_FORMAT_xRGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_YUV422P;
break; /* Deprecated Packed RGB Image Formats (alpha ambiguity) */
case SPA_VIDEO_FORMAT_NV12: { V4L2_PIX_FMT_RGB444, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_NV12; { V4L2_PIX_FMT_RGB555, SPA_VIDEO_FORMAT_RGB15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_RGB555X, SPA_VIDEO_FORMAT_BGR15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_NV12_64Z32: { V4L2_PIX_FMT_BGR32, SPA_VIDEO_FORMAT_BGRx, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_NV12MT; { V4L2_PIX_FMT_RGB32, SPA_VIDEO_FORMAT_xRGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break;
case SPA_VIDEO_FORMAT_NV21: /* Grey formats */
fourcc = V4L2_PIX_FMT_NV21; { V4L2_PIX_FMT_GREY, SPA_VIDEO_FORMAT_GRAY8, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_Y4, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_NV16: { V4L2_PIX_FMT_Y6, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_NV16; { V4L2_PIX_FMT_Y10, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_Y12, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_NV61: { V4L2_PIX_FMT_Y16, SPA_VIDEO_FORMAT_GRAY16_LE, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_NV61; { V4L2_PIX_FMT_Y16_BE, SPA_VIDEO_FORMAT_GRAY16_BE, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_Y10BPACK, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_NV24:
fourcc = V4L2_PIX_FMT_NV24; /* Palette formats */
break; { V4L2_PIX_FMT_PAL8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_YVYU:
fourcc = V4L2_PIX_FMT_YVYU; /* Chrominance formats */
break; { V4L2_PIX_FMT_UV8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_RGB15:
fourcc = V4L2_PIX_FMT_RGB555; /* Luminance+Chrominance formats */
break; { V4L2_PIX_FMT_YVU410, SPA_VIDEO_FORMAT_YVU9, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_RGB16: { V4L2_PIX_FMT_YVU420, SPA_VIDEO_FORMAT_YV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_RGB565; { V4L2_PIX_FMT_YVU420M, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_YUYV, SPA_VIDEO_FORMAT_YUY2, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_RGB: { V4L2_PIX_FMT_YYUV, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_RGB24; { V4L2_PIX_FMT_YVYU, SPA_VIDEO_FORMAT_YVYU, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_UYVY, SPA_VIDEO_FORMAT_UYVY, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_BGR: { V4L2_PIX_FMT_VYUY, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_BGR24; { V4L2_PIX_FMT_YUV422P, SPA_VIDEO_FORMAT_Y42B, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_YUV411P, SPA_VIDEO_FORMAT_Y41B, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_xRGB: { V4L2_PIX_FMT_Y41P, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_RGB32; { V4L2_PIX_FMT_YUV444, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_YUV555, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_ARGB: { V4L2_PIX_FMT_YUV565, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_RGB32; { V4L2_PIX_FMT_YUV32, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_YUV410, SPA_VIDEO_FORMAT_YUV9, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_BGRx: { V4L2_PIX_FMT_YUV420, SPA_VIDEO_FORMAT_I420, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_BGR32; { V4L2_PIX_FMT_YUV420M, SPA_VIDEO_FORMAT_I420, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_HI240, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_BGRA: { V4L2_PIX_FMT_HM12, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_BGR32; { V4L2_PIX_FMT_M420, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break;
case SPA_VIDEO_FORMAT_GRAY8: /* two planes -- one Y, one Cr + Cb interleaved */
fourcc = V4L2_PIX_FMT_GREY; { V4L2_PIX_FMT_NV12, SPA_VIDEO_FORMAT_NV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_NV12M, SPA_VIDEO_FORMAT_NV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_GRAY16_LE: { V4L2_PIX_FMT_NV12MT, SPA_VIDEO_FORMAT_NV12_64Z32, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_Y16; { V4L2_PIX_FMT_NV12MT_16X16, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_NV21, SPA_VIDEO_FORMAT_NV21, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
case SPA_VIDEO_FORMAT_GRAY16_BE: { V4L2_PIX_FMT_NV21M, SPA_VIDEO_FORMAT_NV21, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = V4L2_PIX_FMT_Y16_BE; { V4L2_PIX_FMT_NV16, SPA_VIDEO_FORMAT_NV16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { V4L2_PIX_FMT_NV16M, SPA_VIDEO_FORMAT_NV16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
default: { V4L2_PIX_FMT_NV61, SPA_VIDEO_FORMAT_NV61, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
fourcc = 0; { V4L2_PIX_FMT_NV61M, SPA_VIDEO_FORMAT_NV61, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
break; { 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 #define FOURCC_ARGS(f) (f)&0x7f,((f)>>8)&0x7f,((f)>>16)&0x7f,((f)>>24)&0x7f
static SpaResult static SpaResult
spa_v4l2_enum_format (SpaV4l2Source *this, SpaFormat **format, void **cookie) spa_v4l2_enum_format (SpaV4l2Source *this, SpaFormat **format, void **cookie)
{ {
SpaV4l2State *state = &this->state[0]; SpaV4l2State *state = &this->state[0];
int res; int res, i, pi;
V4l2Format *fmt;
const FormatInfo *info;
if (spa_v4l2_open (this) < 0) if (spa_v4l2_open (this) < 0)
return SPA_RESULT_ERROR; return SPA_RESULT_ERROR;
@ -272,13 +228,9 @@ spa_v4l2_enum_format (SpaV4l2Source *this, SpaFormat **format, void **cookie)
state->fmtdesc.index = 0; state->fmtdesc.index = 0;
state->fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; state->fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
state->next_fmtdesc = true; state->next_fmtdesc = true;
CLEAR (state->frmsize); CLEAR (state->frmsize);
state->next_frmsize = true; state->next_frmsize = true;
CLEAR (state->frmival); CLEAR (state->frmival);
state->next_frmival = true;
*cookie = state; *cookie = state;
} }
@ -295,6 +247,13 @@ again:
state->frmsize.pixel_format = state->fmtdesc.pixelformat; state->frmsize.pixel_format = state->fmtdesc.pixelformat;
state->next_frmsize = true; 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 (state->next_frmsize) {
if ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMESIZES, &state->frmsize)) < 0) { if ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMESIZES, &state->frmsize)) < 0) {
if (errno == EINVAL) { if (errno == EINVAL) {
@ -312,68 +271,112 @@ again:
state->frmival.pixel_format = state->frmsize.pixel_format; state->frmival.pixel_format = state->frmsize.pixel_format;
state->frmival.width = state->frmsize.discrete.width; state->frmival.width = state->frmsize.discrete.width;
state->frmival.height = state->frmsize.discrete.height; 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 ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMEINTERVALS, &state->frmival)) < 0) {
if (errno == EINVAL) { if (errno == EINVAL) {
state->frmsize.index++; state->frmsize.index++;
state->next_frmsize = true; state->next_frmsize = true;
goto again; break;
} }
perror ("VIDIOC_ENUM_FRAMEINTERVALS"); perror ("VIDIOC_ENUM_FRAMEINTERVALS");
return SPA_RESULT_ENUM_END; 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]); *format = &state->format[0].fmt;
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;
return SPA_RESULT_OK; return SPA_RESULT_OK;
} }
static int 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]; SpaV4l2State *state = &this->state[0];
int cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT; int cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
struct v4l2_format reqfmt, fmt; struct v4l2_format reqfmt, fmt;
struct v4l2_streamparm streamparm; struct v4l2_streamparm streamparm;
const FormatInfo *info = NULL;
int i;
CLEAR (fmt); CLEAR (fmt);
CLEAR (streamparm); CLEAR (streamparm);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { for (i = 0; i < SPA_N_ELEMENTS (format_info); i++) {
if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { if (format_info[i].media_type == f->fmt.media_type &&
SpaVideoRawFormat *f = (SpaVideoRawFormat *) format; format_info[i].media_subtype == f->fmt.media_subtype &&
format_info[i].format == f->format) {
fmt.fmt.pix.pixelformat = video_format_to_fourcc (f->info.format); info = &format_info[i];
fmt.fmt.pix.width = f->info.size.width; break;
fmt.fmt.pix.height = f->info.size.height; }
fmt.fmt.pix.field = V4L2_FIELD_ANY; }
streamparm.parm.capture.timeperframe.numerator = f->info.framerate.denom; if (info == NULL)
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
return -1; 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; reqfmt = fmt;
if (spa_v4l2_open (this) < 0) 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->source = this;
b->buffer.refcount = 0; b->buffer.refcount = 0;
b->buffer.notify = v4l2_buffer_free; b->buffer.notify = v4l2_buffer_free;
b->buffer.id = i;
b->buffer.size = buffers[i]->size; b->buffer.size = buffers[i]->size;
b->buffer.n_metas = buffers[i]->n_metas; b->buffer.n_metas = buffers[i]->n_metas;
b->buffer.metas = buffers[i]->metas; b->buffer.metas = buffers[i]->metas;
@ -600,6 +604,7 @@ mmap_init (SpaV4l2Source *this)
b->source = this; b->source = this;
b->buffer.refcount = 0; b->buffer.refcount = 0;
b->buffer.notify = v4l2_buffer_free; b->buffer.notify = v4l2_buffer_free;
b->buffer.id = i;
b->buffer.size = buf.length; b->buffer.size = buf.length;
b->buffer.n_metas = 1; b->buffer.n_metas = 1;
b->buffer.metas = b->metas; b->buffer.metas = b->metas;

View file

@ -269,50 +269,53 @@ alloc_buffers (AppData *data)
} }
#endif #endif
typedef struct {
SpaFormat fmt;
SpaPropInfo infos[3];
SpaVideoFormat format;
SpaRectangle size;
SpaFraction framerate;
} VideoFormat;
static SpaResult static SpaResult
negotiate_formats (AppData *data) negotiate_formats (AppData *data)
{ {
SpaResult res; SpaResult res;
SpaFormat *format;
SpaProps *props;
uint32_t val;
SpaFraction frac;
SpaPropValue value;
const SpaPortInfo *info; const SpaPortInfo *info;
SpaRectangle size; VideoFormat f;
#if 0
void *state = NULL; void *state = NULL;
if ((res = data->source_node->port_enum_formats (data->source, 0, &format, NULL, &state)) < 0) if ((res = data->source_node->port_enum_formats (data->source, 0, &format, NULL, &state)) < 0)
return res; 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; spa_video_raw_fill_prop_info (&f.infos[1],
value.size = sizeof (uint32_t); SPA_PROP_ID_VIDEO_SIZE,
value.value = &val; offsetof (VideoFormat, size));
f.size.width = 320;
f.size.height = 240;
val = SPA_VIDEO_FORMAT_YUY2; spa_video_raw_fill_prop_info (&f.infos[2],
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FORMAT), &value)) < 0) SPA_PROP_ID_VIDEO_FRAMERATE,
return res; offsetof (VideoFormat, framerate));
f.framerate.num = 25;
f.framerate.denom = 1;
#endif
value.type = SPA_PROP_TYPE_RECTANGLE; if ((res = data->source_node->port_set_format (data->source, 0, false, &f.fmt)) < 0)
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)
return res; return res;
if ((res = data->source_node->port_get_info (data->source, 0, &info)) < 0) if ((res = data->source_node->port_get_info (data->source, 0, &info)) < 0)

View file

@ -38,6 +38,8 @@ struct media_subtype_name {
} media_subtype_names[] = { } media_subtype_names[] = {
{ "unknown" }, { "unknown" },
{ "raw" }, { "raw" },
{ "h264" },
{ "mjpg" },
}; };
struct prop_type_name { struct prop_type_name {
@ -64,8 +66,25 @@ struct prop_type_name {
}; };
static void 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) { switch (type) {
case SPA_PROP_TYPE_INVALID: case SPA_PROP_TYPE_INVALID:
printf ("invalid"); printf ("invalid");
@ -104,7 +123,10 @@ print_value (SpaPropType type, int size, const void *value)
printf ("%g", *(double *)value); printf ("%g", *(double *)value);
break; break;
case SPA_PROP_TYPE_STRING: case SPA_PROP_TYPE_STRING:
printf ("\"%s\"", (char *)value); if (enum_string)
printf ("%s", (char *)value);
else
printf ("\"%s\"", (char *)value);
break; break;
case SPA_PROP_TYPE_RECTANGLE: case SPA_PROP_TYPE_RECTANGLE:
{ {
@ -157,7 +179,7 @@ print_props (const SpaProps *props, int print_ranges)
printf ("Default: "); printf ("Default: ");
if (info->default_value) if (info->default_value)
print_value (info->type, info->default_size, info->default_value); print_value (info, info->default_size, info->default_value);
else else
printf ("None"); printf ("None");
@ -165,7 +187,7 @@ print_props (const SpaProps *props, int print_ranges)
printf (". Current: "); printf (". Current: ");
if (res == SPA_RESULT_OK) 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) else if (res == SPA_RESULT_PROPERTY_UNSET)
printf ("Unset"); printf ("Unset");
else else
@ -199,7 +221,7 @@ print_props (const SpaProps *props, int print_ranges)
for (j = 0; j < info->n_range_values; j++) { for (j = 0; j < info->n_range_values; j++) {
const SpaPropRangeInfo *rinfo = &info->range_values[j]; const SpaPropRangeInfo *rinfo = &info->range_values[j];
printf ("%-23.23s ", ""); 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); printf ("\t: %-12s - %s \n", rinfo->name, rinfo->description);
} }
} }
@ -219,7 +241,7 @@ print_format (const SpaFormat *format)
const SpaProps *props = &format->props; const SpaProps *props = &format->props;
int i; 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); media_subtype_names[format->media_subtype].name);
for (i = 0; i < props->n_prop_info; i++) { 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); printf (" %20s : (%s) ", info->name, prop_type_names[info->type].name);
if (res == SPA_RESULT_OK) { 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) { } 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 { } else {
printf ("*Error*"); printf ("*Error*");
} }