summaryrefslogtreecommitdiffstats
path: root/meta-rcar-gen2
diff options
context:
space:
mode:
Diffstat (limited to 'meta-rcar-gen2')
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch391
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch33
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch4183
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/0004-Fix-GST-error-complainings.patch33
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service13
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend12
6 files changed, 0 insertions, 4665 deletions
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch
deleted file mode 100644
index 51c2671..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch
+++ /dev/null
@@ -1,391 +0,0 @@
-From f26e3ef1f484319a6c803158af16050574363587 Mon Sep 17 00:00:00 2001
-From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
-Date: Wed, 2 Nov 2016 17:14:43 +0300
-Subject: [PATCH 1/2] Add virtual output support
-
-To enable virtual output set "virtual" property in core section
-to desirable number of virtual outputs. Then add settings to
-each virtual output in output sections. Name of the outputs
-will be virtual1, virtual2... etc.
-
-Signed-off-by: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
-Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
----
- src/compositor-drm.c | 329 +++++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 329 insertions(+)
-
-diff --git a/src/compositor-drm.c b/src/compositor-drm.c
-index ab493ad..09611a4 100644
---- a/src/compositor-drm.c
-+++ b/src/compositor-drm.c
-@@ -193,6 +193,11 @@ struct drm_output {
-
- struct vaapi_recorder *recorder;
- struct wl_listener recorder_frame_listener;
-+
-+ /* not real output device */
-+ int virtual;
-+ /* Timer for updating frame */
-+ struct wl_event_source *virtual_finish_frame_timer;
- };
-
- /*
-@@ -1337,6 +1342,33 @@ drm_output_destroy(struct weston_output *output_base)
- free(output);
- }
-
-+static void
-+virtual_output_destroy(struct weston_output *output_base)
-+{
-+ struct drm_output *output = (struct drm_output *) output_base;
-+ struct drm_backend *c =
-+ (struct drm_backend *) output->base.compositor;
-+
-+ c->crtc_allocator &= ~(1 << output->crtc_id);
-+ c->connector_allocator &= ~(1 << output->connector_id);
-+
-+ if (c->use_pixman) {
-+ drm_output_fini_pixman(output);
-+ } else {
-+ gl_renderer->output_destroy(output_base);
-+ gbm_surface_destroy(output->surface);
-+ }
-+
-+ weston_plane_release(&output->fb_plane);
-+ weston_plane_release(&output->cursor_plane);
-+
-+ weston_output_destroy(&output->base);
-+
-+
-+ wl_event_source_remove(output->virtual_finish_frame_timer);
-+ free(output);
-+}
-+
- /**
- * Find the closest-matching mode for a given target
- *
-@@ -2459,6 +2491,289 @@ err_free:
- }
-
- static void
-+virtual_output_start_repaint_loop(struct weston_output *output)
-+{
-+ struct timespec now;
-+
-+ weston_compositor_read_presentation_clock(output->compositor, &now);
-+ weston_output_finish_frame(output, &now, PRESENTATION_FEEDBACK_INVALID);
-+}
-+
-+
-+static int
-+virtual_output_repaint(struct weston_output *output_base,
-+ pixman_region32_t *damage)
-+{
-+ struct drm_output *output = (struct drm_output *) output_base;
-+ struct timespec ts;
-+ uint32_t msec_next;
-+ uint32_t msec_current;
-+
-+ msec_next = (output->base.frame_time + 1000000UL / output->base.current_mode->refresh) ;
-+
-+ if (output->destroy_pending)
-+ return -1;
-+
-+ if (!output->next)
-+ drm_output_render(output, damage);
-+ if (!output->next)
-+ return -1;
-+
-+ drm_output_set_cursor(output);
-+
-+ output->page_flip_pending = 1;
-+
-+ weston_compositor_read_presentation_clock(output_base->compositor, &ts);
-+
-+ msec_current = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
-+
-+ /*
-+ * If we somehow late with updating frame, then fireup timer immediately (1 msec)
-+ */
-+ wl_event_source_timer_update(output->virtual_finish_frame_timer, (msec_next > msec_current) ?
-+ msec_next - msec_current : 1);
-+
-+ return 0;
-+}
-+
-+static int
-+virtual_finish_frame_handler(void *data)
-+{
-+ struct drm_output *output = (struct drm_output *) data;
-+ struct timespec ts;
-+
-+ /* We don't set page_flip_pending on start_repaint_loop, in that case
-+ * we just want to page flip to the current buffer to get an accurate
-+ * timestamp */
-+ if (output->page_flip_pending) {
-+ drm_output_release_fb(output, output->current);
-+ output->current = output->next;
-+ output->next = NULL;
-+ }
-+
-+ output->page_flip_pending = 0;
-+
-+ if (output->destroy_pending)
-+ drm_output_destroy(&output->base);
-+ else if (!output->vblank_pending) {
-+ weston_compositor_read_presentation_clock(output->base.compositor, &ts);
-+
-+ weston_output_finish_frame(&output->base, &ts,
-+ PRESENTATION_FEEDBACK_INVALID);
-+
-+ /* We can't call this from frame_notify, because the output's
-+ * repaint needed flag is cleared just after that */
-+ if (output->recorder)
-+ weston_output_schedule_repaint(&output->base);
-+ }
-+
-+ return 1;
-+}
-+
-+/*
-+ * Virtual output connector that could be used for simulating output
-+ * device for clients and/or streaming of video
-+ */
-+static int
-+create_output_for_virtual_connector(struct drm_backend *ec,
-+ int x, int y, struct udev_device *drm_device)
-+{
-+ struct wl_event_loop *loop;
-+ static int virtual_id = 1; /* as other outputs numbered */
-+ struct drm_output *output;
-+ struct drm_mode *drm_mode, *next, *configured;
-+ struct weston_mode *m;
-+ struct weston_config_section *section;
-+ drmModeModeInfo modeline;
-+ int i, width, height, scale, fps;
-+ int recorded_output;
-+ char name[32], *s;
-+ enum output_config config;
-+ uint32_t transform;
-+
-+ output = zalloc(sizeof *output);
-+ if (output == NULL)
-+ return -1;
-+
-+ output->base.subpixel = WL_OUTPUT_SUBPIXEL_NONE; //drm_subpixel_to_wayland(connector->subpixel);
-+ output->base.make = "CogentEmbedded,Inc";
-+ output->base.serial_number = "";
-+ wl_list_init(&output->base.mode_list);
-+
-+ snprintf(name, 32, "virtual%d", virtual_id++);
-+ output->base.name = strdup(name);
-+
-+ section = weston_config_get_section(ec->compositor->config, "output", "name",
-+ output->base.name);
-+
-+ weston_config_section_get_bool(section, "recorder", &recorded_output, 0);
-+ if (recorded_output) {
-+ char model[64];
-+ char *ip;
-+ int port;
-+
-+ weston_config_section_get_string(section, "ip", &ip, "<nil>");
-+ weston_config_section_get_int(section, "port", &port, -1);
-+ snprintf(model, 64, "Virtual RTP %s:%d", ip, port);
-+ output->base.model = strdup(model);
-+ } else {
-+ output->base.model = "Virtual Display";
-+ }
-+
-+ weston_config_section_get_string(section, "mode", &s, "preferred");
-+ if (strcmp(s, "off") == 0)
-+ config = OUTPUT_CONFIG_OFF;
-+ else if (sscanf(s, "%dx%d@%d", &width, &height, &fps) == 3)
-+ config = OUTPUT_CONFIG_MODE;
-+ else if (parse_modeline(s, &modeline) == 0)
-+ config = OUTPUT_CONFIG_MODELINE;
-+ else {
-+ weston_log("Invalid mode \"%s\" for output %s\n",
-+ s, output->base.name);
-+ width = 1280;
-+ height = 720;
-+ fps = 60;
-+ config = OUTPUT_CONFIG_MODE;
-+ }
-+ free(s);
-+
-+ weston_config_section_get_int(section, "scale", &scale, 1);
-+ weston_config_section_get_string(section, "transform", &s, "normal");
-+ if (weston_parse_transform(s, &transform) < 0)
-+ weston_log("Invalid transform \"%s\" for output %s\n",
-+ s, output->base.name);
-+ free(s);
-+
-+ if (get_gbm_format_from_section(section,
-+ ec->format,
-+ &output->format) == -1)
-+ output->format = ec->format;
-+
-+ weston_config_section_get_string(section, "seat", &s, "");
-+ setup_output_seat_constraint(ec, &output->base, s);
-+ free(s);
-+
-+ output->pipe = i;
-+ ec->crtc_allocator |= (1 << output->crtc_id);
-+ output->connector_id = 0;
-+ ec->connector_allocator |= (1 << output->connector_id);
-+
-+ /* this is virtual output */
-+ output->virtual = 1;
-+
-+
-+ output->original_crtc = NULL;
-+ output->dpms_prop = NULL;
-+
-+ /* set static mode */
-+ if (1) {
-+ /* TODO: calculate proper mode settings to get desirable framerate */
-+ drmModeModeInfo static_drm_mode = {
-+ width * height * fps,
-+ width, 0, 0, width, width,
-+ height, 0, 0, height, height,
-+ fps * 1000,
-+ 0, //flags
-+ 0, //type
-+ "virtual"
-+ };
-+
-+ drm_mode = drm_output_add_mode(output, &static_drm_mode);
-+ if (!drm_mode)
-+ goto err_free;
-+
-+ drm_mode->base.refresh = fps * 1000;
-+ }
-+
-+ if (config == OUTPUT_CONFIG_OFF) {
-+ weston_log("Disabling output %s\n", output->base.name);
-+ drmModeSetCrtc(ec->drm.fd, output->crtc_id,
-+ 0, 0, 0, 0, 0, NULL);
-+ goto err_free;
-+ }
-+
-+ wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) {
-+ if (config == OUTPUT_CONFIG_MODE &&
-+ width == drm_mode->base.width &&
-+ height == drm_mode->base.height)
-+ configured = drm_mode;
-+ }
-+
-+ output->base.current_mode = &configured->base;
-+
-+ if (output->base.current_mode == NULL) {
-+ weston_log("no available modes for %s\n", output->base.name);
-+ goto err_free;
-+ }
-+
-+ output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
-+
-+ weston_output_init(&output->base, ec->compositor, x, y,
-+ 100, 100 * height / width, /* FIXME: calculate proper mm_width and mm_height */
-+ transform, scale);
-+
-+ if (ec->use_pixman) {
-+ if (drm_output_init_pixman(output, ec) < 0) {
-+ weston_log("Failed to init output pixman state\n");
-+ goto err_output;
-+ }
-+ } else if (drm_output_init_egl(output, ec) < 0) {
-+ weston_log("Failed to init output gl state\n");
-+ goto err_output;
-+ }
-+
-+ output->backlight = NULL;
-+
-+ weston_compositor_add_output(ec->compositor, &output->base);
-+
-+ output->base.connection_internal = 1;
-+
-+ loop = wl_display_get_event_loop(ec->compositor->wl_display);
-+ output->virtual_finish_frame_timer = wl_event_loop_add_timer(loop, virtual_finish_frame_handler, output);
-+
-+ output->base.start_repaint_loop = virtual_output_start_repaint_loop;
-+ output->base.repaint = virtual_output_repaint;
-+ output->base.destroy = virtual_output_destroy;
-+ output->base.assign_planes = NULL;
-+ output->base.set_backlight = NULL;
-+ output->base.set_dpms = NULL;
-+ output->base.switch_mode = drm_output_switch_mode;
-+
-+ output->base.gamma_size = 0;
-+ output->base.set_gamma = drm_output_set_gamma;
-+
-+ weston_plane_init(&output->cursor_plane, ec->compositor, 0, 0);
-+ weston_plane_init(&output->fb_plane, ec->compositor, 0, 0);
-+
-+ weston_compositor_stack_plane(ec->compositor, &output->cursor_plane, NULL);
-+ weston_compositor_stack_plane(ec->compositor, &output->fb_plane,
-+ &ec->compositor->primary_plane);
-+
-+ weston_log("Output %s, ()\n",
-+ output->base.name);
-+ wl_list_for_each(m, &output->base.mode_list, link)
-+ weston_log_continue(STAMP_SPACE "mode %dx%d@%.1f\n",
-+ m->width, m->height, m->refresh / 1000.0);
-+
-+ return 0;
-+
-+err_output:
-+ weston_output_destroy(&output->base);
-+err_free:
-+ wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
-+ base.link) {
-+ wl_list_remove(&drm_mode->base.link);
-+ free(drm_mode);
-+ }
-+
-+ ec->crtc_allocator &= ~(1 << output->crtc_id);
-+ ec->connector_allocator &= ~(1 << output->connector_id);
-+ free(output);
-+
-+ return -1;
-+}
-+
-+static void
- create_sprites(struct drm_backend *b)
- {
- struct drm_sprite *sprite;
-@@ -2531,10 +2846,12 @@ static int
- create_outputs(struct drm_backend *b, uint32_t option_connector,
- struct udev_device *drm_device)
- {
-+ struct weston_config_section *section;
- drmModeConnector *connector;
- drmModeRes *resources;
- int i;
- int x = 0, y = 0;
-+ int virtual;
-
- resources = drmModeGetResources(b->drm.fd);
- if (!resources) {
-@@ -2580,6 +2897,18 @@ create_outputs(struct drm_backend *b, uint32_t option_connector,
- drmModeFreeConnector(connector);
- }
-
-+ section = weston_config_get_section(b->compositor->config, "core", NULL, NULL);
-+ weston_config_section_get_int(section, "virtual", &virtual, 0);
-+
-+ for (i = 0; i < virtual; i++) {
-+ if (create_output_for_virtual_connector(b, x, y,
-+ drm_device) < 0)
-+ continue;
-+ x += container_of(b->compositor->output_list.prev,
-+ struct weston_output,
-+ link)->width;
-+ }
-+
- if (wl_list_empty(&b->compositor->output_list)) {
- weston_log("No currently active connector found.\n");
- drmModeFreeResources(resources);
---
-2.7.4
-
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch
deleted file mode 100644
index 2683afe..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 6c320c1319f0ef0888d9a9be614c13e5756a3228 Mon Sep 17 00:00:00 2001
-From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
-Date: Wed, 2 Nov 2016 17:16:29 +0300
-Subject: [PATCH 2/2] Get DMA fd on bo
-
----
- src/compositor-drm.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/compositor-drm.c b/src/compositor-drm.c
-index 09611a4..85c7d0a 100644
---- a/src/compositor-drm.c
-+++ b/src/compositor-drm.c
-@@ -143,6 +143,7 @@ struct drm_fb {
- struct drm_output *output;
- uint32_t fb_id, stride, handle, size;
- int fd;
-+ int dmafd;
- int is_client_buffer;
- struct weston_buffer_reference buffer_ref;
-
-@@ -411,6 +412,8 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
- goto err_free;
- }
-
-+ fb->dmafd = gbm_bo_get_fd(bo);
-+
- gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
-
- return fb;
---
-2.7.4
-
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch
deleted file mode 100644
index e6eccc6..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch
+++ /dev/null
@@ -1,4183 +0,0 @@
-From 73a18d5178129591b0c58d0f0150ce0a62d7c39a Mon Sep 17 00:00:00 2001
-From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
-Date: Wed, 2 Nov 2016 17:36:41 +0300
-Subject: [PATCH] Add gst-recorder for h264 output streaming
-
-To use gst-recorder run weston with arg --gst-record. Add
-recorder=true property to output section of one of the outputs
-(real or virtual). Use properties ip, port to set host of RTP
-stream. Use property bitrate to set desirable maximum h264 bitrate.
-
-Signed-off-by: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
-Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
----
- Makefile.am | 17 +
- configure.ac | 11 +
- src/compositor-drm.c | 202 ++++++-
- src/gst-recorder.c | 1222 +++++++++++++++++++++++++++++++++++++++++
- src/gst-recorder.h | 58 ++
- src/media-ctl/libmediactl.c | 955 ++++++++++++++++++++++++++++++++
- src/media-ctl/libv4l2subdev.c | 759 +++++++++++++++++++++++++
- src/media-ctl/mediactl-priv.h | 64 +++
- src/media-ctl/mediactl.h | 423 ++++++++++++++
- src/media-ctl/tools.h | 32 ++
- src/media-ctl/v4l2subdev.h | 258 +++++++++
- 11 files changed, 3999 insertions(+), 2 deletions(-)
- create mode 100644 src/gst-recorder.c
- create mode 100644 src/gst-recorder.h
- create mode 100644 src/media-ctl/libmediactl.c
- create mode 100644 src/media-ctl/libv4l2subdev.c
- create mode 100644 src/media-ctl/mediactl-priv.h
- create mode 100644 src/media-ctl/mediactl.h
- create mode 100644 src/media-ctl/tools.h
- create mode 100644 src/media-ctl/v4l2subdev.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 62719c9..1624e7c 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -267,6 +267,23 @@ drm_backend_la_SOURCES += src/vaapi-recorder.c src/vaapi-recorder.h
- drm_backend_la_LIBADD += $(LIBVA_LIBS)
- drm_backend_la_CFLAGS += $(LIBVA_CFLAGS)
- endif
-+
-+if ENABLE_GST_RECORDER
-+drm_backend_la_SOURCES += \
-+ src/gst-recorder.c \
-+ src/gst-recorder.h \
-+ src/v4l2-device.h \
-+ src/media-ctl/libmediactl.c \
-+ src/media-ctl/libv4l2subdev.c \
-+ src/media-ctl/mediactl-priv.h \
-+ src/media-ctl/mediactl.h \
-+ src/media-ctl/tools.h \
-+ src/media-ctl/v4l2subdev.h
-+drm_backend_la_LIBADD += $(LIBVA_LIBS) \
-+ -lgstallocators-1.0 \
-+ -lgstvideo-1.0
-+drm_backend_la_CFLAGS += $(LIBVA_CFLAGS)
-+endif
- endif
-
- if ENABLE_WAYLAND_COMPOSITOR
-diff --git a/configure.ac b/configure.ac
-index b8fd67f..5040813 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -321,6 +321,17 @@ if test x$enable_vaapi_recorder != xno; then
- fi
- AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$have_libva" = xyes)
-
-+AC_ARG_ENABLE(gst-recorder, [ --enable-gst-recorder],,
-+ enable_gst_recorder=yes)
-+if test x$enable_gst_recorder != xno; then
-+ AC_DEFINE([BUILD_GST_RECORDER], [1], [Build the gst recorder])
-+ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0 >= 1.0])
-+
-+ CPPFLAGS="$CPPFLAGS $GSTREAMER_CFLAGS"
-+ LIBS="$LIBS $GSTREAMER_LIBS -lgstapp-1.0"
-+fi
-+AM_CONDITIONAL(ENABLE_GST_RECORDER, test "x$enable_gst_recorder" != xno)
-+
-
- AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes)
- if test x$have_jpeglib = xyes; then
-diff --git a/src/compositor-drm.c b/src/compositor-drm.c
-index 85c7d0a..4dd0dfa 100644
---- a/src/compositor-drm.c
-+++ b/src/compositor-drm.c
-@@ -55,6 +55,7 @@
- #include "libinput-seat.h"
- #include "launcher-util.h"
- #include "vaapi-recorder.h"
-+#include "gst-recorder.h"
- #include "presentation_timing-server-protocol.h"
- #include "linux-dmabuf.h"
-
-@@ -124,6 +125,8 @@ struct drm_backend {
-
- int use_pixman;
-
-+ int enable_recorder;
-+
- uint32_t prev_state;
-
- struct udev_input input;
-@@ -192,7 +195,12 @@ struct drm_output {
- int current_image;
- pixman_region32_t previous_damage;
-
-+#ifdef BUILD_VAAPI_RECORDER
- struct vaapi_recorder *recorder;
-+#endif
-+#ifdef BUILD_GST_RECORDER
-+ struct gst_recorder *recorder;
-+#endif
- struct wl_listener recorder_frame_listener;
-
- /* not real output device */
-@@ -230,6 +238,7 @@ struct drm_parameters {
- int connector;
- int tty;
- int use_pixman;
-+ int enable_recorder;
- const char *seat_id;
- };
-
-@@ -2140,6 +2149,23 @@ parse_modeline(const char *s, drmModeModeInfo *mode)
- return 0;
- }
-
-+static int parse_crop_rect(const char *s, struct v4l2_rect* crop)
-+{
-+ crop->left = 0;
-+ crop->top = 0;
-+ crop->width = 0;
-+ crop->height = 0;
-+
-+ if (sscanf(s, "%dx%d@%dx%d",
-+ &crop->width,
-+ &crop->height,
-+ &crop->top,
-+ &crop->left) != 4)
-+ return -1;
-+
-+ return 0;
-+}
-+
- static void
- setup_output_seat_constraint(struct drm_backend *b,
- struct weston_output *output,
-@@ -3320,7 +3346,170 @@ recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
- recorder_destroy(output);
- }
- }
--#else
-+#endif
-+
-+#ifdef BUILD_GST_RECORDER
-+static void
-+recorder_destroy(struct drm_backend *c, struct drm_output *output)
-+{
-+ wl_list_remove(&output->recorder_frame_listener.link);
-+
-+ gst_recorder_destroy(output->recorder);
-+ output->recorder = NULL;
-+
-+ output->base.disable_planes--;
-+
-+ weston_log("[gst recorder] done\n");
-+}
-+
-+static void
-+recorder_frame_notify(struct wl_listener *listener, void *data)
-+{
-+ int ret = 0;
-+ struct drm_output *output;
-+ struct drm_backend *c;
-+
-+
-+ output = container_of(listener, struct drm_output,
-+ recorder_frame_listener);
-+ c = (struct drm_backend *) output->base.compositor;
-+
-+ if (!output->recorder) {
-+ weston_log("%s: output have no recorder enabled\n",
-+ output->base.name);
-+ return;
-+ }
-+
-+ if (!output->current) {
-+ weston_log("%s: frame notify while current frame == NULL\n",
-+ output->base.name);
-+ return;
-+ }
-+
-+ if (output->current->dmafd) {
-+ ret = gst_recorder_frame_dmafd(output->recorder, output->current->dmafd,
-+ output->current->stride);
-+ }
-+
-+ if (ret < 0) {
-+ weston_log("[gst recorder] aborted: %m\n");
-+ recorder_destroy(c, output);
-+ }
-+}
-+
-+static int
-+recorder_enable(struct drm_backend *c, struct drm_output *output)
-+{
-+ int enable_recorder = 0;
-+ struct gst_recorder_settings *settings;
-+ struct weston_config_section *section;
-+ char* s;
-+ struct v4l2_rect crop = { .width = output->base.current_mode->width,
-+ .height = output->base.current_mode->height,
-+ .top = 0,
-+ .left = 0 };
-+
-+ if (output->recorder)
-+ return -1;
-+
-+ section = weston_config_get_section(c->compositor->config, "output", "name",
-+ output->base.name);
-+
-+ weston_config_section_get_bool(section, "recorder", &enable_recorder, 0);
-+
-+ if (!enable_recorder)
-+ return 0;
-+
-+ /* TODO: add support for NV16 or NV12 */
-+ if (output->format != GBM_FORMAT_XRGB8888) {
-+ weston_log("[gst recorder] %s: "
-+ "output format not supported\n", output->base.name);
-+ return -1;
-+ }
-+
-+ settings = malloc(sizeof(* settings));
-+ weston_config_section_get_string(section, "ip", &settings->ip, NULL);
-+ if (!settings->ip)
-+ goto err;
-+ weston_config_section_get_int(section, "port", &settings->port, -1);
-+ /* default gives about 16 Mbit/s at 1280x720@60FPS */
-+ weston_config_section_get_int(section, "bitrate", &settings->bitrate, 300000);
-+
-+ settings->width = output->base.current_mode->width;
-+ settings->height = output->base.current_mode->height;
-+
-+ settings->crop = crop;
-+
-+ weston_config_section_get_string(section, "crop", &s, NULL);
-+ if (s) {
-+ if (parse_crop_rect(s, &settings->crop)) {
-+ weston_log("[gst recorder] %s:"
-+ " failed to parse crop parameter\n",
-+ output->base.name);
-+ goto err;
-+ }
-+ }
-+
-+
-+ output->recorder =
-+ gst_recorder_create(settings);
-+ if (!output->recorder) {
-+ weston_log("[gst recorder] %s:"
-+ " failed to create gst recorder\n",
-+ output->base.name);
-+ goto err;
-+ }
-+
-+ output->base.disable_planes++;
-+
-+ output->recorder_frame_listener.notify = recorder_frame_notify;
-+ wl_signal_add(&output->base.frame_signal,
-+ &output->recorder_frame_listener);
-+
-+ weston_output_schedule_repaint(&output->base);
-+
-+ weston_log("[gst recorder] %s:"
-+ " recorder initialized\n",
-+ output->base.name);
-+
-+ return 0;
-+err:
-+ weston_log("[gst recorder] %s:"
-+ " invalid settings\n",
-+ output->base.name);
-+ free(settings);
-+ return -1;
-+}
-+
-+static void
-+recorders_enable(struct drm_backend *c)
-+{
-+ struct drm_output *output;
-+
-+ wl_list_for_each(output, &c->compositor->output_list, base.link) {
-+ recorder_enable(c, output);
-+ }
-+}
-+
-+static void
-+recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
-+ void *data)
-+{
-+ struct drm_backend *c = data;
-+ struct drm_output *output;
-+
-+ /* fix this */
-+ wl_list_for_each(output, &c->compositor->output_list, base.link) {
-+ if (!output->recorder)
-+ recorder_enable(c, output);
-+ else
-+ recorder_destroy(c, output);
-+ }
-+}
-+#endif
-+
-+#if !defined(BUILD_VAAPI_RECORDER) && !defined(BUILD_GST_RECORDER)
-+
- static void
- recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
- void *data)
-@@ -3422,7 +3611,8 @@ drm_backend_create(struct weston_compositor *compositor,
- goto err_base;
-
- b->use_pixman = param->use_pixman;
--
-+ b->enable_recorder = param->enable_recorder;
-+
- /* Check if we run drm-backend using weston-launch */
- compositor->launcher = weston_launcher_connect(compositor, param->tty,
- param->seat_id, true);
-@@ -3465,6 +3655,10 @@ drm_backend_create(struct weston_compositor *compositor,
- }
- }
-
-+#ifdef BUILD_GST_RECORDER
-+ gst_recorder_init();
-+#endif
-+
- b->base.destroy = drm_destroy;
- b->base.restore = drm_restore;
-
-@@ -3529,6 +3723,9 @@ drm_backend_create(struct weston_compositor *compositor,
- recorder_binding, b);
- weston_compositor_add_debug_binding(compositor, KEY_W,
- renderer_switch_binding, b);
-+ /* enable GST recording-streaming */
-+ if (b->enable_recorder)
-+ recorders_enable(b);
-
- if (compositor->renderer->import_dmabuf) {
- if (linux_dmabuf_setup(compositor) < 0)
-@@ -3576,6 +3773,7 @@ backend_init(struct weston_compositor *compositor, int *argc, char *argv[],
- { WESTON_OPTION_INTEGER, "tty", 0, &param.tty },
- { WESTON_OPTION_BOOLEAN, "current-mode", 0, &option_current_mode },
- { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &param.use_pixman },
-+ { WESTON_OPTION_BOOLEAN, "gst-record", 0, &param.enable_recorder },
- };
-
- param.seat_id = default_seat;
-diff --git a/src/gst-recorder.c b/src/gst-recorder.c
-new file mode 100644
-index 0000000..c84d410
---- /dev/null
-+++ b/src/gst-recorder.c
-@@ -0,0 +1,1222 @@
-+/*
-+ * Copyright © 2016 Cogent Embedded Inc
-+ *
-+ * Permission to use, copy, modify, distribute, and sell this software and
-+ * its documentation for any purpose is hereby granted without fee, provided
-+ * that the above copyright notice appear in all copies and that both that
-+ * copyright notice and this permission notice appear in supporting
-+ * documentation, and that the name of the copyright holders not be used in
-+ * advertising or publicity pertaining to distribution of the software
-+ * without specific, written prior permission. The copyright holders make
-+ * no representations about the suitability of this software for any
-+ * purpose. It is provided "as is" without express or implied warranty.
-+ *
-+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
-+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
-+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
-+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
-+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-+ */
-+
-+/*
-+ * TODO:
-+ * 1) Add format parameter to virtual display to render in another format
-+ * with v4l2-renderer
-+ * 2) Add capability to use already NV12 rendered frame
-+ */
-+#include "config.h"
-+
-+#include <stdlib.h>
-+#include <stdint.h>
-+#include <string.h>
-+#include <unistd.h>
-+#include <assert.h>
-+#include <errno.h>
-+
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <sys/mman.h>
-+#include <sys/ioctl.h>
-+#include <fcntl.h>
-+
-+#include <pthread.h>
-+
-+#include "compositor.h"
-+#include "gst-recorder.h"
-+
-+/* VSP includes */
-+#include <mediactl/mediactl.h>
-+#include <mediactl/v4l2subdev.h>
-+
-+/* Gstreamer includes */
-+#include <gst/gst.h>
-+#include <gst/app/gstappsrc.h>
-+#include <gst/rtp/gstrtpbuffer.h>
-+
-+#include <gst/video/video.h>
-+#include <gst/video/gstvideometa.h>
-+#include <gst/video/gstvideopool.h>
-+#include <gst/allocators/gstdmabuf.h>
-+
-+struct vsp_data;
-+
-+#define DEFAULT_FPS 60
-+
-+typedef enum _vsp_port_n {
-+ VSP_PORT_INPUT = 0,
-+ VSP_PORT_INPUT0 = VSP_PORT_INPUT,
-+ VSP_PORT_INPUT1,
-+ VSP_PORT_INPUT2,
-+ VSP_PORT_INPUT3,
-+ VSP_PORT_OUTPUT
-+} vsp_port_n;
-+
-+struct gst_recorder {
-+ struct gst_recorder_settings *set;
-+ int frame_count;
-+ int input_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;
-+
-+ /* GLib */
-+ GMainContext *gcontext;
-+ /* Gstreamer stuff */
-+ GstElement *pipeline;
-+ /* AppSrc */
-+ GstAppSrc *appsrc;
-+ /* ...and source pad */
-+ GstPad *appsrc_pad;
-+ /* OMX encoder buffer pool */
-+ GstBufferPool *omx_pool;
-+ /* bus */
-+ GstBus *bus;
-+ /* timestamp */
-+ GstClockTime timestamp;
-+ uint32_t ts_last_frame;
-+ /* to be removed */
-+ guint callback_tag;
-+
-+ struct vsp_data *vsp;
-+};
-+
-+/*******************************************************************************
-+ * VSP related code
-+ ******************************************************************************/
-+
-+#define VSP_OUTPUT_BUFFERS_PLANE 2
-+
-+/* #define VSP_OUTPUT_NV16 1 */
-+
-+/* ...number of input/output pads (WPF1-3 are not implemented) */
-+#define VSP_PADS_NUM 1
-+
-+/*******************************************************************************
-+ * Local types definition
-+ ******************************************************************************/
-+
-+struct vsp_media_pad
-+{
-+ struct media_pad *infmt_pad;
-+ struct media_pad *outfmt_pad;
-+ struct media_entity *entity;
-+ int fd;
-+};
-+
-+typedef struct vsp_media_pad vsp_media_pad_t;
-+
-+struct vsp_data
-+{
-+ /* ...media device */
-+ struct media_device *media;
-+
-+ /* ...VSP input/output pads */
-+ vsp_media_pad_t input, output;
-+
-+ /* mutex */
-+ pthread_mutex_t mutex;
-+
-+ /* user count */
-+ int users;
-+};
-+
-+struct vsp_data *vsp_g = NULL;
-+
-+/* ...type declarations */
-+typedef struct vsp_data vsp_data_t;
-+
-+static int
-+gst_recorder_process_dmafd(struct gst_recorder *r, int fd, int stride);
-+
-+/* ...module initialization (a bit of salami) */
-+static vsp_data_t *
-+vsp_init(const char *devname)
-+{
-+ vsp_data_t *vsp;
-+ struct media_device *media;
-+ const struct media_device_info *info;
-+ const char *dev;
-+ char buf[256];
-+ char *endp, *p;
-+ struct media_entity *entity;
-+
-+ /* ...create data structure */
-+ if ((vsp = malloc(sizeof(*vsp))) == NULL)
-+ {
-+ weston_log("failed to allocate memory");
-+ errno = ENOMEM;
-+ return NULL;
-+ }
-+ memset(vsp, 0, sizeof(*vsp));
-+
-+ pthread_mutex_init(&vsp->mutex, NULL);
-+
-+ /* ...create media device */
-+ if ((vsp->media = media = media_device_new(devname)) == NULL)
-+ {
-+ weston_log("failed to open device '%s'\n", devname);
-+ goto error;
-+ }
-+ else if ((errno = -media_device_enumerate(media)) != 0)
-+ {
-+ weston_log("failed to enumerate device '%s'\n", devname);
-+ goto error_media;
-+ }
-+ else if ((info = media_get_info(media)) == NULL)
-+ {
-+ weston_log("failed to get media info data\n");
-+ goto error_media;
-+ }
-+ else
-+ {
-+ dev = ((p = strchr(info->bus_info, ':')) ? p + 1 : info->bus_info);
-+ weston_log("open media device: %s (%s)\n", info->bus_info, dev);
-+ }
-+
-+ /* ...reset links */
-+ if (media_reset_links(media) != 0)
-+ {
-+ weston_log("failed to reset media device\n");
-+ goto error_media;
-+ }
-+
-+ /* ...setup RPF.0:1 -> WPF.0:0 link */
-+ snprintf(buf, sizeof(buf), "'%s rpf.0':1 -> '%s wpf.0':0 [1]", dev, dev);
-+ if (media_parse_setup_link(media, buf, &endp) != 0)
-+ {
-+ weston_log("failed to setup link '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ /* ...setup WPF.0:1 -> WPF.0 output link */
-+ snprintf(buf, sizeof(buf), "'%s wpf.0':1 -> '%s wpf.0 output':0 [1]", dev, dev);
-+ if (media_parse_setup_link(media, buf, &endp) != 0)
-+ {
-+ weston_log("failed to setup link '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ /* ...specify input/output-format of RPF pad */
-+ snprintf(buf, sizeof(buf), "'%s rpf.0':0", dev);
-+ if ((vsp->input.infmt_pad = media_parse_pad(media, buf, NULL)) == NULL)
-+ {
-+ weston_log("failed to parse pad '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ snprintf(buf, sizeof(buf), "'%s rpf.0':1", dev);
-+ if ((vsp->input.outfmt_pad = media_parse_pad(media, buf, NULL)) == NULL)
-+ {
-+ weston_log("failed to parse pad '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ snprintf(buf, sizeof(buf), "%s rpf.0", dev);
-+ if ((vsp->input.entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL)
-+ {
-+ weston_log("failed to parse entity '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ /* ...get input file-descriptor */
-+ snprintf(buf, sizeof(buf), "%s rpf.0 input", dev);
-+ if ((entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL)
-+ {
-+ weston_log("entity '%s' not found\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+ else if (v4l2_subdev_open(entity) != 0)
-+ {
-+ weston_log("failed to open subdev '%s'\n", buf);
-+ goto error_media;
-+ }
-+ else if ((vsp->input.fd = open(media_entity_get_devname(entity), O_RDWR/* | O_NONBLOCK*/)) < 0)
-+ {
-+ weston_log("failed to open device '%s'\n", media_entity_get_devname(entity));
-+ goto error_media;
-+ }
-+ else
-+ {
-+ weston_log("input pad setup ('%s':'%s')\n", buf, media_entity_get_devname(entity));
-+ }
-+
-+ /* ...specify input/output formats of WPF pad */
-+ snprintf(buf, sizeof(buf), "'%s wpf.0':0", dev);
-+ if ((vsp->output.infmt_pad = media_parse_pad(media, buf, NULL)) == NULL)
-+ {
-+ weston_log("failed to parse pad '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ snprintf(buf, sizeof(buf), "'%s wpf.0':1", dev);
-+ if ((vsp->output.outfmt_pad = media_parse_pad(media, buf, NULL)) == NULL)
-+ {
-+ weston_log("failed to parse pad '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ snprintf(buf, sizeof(buf), "%s wpf.0", dev);
-+ if ((vsp->output.entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL)
-+ {
-+ weston_log("failed to parse entity '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+
-+ /* ...get a file descriptor for the output */
-+ snprintf(buf, sizeof(buf), "%s wpf.0 output", dev);
-+ if ((entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL)
-+ {
-+ weston_log("failed to get entity '%s'\n", buf);
-+ errno = EINVAL;
-+ goto error_media;
-+ }
-+ else if (v4l2_subdev_open(entity) != 0)
-+ {
-+ weston_log("failed to open subdev '%s'\n", buf);
-+ goto error_media;
-+ }
-+ else if ((vsp->output.fd = open(media_entity_get_devname(entity), O_RDWR | O_NONBLOCK)) < 0)
-+ {
-+ weston_log("failed to open device '%s'\n", media_entity_get_devname(entity));
-+ goto error_media;
-+ }
-+ else
-+ {
-+ weston_log("output pad setup (%s:%s)\n", buf, media_entity_get_devname(entity));
-+ }
-+
-+ weston_log("vsp-device '%s' created\n", devname);
-+
-+ return vsp;
-+
-+error_media:
-+ /* ...destroy media device and all associated structures */
-+ media_device_unref(vsp->media);
-+
-+error:
-+ /* ...destroy data structure */
-+ free(vsp);
-+ return NULL;
-+}
-+
-+static void
-+vsp_deinit(vsp_data_t *vsp)
-+{
-+ /* ...destroy media device and all associated structures */
-+ media_device_unref(vsp->media);
-+
-+ /* ...destroy data structure */
-+ free(vsp);
-+}
-+
-+/* ...set V4L2 device format */
-+static int
-+vsp_set_format(int fd, struct v4l2_format *fmt)
-+{
-+ __u32 w, h, c;
-+
-+ /* ...set format */
-+ if (ioctl(fd, VIDIOC_S_FMT, fmt) < 0) {
-+ weston_log("format set (fd=%d) failed: %d\n",
-+ fd, errno);
-+ }
-+
-+ /* ...it could get changed */
-+ w = fmt->fmt.pix_mp.width;
-+ h = fmt->fmt.pix_mp.height;
-+ c = fmt->fmt.pix_mp.pixelformat;
-+ /* weston_log("VSP: format set (fd=%d): %u*%u ('%c%c%c%c')\n", */
-+ /* fd, w, h, (__u8)c, (__u8)(c >> 8), (__u8)(c >> 16), (__u8)(c >> 24)); */
-+
-+ return 0;
-+}
-+
-+/* ...start streaming on specific V4L2 device */
-+static int
-+vsp_streaming_enable(vsp_data_t *vsp, vsp_port_n port, int enable)
-+{
-+ vsp_media_pad_t *pad = (port == VSP_PORT_INPUT) ? &vsp->input : &vsp->output;
-+ int fd = pad->fd;
-+ int type = (port == VSP_PORT_INPUT) ?
-+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-+
-+ return ioctl(fd, (enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type);
-+}
-+
-+/* ...prepare VSP filter for operation */
-+static int
-+vsp_set_formats(vsp_data_t *vsp, int width, int height, struct v4l2_rect* crop)
-+{
-+ vsp_media_pad_t *input = &vsp->input, *output = &vsp->output;
-+ struct v4l2_mbus_framefmt mfmt = { .width = width, .height = height };
-+ struct v4l2_format format;
-+
-+ /* ...configure RPF input pads; specify pixel format and size (one of YUV variants) */
-+ mfmt.width = width;
-+ mfmt.height = height;
-+ mfmt.code = V4L2_MBUS_FMT_ARGB8888_1X32;
-+ if (v4l2_subdev_set_format(input->infmt_pad->entity,
-+ &mfmt, input->infmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) {
-+ weston_log("VSP: input pad in format set failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ /* set a crop paramters */
-+ if (v4l2_subdev_set_selection(input->infmt_pad->entity, crop, input->infmt_pad->index,
-+ V4L2_SEL_TGT_CROP, V4L2_SUBDEV_FORMAT_ACTIVE)) {
-+ weston_log("set crop parameter failed: %dx%d@(%d,%d).\n",
-+ crop->width, crop->height, crop->left, crop->top);
-+ return -1;
-+ }
-+
-+ /* ...output is NV12 or NV16 or I420*/
-+ mfmt.width = crop->width;
-+ mfmt.height = crop->height;
-+ mfmt.code = V4L2_MBUS_FMT_AYUV8_1X32;
-+ if (v4l2_subdev_set_format(input->outfmt_pad->entity,
-+ &mfmt, input->outfmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) {
-+ weston_log("VSP: input pad out format set failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ /* ...specify input format */
-+ memset(&format, 0, sizeof(format));
-+ format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-+ format.fmt.pix_mp.width = /* crop-> */width;
-+ format.fmt.pix_mp.height = /* crop-> */height;
-+ format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_ABGR32;
-+ format.fmt.pix_mp.num_planes = 1;
-+ /* ...set input port format */
-+ if (vsp_set_format(input->fd, &format) < 0) {
-+ weston_log("VSP: input set format failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ /* ...both input and output are ARGB8888 always (now effective) */
-+ if (v4l2_subdev_set_format(output->infmt_pad->entity,
-+ &mfmt, output->infmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) {
-+ weston_log("VSP: output pad in format set failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ /* ...specify cropping area, probably? - tbd */
-+ if (v4l2_subdev_set_format(output->outfmt_pad->entity,
-+ &mfmt, output->outfmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) {
-+ weston_log("VSP: output pad in format set failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ /* ...setup output pads */
-+ memset(&format, 0, sizeof(format));
-+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-+ format.fmt.pix_mp.width = crop->width;
-+ format.fmt.pix_mp.height = crop->height;
-+#ifdef VSP_OUTPUT_NV16
-+ format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV16M;
-+#else
-+ format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
-+#endif
-+ format.fmt.pix_mp.num_planes = VSP_OUTPUT_BUFFERS_PLANE;
-+ /* ...set output buffer format */
-+ if (vsp_set_format(output->fd, &format) < 0) {
-+ weston_log("VSP: output set format failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+vsp_request_buffers(vsp_data_t *vsp, vsp_port_n port, unsigned int num)
-+{
-+ vsp_media_pad_t *pad = (port == VSP_PORT_INPUT) ? &vsp->input : &vsp->output;
-+ struct v4l2_requestbuffers reqbuf;
-+ int fd = pad->fd;
-+
-+ /* ...input buffers are DMA-fd, output buffers allocated by kernel */
-+ memset(&reqbuf, 0, sizeof(reqbuf));
-+ reqbuf.type = (port == VSP_PORT_INPUT) ?
-+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-+ reqbuf.memory = V4L2_MEMORY_DMABUF;
-+ reqbuf.count = num;
-+ if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
-+ weston_log("VSP: %s REQBUFS failed: %d\n",
-+ (port == VSP_PORT_INPUT) ? "input" : "output", errno);
-+ return -1;
-+ }
-+
-+ if (reqbuf.count != num) {
-+ weston_log("VSP: %s failed to request %d (!= %d) bufs\n",
-+ (port == VSP_PORT_INPUT) ? "input" : "output", num, reqbuf.count);
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+
-+/* ...enqueue dmafd buffer */
-+static int
-+vsp_input_buffer_queue_dmafd(vsp_data_t *vsp, int i, int dmafd)
-+{
-+ vsp_media_pad_t *pad = &vsp->input;
-+ struct v4l2_buffer buf;
-+ struct v4l2_plane planes[1];
-+
-+ /* ...set buffer parameters */
-+ memset(&buf, 0, sizeof(buf));
-+ memset(planes, 0, sizeof(planes));
-+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-+ buf.memory = V4L2_MEMORY_DMABUF;
-+ buf.index = i;
-+ buf.m.planes = planes;
-+ buf.length = 1;
-+ buf.m.planes[0].m.fd = dmafd;
-+
-+ /* ...submit buffer */
-+ if (ioctl(pad->fd, VIDIOC_QBUF, &buf) < 0) {
-+ weston_log("VSP: input dmafd (%d) buffer (%i) queue failed: %d\n",
-+ dmafd, i, errno);
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-+
-+/* ...dequeue dmafd buffer */
-+static int
-+vsp_input_buffer_dequeue_dmafd(vsp_data_t *vsp)
-+{
-+ vsp_media_pad_t *pad = &vsp->input;
-+ struct v4l2_buffer buf;
-+ struct v4l2_plane planes[1];
-+
-+ /* ...set buffer parameters */
-+ memset(&buf, 0, sizeof(buf));
-+ memset(planes, 0, sizeof(planes));
-+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-+ buf.memory = V4L2_MEMORY_DMABUF;
-+ buf.m.planes = planes;
-+ buf.length = 1;
-+
-+ if (ioctl(pad->fd, VIDIOC_DQBUF, &buf) < 0) {
-+ weston_log("VSP: input dmafd buffer de-queue failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ /* ...return buffer index */
-+ return buf.index;
-+}
-+
-+/* ...enqueue output buffer */
-+static int
-+vsp_output_buffer_queue_dmafd(vsp_data_t *vsp, int i, int dmafd[])
-+{
-+ vsp_media_pad_t *pad = &vsp->output;
-+ struct v4l2_plane planes[2];
-+ struct v4l2_buffer buf;
-+
-+ /* ...set buffer parameters (single-plane ARGB always) */
-+ memset(&buf, 0, sizeof(buf));
-+ memset(planes, 0, sizeof(planes));
-+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-+ buf.memory = V4L2_MEMORY_DMABUF;
-+ buf.index = i;
-+ buf.m.planes = planes;
-+ buf.length = VSP_OUTPUT_BUFFERS_PLANE;
-+ buf.m.planes[0].m.fd = dmafd[0];
-+ buf.m.planes[1].m.fd = dmafd[1];
-+
-+ /* ...submit buffer */
-+ if (ioctl(pad->fd, VIDIOC_QBUF, &buf) < 0) {
-+ weston_log("VSP: output dmafd queue failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-+
-+/* ...dequeue output buffer */
-+static int
-+vsp_output_buffer_dequeue_dmafd(vsp_data_t *vsp)
-+{
-+ vsp_media_pad_t *pad = &vsp->output;
-+ struct v4l2_buffer buf;
-+ struct v4l2_plane planes[2];
-+
-+ /* ...set buffer parameters */
-+ memset(&buf, 0, sizeof(buf));
-+ memset(planes, 0, sizeof(planes));
-+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-+ buf.memory = V4L2_MEMORY_DMABUF;
-+ buf.m.planes = planes;
-+ buf.length = VSP_OUTPUT_BUFFERS_PLANE;
-+
-+ if (ioctl(pad->fd, VIDIOC_DQBUF, &buf) < 0) {
-+ weston_log("VSP: output dmafd de-queue failed: %d\n", errno);
-+ return -1;
-+ }
-+
-+ /* ...return dequeue buffer index */
-+ return buf.index;
-+}
-+
-+/* ...get capturing interface file descriptor */
-+static int
-+vsp_capture_fd(vsp_data_t *vsp)
-+{
-+ return vsp->output.fd;
-+}
-+
-+/*******************************************************************************
-+ * Gstreamer stuff
-+ ******************************************************************************/
-+
-+static void
-+print_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
-+{
-+ int i, num;
-+
-+ num = gst_tag_list_get_tag_size (list, tag);
-+ for (i = 0; i < num; ++i) {
-+ const GValue *val;
-+
-+ /* Note: when looking for specific tags, use the gst_tag_list_get_xyz() API,
-+ * we only use the GValue approach here because it is more generic */
-+ val = gst_tag_list_get_value_index (list, tag, i);
-+ if (G_VALUE_HOLDS_STRING (val)) {
-+ weston_log("\t%20s : %s\n", tag, g_value_get_string (val));
-+ } else if (G_VALUE_HOLDS_UINT (val)) {
-+ weston_log("\t%20s : %u\n", tag, g_value_get_uint (val));
-+ } else if (G_VALUE_HOLDS_DOUBLE (val)) {
-+ weston_log("\t%20s : %g\n", tag, g_value_get_double (val));
-+ } else if (G_VALUE_HOLDS_BOOLEAN (val)) {
-+ weston_log("\t%20s : %s\n", tag,
-+ (g_value_get_boolean (val)) ? "true" : "false");
-+ } else if (GST_VALUE_HOLDS_BUFFER (val)) {
-+ GstBuffer *buf = gst_value_get_buffer (val);
-+ guint buffer_size = gst_buffer_get_size (buf);
-+
-+ weston_log("\t%20s : buffer of size %u\n", tag, buffer_size);
-+ } else if (GST_VALUE_HOLDS_DATE_TIME (val)) {
-+ GstDateTime *dt = g_value_get_boxed (val);
-+ gchar *dt_str = gst_date_time_to_iso8601_string (dt);
-+
-+ weston_log("\t%20s : %s\n", tag, dt_str);
-+ g_free (dt_str);
-+ } else {
-+ weston_log("\t%20s : tag of type '%s'\n", tag, G_VALUE_TYPE_NAME (val));
-+ }
-+ }
-+}
-+
-+static gboolean
-+gst_bus_callback(GstBus *bus, GstMessage *message, gpointer user_data)
-+{
-+ GTimeVal time;
-+ struct gst_recorder *r = user_data;
-+
-+ if (!r->pipeline) {
-+ weston_log("gst_pipeline: unexpected gst bus callback event, while pipeline==null\n");
-+ return TRUE;
-+ }
-+
-+ g_get_current_time(&time);
-+
-+ switch (GST_MESSAGE_TYPE(message))
-+ {
-+ case GST_MESSAGE_QOS:
-+ {
-+ GstFormat format;
-+ guint64 processed;
-+ guint64 dropped;
-+
-+ gst_message_parse_qos_stats (message, &format, &processed, &dropped);
-+ weston_log("gst_pipeline: qos from: %s processed %lld, dropped %lld\n",
-+ GST_OBJECT_NAME (message->src),
-+ processed, dropped);
-+ }
-+ break;
-+ case GST_MESSAGE_STREAM_STATUS:
-+ {
-+ const GValue *val;
-+
-+ val = gst_message_get_stream_status_object (message);
-+ weston_log("gst_pipeline: stream status type %s, value %p\n",
-+ G_VALUE_TYPE_NAME(val),
-+ g_value_get_object(val));
-+ }
-+ break;
-+ case GST_MESSAGE_TAG:
-+ {
-+ GstTagList *tags = NULL;
-+
-+ gst_message_parse_tag (message, &tags);
-+ weston_log("gst_pipeline: tag from element %s:\n",
-+ GST_OBJECT_NAME (message->src));
-+ gst_tag_list_foreach (tags, print_one_tag, NULL);
-+ weston_log("\n");
-+ }
-+ break;
-+ case GST_MESSAGE_STATE_CHANGED:
-+ {
-+ GstState oldstate, newstate;
-+
-+ gst_message_parse_state_changed(message, &oldstate, &newstate, NULL);
-+ weston_log("gst_pipeline: element %s changed state from %s to %s.\n",
-+ GST_OBJECT_NAME (message->src),
-+ gst_element_state_get_name (oldstate),
-+ gst_element_state_get_name (newstate));
-+
-+ /* if gstreamer become ready */
-+ if ((GST_MESSAGE_SRC(message) == GST_OBJECT(r->appsrc)) &&
-+ (newstate == GST_STATE_PAUSED)) {
-+ weston_log("gst_pipeline: pipeline ready\n");
-+ }
-+ }
-+ break;
-+ case GST_MESSAGE_ERROR:
-+ {
-+ GError *err;
-+ gchar *debug_info;
-+ gst_message_parse_error(message, &err, &debug_info);
-+ weston_log("gst_pipeline: error received from element %s: %s\n",
-+ GST_OBJECT_NAME(message->src), err->message);
-+ weston_log("gst_pipeline: debugging information: %s\n",
-+ debug_info ? debug_info : "none");
-+ g_clear_error (&err);
-+ g_free (debug_info);
-+ }
-+ break;
-+ case GST_MESSAGE_WARNING:
-+ {
-+ GError *err;
-+ gchar *debug_info;
-+ gst_message_parse_warning(message, &err, &debug_info);
-+ weston_log("gst_pipeline: warning received from element %s: %s\n",
-+ GST_OBJECT_NAME(message->src), err->message);
-+ weston_log("gst_pipeline: debugging information: %s\n",
-+ debug_info ? debug_info : "none");
-+ g_clear_error (&err);
-+ g_free (debug_info);
-+ }
-+ break;
-+ default:
-+ weston_log("gst_pipeline: %s from %s\n",
-+ GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME (message->src));
-+ break;
-+ }
-+ return TRUE;
-+}
-+
-+static void *
-+worker_thread_function(void *data)
-+{
-+ GstMessage *msg;
-+ struct gst_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;
-+
-+ /* TODO: move it to separate thread? */
-+ g_main_context_iteration(r->gcontext, FALSE);
-+
-+ do {
-+ msg = gst_bus_pop_filtered(r->bus,
-+ GST_MESSAGE_ANY);
-+ if (msg) {
-+ gst_bus_callback(r->bus, msg, r);
-+ }
-+ } while (msg);
-+
-+ /* check input */
-+ gst_recorder_process_dmafd(r, r->input.prime_fd, r->input.stride);
-+
-+ r->input.valid = 0;
-+ }
-+
-+ pthread_mutex_unlock(&r->mutex);
-+
-+ return NULL;
-+}
-+
-+static int
-+setup_worker_thread(struct gst_recorder *r)
-+{
-+ r->gcontext = g_main_context_new();
-+ 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 gst_recorder *r)
-+{
-+ /* Make sure the worker thread finishes */
-+ r->destroying = 1;
-+
-+ pthread_cond_signal(&r->input_cond);
-+
-+ pthread_join(r->worker_thread, NULL);
-+
-+ pthread_mutex_destroy(&r->mutex);
-+ pthread_cond_destroy(&r->input_cond);
-+}
-+
-+void weston_debug_function(GstDebugCategory* category, GstDebugLevel level,
-+ const gchar* file, const char* function,
-+ gint line, GObject* object, GstDebugMessage* message,
-+ gpointer data)
-+{
-+ weston_log("[GST]:%s %s:%d %s\n", file, function, line, gst_debug_message_get(message));
-+}
-+
-+void
-+gst_recorder_init(void)
-+{
-+ gst_init(NULL, 0);
-+
-+ /* VSP init */
-+ vsp_g = vsp_init("/dev/media0");
-+ if (!vsp_g)
-+ weston_log("[gst recorder] VSP init failed");
-+}
-+
-+static int
-+gst_recorder_find_omx_pool(struct gst_recorder *r)
-+{
-+ int ret = 0;
-+ GstCaps *caps;
-+ GstQuery *query;
-+ GstBufferPool *pool;
-+ GstStructure *config;
-+ guint size, min, max;
-+
-+ caps = gst_caps_new_simple ( "video/x-raw",
-+#ifdef VSP_OUTPUT_NV16
-+ "format", G_TYPE_STRING, "NV16",
-+#else
-+ "format", G_TYPE_STRING, "NV12",
-+#endif
-+ "width", G_TYPE_INT, r->set->crop.width,
-+ "height", G_TYPE_INT, r->set->crop.height,
-+ "framerate", GST_TYPE_FRACTION, 0, DEFAULT_FPS,
-+ NULL);
-+
-+ /* find a pool for the negotiated caps now */
-+ query = gst_query_new_allocation (caps, TRUE);
-+
-+ if (!gst_pad_peer_query (r->appsrc_pad, query)) {
-+ /* query failed, not a problem, we use the query defaults */
-+ weston_log("allocation query failed");
-+ ret = -1;
-+ goto err;
-+ }
-+
-+ weston_log("goot %d pools\n", gst_query_get_n_allocation_pools (query));
-+ if (gst_query_get_n_allocation_pools (query) > 0) {
-+ /* we got configuration from our peer, parse them */
-+ gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
-+ weston_log(" pool settings size %d, min %d, max %d\n", size, min, max);
-+ } else {
-+ weston_log("no pool queried\n");
-+ ret = -1;
-+ goto err;
-+ }
-+
-+ config = gst_buffer_pool_get_config (pool);
-+ gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
-+ gst_buffer_pool_config_set_params (config, caps, size, min, max);
-+ gst_buffer_pool_set_config (pool, config);
-+
-+ /* and activate */
-+ gst_buffer_pool_set_active (pool, TRUE);
-+
-+ r->omx_pool = pool;
-+
-+err:
-+ gst_query_unref (query);
-+ return ret;
-+}
-+
-+static int
-+gst_recorder_omx_buffer_acquire(struct gst_recorder *r, GstBuffer **ret_buf, int fd[])
-+{
-+ unsigned int i;
-+ GstFlowReturn ret;
-+ GstBuffer *buf;
-+ guint n_mem;
-+ GstMemory *mem;
-+
-+ ret = gst_buffer_pool_acquire_buffer(r->omx_pool, &buf, NULL);
-+ if (ret != GST_FLOW_OK) {
-+ weston_log("OMX buffer acquire failed\n");
-+ return -1;
-+ }
-+
-+ n_mem = gst_buffer_n_memory(buf);
-+ if (n_mem < 1) {
-+ weston_log("Buffer with no mem!\n");
-+ goto err_release;
-+ }
-+
-+ for (i = 0; i < n_mem; i++) {
-+ mem = gst_buffer_peek_memory (buf, i);
-+ if (!gst_is_dmabuf_memory (mem)) {
-+ weston_log("Mem not dmabuf\n");
-+ goto err_release;
-+ }
-+ fd[i] = gst_dmabuf_memory_get_fd (mem);
-+ }
-+
-+ *ret_buf = buf;
-+
-+ return 0;
-+err_release:
-+ gst_buffer_pool_release_buffer(r->omx_pool, buf);
-+ return -1;
-+}
-+
-+static int
-+gst_recorder_omx_buffer_release(struct gst_recorder *r, GstBuffer *buf)
-+{
-+ gst_buffer_pool_release_buffer(r->omx_pool, buf);
-+
-+ return 0;
-+}
-+
-+struct gst_recorder *
-+gst_recorder_create(struct gst_recorder_settings *settings)
-+{
-+ struct gst_recorder *r;
-+ char gst_pipe[1024];
-+ char *ptr = gst_pipe;
-+ GError *perror = NULL;
-+
-+ weston_log("gst_recorder_create (%dx%d) crop %dx%d at %d,%d\n",
-+ settings->width, settings->height, settings->crop.width,
-+ settings->crop.height, settings->crop.top, settings->crop.left);
-+
-+ if (!vsp_g) {
-+ weston_log("gst_recorder_create: no VSP\n");
-+ return NULL;
-+ }
-+
-+ r = calloc(1, sizeof *r);
-+ if (!r)
-+ return NULL;
-+ memset(r, 0, sizeof *r);
-+
-+ r->set = settings;
-+ r->timestamp = 0;
-+
-+ r->vsp = vsp_g;
-+ vsp_g->users++;
-+
-+ /* GST init */
-+ /* source (GST_FORMAT_BYTES) */
-+ ptr += sprintf(ptr,
-+ "appsrc name=src ! ");
-+
-+ /* omx */
-+ ptr += sprintf(ptr,
-+ "omxh264enc target-bitrate=%d control-rate=2 name=my_encoder ! "
-+ "video/x-h264,width=%d,height=%d ! ",
-+ r->set->bitrate, r->set->crop.width, r->set->crop.height);
-+
-+ /* rtp payloader */
-+ ptr += sprintf(ptr,
-+ "rtph264pay config-interval=1 name=my_h264pay ! queue ! ");
-+
-+ /* usp sink */
-+ ptr += sprintf(ptr,
-+ "udpsink host=%s ", r->set->ip);
-+ if (r->set->port > 0)
-+ ptr += sprintf(ptr,
-+ " port=%d name=my_udpsink", r->set->port);
-+
-+ weston_log("gst_pipeline: starting: %s\n", gst_pipe);
-+
-+ /* launch */
-+ r->pipeline = gst_parse_launch (gst_pipe, &perror);
-+ if (!r->pipeline) {
-+ weston_log("gst_pipeline: can not start pipeline: %s\n", perror->message);
-+ goto err_gst;
-+ }
-+
-+ /* get appsrc */
-+ r->appsrc = gst_bin_get_by_name(GST_BIN (r->pipeline), "src");
-+ if (!r->appsrc) {
-+ weston_log("gst_pipeline: can not get appsrc\n");
-+ goto err_gst;
-+ }
-+
-+ /* get bus */
-+ r->bus = gst_pipeline_get_bus (GST_PIPELINE(r->pipeline));
-+ if (!r->bus) {
-+ weston_log("gst_pipeline: can not get bus\n");
-+ goto err_gst;
-+ }
-+
-+ setup_worker_thread(r);
-+
-+ /* setup caps */
-+ g_object_set(G_OBJECT(r->appsrc), "caps",
-+ gst_caps_new_simple ( "video/x-raw",
-+#ifdef VSP_OUTPUT_NV16
-+ "format", G_TYPE_STRING, "NV16",
-+#else
-+ "format", G_TYPE_STRING, "NV12",
-+#endif
-+ "width", G_TYPE_INT, r->set->crop.width,
-+ "height", G_TYPE_INT, r->set->crop.height,
-+ "framerate", GST_TYPE_FRACTION, 0, DEFAULT_FPS,
-+ NULL), NULL);
-+
-+ r->appsrc_pad = gst_element_get_static_pad(GST_ELEMENT_CAST(r->appsrc), "src");
-+ if (!r->appsrc_pad)
-+ weston_log("Failed to get src0 pad of appsrc\n");
-+
-+ /* set playing */
-+ if (gst_element_set_state (r->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
-+ weston_log("gst_pipeline: can not change state to PLAYING\n");
-+ goto err_gst;
-+ }
-+
-+ if (gst_recorder_find_omx_pool(r) != 0) {
-+ weston_log("failed to find OMX buffer pool\n");
-+ goto err_gst_stop;
-+ }
-+
-+ weston_log("gst_recorder_create done\n");
-+
-+ return r;
-+
-+err_gst_stop:
-+ gst_element_set_state (r->pipeline, GST_STATE_NULL);
-+ destroy_worker_thread(r);
-+err_gst:
-+ free(r->pipeline);
-+ free(r);
-+
-+ return NULL;
-+}
-+
-+void
-+gst_recorder_destroy(struct gst_recorder *r)
-+{
-+ r->vsp->users--;
-+
-+ if (r->pipeline) {
-+ gst_element_set_state (r->pipeline, GST_STATE_NULL);
-+ gst_object_unref(r->omx_pool);
-+
-+ destroy_worker_thread(r);
-+
-+ gst_object_unref(GST_OBJECT(r->bus));
-+ gst_object_unref (r->pipeline);
-+ r->pipeline = NULL;
-+ }
-+ free(r);
-+}
-+
-+static int
-+gst_recorder_set_timestamp(struct gst_recorder *r, GstBuffer *buffer)
-+{
-+ uint32_t cur_time = weston_compositor_get_time();
-+
-+ if (r->timestamp == 0) {
-+ /* first frame assume around DEFAULT_FPS FPS */
-+ GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, DEFAULT_FPS);
-+ } else {
-+ uint32_t delta = cur_time - r->ts_last_frame;
-+ /* delta in mS */
-+ GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(delta, GST_SECOND, 1000);
-+ }
-+
-+ r->timestamp += GST_BUFFER_DURATION(buffer);
-+ GST_BUFFER_PTS(buffer) = r->timestamp;
-+ GST_BUFFER_DTS(buffer) = r->timestamp;
-+
-+ r->ts_last_frame = cur_time;
-+
-+ return 0;
-+}
-+
-+
-+static int
-+gst_recorder_process_dmafd(struct gst_recorder *r, int fd, int stride)
-+{
-+ int ret;
-+ GstBuffer *buf;
-+ int omx_fd[2];
-+
-+ /* get GST buffer */
-+ if (gst_recorder_omx_buffer_acquire(r, &buf, omx_fd) < 0) {
-+ weston_log("VSP: can not acquire GST buffer, dropping frame\n");
-+ return 0;
-+ }
-+
-+ pthread_mutex_lock(&r->vsp->mutex);
-+ /* setup vsp */
-+ if (vsp_set_formats(r->vsp, r->set->width, r->set->height, &r->set->crop) < 0) {
-+ weston_log("VSP: format set failed\n");
-+ goto err;
-+ }
-+
-+ /* input */
-+ if (vsp_request_buffers(r->vsp, VSP_PORT_INPUT, 1) < 0) {
-+ weston_log("VSP: input buffer allocation failed\n");
-+ goto err_vsp;
-+ }
-+
-+ /* output */
-+ if (vsp_request_buffers(r->vsp, VSP_PORT_OUTPUT, 1) < 0) {
-+ weston_log("VSP: output buffer allocation failed\n");
-+ goto err_vsp;
-+ }
-+
-+ /* queue output biffer */
-+ if (vsp_output_buffer_queue_dmafd(r->vsp, 0, omx_fd) < 0) {
-+ weston_log("can not queue OMX buffer %d to VSP\n", 0);
-+ gst_recorder_omx_buffer_release(r, buf);
-+ goto err_vsp;
-+ }
-+
-+ /* queue input vsp buffer */
-+ if (vsp_input_buffer_queue_dmafd(r->vsp, 0, fd) < 0) {
-+ weston_log("VSP: failed to queue input buffer\n");
-+ goto err_vsp;
-+ }
-+
-+ /* start input */
-+ if (vsp_streaming_enable(r->vsp, VSP_PORT_INPUT, 1) < 0) {
-+ weston_log("VSP: failed to start input\n");
-+ goto err_vsp;
-+ }
-+
-+ /* start output */
-+ if (vsp_streaming_enable(r->vsp, VSP_PORT_OUTPUT, 1) < 0) {
-+ weston_log("VSP: failed to start output\n");
-+ goto err_vsp;
-+ }
-+
-+ /* dequeue input (do we need this?) */
-+ if (vsp_input_buffer_dequeue_dmafd(r->vsp) < 0) {
-+ weston_log("VSP: failed to dequeue input buffer\n");
-+ /* don't care */
-+ }
-+
-+ /* dequeue output */
-+ if (vsp_output_buffer_dequeue_dmafd(r->vsp) < 0) {
-+ weston_log("VSP: failed to dequeu output buffer\n");
-+ gst_recorder_omx_buffer_release(r, buf);
-+ /* fall through */
-+ } else {
-+ /* set timestamp */
-+ gst_recorder_set_timestamp(r, buf);
-+
-+ ret = gst_app_src_push_buffer(r->appsrc, buf);
-+ r->frame_count++;
-+
-+ if (ret != GST_FLOW_OK) {
-+ /* some error, stop sending data */
-+ weston_log("gst_pipeline: some error %d\n", ret);
-+ }
-+
-+ }
-+ /* stop input */
-+ vsp_streaming_enable(r->vsp, VSP_PORT_INPUT, 0);
-+ /* stop output */
-+ vsp_streaming_enable(r->vsp, VSP_PORT_OUTPUT, 0);
-+
-+ /* deinit */
-+ vsp_request_buffers(r->vsp, VSP_PORT_INPUT, 0);
-+ vsp_request_buffers(r->vsp, VSP_PORT_OUTPUT, 0);
-+
-+ pthread_mutex_unlock(&r->vsp->mutex);
-+ return 0;
-+
-+err_vsp:
-+ /* drop gst buffer */
-+ /* finish vsp here */
-+err:
-+ pthread_mutex_unlock(&r->vsp->mutex);
-+ return -1;
-+}
-+
-+int
-+gst_recorder_frame_dmafd(struct gst_recorder *r, int 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 = fd;
-+ r->input.stride = stride;
-+ r->input.valid = 1;
-+ pthread_cond_signal(&r->input_cond);
-+
-+unlock:
-+ pthread_mutex_unlock(&r->mutex);
-+
-+ return 0;
-+}
-diff --git a/src/gst-recorder.h b/src/gst-recorder.h
-new file mode 100644
-index 0000000..e1c53ff
---- /dev/null
-+++ b/src/gst-recorder.h
-@@ -0,0 +1,58 @@
-+/*
-+ * Copyright © 2016 Cogent Embedded Inc
-+ *
-+ * Permission to use, copy, modify, distribute, and sell this software and
-+ * its documentation for any purpose is hereby granted without fee, provided
-+ * that the above copyright notice appear in all copies and that both that
-+ * copyright notice and this permission notice appear in supporting
-+ * documentation, and that the name of the copyright holders not be used in
-+ * advertising or publicity pertaining to distribution of the software
-+ * without specific, written prior permission. The copyright holders make
-+ * no representations about the suitability of this software for any
-+ * purpose. It is provided "as is" without express or implied warranty.
-+ *
-+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
-+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
-+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
-+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
-+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-+ */
-+
-+#ifndef _GST_RECORDER_H_
-+#define _GST_RECORDER_H_
-+
-+/* VSP includes */
-+#include <mediactl/mediactl.h>
-+#include <mediactl/v4l2subdev.h>
-+
-+struct gst_recorder;
-+
-+struct gst_recorder_settings {
-+ int width;
-+ int height;
-+ int bitrate;
-+ char *ip;
-+ int port;
-+ int latency_test;
-+ int refresh_ratio;
-+
-+ /* Cropping */
-+ struct v4l2_rect crop;
-+};
-+
-+void
-+gst_recorder_init(void);
-+struct gst_recorder *
-+gst_recorder_create(struct gst_recorder_settings *settings);
-+void
-+gst_recorder_destroy(struct gst_recorder *r);
-+int
-+gst_recorder_frame(struct gst_recorder *r, int fd, int stride);
-+int
-+gst_recorder_frame_mmap(struct gst_recorder *r, void *data, int stride);
-+int
-+gst_recorder_frame_dmafd(struct gst_recorder *r, int fd, int stride);
-+
-+#endif /* _GST_RECORDER_H_ */
-diff --git a/src/media-ctl/libmediactl.c b/src/media-ctl/libmediactl.c
-new file mode 100644
-index 0000000..f15b1a3
---- /dev/null
-+++ b/src/media-ctl/libmediactl.c
-@@ -0,0 +1,955 @@
-+/*
-+ * Media controller interface library
-+ *
-+ * Copyright (C) 2010-2014 Ideas on board SPRL
-+ *
-+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU Lesser General Public License as published
-+ * by the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program 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 Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+#include "config.h"
-+
-+#include <sys/ioctl.h>
-+#include <sys/stat.h>
-+#include <sys/types.h>
-+
-+#include <ctype.h>
-+#include <errno.h>
-+#include <fcntl.h>
-+#include <stdbool.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <unistd.h>
-+
-+#include <linux/media.h>
-+#include <linux/videodev2.h>
-+
-+#include "mediactl.h"
-+#include "mediactl-priv.h"
-+#include "tools.h"
-+
-+/* -----------------------------------------------------------------------------
-+ * Graph access
-+ */
-+
-+struct media_pad *media_entity_remote_source(struct media_pad *pad)
-+{
-+ unsigned int i;
-+
-+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
-+ return NULL;
-+
-+ for (i = 0; i < pad->entity->num_links; ++i) {
-+ struct media_link *link = &pad->entity->links[i];
-+
-+ if (!(link->flags & MEDIA_LNK_FL_ENABLED))
-+ continue;
-+
-+ if (link->sink == pad)
-+ return link->source;
-+ }
-+
-+ return NULL;
-+}
-+
-+struct media_entity *media_get_entity_by_name(struct media_device *media,
-+ const char *name, size_t length)
-+{
-+ unsigned int i;
-+
-+ /* A match is impossible if the entity name is longer than the maximum
-+ * size we can get from the kernel.
-+ */
-+ if (length >= FIELD_SIZEOF(struct media_entity_desc, name))
-+ return NULL;
-+
-+ for (i = 0; i < media->entities_count; ++i) {
-+ struct media_entity *entity = &media->entities[i];
-+
-+ if (strncmp(entity->info.name, name, length) == 0 &&
-+ entity->info.name[length] == '\0')
-+ return entity;
-+ }
-+
-+ return NULL;
-+}
-+
-+struct media_entity *media_get_entity_by_id(struct media_device *media,
-+ __u32 id)
-+{
-+ bool next = id & MEDIA_ENT_ID_FLAG_NEXT;
-+ unsigned int i;
-+
-+ id &= ~MEDIA_ENT_ID_FLAG_NEXT;
-+
-+ for (i = 0; i < media->entities_count; ++i) {
-+ struct media_entity *entity = &media->entities[i];
-+
-+ if ((entity->info.id == id && !next) ||
-+ (entity->info.id > id && next))
-+ return entity;
-+ }
-+
-+ return NULL;
-+}
-+
-+unsigned int media_get_entities_count(struct media_device *media)
-+{
-+ return media->entities_count;
-+}
-+
-+struct media_entity *media_get_entity(struct media_device *media, unsigned int index)
-+{
-+ if (index >= media->entities_count)
-+ return NULL;
-+
-+ return &media->entities[index];
-+}
-+
-+const struct media_pad *media_entity_get_pad(struct media_entity *entity, unsigned int index)
-+{
-+ if (index >= entity->info.pads)
-+ return NULL;
-+
-+ return &entity->pads[index];
-+}
-+
-+unsigned int media_entity_get_links_count(struct media_entity *entity)
-+{
-+ return entity->num_links;
-+}
-+
-+const struct media_link *media_entity_get_link(struct media_entity *entity, unsigned int index)
-+{
-+ if (index >= entity->num_links)
-+ return NULL;
-+
-+ return &entity->links[index];
-+}
-+
-+const char *media_entity_get_devname(struct media_entity *entity)
-+{
-+ return entity->devname[0] ? entity->devname : NULL;
-+}
-+
-+struct media_entity *media_get_default_entity(struct media_device *media,
-+ unsigned int type)
-+{
-+ switch (type) {
-+ case MEDIA_ENT_T_DEVNODE_V4L:
-+ return media->def.v4l;
-+ case MEDIA_ENT_T_DEVNODE_FB:
-+ return media->def.fb;
-+ case MEDIA_ENT_T_DEVNODE_ALSA:
-+ return media->def.alsa;
-+ case MEDIA_ENT_T_DEVNODE_DVB:
-+ return media->def.dvb;
-+ }
-+
-+ return NULL;
-+}
-+
-+const struct media_device_info *media_get_info(struct media_device *media)
-+{
-+ return &media->info;
-+}
-+
-+const char *media_get_devnode(struct media_device *media)
-+{
-+ return media->devnode;
-+}
-+
-+const struct media_entity_desc *media_entity_get_info(struct media_entity *entity)
-+{
-+ return &entity->info;
-+}
-+
-+/* -----------------------------------------------------------------------------
-+ * Open/close
-+ */
-+
-+static int media_device_open(struct media_device *media)
-+{
-+ int ret;
-+
-+ if (media->fd != -1)
-+ return 0;
-+
-+ media_dbg(media, "Opening media device %s\n", media->devnode);
-+
-+ media->fd = open(media->devnode, O_RDWR);
-+ if (media->fd < 0) {
-+ ret = -errno;
-+ media_dbg(media, "%s: Can't open media device %s\n",
-+ __func__, media->devnode);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static void media_device_close(struct media_device *media)
-+{
-+ if (media->fd != -1) {
-+ close(media->fd);
-+ media->fd = -1;
-+ }
-+}
-+
-+/* -----------------------------------------------------------------------------
-+ * Link setup
-+ */
-+
-+int media_setup_link(struct media_device *media,
-+ struct media_pad *source,
-+ struct media_pad *sink,
-+ __u32 flags)
-+{
-+ struct media_link *link;
-+ struct media_link_desc ulink;
-+ unsigned int i;
-+ int ret;
-+
-+ ret = media_device_open(media);
-+ if (ret < 0)
-+ goto done;
-+
-+ for (i = 0; i < source->entity->num_links; i++) {
-+ link = &source->entity->links[i];
-+
-+ if (link->source->entity == source->entity &&
-+ link->source->index == source->index &&
-+ link->sink->entity == sink->entity &&
-+ link->sink->index == sink->index)
-+ break;
-+ }
-+
-+ if (i == source->entity->num_links) {
-+ media_dbg(media, "%s: Link not found\n", __func__);
-+ ret = -ENOENT;
-+ goto done;
-+ }
-+
-+ /* source pad */
-+ ulink.source.entity = source->entity->info.id;
-+ ulink.source.index = source->index;
-+ ulink.source.flags = MEDIA_PAD_FL_SOURCE;
-+
-+ /* sink pad */
-+ ulink.sink.entity = sink->entity->info.id;
-+ ulink.sink.index = sink->index;
-+ ulink.sink.flags = MEDIA_PAD_FL_SINK;
-+
-+ ulink.flags = flags | (link->flags & MEDIA_LNK_FL_IMMUTABLE);
-+
-+ ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
-+ if (ret == -1) {
-+ ret = -errno;
-+ media_dbg(media, "%s: Unable to setup link (%s)\n",
-+ __func__, strerror(errno));
-+ goto done;
-+ }
-+
-+ link->flags = ulink.flags;
-+ link->twin->flags = ulink.flags;
-+
-+ ret = 0;
-+
-+done:
-+ return ret;
-+}
-+
-+int media_reset_links(struct media_device *media)
-+{
-+ unsigned int i, j;
-+ int ret;
-+
-+ for (i = 0; i < media->entities_count; ++i) {
-+ struct media_entity *entity = &media->entities[i];
-+
-+ for (j = 0; j < entity->num_links; j++) {
-+ struct media_link *link = &entity->links[j];
-+
-+ if (link->flags & MEDIA_LNK_FL_IMMUTABLE ||
-+ link->source->entity != entity)
-+ continue;
-+
-+ ret = media_setup_link(media, link->source, link->sink,
-+ link->flags & ~MEDIA_LNK_FL_ENABLED);
-+ if (ret < 0)
-+ return ret;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+/* -----------------------------------------------------------------------------
-+ * Entities, pads and links enumeration
-+ */
-+
-+static struct media_link *media_entity_add_link(struct media_entity *entity)
-+{
-+ if (entity->num_links >= entity->max_links) {
-+ struct media_link *links = entity->links;
-+ unsigned int max_links = entity->max_links * 2;
-+ unsigned int i;
-+
-+ links = realloc(links, max_links * sizeof *links);
-+ if (links == NULL)
-+ return NULL;
-+
-+ for (i = 0; i < entity->num_links; ++i)
-+ links[i].twin->twin = &links[i];
-+
-+ entity->max_links = max_links;
-+ entity->links = links;
-+ }
-+
-+ return &entity->links[entity->num_links++];
-+}
-+
-+static int media_enum_links(struct media_device *media)
-+{
-+ __u32 id;
-+ int ret = 0;
-+
-+ for (id = 1; id <= media->entities_count; id++) {
-+ struct media_entity *entity = &media->entities[id - 1];
-+ struct media_links_enum links;
-+ unsigned int i;
-+
-+ links.entity = entity->info.id;
-+ links.pads = calloc(entity->info.pads, sizeof(struct media_pad_desc));
-+ links.links = calloc(entity->info.links, sizeof(struct media_link_desc));
-+
-+ if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
-+ ret = -errno;
-+ media_dbg(media,
-+ "%s: Unable to enumerate pads and links (%s).\n",
-+ __func__, strerror(errno));
-+ free(links.pads);
-+ free(links.links);
-+ return ret;
-+ }
-+
-+ for (i = 0; i < entity->info.pads; ++i) {
-+ entity->pads[i].entity = entity;
-+ entity->pads[i].index = links.pads[i].index;
-+ entity->pads[i].flags = links.pads[i].flags;
-+ }
-+
-+ for (i = 0; i < entity->info.links; ++i) {
-+ struct media_link_desc *link = &links.links[i];
-+ struct media_link *fwdlink;
-+ struct media_link *backlink;
-+ struct media_entity *source;
-+ struct media_entity *sink;
-+
-+ source = media_get_entity_by_id(media, link->source.entity);
-+ sink = media_get_entity_by_id(media, link->sink.entity);
-+
-+ if (source == NULL || sink == NULL) {
-+ media_dbg(media,
-+ "WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
-+ id, i, link->source.entity,
-+ link->source.index,
-+ link->sink.entity,
-+ link->sink.index);
-+ ret = -EINVAL;
-+ } else {
-+ fwdlink = media_entity_add_link(source);
-+ fwdlink->source = &source->pads[link->source.index];
-+ fwdlink->sink = &sink->pads[link->sink.index];
-+ fwdlink->flags = link->flags;
-+
-+ backlink = media_entity_add_link(sink);
-+ backlink->source = &source->pads[link->source.index];
-+ backlink->sink = &sink->pads[link->sink.index];
-+ backlink->flags = link->flags;
-+
-+ fwdlink->twin = backlink;
-+ backlink->twin = fwdlink;
-+ }
-+ }
-+
-+ free(links.pads);
-+ free(links.links);
-+ }
-+
-+ return ret;
-+}
-+
-+#ifdef HAVE_LIBUDEV
-+
-+#include <libudev.h>
-+
-+static inline int media_udev_open(struct udev **udev)
-+{
-+ *udev = udev_new();
-+ if (*udev == NULL)
-+ return -ENOMEM;
-+ return 0;
-+}
-+
-+static inline void media_udev_close(struct udev *udev)
-+{
-+ if (udev != NULL)
-+ udev_unref(udev);
-+}
-+
-+static int media_get_devname_udev(struct udev *udev,
-+ struct media_entity *entity)
-+{
-+ struct udev_device *device;
-+ dev_t devnum;
-+ const char *p;
-+ int ret = -ENODEV;
-+
-+ if (udev == NULL)
-+ return -EINVAL;
-+
-+ devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
-+ media_dbg(entity->media, "looking up device: %u:%u\n",
-+ major(devnum), minor(devnum));
-+ device = udev_device_new_from_devnum(udev, 'c', devnum);
-+ if (device) {
-+ p = udev_device_get_devnode(device);
-+ if (p) {
-+ strncpy(entity->devname, p, sizeof(entity->devname));
-+ entity->devname[sizeof(entity->devname) - 1] = '\0';
-+ }
-+ ret = 0;
-+ }
-+
-+ udev_device_unref(device);
-+
-+ return ret;
-+}
-+
-+#else /* HAVE_LIBUDEV */
-+
-+struct udev;
-+
-+static inline int media_udev_open(struct udev **udev) { return 0; }
-+
-+static inline void media_udev_close(struct udev *udev) { }
-+
-+static inline int media_get_devname_udev(struct udev *udev,
-+ struct media_entity *entity)
-+{
-+ return -ENOTSUP;
-+}
-+
-+#endif /* HAVE_LIBUDEV */
-+
-+static int media_get_devname_sysfs(struct media_entity *entity)
-+{
-+ struct stat devstat;
-+ char devname[32];
-+ char sysname[32];
-+ char target[1024];
-+ char *p;
-+ int ret;
-+
-+ sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
-+ entity->info.v4l.minor);
-+ ret = readlink(sysname, target, sizeof(target) - 1);
-+ if (ret < 0)
-+ return -errno;
-+
-+ target[ret] = '\0';
-+ p = strrchr(target, '/');
-+ if (p == NULL)
-+ return -EINVAL;
-+
-+ sprintf(devname, "/dev/%s", p + 1);
-+ ret = stat(devname, &devstat);
-+ if (ret < 0)
-+ return -errno;
-+
-+ /* Sanity check: udev might have reordered the device nodes.
-+ * Make sure the major/minor match. We should really use
-+ * libudev.
-+ */
-+ if (major(devstat.st_rdev) == entity->info.v4l.major &&
-+ minor(devstat.st_rdev) == entity->info.v4l.minor)
-+ strcpy(entity->devname, devname);
-+
-+ return 0;
-+}
-+
-+static int media_enum_entities(struct media_device *media)
-+{
-+ struct media_entity *entity;
-+ struct udev *udev;
-+ unsigned int size;
-+ __u32 id;
-+ int ret;
-+
-+ ret = media_udev_open(&udev);
-+ if (ret < 0)
-+ media_dbg(media, "Can't get udev context\n");
-+
-+ for (id = 0, ret = 0; ; id = entity->info.id) {
-+ size = (media->entities_count + 1) * sizeof(*media->entities);
-+ media->entities = realloc(media->entities, size);
-+
-+ entity = &media->entities[media->entities_count];
-+ memset(entity, 0, sizeof(*entity));
-+ entity->fd = -1;
-+ entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT;
-+ entity->media = media;
-+
-+ ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
-+ if (ret < 0) {
-+ ret = errno != EINVAL ? -errno : 0;
-+ break;
-+ }
-+
-+ /* Number of links (for outbound links) plus number of pads (for
-+ * inbound links) is a good safe initial estimate of the total
-+ * number of links.
-+ */
-+ entity->max_links = entity->info.pads + entity->info.links;
-+
-+ entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
-+ entity->links = malloc(entity->max_links * sizeof(*entity->links));
-+ if (entity->pads == NULL || entity->links == NULL) {
-+ ret = -ENOMEM;
-+ break;
-+ }
-+
-+ media->entities_count++;
-+
-+ if (entity->info.flags & MEDIA_ENT_FL_DEFAULT) {
-+ switch (entity->info.type) {
-+ case MEDIA_ENT_T_DEVNODE_V4L:
-+ media->def.v4l = entity;
-+ break;
-+ case MEDIA_ENT_T_DEVNODE_FB:
-+ media->def.fb = entity;
-+ break;
-+ case MEDIA_ENT_T_DEVNODE_ALSA:
-+ media->def.alsa = entity;
-+ break;
-+ case MEDIA_ENT_T_DEVNODE_DVB:
-+ media->def.dvb = entity;
-+ break;
-+ }
-+ }
-+
-+ /* Find the corresponding device name. */
-+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE &&
-+ media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
-+ continue;
-+
-+ /* Try to get the device name via udev */
-+ if (!media_get_devname_udev(udev, entity))
-+ continue;
-+
-+ /* Fall back to get the device name via sysfs */
-+ media_get_devname_sysfs(entity);
-+ }
-+
-+ media_udev_close(udev);
-+ return ret;
-+}
-+
-+int media_device_enumerate(struct media_device *media)
-+{
-+ int ret;
-+
-+ if (media->entities)
-+ return 0;
-+
-+ ret = media_device_open(media);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ioctl(media->fd, MEDIA_IOC_DEVICE_INFO, &media->info);
-+ if (ret < 0) {
-+ ret = -errno;
-+ media_dbg(media, "%s: Unable to retrieve media device "
-+ "information for device %s (%s)\n", __func__,
-+ media->devnode, strerror(errno));
-+ goto done;
-+ }
-+
-+ media_dbg(media, "Enumerating entities\n");
-+
-+ ret = media_enum_entities(media);
-+ if (ret < 0) {
-+ media_dbg(media,
-+ "%s: Unable to enumerate entities for device %s (%s)\n",
-+ __func__, media->devnode, strerror(-ret));
-+ goto done;
-+ }
-+
-+ media_dbg(media, "Found %u entities\n", media->entities_count);
-+ media_dbg(media, "Enumerating pads and links\n");
-+
-+ ret = media_enum_links(media);
-+ if (ret < 0) {
-+ media_dbg(media,
-+ "%s: Unable to enumerate pads and linksfor device %s\n",
-+ __func__, media->devnode);
-+ goto done;
-+ }
-+
-+ ret = 0;
-+
-+done:
-+ return ret;
-+}
-+
-+/* -----------------------------------------------------------------------------
-+ * Create/destroy
-+ */
-+
-+static void media_debug_default(void *ptr, ...)
-+{
-+}
-+
-+void media_debug_set_handler(struct media_device *media,
-+ void (*debug_handler)(void *, ...),
-+ void *debug_priv)
-+{
-+ if (debug_handler) {
-+ media->debug_handler = debug_handler;
-+ media->debug_priv = debug_priv;
-+ } else {
-+ media->debug_handler = media_debug_default;
-+ media->debug_priv = NULL;
-+ }
-+}
-+
-+static struct media_device *__media_device_new(void)
-+{
-+ struct media_device *media;
-+
-+ media = calloc(1, sizeof(*media));
-+ if (media == NULL)
-+ return NULL;
-+
-+ media->fd = -1;
-+ media->refcount = 1;
-+
-+ media_debug_set_handler(media, NULL, NULL);
-+
-+ return media;
-+}
-+
-+struct media_device *media_device_new(const char *devnode)
-+{
-+ struct media_device *media;
-+
-+ media = __media_device_new();
-+ if (media == NULL)
-+ return NULL;
-+
-+ media->devnode = strdup(devnode);
-+ if (media->devnode == NULL) {
-+ media_device_unref(media);
-+ return NULL;
-+ }
-+
-+ return media;
-+}
-+
-+struct media_device *media_device_new_emulated(struct media_device_info *info)
-+{
-+ struct media_device *media;
-+
-+ media = __media_device_new();
-+ if (media == NULL)
-+ return NULL;
-+
-+ media->info = *info;
-+
-+ return media;
-+}
-+
-+struct media_device *media_device_ref(struct media_device *media)
-+{
-+ media->refcount++;
-+ return media;
-+}
-+
-+void media_device_unref(struct media_device *media)
-+{
-+ unsigned int i;
-+
-+ media->refcount--;
-+ if (media->refcount > 0)
-+ return;
-+
-+ for (i = 0; i < media->entities_count; ++i) {
-+ struct media_entity *entity = &media->entities[i];
-+
-+ free(entity->pads);
-+ free(entity->links);
-+ if (entity->fd != -1)
-+ close(entity->fd);
-+ }
-+
-+ free(media->entities);
-+ free(media->devnode);
-+ free(media);
-+}
-+
-+int media_device_add_entity(struct media_device *media,
-+ const struct media_entity_desc *desc,
-+ const char *devnode)
-+{
-+ struct media_entity **defent = NULL;
-+ struct media_entity *entity;
-+ unsigned int size;
-+
-+ size = (media->entities_count + 1) * sizeof(*media->entities);
-+ entity = realloc(media->entities, size);
-+ if (entity == NULL)
-+ return -ENOMEM;
-+
-+ media->entities = entity;
-+ media->entities_count++;
-+
-+ entity = &media->entities[media->entities_count - 1];
-+ memset(entity, 0, sizeof *entity);
-+
-+ entity->fd = -1;
-+ entity->media = media;
-+ strncpy(entity->devname, devnode, sizeof entity->devname);
-+ entity->devname[sizeof entity->devname - 1] = '\0';
-+
-+ entity->info.id = 0;
-+ entity->info.type = desc->type;
-+ entity->info.flags = 0;
-+ memcpy(entity->info.name, desc->name, sizeof entity->info.name);
-+
-+ switch (entity->info.type) {
-+ case MEDIA_ENT_T_DEVNODE_V4L:
-+ defent = &media->def.v4l;
-+ entity->info.v4l = desc->v4l;
-+ break;
-+ case MEDIA_ENT_T_DEVNODE_FB:
-+ defent = &media->def.fb;
-+ entity->info.fb = desc->fb;
-+ break;
-+ case MEDIA_ENT_T_DEVNODE_ALSA:
-+ defent = &media->def.alsa;
-+ entity->info.alsa = desc->alsa;
-+ break;
-+ case MEDIA_ENT_T_DEVNODE_DVB:
-+ defent = &media->def.dvb;
-+ entity->info.dvb = desc->dvb;
-+ break;
-+ }
-+
-+ if (desc->flags & MEDIA_ENT_FL_DEFAULT) {
-+ entity->info.flags |= MEDIA_ENT_FL_DEFAULT;
-+ if (defent)
-+ *defent = entity;
-+ }
-+
-+ return 0;
-+}
-+
-+struct media_pad *media_parse_pad(struct media_device *media,
-+ const char *p, char **endp)
-+{
-+ unsigned int entity_id, pad;
-+ struct media_entity *entity;
-+ char *end;
-+
-+ /* endp can be NULL. To avoid spreading NULL checks across the function,
-+ * set endp to &end in that case.
-+ */
-+ if (endp == NULL)
-+ endp = &end;
-+
-+ for (; isspace(*p); ++p);
-+
-+ if (*p == '"' || *p == '\'') {
-+ for (end = (char *)p + 1; *end && *end != '"' && *end != '\''; ++end);
-+ if (*end != '"' && *end != '\'') {
-+ media_dbg(media, "missing matching '\"'\n");
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ entity = media_get_entity_by_name(media, p + 1, end - p - 1);
-+ if (entity == NULL) {
-+ media_dbg(media, "no such entity \"%.*s\"\n", end - p - 1, p + 1);
-+ *endp = (char *)p + 1;
-+ return NULL;
-+ }
-+
-+ ++end;
-+ } else {
-+ entity_id = strtoul(p, &end, 10);
-+ entity = media_get_entity_by_id(media, entity_id);
-+ if (entity == NULL) {
-+ media_dbg(media, "no such entity %d\n", entity_id);
-+ *endp = (char *)p;
-+ return NULL;
-+ }
-+ }
-+ for (; isspace(*end); ++end);
-+
-+ if (*end != ':') {
-+ media_dbg(media, "Expected ':'\n", *end);
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ for (p = end + 1; isspace(*p); ++p);
-+
-+ pad = strtoul(p, &end, 10);
-+
-+ if (pad >= entity->info.pads) {
-+ media_dbg(media, "No pad '%d' on entity \"%s\". Maximum pad number is %d\n",
-+ pad, entity->info.name, entity->info.pads - 1);
-+ *endp = (char *)p;
-+ return NULL;
-+ }
-+
-+ for (p = end; isspace(*p); ++p);
-+ *endp = (char *)p;
-+
-+ return &entity->pads[pad];
-+}
-+
-+struct media_link *media_parse_link(struct media_device *media,
-+ const char *p, char **endp)
-+{
-+ struct media_link *link;
-+ struct media_pad *source;
-+ struct media_pad *sink;
-+ unsigned int i;
-+ char *end;
-+
-+ source = media_parse_pad(media, p, &end);
-+ if (source == NULL) {
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ if (end[0] != '-' || end[1] != '>') {
-+ *endp = end;
-+ media_dbg(media, "Expected '->'\n");
-+ return NULL;
-+ }
-+
-+ p = end + 2;
-+
-+ sink = media_parse_pad(media, p, &end);
-+ if (sink == NULL) {
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ *endp = end;
-+
-+ for (i = 0; i < source->entity->num_links; i++) {
-+ link = &source->entity->links[i];
-+
-+ if (link->source == source && link->sink == sink)
-+ return link;
-+ }
-+
-+ media_dbg(media, "No link between \"%s\":%d and \"%s\":%d\n",
-+ source->entity->info.name, source->index,
-+ sink->entity->info.name, sink->index);
-+ return NULL;
-+}
-+
-+int media_parse_setup_link(struct media_device *media,
-+ const char *p, char **endp)
-+{
-+ struct media_link *link;
-+ __u32 flags;
-+ char *end;
-+
-+ link = media_parse_link(media, p, &end);
-+ if (link == NULL) {
-+ media_dbg(media,
-+ "%s: Unable to parse link\n", __func__);
-+ *endp = end;
-+ return -EINVAL;
-+ }
-+
-+ p = end;
-+ if (*p++ != '[') {
-+ media_dbg(media, "Unable to parse link flags: expected '['.\n");
-+ *endp = (char *)p - 1;
-+ return -EINVAL;
-+ }
-+
-+ flags = strtoul(p, &end, 10);
-+ for (p = end; isspace(*p); p++);
-+ if (*p++ != ']') {
-+ media_dbg(media, "Unable to parse link flags: expected ']'.\n");
-+ *endp = (char *)p - 1;
-+ return -EINVAL;
-+ }
-+
-+ for (; isspace(*p); p++);
-+ *endp = (char *)p;
-+
-+ media_dbg(media,
-+ "Setting up link %u:%u -> %u:%u [%u]\n",
-+ link->source->entity->info.id, link->source->index,
-+ link->sink->entity->info.id, link->sink->index,
-+ flags);
-+
-+ return media_setup_link(media, link->source, link->sink, flags);
-+}
-+
-+void media_print_streampos(struct media_device *media, const char *p,
-+ const char *end)
-+{
-+ int pos;
-+
-+ pos = end - p + 1;
-+
-+ if (pos < 0)
-+ pos = 0;
-+ if (pos > strlen(p))
-+ pos = strlen(p);
-+
-+ media_dbg(media, "\n");
-+ media_dbg(media, " %s\n", p);
-+ media_dbg(media, " %*s\n", pos, "^");
-+}
-+
-+int media_parse_setup_links(struct media_device *media, const char *p)
-+{
-+ char *end;
-+ int ret;
-+
-+ do {
-+ ret = media_parse_setup_link(media, p, &end);
-+ if (ret < 0) {
-+ media_print_streampos(media, p, end);
-+ return ret;
-+ }
-+
-+ p = end + 1;
-+ } while (*end == ',');
-+
-+ return *end ? -EINVAL : 0;
-+}
-diff --git a/src/media-ctl/libv4l2subdev.c b/src/media-ctl/libv4l2subdev.c
-new file mode 100644
-index 0000000..4ede4fa
---- /dev/null
-+++ b/src/media-ctl/libv4l2subdev.c
-@@ -0,0 +1,759 @@
-+/*
-+ * V4L2 subdev interface library
-+ *
-+ * Copyright (C) 2010-2014 Ideas on board SPRL
-+ *
-+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU Lesser General Public License as published
-+ * by the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program 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 Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+#include <sys/ioctl.h>
-+#include <sys/stat.h>
-+#include <sys/types.h>
-+
-+#include <ctype.h>
-+#include <errno.h>
-+#include <fcntl.h>
-+#include <stdbool.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <unistd.h>
-+
-+#include <linux/v4l2-subdev.h>
-+
-+#include "mediactl.h"
-+#include "mediactl-priv.h"
-+#include "tools.h"
-+#include "v4l2subdev.h"
-+
-+int v4l2_subdev_open(struct media_entity *entity)
-+{
-+ if (entity->fd != -1)
-+ return 0;
-+
-+ entity->fd = open(entity->devname, O_RDWR);
-+ if (entity->fd == -1) {
-+ int ret = -errno;
-+ media_dbg(entity->media,
-+ "%s: Failed to open subdev device node %s\n", __func__,
-+ entity->devname);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+void v4l2_subdev_close(struct media_entity *entity)
-+{
-+ close(entity->fd);
-+ entity->fd = -1;
-+}
-+
-+int v4l2_subdev_get_format(struct media_entity *entity,
-+ struct v4l2_mbus_framefmt *format, unsigned int pad,
-+ enum v4l2_subdev_format_whence which)
-+{
-+ struct v4l2_subdev_format fmt;
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(&fmt, 0, sizeof(fmt));
-+ fmt.pad = pad;
-+ fmt.which = which;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt);
-+ if (ret < 0)
-+ return -errno;
-+
-+ *format = fmt.format;
-+ return 0;
-+}
-+
-+int v4l2_subdev_set_format(struct media_entity *entity,
-+ struct v4l2_mbus_framefmt *format, unsigned int pad,
-+ enum v4l2_subdev_format_whence which)
-+{
-+ struct v4l2_subdev_format fmt;
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(&fmt, 0, sizeof(fmt));
-+ fmt.pad = pad;
-+ fmt.which = which;
-+ fmt.format = *format;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FMT, &fmt);
-+ if (ret < 0)
-+ return -errno;
-+
-+ *format = fmt.format;
-+ return 0;
-+}
-+
-+int v4l2_subdev_get_selection(struct media_entity *entity,
-+ struct v4l2_rect *rect, unsigned int pad, unsigned int target,
-+ enum v4l2_subdev_format_whence which)
-+{
-+ union {
-+ struct v4l2_subdev_selection sel;
-+ struct v4l2_subdev_crop crop;
-+ } u;
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(&u.sel, 0, sizeof(u.sel));
-+ u.sel.pad = pad;
-+ u.sel.target = target;
-+ u.sel.which = which;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_SELECTION, &u.sel);
-+ if (ret >= 0) {
-+ *rect = u.sel.r;
-+ return 0;
-+ }
-+ if (errno != ENOTTY || target != V4L2_SEL_TGT_CROP)
-+ return -errno;
-+
-+ memset(&u.crop, 0, sizeof(u.crop));
-+ u.crop.pad = pad;
-+ u.crop.which = which;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_CROP, &u.crop);
-+ if (ret < 0)
-+ return -errno;
-+
-+ *rect = u.crop.rect;
-+ return 0;
-+}
-+
-+int v4l2_subdev_set_selection(struct media_entity *entity,
-+ struct v4l2_rect *rect, unsigned int pad, unsigned int target,
-+ enum v4l2_subdev_format_whence which)
-+{
-+ union {
-+ struct v4l2_subdev_selection sel;
-+ struct v4l2_subdev_crop crop;
-+ } u;
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(&u.sel, 0, sizeof(u.sel));
-+ u.sel.pad = pad;
-+ u.sel.target = target;
-+ u.sel.which = which;
-+ u.sel.r = *rect;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_SELECTION, &u.sel);
-+ if (ret >= 0) {
-+ *rect = u.sel.r;
-+ return 0;
-+ }
-+ if (errno != ENOTTY || target != V4L2_SEL_TGT_CROP)
-+ return -errno;
-+
-+ memset(&u.crop, 0, sizeof(u.crop));
-+ u.crop.pad = pad;
-+ u.crop.which = which;
-+ u.crop.rect = *rect;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_CROP, &u.crop);
-+ if (ret < 0)
-+ return -errno;
-+
-+ *rect = u.crop.rect;
-+ return 0;
-+}
-+
-+#if 0
-+int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity,
-+ struct v4l2_dv_timings_cap *caps)
-+{
-+ unsigned int pad = caps->pad;
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(caps, 0, sizeof(*caps));
-+ caps->pad = pad;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_DV_TIMINGS_CAP, caps);
-+ if (ret < 0)
-+ return -errno;
-+
-+ return 0;
-+}
-+
-+int v4l2_subdev_query_dv_timings(struct media_entity *entity,
-+ struct v4l2_dv_timings *timings)
-+{
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(timings, 0, sizeof(*timings));
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_QUERY_DV_TIMINGS, timings);
-+ if (ret < 0)
-+ return -errno;
-+
-+ return 0;
-+}
-+
-+int v4l2_subdev_get_dv_timings(struct media_entity *entity,
-+ struct v4l2_dv_timings *timings)
-+{
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(timings, 0, sizeof(*timings));
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_DV_TIMINGS, timings);
-+ if (ret < 0)
-+ return -errno;
-+
-+ return 0;
-+}
-+
-+int v4l2_subdev_set_dv_timings(struct media_entity *entity,
-+ struct v4l2_dv_timings *timings)
-+{
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_DV_TIMINGS, timings);
-+ if (ret < 0)
-+ return -errno;
-+
-+ return 0;
-+}
-+#endif
-+
-+int v4l2_subdev_get_frame_interval(struct media_entity *entity,
-+ struct v4l2_fract *interval)
-+{
-+ struct v4l2_subdev_frame_interval ival;
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(&ival, 0, sizeof(ival));
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival);
-+ if (ret < 0)
-+ return -errno;
-+
-+ *interval = ival.interval;
-+ return 0;
-+}
-+
-+int v4l2_subdev_set_frame_interval(struct media_entity *entity,
-+ struct v4l2_fract *interval)
-+{
-+ struct v4l2_subdev_frame_interval ival;
-+ int ret;
-+
-+ ret = v4l2_subdev_open(entity);
-+ if (ret < 0)
-+ return ret;
-+
-+ memset(&ival, 0, sizeof(ival));
-+ ival.interval = *interval;
-+
-+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival);
-+ if (ret < 0)
-+ return -errno;
-+
-+ *interval = ival.interval;
-+ return 0;
-+}
-+
-+static int v4l2_subdev_parse_format(struct media_device *media,
-+ struct v4l2_mbus_framefmt *format,
-+ const char *p, char **endp)
-+{
-+ enum v4l2_mbus_pixelcode code;
-+ unsigned int width, height;
-+ char *end;
-+
-+ /*
-+ * Compatibility with the old syntax: consider space as valid
-+ * separator between the media bus pixel code and the size.
-+ */
-+ for (; isspace(*p); ++p);
-+ for (end = (char *)p;
-+ *end != '/' && *end != ' ' && *end != '\0'; ++end);
-+
-+ code = v4l2_subdev_string_to_pixelcode(p, end - p);
-+ if (code == (enum v4l2_mbus_pixelcode)-1) {
-+ media_dbg(media, "Invalid pixel code '%.*s'\n", end - p, p);
-+ return -EINVAL;
-+ }
-+
-+ p = end + 1;
-+ width = strtoul(p, &end, 10);
-+ if (*end != 'x') {
-+ media_dbg(media, "Expected 'x'\n");
-+ return -EINVAL;
-+ }
-+
-+ p = end + 1;
-+ height = strtoul(p, &end, 10);
-+ *endp = end;
-+
-+ memset(format, 0, sizeof(*format));
-+ format->width = width;
-+ format->height = height;
-+ format->code = code;
-+
-+ return 0;
-+}
-+
-+static int v4l2_subdev_parse_rectangle(struct media_device *media,
-+ struct v4l2_rect *r, const char *p,
-+ char **endp)
-+{
-+ char *end;
-+
-+ if (*p++ != '(') {
-+ media_dbg(media, "Expected '('\n");
-+ *endp = (char *)p - 1;
-+ return -EINVAL;
-+ }
-+
-+ r->left = strtoul(p, &end, 10);
-+ if (*end != ',') {
-+ media_dbg(media, "Expected ','\n");
-+ *endp = end;
-+ return -EINVAL;
-+ }
-+
-+ p = end + 1;
-+ r->top = strtoul(p, &end, 10);
-+ if (*end++ != ')') {
-+ media_dbg(media, "Expected ')'\n");
-+ *endp = end - 1;
-+ return -EINVAL;
-+ }
-+ if (*end != '/') {
-+ media_dbg(media, "Expected '/'\n");
-+ *endp = end;
-+ return -EINVAL;
-+ }
-+
-+ p = end + 1;
-+ r->width = strtoul(p, &end, 10);
-+ if (*end != 'x') {
-+ media_dbg(media, "Expected 'x'\n");
-+ *endp = end;
-+ return -EINVAL;
-+ }
-+
-+ p = end + 1;
-+ r->height = strtoul(p, &end, 10);
-+ *endp = end;
-+
-+ return 0;
-+}
-+
-+static int v4l2_subdev_parse_frame_interval(struct media_device *media,
-+ struct v4l2_fract *interval,
-+ const char *p, char **endp)
-+{
-+ char *end;
-+
-+ for (; isspace(*p); ++p);
-+
-+ interval->numerator = strtoul(p, &end, 10);
-+
-+ for (p = end; isspace(*p); ++p);
-+ if (*p++ != '/') {
-+ media_dbg(media, "Expected '/'\n");
-+ *endp = (char *)p - 1;
-+ return -EINVAL;
-+ }
-+
-+ for (; isspace(*p); ++p);
-+ interval->denominator = strtoul(p, &end, 10);
-+
-+ *endp = end;
-+ return 0;
-+}
-+
-+/*
-+ * The debate over whether this function should be named icanhasstr() instead
-+ * has been strong and heated. If you feel like this would be an important
-+ * change, patches are welcome (or not).
-+ */
-+static bool strhazit(const char *str, const char **p)
-+{
-+ int len = strlen(str);
-+
-+ if (strncmp(str, *p, len))
-+ return false;
-+
-+ for (*p += len; isspace(**p); ++*p);
-+ return true;
-+}
-+
-+static struct media_pad *v4l2_subdev_parse_pad_format(
-+ struct media_device *media, struct v4l2_mbus_framefmt *format,
-+ struct v4l2_rect *crop, struct v4l2_rect *compose,
-+ struct v4l2_fract *interval, const char *p, char **endp)
-+{
-+ struct media_pad *pad;
-+ bool first;
-+ char *end;
-+ int ret;
-+
-+ for (; isspace(*p); ++p);
-+
-+ pad = media_parse_pad(media, p, &end);
-+ if (pad == NULL) {
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ for (p = end; isspace(*p); ++p);
-+ if (*p++ != '[') {
-+ media_dbg(media, "Expected '['\n");
-+ *endp = (char *)p - 1;
-+ return NULL;
-+ }
-+
-+ for (first = true; ; first = false) {
-+ for (; isspace(*p); p++);
-+
-+ /*
-+ * Backward compatibility: if the first property starts with an
-+ * uppercase later, process it as a format description.
-+ */
-+ if (strhazit("fmt:", &p) || (first && isupper(*p))) {
-+ ret = v4l2_subdev_parse_format(media, format, p, &end);
-+ if (ret < 0) {
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ p = end;
-+ continue;
-+ }
-+
-+ /*
-+ * Backward compatibility: crop rectangles can be specified
-+ * implicitly without the 'crop:' property name.
-+ */
-+ if (strhazit("crop:", &p) || *p == '(') {
-+ ret = v4l2_subdev_parse_rectangle(media, crop, p, &end);
-+ if (ret < 0) {
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ p = end;
-+ continue;
-+ }
-+
-+ if (strhazit("compose:", &p)) {
-+ ret = v4l2_subdev_parse_rectangle(media, compose, p, &end);
-+ if (ret < 0) {
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ for (p = end; isspace(*p); p++);
-+ continue;
-+ }
-+
-+ if (*p == '@') {
-+ ret = v4l2_subdev_parse_frame_interval(media, interval, ++p, &end);
-+ if (ret < 0) {
-+ *endp = end;
-+ return NULL;
-+ }
-+
-+ p = end;
-+ continue;
-+ }
-+
-+ break;
-+ }
-+
-+ if (*p != ']') {
-+ media_dbg(media, "Expected ']'\n");
-+ *endp = (char *)p;
-+ return NULL;
-+ }
-+
-+ *endp = (char *)p + 1;
-+ return pad;
-+}
-+
-+static int set_format(struct media_pad *pad,
-+ struct v4l2_mbus_framefmt *format)
-+{
-+ int ret;
-+
-+ if (format->width == 0 || format->height == 0)
-+ return 0;
-+
-+ media_dbg(pad->entity->media,
-+ "Setting up format %s %ux%u on pad %s/%u\n",
-+ v4l2_subdev_pixelcode_to_string(format->code),
-+ format->width, format->height,
-+ pad->entity->info.name, pad->index);
-+
-+ ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
-+ V4L2_SUBDEV_FORMAT_ACTIVE);
-+ if (ret < 0) {
-+ media_dbg(pad->entity->media,
-+ "Unable to set format: %s (%d)\n",
-+ strerror(-ret), ret);
-+ return ret;
-+ }
-+
-+ media_dbg(pad->entity->media,
-+ "Format set: %s %ux%u\n",
-+ v4l2_subdev_pixelcode_to_string(format->code),
-+ format->width, format->height);
-+
-+ return 0;
-+}
-+
-+static int set_selection(struct media_pad *pad, unsigned int target,
-+ struct v4l2_rect *rect)
-+{
-+ int ret;
-+
-+ if (rect->left == -1 || rect->top == -1)
-+ return 0;
-+
-+ media_dbg(pad->entity->media,
-+ "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u\n",
-+ target, rect->left, rect->top, rect->width, rect->height,
-+ pad->entity->info.name, pad->index);
-+
-+ ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index,
-+ target, V4L2_SUBDEV_FORMAT_ACTIVE);
-+ if (ret < 0) {
-+ media_dbg(pad->entity->media,
-+ "Unable to set selection rectangle: %s (%d)\n",
-+ strerror(-ret), ret);
-+ return ret;
-+ }
-+
-+ media_dbg(pad->entity->media,
-+ "Selection rectangle set: (%u,%u)/%ux%u\n",
-+ rect->left, rect->top, rect->width, rect->height);
-+
-+ return 0;
-+}
-+
-+static int set_frame_interval(struct media_entity *entity,
-+ struct v4l2_fract *interval)
-+{
-+ int ret;
-+
-+ if (interval->numerator == 0)
-+ return 0;
-+
-+ media_dbg(entity->media,
-+ "Setting up frame interval %u/%u on entity %s\n",
-+ interval->numerator, interval->denominator,
-+ entity->info.name);
-+
-+ ret = v4l2_subdev_set_frame_interval(entity, interval);
-+ if (ret < 0) {
-+ media_dbg(entity->media,
-+ "Unable to set frame interval: %s (%d)",
-+ strerror(-ret), ret);
-+ return ret;
-+ }
-+
-+ media_dbg(entity->media, "Frame interval set: %u/%u\n",
-+ interval->numerator, interval->denominator);
-+
-+ return 0;
-+}
-+
-+
-+static int v4l2_subdev_parse_setup_format(struct media_device *media,
-+ const char *p, char **endp)
-+{
-+ struct v4l2_mbus_framefmt format = { 0, 0, 0 };
-+ struct media_pad *pad;
-+ struct v4l2_rect crop = { -1, -1, -1, -1 };
-+ struct v4l2_rect compose = crop;
-+ struct v4l2_fract interval = { 0, 0 };
-+ unsigned int i;
-+ char *end;
-+ int ret;
-+
-+ pad = v4l2_subdev_parse_pad_format(media, &format, &crop, &compose,
-+ &interval, p, &end);
-+ if (pad == NULL) {
-+ media_print_streampos(media, p, end);
-+ media_dbg(media, "Unable to parse format\n");
-+ return -EINVAL;
-+ }
-+
-+ if (pad->flags & MEDIA_PAD_FL_SINK) {
-+ ret = set_format(pad, &format);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ ret = set_selection(pad, V4L2_SEL_TGT_CROP, &crop);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = set_selection(pad, V4L2_SEL_TGT_COMPOSE, &compose);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (pad->flags & MEDIA_PAD_FL_SOURCE) {
-+ ret = set_format(pad, &format);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ ret = set_frame_interval(pad->entity, &interval);
-+ if (ret < 0)
-+ return ret;
-+
-+
-+ /* If the pad is an output pad, automatically set the same format on
-+ * the remote subdev input pads, if any.
-+ */
-+ if (pad->flags & MEDIA_PAD_FL_SOURCE) {
-+ for (i = 0; i < pad->entity->num_links; ++i) {
-+ struct media_link *link = &pad->entity->links[i];
-+ struct v4l2_mbus_framefmt remote_format;
-+
-+ if (!(link->flags & MEDIA_LNK_FL_ENABLED))
-+ continue;
-+
-+ if (link->source == pad &&
-+ link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) {
-+ remote_format = format;
-+ set_format(link->sink, &remote_format);
-+ }
-+ }
-+ }
-+
-+ *endp = end;
-+ return 0;
-+}
-+
-+int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p)
-+{
-+ char *end;
-+ int ret;
-+
-+ do {
-+ ret = v4l2_subdev_parse_setup_format(media, p, &end);
-+ if (ret < 0)
-+ return ret;
-+
-+ p = end + 1;
-+ } while (*end == ',');
-+
-+ return *end ? -EINVAL : 0;
-+}
-+
-+static struct {
-+ const char *name;
-+ enum v4l2_mbus_pixelcode code;
-+} mbus_formats[] = {
-+ { "Y8", V4L2_MBUS_FMT_Y8_1X8},
-+ { "Y10", V4L2_MBUS_FMT_Y10_1X10 },
-+ { "Y12", V4L2_MBUS_FMT_Y12_1X12 },
-+ { "YUYV", V4L2_MBUS_FMT_YUYV8_1X16 },
-+ { "YUYV1_5X8", V4L2_MBUS_FMT_YUYV8_1_5X8 },
-+ { "YUYV2X8", V4L2_MBUS_FMT_YUYV8_2X8 },
-+ { "UYVY", V4L2_MBUS_FMT_UYVY8_1X16 },
-+ { "UYVY1_5X8", V4L2_MBUS_FMT_UYVY8_1_5X8 },
-+ { "UYVY2X8", V4L2_MBUS_FMT_UYVY8_2X8 },
-+ { "SBGGR8", V4L2_MBUS_FMT_SBGGR8_1X8 },
-+ { "SGBRG8", V4L2_MBUS_FMT_SGBRG8_1X8 },
-+ { "SGRBG8", V4L2_MBUS_FMT_SGRBG8_1X8 },
-+ { "SRGGB8", V4L2_MBUS_FMT_SRGGB8_1X8 },
-+ { "SBGGR10", V4L2_MBUS_FMT_SBGGR10_1X10 },
-+ { "SGBRG10", V4L2_MBUS_FMT_SGBRG10_1X10 },
-+ { "SGRBG10", V4L2_MBUS_FMT_SGRBG10_1X10 },
-+ { "SRGGB10", V4L2_MBUS_FMT_SRGGB10_1X10 },
-+ { "SBGGR10_DPCM8", V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 },
-+ { "SGBRG10_DPCM8", V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 },
-+ { "SGRBG10_DPCM8", V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 },
-+ { "SRGGB10_DPCM8", V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 },
-+ { "SBGGR12", V4L2_MBUS_FMT_SBGGR12_1X12 },
-+ { "SGBRG12", V4L2_MBUS_FMT_SGBRG12_1X12 },
-+ { "SGRBG12", V4L2_MBUS_FMT_SGRBG12_1X12 },
-+ { "SRGGB12", V4L2_MBUS_FMT_SRGGB12_1X12 },
-+ { "AYUV32", V4L2_MBUS_FMT_AYUV8_1X32 },
-+ { "ARGB32", V4L2_MBUS_FMT_ARGB8888_1X32 },
-+};
-+
-+const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code)
-+{
-+ unsigned int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) {
-+ if (mbus_formats[i].code == code)
-+ return mbus_formats[i].name;
-+ }
-+
-+ return "unknown";
-+}
-+
-+enum v4l2_mbus_pixelcode v4l2_subdev_string_to_pixelcode(const char *string,
-+ unsigned int length)
-+{
-+ unsigned int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) {
-+ if (strncmp(mbus_formats[i].name, string, length) == 0)
-+ break;
-+ }
-+
-+ if (i == ARRAY_SIZE(mbus_formats))
-+ return (enum v4l2_mbus_pixelcode)-1;
-+
-+ return mbus_formats[i].code;
-+}
-diff --git a/src/media-ctl/mediactl-priv.h b/src/media-ctl/mediactl-priv.h
-new file mode 100644
-index 0000000..a0d3a55
---- /dev/null
-+++ b/src/media-ctl/mediactl-priv.h
-@@ -0,0 +1,64 @@
-+/*
-+ * Media controller interface library
-+ *
-+ * Copyright (C) 2010-2014 Ideas on board SPRL
-+ *
-+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU Lesser General Public License as published
-+ * by the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program 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 Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+#ifndef __MEDIA_PRIV_H__
-+#define __MEDIA_PRIV_H__
-+
-+#include <linux/media.h>
-+
-+#include "mediactl.h"
-+
-+struct media_entity {
-+ struct media_device *media;
-+ struct media_entity_desc info;
-+ struct media_pad *pads;
-+ struct media_link *links;
-+ unsigned int max_links;
-+ unsigned int num_links;
-+
-+ char devname[32];
-+ int fd;
-+};
-+
-+struct media_device {
-+ int fd;
-+ int refcount;
-+ char *devnode;
-+
-+ struct media_device_info info;
-+ struct media_entity *entities;
-+ unsigned int entities_count;
-+
-+ void (*debug_handler)(void *, ...);
-+ void *debug_priv;
-+
-+ struct {
-+ struct media_entity *v4l;
-+ struct media_entity *fb;
-+ struct media_entity *alsa;
-+ struct media_entity *dvb;
-+ } def;
-+};
-+
-+#define media_dbg(media, ...) \
-+ (media)->debug_handler((media)->debug_priv, __VA_ARGS__)
-+
-+#endif /* __MEDIA_PRIV_H__ */
-diff --git a/src/media-ctl/mediactl.h b/src/media-ctl/mediactl.h
-new file mode 100644
-index 0000000..77ac182
---- /dev/null
-+++ b/src/media-ctl/mediactl.h
-@@ -0,0 +1,423 @@
-+/*
-+ * Media controller interface library
-+ *
-+ * Copyright (C) 2010-2014 Ideas on board SPRL
-+ *
-+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU Lesser General Public License as published
-+ * by the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program 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 Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+#ifndef __MEDIA_H__
-+#define __MEDIA_H__
-+
-+#include <linux/media.h>
-+
-+struct media_link {
-+ struct media_pad *source;
-+ struct media_pad *sink;
-+ struct media_link *twin;
-+ __u32 flags;
-+ __u32 padding[3];
-+};
-+
-+struct media_pad {
-+ struct media_entity *entity;
-+ __u32 index;
-+ __u32 flags;
-+ __u32 padding[3];
-+};
-+
-+struct media_device;
-+struct media_entity;
-+
-+/**
-+ * @brief Create a new media device.
-+ * @param devnode - device node path.
-+ *
-+ * Create a media device instance for the given device node and return it. The
-+ * device node is not accessed by this function, device node access errors will
-+ * not be caught and reported here. The media device needs to be enumerated
-+ * before it can be accessed, see media_device_enumerate().
-+ *
-+ * Media devices are reference-counted, see media_device_ref() and
-+ * media_device_unref() for more information.
-+ *
-+ * @return A pointer to the new media device or NULL if memory cannot be
-+ * allocated.
-+ */
-+struct media_device *media_device_new(const char *devnode);
-+
-+/**
-+ * @brief Create a new emulated media device.
-+ * @param info - device information.
-+ *
-+ * Emulated media devices are userspace-only objects not backed by a kernel
-+ * media device. They are created for ALSA and V4L2 devices that are not
-+ * associated with a media controller device.
-+ *
-+ * Only device query functions are available for media devices. Enumerating or
-+ * setting up links is invalid.
-+ *
-+ * @return A pointer to the new media device or NULL if memory cannot be
-+ * allocated.
-+ */
-+struct media_device *media_device_new_emulated(struct media_device_info *info);
-+
-+/**
-+ * @brief Take a reference to the device.
-+ * @param media - device instance.
-+ *
-+ * Media devices are reference-counted. Taking a reference to a device prevents
-+ * it from being freed until all references are released. The reference count is
-+ * initialized to 1 when the device is created.
-+ *
-+ * @return A pointer to @a media.
-+ */
-+struct media_device *media_device_ref(struct media_device *media);
-+
-+/**
-+ * @brief Release a reference to the device.
-+ * @param media - device instance.
-+ *
-+ * Release a reference to the media device. When the reference count reaches 0
-+ * this function frees the device.
-+ */
-+void media_device_unref(struct media_device *media);
-+
-+/**
-+ * @brief Add an entity to an existing media device
-+ * @param media - device instance.
-+ * @param desc - description of the entity to be added
-+ * @param devnode - device node corresponding to the entity
-+ *
-+ * Entities are usually created and added to media devices automatically when
-+ * the media device is enumerated through the media controller API. However,
-+ * when an emulated media device (thus not backed with a kernel-side media
-+ * controller device) is created, entities need to be manually added.
-+ *
-+ * Entities can also be manually added to a successfully enumerated media device
-+ * to group several functions provided by separate kernel devices. The most
-+ * common use case is to group the audio and video functions of a USB webcam in
-+ * a single media device. Those functions are exposed through separate USB
-+ * interfaces and handled through unrelated kernel drivers, they must thus be
-+ * manually added to the same media device.
-+ *
-+ * This function adds a new entity to the given media device and initializes it
-+ * from the given entity description and device node name. Only the following
-+ * fields of the description are copied over to the new entity:
-+ *
-+ * - type
-+ * - flags (MEDIA_ENT_FL_DEFAULT only)
-+ * - name
-+ * - v4l, fb, alsa or dvb (depending on the device type)
-+ *
-+ * All other fields of the newly created entity id are initialized to 0,
-+ * including the entity ID.
-+ *
-+ * @return Zero on success or -ENOMEM if memory cannot be allocated.
-+ */
-+int media_device_add_entity(struct media_device *media,
-+ const struct media_entity_desc *desc,
-+ const char *devnode);
-+
-+/**
-+ * @brief Set a handler for debug messages.
-+ * @param media - device instance.
-+ * @param debug_handler - debug message handler
-+ * @param debug_priv - first argument to debug message handler
-+ *
-+ * Set a handler for debug messages that will be called whenever
-+ * debugging information is to be printed. The handler expects an
-+ * fprintf-like function.
-+ */
-+void media_debug_set_handler(
-+ struct media_device *media, void (*debug_handler)(void *, ...),
-+ void *debug_priv);
-+
-+/**
-+ * @brief Enumerate the device topology
-+ * @param media - device instance.
-+ *
-+ * Enumerate the media device entities, pads and links. Calling this function is
-+ * mandatory before accessing the media device contents.
-+ *
-+ * @return Zero on success or a negative error code on failure.
-+ */
-+int media_device_enumerate(struct media_device *media);
-+
-+/**
-+ * @brief Locate the pad at the other end of a link.
-+ * @param pad - sink pad at one end of the link.
-+ *
-+ * Locate the source pad connected to @a pad through an enabled link. As only one
-+ * link connected to a sink pad can be enabled at a time, the connected source
-+ * pad is guaranteed to be unique.
-+ *
-+ * @return A pointer to the connected source pad, or NULL if all links connected
-+ * to @a pad are disabled. Return NULL also if @a pad is not a sink pad.
-+ */
-+struct media_pad *media_entity_remote_source(struct media_pad *pad);
-+
-+/**
-+ * @brief Get information about a media entity
-+ * @param entity - media entity.
-+ *
-+ * The information structure is owned by the media entity object and will be
-+ * freed when the object is destroyed.
-+ *
-+ * @return A pointer to the media entity information
-+ */
-+const struct media_entity_desc *media_entity_get_info(struct media_entity *entity);
-+
-+/**
-+ * @brief Get an entity pad
-+ * @param entity - media entity.
-+ * @param index - pad index.
-+ *
-+ * This function returns a pointer to the pad object identified by its index
-+ * for the given entity. If the pad index is out of bounds it will return NULL.
-+ *
-+ * @return A pointer to the pad
-+ */
-+const struct media_pad *media_entity_get_pad(struct media_entity *entity,
-+ unsigned int index);
-+
-+/**
-+ * @brief Get the number of links
-+ * @param entity - media entity.
-+ *
-+ * This function returns the total number of links that originate from or arrive
-+ * at the the media entity.
-+ *
-+ * @return The number of links for the entity
-+ */
-+unsigned int media_entity_get_links_count(struct media_entity *entity);
-+
-+/**
-+ * @brief Get an entity link
-+ * @param entity - media entity.
-+ * @param index - link index.
-+ *
-+ * This function returns a pointer to the link object identified by its index
-+ * for the given entity. If the link index is out of bounds it will return NULL.
-+ *
-+ * @return A pointer to the link
-+ */
-+const struct media_link *media_entity_get_link(struct media_entity *entity,
-+ unsigned int index);
-+
-+/**
-+ * @brief Get the device node name for an entity
-+ * @param entity - media entity.
-+ *
-+ * This function returns the full path and name to the device node corresponding
-+ * to the given entity.
-+ *
-+ * @return A pointer to the device node name or NULL if the entity has no
-+ * associated device node
-+ */
-+const char *media_entity_get_devname(struct media_entity *entity);
-+
-+/**
-+ * @brief Get the type of an entity.
-+ * @param entity - the entity.
-+ *
-+ * @return The type of @a entity.
-+ */
-+static inline unsigned int media_entity_type(struct media_entity *entity)
-+{
-+ return media_entity_get_info(entity)->type & MEDIA_ENT_TYPE_MASK;
-+}
-+
-+/**
-+ * @brief Find an entity by its name.
-+ * @param media - media device.
-+ * @param name - entity name.
-+ * @param length - size of @a name.
-+ *
-+ * Search for an entity with a name equal to @a name.
-+ *
-+ * @return A pointer to the entity if found, or NULL otherwise.
-+ */
-+struct media_entity *media_get_entity_by_name(struct media_device *media,
-+ const char *name, size_t length);
-+
-+/**
-+ * @brief Find an entity by its ID.
-+ * @param media - media device.
-+ * @param id - entity ID.
-+ *
-+ * This function searches for an entity based on its ID using an exact match or
-+ * next ID method based on the given @a id. If @a id is ORed with
-+ * MEDIA_ENT_ID_FLAG_NEXT, the function will return the entity with the smallest
-+ * ID larger than @a id. Otherwise it will return the entity with an ID equal to
-+ * @a id.
-+ *
-+ * @return A pointer to the entity if found, or NULL otherwise.
-+ */
-+struct media_entity *media_get_entity_by_id(struct media_device *media,
-+ __u32 id);
-+
-+/**
-+ * @brief Get the number of entities
-+ * @param media - media device.
-+ *
-+ * This function returns the total number of entities in the media device. If
-+ * entities haven't been enumerated yet it will return 0.
-+ *
-+ * @return The number of entities in the media device
-+ */
-+unsigned int media_get_entities_count(struct media_device *media);
-+
-+/**
-+ * @brief Get the entities
-+ * @param media - media device.
-+ *
-+ * This function returns a pointer to the array of entities for the media
-+ * device. If entities haven't been enumerated yet it will return NULL.
-+ *
-+ * The array of entities is owned by the media device object and will be freed
-+ * when the media object is destroyed.
-+ *
-+ * @return A pointer to an array of entities
-+ */
-+struct media_entity *media_get_entity(struct media_device *media, unsigned int index);
-+
-+/**
-+ * @brief Get the default entity for a given type
-+ * @param media - media device.
-+ * @param type - entity type.
-+ *
-+ * This function returns the default entity of the requested type. @a type must
-+ * be one of
-+ *
-+ * MEDIA_ENT_T_DEVNODE_V4L
-+ * MEDIA_ENT_T_DEVNODE_FB
-+ * MEDIA_ENT_T_DEVNODE_ALSA
-+ * MEDIA_ENT_T_DEVNODE_DVB
-+ *
-+ * @return A pointer to the default entity for the type if it exists, or NULL
-+ * otherwise.
-+ */
-+struct media_entity *media_get_default_entity(struct media_device *media,
-+ unsigned int type);
-+
-+/**
-+ * @brief Get the media device information
-+ * @param media - media device.
-+ *
-+ * The information structure is owned by the media device object and will be freed
-+ * when the media object is destroyed.
-+ *
-+ * @return A pointer to the media device information
-+ */
-+const struct media_device_info *media_get_info(struct media_device *media);
-+
-+/**
-+ * @brief Get the media device node name
-+ * @param media - media device.
-+ *
-+ * The device node name string is owned by the media device object and will be
-+ * freed when the media object is destroyed.
-+ *
-+ * @return A pointer to the media device node name
-+ */
-+const char *media_get_devnode(struct media_device *media);
-+
-+/**
-+ * @brief Configure a link.
-+ * @param media - media device.
-+ * @param source - source pad at the link origin.
-+ * @param sink - sink pad at the link target.
-+ * @param flags - configuration flags.
-+ *
-+ * Locate the link between @a source and @a sink, and configure it by applying
-+ * the new @a flags.
-+ *
-+ * Only the MEDIA_LINK_FLAG_ENABLED flag is writable.
-+ *
-+ * @return 0 on success, -1 on failure:
-+ * -ENOENT: link not found
-+ * - other error codes returned by MEDIA_IOC_SETUP_LINK
-+ */
-+int media_setup_link(struct media_device *media,
-+ struct media_pad *source, struct media_pad *sink,
-+ __u32 flags);
-+
-+/**
-+ * @brief Reset all links to the disabled state.
-+ * @param media - media device.
-+ *
-+ * Disable all links in the media device. This function is usually used after
-+ * opening a media device to reset all links to a known state.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int media_reset_links(struct media_device *media);
-+
-+/**
-+ * @brief Parse string to a pad on the media device.
-+ * @param media - media device.
-+ * @param p - input string
-+ * @param endp - pointer to string where parsing ended
-+ *
-+ * Parse NULL terminated string describing a pad and return its struct
-+ * media_pad instance.
-+ *
-+ * @return Pointer to struct media_pad on success, NULL on failure.
-+ */
-+struct media_pad *media_parse_pad(struct media_device *media,
-+ const char *p, char **endp);
-+
-+/**
-+ * @brief Parse string to a link on the media device.
-+ * @param media - media device.
-+ * @param p - input string
-+ * @param endp - pointer to p where parsing ended
-+ *
-+ * Parse NULL terminated string p describing a link and return its struct
-+ * media_link instance.
-+ *
-+ * @return Pointer to struct media_link on success, NULL on failure.
-+ */
-+struct media_link *media_parse_link(struct media_device *media,
-+ const char *p, char **endp);
-+
-+/**
-+ * @brief Parse string to a link on the media device and set it up.
-+ * @param media - media device.
-+ * @param p - input string
-+ *
-+ * Parse NULL terminated string p describing a link and its configuration
-+ * and configure the link.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int media_parse_setup_link(struct media_device *media,
-+ const char *p, char **endp);
-+
-+/**
-+ * @brief Parse string to link(s) on the media device and set it up.
-+ * @param media - media device.
-+ * @param p - input string
-+ *
-+ * Parse NULL terminated string p describing link(s) separated by
-+ * commas (,) and configure the link(s).
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int media_parse_setup_links(struct media_device *media, const char *p);
-+
-+#endif
-diff --git a/src/media-ctl/tools.h b/src/media-ctl/tools.h
-new file mode 100644
-index 0000000..815534c
---- /dev/null
-+++ b/src/media-ctl/tools.h
-@@ -0,0 +1,32 @@
-+/*
-+ * Media controller test application
-+ *
-+ * Copyright (C) 2010-2014 Ideas on board SPRL
-+ *
-+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU Lesser General Public License as published
-+ * by the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program 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 Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+#ifndef __TOOLS_H__
-+#define __TOOLS_H__
-+
-+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
-+#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
-+
-+void media_print_streampos(struct media_device *media, const char *p,
-+ const char *end);
-+
-+#endif /* __TOOLS_H__ */
-+
-diff --git a/src/media-ctl/v4l2subdev.h b/src/media-ctl/v4l2subdev.h
-new file mode 100644
-index 0000000..1cb53ff
---- /dev/null
-+++ b/src/media-ctl/v4l2subdev.h
-@@ -0,0 +1,258 @@
-+/*
-+ * V4L2 subdev interface library
-+ *
-+ * Copyright (C) 2010-2014 Ideas on board SPRL
-+ *
-+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU Lesser General Public License as published
-+ * by the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program 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 Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+#ifndef __SUBDEV_H__
-+#define __SUBDEV_H__
-+
-+#include <linux/v4l2-subdev.h>
-+
-+struct media_entity;
-+
-+/**
-+ * @brief Open a sub-device.
-+ * @param entity - sub-device media entity.
-+ *
-+ * Open the V4L2 subdev device node associated with @a entity. The file
-+ * descriptor is stored in the media_entity structure.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_open(struct media_entity *entity);
-+
-+/**
-+ * @brief Close a sub-device.
-+ * @param entity - sub-device media entity.
-+ *
-+ * Close the V4L2 subdev device node associated with the @a entity and opened by
-+ * a previous call to v4l2_subdev_open() (either explicit or implicit).
-+ */
-+void v4l2_subdev_close(struct media_entity *entity);
-+
-+/**
-+ * @brief Retrieve the format on a pad.
-+ * @param entity - subdev-device media entity.
-+ * @param format - format to be filled.
-+ * @param pad - pad number.
-+ * @param which - identifier of the format to get.
-+ *
-+ * Retrieve the current format on the @a entity @a pad and store it in the
-+ * @a format structure.
-+ *
-+ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try format stored
-+ * in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the current
-+ * active format.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_get_format(struct media_entity *entity,
-+ struct v4l2_mbus_framefmt *format, unsigned int pad,
-+ enum v4l2_subdev_format_whence which);
-+
-+/**
-+ * @brief Set the format on a pad.
-+ * @param entity - subdev-device media entity.
-+ * @param format - format.
-+ * @param pad - pad number.
-+ * @param which - identifier of the format to set.
-+ *
-+ * Set the format on the @a entity @a pad to @a format. The driver is allowed to
-+ * modify the requested format, in which case @a format is updated with the
-+ * modifications.
-+ *
-+ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try format stored in the
-+ * file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the device with an
-+ * active format.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_set_format(struct media_entity *entity,
-+ struct v4l2_mbus_framefmt *format, unsigned int pad,
-+ enum v4l2_subdev_format_whence which);
-+
-+/**
-+ * @brief Retrieve a selection rectangle on a pad.
-+ * @param entity - subdev-device media entity.
-+ * @param r - rectangle to be filled.
-+ * @param pad - pad number.
-+ * @param target - selection target
-+ * @param which - identifier of the format to get.
-+ *
-+ * Retrieve the @a target selection rectangle on the @a entity @a pad
-+ * and store it in the @a rect structure.
-+ *
-+ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try
-+ * selection rectangle stored in the file handle, or
-+ * V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the current active selection
-+ * rectangle.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_get_selection(struct media_entity *entity,
-+ struct v4l2_rect *rect, unsigned int pad, unsigned int target,
-+ enum v4l2_subdev_format_whence which);
-+
-+/**
-+ * @brief Set a selection rectangle on a pad.
-+ * @param entity - subdev-device media entity.
-+ * @param rect - crop rectangle.
-+ * @param pad - pad number.
-+ * @param target - selection target
-+ * @param which - identifier of the format to set.
-+ *
-+ * Set the @a target selection rectangle on the @a entity @a pad to @a
-+ * rect. The driver is allowed to modify the requested rectangle, in
-+ * which case @a rect is updated with the modifications.
-+ *
-+ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try crop rectangle
-+ * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the
-+ * device with an active crop rectangle.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_set_selection(struct media_entity *entity,
-+ struct v4l2_rect *rect, unsigned int pad, unsigned int target,
-+ enum v4l2_subdev_format_whence which);
-+
-+/**
-+ * @brief Query the digital video capabilities of a pad.
-+ * @param entity - subdev-device media entity.
-+ * @param cap - capabilities to be filled.
-+ *
-+ * Retrieve the digital video capabilities of the @a entity pad specified by
-+ * @a cap.pad and store it in the @a cap structure.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity,
-+ struct v4l2_dv_timings_cap *caps);
-+
-+/**
-+ * @brief Query the digital video timings of a sub-device
-+ * @param entity - subdev-device media entity.
-+ * @param timings timings to be filled.
-+ *
-+ * Retrieve the detected digital video timings for the currently selected input
-+ * of @a entity and store them in the @a timings structure.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_query_dv_timings(struct media_entity *entity,
-+ struct v4l2_dv_timings *timings);
-+
-+/**
-+ * @brief Get the current digital video timings of a sub-device
-+ * @param entity - subdev-device media entity.
-+ * @param timings timings to be filled.
-+ *
-+ * Retrieve the current digital video timings for the currently selected input
-+ * of @a entity and store them in the @a timings structure.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_get_dv_timings(struct media_entity *entity,
-+ struct v4l2_dv_timings *timings);
-+
-+/**
-+ * @brief Set the digital video timings of a sub-device
-+ * @param entity - subdev-device media entity.
-+ * @param timings timings to be set.
-+ *
-+ * Set the digital video timings of @a entity to @a timings. The driver is
-+ * allowed to modify the requested format, in which case @a timings is updated
-+ * with the modifications.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_set_dv_timings(struct media_entity *entity,
-+ struct v4l2_dv_timings *timings);
-+
-+/**
-+ * @brief Retrieve the frame interval on a sub-device.
-+ * @param entity - subdev-device media entity.
-+ * @param interval - frame interval to be filled.
-+ *
-+ * Retrieve the current frame interval on subdev @a entity and store it in the
-+ * @a interval structure.
-+ *
-+ * Frame interval retrieving is usually supported only on devices at the
-+ * beginning of video pipelines, such as sensors.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+
-+int v4l2_subdev_get_frame_interval(struct media_entity *entity,
-+ struct v4l2_fract *interval);
-+
-+/**
-+ * @brief Set the frame interval on a sub-device.
-+ * @param entity - subdev-device media entity.
-+ * @param interval - frame interval.
-+ *
-+ * Set the frame interval on subdev @a entity to @a interval. The driver is
-+ * allowed to modify the requested frame interval, in which case @a interval is
-+ * updated with the modifications.
-+ *
-+ * Frame interval setting is usually supported only on devices at the beginning
-+ * of video pipelines, such as sensors.
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_set_frame_interval(struct media_entity *entity,
-+ struct v4l2_fract *interval);
-+
-+/**
-+ * @brief Parse a string and apply format, crop and frame interval settings.
-+ * @param media - media device.
-+ * @param p - input string
-+ * @param endp - pointer to string p where parsing ended (return)
-+ *
-+ * Parse string @a p and apply format, crop and frame interval settings to a
-+ * subdev pad specified in @a p. @a endp will be written a pointer where
-+ * parsing of @a p ended.
-+ *
-+ * Format strings are separeted by commas (,).
-+ *
-+ * @return 0 on success, or a negative error code on failure.
-+ */
-+int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p);
-+
-+/**
-+ * @brief Convert media bus pixel code to string.
-+ * @param code - input string
-+ *
-+ * Convert media bus pixel code @a code to a human-readable string.
-+ *
-+ * @return A pointer to a string on success, NULL on failure.
-+ */
-+const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code);
-+
-+/**
-+ * @brief Parse string to media bus pixel code.
-+ * @param string - input string
-+ * @param lenght - length of the string
-+ *
-+ * Parse human readable string @a string to an media bus pixel code.
-+ *
-+ * @return media bus pixelcode on success, -1 on failure.
-+ */
-+enum v4l2_mbus_pixelcode v4l2_subdev_string_to_pixelcode(const char *string,
-+ unsigned int length);
-+#endif
---
-2.7.4
-
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0004-Fix-GST-error-complainings.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0004-Fix-GST-error-complainings.patch
deleted file mode 100644
index 40f2e53..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/0004-Fix-GST-error-complainings.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 1f0c74cb16056d4338188f5de4a9bcf1e1fc79a0 Mon Sep 17 00:00:00 2001
-From: Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com>
-Date: Tue, 17 Jan 2017 19:48:01 +0300
-Subject: [PATCH] Fix GST error complainings
-
-GStreamer- CRITICAL **: gst_segment_to_running_time: assertion 'segment->format == format' failed
-
-Signed-off-by: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
-Signed-off-by: Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com>
----
- src/gst-recorder.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/src/gst-recorder.c b/src/gst-recorder.c
-index c84d410..91ba0c7 100644
---- a/src/gst-recorder.c
-+++ b/src/gst-recorder.c
-@@ -1021,6 +1021,12 @@ gst_recorder_create(struct gst_recorder_settings *settings)
- "framerate", GST_TYPE_FRACTION, 0, DEFAULT_FPS,
- NULL), NULL);
-
-+ g_object_set (G_OBJECT (r->appsrc),
-+ "stream-type", 0, /* GST_APP_STREAM_TYPE_STREAM */
-+ "format", GST_FORMAT_TIME,
-+ "is-live", TRUE,
-+ NULL);
-+
- r->appsrc_pad = gst_element_get_static_pad(GST_ELEMENT_CAST(r->appsrc), "src");
- if (!r->appsrc_pad)
- weston_log("Failed to get src0 pad of appsrc\n");
---
-2.9.2
-
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service
deleted file mode 100644
index db0fd29..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service
+++ /dev/null
@@ -1,13 +0,0 @@
-[Unit]
-Description=Weston reference Wayland compositor
-Conflicts=getty@tty1.service
-After=dbus.service rc.pvr.service
-
-[Service]
-ExecStartPre=/bin/rm -rf /home/root/.cache/gstreamer-1.0
-ExecStart=/usr/bin/weston-launch -u root -- --idle-time=4294967 --gst-record
-ExecStop=/usr/bin/killall -s KILL weston
-Type=simple
-
-[Install]
-WantedBy=multi-user.target
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend
deleted file mode 100644
index 533be7e..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend
+++ /dev/null
@@ -1,12 +0,0 @@
-EXTRA_OECONF += "--enable-gst-recorder "
-DEPENDS += "media-ctl gstreamer1.0 gstreamer1.0-plugins-base libjpeg-turbo"
-
-FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
-
-SRC_URI_append = " \
- file://0001-Add-virtual-output-support.patch \
- file://0002-Get-DMA-fd-on-bo.patch \
- file://0003-Add-gst-recorder-for-h264-output-streaming.patch \
- file://0004-Fix-GST-error-complainings.patch \
- file://weston.service \
-"