From 6ab8af91e091c5c556291109894e645a1d063836 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 13 Jul 2016 18:29:55 +0200 Subject: [PATCH] More hacking Add spa based v4l2 pinos module Add allocation params to port_alloc_buffers Let the app do allocation for handles. --- pinos/Makefile.am | 1 + pinos/daemon/main.c | 1 + pinos/modules/spa/spa-alsa-sink.c | 6 +- pinos/modules/spa/spa-v4l2-source.c | 645 +++++++++++++ pinos/modules/spa/spa-v4l2-source.h | 61 ++ spa/include/spa/buffer.h | 14 + spa/include/spa/event.h | 4 +- spa/include/spa/node.h | 2 + spa/include/spa/plugin.h | 17 +- spa/include/spa/port.h | 51 +- spa/lib/debug.c | 80 ++ spa/lib/meson.build | 1 + spa/meson.build | 4 + spa/plugins/alsa/alsa-sink.c | 44 +- spa/plugins/alsa/alsa.c | 56 +- spa/plugins/audiomixer/audiomixer.c | 43 +- spa/plugins/audiomixer/plugin.c | 58 +- spa/plugins/audiotestsrc/audiotestsrc.c | 43 +- spa/plugins/audiotestsrc/plugin.c | 58 +- spa/plugins/ffmpeg/ffmpeg-dec.c | 529 +++++++++++ spa/plugins/ffmpeg/ffmpeg-enc.c | 529 +++++++++++ spa/plugins/ffmpeg/ffmpeg.c | 109 +++ spa/plugins/ffmpeg/meson.build | 10 + spa/plugins/libva/libva-dec.c | 530 +++++++++++ spa/plugins/libva/libva-enc.c | 530 +++++++++++ spa/plugins/libva/libva.c | 96 ++ spa/plugins/libva/meson.build | 10 + spa/plugins/libva/vaapi-recorder.c | 1161 +++++++++++++++++++++++ spa/plugins/libva/vaapi-recorder.h | 38 + spa/plugins/meson.build | 2 + spa/plugins/v4l2/v4l2-source.c | 46 +- spa/plugins/v4l2/v4l2-utils.c | 17 +- spa/plugins/v4l2/v4l2.c | 58 +- spa/plugins/volume/plugin.c | 58 +- spa/plugins/volume/volume.c | 43 +- spa/plugins/xv/xv-sink.c | 44 +- spa/plugins/xv/xv.c | 58 +- spa/tests/meson.build | 2 + spa/tests/test-mixer.c | 7 +- spa/tests/test-v4l2.c | 15 +- spa/tools/spa-inspect.c | 3 +- 41 files changed, 4733 insertions(+), 351 deletions(-) create mode 100644 pinos/modules/spa/spa-v4l2-source.c create mode 100644 pinos/modules/spa/spa-v4l2-source.h create mode 100644 spa/lib/debug.c create mode 100644 spa/plugins/ffmpeg/ffmpeg-dec.c create mode 100644 spa/plugins/ffmpeg/ffmpeg-enc.c create mode 100644 spa/plugins/ffmpeg/ffmpeg.c create mode 100644 spa/plugins/ffmpeg/meson.build create mode 100644 spa/plugins/libva/libva-dec.c create mode 100644 spa/plugins/libva/libva-enc.c create mode 100644 spa/plugins/libva/libva.c create mode 100644 spa/plugins/libva/meson.build create mode 100644 spa/plugins/libva/vaapi-recorder.c create mode 100644 spa/plugins/libva/vaapi-recorder.h diff --git a/pinos/Makefile.am b/pinos/Makefile.am index bfc04a3ee..db25d8e32 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -223,6 +223,7 @@ libpinoscore_@PINOS_MAJORMINOR@_la_SOURCES = \ modules/gst/gst-sink.c modules/gst/gst-sink.h \ modules/gst/gst-node-factory.c modules/gst/gst-node-factory.h \ modules/spa/spa-alsa-sink.c modules/spa/spa-alsa-sink.h \ + modules/spa/spa-v4l2-source.c modules/spa/spa-v4l2-source.h \ dbus/org-pinos.c dbus/org-pinos.h libpinoscore_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) diff --git a/pinos/daemon/main.c b/pinos/daemon/main.c index 113ac532c..71f3b823c 100644 --- a/pinos/daemon/main.c +++ b/pinos/daemon/main.c @@ -46,6 +46,7 @@ main (gint argc, gchar *argv[]) pinos_daemon_add_node_factory (daemon, factory); pinos_spa_alsa_sink_new (daemon, "alsa-sink", NULL); + pinos_spa_v4l2_source_new (daemon, "v4l2-source", NULL); pinos_daemon_start (daemon); g_main_loop_run (loop); diff --git a/pinos/modules/spa/spa-alsa-sink.c b/pinos/modules/spa/spa-alsa-sink.c index 600b6e642..777344587 100644 --- a/pinos/modules/spa/spa-alsa-sink.c +++ b/pinos/modules/spa/spa-alsa-sink.c @@ -146,7 +146,7 @@ on_sink_event (SpaHandle *handle, SpaEvent *event, void *user_data) iinfo.event = oinfo.event; g_debug ("push sink %p", iinfo.buffer); - if ((res = priv->sink_node->push_port_input (priv->sink, 1, &iinfo)) < 0) + if ((res = priv->sink_node->port_push_input (priv->sink, 1, &iinfo)) < 0) g_debug ("got error %d", res); break; } @@ -314,7 +314,7 @@ negotiate_formats (PinosSpaAlsaSink *this) uint32_t val; SpaPropValue value; - if ((res = priv->sink_node->enum_port_formats (priv->sink, 0, 0, &format)) < 0) + if ((res = priv->sink_node->port_enum_formats (priv->sink, 0, 0, &format)) < 0) return res; props = &format->props; @@ -336,7 +336,7 @@ negotiate_formats (PinosSpaAlsaSink *this) if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_CHANNELS), &value)) < 0) return res; - if ((res = priv->sink_node->set_port_format (priv->sink, 0, 0, format)) < 0) + if ((res = priv->sink_node->port_set_format (priv->sink, 0, 0, format)) < 0) return res; priv->ringbuffer = pinos_ringbuffer_new (PINOS_RINGBUFFER_MODE_READ, 64 * 1024); diff --git a/pinos/modules/spa/spa-v4l2-source.c b/pinos/modules/spa/spa-v4l2-source.c new file mode 100644 index 000000000..ff0b28a0f --- /dev/null +++ b/pinos/modules/spa/spa-v4l2-source.c @@ -0,0 +1,645 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "spa-v4l2-source.h" + +#define PINOS_SPA_V4L2_SOURCE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SPA_V4L2_SOURCE, PinosSpaV4l2SourcePrivate)) + +typedef struct { + PinosSpaV4l2Source *source; + + gboolean have_format; + PinosServerPort *port; +} SourcePortData; + +struct _PinosSpaV4l2SourcePrivate +{ + SpaHandle *source; + const SpaNode *source_node; + + SpaPollFd fds[16]; + unsigned int n_fds; + SpaPollItem poll; + + gboolean running; + pthread_t thread; + + const void *format; + + GList *ports; + PinosFdManager *fdmanager; +}; + +enum { + PROP_0, +}; + +G_DEFINE_TYPE (PinosSpaV4l2Source, pinos_spa_v4l2_source, PINOS_TYPE_SERVER_NODE); + +static SpaResult +make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char *name) +{ + SpaResult res; + void *hnd; + SpaEnumHandleFactoryFunc enum_func; + unsigned int i; + + if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) { + g_error ("can't load %s: %s", lib, dlerror()); + return SPA_RESULT_ERROR; + } + if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) { + g_error ("can't find enum function"); + return SPA_RESULT_ERROR; + } + + for (i = 0; ;i++) { + const SpaHandleFactory *factory; + const void *iface; + + if ((res = enum_func (i, &factory)) < 0) { + if (res != SPA_RESULT_ENUM_END) + g_error ("can't enumerate factories: %d", res); + break; + } + if (strcmp (factory->name, name)) + continue; + + if ((res = factory->instantiate (factory, handle)) < 0) { + g_error ("can't make factory instance: %d", res); + return res; + } + if ((res = (*handle)->get_interface (*handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) { + g_error ("can't get interface %d", res); + return res; + } + *node = iface; + return SPA_RESULT_OK; + } + 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, result; + void *p; + + fd = mkostemp (filename, O_CLOEXEC); + if (fd == -1) { + g_debug ("Failed to create temporary file: %s", strerror (errno)); + return -1; + } + unlink (filename); + + result = ftruncate (fd, size); + if (result == -1) { + g_debug ("Failed to resize temporary file: %s", strerror (errno)); + close (fd); + return -1; + } + p = mmap (0, size, PROT_WRITE, MAP_SHARED, fd, 0); + memcpy (p, data, size); + munmap (p, size); + + return fd; +} + +static void +on_source_event (SpaHandle *handle, SpaEvent *event, void *user_data) +{ + PinosSpaV4l2Source *source = user_data; + PinosSpaV4l2SourcePrivate *priv = source->priv; + + switch (event->type) { + case SPA_EVENT_TYPE_CAN_PULL_OUTPUT: + { + SpaOutputInfo info[1] = { 0, }; + SpaResult res; + SpaBuffer *b; + PinosBuffer pbuf; + PinosBufferBuilder builder; + PinosPacketHeader hdr; + PinosPacketFDPayload p; + GList *walk; + gint fd; + guint8 buf[1024]; + gint fdbuf[8]; + + if ((res = priv->source_node->port_pull_output (priv->source, 1, info)) < 0) + g_debug ("spa-v4l2-source %p: got pull error %d", source, res); + + b = info[0].buffer; + + hdr.flags = 0; + hdr.seq = 0; + hdr.pts = 0; + hdr.dts_offset = 0; + + pinos_buffer_builder_init_into (&builder, buf, 1024, fdbuf, 8); + pinos_buffer_builder_add_header (&builder, &hdr); + + fd = tmpfile_create (source, b->datas[0].data, b->size); + p.fd_index = pinos_buffer_builder_add_fd (&builder, fd); + p.id = pinos_fd_manager_get_id (priv->fdmanager); + p.offset = 0; + p.size = b->size; + pinos_buffer_builder_add_fd_payload (&builder, &p); + pinos_buffer_builder_end (&builder, &pbuf); + + for (walk = priv->ports; walk; walk = g_list_next (walk)) { + SourcePortData *data = walk->data; + GError *error = NULL; + + if (!data->have_format) + send_format (source, data); + + if (!pinos_port_send_buffer (PINOS_PORT (data->port), &pbuf, &error)) { + g_debug ("send failed: %s", error->message); + g_clear_error (&error); + } + } + pinos_buffer_unref (&pbuf); + + spa_buffer_unref (b); + break; + } + + case SPA_EVENT_TYPE_ADD_POLL: + { + SpaPollItem *poll = event->data; + + priv->poll = *poll; + priv->fds[0] = poll->fds[0]; + priv->n_fds = 1; + priv->poll.fds = priv->fds; + break; + } + default: + g_debug ("got event %d", event->type); + break; + } +} + +static void +create_pipeline (PinosSpaV4l2Source *this) +{ + PinosSpaV4l2SourcePrivate *priv = this->priv; + SpaResult res; + SpaProps *props; + SpaPropValue value; + + if ((res = make_node (&priv->source, + &priv->source_node, + "spa/build/plugins/v4l2/libspa-v4l2.so", + "v4l2-source")) < 0) { + g_error ("can't create v4l2-source: %d", res); + return; + } + priv->source_node->set_event_callback (priv->source, on_source_event, this); + + if ((res = priv->source_node->get_props (priv->source, &props)) < 0) + g_debug ("got get_props error %d", res); + + value.type = SPA_PROP_TYPE_STRING; + value.value = "/dev/video1"; + value.size = strlen (value.value)+1; + props->set_prop (props, spa_props_index_for_name (props, "device"), &value); + + if ((res = priv->source_node->set_props (priv->source, props)) < 0) + g_debug ("got set_props error %d", res); +} + +static SpaResult +negotiate_formats (PinosSpaV4l2Source *this) +{ + PinosSpaV4l2SourcePrivate *priv = this->priv; + SpaResult res; + SpaFormat *format; + SpaProps *props; + uint32_t val; + SpaPropValue value; + SpaFraction frac; + + if ((res = priv->source_node->port_enum_formats (priv->source, 0, 0, &format)) < 0) + return res; + + props = &format->props; + + value.type = SPA_PROP_TYPE_UINT32; + value.size = sizeof (uint32_t); + value.value = &val; + + val = SPA_VIDEO_FORMAT_YUY2; + if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FORMAT), &value)) < 0) + return res; + val = 320; + if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_WIDTH), &value)) < 0) + return res; + val = 240; + if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_HEIGHT), &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 = priv->source_node->port_set_format (priv->source, 0, 0, format)) < 0) + return res; + + priv->format = "video/x-raw," + " format=(string)YUY2," + " width=(int)320," + " height=(int)240," + " framerate=(fraction)30/1"; + + return SPA_RESULT_OK; +} + +static void * +loop (void *user_data) +{ + PinosSpaV4l2Source *this = user_data; + PinosSpaV4l2SourcePrivate *priv = this->priv; + int r; + + g_debug ("spa-v4l2-source %p: enter thread", this); + while (priv->running) { + SpaPollNotifyData ndata; + + r = poll ((struct pollfd *) priv->fds, priv->n_fds, -1); + if (r < 0) { + if (errno == EINTR) + continue; + break; + } + if (r == 0) { + g_debug ("spa-v4l2-source %p: select timeout", this); + break; + } + if (priv->poll.after_cb) { + ndata.fds = priv->poll.fds; + ndata.n_fds = priv->poll.n_fds; + ndata.user_data = priv->poll.user_data; + priv->poll.after_cb (&ndata); + } + } + g_debug ("spa-v4l2-source %p: leave thread", this); + return NULL; +} + +static void +start_pipeline (PinosSpaV4l2Source *source) +{ + PinosSpaV4l2SourcePrivate *priv = source->priv; + SpaResult res; + SpaCommand cmd; + int err; + + g_debug ("spa-v4l2-source %p: starting pipeline", source); + negotiate_formats (source); + + cmd.type = SPA_COMMAND_START; + if ((res = priv->source_node->send_command (priv->source, &cmd)) < 0) + g_debug ("got error %d", res); + + priv->running = true; + if ((err = pthread_create (&priv->thread, NULL, loop, source)) != 0) { + g_debug ("spa-v4l2-source %p: can't create thread", strerror (err)); + priv->running = false; + } +} + +static void +stop_pipeline (PinosSpaV4l2Source *source) +{ + PinosSpaV4l2SourcePrivate *priv = source->priv; + SpaResult res; + SpaCommand cmd; + + g_debug ("spa-v4l2-source %p: stopping pipeline", source); + + if (priv->running) { + priv->running = false; + pthread_join (priv->thread, NULL); + } + + cmd.type = SPA_COMMAND_STOP; + if ((res = priv->source_node->send_command (priv->source, &cmd)) < 0) + g_debug ("got error %d", res); +} + +static void +destroy_pipeline (PinosSpaV4l2Source *source) +{ + g_debug ("spa-v4l2-source %p: destroy pipeline", source); +} + +static gboolean +set_state (PinosNode *node, + PinosNodeState state) +{ + PinosSpaV4l2Source *this = PINOS_SPA_V4L2_SOURCE (node); + PinosSpaV4l2SourcePrivate *priv = this->priv; + + g_debug ("spa-source %p: set state %s", node, pinos_node_state_as_string (state)); + + switch (state) { + case PINOS_NODE_STATE_SUSPENDED: + break; + + case PINOS_NODE_STATE_INITIALIZING: + break; + + case PINOS_NODE_STATE_IDLE: + stop_pipeline (this); + break; + + case PINOS_NODE_STATE_RUNNING: + start_pipeline (this); + break; + + case PINOS_NODE_STATE_ERROR: + break; + } + pinos_node_update_state (node, state); + return TRUE; +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PinosSpaV4l2Source *source = PINOS_SPA_V4L2_SOURCE (object); + PinosSpaV4l2SourcePrivate *priv = source->priv; + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PinosSpaV4l2Source *source = PINOS_SPA_V4L2_SOURCE (object); + PinosSpaV4l2SourcePrivate *priv = source->priv; + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +on_linked (PinosPort *port, PinosPort *peer, gpointer user_data) +{ + SourcePortData *data = user_data; + PinosSpaV4l2Source *source = data->source; + PinosSpaV4l2SourcePrivate *priv = source->priv; + guint n_links; + + pinos_port_get_links (port, &n_links); + g_debug ("port %p: linked, now %d", port, n_links); + if (n_links == 0) + pinos_node_report_busy (PINOS_NODE (source)); + + return TRUE; +} + +static void +on_unlinked (PinosPort *port, PinosPort *peer, gpointer user_data) +{ + SourcePortData *data = user_data; + PinosSpaV4l2Source *source = data->source; + PinosSpaV4l2SourcePrivate *priv = source->priv; + guint n_links; + + pinos_port_get_links (port, &n_links); + g_debug ("port %p: unlinked, now %d", port, n_links); + if (n_links == 1) + pinos_node_report_busy (PINOS_NODE (source)); +} + +static void +on_received_buffer (PinosPort *port, + gpointer user_data) +{ + PinosSpaV4l2Source *this = user_data; + PinosSpaV4l2SourcePrivate *priv = this->priv; + PinosBuffer *pbuf; + PinosBufferIter it; + + pbuf = pinos_port_peek_buffer (port); + + 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); +} + +static void +free_source_port_data (SourcePortData *data) +{ + PinosSpaV4l2Source *source = data->source; + PinosSpaV4l2SourcePrivate *priv = source->priv; + + g_slice_free (SourcePortData, data); +} + +static void +remove_port (PinosNode *node, + PinosPort *port) +{ + PinosSpaV4l2Source *source = PINOS_SPA_V4L2_SOURCE (node); + PinosSpaV4l2SourcePrivate *priv = source->priv; + GList *walk; + + for (walk = priv->ports; walk; walk = g_list_next (walk)) { + SourcePortData *data = walk->data; + + if (data->port == PINOS_SERVER_PORT_CAST (port)) { + free_source_port_data (data); + priv->ports = g_list_delete_link (priv->ports, walk); + break; + } + } + if (priv->ports == NULL) + pinos_node_report_idle (node); +} + +static void +source_constructed (GObject * object) +{ + PinosSpaV4l2Source *source = PINOS_SPA_V4L2_SOURCE (object); + PinosSpaV4l2SourcePrivate *priv = source->priv; + + G_OBJECT_CLASS (pinos_spa_v4l2_source_parent_class)->constructed (object); + + create_pipeline (source); +} + +static void +source_finalize (GObject * object) +{ + PinosSpaV4l2Source *source = PINOS_SPA_V4L2_SOURCE (object); + PinosSpaV4l2SourcePrivate *priv = source->priv; + + g_debug ("spa-source %p: dispose", source); + destroy_pipeline (source); + + G_OBJECT_CLASS (pinos_spa_v4l2_source_parent_class)->finalize (object); +} + +static PinosServerPort * +create_port_sync (PinosServerNode *node, + PinosDirection direction, + const gchar *name, + GBytes *possible_formats, + PinosProperties *props) +{ + PinosSpaV4l2Source *source = PINOS_SPA_V4L2_SOURCE (node); + PinosSpaV4l2SourcePrivate *priv = source->priv; + SourcePortData *data; + + data = g_slice_new0 (SourcePortData); + data->source = source; + data->have_format = FALSE; + + data->port = PINOS_SERVER_NODE_CLASS (pinos_spa_v4l2_source_parent_class) + ->create_port_sync (node, + direction, + name, + possible_formats, + props); + + pinos_port_set_received_buffer_cb (PINOS_PORT (data->port), on_received_buffer, source, NULL); + + g_debug ("connecting signals"); + g_signal_connect (data->port, "linked", (GCallback) on_linked, data); + g_signal_connect (data->port, "unlinked", (GCallback) on_unlinked, data); + + priv->ports = g_list_append (priv->ports, data); + + return data->port; +} + +static void +pinos_spa_v4l2_source_class_init (PinosSpaV4l2SourceClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + PinosNodeClass *node_class = PINOS_NODE_CLASS (klass); + PinosServerNodeClass *server_node_class = PINOS_SERVER_NODE_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PinosSpaV4l2SourcePrivate)); + + gobject_class->constructed = source_constructed; + gobject_class->finalize = source_finalize; + gobject_class->get_property = get_property; + gobject_class->set_property = set_property; + + node_class->set_state = set_state; + node_class->remove_port = remove_port; + + server_node_class->create_port_sync = create_port_sync; +} + +static void +pinos_spa_v4l2_source_init (PinosSpaV4l2Source * source) +{ + PinosSpaV4l2SourcePrivate *priv = source->priv = PINOS_SPA_V4L2_SOURCE_GET_PRIVATE (source); + + priv->fdmanager = pinos_fd_manager_get (PINOS_FD_MANAGER_DEFAULT); + +} + +PinosServerNode * +pinos_spa_v4l2_source_new (PinosDaemon *daemon, + const gchar *name, + PinosProperties *properties) +{ + PinosServerNode *node; + + node = g_object_new (PINOS_TYPE_SPA_V4L2_SOURCE, + "daemon", daemon, + "name", name, + "properties", properties, + NULL); + + return node; +} diff --git a/pinos/modules/spa/spa-v4l2-source.h b/pinos/modules/spa/spa-v4l2-source.h new file mode 100644 index 000000000..05da8c68b --- /dev/null +++ b/pinos/modules/spa/spa-v4l2-source.h @@ -0,0 +1,61 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PINOS_SPA_V4L2_SOURCE_H__ +#define __PINOS_SPA_V4L2_SOURCE_H__ + +#include + +#include +#include + +G_BEGIN_DECLS + +#define PINOS_TYPE_SPA_V4L2_SOURCE (pinos_spa_v4l2_source_get_type ()) +#define PINOS_IS_SPA_V4L2_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SPA_V4L2_SOURCE)) +#define PINOS_IS_SPA_V4L2_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SPA_V4L2_SOURCE)) +#define PINOS_SPA_V4L2_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SPA_V4L2_SOURCE, PinosSpaV4l2SourceClass)) +#define PINOS_SPA_V4L2_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SPA_V4L2_SOURCE, PinosSpaV4l2Source)) +#define PINOS_SPA_V4L2_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SPA_V4L2_SOURCE, PinosSpaV4l2SourceClass)) +#define PINOS_SPA_V4L2_SOURCE_CAST(obj) ((PinosSpaV4l2Source*)(obj)) +#define PINOS_SPA_V4L2_SOURCE_CLASS_CAST(klass) ((PinosSpaV4l2SourceClass*)(klass)) + +typedef struct _PinosSpaV4l2Source PinosSpaV4l2Source; +typedef struct _PinosSpaV4l2SourceClass PinosSpaV4l2SourceClass; +typedef struct _PinosSpaV4l2SourcePrivate PinosSpaV4l2SourcePrivate; + +struct _PinosSpaV4l2Source { + PinosServerNode object; + + PinosSpaV4l2SourcePrivate *priv; +}; + +struct _PinosSpaV4l2SourceClass { + PinosServerNodeClass parent_class; +}; + +GType pinos_spa_v4l2_source_get_type (void); + +PinosServerNode * pinos_spa_v4l2_source_new (PinosDaemon *daemon, + const gchar *name, + PinosProperties *properties); + +G_END_DECLS + +#endif /* __PINOS_SPA_V4L2_SOURCE_H__ */ diff --git a/spa/include/spa/buffer.h b/spa/include/spa/buffer.h index d7c4b04f4..1540be550 100644 --- a/spa/include/spa/buffer.h +++ b/spa/include/spa/buffer.h @@ -25,6 +25,7 @@ extern "C" { #endif typedef struct _SpaBuffer SpaBuffer; +typedef struct _SpaBufferGroup SpaBufferGroup; #include @@ -37,6 +38,7 @@ typedef enum { SPA_META_TYPE_INVALID = 0, SPA_META_TYPE_HEADER, SPA_META_TYPE_POINTER, + SPA_META_TYPE_VIDEO_CROP, } SpaMetaType; /** @@ -72,6 +74,18 @@ typedef struct { void *ptr; } SpaMetaPointer; +/** + * SpaMetaVideoCrop: + * @x: + * @y: + * @width: + * @height + */ +typedef struct { + int x, y; + int width, height; +} SpaMetaVideoCrop; + /** * SpaMeta: * @type: metadata type diff --git a/spa/include/spa/event.h b/spa/include/spa/event.h index f3037be59..2d28dc4e2 100644 --- a/spa/include/spa/event.h +++ b/spa/include/spa/event.h @@ -34,7 +34,7 @@ typedef struct _SpaEvent SpaEvent; * @SPA_EVENT_TYPE_STARTED: emited when the START command completes * @SPA_EVENT_TYPE_STOPPED: emited when the STOP command completes * @SPA_EVENT_TYPE_CAN_PULL_OUTPUT: emited when an async node has output that can be pulled - * @SPA_EVENT_TYPE_CAN_PUSH_INTPUT: emited when more data can be pushed to an async node + * @SPA_EVENT_TYPE_CAN_PUSH_INPUT: emited when more data can be pushed to an async node * @SPA_EVENT_TYPE_PULL_INPUT: emited when data needs to be provided on an input * @SPA_EVENT_TYPE_ALLOC_OUTPUT: emited when an output buffer needs to be allocated * @SPA_EVENT_TYPE_ADD_POLL: emited when a pollfd should be added @@ -49,7 +49,7 @@ typedef enum { SPA_EVENT_TYPE_STARTED, SPA_EVENT_TYPE_STOPPED, SPA_EVENT_TYPE_CAN_PULL_OUTPUT, - SPA_EVENT_TYPE_CAN_PUSH_INTPUT, + SPA_EVENT_TYPE_CAN_PUSH_INPUT, SPA_EVENT_TYPE_PULL_INPUT, SPA_EVENT_TYPE_ALLOC_OUTPUT, SPA_EVENT_TYPE_ADD_POLL, diff --git a/spa/include/spa/node.h b/spa/include/spa/node.h index 74cf3f9f6..c3c04e3df 100644 --- a/spa/include/spa/node.h +++ b/spa/include/spa/node.h @@ -320,6 +320,8 @@ struct _SpaNode { uint32_t n_buffers); SpaResult (*port_alloc_buffers) (SpaHandle *handle, uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, SpaBuffer **buffers, uint32_t *n_buffers); diff --git a/spa/include/spa/plugin.h b/spa/include/spa/plugin.h index 9747d34b4..b5e037822 100644 --- a/spa/include/spa/plugin.h +++ b/spa/include/spa/plugin.h @@ -78,20 +78,27 @@ struct _SpaHandleFactory { * Extra information about the handles of this factory. */ const SpaProps * info; + /** + * SpaHandleFactory::size + * + * The size of handles from this factory + */ + const size_t size; /** - * SpaHandleFactory::instantiate + * SpaHandleFactory::init * @factory: a #SpaHandleFactory - * @handle: a pointer to hold the result + * @handle: a pointer to memory * - * Make an instance of this factory. + * Initialize an instance of this factory. The caller should allocate + * memory at least SpaHandleFactory::size bytes and pass this as @handle. * * Returns: #SPA_RESULT_OK on success * #SPA_RESULT_NOT_IMPLEMENTED when an instance can't be made * #SPA_RESULT_INVALID_ARGUMENTS when factory or handle are %NULL */ - SpaResult (*instantiate) (const SpaHandleFactory *factory, - SpaHandle **handle); + SpaResult (*init) (const SpaHandleFactory *factory, + SpaHandle *handle); /** * SpaHandle::enum_interface_info: * @factory: a #SpaHandleFactory diff --git a/spa/include/spa/port.h b/spa/include/spa/port.h index 866f2b4be..139b25b5f 100644 --- a/spa/include/spa/port.h +++ b/spa/include/spa/port.h @@ -25,6 +25,48 @@ extern "C" { #endif #include +#include + +/** + * SpaAllocParamType: + * @SPA_ALLOC_PARAM_TYPE_INVALID: invalid type, should be ignored + * @SPA_ALLOC_PARAM_TYPE_META_ENABLE: enable a certain metadata on buffers + * @SPA_ALLOC_PARAM_TYPE_VIDEO_PADDING: do specialized video padding + */ +typedef enum { + SPA_ALLOC_PARAM_TYPE_INVALID, + SPA_ALLOC_PARAM_TYPE_BUFFERS, + SPA_ALLOC_PARAM_TYPE_META_ENABLE, + SPA_ALLOC_PARAM_TYPE_VIDEO_PADDING, +} SpaAllocParamType; + +typedef struct { + uint32_t type; + size_t size; +} SpaAllocParam; + +typedef struct { + SpaAllocParam param; + size_t minsize; + size_t stride; + uint32_t min_buffers; + uint32_t max_buffers; + uint32_t align; +} SpaAllocParamBuffers; + +typedef struct { + SpaAllocParam param; + SpaMetaType type; +} SpaAllocParamMetaEnable; + +typedef struct { + SpaAllocParam param; + unsigned int padding_top; + unsigned int padding_bottom; + unsigned int padding_left; + unsigned int padding_right; + unsigned int stride_align[4]; +} SpaAllocParamVideoPadding; /** * SpaPortInfoFlags: @@ -58,18 +100,17 @@ typedef enum { * @maxbuffering: the maximum amount of bytes that the element will keep * around internally * @latency: latency on this port in nanoseconds + * @params: extra allocation parameters + * @n_params: number of elements in @params; * @features: NULL terminated array of extra port features * */ typedef struct { SpaPortInfoFlags flags; - size_t minsize; - size_t stride; - uint32_t min_buffers; - uint32_t max_buffers; - uint32_t align; unsigned int maxbuffering; uint64_t latency; + SpaAllocParam **params; + uint32_t n_params; const char **features; } SpaPortInfo; diff --git a/spa/lib/debug.c b/spa/lib/debug.c new file mode 100644 index 000000000..a8960070f --- /dev/null +++ b/spa/lib/debug.c @@ -0,0 +1,80 @@ +/* Simple Plugin API + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include "spa/debug.h" + +SpaResult +spa_debug_port_info (const SpaPortInfo *info) +{ + int i; + + if (info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + fprintf (stderr, "SpaPortInfo %p:\n", info); + fprintf (stderr, " flags: \t%08x\n", info->flags); + fprintf (stderr, " maxbuffering: \t%u\n", info->maxbuffering); + fprintf (stderr, " latency: \t%" PRIu64 "\n", info->latency); + fprintf (stderr, " n_params: \t%d\n", info->n_params); + for (i = 0; i < info->n_params; i++) { + SpaAllocParam *param = info->params[i]; + fprintf (stderr, " param %d, type %d, size %zd:\n", i, param->type, param->size); + switch (param->type) { + case SPA_ALLOC_PARAM_TYPE_INVALID: + fprintf (stderr, " INVALID\n"); + break; + case SPA_ALLOC_PARAM_TYPE_BUFFERS: + { + SpaAllocParamBuffers *p = (SpaAllocParamBuffers *)param; + fprintf (stderr, " SpaAllocParamBuffers:\n"); + fprintf (stderr, " minsize: \t\t%zd\n", p->minsize); + fprintf (stderr, " stride: \t\t%zd\n", p->stride); + fprintf (stderr, " min_buffers: \t%d\n", p->min_buffers); + fprintf (stderr, " max_buffers: \t%d\n", p->max_buffers); + fprintf (stderr, " align: \t\t%d\n", p->align); + break; + } + case SPA_ALLOC_PARAM_TYPE_META_ENABLE: + { + SpaAllocParamMetaEnable *p = (SpaAllocParamMetaEnable *)param; + fprintf (stderr, " SpaAllocParamMetaEnable:\n"); + fprintf (stderr, " type: \t%d\n", p->type); + break; + } + case SPA_ALLOC_PARAM_TYPE_VIDEO_PADDING: + { + SpaAllocParamVideoPadding *p = (SpaAllocParamVideoPadding *)param; + fprintf (stderr, " SpaAllocParamVideoPadding:\n"); + fprintf (stderr, " padding_top: \t%d\n", p->padding_top); + fprintf (stderr, " padding_bottom: \t%d\n", p->padding_bottom); + fprintf (stderr, " padding_left: \t%d\n", p->padding_left); + fprintf (stderr, " padding_right: \t%d\n", p->padding_right); + fprintf (stderr, " stide_align: \t[%d, %d, %d, %d]\n", + p->stride_align[0], p->stride_align[1], p->stride_align[2], p->stride_align[3]); + break; + } + default: + fprintf (stderr, " UNKNOWN\n"); + break; + } + } + return SPA_RESULT_OK; +} diff --git a/spa/lib/meson.build b/spa/lib/meson.build index d598b9b02..a155845b9 100644 --- a/spa/lib/meson.build +++ b/spa/lib/meson.build @@ -1,4 +1,5 @@ spalib_sources = ['audio-raw.c', + 'debug.c', 'props.c', 'ringbuffer.c', 'video-raw.c'] diff --git a/spa/meson.build b/spa/meson.build index 0160cca03..68c8f5c0f 100644 --- a/spa/meson.build +++ b/spa/meson.build @@ -4,6 +4,10 @@ alsa_dep = dependency('alsa') v4l2_dep = dependency('libv4l2') xv_dep = dependency('x11') sdl_dep = dependency('sdl2') +avcodec_dep = dependency('libavcodec') +avformat_dep = dependency('libavformat') +avfilter_dep = dependency('libavfilter') +libva_dep = dependency('libva') cc = meson.get_compiler('c') diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index fdff26754..f319fa5d4 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -474,6 +474,8 @@ spa_alsa_sink_node_port_use_buffers (SpaHandle *handle, static SpaResult spa_alsa_sink_node_port_alloc_buffers (SpaHandle *handle, uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, SpaBuffer **buffers, uint32_t *n_buffers) { @@ -572,13 +574,15 @@ spa_alsa_sink_get_interface (SpaHandle *handle, return SPA_RESULT_OK; } -SpaHandle * -spa_alsa_sink_new (void) +static SpaResult +alsa_sink_init (const SpaHandleFactory *factory, + SpaHandle *handle) { - SpaHandle *handle; SpaALSASink *this; - handle = calloc (1, sizeof (SpaALSASink)); + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + handle->get_interface = spa_alsa_sink_get_interface; this = (SpaALSASink *) handle; @@ -590,5 +594,35 @@ spa_alsa_sink_new (void) this->info.flags = SPA_PORT_INFO_FLAG_NONE; this->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT; - return handle; + + return SPA_RESULT_OK; } + +static const SpaInterfaceInfo alsa_sink_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +alsa_sink_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &alsa_sink_interfaces[index]; + + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_alsa_sink_factory = +{ "alsa-sink", + NULL, + sizeof (SpaALSASink), + alsa_sink_init, + alsa_sink_enum_interface_info, +}; diff --git a/spa/plugins/alsa/alsa.c b/spa/plugins/alsa/alsa.c index f6ebd2a15..58e9ef8c6 100644 --- a/spa/plugins/alsa/alsa.c +++ b/spa/plugins/alsa/alsa.c @@ -20,59 +20,21 @@ #include #include -SpaHandle * spa_alsa_sink_new (void); - -static SpaResult -alsa_sink_instantiate (const SpaHandleFactory *factory, - SpaHandle **handle) -{ - if (factory == NULL || handle == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; - - *handle = spa_alsa_sink_new (); - - return SPA_RESULT_OK; -} - -static const SpaInterfaceInfo alsa_sink_interfaces[] = -{ - { SPA_INTERFACE_ID_NODE, - SPA_INTERFACE_ID_NODE_NAME, - SPA_INTERFACE_ID_NODE_DESCRIPTION, - }, -}; - -static SpaResult -alsa_sink_enum_interface_info (const SpaHandleFactory *factory, - unsigned int index, - const SpaInterfaceInfo **info) -{ - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *info = &alsa_sink_interfaces[index]; - - return SPA_RESULT_OK; -} - -static const SpaHandleFactory factories[] = -{ - { "alsa-sink", - NULL, - alsa_sink_instantiate, - alsa_sink_enum_interface_info, - }, -}; - +extern const SpaHandleFactory spa_alsa_sink_factory; SpaResult spa_enum_handle_factory (unsigned int index, const SpaHandleFactory **factory) { - if (index >= 1) + if (factory == NULL) return SPA_RESULT_ENUM_END; - *factory = &factories[index]; - + switch (index) { + case 0: + *factory = &spa_alsa_sink_factory; + break; + default: + return SPA_RESULT_ENUM_END; + } return SPA_RESULT_OK; } diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c index 4c78e71e3..d4a9b583d 100644 --- a/spa/plugins/audiomixer/audiomixer.c +++ b/spa/plugins/audiomixer/audiomixer.c @@ -440,6 +440,8 @@ spa_audiomixer_node_port_use_buffers (SpaHandle *handle, static SpaResult spa_audiomixer_node_port_alloc_buffers (SpaHandle *handle, uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, SpaBuffer **buffers, uint32_t *n_buffers) { @@ -733,13 +735,15 @@ spa_audiomixer_get_interface (SpaHandle *handle, return SPA_RESULT_OK; } -SpaHandle * -spa_audiomixer_new (void) +static SpaResult +spa_audiomixer_init (const SpaHandleFactory *factory, + SpaHandle *handle) { - SpaHandle *handle; SpaAudioMixer *this; - handle = calloc (1, sizeof (SpaAudioMixer)); + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + handle->get_interface = spa_audiomixer_get_interface; this = (SpaAudioMixer *) handle; @@ -753,5 +757,34 @@ spa_audiomixer_new (void) this->ports[0].info.flags = SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER | SPA_PORT_INFO_FLAG_CAN_USE_BUFFER | SPA_PORT_INFO_FLAG_NO_REF; - return handle; + return SPA_RESULT_OK; } + +static const SpaInterfaceInfo audiomixer_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +audiomixer_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &audiomixer_interfaces[index]; + + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_audiomixer_factory = +{ "audiomixer", + NULL, + sizeof (SpaAudioMixer), + spa_audiomixer_init, + audiomixer_enum_interface_info, +}; diff --git a/spa/plugins/audiomixer/plugin.c b/spa/plugins/audiomixer/plugin.c index 4e8f626de..5ad38a2ff 100644 --- a/spa/plugins/audiomixer/plugin.c +++ b/spa/plugins/audiomixer/plugin.c @@ -20,59 +20,21 @@ #include #include -SpaHandle * spa_audiomixer_new (void); - -static SpaResult -audiomixer_instantiate (const SpaHandleFactory *factory, - SpaHandle **handle) -{ - if (factory == NULL || handle == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; - - *handle = spa_audiomixer_new (); - - return SPA_RESULT_OK; -} - -static const SpaInterfaceInfo audiomixer_interfaces[] = -{ - { SPA_INTERFACE_ID_NODE, - SPA_INTERFACE_ID_NODE_NAME, - SPA_INTERFACE_ID_NODE_DESCRIPTION, - }, -}; - -static SpaResult -audiomixer_enum_interface_info (const SpaHandleFactory *factory, - unsigned int index, - const SpaInterfaceInfo **info) -{ - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *info = &audiomixer_interfaces[index]; - - return SPA_RESULT_OK; -} - -static const SpaHandleFactory factories[] = -{ - { "audiomixer", - NULL, - audiomixer_instantiate, - audiomixer_enum_interface_info, - }, -}; - +extern const SpaHandleFactory spa_audiomixer_factory; SpaResult spa_enum_handle_factory (unsigned int index, const SpaHandleFactory **factory) { - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *factory = &factories[index]; + if (factory == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + switch (index) { + case 0: + *factory = &spa_audiomixer_factory; + break; + default: + return SPA_RESULT_ENUM_END; + } return SPA_RESULT_OK; } diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c index e0cd4d825..357835fbc 100644 --- a/spa/plugins/audiotestsrc/audiotestsrc.c +++ b/spa/plugins/audiotestsrc/audiotestsrc.c @@ -417,6 +417,8 @@ spa_audiotestsrc_node_port_use_buffers (SpaHandle *handle, static SpaResult spa_audiotestsrc_node_port_alloc_buffers (SpaHandle *handle, uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, SpaBuffer **buffers, uint32_t *n_buffers) { @@ -520,13 +522,15 @@ spa_audiotestsrc_get_interface (SpaHandle *handle, return SPA_RESULT_OK; } -SpaHandle * -spa_audiotestsrc_new (void) +static SpaResult +audiotestsrc_init (const SpaHandleFactory *factory, + SpaHandle *handle) { - SpaHandle *handle; SpaAudioTestSrc *this; - handle = calloc (1, sizeof (SpaAudioTestSrc)); + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + handle->get_interface = spa_audiotestsrc_get_interface; this = (SpaAudioTestSrc *) handle; @@ -540,5 +544,34 @@ spa_audiotestsrc_new (void) SPA_PORT_INFO_FLAG_NO_REF; this->status.flags = SPA_PORT_STATUS_FLAG_HAVE_OUTPUT; - return handle; + return SPA_RESULT_OK; } + +static const SpaInterfaceInfo audiotestsrc_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +audiotestsrc_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &audiotestsrc_interfaces[index]; + + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_audiotestsrc_factory = +{ "audiotestsrc", + NULL, + sizeof (SpaAudioTestSrc), + audiotestsrc_init, + audiotestsrc_enum_interface_info, +}; diff --git a/spa/plugins/audiotestsrc/plugin.c b/spa/plugins/audiotestsrc/plugin.c index 5c9afa496..21f76e290 100644 --- a/spa/plugins/audiotestsrc/plugin.c +++ b/spa/plugins/audiotestsrc/plugin.c @@ -20,59 +20,21 @@ #include #include -SpaHandle * spa_audiotestsrc_new (void); - -static SpaResult -audiotestsrc_instantiate (const SpaHandleFactory *factory, - SpaHandle **handle) -{ - if (factory == NULL || handle == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; - - *handle = spa_audiotestsrc_new (); - - return SPA_RESULT_OK; -} - -static const SpaInterfaceInfo audiotestsrc_interfaces[] = -{ - { SPA_INTERFACE_ID_NODE, - SPA_INTERFACE_ID_NODE_NAME, - SPA_INTERFACE_ID_NODE_DESCRIPTION, - }, -}; - -static SpaResult -audiotestsrc_enum_interface_info (const SpaHandleFactory *factory, - unsigned int index, - const SpaInterfaceInfo **info) -{ - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *info = &audiotestsrc_interfaces[index]; - - return SPA_RESULT_OK; -} - -static const SpaHandleFactory factories[] = -{ - { "audiotestsrc", - NULL, - audiotestsrc_instantiate, - audiotestsrc_enum_interface_info, - }, -}; - +extern const SpaHandleFactory spa_audiotestsrc_factory; SpaResult spa_enum_handle_factory (unsigned int index, const SpaHandleFactory **factory) { - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *factory = &factories[index]; + if (factory == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + switch (index) { + case 0: + *factory = &spa_audiotestsrc_factory; + break; + default: + return SPA_RESULT_ENUM_END; + } return SPA_RESULT_OK; } diff --git a/spa/plugins/ffmpeg/ffmpeg-dec.c b/spa/plugins/ffmpeg/ffmpeg-dec.c new file mode 100644 index 000000000..333d2b496 --- /dev/null +++ b/spa/plugins/ffmpeg/ffmpeg-dec.c @@ -0,0 +1,529 @@ +/* Spa FFMpeg Decoder + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include + +typedef struct _SpaFFMpegDec SpaFFMpegDec; + +typedef struct { + SpaProps props; +} SpaFFMpegDecProps; + +static void +reset_ffmpeg_dec_props (SpaFFMpegDecProps *props) +{ +} + +#define INPUT_PORT_ID 0 +#define OUTPUT_PORT_ID 1 +#define IS_VALID_PORT(id) ((id) < 2) +#define MAX_BUFFERS 32 + +typedef struct _FFMpegBuffer FFMpegBuffer; + +struct _FFMpegBuffer { + SpaBuffer buffer; + SpaMeta metas[1]; + SpaMetaHeader header; + SpaData datas[1]; + SpaFFMpegDec *dec; + SpaBuffer *imported; + bool outstanding; + FFMpegBuffer *next; +}; + +typedef struct { + SpaVideoRawFormat raw_format[2]; + SpaFormat *current_format; + bool have_buffers; + FFMpegBuffer buffers[MAX_BUFFERS]; + SpaPortInfo info; + SpaAllocParam *params[1]; + SpaAllocParamBuffers param_buffers; + SpaPortStatus status; +} SpaFFMpegState; + +struct _SpaFFMpegDec { + SpaHandle handle; + + SpaFFMpegDecProps props[2]; + + SpaEventCallback event_cb; + void *user_data; + + SpaFFMpegState state[2]; +}; + +enum { + PROP_ID_LAST, +}; + +static const SpaPropInfo prop_info[] = +{ + { 0, }, +}; + +static SpaResult +spa_ffmpeg_dec_node_get_props (SpaHandle *handle, + SpaProps **props) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + + if (handle == NULL || props == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + memcpy (&this->props[0], &this->props[1], sizeof (this->props[1])); + *props = &this->props[0].props; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_set_props (SpaHandle *handle, + const SpaProps *props) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + SpaFFMpegDecProps *p = &this->props[1]; + SpaResult res; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (props == NULL) { + reset_ffmpeg_dec_props (p); + return SPA_RESULT_OK; + } + + res = spa_props_copy (props, &p->props); + + return res; +} + +static SpaResult +spa_ffmpeg_dec_node_send_command (SpaHandle *handle, + SpaCommand *command) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + + if (handle == NULL || command == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (command->type) { + case SPA_COMMAND_INVALID: + return SPA_RESULT_INVALID_COMMAND; + + case SPA_COMMAND_START: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STARTED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + case SPA_COMMAND_STOP: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STOPPED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + + case SPA_COMMAND_FLUSH: + case SPA_COMMAND_DRAIN: + case SPA_COMMAND_MARKER: + return SPA_RESULT_NOT_IMPLEMENTED; + } + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_set_event_callback (SpaHandle *handle, + SpaEventCallback event, + void *user_data) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this->event_cb = event; + this->user_data = user_data; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_get_n_ports (SpaHandle *handle, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports) + *n_input_ports = 1; + if (n_output_ports) + *n_output_ports = 1; + if (max_input_ports) + *max_input_ports = 1; + if (max_output_ports) + *max_output_ports = 1; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_get_port_ids (SpaHandle *handle, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports > 0) + input_ids[0] = 0; + if (n_output_ports > 0) + output_ids[0] = 1; + + return SPA_RESULT_OK; +} + + +static SpaResult +spa_ffmpeg_dec_node_add_port (SpaHandle *handle, + SpaDirection direction, + uint32_t *port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_dec_node_remove_port (SpaHandle *handle, + uint32_t port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_dec_node_port_enum_formats (SpaHandle *handle, + uint32_t port_id, + unsigned int index, + SpaFormat **format) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + SpaFFMpegState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + switch (index) { + case 0: + spa_video_raw_format_init (&state->raw_format[0]); + break; + default: + return SPA_RESULT_ENUM_END; + } + *format = &state->raw_format[0].format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_port_set_format (SpaHandle *handle, + uint32_t port_id, + bool test_only, + const SpaFormat *format) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + SpaFFMpegState *state; + SpaResult res; + SpaFormat *f, *tf; + size_t fs; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (format == NULL) { + state->current_format = NULL; + return SPA_RESULT_OK; + } + + if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { + if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { + if ((res = spa_video_raw_format_parse (format, &state->raw_format[0]) < 0)) + return res; + + f = &state->raw_format[0].format; + tf = &state->raw_format[1].format; + fs = sizeof (SpaVideoRawFormat); + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + + if (!test_only) { + memcpy (tf, f, fs); + state->current_format = tf; + } + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_port_get_format (SpaHandle *handle, + uint32_t port_id, + const SpaFormat **format) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + SpaFFMpegState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (state->current_format == NULL) + return SPA_RESULT_NO_FORMAT; + + *format = state->current_format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_port_get_info (SpaHandle *handle, + uint32_t port_id, + const SpaPortInfo **info) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + + if (handle == NULL || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *info = &this->state[port_id].info; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_port_get_props (SpaHandle *handle, + uint32_t port_id, + SpaProps **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_dec_node_port_set_props (SpaHandle *handle, + uint32_t port_id, + const SpaProps *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_dec_node_port_get_status (SpaHandle *handle, + uint32_t port_id, + const SpaPortStatus **status) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + + if (handle == NULL || status == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *status = &this->state[port_id].status; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_port_use_buffers (SpaHandle *handle, + uint32_t port_id, + SpaBuffer **buffers, + uint32_t n_buffers) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_dec_node_port_alloc_buffers (SpaHandle *handle, + uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, + SpaBuffer **buffers, + uint32_t *n_buffers) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + + +static SpaResult +spa_ffmpeg_dec_node_port_push_input (SpaHandle *handle, + unsigned int n_info, + SpaInputInfo *info) +{ + return SPA_RESULT_INVALID_PORT; +} + +static SpaResult +spa_ffmpeg_dec_node_port_pull_output (SpaHandle *handle, + unsigned int n_info, + SpaOutputInfo *info) +{ + SpaFFMpegDec *this = (SpaFFMpegDec *) handle; + SpaFFMpegState *state; + unsigned int i; + bool have_error = false; + + if (handle == NULL || n_info == 0 || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + + for (i = 0; i < n_info; i++) { + if (info[i].port_id != OUTPUT_PORT_ID) { + info[i].status = SPA_RESULT_INVALID_PORT; + have_error = true; + continue; + } + state = &this->state[info[i].port_id]; + + if (state->current_format == NULL) { + info[i].status = SPA_RESULT_NO_FORMAT; + have_error = true; + continue; + } + info[i].status = SPA_RESULT_OK; + } + if (have_error) + return SPA_RESULT_ERROR; + + return SPA_RESULT_OK; +} + +static const SpaNode ffmpeg_dec_node = { + sizeof (SpaNode), + spa_ffmpeg_dec_node_get_props, + spa_ffmpeg_dec_node_set_props, + spa_ffmpeg_dec_node_send_command, + spa_ffmpeg_dec_node_set_event_callback, + spa_ffmpeg_dec_node_get_n_ports, + spa_ffmpeg_dec_node_get_port_ids, + spa_ffmpeg_dec_node_add_port, + spa_ffmpeg_dec_node_remove_port, + spa_ffmpeg_dec_node_port_enum_formats, + spa_ffmpeg_dec_node_port_set_format, + spa_ffmpeg_dec_node_port_get_format, + spa_ffmpeg_dec_node_port_get_info, + spa_ffmpeg_dec_node_port_get_props, + spa_ffmpeg_dec_node_port_set_props, + spa_ffmpeg_dec_node_port_use_buffers, + spa_ffmpeg_dec_node_port_alloc_buffers, + spa_ffmpeg_dec_node_port_get_status, + spa_ffmpeg_dec_node_port_push_input, + spa_ffmpeg_dec_node_port_pull_output, +}; + +static SpaResult +spa_ffmpeg_dec_get_interface (SpaHandle *handle, + uint32_t interface_id, + const void **interface) +{ + if (handle == NULL || interface == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (interface_id) { + case SPA_INTERFACE_ID_NODE: + *interface = &ffmpeg_dec_node; + break; + default: + return SPA_RESULT_UNKNOWN_INTERFACE; + } + return SPA_RESULT_OK; +} + +SpaResult +spa_ffmpeg_dec_init (SpaHandle *handle) +{ + SpaFFMpegDec *this; + + handle->get_interface = spa_ffmpeg_dec_get_interface; + + this = (SpaFFMpegDec *) handle; + this->props[1].props.n_prop_info = PROP_ID_LAST; + this->props[1].props.prop_info = prop_info; + this->props[1].props.set_prop = spa_props_generic_set_prop; + this->props[1].props.get_prop = spa_props_generic_get_prop; + reset_ffmpeg_dec_props (&this->props[1]); + + this->state[INPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[INPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + + this->state[OUTPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[OUTPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + + return SPA_RESULT_OK; +} diff --git a/spa/plugins/ffmpeg/ffmpeg-enc.c b/spa/plugins/ffmpeg/ffmpeg-enc.c new file mode 100644 index 000000000..a66d7e888 --- /dev/null +++ b/spa/plugins/ffmpeg/ffmpeg-enc.c @@ -0,0 +1,529 @@ +/* Spa FFMpeg Encoder + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include + +typedef struct _SpaFFMpegEnc SpaFFMpegEnc; + +typedef struct { + SpaProps props; +} SpaFFMpegEncProps; + +static void +reset_ffmpeg_enc_props (SpaFFMpegEncProps *props) +{ +} + +#define INPUT_PORT_ID 0 +#define OUTPUT_PORT_ID 1 +#define IS_VALID_PORT(id) ((id) < 2) +#define MAX_BUFFERS 32 + +typedef struct _FFMpegBuffer FFMpegBuffer; + +struct _FFMpegBuffer { + SpaBuffer buffer; + SpaMeta metas[1]; + SpaMetaHeader header; + SpaData datas[1]; + SpaFFMpegEnc *enc; + SpaBuffer *imported; + bool outstanding; + FFMpegBuffer *next; +}; + +typedef struct { + SpaVideoRawFormat raw_format[2]; + SpaFormat *current_format; + bool have_buffers; + FFMpegBuffer buffers[MAX_BUFFERS]; + SpaPortInfo info; + SpaAllocParam *params[1]; + SpaAllocParamBuffers param_buffers; + SpaPortStatus status; +} SpaFFMpegState; + +struct _SpaFFMpegEnc { + SpaHandle handle; + + SpaFFMpegEncProps props[2]; + + SpaEventCallback event_cb; + void *user_data; + + SpaFFMpegState state[2]; +}; + +enum { + PROP_ID_LAST, +}; + +static const SpaPropInfo prop_info[] = +{ + { 0, }, +}; + +static SpaResult +spa_ffmpeg_enc_node_get_props (SpaHandle *handle, + SpaProps **props) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + + if (handle == NULL || props == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + memcpy (&this->props[0], &this->props[1], sizeof (this->props[1])); + *props = &this->props[0].props; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_set_props (SpaHandle *handle, + const SpaProps *props) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + SpaFFMpegEncProps *p = &this->props[1]; + SpaResult res; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (props == NULL) { + reset_ffmpeg_enc_props (p); + return SPA_RESULT_OK; + } + + res = spa_props_copy (props, &p->props); + + return res; +} + +static SpaResult +spa_ffmpeg_enc_node_send_command (SpaHandle *handle, + SpaCommand *command) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + + if (handle == NULL || command == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (command->type) { + case SPA_COMMAND_INVALID: + return SPA_RESULT_INVALID_COMMAND; + + case SPA_COMMAND_START: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STARTED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + case SPA_COMMAND_STOP: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STOPPED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + + case SPA_COMMAND_FLUSH: + case SPA_COMMAND_DRAIN: + case SPA_COMMAND_MARKER: + return SPA_RESULT_NOT_IMPLEMENTED; + } + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_set_event_callback (SpaHandle *handle, + SpaEventCallback event, + void *user_data) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this->event_cb = event; + this->user_data = user_data; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_get_n_ports (SpaHandle *handle, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports) + *n_input_ports = 1; + if (n_output_ports) + *n_output_ports = 1; + if (max_input_ports) + *max_input_ports = 1; + if (max_output_ports) + *max_output_ports = 1; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_get_port_ids (SpaHandle *handle, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports > 0) + input_ids[0] = 0; + if (n_output_ports > 0) + output_ids[0] = 1; + + return SPA_RESULT_OK; +} + + +static SpaResult +spa_ffmpeg_enc_node_add_port (SpaHandle *handle, + SpaDirection direction, + uint32_t *port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_enc_node_remove_port (SpaHandle *handle, + uint32_t port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_enc_node_port_enum_formats (SpaHandle *handle, + uint32_t port_id, + unsigned int index, + SpaFormat **format) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + SpaFFMpegState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + switch (index) { + case 0: + spa_video_raw_format_init (&state->raw_format[0]); + break; + default: + return SPA_RESULT_ENUM_END; + } + *format = &state->raw_format[0].format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_port_set_format (SpaHandle *handle, + uint32_t port_id, + bool test_only, + const SpaFormat *format) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + SpaFFMpegState *state; + SpaResult res; + SpaFormat *f, *tf; + size_t fs; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (format == NULL) { + state->current_format = NULL; + return SPA_RESULT_OK; + } + + if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { + if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { + if ((res = spa_video_raw_format_parse (format, &state->raw_format[0]) < 0)) + return res; + + f = &state->raw_format[0].format; + tf = &state->raw_format[1].format; + fs = sizeof (SpaVideoRawFormat); + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + + if (!test_only) { + memcpy (tf, f, fs); + state->current_format = tf; + } + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_port_get_format (SpaHandle *handle, + uint32_t port_id, + const SpaFormat **format) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + SpaFFMpegState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (state->current_format == NULL) + return SPA_RESULT_NO_FORMAT; + + *format = state->current_format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_port_get_info (SpaHandle *handle, + uint32_t port_id, + const SpaPortInfo **info) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + + if (handle == NULL || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *info = &this->state[port_id].info; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_port_get_props (SpaHandle *handle, + uint32_t port_id, + SpaProps **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_enc_node_port_set_props (SpaHandle *handle, + uint32_t port_id, + const SpaProps *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_ffmpeg_enc_node_port_get_status (SpaHandle *handle, + uint32_t port_id, + const SpaPortStatus **status) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + + if (handle == NULL || status == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *status = &this->state[port_id].status; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_port_use_buffers (SpaHandle *handle, + uint32_t port_id, + SpaBuffer **buffers, + uint32_t n_buffers) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_ffmpeg_enc_node_port_alloc_buffers (SpaHandle *handle, + uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, + SpaBuffer **buffers, + uint32_t *n_buffers) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + + +static SpaResult +spa_ffmpeg_enc_node_port_push_input (SpaHandle *handle, + unsigned int n_info, + SpaInputInfo *info) +{ + return SPA_RESULT_INVALID_PORT; +} + +static SpaResult +spa_ffmpeg_enc_node_port_pull_output (SpaHandle *handle, + unsigned int n_info, + SpaOutputInfo *info) +{ + SpaFFMpegEnc *this = (SpaFFMpegEnc *) handle; + SpaFFMpegState *state; + unsigned int i; + bool have_error = false; + + if (handle == NULL || n_info == 0 || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + + for (i = 0; i < n_info; i++) { + if (info[i].port_id != OUTPUT_PORT_ID) { + info[i].status = SPA_RESULT_INVALID_PORT; + have_error = true; + continue; + } + state = &this->state[info[i].port_id]; + + if (state->current_format == NULL) { + info[i].status = SPA_RESULT_NO_FORMAT; + have_error = true; + continue; + } + info[i].status = SPA_RESULT_OK; + } + if (have_error) + return SPA_RESULT_ERROR; + + return SPA_RESULT_OK; +} + +static const SpaNode ffmpeg_enc_node = { + sizeof (SpaNode), + spa_ffmpeg_enc_node_get_props, + spa_ffmpeg_enc_node_set_props, + spa_ffmpeg_enc_node_send_command, + spa_ffmpeg_enc_node_set_event_callback, + spa_ffmpeg_enc_node_get_n_ports, + spa_ffmpeg_enc_node_get_port_ids, + spa_ffmpeg_enc_node_add_port, + spa_ffmpeg_enc_node_remove_port, + spa_ffmpeg_enc_node_port_enum_formats, + spa_ffmpeg_enc_node_port_set_format, + spa_ffmpeg_enc_node_port_get_format, + spa_ffmpeg_enc_node_port_get_info, + spa_ffmpeg_enc_node_port_get_props, + spa_ffmpeg_enc_node_port_set_props, + spa_ffmpeg_enc_node_port_use_buffers, + spa_ffmpeg_enc_node_port_alloc_buffers, + spa_ffmpeg_enc_node_port_get_status, + spa_ffmpeg_enc_node_port_push_input, + spa_ffmpeg_enc_node_port_pull_output, +}; + +static SpaResult +spa_ffmpeg_enc_get_interface (SpaHandle *handle, + uint32_t interface_id, + const void **interface) +{ + if (handle == NULL || interface == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (interface_id) { + case SPA_INTERFACE_ID_NODE: + *interface = &ffmpeg_enc_node; + break; + default: + return SPA_RESULT_UNKNOWN_INTERFACE; + } + return SPA_RESULT_OK; +} + +SpaResult +spa_ffmpeg_enc_init (SpaHandle *handle) +{ + SpaFFMpegEnc *this; + + handle->get_interface = spa_ffmpeg_enc_get_interface; + + this = (SpaFFMpegEnc *) handle; + this->props[1].props.n_prop_info = PROP_ID_LAST; + this->props[1].props.prop_info = prop_info; + this->props[1].props.set_prop = spa_props_generic_set_prop; + this->props[1].props.get_prop = spa_props_generic_get_prop; + reset_ffmpeg_enc_props (&this->props[1]); + + this->state[INPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[INPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + + this->state[OUTPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[OUTPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + + return SPA_RESULT_OK; +} diff --git a/spa/plugins/ffmpeg/ffmpeg.c b/spa/plugins/ffmpeg/ffmpeg.c new file mode 100644 index 000000000..33c0d1c20 --- /dev/null +++ b/spa/plugins/ffmpeg/ffmpeg.c @@ -0,0 +1,109 @@ +/* Spa V4l2 support + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include +#include + +SpaResult spa_ffmpeg_dec_init (SpaHandle *handle); +SpaResult spa_ffmpeg_enc_init (SpaHandle *handle); + +static SpaResult +ffmpeg_dec_init (const SpaHandleFactory *factory, + SpaHandle *handle) +{ + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + return spa_ffmpeg_dec_init (handle); +} + +static SpaResult +ffmpeg_enc_init (const SpaHandleFactory *factory, + SpaHandle *handle) +{ + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + return spa_ffmpeg_enc_init (handle); +} + +static const SpaInterfaceInfo ffmpeg_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +ffmpeg_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &ffmpeg_interfaces[index]; + + return SPA_RESULT_OK; +} + +SpaResult +spa_enum_handle_factory (unsigned int index, + const SpaHandleFactory **factory) +{ + static const AVCodec *c = NULL; + static int ci = 0; + static SpaHandleFactory f; + static char name[128]; + + av_register_all(); + + if (index == 0 || index < ci) { + c = av_codec_next (NULL); + ci = 0; + } + while (index > ci && c) { + c = av_codec_next (c); + ci++; + } + if (c == NULL) + return SPA_RESULT_ENUM_END; + + if (av_codec_is_encoder (c)) { + snprintf (name, 128, "ffenc_%s", c->name); + f.init = ffmpeg_enc_init; + } + else { + snprintf (name, 128, "ffdec_%s", c->name); + f.init = ffmpeg_dec_init; + } + f.name = name; + f.info = NULL; + f.enum_interface_info = ffmpeg_enum_interface_info; + + *factory = &f; + + return SPA_RESULT_OK; +} diff --git a/spa/plugins/ffmpeg/meson.build b/spa/plugins/ffmpeg/meson.build new file mode 100644 index 000000000..ad886fe84 --- /dev/null +++ b/spa/plugins/ffmpeg/meson.build @@ -0,0 +1,10 @@ +ffmpeg_sources = ['ffmpeg.c', + 'ffmpeg-dec.c', + 'ffmpeg-enc.c'] + +ffmpeglib = shared_library('spa-ffmpeg', + ffmpeg_sources, + include_directories : inc, + dependencies : [ avcodec_dep, avformat_dep ], + link_with : spalib, + install : true) diff --git a/spa/plugins/libva/libva-dec.c b/spa/plugins/libva/libva-dec.c new file mode 100644 index 000000000..182ad2446 --- /dev/null +++ b/spa/plugins/libva/libva-dec.c @@ -0,0 +1,530 @@ +/* Spa Libva Decoder + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include + +typedef struct _SpaLibvaDec SpaLibvaDec; + +typedef struct { + SpaProps props; +} SpaLibvaDecProps; + +static void +reset_libva_dec_props (SpaLibvaDecProps *props) +{ +} + +#define INPUT_PORT_ID 0 +#define OUTPUT_PORT_ID 1 +#define IS_VALID_PORT(id) ((id) < 2) +#define MAX_BUFFERS 32 + +typedef struct _LibvaBuffer LibvaBuffer; + +struct _LibvaBuffer { + SpaBuffer buffer; + SpaMeta metas[1]; + SpaMetaHeader header; + SpaData datas[1]; + SpaLibvaDec *dec; + SpaBuffer *imported; + bool outstanding; + LibvaBuffer *next; +}; + +typedef struct { + SpaVideoRawFormat raw_format[2]; + SpaFormat *current_format; + bool have_buffers; + LibvaBuffer buffers[MAX_BUFFERS]; + SpaPortInfo info; + SpaAllocParam *params[1]; + SpaAllocParamBuffers param_buffers; + SpaPortStatus status; +} SpaLibvaState; + +struct _SpaLibvaDec { + SpaHandle handle; + + SpaLibvaDecProps props[2]; + + SpaEventCallback event_cb; + void *user_data; + + SpaLibvaState state[2]; +}; + +enum { + PROP_ID_LAST, +}; + +static const SpaPropInfo prop_info[] = +{ + { 0, }, +}; + +static SpaResult +spa_libva_dec_node_get_props (SpaHandle *handle, + SpaProps **props) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + + if (handle == NULL || props == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + memcpy (&this->props[0], &this->props[1], sizeof (this->props[1])); + *props = &this->props[0].props; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_set_props (SpaHandle *handle, + const SpaProps *props) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + SpaLibvaDecProps *p = &this->props[1]; + SpaResult res; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (props == NULL) { + reset_libva_dec_props (p); + return SPA_RESULT_OK; + } + + res = spa_props_copy (props, &p->props); + + return res; +} + +static SpaResult +spa_libva_dec_node_send_command (SpaHandle *handle, + SpaCommand *command) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + + if (handle == NULL || command == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (command->type) { + case SPA_COMMAND_INVALID: + return SPA_RESULT_INVALID_COMMAND; + + case SPA_COMMAND_START: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STARTED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + case SPA_COMMAND_STOP: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STOPPED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + + case SPA_COMMAND_FLUSH: + case SPA_COMMAND_DRAIN: + case SPA_COMMAND_MARKER: + return SPA_RESULT_NOT_IMPLEMENTED; + } + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_set_event_callback (SpaHandle *handle, + SpaEventCallback event, + void *user_data) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this->event_cb = event; + this->user_data = user_data; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_get_n_ports (SpaHandle *handle, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports) + *n_input_ports = 1; + if (n_output_ports) + *n_output_ports = 1; + if (max_input_ports) + *max_input_ports = 1; + if (max_output_ports) + *max_output_ports = 1; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_get_port_ids (SpaHandle *handle, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports > 0) + input_ids[0] = 0; + if (n_output_ports > 0) + output_ids[0] = 1; + + return SPA_RESULT_OK; +} + + +static SpaResult +spa_libva_dec_node_add_port (SpaHandle *handle, + SpaDirection direction, + uint32_t *port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_dec_node_remove_port (SpaHandle *handle, + uint32_t port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_dec_node_port_enum_formats (SpaHandle *handle, + uint32_t port_id, + unsigned int index, + SpaFormat **format) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + SpaLibvaState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + switch (index) { + case 0: + spa_video_raw_format_init (&state->raw_format[0]); + break; + default: + return SPA_RESULT_ENUM_END; + } + *format = &state->raw_format[0].format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_port_set_format (SpaHandle *handle, + uint32_t port_id, + bool test_only, + const SpaFormat *format) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + SpaLibvaState *state; + SpaResult res; + SpaFormat *f, *tf; + size_t fs; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (format == NULL) { + state->current_format = NULL; + return SPA_RESULT_OK; + } + + if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { + if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { + if ((res = spa_video_raw_format_parse (format, &state->raw_format[0]) < 0)) + return res; + + f = &state->raw_format[0].format; + tf = &state->raw_format[1].format; + fs = sizeof (SpaVideoRawFormat); + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + + if (!test_only) { + memcpy (tf, f, fs); + state->current_format = tf; + } + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_port_get_format (SpaHandle *handle, + uint32_t port_id, + const SpaFormat **format) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + SpaLibvaState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (state->current_format == NULL) + return SPA_RESULT_NO_FORMAT; + + *format = state->current_format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_port_get_info (SpaHandle *handle, + uint32_t port_id, + const SpaPortInfo **info) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + + if (handle == NULL || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *info = &this->state[port_id].info; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_port_get_props (SpaHandle *handle, + uint32_t port_id, + SpaProps **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_dec_node_port_set_props (SpaHandle *handle, + uint32_t port_id, + const SpaProps *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_dec_node_port_get_status (SpaHandle *handle, + uint32_t port_id, + const SpaPortStatus **status) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + + if (handle == NULL || status == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *status = &this->state[port_id].status; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_port_use_buffers (SpaHandle *handle, + uint32_t port_id, + SpaBuffer **buffers, + uint32_t n_buffers) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_dec_node_port_alloc_buffers (SpaHandle *handle, + uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, + SpaBuffer **buffers, + uint32_t *n_buffers) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + + +static SpaResult +spa_libva_dec_node_port_push_input (SpaHandle *handle, + unsigned int n_info, + SpaInputInfo *info) +{ + return SPA_RESULT_INVALID_PORT; +} + +static SpaResult +spa_libva_dec_node_port_pull_output (SpaHandle *handle, + unsigned int n_info, + SpaOutputInfo *info) +{ + SpaLibvaDec *this = (SpaLibvaDec *) handle; + SpaLibvaState *state; + unsigned int i; + bool have_error = false; + + if (handle == NULL || n_info == 0 || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + + for (i = 0; i < n_info; i++) { + if (info[i].port_id != OUTPUT_PORT_ID) { + info[i].status = SPA_RESULT_INVALID_PORT; + have_error = true; + continue; + } + state = &this->state[info[i].port_id]; + + if (state->current_format == NULL) { + info[i].status = SPA_RESULT_NO_FORMAT; + have_error = true; + continue; + } + info[i].status = SPA_RESULT_OK; + } + if (have_error) + return SPA_RESULT_ERROR; + + return SPA_RESULT_OK; +} + +static const SpaNode libva_dec_node = { + sizeof (SpaNode), + spa_libva_dec_node_get_props, + spa_libva_dec_node_set_props, + spa_libva_dec_node_send_command, + spa_libva_dec_node_set_event_callback, + spa_libva_dec_node_get_n_ports, + spa_libva_dec_node_get_port_ids, + spa_libva_dec_node_add_port, + spa_libva_dec_node_remove_port, + spa_libva_dec_node_port_enum_formats, + spa_libva_dec_node_port_set_format, + spa_libva_dec_node_port_get_format, + spa_libva_dec_node_port_get_info, + spa_libva_dec_node_port_get_props, + spa_libva_dec_node_port_set_props, + spa_libva_dec_node_port_use_buffers, + spa_libva_dec_node_port_alloc_buffers, + spa_libva_dec_node_port_get_status, + spa_libva_dec_node_port_push_input, + spa_libva_dec_node_port_pull_output, +}; + +static SpaResult +spa_libva_dec_get_interface (SpaHandle *handle, + uint32_t interface_id, + const void **interface) +{ + if (handle == NULL || interface == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (interface_id) { + case SPA_INTERFACE_ID_NODE: + *interface = &libva_dec_node; + break; + default: + return SPA_RESULT_UNKNOWN_INTERFACE; + } + return SPA_RESULT_OK; +} + +SpaHandle * +spa_libva_dec_new (void) +{ + SpaHandle *handle; + SpaLibvaDec *this; + + handle = calloc (1, sizeof (SpaLibvaDec)); + handle->get_interface = spa_libva_dec_get_interface; + + this = (SpaLibvaDec *) handle; + this->props[1].props.n_prop_info = PROP_ID_LAST; + this->props[1].props.prop_info = prop_info; + this->props[1].props.set_prop = spa_props_generic_set_prop; + this->props[1].props.get_prop = spa_props_generic_get_prop; + reset_libva_dec_props (&this->props[1]); + + this->state[INPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[INPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + + this->state[OUTPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[OUTPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + return handle; +} diff --git a/spa/plugins/libva/libva-enc.c b/spa/plugins/libva/libva-enc.c new file mode 100644 index 000000000..593d4119c --- /dev/null +++ b/spa/plugins/libva/libva-enc.c @@ -0,0 +1,530 @@ +/* Spa Libva Encoder + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include + +typedef struct _SpaLibvaEnc SpaLibvaEnc; + +typedef struct { + SpaProps props; +} SpaLibvaEncProps; + +static void +reset_libva_enc_props (SpaLibvaEncProps *props) +{ +} + +#define INPUT_PORT_ID 0 +#define OUTPUT_PORT_ID 1 +#define IS_VALID_PORT(id) ((id) < 2) +#define MAX_BUFFERS 32 + +typedef struct _LibvaBuffer LibvaBuffer; + +struct _LibvaBuffer { + SpaBuffer buffer; + SpaMeta metas[1]; + SpaMetaHeader header; + SpaData datas[1]; + SpaLibvaEnc *enc; + SpaBuffer *imported; + bool outstanding; + LibvaBuffer *next; +}; + +typedef struct { + SpaVideoRawFormat raw_format[2]; + SpaFormat *current_format; + bool have_buffers; + LibvaBuffer buffers[MAX_BUFFERS]; + SpaPortInfo info; + SpaAllocParam *params[1]; + SpaAllocParamBuffers param_buffers; + SpaPortStatus status; +} SpaLibvaState; + +struct _SpaLibvaEnc { + SpaHandle handle; + + SpaLibvaEncProps props[2]; + + SpaEventCallback event_cb; + void *user_data; + + SpaLibvaState state[2]; +}; + +enum { + PROP_ID_LAST, +}; + +static const SpaPropInfo prop_info[] = +{ + { 0, }, +}; + +static SpaResult +spa_libva_enc_node_get_props (SpaHandle *handle, + SpaProps **props) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + + if (handle == NULL || props == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + memcpy (&this->props[0], &this->props[1], sizeof (this->props[1])); + *props = &this->props[0].props; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_set_props (SpaHandle *handle, + const SpaProps *props) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + SpaLibvaEncProps *p = &this->props[1]; + SpaResult res; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (props == NULL) { + reset_libva_enc_props (p); + return SPA_RESULT_OK; + } + + res = spa_props_copy (props, &p->props); + + return res; +} + +static SpaResult +spa_libva_enc_node_send_command (SpaHandle *handle, + SpaCommand *command) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + + if (handle == NULL || command == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (command->type) { + case SPA_COMMAND_INVALID: + return SPA_RESULT_INVALID_COMMAND; + + case SPA_COMMAND_START: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STARTED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + case SPA_COMMAND_STOP: + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_STOPPED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + + case SPA_COMMAND_FLUSH: + case SPA_COMMAND_DRAIN: + case SPA_COMMAND_MARKER: + return SPA_RESULT_NOT_IMPLEMENTED; + } + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_set_event_callback (SpaHandle *handle, + SpaEventCallback event, + void *user_data) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this->event_cb = event; + this->user_data = user_data; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_get_n_ports (SpaHandle *handle, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports) + *n_input_ports = 1; + if (n_output_ports) + *n_output_ports = 1; + if (max_input_ports) + *max_input_ports = 1; + if (max_output_ports) + *max_output_ports = 1; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_get_port_ids (SpaHandle *handle, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports > 0) + input_ids[0] = 0; + if (n_output_ports > 0) + output_ids[0] = 1; + + return SPA_RESULT_OK; +} + + +static SpaResult +spa_libva_enc_node_add_port (SpaHandle *handle, + SpaDirection direction, + uint32_t *port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_enc_node_remove_port (SpaHandle *handle, + uint32_t port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_enc_node_port_enum_formats (SpaHandle *handle, + uint32_t port_id, + unsigned int index, + SpaFormat **format) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + SpaLibvaState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + switch (index) { + case 0: + spa_video_raw_format_init (&state->raw_format[0]); + break; + default: + return SPA_RESULT_ENUM_END; + } + *format = &state->raw_format[0].format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_port_set_format (SpaHandle *handle, + uint32_t port_id, + bool test_only, + const SpaFormat *format) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + SpaLibvaState *state; + SpaResult res; + SpaFormat *f, *tf; + size_t fs; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (format == NULL) { + state->current_format = NULL; + return SPA_RESULT_OK; + } + + if (format->media_type == SPA_MEDIA_TYPE_VIDEO) { + if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) { + if ((res = spa_video_raw_format_parse (format, &state->raw_format[0]) < 0)) + return res; + + f = &state->raw_format[0].format; + tf = &state->raw_format[1].format; + fs = sizeof (SpaVideoRawFormat); + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + } else + return SPA_RESULT_INVALID_MEDIA_TYPE; + + if (!test_only) { + memcpy (tf, f, fs); + state->current_format = tf; + } + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_port_get_format (SpaHandle *handle, + uint32_t port_id, + const SpaFormat **format) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + SpaLibvaState *state; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + state = &this->state[port_id]; + + if (state->current_format == NULL) + return SPA_RESULT_NO_FORMAT; + + *format = state->current_format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_port_get_info (SpaHandle *handle, + uint32_t port_id, + const SpaPortInfo **info) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + + if (handle == NULL || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *info = &this->state[port_id].info; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_port_get_props (SpaHandle *handle, + uint32_t port_id, + SpaProps **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_enc_node_port_set_props (SpaHandle *handle, + uint32_t port_id, + const SpaProps *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_libva_enc_node_port_get_status (SpaHandle *handle, + uint32_t port_id, + const SpaPortStatus **status) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + + if (handle == NULL || status == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + *status = &this->state[port_id].status; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_port_use_buffers (SpaHandle *handle, + uint32_t port_id, + SpaBuffer **buffers, + uint32_t n_buffers) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (!IS_VALID_PORT (port_id)) + return SPA_RESULT_INVALID_PORT; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_libva_enc_node_port_alloc_buffers (SpaHandle *handle, + uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, + SpaBuffer **buffers, + uint32_t *n_buffers) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + + +static SpaResult +spa_libva_enc_node_port_push_input (SpaHandle *handle, + unsigned int n_info, + SpaInputInfo *info) +{ + return SPA_RESULT_INVALID_PORT; +} + +static SpaResult +spa_libva_enc_node_port_pull_output (SpaHandle *handle, + unsigned int n_info, + SpaOutputInfo *info) +{ + SpaLibvaEnc *this = (SpaLibvaEnc *) handle; + SpaLibvaState *state; + unsigned int i; + bool have_error = false; + + if (handle == NULL || n_info == 0 || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + + for (i = 0; i < n_info; i++) { + if (info[i].port_id != OUTPUT_PORT_ID) { + info[i].status = SPA_RESULT_INVALID_PORT; + have_error = true; + continue; + } + state = &this->state[info[i].port_id]; + + if (state->current_format == NULL) { + info[i].status = SPA_RESULT_NO_FORMAT; + have_error = true; + continue; + } + info[i].status = SPA_RESULT_OK; + } + if (have_error) + return SPA_RESULT_ERROR; + + return SPA_RESULT_OK; +} + +static const SpaNode libva_enc_node = { + sizeof (SpaNode), + spa_libva_enc_node_get_props, + spa_libva_enc_node_set_props, + spa_libva_enc_node_send_command, + spa_libva_enc_node_set_event_callback, + spa_libva_enc_node_get_n_ports, + spa_libva_enc_node_get_port_ids, + spa_libva_enc_node_add_port, + spa_libva_enc_node_remove_port, + spa_libva_enc_node_port_enum_formats, + spa_libva_enc_node_port_set_format, + spa_libva_enc_node_port_get_format, + spa_libva_enc_node_port_get_info, + spa_libva_enc_node_port_get_props, + spa_libva_enc_node_port_set_props, + spa_libva_enc_node_port_use_buffers, + spa_libva_enc_node_port_alloc_buffers, + spa_libva_enc_node_port_get_status, + spa_libva_enc_node_port_push_input, + spa_libva_enc_node_port_pull_output, +}; + +static SpaResult +spa_libva_enc_get_interface (SpaHandle *handle, + uint32_t interface_id, + const void **interface) +{ + if (handle == NULL || interface == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (interface_id) { + case SPA_INTERFACE_ID_NODE: + *interface = &libva_enc_node; + break; + default: + return SPA_RESULT_UNKNOWN_INTERFACE; + } + return SPA_RESULT_OK; +} + +SpaHandle * +spa_libva_enc_new (void) +{ + SpaHandle *handle; + SpaLibvaEnc *this; + + handle = calloc (1, sizeof (SpaLibvaEnc)); + handle->get_interface = spa_libva_enc_get_interface; + + this = (SpaLibvaEnc *) handle; + this->props[1].props.n_prop_info = PROP_ID_LAST; + this->props[1].props.prop_info = prop_info; + this->props[1].props.set_prop = spa_props_generic_set_prop; + this->props[1].props.get_prop = spa_props_generic_get_prop; + reset_libva_enc_props (&this->props[1]); + + this->state[INPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[INPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + + this->state[OUTPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[OUTPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + return handle; +} diff --git a/spa/plugins/libva/libva.c b/spa/plugins/libva/libva.c new file mode 100644 index 000000000..8397e0c1c --- /dev/null +++ b/spa/plugins/libva/libva.c @@ -0,0 +1,96 @@ +/* Spa V4l2 support + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +SpaHandle * spa_libva_dec_new (void); +SpaHandle * spa_libva_enc_new (void); + +static SpaResult +libva_dec_instantiate (const SpaHandleFactory *factory, + SpaHandle **handle) +{ + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + *handle = spa_libva_dec_new (); + + return SPA_RESULT_OK; +} + +static SpaResult +libva_enc_instantiate (const SpaHandleFactory *factory, + SpaHandle **handle) +{ + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + *handle = spa_libva_enc_new (); + + return SPA_RESULT_OK; +} + + +static const SpaInterfaceInfo libva_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +libva_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &libva_interfaces[index]; + + return SPA_RESULT_OK; +} + +static const SpaHandleFactory factories[] = +{ + { "libva-dec", + NULL, + libva_dec_instantiate, + libva_enum_interface_info, + }, + { "libva-enc", + NULL, + libva_enc_instantiate, + libva_enum_interface_info, + } +}; + +SpaResult +spa_enum_handle_factory (unsigned int index, + const SpaHandleFactory **factory) +{ + if (index >= 2) + return SPA_RESULT_ENUM_END; + + *factory = &factories[index]; + + return SPA_RESULT_OK; +} diff --git a/spa/plugins/libva/meson.build b/spa/plugins/libva/meson.build new file mode 100644 index 000000000..ce8142f93 --- /dev/null +++ b/spa/plugins/libva/meson.build @@ -0,0 +1,10 @@ +libva_sources = ['libva.c', + 'libva-dec.c', + 'libva-enc.c'] + +libvalib = shared_library('spa-libva', + libva_sources, + include_directories : inc, + dependencies : libva_dep, + link_with : spalib, + install : true) diff --git a/spa/plugins/libva/vaapi-recorder.c b/spa/plugins/libva/vaapi-recorder.c new file mode 100644 index 000000000..1228f7d1e --- /dev/null +++ b/spa/plugins/libva/vaapi-recorder.c @@ -0,0 +1,1161 @@ +/* + * Copyright (c) 2012 Intel Corporation. All Rights Reserved. + * Copyright © 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "compositor.h" +#include "vaapi-recorder.h" + +#define NAL_REF_IDC_NONE 0 +#define NAL_REF_IDC_LOW 1 +#define NAL_REF_IDC_MEDIUM 2 +#define NAL_REF_IDC_HIGH 3 + +#define NAL_NON_IDR 1 +#define NAL_IDR 5 +#define NAL_SPS 7 +#define NAL_PPS 8 +#define NAL_SEI 6 + +#define SLICE_TYPE_P 0 +#define SLICE_TYPE_B 1 +#define SLICE_TYPE_I 2 + +#define ENTROPY_MODE_CAVLC 0 +#define ENTROPY_MODE_CABAC 1 + +#define PROFILE_IDC_BASELINE 66 +#define PROFILE_IDC_MAIN 77 +#define PROFILE_IDC_HIGH 100 + +struct vaapi_recorder { + int drm_fd, output_fd; + int width, height; + int frame_count; + + int error; + int destroying; + pthread_t worker_thread; + pthread_mutex_t mutex; + pthread_cond_t input_cond; + + struct { + int valid; + int prime_fd, stride; + } input; + + VADisplay va_dpy; + + /* video post processing is used for colorspace conversion */ + struct { + VAConfigID cfg; + VAContextID ctx; + VABufferID pipeline_buf; + VASurfaceID output; + } vpp; + + struct { + VAConfigID cfg; + VAContextID ctx; + VASurfaceID reference_picture[3]; + + int intra_period; + int output_size; + int constraint_set_flag; + + struct { + VAEncSequenceParameterBufferH264 seq; + VAEncPictureParameterBufferH264 pic; + VAEncSliceParameterBufferH264 slice; + } param; + } encoder; +}; + +static void * +worker_thread_function(void *); + +/* bistream code used for writing the packed headers */ + +#define BITSTREAM_ALLOCATE_STEPPING 4096 + +struct bitstream { + unsigned int *buffer; + int bit_offset; + int max_size_in_dword; +}; + +static unsigned int +va_swap32(unsigned int val) +{ + unsigned char *pval = (unsigned char *)&val; + + return ((pval[0] << 24) | + (pval[1] << 16) | + (pval[2] << 8) | + (pval[3] << 0)); +} + +static void +bitstream_start(struct bitstream *bs) +{ + bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING; + bs->buffer = calloc(bs->max_size_in_dword * sizeof(unsigned int), 1); + bs->bit_offset = 0; +} + +static void +bitstream_end(struct bitstream *bs) +{ + int pos = (bs->bit_offset >> 5); + int bit_offset = (bs->bit_offset & 0x1f); + int bit_left = 32 - bit_offset; + + if (bit_offset) { + bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left)); + } +} + +static void +bitstream_put_ui(struct bitstream *bs, unsigned int val, int size_in_bits) +{ + int pos = (bs->bit_offset >> 5); + int bit_offset = (bs->bit_offset & 0x1f); + int bit_left = 32 - bit_offset; + + if (!size_in_bits) + return; + + bs->bit_offset += size_in_bits; + + if (bit_left > size_in_bits) { + bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val); + return; + } + + size_in_bits -= bit_left; + bs->buffer[pos] = + (bs->buffer[pos] << bit_left) | (val >> size_in_bits); + bs->buffer[pos] = va_swap32(bs->buffer[pos]); + + if (pos + 1 == bs->max_size_in_dword) { + bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING; + bs->buffer = + realloc(bs->buffer, + bs->max_size_in_dword * sizeof(unsigned int)); + } + + bs->buffer[pos + 1] = val; +} + +static void +bitstream_put_ue(struct bitstream *bs, unsigned int val) +{ + int size_in_bits = 0; + int tmp_val = ++val; + + while (tmp_val) { + tmp_val >>= 1; + size_in_bits++; + } + + bitstream_put_ui(bs, 0, size_in_bits - 1); /* leading zero */ + bitstream_put_ui(bs, val, size_in_bits); +} + +static void +bitstream_put_se(struct bitstream *bs, int val) +{ + unsigned int new_val; + + if (val <= 0) + new_val = -2 * val; + else + new_val = 2 * val - 1; + + bitstream_put_ue(bs, new_val); +} + +static void +bitstream_byte_aligning(struct bitstream *bs, int bit) +{ + int bit_offset = (bs->bit_offset & 0x7); + int bit_left = 8 - bit_offset; + int new_val; + + if (!bit_offset) + return; + + if (bit) + new_val = (1 << bit_left) - 1; + else + new_val = 0; + + bitstream_put_ui(bs, new_val, bit_left); +} + +static VAStatus +encoder_create_config(struct vaapi_recorder *r) +{ + VAConfigAttrib attrib[2]; + VAStatus status; + + /* FIXME: should check if VAEntrypointEncSlice is supported */ + + /* FIXME: should check if specified attributes are supported */ + + attrib[0].type = VAConfigAttribRTFormat; + attrib[0].value = VA_RT_FORMAT_YUV420; + + attrib[1].type = VAConfigAttribRateControl; + attrib[1].value = VA_RC_CQP; + + status = vaCreateConfig(r->va_dpy, VAProfileH264Main, + VAEntrypointEncSlice, attrib, 2, + &r->encoder.cfg); + if (status != VA_STATUS_SUCCESS) + return status; + + status = vaCreateContext(r->va_dpy, r->encoder.cfg, + r->width, r->height, VA_PROGRESSIVE, 0, 0, + &r->encoder.ctx); + if (status != VA_STATUS_SUCCESS) { + vaDestroyConfig(r->va_dpy, r->encoder.cfg); + return status; + } + + return VA_STATUS_SUCCESS; +} + +static void +encoder_destroy_config(struct vaapi_recorder *r) +{ + vaDestroyContext(r->va_dpy, r->encoder.ctx); + vaDestroyConfig(r->va_dpy, r->encoder.cfg); +} + +static void +encoder_init_seq_parameters(struct vaapi_recorder *r) +{ + int width_in_mbs, height_in_mbs; + int frame_cropping_flag = 0; + int frame_crop_bottom_offset = 0; + + width_in_mbs = (r->width + 15) / 16; + height_in_mbs = (r->height + 15) / 16; + + r->encoder.param.seq.level_idc = 41; + r->encoder.param.seq.intra_period = r->encoder.intra_period; + r->encoder.param.seq.max_num_ref_frames = 4; + r->encoder.param.seq.picture_width_in_mbs = width_in_mbs; + r->encoder.param.seq.picture_height_in_mbs = height_in_mbs; + r->encoder.param.seq.seq_fields.bits.frame_mbs_only_flag = 1; + + /* Tc = num_units_in_tick / time_scale */ + r->encoder.param.seq.time_scale = 1800; + r->encoder.param.seq.num_units_in_tick = 15; + + if (height_in_mbs * 16 - r->height > 0) { + frame_cropping_flag = 1; + frame_crop_bottom_offset = (height_in_mbs * 16 - r->height) / 2; + } + + r->encoder.param.seq.frame_cropping_flag = frame_cropping_flag; + r->encoder.param.seq.frame_crop_bottom_offset = frame_crop_bottom_offset; + + r->encoder.param.seq.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2; +} + +static VABufferID +encoder_update_seq_parameters(struct vaapi_recorder *r) +{ + VABufferID seq_buf; + VAStatus status; + + status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, + VAEncSequenceParameterBufferType, + sizeof(r->encoder.param.seq), + 1, &r->encoder.param.seq, + &seq_buf); + + if (status == VA_STATUS_SUCCESS) + return seq_buf; + else + return VA_INVALID_ID; +} + +static void +encoder_init_pic_parameters(struct vaapi_recorder *r) +{ + VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic; + + pic->pic_init_qp = 0; + + /* ENTROPY_MODE_CABAC */ + pic->pic_fields.bits.entropy_coding_mode_flag = 1; + + pic->pic_fields.bits.deblocking_filter_control_present_flag = 1; +} + +static VABufferID +encoder_update_pic_parameters(struct vaapi_recorder *r, + VABufferID output_buf) +{ + VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic; + VAStatus status; + VABufferID pic_param_buf; + VASurfaceID curr_pic, pic0; + + curr_pic = r->encoder.reference_picture[r->frame_count % 2]; + pic0 = r->encoder.reference_picture[(r->frame_count + 1) % 2]; + + pic->CurrPic.picture_id = curr_pic; + pic->CurrPic.TopFieldOrderCnt = r->frame_count * 2; + pic->ReferenceFrames[0].picture_id = pic0; + pic->ReferenceFrames[1].picture_id = r->encoder.reference_picture[2]; + pic->ReferenceFrames[2].picture_id = VA_INVALID_ID; + + pic->coded_buf = output_buf; + pic->frame_num = r->frame_count; + + pic->pic_fields.bits.idr_pic_flag = (r->frame_count == 0); + pic->pic_fields.bits.reference_pic_flag = 1; + + status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, + VAEncPictureParameterBufferType, + sizeof(VAEncPictureParameterBufferH264), 1, + pic, &pic_param_buf); + + if (status == VA_STATUS_SUCCESS) + return pic_param_buf; + else + return VA_INVALID_ID; +} + +static VABufferID +encoder_update_slice_parameter(struct vaapi_recorder *r, int slice_type) +{ + VABufferID slice_param_buf; + VAStatus status; + + int width_in_mbs = (r->width + 15) / 16; + int height_in_mbs = (r->height + 15) / 16; + + memset(&r->encoder.param.slice, 0, sizeof r->encoder.param.slice); + + r->encoder.param.slice.num_macroblocks = width_in_mbs * height_in_mbs; + r->encoder.param.slice.slice_type = slice_type; + + r->encoder.param.slice.slice_alpha_c0_offset_div2 = 2; + r->encoder.param.slice.slice_beta_offset_div2 = 2; + + status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, + VAEncSliceParameterBufferType, + sizeof(r->encoder.param.slice), 1, + &r->encoder.param.slice, + &slice_param_buf); + + if (status == VA_STATUS_SUCCESS) + return slice_param_buf; + else + return VA_INVALID_ID; +} + +static VABufferID +encoder_update_misc_hdr_parameter(struct vaapi_recorder *r) +{ + VAEncMiscParameterBuffer *misc_param; + VAEncMiscParameterHRD *hrd; + VABufferID buffer; + VAStatus status; + + int total_size = + sizeof(VAEncMiscParameterBuffer) + + sizeof(VAEncMiscParameterRateControl); + + status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, + VAEncMiscParameterBufferType, total_size, + 1, NULL, &buffer); + if (status != VA_STATUS_SUCCESS) + return VA_INVALID_ID; + + status = vaMapBuffer(r->va_dpy, buffer, (void **) &misc_param); + if (status != VA_STATUS_SUCCESS) { + vaDestroyBuffer(r->va_dpy, buffer); + return VA_INVALID_ID; + } + + misc_param->type = VAEncMiscParameterTypeHRD; + hrd = (VAEncMiscParameterHRD *) misc_param->data; + + hrd->initial_buffer_fullness = 0; + hrd->buffer_size = 0; + + vaUnmapBuffer(r->va_dpy, buffer); + + return buffer; +} + +static int +setup_encoder(struct vaapi_recorder *r) +{ + VAStatus status; + + status = encoder_create_config(r); + if (status != VA_STATUS_SUCCESS) { + return -1; + } + + status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420, + r->width, r->height, + r->encoder.reference_picture, 3, + NULL, 0); + if (status != VA_STATUS_SUCCESS) { + encoder_destroy_config(r); + return -1; + } + + /* VAProfileH264Main */ + r->encoder.constraint_set_flag |= (1 << 1); /* Annex A.2.2 */ + + r->encoder.output_size = r->width * r->height; + + r->encoder.intra_period = 30; + + encoder_init_seq_parameters(r); + encoder_init_pic_parameters(r); + + return 0; +} + +static void +encoder_destroy(struct vaapi_recorder *r) +{ + vaDestroySurfaces(r->va_dpy, r->encoder.reference_picture, 3); + + encoder_destroy_config(r); +} + +static void +nal_start_code_prefix(struct bitstream *bs) +{ + bitstream_put_ui(bs, 0x00000001, 32); +} + +static void +nal_header(struct bitstream *bs, int nal_ref_idc, int nal_unit_type) +{ + /* forbidden_zero_bit: 0 */ + bitstream_put_ui(bs, 0, 1); + + bitstream_put_ui(bs, nal_ref_idc, 2); + bitstream_put_ui(bs, nal_unit_type, 5); +} + +static void +rbsp_trailing_bits(struct bitstream *bs) +{ + bitstream_put_ui(bs, 1, 1); + bitstream_byte_aligning(bs, 0); +} + +static void sps_rbsp(struct bitstream *bs, + VAEncSequenceParameterBufferH264 *seq, + int constraint_set_flag) +{ + int i; + + bitstream_put_ui(bs, PROFILE_IDC_MAIN, 8); + + /* constraint_set[0-3] flag */ + for (i = 0; i < 4; i++) { + int set = (constraint_set_flag & (1 << i)) ? 1 : 0; + bitstream_put_ui(bs, set, 1); + } + + /* reserved_zero_4bits */ + bitstream_put_ui(bs, 0, 4); + bitstream_put_ui(bs, seq->level_idc, 8); + bitstream_put_ue(bs, seq->seq_parameter_set_id); + + bitstream_put_ue(bs, seq->seq_fields.bits.log2_max_frame_num_minus4); + bitstream_put_ue(bs, seq->seq_fields.bits.pic_order_cnt_type); + bitstream_put_ue(bs, + seq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4); + + bitstream_put_ue(bs, seq->max_num_ref_frames); + + /* gaps_in_frame_num_value_allowed_flag */ + bitstream_put_ui(bs, 0, 1); + + /* pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 */ + bitstream_put_ue(bs, seq->picture_width_in_mbs - 1); + bitstream_put_ue(bs, seq->picture_height_in_mbs - 1); + + bitstream_put_ui(bs, seq->seq_fields.bits.frame_mbs_only_flag, 1); + bitstream_put_ui(bs, seq->seq_fields.bits.direct_8x8_inference_flag, 1); + + bitstream_put_ui(bs, seq->frame_cropping_flag, 1); + + if (seq->frame_cropping_flag) { + bitstream_put_ue(bs, seq->frame_crop_left_offset); + bitstream_put_ue(bs, seq->frame_crop_right_offset); + bitstream_put_ue(bs, seq->frame_crop_top_offset); + bitstream_put_ue(bs, seq->frame_crop_bottom_offset); + } + + /* vui_parameters_present_flag */ + bitstream_put_ui(bs, 1, 1); + + /* aspect_ratio_info_present_flag */ + bitstream_put_ui(bs, 0, 1); + /* overscan_info_present_flag */ + bitstream_put_ui(bs, 0, 1); + + /* video_signal_type_present_flag */ + bitstream_put_ui(bs, 0, 1); + /* chroma_loc_info_present_flag */ + bitstream_put_ui(bs, 0, 1); + + /* timing_info_present_flag */ + bitstream_put_ui(bs, 1, 1); + bitstream_put_ui(bs, seq->num_units_in_tick, 32); + bitstream_put_ui(bs, seq->time_scale, 32); + /* fixed_frame_rate_flag */ + bitstream_put_ui(bs, 1, 1); + + /* nal_hrd_parameters_present_flag */ + bitstream_put_ui(bs, 0, 1); + + /* vcl_hrd_parameters_present_flag */ + bitstream_put_ui(bs, 0, 1); + + /* low_delay_hrd_flag */ + bitstream_put_ui(bs, 0, 1); + + /* pic_struct_present_flag */ + bitstream_put_ui(bs, 0, 1); + /* bitstream_restriction_flag */ + bitstream_put_ui(bs, 0, 1); + + rbsp_trailing_bits(bs); +} + +static void pps_rbsp(struct bitstream *bs, + VAEncPictureParameterBufferH264 *pic) +{ + /* pic_parameter_set_id, seq_parameter_set_id */ + bitstream_put_ue(bs, pic->pic_parameter_set_id); + bitstream_put_ue(bs, pic->seq_parameter_set_id); + + bitstream_put_ui(bs, pic->pic_fields.bits.entropy_coding_mode_flag, 1); + + /* pic_order_present_flag: 0 */ + bitstream_put_ui(bs, 0, 1); + + /* num_slice_groups_minus1 */ + bitstream_put_ue(bs, 0); + + bitstream_put_ue(bs, pic->num_ref_idx_l0_active_minus1); + bitstream_put_ue(bs, pic->num_ref_idx_l1_active_minus1); + + bitstream_put_ui(bs, pic->pic_fields.bits.weighted_pred_flag, 1); + bitstream_put_ui(bs, pic->pic_fields.bits.weighted_bipred_idc, 2); + + /* pic_init_qp_minus26, pic_init_qs_minus26, chroma_qp_index_offset */ + bitstream_put_se(bs, pic->pic_init_qp - 26); + bitstream_put_se(bs, 0); + bitstream_put_se(bs, 0); + + bitstream_put_ui(bs, pic->pic_fields.bits.deblocking_filter_control_present_flag, 1); + + /* constrained_intra_pred_flag, redundant_pic_cnt_present_flag */ + bitstream_put_ui(bs, 0, 1); + bitstream_put_ui(bs, 0, 1); + + bitstream_put_ui(bs, pic->pic_fields.bits.transform_8x8_mode_flag, 1); + + /* pic_scaling_matrix_present_flag */ + bitstream_put_ui(bs, 0, 1); + bitstream_put_se(bs, pic->second_chroma_qp_index_offset ); + + rbsp_trailing_bits(bs); +} + +static int +build_packed_pic_buffer(struct vaapi_recorder *r, + void **header_buffer) +{ + struct bitstream bs; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS); + pps_rbsp(&bs, &r->encoder.param.pic); + bitstream_end(&bs); + + *header_buffer = bs.buffer; + return bs.bit_offset; +} + +static int +build_packed_seq_buffer(struct vaapi_recorder *r, + void **header_buffer) +{ + struct bitstream bs; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS); + sps_rbsp(&bs, &r->encoder.param.seq, r->encoder.constraint_set_flag); + bitstream_end(&bs); + + *header_buffer = bs.buffer; + return bs.bit_offset; +} + +static int +create_packed_header_buffers(struct vaapi_recorder *r, VABufferID *buffers, + VAEncPackedHeaderType type, + void *data, int bit_length) +{ + VAEncPackedHeaderParameterBuffer packed_header; + VAStatus status; + + packed_header.type = type; + packed_header.bit_length = bit_length; + packed_header.has_emulation_bytes = 0; + + status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, + VAEncPackedHeaderParameterBufferType, + sizeof packed_header, 1, &packed_header, + &buffers[0]); + if (status != VA_STATUS_SUCCESS) + return 0; + + status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, + VAEncPackedHeaderDataBufferType, + (bit_length + 7) / 8, 1, data, &buffers[1]); + if (status != VA_STATUS_SUCCESS) { + vaDestroyBuffer(r->va_dpy, buffers[0]); + return 0; + } + + return 2; +} + +static int +encoder_prepare_headers(struct vaapi_recorder *r, VABufferID *buffers) +{ + VABufferID *p; + + int bit_length; + void *data; + + p = buffers; + + bit_length = build_packed_seq_buffer(r, &data); + p += create_packed_header_buffers(r, p, VAEncPackedHeaderSequence, + data, bit_length); + free(data); + + bit_length = build_packed_pic_buffer(r, &data); + p += create_packed_header_buffers(r, p, VAEncPackedHeaderPicture, + data, bit_length); + free(data); + + return p - buffers; +} + +static VAStatus +encoder_render_picture(struct vaapi_recorder *r, VASurfaceID input, + VABufferID *buffers, int count) +{ + VAStatus status; + + status = vaBeginPicture(r->va_dpy, r->encoder.ctx, input); + if (status != VA_STATUS_SUCCESS) + return status; + + status = vaRenderPicture(r->va_dpy, r->encoder.ctx, buffers, count); + if (status != VA_STATUS_SUCCESS) + return status; + + status = vaEndPicture(r->va_dpy, r->encoder.ctx); + if (status != VA_STATUS_SUCCESS) + return status; + + return vaSyncSurface(r->va_dpy, input); +} + +static VABufferID +encoder_create_output_buffer(struct vaapi_recorder *r) +{ + VABufferID output_buf; + VAStatus status; + + status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, + VAEncCodedBufferType, r->encoder.output_size, + 1, NULL, &output_buf); + if (status == VA_STATUS_SUCCESS) + return output_buf; + else + return VA_INVALID_ID; +} + +enum output_write_status { + OUTPUT_WRITE_SUCCESS, + OUTPUT_WRITE_OVERFLOW, + OUTPUT_WRITE_FATAL +}; + +static enum output_write_status +encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf) +{ + VACodedBufferSegment *segment; + VAStatus status; + int count; + + status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment); + if (status != VA_STATUS_SUCCESS) + return OUTPUT_WRITE_FATAL; + + if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) { + r->encoder.output_size *= 2; + vaUnmapBuffer(r->va_dpy, output_buf); + return OUTPUT_WRITE_OVERFLOW; + } + + count = write(r->output_fd, segment->buf, segment->size); + + vaUnmapBuffer(r->va_dpy, output_buf); + + if (count < 0) + return OUTPUT_WRITE_FATAL; + + return OUTPUT_WRITE_SUCCESS; +} + +static void +encoder_encode(struct vaapi_recorder *r, VASurfaceID input) +{ + VABufferID output_buf = VA_INVALID_ID; + + VABufferID buffers[8]; + int count = 0; + int i, slice_type; + enum output_write_status ret; + + if ((r->frame_count % r->encoder.intra_period) == 0) + slice_type = SLICE_TYPE_I; + else + slice_type = SLICE_TYPE_P; + + buffers[count++] = encoder_update_seq_parameters(r); + buffers[count++] = encoder_update_misc_hdr_parameter(r); + buffers[count++] = encoder_update_slice_parameter(r, slice_type); + + for (i = 0; i < count; i++) + if (buffers[i] == VA_INVALID_ID) + goto bail; + + if (r->frame_count == 0) + count += encoder_prepare_headers(r, buffers + count); + + do { + output_buf = encoder_create_output_buffer(r); + if (output_buf == VA_INVALID_ID) + goto bail; + + buffers[count++] = + encoder_update_pic_parameters(r, output_buf); + if (buffers[count - 1] == VA_INVALID_ID) + goto bail; + + encoder_render_picture(r, input, buffers, count); + ret = encoder_write_output(r, output_buf); + + vaDestroyBuffer(r->va_dpy, output_buf); + output_buf = VA_INVALID_ID; + + vaDestroyBuffer(r->va_dpy, buffers[--count]); + } while (ret == OUTPUT_WRITE_OVERFLOW); + + if (ret == OUTPUT_WRITE_FATAL) + r->error = errno; + + for (i = 0; i < count; i++) + vaDestroyBuffer(r->va_dpy, buffers[i]); + + r->frame_count++; + return; + +bail: + for (i = 0; i < count; i++) + vaDestroyBuffer(r->va_dpy, buffers[i]); + if (output_buf != VA_INVALID_ID) + vaDestroyBuffer(r->va_dpy, output_buf); +} + + +static int +setup_vpp(struct vaapi_recorder *r) +{ + VAStatus status; + + status = vaCreateConfig(r->va_dpy, VAProfileNone, + VAEntrypointVideoProc, NULL, 0, + &r->vpp.cfg); + if (status != VA_STATUS_SUCCESS) { + weston_log("vaapi: failed to create VPP config\n"); + return -1; + } + + status = vaCreateContext(r->va_dpy, r->vpp.cfg, r->width, r->height, + 0, NULL, 0, &r->vpp.ctx); + if (status != VA_STATUS_SUCCESS) { + weston_log("vaapi: failed to create VPP context\n"); + goto err_cfg; + } + + status = vaCreateBuffer(r->va_dpy, r->vpp.ctx, + VAProcPipelineParameterBufferType, + sizeof(VAProcPipelineParameterBuffer), + 1, NULL, &r->vpp.pipeline_buf); + if (status != VA_STATUS_SUCCESS) { + weston_log("vaapi: failed to create VPP pipeline buffer\n"); + goto err_ctx; + } + + status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420, + r->width, r->height, &r->vpp.output, 1, + NULL, 0); + if (status != VA_STATUS_SUCCESS) { + weston_log("vaapi: failed to create YUV surface\n"); + goto err_buf; + } + + return 0; + +err_buf: + vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf); +err_ctx: + vaDestroyConfig(r->va_dpy, r->vpp.ctx); +err_cfg: + vaDestroyConfig(r->va_dpy, r->vpp.cfg); + + return -1; +} + +static void +vpp_destroy(struct vaapi_recorder *r) +{ + vaDestroySurfaces(r->va_dpy, &r->vpp.output, 1); + vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf); + vaDestroyConfig(r->va_dpy, r->vpp.ctx); + vaDestroyConfig(r->va_dpy, r->vpp.cfg); +} + +static int +setup_worker_thread(struct vaapi_recorder *r) +{ + pthread_mutex_init(&r->mutex, NULL); + pthread_cond_init(&r->input_cond, NULL); + pthread_create(&r->worker_thread, NULL, worker_thread_function, r); + + return 1; +} + +static void +destroy_worker_thread(struct vaapi_recorder *r) +{ + pthread_mutex_lock(&r->mutex); + + /* Make sure the worker thread finishes */ + r->destroying = 1; + pthread_cond_signal(&r->input_cond); + + pthread_mutex_unlock(&r->mutex); + + pthread_join(r->worker_thread, NULL); + + pthread_mutex_destroy(&r->mutex); + pthread_cond_destroy(&r->input_cond); +} + +struct vaapi_recorder * +vaapi_recorder_create(int drm_fd, int width, int height, const char *filename) +{ + struct vaapi_recorder *r; + VAStatus status; + int major, minor; + int flags; + + r = zalloc(sizeof *r); + if (r == NULL) + return NULL; + + r->width = width; + r->height = height; + r->drm_fd = drm_fd; + + if (setup_worker_thread(r) < 0) + goto err_free; + + flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC; + r->output_fd = open(filename, flags, 0644); + if (r->output_fd < 0) + goto err_thread; + + r->va_dpy = vaGetDisplayDRM(drm_fd); + if (!r->va_dpy) { + weston_log("failed to create VA display\n"); + goto err_fd; + } + + status = vaInitialize(r->va_dpy, &major, &minor); + if (status != VA_STATUS_SUCCESS) { + weston_log("vaapi: failed to initialize display\n"); + goto err_fd; + } + + if (setup_vpp(r) < 0) { + weston_log("vaapi: failed to initialize VPP pipeline\n"); + goto err_va_dpy; + } + + if (setup_encoder(r) < 0) { + goto err_vpp; + } + + return r; + +err_vpp: + vpp_destroy(r); +err_va_dpy: + vaTerminate(r->va_dpy); +err_fd: + close(r->output_fd); +err_thread: + destroy_worker_thread(r); +err_free: + free(r); + + return NULL; +} + +void +vaapi_recorder_destroy(struct vaapi_recorder *r) +{ + destroy_worker_thread(r); + + encoder_destroy(r); + vpp_destroy(r); + + vaTerminate(r->va_dpy); + + close(r->output_fd); + close(r->drm_fd); + + free(r); +} + +static VAStatus +create_surface_from_fd(struct vaapi_recorder *r, int prime_fd, + int stride, VASurfaceID *surface) +{ + VASurfaceAttrib va_attribs[2]; + VASurfaceAttribExternalBuffers va_attrib_extbuf; + VAStatus status; + + unsigned long buffer_fd = prime_fd; + + va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX; + va_attrib_extbuf.width = r->width; + va_attrib_extbuf.height = r->height; + va_attrib_extbuf.data_size = r->height * stride; + va_attrib_extbuf.num_planes = 1; + va_attrib_extbuf.pitches[0] = stride; + va_attrib_extbuf.offsets[0] = 0; + va_attrib_extbuf.buffers = &buffer_fd; + va_attrib_extbuf.num_buffers = 1; + va_attrib_extbuf.flags = 0; + va_attrib_extbuf.private_data = NULL; + + va_attribs[0].type = VASurfaceAttribMemoryType; + va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; + va_attribs[0].value.type = VAGenericValueTypeInteger; + va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; + + va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; + va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; + va_attribs[1].value.type = VAGenericValueTypePointer; + va_attribs[1].value.value.p = &va_attrib_extbuf; + + status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_RGB32, + r->width, r->height, surface, 1, + va_attribs, 2); + + return status; +} + +static VAStatus +convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface) +{ + VAProcPipelineParameterBuffer *pipeline_param; + VAStatus status; + + status = vaMapBuffer(r->va_dpy, r->vpp.pipeline_buf, + (void **) &pipeline_param); + if (status != VA_STATUS_SUCCESS) + return status; + + memset(pipeline_param, 0, sizeof *pipeline_param); + + pipeline_param->surface = rgb_surface; + pipeline_param->surface_color_standard = VAProcColorStandardNone; + + pipeline_param->output_background_color = 0xff000000; + pipeline_param->output_color_standard = VAProcColorStandardNone; + + status = vaUnmapBuffer(r->va_dpy, r->vpp.pipeline_buf); + if (status != VA_STATUS_SUCCESS) + return status; + + status = vaBeginPicture(r->va_dpy, r->vpp.ctx, r->vpp.output); + if (status != VA_STATUS_SUCCESS) + return status; + + status = vaRenderPicture(r->va_dpy, r->vpp.ctx, + &r->vpp.pipeline_buf, 1); + if (status != VA_STATUS_SUCCESS) + return status; + + status = vaEndPicture(r->va_dpy, r->vpp.ctx); + if (status != VA_STATUS_SUCCESS) + return status; + + return status; +} + +static void +recorder_frame(struct vaapi_recorder *r) +{ + VASurfaceID rgb_surface; + VAStatus status; + + status = create_surface_from_fd(r, r->input.prime_fd, + r->input.stride, &rgb_surface); + if (status != VA_STATUS_SUCCESS) { + weston_log("[libva recorder] " + "failed to create surface from bo\n"); + return; + } + + close(r->input.prime_fd); + + status = convert_rgb_to_yuv(r, rgb_surface); + if (status != VA_STATUS_SUCCESS) { + weston_log("[libva recorder] " + "color space conversion failed\n"); + return; + } + + encoder_encode(r, r->vpp.output); + + vaDestroySurfaces(r->va_dpy, &rgb_surface, 1); +} + +static void * +worker_thread_function(void *data) +{ + struct vaapi_recorder *r = data; + + pthread_mutex_lock(&r->mutex); + + while (!r->destroying) { + if (!r->input.valid) + pthread_cond_wait(&r->input_cond, &r->mutex); + + /* If the thread is awaken by destroy_worker_thread(), + * there might not be valid input */ + if (!r->input.valid) + continue; + + recorder_frame(r); + r->input.valid = 0; + } + + pthread_mutex_unlock(&r->mutex); + + return NULL; +} + +int +vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride) +{ + int ret = 0; + + pthread_mutex_lock(&r->mutex); + + if (r->error) { + errno = r->error; + ret = -1; + goto unlock; + } + + /* The mutex is never released while encoding, so this point should + * never be reached if input.valid is true. */ + assert(!r->input.valid); + + r->input.prime_fd = prime_fd; + r->input.stride = stride; + r->input.valid = 1; + pthread_cond_signal(&r->input_cond); + +unlock: + pthread_mutex_unlock(&r->mutex); + + return ret; +} diff --git a/spa/plugins/libva/vaapi-recorder.h b/spa/plugins/libva/vaapi-recorder.h new file mode 100644 index 000000000..6b194aa81 --- /dev/null +++ b/spa/plugins/libva/vaapi-recorder.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VAAPI_RECORDER_H_ +#define _VAAPI_RECORDER_H_ + +struct vaapi_recorder; + +struct vaapi_recorder * +vaapi_recorder_create(int drm_fd, int width, int height, const char *filename); +void +vaapi_recorder_destroy(struct vaapi_recorder *r); +int +vaapi_recorder_frame(struct vaapi_recorder *r, int fd, int stride); + +#endif /* _VAAPI_RECORDER_H_ */ diff --git a/spa/plugins/meson.build b/spa/plugins/meson.build index d4adc3430..e8a9e2310 100644 --- a/spa/plugins/meson.build +++ b/spa/plugins/meson.build @@ -1,6 +1,8 @@ subdir('alsa') subdir('audiomixer') subdir('audiotestsrc') +subdir('ffmpeg') +#subdir('libva') subdir('volume') subdir('v4l2') subdir('xv') diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index f47afb843..06a11d4ac 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -77,6 +77,8 @@ typedef struct { SpaPollFd fds[1]; SpaPollItem poll; SpaPortInfo info; + SpaAllocParam *params[1]; + SpaAllocParamBuffers param_buffers; SpaPortStatus status; } SpaV4l2State; @@ -467,6 +469,8 @@ spa_v4l2_source_node_port_use_buffers (SpaHandle *handle, static SpaResult spa_v4l2_source_node_port_alloc_buffers (SpaHandle *handle, uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, SpaBuffer **buffers, uint32_t *n_buffers) { @@ -573,13 +577,15 @@ spa_v4l2_source_get_interface (SpaHandle *handle, return SPA_RESULT_OK; } -SpaHandle * -spa_v4l2_source_new (void) +static SpaResult +v4l2_source_init (const SpaHandleFactory *factory, + SpaHandle *handle) { - SpaHandle *handle; SpaV4l2Source *this; - handle = calloc (1, sizeof (SpaV4l2Source)); + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + handle->get_interface = spa_v4l2_source_get_interface; this = (SpaV4l2Source *) handle; @@ -591,5 +597,35 @@ spa_v4l2_source_new (void) this->state[0].info.flags = SPA_PORT_INFO_FLAG_NONE; this->state[0].status.flags = SPA_PORT_STATUS_FLAG_NONE; - return handle; + + return SPA_RESULT_OK; } + +static const SpaInterfaceInfo v4l2_source_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +v4l2_source_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &v4l2_source_interfaces[index]; + + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_v4l2_source_factory = +{ "v4l2-source", + NULL, + sizeof (SpaV4l2Source), + v4l2_source_init, + v4l2_source_enum_interface_info, +}; diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index a5e53c56d..d730aac99 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -314,13 +314,19 @@ spa_v4l2_set_format (SpaV4l2Source *this, SpaFormat *format, bool try_only) state->fmt = fmt; state->info.flags = SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER; - state->info.minsize = fmt.fmt.pix.sizeimage; - state->info.stride = fmt.fmt.pix.bytesperline; - state->info.min_buffers = 2; - state->info.max_buffers = MAX_BUFFERS; - state->info.align = 16; state->info.maxbuffering = -1; state->info.latency = -1; + + state->info.n_params = 1; + state->info.params = state->params; + state->params[0] = &state->param_buffers.param; + state->param_buffers.param.type = SPA_ALLOC_PARAM_TYPE_BUFFERS; + state->param_buffers.param.size = sizeof (&state->buffers); + state->param_buffers.minsize = fmt.fmt.pix.sizeimage; + state->param_buffers.stride = fmt.fmt.pix.bytesperline; + state->param_buffers.min_buffers = 2; + state->param_buffers.max_buffers = MAX_BUFFERS; + state->param_buffers.align = 16; state->info.features = NULL; return 0; @@ -334,6 +340,7 @@ spa_v4l2_close (SpaV4l2Source *this) if (!state->opened) return 0; + fprintf (stderr, "close\n"); if (close(state->fd)) perror ("close"); diff --git a/spa/plugins/v4l2/v4l2.c b/spa/plugins/v4l2/v4l2.c index 95a3e5cb5..7534bc7e0 100644 --- a/spa/plugins/v4l2/v4l2.c +++ b/spa/plugins/v4l2/v4l2.c @@ -20,59 +20,21 @@ #include #include -SpaHandle * spa_v4l2_source_new (void); - -static SpaResult -v4l2_source_instantiate (const SpaHandleFactory *factory, - SpaHandle **handle) -{ - if (factory == NULL || handle == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; - - *handle = spa_v4l2_source_new (); - - return SPA_RESULT_OK; -} - -static const SpaInterfaceInfo v4l2_source_interfaces[] = -{ - { SPA_INTERFACE_ID_NODE, - SPA_INTERFACE_ID_NODE_NAME, - SPA_INTERFACE_ID_NODE_DESCRIPTION, - }, -}; - -static SpaResult -v4l2_source_enum_interface_info (const SpaHandleFactory *factory, - unsigned int index, - const SpaInterfaceInfo **info) -{ - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *info = &v4l2_source_interfaces[index]; - - return SPA_RESULT_OK; -} - -static const SpaHandleFactory factories[] = -{ - { "v4l2-source", - NULL, - v4l2_source_instantiate, - v4l2_source_enum_interface_info, - }, -}; - +extern const SpaHandleFactory spa_v4l2_source_factory; SpaResult spa_enum_handle_factory (unsigned int index, const SpaHandleFactory **factory) { - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *factory = &factories[index]; + if (factory == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + switch (index) { + case 0: + *factory = &spa_v4l2_source_factory; + break; + default: + return SPA_RESULT_ENUM_END; + } return SPA_RESULT_OK; } diff --git a/spa/plugins/volume/plugin.c b/spa/plugins/volume/plugin.c index 7b22036c9..b61991047 100644 --- a/spa/plugins/volume/plugin.c +++ b/spa/plugins/volume/plugin.c @@ -20,59 +20,21 @@ #include #include -SpaHandle * spa_volume_new (void); - -static SpaResult -volume_instantiate (const SpaHandleFactory *factory, - SpaHandle **handle) -{ - if (factory == NULL || handle == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; - - *handle = spa_volume_new (); - - return SPA_RESULT_OK; -} - -static const SpaInterfaceInfo volume_interfaces[] = -{ - { SPA_INTERFACE_ID_NODE, - SPA_INTERFACE_ID_NODE_NAME, - SPA_INTERFACE_ID_NODE_DESCRIPTION, - }, -}; - -static SpaResult -volume_enum_interface_info (const SpaHandleFactory *factory, - unsigned int index, - const SpaInterfaceInfo **info) -{ - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *info = &volume_interfaces[index]; - - return SPA_RESULT_OK; -} - -static const SpaHandleFactory factories[] = -{ - { "volume", - NULL, - volume_instantiate, - volume_enum_interface_info, - }, -}; - +extern const SpaHandleFactory spa_volume_factory; SpaResult spa_enum_handle_factory (unsigned int index, const SpaHandleFactory **factory) { - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *factory = &factories[index]; + if (factory == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + switch (index) { + case 0: + *factory = &spa_volume_factory; + break; + default: + return SPA_RESULT_ENUM_END; + } return SPA_RESULT_OK; } diff --git a/spa/plugins/volume/volume.c b/spa/plugins/volume/volume.c index a061aac09..8e41cac2b 100644 --- a/spa/plugins/volume/volume.c +++ b/spa/plugins/volume/volume.c @@ -398,6 +398,8 @@ spa_volume_node_port_use_buffers (SpaHandle *handle, static SpaResult spa_volume_node_port_alloc_buffers (SpaHandle *handle, uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, SpaBuffer **buffers, uint32_t *n_buffers) { @@ -594,13 +596,15 @@ spa_volume_get_interface (SpaHandle *handle, return SPA_RESULT_OK; } -SpaHandle * -spa_volume_new (void) +static SpaResult +volume_instantiate (const SpaHandleFactory *factory, + SpaHandle *handle) { - SpaHandle *handle; SpaVolume *this; - handle = calloc (1, sizeof (SpaVolume)); + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + handle->get_interface = spa_volume_get_interface; this = (SpaVolume *) handle; @@ -619,5 +623,34 @@ spa_volume_new (void) this->ports[0].status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT; this->ports[1].status.flags = SPA_PORT_STATUS_FLAG_NONE; - return handle; + return SPA_RESULT_OK; } + +static const SpaInterfaceInfo volume_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +volume_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &volume_interfaces[index]; + + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_volume_factory = +{ "volume", + NULL, + sizeof (SpaVolume), + volume_instantiate, + volume_enum_interface_info, +}; diff --git a/spa/plugins/xv/xv-sink.c b/spa/plugins/xv/xv-sink.c index c19794d30..0c63828a4 100644 --- a/spa/plugins/xv/xv-sink.c +++ b/spa/plugins/xv/xv-sink.c @@ -442,6 +442,8 @@ spa_xv_sink_node_port_use_buffers (SpaHandle *handle, static SpaResult spa_xv_sink_node_port_alloc_buffers (SpaHandle *handle, uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, SpaBuffer **buffers, uint32_t *n_buffers) { @@ -546,13 +548,15 @@ spa_xv_sink_get_interface (SpaHandle *handle, return SPA_RESULT_OK; } -SpaHandle * -spa_xv_sink_new (void) +static SpaResult +xv_sink_init (const SpaHandleFactory *factory, + SpaHandle *handle) { - SpaHandle *handle; SpaXvSink *this; - handle = calloc (1, sizeof (SpaXvSink)); + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + handle->get_interface = spa_xv_sink_get_interface; this = (SpaXvSink *) handle; @@ -564,5 +568,35 @@ spa_xv_sink_new (void) this->info.flags = SPA_PORT_INFO_FLAG_NONE; this->status.flags = SPA_PORT_STATUS_FLAG_NONE; - return handle; + + return SPA_RESULT_OK; } + +static const SpaInterfaceInfo xv_sink_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +xv_sink_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &xv_sink_interfaces[index]; + + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_xv_sink_factory = +{ "xv-sink", + NULL, + sizeof (SpaXvSink), + xv_sink_init, + xv_sink_enum_interface_info, +}; diff --git a/spa/plugins/xv/xv.c b/spa/plugins/xv/xv.c index b381562e3..c117ed54d 100644 --- a/spa/plugins/xv/xv.c +++ b/spa/plugins/xv/xv.c @@ -20,59 +20,21 @@ #include #include -SpaHandle * spa_xv_sink_new (void); - -static SpaResult -xv_sink_instantiate (const SpaHandleFactory *factory, - SpaHandle **handle) -{ - if (factory == NULL || handle == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; - - *handle = spa_xv_sink_new (); - - return SPA_RESULT_OK; -} - -static const SpaInterfaceInfo xv_sink_interfaces[] = -{ - { SPA_INTERFACE_ID_NODE, - SPA_INTERFACE_ID_NODE_NAME, - SPA_INTERFACE_ID_NODE_DESCRIPTION, - }, -}; - -static SpaResult -xv_sink_enum_interface_info (const SpaHandleFactory *factory, - unsigned int index, - const SpaInterfaceInfo **info) -{ - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *info = &xv_sink_interfaces[index]; - - return SPA_RESULT_OK; -} - -static const SpaHandleFactory factories[] = -{ - { "xv-sink", - NULL, - xv_sink_instantiate, - xv_sink_enum_interface_info, - }, -}; - +extern const SpaHandleFactory spa_xv_sink_factory; SpaResult spa_enum_handle_factory (unsigned int index, const SpaHandleFactory **factory) { - if (index >= 1) - return SPA_RESULT_ENUM_END; - - *factory = &factories[index]; + if (factory == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + switch (index) { + case 0: + *factory = &spa_xv_sink_factory; + break; + default: + return SPA_RESULT_ENUM_END; + } return SPA_RESULT_OK; } diff --git a/spa/tests/meson.build b/spa/tests/meson.build index d2df5f149..b5964cf4f 100644 --- a/spa/tests/meson.build +++ b/spa/tests/meson.build @@ -1,9 +1,11 @@ executable('test-mixer', 'test-mixer.c', include_directories : inc, dependencies : [dl_lib, pthread_lib], + link_with : spalib, install : false) executable('test-v4l2', 'test-v4l2.c', include_directories : inc, dependencies : [dl_lib, sdl_dep, pthread_lib], + link_with : spalib, install : false) diff --git a/spa/tests/test-mixer.c b/spa/tests/test-mixer.c index 217c20b2e..35afc55dc 100644 --- a/spa/tests/test-mixer.c +++ b/spa/tests/test-mixer.c @@ -75,7 +75,8 @@ make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char if (strcmp (factory->name, name)) continue; - if ((res = factory->instantiate (factory, handle)) < 0) { + *handle = calloc (1, factory->size); + if ((res = factory->init (factory, *handle)) < 0) { printf ("can't make factory instance: %d\n", res); return res; } @@ -200,8 +201,8 @@ make_nodes (AppData *data) printf ("got get_props error %d\n", res); value.type = SPA_PROP_TYPE_STRING; - value.size = strlen ("hw:1")+1; - value.value = "hw:1"; + value.value = "hw:0"; + value.size = strlen (value.value)+1; props->set_prop (props, spa_props_index_for_name (props, "device"), &value); if ((res = data->sink_node->set_props (data->sink, props)) < 0) diff --git a/spa/tests/test-v4l2.c b/spa/tests/test-v4l2.c index 763b63282..3a1a54090 100644 --- a/spa/tests/test-v4l2.c +++ b/spa/tests/test-v4l2.c @@ -29,9 +29,10 @@ #include #include +#include #include -#define USE_BUFFER +#undef USE_BUFFER #define MAX_BUFFERS 8 @@ -87,7 +88,8 @@ make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char if (strcmp (factory->name, name)) continue; - if ((res = factory->instantiate (factory, handle)) < 0) { + *handle = calloc (1, factory->size); + if ((res = factory->init (factory, *handle)) < 0) { printf ("can't make factory instance: %d\n", res); return res; } @@ -309,14 +311,7 @@ negotiate_formats (AppData *data) if ((res = data->source_node->port_get_info (data->source, 0, &info)) < 0) return res; - printf ("flags: %d\n", info->flags); - printf ("minsize: %zd\n", info->minsize); - printf ("stride: %zd\n", info->stride); - printf ("min_buffers: %d\n", info->min_buffers); - printf ("max_buffers: %d\n", info->max_buffers); - printf ("align: %d\n", info->align); - printf ("maxbuffering: %d\n", info->maxbuffering); - printf ("latency: %lu\n", info->latency); + spa_debug_port_info (info); #ifdef USE_BUFFER alloc_buffers (data); diff --git a/spa/tools/spa-inspect.c b/spa/tools/spa-inspect.c index a15d5893f..81a267072 100644 --- a/spa/tools/spa-inspect.c +++ b/spa/tools/spa-inspect.c @@ -188,7 +188,8 @@ inspect_factory (const SpaHandleFactory *factory) else printf (" none\n"); - if ((res = factory->instantiate (factory, &handle)) < 0) { + handle = calloc (1, factory->size); + if ((res = factory->init (factory, handle)) < 0) { printf ("can't make factory instance: %d\n", res); return; }