summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerrit Code Review <gerrit@automotivelinux.org>2017-01-19 07:01:22 +0000
committerGerrit Code Review <gerrit@automotivelinux.org>2017-01-19 07:01:22 +0000
commita89b8302db0346269f4202e95ca85dbbb6f75a8e (patch)
tree873e234062fb0fcd8c71bd031bdaa217c877648c
parent73e6110c6c12a875da9b6da46c8ddaaff79e3c79 (diff)
parent793d8d01d0377dad21f4e1a330f00d822eced086 (diff)
Merge "Merge branch 'chinook_fixed' into chinook" into chinook
-rw-r--r--meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch4
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb4
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch90
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch50
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb41
-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/disable-wayland-scanner-pkg-check.patch13
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch43
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch37
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch81
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop9
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/weston.pngbin2383 -> 0 bytes
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service13
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bb87
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend26
-rw-r--r--meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend11
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch29
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch2
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch68
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb4
-rw-r--r--meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb58
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch85
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch65
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch2759
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0004-Export-a-first-dmabuf-file-descriptor-with-the-whole.patch65
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch75
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch46
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch140
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch47
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch113
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch186
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf62
-rw-r--r--meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend29
-rw-r--r--meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb4
36 files changed, 8559 insertions, 394 deletions
diff --git a/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch b/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch
index 846527b..0c48b09 100644
--- a/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch
+++ b/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch
@@ -403,9 +403,9 @@ index 0000000..e6ded08
+ u8 val;
+
+ i2c_init(CONFIG_SYS_I2C_SPEED, 0);
-+ i2c_read(0x58, 0x13, 1, &val, 1);
++ i2c_read(0x5a, 0x13, 1, &val, 1);
+ val |= 0x02;
-+ i2c_write(0x58, 0x13, 1, &val, 1);
++ i2c_write(0x5a, 0x13, 1, &val, 1);
+}
+
+enum {
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb b/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb
index 329e972..b0ddcaf 100644
--- a/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb
+++ b/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb
@@ -7,6 +7,10 @@ LIC_FILES_CHKSUM = "file://gbm.c;beginline=4;endline=22;md5=5cdaac262c876e98e477
SRCREV = "d5a58c689932d42add1301c5cd323da5244374af"
SRC_URI = "git://github.com/thayama/libgbm;protocol=git;branch=master"
+SRC_URI_append = " \
+ file://0001-Add-gbm_bo_get_fd-function.patch \
+"
+
S = "${WORKDIR}/git"
COMPATIBLE_MACHINE = "(r8a7790|r8a7791|r8a7793|r8a7794)"
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch b/meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch
new file mode 100644
index 0000000..e688cde
--- /dev/null
+++ b/meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch
@@ -0,0 +1,90 @@
+From e6d4594481a2c7d9625d1f4abf898cd461c30c42 Mon Sep 17 00:00:00 2001
+From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
+Date: Wed, 2 Nov 2016 16:17:16 +0300
+Subject: [PATCH] Add gbm_bo_get_fd() function
+
+---
+ backend_kms.c | 8 ++++++++
+ gbm.c | 15 +++++++++++++++
+ gbm.h | 3 +++
+ gbmint.h | 1 +
+ 4 files changed, 27 insertions(+)
+
+diff --git a/backend_kms.c b/backend_kms.c
+index cfee3b0..3fc1d66 100644
+--- a/backend_kms.c
++++ b/backend_kms.c
+@@ -339,6 +339,13 @@ static int gbm_kms_surface_has_free_buffers(struct gbm_surface *_surface)
+ return ((!surface->bo[0]->locked) || (!surface->bo[1]->locked));
+ }
+
++static int gbm_kms_bo_get_fd(struct gbm_bo *_bo)
++{
++ struct gbm_kms_bo *bo = (struct gbm_kms_bo*)_bo;
++
++ return bo->fd;
++}
++
+ struct gbm_device kms_gbm_device = {
+ .name = "kms",
+
+@@ -349,6 +356,7 @@ struct gbm_device kms_gbm_device = {
+ .bo_import = gbm_kms_bo_import,
+ .bo_write = gbm_kms_bo_write,
+ .bo_destroy = gbm_kms_bo_destroy,
++ .bo_get_fd = gbm_kms_bo_get_fd,
+
+ .surface_create = gbm_kms_surface_create,
+ .surface_lock_front_buffer = gbm_kms_surface_lock_front_buffer,
+diff --git a/gbm.c b/gbm.c
+index c58576d..458fac0 100644
+--- a/gbm.c
++++ b/gbm.c
+@@ -470,3 +470,18 @@ gbm_surface_has_free_buffers(struct gbm_surface *surf)
+ {
+ return surf->gbm->surface_has_free_buffers(surf);
+ }
++
++/** Get a DMA-BUF file descriptor for the buffer object
++ *
++ * This function creates a DMA-BUF (also known as PRIME) file descriptor
++ * handle for the buffer object. Eeach call to gbm_bo_get_fd() returns a new
++ * file descriptor and the caller is responsible for closing the file
++ * descriptor.
++ * \param bo The buffer object
++ * \return Returns a file descriptor referring to the underlying buffer
++ */
++GBM_EXPORT int
++gbm_bo_get_fd(struct gbm_bo *bo)
++{
++ return bo->gbm->bo_get_fd(bo);
++}
+diff --git a/gbm.h b/gbm.h
+index 9d2a030..ad92935 100644
+--- a/gbm.h
++++ b/gbm.h
+@@ -285,6 +285,9 @@ gbm_surface_has_free_buffers(struct gbm_surface *surface);
+ void
+ gbm_surface_destroy(struct gbm_surface *surface);
+
++int
++gbm_bo_get_fd(struct gbm_bo *bo);
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/gbmint.h b/gbmint.h
+index a467bea..70a8d4a 100644
+--- a/gbmint.h
++++ b/gbmint.h
+@@ -70,6 +70,7 @@ struct gbm_device {
+ void *buffer, uint32_t usage);
+ int (*bo_write)(struct gbm_bo *bo, const void *buf, size_t data);
+ void (*bo_destroy)(struct gbm_bo *bo);
++ int (*bo_get_fd)(struct gbm_bo *_bo);
+
+ struct gbm_surface *(*surface_create)(struct gbm_device *gbm,
+ uint32_t width, uint32_t height,
+--
+2.7.4
+
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch b/meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch
deleted file mode 100644
index cd8bc55..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-disable macro checks not used for scanner
-
-We only build wayland-native for the scanner, so disable the bits we don't
-actually need. This avoid build issue on older distro such as Centos 5.x:
-| error: 'O_CLOEXEC' undeclared (first use in this function)
-| error: sys/timerfd.h: No such file or directory
-| error: 'CLOCK_MONOTONIC' undeclared (first use in this function)
-| error: 'TFD_CLOEXEC' undeclared (first use in this function)
-| error: 'SFD_CLOEXEC' undeclared (first use in this function)
-
-Upstream-Status: Pending
-
-Signed-off-by: Ting Liu <b28495@freescale.com>
----
- configure.ac | 20 ++++++++++----------
- 1 file changed, 10 insertions(+), 10 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
---- a/configure.ac
-+++ b/configure.ac
-@@ -41,16 +41,16 @@ AC_SUBST(GCC_CFLAGS)
-
- AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate])
-
--AC_CHECK_DECL(SFD_CLOEXEC,[],
-- [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland")],
-- [[#include <sys/signalfd.h>]])
--AC_CHECK_DECL(TFD_CLOEXEC,[],
-- [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland")],
-- [[#include <sys/timerfd.h>]])
--AC_CHECK_DECL(CLOCK_MONOTONIC,[],
-- [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland")],
-- [[#include <time.h>]])
--AC_CHECK_HEADERS([execinfo.h])
-+##AC_CHECK_DECL(SFD_CLOEXEC,[],
-+# [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland")],
-+# [[#include <sys/signalfd.h>]])
-+#AC_CHECK_DECL(TFD_CLOEXEC,[],
-+# [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland")],
-+# [[#include <sys/timerfd.h>]])
-+#AC_CHECK_DECL(CLOCK_MONOTONIC,[],
-+# [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland")],
-+# [[#include <time.h>]])
-+#AC_CHECK_HEADERS([execinfo.h])
-
- AC_ARG_ENABLE([scanner],
- [AC_HELP_STRING([--disable-scanner],
---
-1.8.3.2
-
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb b/meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb
deleted file mode 100644
index b1ae59e..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb
+++ /dev/null
@@ -1,41 +0,0 @@
-SUMMARY = "Wayland, a protocol between a compositor and clients"
-DESCRIPTION = "Wayland is a protocol for a compositor to talk to its clients \
-as well as a C library implementation of that protocol. The compositor can be \
-a standalone display server running on Linux kernel modesetting and evdev \
-input devices, an X application, or a wayland client itself. The clients can \
-be traditional applications, X servers (rootless or fullscreen) or other \
-display servers."
-HOMEPAGE = "http://wayland.freedesktop.org"
-LICENSE = "MIT"
-LIC_FILES_CHKSUM = "file://COPYING;md5=1d4476a7d98dd5691c53d4d43a510c72 \
- file://src/wayland-server.c;endline=21;md5=079ae21dbf98ada52ec23744851b0a5c"
-
-SRC_URI = "http://wayland.freedesktop.org/releases/${BPN}-${PV}.tar.xz"
-SRC_URI[md5sum] = "1d882776b27329b91d2d500b6d66dd1d"
-SRC_URI[sha256sum] = "0069e1e9af888b3e05384380ad8cc6c976ea3e81d08ba19b7675ce1d693a41b5"
-SRC_URI_append_class-native = " \
- file://disable-macro-checks-not-used-for-scanner.patch \
- "
-EXTRA_OECONF_class-native = "--disable-documentation --enable-scanner"
-
-inherit autotools pkgconfig
-
-# We need wayland-native for the wayland-scanner utility
-BBCLASSEXTEND = "native"
-
-DEPENDS_class-native = "expat-native libffi-native"
-DEPENDS = "expat libffi wayland-native"
-
-EXTRA_OECONF = "--disable-documentation --disable-scanner"
-
-# Wayland installs a M4 macro for other projects to use, which uses the target
-# pkg-config to find files. Replace pkg-config with pkg-config-native.
-do_install_append_class-native() {
- sed -e 's,PKG_CHECK_MODULES(.*),,g' \
- -e 's,$PKG_CONFIG,pkg-config-native,g' \
- -i ${D}/${datadir}/aclocal/wayland-scanner.m4
-}
-
-sysroot_stage_all_append_class-target () {
- cp ${STAGING_DATADIR_NATIVE}/aclocal/wayland-scanner.m4 ${SYSROOT_DESTDIR}/${datadir}/aclocal/
-}
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
new file mode 100644
index 0000000..51c2671
--- /dev/null
+++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch
@@ -0,0 +1,391 @@
+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
new file mode 100644
index 0000000..2683afe
--- /dev/null
+++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch
@@ -0,0 +1,33 @@
+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
new file mode 100644
index 0000000..e6eccc6
--- /dev/null
+++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch
@@ -0,0 +1,4183 @@
+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/disable-wayland-scanner-pkg-check.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/disable-wayland-scanner-pkg-check.patch
deleted file mode 100644
index 062da5c..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/disable-wayland-scanner-pkg-check.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-Index: weston-1.5.0/configure.ac
-===================================================================
---- weston-1.5.0.orig/configure.ac
-+++ weston-1.5.0/configure.ac
-@@ -503,7 +503,7 @@ if test x$wayland_scanner = x; then
- AC_MSG_ERROR([wayland-scanner is needed to compile weston])
- fi
-
--PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner)
-+#PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner)
-
- AC_CONFIG_FILES([Makefile src/version.h src/weston.pc])
-
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch
deleted file mode 100644
index 35e6d6f..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-weston-1.5.0/configure.ac: make lcms explicitly configurable
-
-The lcms package is outside of openembedded-core, so make it
-explicitly configurable. Make it deterministic, so that if lcms
-dependencies are missing, autoconf throws a fatal error. Follow
-upstream style to make it more likely to be merged.
-
-Upstream-Status: Pending
-
-Index: weston-1.5.0/configure.ac
-===================================================================
---- weston-1.5.0.orig/configure.ac
-+++ weston-1.5.0/configure.ac
-@@ -491,12 +491,24 @@ AC_ARG_ENABLE(demo-clients-install,
- enable_demo_clients_install=no)
- AM_CONDITIONAL(INSTALL_DEMO_CLIENTS, [test "x$enable_demo_clients_install" = "xyes"])
-
--PKG_CHECK_MODULES(LCMS, lcms2,
-- [have_lcms=yes], [have_lcms=no])
--if test "x$have_lcms" = xyes; then
-- AC_DEFINE(HAVE_LCMS, 1, [Have lcms support])
-+AC_ARG_ENABLE(lcms,
-+ AS_HELP_STRING([--disable-lcms],
-+ [Disable lcms support]),,
-+ enable_lcms=auto)
-+AM_CONDITIONAL(HAVE_LCMS, [test "x$enable_lcms" = xyes])
-+if test "x$enable_lcms" != "xno"; then
-+ PKG_CHECK_MODULES(LCMS,
-+ lcms2,
-+ [have_lcms=yes],
-+ [have_lcms=no])
-+ if test "x$have_lcms" = "xno" -a "x$enable_lcms" = "xyes"; then
-+ AC_MSG_ERROR([lcms support explicitly requested, but lcms couldn't be found])
-+ fi
-+ if test "x$have_lcms" = "xyes"; then
-+ enable_lcms=yes
-+ AC_DEFINE(HAVE_LCMS, 1, [Have lcms support])
-+ fi
- fi
--AM_CONDITIONAL(HAVE_LCMS, [test "x$have_lcms" = xyes])
-
- AC_PATH_PROG([wayland_scanner], [wayland-scanner])
- if test x$wayland_scanner = x; then
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch
deleted file mode 100644
index ad07d4f..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-
-The libwebp package is outside of openembedded-core, so make it
-explicitly configurable. Make it deterministic, so that if libwebp
-dependencies are missing, autoconf throws a fatal error.
-
-Upstream-Status: Pending
-
-Index: weston-1.5.0/configure.ac
-===================================================================
---- weston-1.5.0.orig/configure.ac
-+++ weston-1.5.0/configure.ac
-@@ -268,9 +268,22 @@ fi
-
- PKG_CHECK_MODULES(PIXMAN, [pixman-1])
- PKG_CHECK_MODULES(PNG, [libpng])
--PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no])
--AS_IF([test "x$have_webp" = "xyes"],
-- [AC_DEFINE([HAVE_WEBP], [1], [Have webp])])
-+AC_ARG_ENABLE(webp,
-+ AS_HELP_STRING([--disable-webp],
-+ [Disable libwebp support]),,
-+ enable_webp=auto)
-+AM_CONDITIONAL(HAVE_WEBP, [test "x$enable_webp" = xyes])
-+AS_IF([test "x$enable_webp" != "xno"],
-+ PKG_CHECK_MODULES(WEBP,
-+ [libwebp],
-+ [have_webp=yes],
-+ [have_webp=no])
-+ AS_IF([test "x$have_webp" = "xno" -a "x$enable_webp" = "xyes"],
-+ AC_MSG_ERROR([libwebp support explicitly request, but lipwebp could not be found]))
-+ AS_IF([test "x$have_webp" = "xyes"],
-+ [enable_webp=yes]
-+ [AC_DEFINE([HAVE_WEBP], [1], [Have webp])])
-+)
-
- AC_ARG_ENABLE(vaapi-recorder, [ --enable-vaapi-recorder],,
- enable_vaapi_recorder=auto)
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch
deleted file mode 100644
index 9bf5752..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From be437c6b27a2c6b83b2e60e30e5f0cdcbda28ad0 Mon Sep 17 00:00:00 2001
-From: Yannick Gicquel <yannick.gicquel@iot.bzh>
-Date: Fri, 6 Nov 2015 09:01:43 +0100
-Subject: [PATCH] weston: workaround to avoid segfault on hotplug event
-
-When user unplug/replug a monitor, HDMI link for example,
-weston segfault while calling gbm_surface_destroy from
-drm_output_destroy function.
-
-This workaround basically disable the output destroy
-mechanism on unplug/replug event callbacks. It also force
-the compositor to do a pageflip in order to refresh the
-output when it is connected back.
-
-All this workaround is under "KEEP_OUTPUTS_ON_HOTPLUG"
-compilation flag, which can be disable if required.
-
-Signed-off-by: Yannick Gicquel <yannick.gicquel@iot.bzh>
-Signed-off-by: Manuel Bachmann <mbc@iot.bzh>
----
- src/compositor-drm.c | 22 ++++++++++++++++++++++
- 1 file changed, 22 insertions(+)
-
-diff --git a/src/compositor-drm.c b/src/compositor-drm.c
-index 2087c93..21b7776 100644
---- a/src/compositor-drm.c
-+++ b/src/compositor-drm.c
-@@ -56,6 +56,12 @@
- #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
- #endif
-
-+/* This allow weston to keep outputs when re-attached after
-+ * a previous unplug event. Comment line below to fallback
-+ * to initial behavior.
-+ */
-+#define KEEP_OUTPUTS_ON_HOTPLUG
-+
- static int option_current_mode = 0;
-
- enum output_config {
-@@ -2383,6 +2389,11 @@ create_outputs(struct drm_compositor *ec, uint32_t option_connector,
- return 0;
- }
-
-+#if defined(KEEP_OUTPUTS_ON_HOTPLUG)
-+static void
-+drm_compositor_set_modes(struct drm_compositor *compositor);
-+#endif
-+
- static void
- update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
- {
-@@ -2443,14 +2454,25 @@ update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
- disconnects &= ~(1 << output->connector_id);
- weston_log("connector %d disconnected\n",
- output->connector_id);
-+#if !defined(KEEP_OUTPUTS_ON_HOTPLUG)
- drm_output_destroy(&output->base);
-+#endif
- }
- }
- }
-
-+#if defined(KEEP_OUTPUTS_ON_HOTPLUG)
-+ /* This refresh the display when connector is plugged back */
-+ wl_list_for_each_safe(output, next, &ec->base.output_list,
-+ base.link) {
-+ /* FIXME: following is required only on 'connected' event. */
-+ drm_compositor_set_modes(ec);
-+ }
-+#else
- /* FIXME: handle zero outputs, without terminating */
- if (ec->connector_allocator == 0)
- wl_display_terminate(ec->base.wl_display);
-+#endif
- }
-
- static int
---
-1.9.1
-
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop
deleted file mode 100644
index 1086ae8..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop
+++ /dev/null
@@ -1,9 +0,0 @@
-[Desktop Entry]
-Encoding=UTF-8
-Type=Application
-Name=Weston
-Comment=Wayland Compostitor
-Exec=weston
-Icon=weston
-Terminal=false
-Categories=Utility;
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png
deleted file mode 100644
index ea8b7e0..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png
+++ /dev/null
Binary files differ
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service
new file mode 100644
index 0000000..db0fd29
--- /dev/null
+++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service
@@ -0,0 +1,13 @@
+[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.5.0.bb b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bb
deleted file mode 100644
index 4a8584f..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bb
+++ /dev/null
@@ -1,87 +0,0 @@
-SUMMARY = "Weston, a Wayland compositor"
-DESCRIPTION = "Weston is the reference implementation of a Wayland compositor"
-HOMEPAGE = "http://wayland.freedesktop.org"
-LICENSE = "MIT"
-LIC_FILES_CHKSUM = "file://COPYING;md5=275efac2559a224527bd4fd593d38466 \
- file://src/compositor.c;endline=23;md5=aa98a8db03480fe7d500d0b1f4b8850c"
-
-SRC_URI = "http://wayland.freedesktop.org/releases/${BPN}-${PV}.tar.xz \
- file://weston.png \
- file://weston.desktop \
- file://disable-wayland-scanner-pkg-check.patch \
- file://make-lcms-explicitly-configurable.patch \
- file://make-libwebp-explicitly-configurable.patch \
-"
-SRC_URI[md5sum] = "8eb40d230efc2411f083c20656534780"
-SRC_URI[sha256sum] = "06388ba04ac79aa72d685cc1a8e646ddb2b8cfe11fcc742294f9addac48b7684"
-
-inherit autotools pkgconfig useradd
-
-DEPENDS = "libxkbcommon gdk-pixbuf pixman cairo glib-2.0 jpeg"
-DEPENDS += "wayland virtual/egl pango"
-
-EXTRA_OECONF = "--enable-setuid-install \
- --disable-xwayland \
- --enable-simple-clients \
- --enable-clients \
- --enable-demo-clients-install \
- --disable-libunwind \
- --disable-rpi-compositor \
- --disable-rdp-compositor \
- "
-
-
-PACKAGECONFIG ??= "${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'kms fbdev wayland egl', '', d)} \
- ${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'x11', '', d)} \
- ${@bb.utils.contains('DISTRO_FEATURES', 'pam', 'launch', '', d)} \
- "
-#
-# Compositor choices
-#
-# Weston on KMS
-PACKAGECONFIG[kms] = "--enable-drm-compositor,--disable-drm-compositor,drm udev virtual/mesa mtdev"
-# Weston on Wayland (nested Weston)
-PACKAGECONFIG[wayland] = "--enable-wayland-compositor,--disable-wayland-compositor,virtual/mesa"
-# Weston on X11
-PACKAGECONFIG[x11] = "--enable-x11-compositor,--disable-x11-compositor,virtual/libx11 libxcb libxcb libxcursor cairo"
-# Headless Weston
-PACKAGECONFIG[headless] = "--enable-headless-compositor,--disable-headless-compositor"
-# Weston on framebuffer
-PACKAGECONFIG[fbdev] = "--enable-fbdev-compositor,--disable-fbdev-compositor,udev mtdev"
-# weston-launch
-PACKAGECONFIG[launch] = "--enable-weston-launch,--disable-weston-launch,libpam drm"
-# VA-API desktop recorder
-PACKAGECONFIG[vaapi] = "--enable-vaapi-recorder,--disable-vaapi-recorder,libva"
-# Weston with EGL support
-PACKAGECONFIG[egl] = "--enable-egl --enable-simple-egl-clients,--disable-egl --disable-simple-egl-clients,virtual/egl"
-# Weston with cairo glesv2 support
-PACKAGECONFIG[cairo-glesv2] = "--with-cairo-glesv2,--with-cairo=image,cairo"
-# Weston with lcms support
-PACKAGECONFIG[lcms] = "--enable-lcms,--disable-lcms,lcms"
-# Weston with webp support
-PACKAGECONFIG[webp] = "--enable-webp,--disable-webp,libwebp"
-
-do_install_append() {
- # Weston doesn't need the .la files to load modules, so wipe them
- rm -f ${D}/${libdir}/weston/*.la
-
- # If X11, ship a desktop file to launch it
- if [ "${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'x11', '', d)}" = "x11" ]; then
- install -d ${D}${datadir}/applications
- install ${WORKDIR}/weston.desktop ${D}${datadir}/applications
-
- install -d ${D}${datadir}/icons/hicolor/48x48/apps
- install ${WORKDIR}/weston.png ${D}${datadir}/icons/hicolor/48x48/apps
- fi
-}
-
-PACKAGES += "${PN}-examples"
-
-FILES_${PN} = "${bindir}/weston ${bindir}/weston-terminal ${bindir}/weston-info ${bindir}/weston-launch ${bindir}/wcap-decode ${libexecdir} ${datadir}"
-FILES_${PN}-examples = "${bindir}/*"
-
-RDEPENDS_${PN} += "xkeyboard-config"
-RRECOMMENDS_${PN} = "liberation-fonts"
-
-USERADD_PACKAGES = "${PN}"
-GROUPADD_PARAM_${PN} = "--system weston-launch"
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend
deleted file mode 100644
index 6a18dba..0000000
--- a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend
+++ /dev/null
@@ -1,26 +0,0 @@
-require ../../include/gles-control.inc
-
-PACKAGECONFIG_rcar-gen2 := "${@'${PACKAGECONFIG}'.replace('x11', '')}"
-
-PACKAGECONFIG_append_rcar-gen2 = " \
- ${@base_conditional('USE_GLES', '1', '', 'fbdev', d)}"
-DEPENDS_append_rcar-gen2 = " \
- ${@base_conditional('USE_GLES', '1', 'gles-user-module', '', d)}"
-EXTRA_OECONF_append_rcar-gen2 = " \
- ${@base_conditional('USE_GLES', '1', '--enable-v4l2', \
- '--disable-xwayland-test WESTON_NATIVE_BACKEND=fbdev-backend.so', d)}"
-
-SRCREV_rcar-gen2 = "${@'decdc587ceb147d22eb989a3334e627208030f69' \
- if '1' in '${USE_GLES}' else '00781bcf518f6bab0d08e6962630b0994e8bf632'}"
-SRC_URI_rcar-gen2 = "git://github.com/renesas-devel/weston.git;protocol=git;branch=RCAR-GEN2/1.5.0/gl-fallback \
- file://weston.desktop \
- file://weston.png \
- file://disable-wayland-scanner-pkg-check.patch \
- file://make-lcms-explicitly-configurable.patch \
- file://make-libwebp-explicitly-configurable.patch \
- file://weston-workaround-to-avoid-segfault-on-hotplug-event.patch \
-"
-S = "${WORKDIR}/git"
-
-RDEPENDS_${PN}_append_rcar-gen2 = " \
- ${@base_conditional('USE_GLES', '1', 'media-ctl', '', d)}"
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
new file mode 100644
index 0000000..33efb49
--- /dev/null
+++ b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend
@@ -0,0 +1,11 @@
+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://weston.service \
+"
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch
new file mode 100644
index 0000000..7bfff9a
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch
@@ -0,0 +1,29 @@
+From 21b85faf93b8028cfa477279354bedab93dcea04 Mon Sep 17 00:00:00 2001
+From: Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com>
+Date: Wed, 21 Dec 2016 11:27:34 +0900
+Subject: [PATCH] ASoC: ak4642: Replace mdelay function to msleep
+
+Replace mdelay to msleep to avoid busy loop on ak4642_lout_event().
+Otherwise, sometimes playback doesn't work correctly when pulseaudio was used.
+
+Signed-off-by: Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com>
+---
+ sound/soc/codecs/ak4642.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
+index 40500cd..0205ae1 100644
+--- a/sound/soc/codecs/ak4642.c
++++ b/sound/soc/codecs/ak4642.c
+@@ -186,7 +186,7 @@ static int ak4642_lout_event(struct snd_soc_dapm_widget *w,
+ break;
+ case SND_SOC_DAPM_POST_PMU: /* after widget power up */
+ /* Power save mode OFF */
+- mdelay(popup_wait);
++ msleep(popup_wait);
+ snd_soc_update_bits(codec, SG_SL2, LOPS, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD: /* before widget power down */
+--
+2.9.2
+
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch
index 13fc68b..e452f0e 100644
--- a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch
@@ -1322,7 +1322,7 @@ index 0000000..d481ecd
+
+/* POWER IC */
+static struct i2c_board_info poweric_i2c[] = {
-+ { I2C_BOARD_INFO("da9063", 0x58), },
++ { I2C_BOARD_INFO("da9063", 0x5a), },
+};
+
+static void porter_restart(char mode, const char *cmd)
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch
new file mode 100644
index 0000000..e7a143f
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch
@@ -0,0 +1,68 @@
+From 8003e1524789537680204d44d5bf7a82561f8ba3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jan-Simon=20M=C3=B6ller?= <jsmoeller@linuxfoundation.org>
+Date: Fri, 4 Nov 2016 20:58:46 +0100
+Subject: [PATCH] Backport fix for CVE-2016-5195
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Jan-Simon Möller <jsmoeller@linuxfoundation.org>
+---
+ include/linux/mm.h | 1 +
+ mm/memory.c | 14 ++++++++++++--
+ 2 files changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/include/linux/mm.h b/include/linux/mm.h
+index 3bf21c3..263b405 100644
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -1702,6 +1702,7 @@ static inline struct page *follow_page(struct vm_area_struct *vma,
+ #define FOLL_HWPOISON 0x100 /* check page is hwpoisoned */
+ #define FOLL_NUMA 0x200 /* force NUMA hinting page fault */
+ #define FOLL_MIGRATION 0x400 /* wait for page to replace migration entry */
++#define FOLL_COW 0x4000 /* internal GUP flag */
+
+ typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,
+ void *data);
+diff --git a/mm/memory.c b/mm/memory.c
+index 48aa275..3a3f316 100644
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -1462,6 +1462,16 @@ int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
+ }
+ EXPORT_SYMBOL_GPL(zap_vma_ptes);
+
++/*
++ * FOLL_FORCE can write to even unwritable pte's, but only
++ * after we've gone through a COW cycle and they are dirty.
++ */
++static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
++{
++ return pte_write(pte) ||
++ ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
++}
++
+ /**
+ * follow_page_mask - look up a page descriptor from a user-virtual address
+ * @vma: vm_area_struct mapping @address
+@@ -1569,7 +1579,7 @@ split_fallthrough:
+ }
+ if ((flags & FOLL_NUMA) && pte_numa(pte))
+ goto no_page;
+- if ((flags & FOLL_WRITE) && !pte_write(pte))
++ if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags))
+ goto unlock;
+
+ page = vm_normal_page(vma, address, pte);
+@@ -1876,7 +1886,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
+ */
+ if ((ret & VM_FAULT_WRITE) &&
+ !(vma->vm_flags & VM_WRITE))
+- foll_flags &= ~FOLL_WRITE;
++ foll_flags |= FOLL_COW;
+
+ cond_resched();
+ }
+--
+2.1.4
+
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb
index 4a3e515..e8e80f2 100644
--- a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb
@@ -17,6 +17,7 @@ SRC_URI = "${RENESAS_BACKPORTS_URL};protocol=git;branch=bsp/v3.10.31-ltsi/rcar-g
file://0001-arm-koelsch-Add-vmalloc-384M-to-bootargs-of-DTS.patch \
file://0001-arm-alt-Add-vmalloc-384M-to-bootargs-of-DTS.patch \
file://0001-arm-gose-Add-vmalloc-384M-to-bootargs-of-DTS.patch \
+ file://0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch \
"
SRC_URI_append_porter = " \
@@ -156,6 +157,9 @@ SRC_URI_append_smack = " \
SRC_URI_append_porter = " file://can-rcar.cfg"
+# Backport fix for CVE-2016-5195
+SRC_URI_append = " file://9999-Backport-fix-for-CVE-2016-5195.patch "
+
S = "${WORKDIR}/git"
KERNEL_DEFCONFIG = "shmobile_defconfig"
diff --git a/meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb b/meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb
new file mode 100644
index 0000000..a87314a
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb
@@ -0,0 +1,58 @@
+require ../../include/rcar-gen2-modules-common.inc
+
+LICENSE = "GPLv2&MIT"
+LIC_FILES_CHKSUM = " \
+ file://GPL-COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263 \
+ file://MIT-COPYING;md5=fea016ce2bdf2ec10080f69e9381d378 \
+"
+
+DEPENDS = "linux-renesas vspm-kernel-module"
+PN = "vsp2-kernel-module"
+PR = "r0"
+
+SRCREV = "c231aff0fba0a2c559968098e5573050a1aa336d"
+SRC_URI = " \
+ git://github.com/renesas-devel/vsp2driver.git;protocol=git;branch=RCAR-GEN2/1.0.0 \
+"
+S = "${WORKDIR}/git"
+
+do_configure[noexec] = "1"
+do_compile() {
+ export VSP2_VSPMDIR=${KERNELSRC}/include
+ export VSP2_VSPMSYMVERS=vspm.symvers
+ cd ${S}/drv
+ make all ARCH=arm
+}
+
+do_install() {
+ # Create destination folder
+ mkdir -p ${D}/lib/modules/${KERNEL_VERSION}/extra/ ${D}/usr/src/kernel/include/
+
+ # Copy kernel module
+ cp -f ${S}/drv/vsp2.ko ${D}/lib/modules/${KERNEL_VERSION}/extra/
+
+ # Copy shared library for reference from other modules
+ cp -f ${S}/drv/Module.symvers ${D}/usr/src/kernel/include/vsp2.symvers
+ cp -f ${S}/drv/Module.symvers ${KERNELSRC}/include/vsp2.symvers
+}
+
+PACKAGES = "\
+ ${PN} \
+ ${PN}-dev \
+"
+
+FILES_${PN} = " \
+ /lib/modules/${KERNEL_VERSION}/extra/vsp2.ko \
+ ${sysconfdir}/* \
+"
+
+FILES_${PN}-dev = " \
+ /usr/src/kernel/include/vsp2.symvers \
+"
+RPROVIDES_${PN} += "vsp2-kernel-module"
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+
+ALLOW_EMPTY_kernel-module-vsp2 = "1"
+
+# Autoload VSP2Driver
+KERNEL_MODULE_AUTOLOAD = "vsp2"
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch
new file mode 100644
index 0000000..d863414
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch
@@ -0,0 +1,85 @@
+diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c
+index c5b69ab..647ac88 100644
+--- a/omx/gstomxvideodec.c
++++ b/omx/gstomxvideodec.c
+@@ -581,7 +581,7 @@ gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
+ #ifdef HAVE_MMNGRBUF
+ static GstBuffer *
+ gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool,
+- gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES])
++ gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gpointer plane_buf[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES])
+ {
+ GstQuery *query;
+ GValue val = { 0, };
+@@ -590,6 +590,7 @@ gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool,
+ GstBuffer *buffer;
+ GArray *dmabuf_array;
+ GArray *stride_array;
++ GArray *planebuf_array;
+ gint n_planes;
+ gint i;
+
+@@ -598,11 +599,13 @@ gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool,
+
+ dmabuf_array = g_array_new (FALSE, FALSE, sizeof (gint));
+ stride_array = g_array_new (FALSE, FALSE, sizeof (gint));
++ planebuf_array = g_array_new (FALSE, FALSE, sizeof (gpointer));
+
+ n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info);
+ for (i = 0; i < n_planes; i++) {
+ g_array_append_val (dmabuf_array, dmabuf_fd[i]);
+ g_array_append_val (stride_array, stride[i]);
++ g_array_append_val (planebuf_array, plane_buf[i]);
+ }
+
+ structure = gst_structure_new ("videosink_buffer_creation_request",
+@@ -610,6 +613,7 @@ gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool,
+ "height", G_TYPE_INT, pool->port->port_def.format.video.nFrameHeight,
+ "stride", G_TYPE_ARRAY, stride_array,
+ "dmabuf", G_TYPE_ARRAY, dmabuf_array,
++ "planebuf", G_TYPE_ARRAY, planebuf_array,
+ "allocator", G_TYPE_POINTER, &val,
+ "format", G_TYPE_STRING,
+ gst_video_format_to_string (pool->video_info.finfo->format),
+@@ -704,6 +708,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+ gint i;
+ gint dmabuf_fd[GST_VIDEO_MAX_PLANES];
+ gint plane_size[GST_VIDEO_MAX_PLANES];
++ gpointer plane_buf[GST_VIDEO_MAX_PLANES];
+ guint phys_addr;
+ OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res =
+ (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf->
+@@ -730,6 +735,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+
+ plane_size[0] = vmeta->stride[0] *
+ GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, 0);
++ plane_buf[0] = omx_buf->omx_buf->pBuffer;
+
+ /* Export dmabuf file descriptors from second and subsequent planes */
+ n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info);
+@@ -737,6 +743,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+ phys_addr = (guint) decode_res->pvPhysImageAddressY + vmeta->offset[i];
+ plane_size[i] = vmeta->stride[i] *
+ GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, i);
++ plane_buf[i] = omx_buf->omx_buf->pBuffer + vmeta->offset[i];
+
+ if (!gst_omx_buffer_pool_export_dmabuf (pool, phys_addr, plane_size[i],
+ page_size, &vdbuf_data->id_export[i], &dmabuf_fd[i])) {
+@@ -747,7 +754,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+
+ if (pool->vsink_buf_req_supported)
+ new_buf = gst_omx_buffer_pool_request_videosink_buffer_creation (pool,
+- dmabuf_fd, vmeta->stride);
++ dmabuf_fd, plane_buf, vmeta->stride);
+ else {
+ GstVideoMeta *new_meta;
+
+@@ -1947,6 +1954,8 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
+ goto caps_failed;
+ }
+
++ /* ...force clearing of reconfiguration flag to prevent subsequent buffers allocation */
++ gst_pad_check_reconfigure(GST_VIDEO_DECODER_SRC_PAD(self));
+ gst_video_codec_state_unref (state);
+
+ GST_VIDEO_DECODER_STREAM_UNLOCK (self);
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch
new file mode 100644
index 0000000..354a7a9
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch
@@ -0,0 +1,65 @@
+From 3e528cda6fb9d0da2be52e18a305096cf6e37528 Mon Sep 17 00:00:00 2001
+From: Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>
+Date: Sat, 18 Jul 2015 15:52:59 +0300
+Subject: [PATCH] Fixed memory corruption and bad access
+
+---
+ omx/gstomxh264dec.c | 4 ++--
+ omx/gstomxvideodec.c | 12 ++++++------
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/omx/gstomxh264dec.c b/omx/gstomxh264dec.c
+index 7799e69..5509cc0 100644
+--- a/omx/gstomxh264dec.c
++++ b/omx/gstomxh264dec.c
+@@ -104,7 +104,7 @@ gst_omx_h264_dec_retrieve_sps_pps (GstOMXH264Dec * self, guint8 * data)
+
+ sps_num = ptr[5] & 0x1f; /* reserved(3bit) + numOfSequenceParameterSets(uint 5bit) */
+
+- sps_size_list = g_malloc (sps_num);
++ sps_size_list = g_malloc (sps_num * sizeof (guint));
+ if (!sps_size_list) {
+ GST_ERROR_OBJECT (self, "failed g_malloc");
+ return NULL;
+@@ -119,7 +119,7 @@ gst_omx_h264_dec_retrieve_sps_pps (GstOMXH264Dec * self, guint8 * data)
+ }
+
+ pps_num = *ptr++; /* numOfPictureParameterSets (unint 8bit) */
+- pps_size_list = g_malloc (pps_num);
++ pps_size_list = g_malloc (pps_num * sizeof (guint));
+ if (!pps_size_list) {
+ GST_ERROR_OBJECT (self, "failed g_malloc");
+ g_free (sps_size_list);
+diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c
+index c5b69ab..4a9706f 100644
+--- a/omx/gstomxvideodec.c
++++ b/omx/gstomxvideodec.c
+@@ -563,10 +563,12 @@ gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
+ gst_omx_buffer_data_quark);
+ #ifdef HAVE_MMNGRBUF
+ if (self->use_dmabuf) {
+- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data;
+- for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
+- if (vdbuf_data->id_export[i] >= 0)
+- mmngr_export_end_in_user (vdbuf_data->id_export[i]);
++ vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data;
++ if (vdbuf_data) {
++ for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
++ if (vdbuf_data->id_export[i] >= 0)
++ mmngr_export_end_in_user (vdbuf_data->id_export[i]);
++ }
+ }
+ #endif
+ g_slice_free (GstOMXVideoDecBufferData, omx_buf->private_data);
+@@ -1684,8 +1686,6 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self)
+
+ static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *release)
+ {
+- gint i;
+-
+ if (!release)
+ return;
+
+--
+2.1.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch
new file mode 100644
index 0000000..23a3dfe
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch
@@ -0,0 +1,2759 @@
+From dcf585068bbb591431a999656359627cccd38716 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 25 Aug 2016 18:37:44 +0300
+Subject: [PATCH 03/10] omxvideoenc: export dmafd buffer through own buffer
+ pool
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ configure.ac | 17 +
+ omx/Makefile.am | 1 +
+ omx/gstomxbufferpool.c | 845 +++++++++++++++++++++++++++++++++++++++++++
+ omx/gstomxbufferpool.h | 106 ++++++
+ omx/gstomxvideodec.c | 924 +-----------------------------------------------
+ omx/gstomxvideoenc.c | 578 ++++++++++++++++++++++--------
+ omx/gstomxvideoenc.h | 11 +
+ 7 files changed, 1420 insertions(+), 1062 deletions(-)
+ create mode 100644 omx/gstomxbufferpool.c
+ create mode 100644 omx/gstomxbufferpool.h
+
+diff --git a/configure.ac b/configure.ac
+index 6aae527..57f5ae9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -245,6 +245,23 @@ AC_CHECK_LIB([mmngrbuf], [mmngr_export_start_in_user],
+ ])
+ fi
+
++dnl check OMXR_Extension_video.h
++AC_CHECK_HEADER([OMXR_Extension_video.h],
++ [AC_DEFINE(HAVE_VIDEOR_EXT, 1, [Define if you have OMXR_Extension_video.h header])],
++ [],
++ [AC_INCLUDES_DEFAULT])
++dnl check OMXR_Extension_vecmn.h
++AC_CHECK_HEADER([OMXR_Extension_vecmn.h],
++ [AC_DEFINE(HAVE_VIDEOENC_EXT, 1, [Define if you have OMXR_Extension_vecmn.h header])],
++ [],
++ [AC_INCLUDES_DEFAULT])
++
++dnl check OMXR_Extension_vdcmn.h
++AC_CHECK_HEADER([OMXR_Extension_vdcmn.h],
++ [AC_DEFINE(HAVE_VIDEODEC_EXT, 1, [Define if you have OMXR_Extension_vdcmn.h header])],
++ [],
++ [AC_INCLUDES_DEFAULT])
++
+ dnl check page alignment option for NV12 planes
+ AC_ARG_ENABLE([nv12-page-alignment],
+ [AS_HELP_STRING([--enable-nv12-page-alignment],
+diff --git a/omx/Makefile.am b/omx/Makefile.am
+index 3ec6173..3619281 100644
+--- a/omx/Makefile.am
++++ b/omx/Makefile.am
+@@ -12,6 +12,7 @@ endif
+
+ libgstomx_la_SOURCES = \
+ gstomx.c \
++ gstomxbufferpool.c \
+ gstomxvideodec.c \
+ gstomxvideoenc.c \
+ gstomxaudioenc.c \
+diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c
+new file mode 100644
+index 0000000..2585a72
+--- /dev/null
++++ b/omx/gstomxbufferpool.c
+@@ -0,0 +1,845 @@
++/*
++ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
++ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
++ * Copyright (C) 2013, Collabora Ltd.
++ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
++ * Copyright (C) 2015, Renesas Electronics Corporation
++ *
++ * This library 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
++ * version 2.1 of the License.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include "gstomxbufferpool.h"
++#include "gstomxvideodec.h"
++#include "gstomxvideoenc.h"
++#include "gst/allocators/gstdmabuf.h"
++#ifdef HAVE_MMNGRBUF
++#include "mmngr_buf_user_public.h"
++#endif
++#ifdef HAVE_VIDEODEC_EXT
++#include "OMXR_Extension_vdcmn.h"
++#endif
++#ifdef HAVE_VIDEOENC_EXT
++#include "OMXR_Extension_vecmn.h"
++#endif
++#include <unistd.h> /* getpagesize() */
++
++/**
++ * GST_ROUND_UP_N:
++ * @num: integrer value to round up
++ * @align: a power of two to round up to
++ *
++ * Rounds an integer value up to the next multiple of @align. @align MUST be a
++ * power of two.
++ */
++#define GST_ROUND_UP_N(num,align) ((((num) + ((align) - 1)) & ~((align) - 1)))
++
++GST_DEBUG_CATEGORY_STATIC (gst_omx_buffer_pool_debug_category);
++#define GST_CAT_DEFAULT gst_omx_buffer_pool_debug_category
++
++typedef struct _GstOMXMemory GstOMXMemory;
++typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator;
++typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass;
++
++struct _GstOMXMemory
++{
++ GstMemory mem;
++
++ GstOMXBuffer *buf;
++};
++
++struct _GstOMXMemoryAllocator
++{
++ GstAllocator parent;
++};
++
++struct _GstOMXMemoryAllocatorClass
++{
++ GstAllocatorClass parent_class;
++};
++
++#define GST_OMX_MEMORY_TYPE "openmax"
++
++static GstMemory *
++gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size,
++ GstAllocationParams * params)
++{
++ g_assert_not_reached ();
++ return NULL;
++}
++
++static void
++gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem)
++{
++ GstOMXMemory *omem = (GstOMXMemory *) mem;
++
++ /* TODO: We need to remember which memories are still used
++ * so we can wait until everything is released before allocating
++ * new memory
++ */
++
++ g_slice_free (GstOMXMemory, omem);
++}
++
++static gpointer
++gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
++{
++ GstOMXMemory *omem = (GstOMXMemory *) mem;
++
++ return omem->buf->omx_buf->pBuffer + omem->mem.offset;
++}
++
++static void
++gst_omx_memory_unmap (GstMemory * mem)
++{
++}
++
++static GstMemory *
++gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size)
++{
++ g_assert_not_reached ();
++ return NULL;
++}
++
++GType gst_omx_memory_allocator_get_type (void);
++G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator,
++ GST_TYPE_ALLOCATOR);
++
++#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type())
++#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR))
++
++static void
++gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass)
++{
++ GstAllocatorClass *allocator_class;
++
++ allocator_class = (GstAllocatorClass *) klass;
++
++ allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy;
++ allocator_class->free = gst_omx_memory_allocator_free;
++}
++
++static void
++gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator)
++{
++ GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
++
++ alloc->mem_type = GST_OMX_MEMORY_TYPE;
++ alloc->mem_map = gst_omx_memory_map;
++ alloc->mem_unmap = gst_omx_memory_unmap;
++ alloc->mem_share = gst_omx_memory_share;
++
++ /* default copy & is_span */
++
++ GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
++}
++
++static GstMemory *
++gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags,
++ GstOMXBuffer * buf)
++{
++ GstOMXMemory *mem;
++ gint align;
++
++ /* FIXME: We don't allow sharing because we need to know
++ * when the memory becomes unused and can only then put
++ * it back to the pool. Which is done in the pool's release
++ * function
++ */
++ flags |= GST_MEMORY_FLAG_NO_SHARE;
++
++ /* GStreamer uses a bitmask for the alignment while
++ * OMX uses the alignment itself. So we have to convert
++ * here */
++ align = buf->port->port_def.nBufferAlignment;
++ if (align > 0)
++ align -= 1;
++ if (((align + 1) & align) != 0) {
++ GST_WARNING ("Invalid alignment that is not a power of two: %u",
++ (guint) buf->port->port_def.nBufferAlignment);
++ align = 0;
++ }
++
++ mem = g_slice_new (GstOMXMemory);
++ /* the shared memory is always readonly */
++ gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL,
++ buf->omx_buf->nAllocLen, align, 0, buf->omx_buf->nAllocLen);
++
++ mem->buf = buf;
++
++ return GST_MEMORY_CAST (mem);
++}
++
++/* Buffer pool for the buffers of an OpenMAX port.
++ *
++ * This pool is only used if we either passed buffers from another
++ * pool to the OMX port or provide the OMX buffers directly to other
++ * elements.
++ *
++ *
++ * A buffer is in the pool if it is currently owned by the port,
++ * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside
++ * the pool after it was taken from the port after it was handled
++ * by the port, i.e. {Empty,Fill}BufferDone.
++ *
++ * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated
++ * by someone else and (temporarily) passed to this pool
++ * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of
++ * the buffer will be overriden, and restored in free_buffer(). Other
++ * buffers are just freed there.
++ *
++ * The pool always has a fixed number of minimum and maximum buffers
++ * and these are allocated while starting the pool and released afterwards.
++ * They correspond 1:1 to the OMX buffers of the port, which are allocated
++ * before the pool is started.
++ *
++ * Acquiring a buffer from this pool happens after the OMX buffer has
++ * been acquired from the port. gst_buffer_pool_acquire_buffer() is
++ * supposed to return the buffer that corresponds to the OMX buffer.
++ *
++ * For buffers provided to upstream, the buffer will be passed to
++ * the component manually when it arrives and then unreffed. If the
++ * buffer is released before reaching the component it will be just put
++ * back into the pool as if EmptyBufferDone has happened. If it was
++ * passed to the component, it will be back into the pool when it was
++ * released and EmptyBufferDone has happened.
++ *
++ * For buffers provided to downstream, the buffer will be returned
++ * back to the component (OMX_FillThisBuffer()) when it is released.
++ */
++
++static GQuark gst_omx_buffer_data_quark = 0;
++
++#define DEBUG_INIT \
++ GST_DEBUG_CATEGORY_INIT (gst_omx_buffer_pool_debug_category, "omxbufferpool", 0, \
++ "debug category for gst-omx buffer pool base class");
++
++G_DEFINE_TYPE_WITH_CODE (GstOMXBufferPool, gst_omx_buffer_pool,
++ GST_TYPE_BUFFER_POOL, DEBUG_INIT);
++
++static void gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool,
++ GstBuffer * buffer);
++
++static gboolean
++gst_omx_buffer_pool_start (GstBufferPool * bpool)
++{
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++
++ /* Only allow to start the pool if we still are attached
++ * to a component and port */
++ GST_OBJECT_LOCK (pool);
++ if (!pool->component || !pool->port) {
++ GST_OBJECT_UNLOCK (pool);
++ return FALSE;
++ }
++ GST_OBJECT_UNLOCK (pool);
++
++ return
++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool);
++}
++
++static gboolean
++gst_omx_buffer_pool_stop (GstBufferPool * bpool)
++{
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++ gint i = 0;
++
++ /* When not using the default GstBufferPool::GstAtomicQueue then
++ * GstBufferPool::free_buffer is not called while stopping the pool
++ * (because the queue is empty) */
++ for (i = 0; i < pool->buffers->len; i++)
++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer
++ (bpool, g_ptr_array_index (pool->buffers, i));
++
++ /* Remove any buffers that are there */
++ g_ptr_array_set_size (pool->buffers, 0);
++
++ if (pool->caps)
++ gst_caps_unref (pool->caps);
++ pool->caps = NULL;
++
++ pool->add_videometa = FALSE;
++
++ return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool);
++}
++
++static const gchar **
++gst_omx_buffer_pool_get_options (GstBufferPool * bpool)
++{
++ static const gchar *raw_video_options[] =
++ { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
++ static const gchar *options[] = { NULL };
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++
++ GST_OBJECT_LOCK (pool);
++ if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
++ && pool->port->port_def.format.video.eCompressionFormat ==
++ OMX_VIDEO_CodingUnused) {
++ GST_OBJECT_UNLOCK (pool);
++ return raw_video_options;
++ }
++ GST_OBJECT_UNLOCK (pool);
++
++ return options;
++}
++
++static gboolean
++gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
++{
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++ GstCaps *caps;
++
++ GST_OBJECT_LOCK (pool);
++
++ if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
++ goto wrong_config;
++
++ if (caps == NULL)
++ goto no_caps;
++
++ if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
++ && pool->port->port_def.format.video.eCompressionFormat ==
++ OMX_VIDEO_CodingUnused) {
++ GstVideoInfo info;
++
++ /* now parse the caps from the config */
++ if (!gst_video_info_from_caps (&info, caps))
++ goto wrong_video_caps;
++
++ /* enable metadata based on config of the pool */
++ pool->add_videometa =
++ gst_buffer_pool_config_has_option (config,
++ GST_BUFFER_POOL_OPTION_VIDEO_META);
++
++ pool->video_info = info;
++ }
++
++ if (pool->caps)
++ gst_caps_unref (pool->caps);
++ pool->caps = gst_caps_ref (caps);
++
++ GST_OBJECT_UNLOCK (pool);
++
++ return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config
++ (bpool, config);
++
++ /* ERRORS */
++wrong_config:
++ {
++ GST_OBJECT_UNLOCK (pool);
++ GST_WARNING_OBJECT (pool, "invalid config");
++ return FALSE;
++ }
++no_caps:
++ {
++ GST_OBJECT_UNLOCK (pool);
++ GST_WARNING_OBJECT (pool, "no caps in config");
++ return FALSE;
++ }
++wrong_video_caps:
++ {
++ GST_OBJECT_UNLOCK (pool);
++ GST_WARNING_OBJECT (pool,
++ "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
++ return FALSE;
++ }
++}
++
++#if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT)
++static gboolean
++gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool,
++ guint phys_addr, gint size, gint * id_export, gint * dmabuf_fd)
++{
++ gint res;
++
++ res =
++ mmngr_export_start_in_user (id_export,
++ (gsize) size, phys_addr, dmabuf_fd);
++ if (res != R_MM_OK) {
++ GST_ERROR_OBJECT (pool,
++ "mmngr_export_start_in_user failed (phys_addr:0x%08x)", phys_addr);
++ return FALSE;
++ }
++ GST_DEBUG_OBJECT (pool,
++ "Export dmabuf:%d id_export:%d (phys_addr:0x%08x)", *dmabuf_fd,
++ *id_export, phys_addr);
++
++ return TRUE;
++}
++
++/* This function will create a GstBuffer contain dmabuf_fd of decoded
++ * video got from Media Component
++ */
++static GstBuffer *
++gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self,
++ GstOMXBuffer * omx_buf, gint * stride, gsize * offset)
++{
++ gint dmabuf_fd[GST_VIDEO_MAX_PLANES];
++ gint plane_size[GST_VIDEO_MAX_PLANES];
++ gint plane_size_ext[GST_VIDEO_MAX_PLANES];
++ gint dmabuf_id[GST_VIDEO_MAX_PLANES];
++ gint page_offset[GST_VIDEO_MAX_PLANES];
++ GstBuffer *new_buf;
++ gint i;
++ gint page_size;
++ guint phys_addr = 0;
++
++ new_buf = gst_buffer_new ();
++ page_size = getpagesize ();
++
++ GST_DEBUG_OBJECT (self, "Creating dmabuf mem pBuffer=%p",
++ omx_buf->omx_buf->pBuffer);
++
++ if (GST_IS_OMX_VIDEO_DEC (self->element)) {
++ OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res =
++ (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf->omx_buf->pOutputPortPrivate;
++ phys_addr = decode_res->pvPhysImageAddressY;
++ } else if (GST_IS_OMX_VIDEO_ENC (self->element)) {
++ /* private data is a physical address of HW buffer */
++ phys_addr = (guint) omx_buf->omx_buf->pInputPortPrivate;
++ }
++
++ if (phys_addr == 0) {
++ GST_ERROR_OBJECT (self, "Invalid phys addr for OMX buffer");
++ return NULL;
++ }
++
++ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->video_info); i++) {
++ guint plane_addr = 0;
++ GstMemory *mem;
++
++ plane_addr = phys_addr + offset[i];
++ /* Calculate offset between physical address and page boundary */
++ page_offset[i] = plane_addr & (page_size - 1);
++
++ plane_size[i] = stride[i] *
++ GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i);
++
++ /* When downstream plugins do mapping from dmabuf fd it requires
++ * mapping from boundary page and size align for page size so
++ * memory for plane must increase to handle for this case */
++ plane_size_ext[i] = GST_ROUND_UP_N (plane_size[i] + page_offset[i],
++ page_size);
++
++ if (!gst_omx_buffer_pool_export_dmabuf (self, plane_addr,
++ plane_size_ext[i], &dmabuf_id[i], &dmabuf_fd[i])) {
++ GST_ERROR_OBJECT (self, "dmabuf exporting failed");
++ return NULL;
++ }
++
++ g_array_append_val (self->id_array, dmabuf_id[i]);
++ /* Set offset's information */
++ mem = gst_dmabuf_allocator_alloc (self->allocator, dmabuf_fd[i],
++ plane_size_ext[i]);
++ mem->offset = page_offset[i];
++ mem->size = plane_size[i];
++ gst_buffer_append_memory (new_buf, mem);
++ }
++
++ g_ptr_array_add (self->buffers, new_buf);
++ gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE,
++ GST_VIDEO_INFO_FORMAT (&self->video_info),
++ GST_VIDEO_INFO_WIDTH (&self->video_info),
++ GST_VIDEO_INFO_HEIGHT (&self->video_info),
++ GST_VIDEO_INFO_N_PLANES (&self->video_info), offset, stride);
++
++ return new_buf;
++}
++#endif
++
++static GstFlowReturn
++gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool,
++ GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
++{
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++ GstBuffer *buf;
++ GstOMXBuffer *omx_buf;
++
++ g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR);
++
++ omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index);
++ g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR);
++
++ if (pool->other_pool) {
++ guint i, n;
++
++ buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index);
++ g_assert (pool->other_pool == buf->pool);
++ gst_object_replace ((GstObject **) & buf->pool, NULL);
++
++ n = gst_buffer_n_memory (buf);
++ for (i = 0; i < n; i++) {
++ GstMemory *mem = gst_buffer_peek_memory (buf, i);
++
++ /* FIXME: We don't allow sharing because we need to know
++ * when the memory becomes unused and can only then put
++ * it back to the pool. Which is done in the pool's release
++ * function
++ */
++ GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE);
++ }
++
++ if (pool->add_videometa) {
++ GstVideoMeta *meta;
++
++ meta = gst_buffer_get_video_meta (buf);
++ if (!meta) {
++ gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
++ GST_VIDEO_INFO_FORMAT (&pool->video_info),
++ GST_VIDEO_INFO_WIDTH (&pool->video_info),
++ GST_VIDEO_INFO_HEIGHT (&pool->video_info));
++ }
++ }
++
++ pool->need_copy = FALSE;
++ } else {
++ GstMemory *mem;
++ const guint nstride = pool->port->port_def.format.video.nStride;
++ const guint nslice = pool->port->port_def.format.video.nSliceHeight;
++ gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
++ gint stride[GST_VIDEO_MAX_PLANES] = { nstride, 0, };
++
++ switch (GST_VIDEO_INFO_FORMAT (&pool->video_info)) {
++ case GST_VIDEO_FORMAT_ABGR:
++ case GST_VIDEO_FORMAT_ARGB:
++ case GST_VIDEO_FORMAT_RGB16:
++ case GST_VIDEO_FORMAT_BGR16:
++ case GST_VIDEO_FORMAT_YUY2:
++ case GST_VIDEO_FORMAT_UYVY:
++ case GST_VIDEO_FORMAT_YVYU:
++ case GST_VIDEO_FORMAT_GRAY8:
++ break;
++ case GST_VIDEO_FORMAT_I420:
++ stride[1] = nstride / 2;
++ offset[1] = offset[0] + stride[0] * nslice;
++ stride[2] = nstride / 2;
++ offset[2] = offset[1] + (stride[1] * nslice / 2);
++ break;
++ case GST_VIDEO_FORMAT_NV12:
++ case GST_VIDEO_FORMAT_NV16:
++ stride[1] = nstride;
++ offset[1] = offset[0] + stride[0] * nslice;
++ break;
++ default:
++ g_assert_not_reached ();
++ break;
++ }
++
++ if (GST_IS_OMX_VIDEO_DEC (pool->element) &&
++ GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf == TRUE &&
++ (omx_buf->omx_buf->pOutputPortPrivate)) {
++#if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT)
++ if (pool->allocator)
++ gst_object_unref (pool->allocator);
++ pool->allocator = gst_dmabuf_allocator_new ();
++ buf = gst_omx_buffer_pool_create_buffer_contain_dmabuf (pool,
++ omx_buf, (gint *) (&stride), (gsize *) (&offset));
++ if (!buf) {
++ GST_ERROR_OBJECT (pool, "Can not create buffer contain dmabuf");
++ return GST_FLOW_ERROR;
++ }
++#else
++ GST_ELEMENT_ERROR (pool->element, STREAM, FAILED, (NULL),
++ ("dmabuf mode is invalid now due to not have MMNGR_BUF or MC does not support getting physical address"));
++ return GST_FLOW_ERROR;
++#endif
++ } else {
++ if (GST_IS_OMX_VIDEO_ENC (pool->element) &&
++ pool->port->port_def.eDir == OMX_DirInput)
++ /* Propose actual area of encoder to upstream */
++ mem = gst_memory_new_wrapped (0, omx_buf->omx_buf->pBuffer,
++ omx_buf->omx_buf->nAllocLen, 0, 0, NULL, NULL);
++ else
++ mem = gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf);
++
++ buf = gst_buffer_new ();
++ gst_buffer_append_memory (buf, mem);
++ g_ptr_array_add (pool->buffers, buf);
++ if (pool->add_videometa) {
++ pool->need_copy = FALSE;
++ } else {
++ GstVideoInfo info;
++ gboolean need_copy = FALSE;
++ gint i;
++
++ gst_video_info_init (&info);
++ gst_video_info_set_format (&info,
++ GST_VIDEO_INFO_FORMAT (&pool->video_info),
++ GST_VIDEO_INFO_WIDTH (&pool->video_info),
++ GST_VIDEO_INFO_HEIGHT (&pool->video_info));
++
++ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&pool->video_info); i++) {
++ if (info.stride[i] != stride[i] || info.offset[i] != offset[i]) {
++ need_copy = TRUE;
++ break;
++ }
++ }
++
++ pool->need_copy = need_copy;
++ }
++
++ if (pool->need_copy || pool->add_videometa) {
++ /* We always add the videometa. It's the job of the user
++ * to copy the buffer if pool->need_copy is TRUE
++ */
++ gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
++ GST_VIDEO_INFO_FORMAT (&pool->video_info),
++ GST_VIDEO_INFO_WIDTH (&pool->video_info),
++ GST_VIDEO_INFO_HEIGHT (&pool->video_info),
++ GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride);
++ }
++ }
++ }
++
++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf),
++ gst_omx_buffer_data_quark, omx_buf, NULL);
++
++ *buffer = buf;
++
++ pool->current_buffer_index++;
++
++ return GST_FLOW_OK;
++}
++
++static void
++gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
++{
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++
++ /* If the buffers belong to another pool, restore them now */
++ GST_OBJECT_LOCK (pool);
++ if (pool->other_pool) {
++ gst_object_replace ((GstObject **) & buffer->pool,
++ (GstObject *) pool->other_pool);
++ }
++ GST_OBJECT_UNLOCK (pool);
++
++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer),
++ gst_omx_buffer_data_quark, NULL, NULL);
++
++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool,
++ buffer);
++}
++
++static GstFlowReturn
++gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
++ GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
++{
++ GstFlowReturn ret;
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++
++ if (pool->port->port_def.eDir == OMX_DirOutput) {
++ GstBuffer *buf;
++
++ g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR);
++
++ buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index);
++ g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
++ *buffer = buf;
++ ret = GST_FLOW_OK;
++
++ /* If it's our own memory we have to set the sizes */
++ if ((!pool->other_pool) &&
++ ((GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf) == FALSE)) {
++ GstMemory *mem = gst_buffer_peek_memory (*buffer, 0);
++
++ g_assert (mem
++ && g_strcmp0 (mem->allocator->mem_type, GST_OMX_MEMORY_TYPE) == 0);
++ mem->size = ((GstOMXMemory *) mem)->buf->omx_buf->nFilledLen;
++ mem->offset = ((GstOMXMemory *) mem)->buf->omx_buf->nOffset;
++ }
++ } else {
++ if (GST_IS_OMX_VIDEO_ENC (pool->element)) {
++ GstBuffer *buf;
++ GstOMXBuffer *omx_buf;
++ gint count = 0;
++
++ /* Search on number of OMXBuffer of port to find available GstBuffer
++ * (emptied OMXBuffer) to propose to upstream. If after 3 times searching,
++ * can not find target GstBuffer, return flow error
++ */
++ do {
++ buf = g_ptr_array_index (pool->buffers, pool->enc_buffer_index);
++ g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
++
++ omx_buf =
++ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buf),
++ gst_omx_buffer_data_quark);
++ pool->enc_buffer_index++;
++ if (pool->enc_buffer_index == pool->port->port_def.nBufferCountActual)
++ pool->enc_buffer_index = 0;
++
++ count += 1;
++ } while (omx_buf->used == TRUE &&
++ count < pool->port->port_def.nBufferCountActual * 3);
++
++ if (count == pool->port->port_def.nBufferCountActual * 3) {
++ ret = GST_FLOW_ERROR;
++ GST_ERROR_OBJECT (pool,
++ "Can not acquire buffer after 3 times searching");
++ } else {
++ *buffer = buf;
++ ret = GST_FLOW_OK;
++ }
++ } else {
++ /* Acquire any buffer that is available to be filled by upstream */
++ ret =
++ GST_BUFFER_POOL_CLASS
++ (gst_omx_buffer_pool_parent_class)->acquire_buffer (bpool, buffer,
++ params);
++ }
++ }
++
++ return ret;
++}
++
++static void
++gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
++{
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
++ OMX_ERRORTYPE err;
++ GstOMXBuffer *omx_buf;
++
++ g_assert (pool->component && pool->port);
++
++ if (!pool->allocating && !pool->deactivated) {
++ omx_buf =
++ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer),
++ gst_omx_buffer_data_quark);
++ if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) {
++ /* Release back to the port, can be filled again */
++ err = gst_omx_port_release_buffer (pool->port, omx_buf);
++ if (err != OMX_ErrorNone) {
++ GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL),
++ ("Failed to relase output buffer to component: %s (0x%08x)",
++ gst_omx_error_to_string (err), err));
++ }
++ } else if (!omx_buf->used) {
++ /* TODO: Implement.
++ *
++ * If not used (i.e. was not passed to the component) this should do
++ * the same as EmptyBufferDone.
++ * If it is used (i.e. was passed to the component) this should do
++ * nothing until EmptyBufferDone.
++ *
++ * EmptyBufferDone should release the buffer to the pool so it can
++ * be allocated again
++ *
++ * Needs something to call back here in EmptyBufferDone, like keeping
++ * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which
++ * would ensure that the buffer is always unused when this is called.
++ */
++ if (GST_OMX_VIDEO_ENC (pool->element)->no_copy == FALSE) {
++ g_assert_not_reached ();
++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer
++ (bpool, buffer);
++ }
++ }
++ }
++}
++
++static void
++gst_omx_buffer_pool_finalize (GObject * object)
++{
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object);
++
++#ifdef HAVE_MMNGRBUF
++ if (GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf) {
++ gint i;
++ gint dmabuf_id;
++
++ for (i = 0; i < pool->id_array->len; i++) {
++ dmabuf_id = g_array_index (pool->id_array, gint, i);
++ if (dmabuf_id >= 0) {
++ GST_DEBUG_OBJECT (pool, "mmngr_export_end_in_user (%d)", dmabuf_id);
++ mmngr_export_end_in_user (dmabuf_id);
++ } else {
++ GST_WARNING_OBJECT (pool, "Invalid dmabuf_id");
++ }
++ }
++ }
++ g_array_free (pool->id_array, TRUE);
++#endif
++
++ if (pool->element)
++ gst_object_unref (pool->element);
++ pool->element = NULL;
++
++ if (pool->buffers)
++ g_ptr_array_unref (pool->buffers);
++ pool->buffers = NULL;
++
++ if (pool->other_pool)
++ gst_object_unref (pool->other_pool);
++ pool->other_pool = NULL;
++
++ if (pool->allocator)
++ gst_object_unref (pool->allocator);
++ pool->allocator = NULL;
++
++ if (pool->caps)
++ gst_caps_unref (pool->caps);
++ pool->caps = NULL;
++
++ G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object);
++}
++
++static void
++gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass)
++{
++ GObjectClass *gobject_class = (GObjectClass *) klass;
++ GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
++
++ gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData");
++
++ gobject_class->finalize = gst_omx_buffer_pool_finalize;
++ gstbufferpool_class->start = gst_omx_buffer_pool_start;
++ gstbufferpool_class->stop = gst_omx_buffer_pool_stop;
++ gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options;
++ gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config;
++ gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer;
++ gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer;
++ gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer;
++ gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer;
++}
++
++static void
++gst_omx_buffer_pool_init (GstOMXBufferPool * pool)
++{
++ pool->buffers = g_ptr_array_new ();
++ pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL);
++#ifdef HAVE_MMNGRBUF
++ pool->id_array = g_array_new (FALSE, FALSE, sizeof (gint));
++#endif
++ pool->enc_buffer_index = 0;
++}
++
++GstBufferPool *
++gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component,
++ GstOMXPort * port)
++{
++ GstOMXBufferPool *pool;
++
++ pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL);
++ pool->element = gst_object_ref (element);
++ pool->component = component;
++ pool->port = port;
++
++ return GST_BUFFER_POOL (pool);
++}
+diff --git a/omx/gstomxbufferpool.h b/omx/gstomxbufferpool.h
+new file mode 100644
+index 0000000..09cab8d
+--- /dev/null
++++ b/omx/gstomxbufferpool.h
+@@ -0,0 +1,106 @@
++/*
++ * Copyright 2014 Advanced Micro Devices, Inc.
++ * Author: Christian König <christian.koenig@amd.com>
++ * Copyright (C) 2015, Renesas Electronics Corporation
++ *
++ * This library 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
++ * version 2.1 of the License.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#ifndef __GST_OMX_BUFFER_POOL_H__
++#define __GST_OMX_BUFFER_POOL_H__
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <gst/gst.h>
++#include <gst/video/gstvideometa.h>
++#include <gst/video/gstvideopool.h>
++
++#include "gstomx.h"
++
++G_BEGIN_DECLS
++
++#define GST_TYPE_OMX_BUFFER_POOL \
++ (gst_omx_buffer_pool_get_type())
++#define GST_OMX_BUFFER_POOL(obj) \
++ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_BUFFER_POOL,GstOMXBufferPool))
++#define GST_IS_OMX_BUFFER_POOL(obj) \
++ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_BUFFER_POOL))
++
++typedef struct _GstOMXBufferPool GstOMXBufferPool;
++typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass;
++
++struct _GstOMXBufferPool
++{
++ GstVideoBufferPool parent;
++
++ GstElement *element;
++
++ GstCaps *caps;
++ gboolean add_videometa;
++ gboolean need_copy;
++ GstVideoInfo video_info;
++
++ /* Owned by element, element has to stop this pool before
++ * it destroys component or port */
++ GstOMXComponent *component;
++ GstOMXPort *port;
++
++ /* For handling OpenMAX allocated memory */
++ GstAllocator *allocator;
++
++ /* Set from outside this pool */
++ /* TRUE if we're currently allocating all our buffers */
++ gboolean allocating;
++ /* TRUE if the pool is not used anymore */
++ gboolean deactivated;
++
++ /* For populating the pool from another one */
++ GstBufferPool *other_pool;
++ GPtrArray *buffers;
++
++ /* Used during acquire for output ports to
++ * specify which buffer has to be retrieved
++ * and during alloc, which buffer has to be
++ * wrapped
++ */
++ gint current_buffer_index;
++
++ /* Used during acquire for input port */
++ gint enc_buffer_index;
++#ifdef HAVE_MMNGRBUF
++ /* Array use to contain dma_id. It is used in export_end dmabuf area */
++ GArray *id_array;
++#endif
++
++ /* TRUE if the downstream buffer pool can handle
++ "videosink_buffer_creation_request" query */
++ gboolean vsink_buf_req_supported;
++};
++
++struct _GstOMXBufferPoolClass
++{
++ GstVideoBufferPoolClass parent_class;
++};
++
++GType gst_omx_buffer_pool_get_type (void);
++
++GstBufferPool *gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, GstOMXPort * port);
++
++G_END_DECLS
++
++#endif /* __GST_OMX_BUFFER_POOL_H__ */
+diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c
+index 837c623..25b6b30 100644
+--- a/omx/gstomxvideodec.c
++++ b/omx/gstomxvideodec.c
+@@ -32,6 +32,7 @@
+ #include <unistd.h> /* getpagesize() */
+
+ #include "gstomxvideodec.h"
++#include "gstomxbufferpool.h"
+
+ #ifdef HAVE_MMNGRBUF
+ #include "gst/allocators/gstdmabuf.h"
+@@ -42,898 +43,6 @@
+ GST_DEBUG_CATEGORY_STATIC (gst_omx_video_dec_debug_category);
+ #define GST_CAT_DEFAULT gst_omx_video_dec_debug_category
+
+-typedef struct _GstOMXMemory GstOMXMemory;
+-typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator;
+-typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass;
+-
+-struct _GstOMXMemory
+-{
+- GstMemory mem;
+-
+- GstOMXBuffer *buf;
+-};
+-
+-struct _GstOMXMemoryAllocator
+-{
+- GstAllocator parent;
+-};
+-
+-struct _GstOMXMemoryAllocatorClass
+-{
+- GstAllocatorClass parent_class;
+-};
+-
+-/* User data and function for release OMX buffer in no-copy mode */
+-struct GstOMXBufferCallback
+-{
+- GstOMXPort * out_port;
+- GstOMXBuffer * buf;
+-};
+-
+-#define GST_OMX_MEMORY_TYPE "openmax"
+-#define DEFAULT_FRAME_PER_SECOND 30
+-
+-static GstMemory *
+-gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size,
+- GstAllocationParams * params)
+-{
+- g_assert_not_reached ();
+- return NULL;
+-}
+-
+-static void
+-gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem)
+-{
+- GstOMXMemory *omem = (GstOMXMemory *) mem;
+-
+- /* TODO: We need to remember which memories are still used
+- * so we can wait until everything is released before allocating
+- * new memory
+- */
+-
+- g_slice_free (GstOMXMemory, omem);
+-}
+-
+-static gpointer
+-gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
+-{
+- GstOMXMemory *omem = (GstOMXMemory *) mem;
+-
+- return omem->buf->omx_buf->pBuffer;
+-}
+-
+-static void
+-gst_omx_memory_unmap (GstMemory * mem)
+-{
+-}
+-
+-static GstMemory *
+-gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size)
+-{
+- g_assert_not_reached ();
+- return NULL;
+-}
+-
+-GType gst_omx_memory_allocator_get_type (void);
+-G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator,
+- GST_TYPE_ALLOCATOR);
+-
+-#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type())
+-#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR))
+-
+-static void
+-gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass)
+-{
+- GstAllocatorClass *allocator_class;
+-
+- allocator_class = (GstAllocatorClass *) klass;
+-
+- allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy;
+- allocator_class->free = gst_omx_memory_allocator_free;
+-}
+-
+-static void
+-gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator)
+-{
+- GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
+-
+- alloc->mem_type = GST_OMX_MEMORY_TYPE;
+- alloc->mem_map = gst_omx_memory_map;
+- alloc->mem_unmap = gst_omx_memory_unmap;
+- alloc->mem_share = gst_omx_memory_share;
+-
+- /* default copy & is_span */
+-
+- GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
+-}
+-
+-#ifndef HAVE_MMNGRBUF
+-static GstMemory *
+-gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags,
+- GstOMXBuffer * buf, gsize offset, gsize size)
+-{
+- GstOMXMemory *mem;
+-
+- /* FIXME: We don't allow sharing because we need to know
+- * when the memory becomes unused and can only then put
+- * it back to the pool. Which is done in the pool's release
+- * function
+- */
+- flags |= GST_MEMORY_FLAG_NO_SHARE;
+-
+- mem = g_slice_new (GstOMXMemory);
+- /* the shared memory is always readonly */
+- gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL,
+- buf->omx_buf->nAllocLen, buf->port->port_def.nBufferAlignment,
+- offset, size);
+-
+- mem->buf = buf;
+-
+- return GST_MEMORY_CAST (mem);
+-}
+-#endif
+-
+-/* Buffer pool for the buffers of an OpenMAX port.
+- *
+- * This pool is only used if we either passed buffers from another
+- * pool to the OMX port or provide the OMX buffers directly to other
+- * elements.
+- *
+- *
+- * A buffer is in the pool if it is currently owned by the port,
+- * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside
+- * the pool after it was taken from the port after it was handled
+- * by the port, i.e. {Empty,Fill}BufferDone.
+- *
+- * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated
+- * by someone else and (temporarily) passed to this pool
+- * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of
+- * the buffer will be overriden, and restored in free_buffer(). Other
+- * buffers are just freed there.
+- *
+- * The pool always has a fixed number of minimum and maximum buffers
+- * and these are allocated while starting the pool and released afterwards.
+- * They correspond 1:1 to the OMX buffers of the port, which are allocated
+- * before the pool is started.
+- *
+- * Acquiring a buffer from this pool happens after the OMX buffer has
+- * been acquired from the port. gst_buffer_pool_acquire_buffer() is
+- * supposed to return the buffer that corresponds to the OMX buffer.
+- *
+- * For buffers provided to upstream, the buffer will be passed to
+- * the component manually when it arrives and then unreffed. If the
+- * buffer is released before reaching the component it will be just put
+- * back into the pool as if EmptyBufferDone has happened. If it was
+- * passed to the component, it will be back into the pool when it was
+- * released and EmptyBufferDone has happened.
+- *
+- * For buffers provided to downstream, the buffer will be returned
+- * back to the component (OMX_FillThisBuffer()) when it is released.
+- */
+-
+-static GQuark gst_omx_buffer_data_quark = 0;
+-
+-#define GST_OMX_BUFFER_POOL(pool) ((GstOMXBufferPool *) pool)
+-typedef struct _GstOMXBufferPool GstOMXBufferPool;
+-typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass;
+-
+-typedef struct _GstOMXVideoDecBufferData GstOMXVideoDecBufferData;
+-
+-struct _GstOMXBufferPool
+-{
+- GstVideoBufferPool parent;
+-
+- GstElement *element;
+-
+- GstCaps *caps;
+- gboolean add_videometa;
+- GstVideoInfo video_info;
+-
+- /* Owned by element, element has to stop this pool before
+- * it destroys component or port */
+- GstOMXComponent *component;
+- GstOMXPort *port;
+-
+- /* For handling OpenMAX allocated memory */
+- GstAllocator *allocator;
+-
+- /* Set from outside this pool */
+- /* TRUE if we're currently allocating all our buffers */
+- gboolean allocating;
+-
+- /* TRUE if the pool is not used anymore */
+- gboolean deactivated;
+-
+- /* For populating the pool from another one */
+- GstBufferPool *other_pool;
+- GPtrArray *buffers;
+-
+- /* Used during acquire for output ports to
+- * specify which buffer has to be retrieved
+- * and during alloc, which buffer has to be
+- * wrapped
+- */
+- gint current_buffer_index;
+-
+- /* TRUE if the downstream buffer pool can handle
+- "videosink_buffer_creation_request" query */
+- gboolean vsink_buf_req_supported;
+-};
+-
+-struct _GstOMXBufferPoolClass
+-{
+- GstVideoBufferPoolClass parent_class;
+-};
+-
+-struct _GstOMXVideoDecBufferData
+-{
+- gboolean already_acquired;
+-
+-#ifdef HAVE_MMNGRBUF
+- gint id_export[GST_VIDEO_MAX_PLANES];
+-#endif
+-};
+-
+-GType gst_omx_buffer_pool_get_type (void);
+-
+-G_DEFINE_TYPE (GstOMXBufferPool, gst_omx_buffer_pool, GST_TYPE_BUFFER_POOL);
+-
+-static void gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool,
+- GstBuffer * buffer);
+-
+-static gboolean
+-gst_omx_buffer_pool_start (GstBufferPool * bpool)
+-{
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+-
+- /* Only allow to start the pool if we still are attached
+- * to a component and port */
+- GST_OBJECT_LOCK (pool);
+- if (!pool->component || !pool->port) {
+- GST_OBJECT_UNLOCK (pool);
+- return FALSE;
+- }
+- GST_OBJECT_UNLOCK (pool);
+-
+- return
+- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool);
+-}
+-
+-static gboolean
+-gst_omx_buffer_pool_stop (GstBufferPool * bpool)
+-{
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+- gint i = 0;
+-
+- /* When not using the default GstBufferPool::GstAtomicQueue then
+- * GstBufferPool::free_buffer is not called while stopping the pool
+- * (because the queue is empty) */
+- for (i = 0; i < pool->buffers->len; i++)
+- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer
+- (bpool, g_ptr_array_index (pool->buffers, i));
+-
+- /* Remove any buffers that are there */
+- g_ptr_array_set_size (pool->buffers, 0);
+-
+- if (pool->caps)
+- gst_caps_unref (pool->caps);
+- pool->caps = NULL;
+-
+- pool->add_videometa = FALSE;
+-
+- return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool);
+-}
+-
+-static const gchar **
+-gst_omx_buffer_pool_get_options (GstBufferPool * bpool)
+-{
+- static const gchar *raw_video_options[] =
+- { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
+- static const gchar *options[] = { NULL };
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+-
+- GST_OBJECT_LOCK (pool);
+- if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
+- && pool->port->port_def.format.video.eCompressionFormat ==
+- OMX_VIDEO_CodingUnused) {
+- GST_OBJECT_UNLOCK (pool);
+- return raw_video_options;
+- }
+- GST_OBJECT_UNLOCK (pool);
+-
+- return options;
+-}
+-
+-static gboolean
+-gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
+-{
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+- GstCaps *caps;
+-
+- GST_OBJECT_LOCK (pool);
+-
+- if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
+- goto wrong_config;
+-
+- if (caps == NULL)
+- goto no_caps;
+-
+- if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
+- && pool->port->port_def.format.video.eCompressionFormat ==
+- OMX_VIDEO_CodingUnused) {
+- GstVideoInfo info;
+-
+- /* now parse the caps from the config */
+- if (!gst_video_info_from_caps (&info, caps))
+- goto wrong_video_caps;
+-
+- /* enable metadata based on config of the pool */
+- pool->add_videometa =
+- gst_buffer_pool_config_has_option (config,
+- GST_BUFFER_POOL_OPTION_VIDEO_META);
+-
+- pool->video_info = info;
+- }
+-
+- if (pool->caps)
+- gst_caps_unref (pool->caps);
+- pool->caps = gst_caps_ref (caps);
+-
+- GST_OBJECT_UNLOCK (pool);
+-
+- return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config
+- (bpool, config);
+-
+- /* ERRORS */
+-wrong_config:
+- {
+- GST_OBJECT_UNLOCK (pool);
+- GST_WARNING_OBJECT (pool, "invalid config");
+- return FALSE;
+- }
+-no_caps:
+- {
+- GST_OBJECT_UNLOCK (pool);
+- GST_WARNING_OBJECT (pool, "no caps in config");
+- return FALSE;
+- }
+-wrong_video_caps:
+- {
+- GST_OBJECT_UNLOCK (pool);
+- GST_WARNING_OBJECT (pool,
+- "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
+- return FALSE;
+- }
+-}
+-
+-static GstFlowReturn
+-gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool,
+- GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
+-{
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+- GstBuffer *buf;
+- GstOMXBuffer *omx_buf;
+- GstOMXVideoDec *self;
+- self = GST_OMX_VIDEO_DEC (pool->element);
+-
+- g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR);
+-
+- omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index);
+- g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR);
+-
+- if (pool->other_pool) {
+- guint i, n;
+-
+- buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index);
+- g_assert (pool->other_pool == buf->pool);
+- gst_object_replace ((GstObject **) & buf->pool, NULL);
+-
+- n = gst_buffer_n_memory (buf);
+- for (i = 0; i < n; i++) {
+- GstMemory *mem = gst_buffer_peek_memory (buf, i);
+-
+- /* FIXME: We don't allow sharing because we need to know
+- * when the memory becomes unused and can only then put
+- * it back to the pool. Which is done in the pool's release
+- * function
+- */
+- GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE);
+- }
+-
+- if (pool->add_videometa) {
+- GstVideoMeta *meta;
+-
+- meta = gst_buffer_get_video_meta (buf);
+- if (!meta) {
+- gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
+- GST_VIDEO_INFO_FORMAT (&pool->video_info),
+- GST_VIDEO_INFO_WIDTH (&pool->video_info),
+- GST_VIDEO_INFO_HEIGHT (&pool->video_info));
+- }
+- }
+- } else {
+- gsize offset[4] = { 0, };
+- gint stride[4] = { 0, };
+- gsize plane_size[4] = { 0, };
+-#ifndef HAVE_MMNGRBUF
+- guint n_planes;
+-#endif
+- gint i;
+- GstOMXVideoDecBufferData *vdbuf_data;
+-
+- switch (pool->video_info.finfo->format) {
+- case GST_VIDEO_FORMAT_I420:
+- offset[0] = 0;
+- stride[0] = pool->port->port_def.format.video.nStride;
+- offset[1] = stride[0] * pool->port->port_def.format.video.nSliceHeight;
+- stride[1] = pool->port->port_def.format.video.nStride / 2;
+- offset[2] =
+- offset[1] +
+- stride[1] * (pool->port->port_def.format.video.nSliceHeight / 2);
+- stride[2] = pool->port->port_def.format.video.nStride / 2;
+- plane_size[0] = pool->port->port_def.format.video.nStride *
+- pool->port->port_def.format.video.nFrameHeight;
+- plane_size[1] = plane_size[2] = plane_size[0] / 4;
+-
+-#ifndef HAVE_MMNGRBUF
+- n_planes = 3;
+-#endif
+- break;
+- case GST_VIDEO_FORMAT_NV12:
+- offset[0] = 0;
+- stride[0] = pool->port->port_def.format.video.nStride;
+- offset[1] = stride[0] * pool->port->port_def.format.video.nSliceHeight;
+- stride[1] = pool->port->port_def.format.video.nStride;
+- plane_size[0] = pool->port->port_def.format.video.nStride *
+- pool->port->port_def.format.video.nFrameHeight;
+- plane_size[1] = plane_size[0] / 2;
+-
+-#ifndef HAVE_MMNGRBUF
+- n_planes = 2;
+-#endif
+- break;
+- default:
+- g_assert_not_reached ();
+- break;
+- }
+-
+- buf = gst_buffer_new ();
+-
+-#ifndef HAVE_MMNGRBUF
+- if (self->use_dmabuf == FALSE)
+- for (i = 0; i < n_planes; i++)
+- gst_buffer_append_memory (buf,
+- gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf,
+- offset[i], plane_size[i]));
+-#endif
+-
+- g_ptr_array_add (pool->buffers, buf);
+-
+- if (pool->add_videometa)
+- gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
+- GST_VIDEO_INFO_FORMAT (&pool->video_info),
+- GST_VIDEO_INFO_WIDTH (&pool->video_info),
+- GST_VIDEO_INFO_HEIGHT (&pool->video_info),
+- GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride);
+-
+- /* Initialize an already_acquired flag */
+- vdbuf_data = g_slice_new (GstOMXVideoDecBufferData);
+- vdbuf_data->already_acquired = FALSE;
+-#ifdef HAVE_MMNGRBUF
+- if (self->use_dmabuf)
+- for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
+- vdbuf_data->id_export[i] = -1;
+-#endif
+-
+- omx_buf->private_data = (void *) vdbuf_data;
+- }
+-
+- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf),
+- gst_omx_buffer_data_quark, omx_buf, NULL);
+-
+- *buffer = buf;
+-
+- pool->current_buffer_index++;
+-
+- return GST_FLOW_OK;
+-}
+-
+-static void
+-gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
+-{
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+- GstOMXBuffer *omx_buf;
+- GstOMXVideoDec *self;
+- self = GST_OMX_VIDEO_DEC (pool->element);
+-#ifdef HAVE_MMNGRBUF
+- GstOMXVideoDecBufferData *vdbuf_data;
+- gint i;
+-#endif
+-
+- /* If the buffers belong to another pool, restore them now */
+- GST_OBJECT_LOCK (pool);
+- if (pool->other_pool) {
+- gst_object_replace ((GstObject **) & buffer->pool,
+- (GstObject *) pool->other_pool);
+- }
+- GST_OBJECT_UNLOCK (pool);
+-
+- omx_buf =
+- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer),
+- gst_omx_buffer_data_quark);
+-#ifdef HAVE_MMNGRBUF
+- if (self->use_dmabuf) {
+- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data;
+- if (vdbuf_data) {
+- for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
+- if (vdbuf_data->id_export[i] >= 0)
+- mmngr_export_end_in_user (vdbuf_data->id_export[i]);
+- }
+- }
+-#endif
+- g_slice_free (GstOMXVideoDecBufferData, omx_buf->private_data);
+-
+- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer),
+- gst_omx_buffer_data_quark, NULL, NULL);
+-
+- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool,
+- buffer);
+-}
+-
+-#ifdef HAVE_MMNGRBUF
+-static GstBuffer *
+-gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool,
+- gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gpointer plane_buf[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES])
+-{
+- GstQuery *query;
+- GValue val = { 0, };
+- GstStructure *structure;
+- const GValue *value;
+- GstBuffer *buffer;
+- GArray *dmabuf_array;
+- GArray *stride_array;
+- GArray *planebuf_array;
+- gint n_planes;
+- gint i;
+-
+- g_value_init (&val, G_TYPE_POINTER);
+- g_value_set_pointer (&val, (gpointer) pool->allocator);
+-
+- dmabuf_array = g_array_new (FALSE, FALSE, sizeof (gint));
+- stride_array = g_array_new (FALSE, FALSE, sizeof (gint));
+- planebuf_array = g_array_new (FALSE, FALSE, sizeof (gpointer));
+-
+- n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info);
+- for (i = 0; i < n_planes; i++) {
+- g_array_append_val (dmabuf_array, dmabuf_fd[i]);
+- g_array_append_val (stride_array, stride[i]);
+- g_array_append_val (planebuf_array, plane_buf[i]);
+- }
+-
+- structure = gst_structure_new ("videosink_buffer_creation_request",
+- "width", G_TYPE_INT, pool->port->port_def.format.video.nFrameWidth,
+- "height", G_TYPE_INT, pool->port->port_def.format.video.nFrameHeight,
+- "stride", G_TYPE_ARRAY, stride_array,
+- "dmabuf", G_TYPE_ARRAY, dmabuf_array,
+- "planebuf", G_TYPE_ARRAY, planebuf_array,
+- "allocator", G_TYPE_POINTER, &val,
+- "format", G_TYPE_STRING,
+- gst_video_format_to_string (pool->video_info.finfo->format),
+- "n_planes", G_TYPE_INT, n_planes, NULL);
+-
+- query = gst_query_new_custom (GST_QUERY_CUSTOM, structure);
+-
+- GST_DEBUG_OBJECT (pool, "send a videosink_buffer_creation_request query");
+-
+- if (!gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (pool->element), query)) {
+- GST_ERROR_OBJECT (pool, "videosink_buffer_creation_request query failed");
+- return NULL;
+- }
+-
+- value = gst_structure_get_value (structure, "buffer");
+- buffer = gst_value_get_buffer (value);
+- if (buffer == NULL) {
+- GST_ERROR_OBJECT (pool,
+- "could not get a buffer from videosink_buffer_creation query");
+- return NULL;
+- }
+-
+- gst_query_unref (query);
+-
+- g_array_free (dmabuf_array, TRUE);
+- g_array_free (stride_array, TRUE);
+-
+- return buffer;
+-}
+-#endif
+-
+-#ifdef HAVE_MMNGRBUF
+-static gboolean
+-gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool,
+- guint phys_addr, gint size, gint boundary, gint * id_export,
+- gint * dmabuf_fd)
+-{
+- gint res;
+-
+- res =
+- mmngr_export_start_in_user (id_export,
+- (size + boundary - 1) & ~(boundary - 1), (unsigned long) phys_addr,
+- dmabuf_fd);
+- if (res != R_MM_OK) {
+- GST_ERROR_OBJECT (pool,
+- "mmngr_export_start_in_user failed (phys_addr:0x%08x)", phys_addr);
+- return FALSE;
+- }
+- GST_DEBUG_OBJECT (pool,
+- "Export dmabuf:%d id_export:%d (phys_addr:0x%08x)", *dmabuf_fd,
+- *id_export, phys_addr);
+-
+- return TRUE;
+-}
+-#endif
+-
+-static GstFlowReturn
+-gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+- GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
+-{
+- GstFlowReturn ret;
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+- GstOMXVideoDec *self;
+- self = GST_OMX_VIDEO_DEC (pool->element);
+-
+- if (pool->port->port_def.eDir == OMX_DirOutput) {
+- GstBuffer *buf;
+- GstOMXBuffer *omx_buf;
+- GstOMXVideoDecBufferData *vdbuf_data;
+-#ifdef HAVE_MMNGRBUF
+- guint n_mem;
+-#endif
+-
+- g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR);
+-
+- buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index);
+- g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
+-
+- omx_buf =
+- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buf),
+- gst_omx_buffer_data_quark);
+-
+- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data;
+-#ifdef HAVE_MMNGRBUF
+- if (self->use_dmabuf)
+- {
+- n_mem = gst_buffer_n_memory (buf);
+- if (n_mem == 0) {
+- GstBuffer *new_buf;
+- GstVideoMeta *vmeta;
+- gint n_planes;
+- gint i;
+- gint dmabuf_fd[GST_VIDEO_MAX_PLANES];
+- gint plane_size[GST_VIDEO_MAX_PLANES];
+- gpointer plane_buf[GST_VIDEO_MAX_PLANES];
+- guint phys_addr;
+- OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res =
+- (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf->
+- omx_buf->pOutputPortPrivate;
+- gint page_size;
+-
+- GST_DEBUG_OBJECT (pool, "Create dmabuf mem pBuffer=%p",
+- omx_buf->omx_buf->pBuffer);
+-
+- vmeta = gst_buffer_get_video_meta (buf);
+-
+- phys_addr = (guint) decode_res->pvPhysImageAddressY;
+- page_size = getpagesize ();
+-
+- /* Export a dmabuf file descriptor from the head of Y plane to
+- * the end of the buffer so that mapping the whole plane as
+- * contiguous memory is available. */
+- if (!gst_omx_buffer_pool_export_dmabuf (pool, phys_addr,
+- pool->port->port_def.nBufferSize, page_size,
+- &vdbuf_data->id_export[0], &dmabuf_fd[0])) {
+- GST_ERROR_OBJECT (pool, "dmabuf exporting failed");
+- return GST_FLOW_ERROR;
+- }
+-
+- plane_size[0] = vmeta->stride[0] *
+- GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, 0);
+- plane_buf[0] = omx_buf->omx_buf->pBuffer;
+-
+- /* Export dmabuf file descriptors from second and subsequent planes */
+- n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info);
+- for (i = 1; i < n_planes; i++) {
+- phys_addr = (guint) decode_res->pvPhysImageAddressY + vmeta->offset[i];
+- plane_size[i] = vmeta->stride[i] *
+- GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, i);
+- plane_buf[i] = omx_buf->omx_buf->pBuffer + vmeta->offset[i];
+-
+- if (!gst_omx_buffer_pool_export_dmabuf (pool, phys_addr, plane_size[i],
+- page_size, &vdbuf_data->id_export[i], &dmabuf_fd[i])) {
+- GST_ERROR_OBJECT (pool, "dmabuf exporting failed");
+- return GST_FLOW_ERROR;
+- }
+- }
+-
+- if (pool->vsink_buf_req_supported)
+- new_buf = gst_omx_buffer_pool_request_videosink_buffer_creation (pool,
+- dmabuf_fd, plane_buf, vmeta->stride);
+- else {
+- GstVideoMeta *new_meta;
+-
+- new_buf = gst_buffer_new ();
+- for (i = 0; i < n_planes; i++)
+- gst_buffer_append_memory (new_buf,
+- gst_dmabuf_allocator_alloc (pool->allocator, dmabuf_fd[i],
+- plane_size[i]));
+-
+- gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE,
+- GST_VIDEO_INFO_FORMAT (&pool->video_info),
+- GST_VIDEO_INFO_WIDTH (&pool->video_info),
+- GST_VIDEO_INFO_HEIGHT (&pool->video_info),
+- GST_VIDEO_INFO_N_PLANES (&pool->video_info), vmeta->offset,
+- vmeta->stride);
+-
+- new_meta = gst_buffer_get_video_meta (new_buf);
+- /* To avoid detaching meta data when a buffer returns
+- to the buffer pool */
+- GST_META_FLAG_SET (new_meta, GST_META_FLAG_POOLED);
+- }
+-
+- g_ptr_array_remove_index (pool->buffers, pool->current_buffer_index);
+-
+- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf),
+- gst_omx_buffer_data_quark, NULL, NULL);
+-
+- gst_buffer_unref (buf);
+-
+- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (new_buf),
+- gst_omx_buffer_data_quark, omx_buf, NULL);
+-
+- g_ptr_array_add (pool->buffers, new_buf);
+-
+- *buffer = new_buf;
+- } else
+- *buffer = buf;
+- } else
+-#endif
+- *buffer = buf;
+-
+- vdbuf_data->already_acquired = TRUE;
+-
+- ret = GST_FLOW_OK;
+- } else {
+- /* Acquire any buffer that is available to be filled by upstream */
+- ret =
+- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer
+- (bpool, buffer, params);
+- }
+-
+- return ret;
+-}
+-
+-static void
+-gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
+-{
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+- OMX_ERRORTYPE err;
+- GstOMXBuffer *omx_buf;
+-
+- g_assert (pool->component && pool->port);
+-
+- if (pool->allocating && !pool->deactivated) {
+- GstOMXVideoDecBufferData *vdbuf_data;
+-
+- omx_buf =
+- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer),
+- gst_omx_buffer_data_quark);
+-
+- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data;
+-
+- if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used &&
+- vdbuf_data->already_acquired) {
+- /* Release back to the port, can be filled again */
+- err = gst_omx_port_release_buffer (pool->port, omx_buf);
+- if (err != OMX_ErrorNone) {
+- GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL),
+- ("Failed to relase output buffer to component: %s (0x%08x)",
+- gst_omx_error_to_string (err), err));
+- }
+- vdbuf_data->already_acquired = FALSE;
+- } else if (pool->port->port_def.eDir == OMX_DirInput && !omx_buf->used) {
+- /* TODO: Implement.
+- *
+- * If not used (i.e. was not passed to the component) this should do
+- * the same as EmptyBufferDone.
+- * If it is used (i.e. was passed to the component) this should do
+- * nothing until EmptyBufferDone.
+- *
+- * EmptyBufferDone should release the buffer to the pool so it can
+- * be allocated again
+- *
+- * Needs something to call back here in EmptyBufferDone, like keeping
+- * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which
+- * would ensure that the buffer is always unused when this is called.
+- */
+- g_assert_not_reached ();
+- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer
+- (bpool, buffer);
+- }
+- }
+-}
+-
+-static void
+-gst_omx_buffer_pool_finalize (GObject * object)
+-{
+- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object);
+-
+- if (pool->element)
+- gst_object_unref (pool->element);
+- pool->element = NULL;
+-
+- if (pool->buffers)
+- g_ptr_array_unref (pool->buffers);
+- pool->buffers = NULL;
+-
+- if (pool->other_pool)
+- gst_object_unref (pool->other_pool);
+- pool->other_pool = NULL;
+-
+- if (pool->allocator)
+- gst_object_unref (pool->allocator);
+- pool->allocator = NULL;
+-
+- if (pool->caps)
+- gst_caps_unref (pool->caps);
+- pool->caps = NULL;
+-
+- G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object);
+-}
+-
+-static void
+-gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass)
+-{
+- GObjectClass *gobject_class = (GObjectClass *) klass;
+- GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
+-
+- gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData");
+-
+- gobject_class->finalize = gst_omx_buffer_pool_finalize;
+- gstbufferpool_class->start = gst_omx_buffer_pool_start;
+- gstbufferpool_class->stop = gst_omx_buffer_pool_stop;
+- gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options;
+- gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config;
+- gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer;
+- gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer;
+- gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer;
+- gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer;
+-}
+-
+-static void
+-gst_omx_buffer_pool_init (GstOMXBufferPool * pool)
+-{
+- pool->buffers = g_ptr_array_new ();
+-#ifdef HAVE_MMNGRBUF
+- pool->allocator = gst_dmabuf_allocator_new ();
+-#else
+- pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL);
+-#endif
+-}
+-
+-static GstBufferPool *
+-gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component,
+- GstOMXPort * port)
+-{
+- GstOMXBufferPool *pool;
+-
+- pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL);
+- pool->element = gst_object_ref (element);
+- pool->component = component;
+- pool->port = port;
+- pool->vsink_buf_req_supported = FALSE;
+-
+- return GST_BUFFER_POOL (pool);
+-}
+-
+ typedef struct _BufferIdentification BufferIdentification;
+ struct _BufferIdentification
+ {
+@@ -979,8 +88,9 @@ static void gst_omx_video_dec_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+ static void gst_omx_video_dec_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
++/*
+ static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *);
+-
++*/
+ enum
+ {
+ PROP_0,
+@@ -999,6 +109,9 @@ enum
+ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstOMXVideoDec, gst_omx_video_dec,
+ GST_TYPE_VIDEO_DECODER, DEBUG_INIT);
+
++/* Default fps for input files that does not support fps */
++#define DEFAULT_FRAME_PER_SECOND 30
++
+ static gsize
+ gst_omx_video_dec_copy_frame (GstOMXVideoDec * self, GstBuffer * inbuf,
+ guint offset, GstOMXBuffer * outbuf)
+@@ -1691,6 +804,7 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self)
+ return err;
+ }
+
++/*
+ static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *release)
+ {
+ if (!release)
+@@ -1702,6 +816,7 @@ static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *release)
+
+ g_free (release);
+ }
++*/
+
+ static GstBuffer *
+ gst_omx_video_dec_create_buffer_from_omx_output (GstOMXVideoDec * self,
+@@ -1762,7 +877,7 @@ gst_omx_video_dec_create_buffer_from_omx_output (GstOMXVideoDec * self,
+ GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, sliceheigh);
+ used_size = stride[i] *
+ GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, height);
+-
++#if 0
+ if (i == 0) {
+ struct GstOMXBufferCallback *release;
+ release = g_malloc (sizeof(struct GstOMXBufferCallback));
+@@ -1774,6 +889,7 @@ gst_omx_video_dec_create_buffer_from_omx_output (GstOMXVideoDec * self,
+ plane_size, 0, used_size, release, GstOMXBufCallbackfunc);
+ }
+ else
++#endif
+ /* Only release OMX buffer one time. Do not add callback
+ * function to other planes
+ * (These planes are from same OMX buffer) */
+@@ -2042,15 +1158,7 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
+
+ n = port->buffers->len;
+ for (i = 0; i < n; i++) {
+- GstBuffer *outbuf;
+- GstOMXBuffer *tmp;
+-
+- outbuf =
+- g_ptr_array_index (GST_OMX_BUFFER_POOL (self->
+- out_port_pool)->buffers, i);
+- tmp =
+- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (outbuf),
+- gst_omx_buffer_data_quark);
++ GstOMXBuffer *tmp = g_ptr_array_index (port->buffers, i);
+
+ if (tmp == buf)
+ break;
+@@ -2084,15 +1192,7 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
+
+ n = port->buffers->len;
+ for (i = 0; i < n; i++) {
+- GstBuffer *outbuf;
+- GstOMXBuffer *tmp;
+-
+- outbuf =
+- g_ptr_array_index (GST_OMX_BUFFER_POOL (self->
+- out_port_pool)->buffers, i);
+- tmp =
+- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (outbuf),
+- gst_omx_buffer_data_quark);
++ GstOMXBuffer *tmp = g_ptr_array_index (port->buffers, i);
+
+ if (tmp == buf)
+ break;
+diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c
+index cec44cb..b36c46e 100644
+--- a/omx/gstomxvideoenc.c
++++ b/omx/gstomxvideoenc.c
+@@ -28,6 +28,11 @@
+ #include <string.h>
+
+ #include "gstomxvideoenc.h"
++#include "gstomxbufferpool.h"
++#ifdef HAVE_MMNGRBUF
++#include "mmngr_buf_user_public.h"
++#endif
++#include "gst/allocators/gstdmabuf.h"
+
+ GST_DEBUG_CATEGORY_STATIC (gst_omx_video_enc_debug_category);
+ #define GST_CAT_DEFAULT gst_omx_video_enc_debug_category
+@@ -68,6 +73,13 @@ buffer_identification_free (BufferIdentification * id)
+ g_slice_free (BufferIdentification, id);
+ }
+
++/* Used in dmabuf mode */
++struct _GstOMXVideoEncPrivate
++{
++ /* Array contain id when using mmngrbuf to import fd */
++ GArray *id_array;
++};
++
+ /* prototypes */
+ static void gst_omx_video_enc_finalize (GObject * object);
+ static void gst_omx_video_enc_set_property (GObject * object, guint prop_id,
+@@ -109,7 +121,8 @@ enum
+ PROP_TARGET_BITRATE,
+ PROP_QUANT_I_FRAMES,
+ PROP_QUANT_P_FRAMES,
+- PROP_QUANT_B_FRAMES
++ PROP_QUANT_B_FRAMES,
++ PROP_NO_COPY
+ };
+
+ /* FIXME: Better defaults */
+@@ -175,6 +188,11 @@ gst_omx_video_enc_class_init (GstOMXVideoEncClass * klass)
+ 0, G_MAXUINT, GST_OMX_VIDEO_ENC_QUANT_B_FRAMES_DEFAULT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ GST_PARAM_MUTABLE_READY));
++ g_object_class_install_property (gobject_class, PROP_NO_COPY,
++ g_param_spec_boolean ("no-copy", "Propose buffer to upstream",
++ "Whether or not to use no copy method",
++ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
++ GST_PARAM_MUTABLE_READY));
+
+ element_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_omx_video_enc_change_state);
+@@ -210,7 +228,11 @@ gst_omx_video_enc_init (GstOMXVideoEnc * self)
+ self->quant_i_frames = GST_OMX_VIDEO_ENC_QUANT_I_FRAMES_DEFAULT;
+ self->quant_p_frames = GST_OMX_VIDEO_ENC_QUANT_P_FRAMES_DEFAULT;
+ self->quant_b_frames = GST_OMX_VIDEO_ENC_QUANT_B_FRAMES_DEFAULT;
+-
++ self->no_copy = TRUE;
++ self->priv =
++ G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_OMX_VIDEO_ENC,
++ GstOMXVideoEncPrivate);
++ self->priv->id_array = g_array_new (FALSE, FALSE, sizeof (gint));
+ g_mutex_init (&self->drain_lock);
+ g_cond_init (&self->drain_cond);
+ }
+@@ -413,6 +435,16 @@ gst_omx_video_enc_finalize (GObject * object)
+ g_mutex_clear (&self->drain_lock);
+ g_cond_clear (&self->drain_cond);
+
++#ifdef HAVE_MMNGRBUF
++ if (self->priv->id_array->len > 0) {
++ gint i;
++ for (i = 0; i < self->priv->id_array->len; i++)
++ mmngr_import_end_in_user (g_array_index (self->priv->id_array, gint,
++ i));
++ }
++#endif;
++ g_array_free (self->priv->id_array, TRUE);
++
+ G_OBJECT_CLASS (gst_omx_video_enc_parent_class)->finalize (object);
+ }
+
+@@ -453,6 +485,9 @@ gst_omx_video_enc_set_property (GObject * object, guint prop_id,
+ case PROP_QUANT_B_FRAMES:
+ self->quant_b_frames = g_value_get_uint (value);
+ break;
++ case PROP_NO_COPY:
++ self->no_copy = g_value_get_boolean (value);
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -481,6 +516,9 @@ gst_omx_video_enc_get_property (GObject * object, guint prop_id, GValue * value,
+ case PROP_QUANT_B_FRAMES:
+ g_value_set_uint (value, self->quant_b_frames);
+ break;
++ case PROP_NO_COPY:
++ g_value_set_boolean (value, self->no_copy);
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -1125,163 +1163,168 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder,
+ GST_DEBUG_OBJECT (self, "Setting new format %s",
+ gst_video_format_to_string (info->finfo->format));
+
+- gst_omx_port_get_port_definition (self->enc_in_port, &port_def);
+-
+- needs_disable =
+- gst_omx_component_get_state (self->enc,
+- GST_CLOCK_TIME_NONE) != OMX_StateLoaded;
+- /* If the component is not in Loaded state and a real format change happens
+- * we have to disable the port and re-allocate all buffers. If no real
+- * format change happened we can just exit here.
++ /* If there is inport pool, it means that OMXBuffer has already allocated on
++ * propose_allocation. Do not allocate OMXBuffer on set_format
+ */
+- if (needs_disable) {
+- GST_DEBUG_OBJECT (self, "Need to disable and drain encoder");
+- gst_omx_video_enc_drain (self, FALSE);
+- gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE);
+-
+- /* Wait until the srcpad loop is finished,
+- * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks
+- * caused by using this lock from inside the loop function */
+- GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
+- gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder));
+- GST_VIDEO_ENCODER_STREAM_LOCK (self);
+-
+- if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_buffers_released (self->enc_in_port,
+- 5 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_buffers_released (self->enc_out_port,
+- 1 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_enabled (self->enc_in_port,
+- 1 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_enabled (self->enc_out_port,
+- 1 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+-
+- GST_DEBUG_OBJECT (self, "Encoder drained and disabled");
+- }
++ if (!self->in_port_pool) {
++ gst_omx_port_get_port_definition (self->enc_in_port, &port_def);
++
++ needs_disable =
++ gst_omx_component_get_state (self->enc,
++ GST_CLOCK_TIME_NONE) != OMX_StateLoaded;
++ /* If the component is not in Loaded state and a real format change happens
++ * we have to disable the port and re-allocate all buffers. If no real
++ * format change happened we can just exit here.
++ */
++ if (needs_disable) {
++ GST_DEBUG_OBJECT (self, "Need to disable and drain encoder");
++ gst_omx_video_enc_drain (self, FALSE);
++ gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE);
++
++ /* Wait until the srcpad loop is finished,
++ * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks
++ * caused by using this lock from inside the loop function */
++ GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
++ gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder));
++ GST_VIDEO_ENCODER_STREAM_LOCK (self);
+
+- negotiation_map = gst_omx_video_enc_get_supported_colorformats (self);
+- if (!negotiation_map) {
+- /* Fallback */
+- switch (info->finfo->format) {
+- case GST_VIDEO_FORMAT_I420:
+- port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+- break;
+- case GST_VIDEO_FORMAT_NV16:
+- case GST_VIDEO_FORMAT_NV12:
+- port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+- break;
+- default:
+- GST_ERROR_OBJECT (self, "Unsupported format %s",
+- gst_video_format_to_string (info->finfo->format));
++ if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone)
+ return FALSE;
+- break;
++ if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_buffers_released (self->enc_in_port,
++ 5 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_buffers_released (self->enc_out_port,
++ 1 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_enabled (self->enc_in_port,
++ 1 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_enabled (self->enc_out_port,
++ 1 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++
++ GST_DEBUG_OBJECT (self, "Encoder drained and disabled");
+ }
+- } else {
+- for (l = negotiation_map; l; l = l->next) {
+- VideoNegotiationMap *m = l->data;
+
+- if (m->format == info->finfo->format) {
+- port_def.format.video.eColorFormat = m->type;
+- break;
++ negotiation_map = gst_omx_video_enc_get_supported_colorformats (self);
++ if (!negotiation_map) {
++ /* Fallback */
++ switch (info->finfo->format) {
++ case GST_VIDEO_FORMAT_I420:
++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
++ break;
++ case GST_VIDEO_FORMAT_NV16:
++ case GST_VIDEO_FORMAT_NV12:
++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
++ break;
++ default:
++ GST_ERROR_OBJECT (self, "Unsupported format %s",
++ gst_video_format_to_string (info->finfo->format));
++ return FALSE;
++ break;
++ }
++ } else {
++ for (l = negotiation_map; l; l = l->next) {
++ VideoNegotiationMap *m = l->data;
++
++ if (m->format == info->finfo->format) {
++ port_def.format.video.eColorFormat = m->type;
++ break;
++ }
+ }
++ g_list_free_full (negotiation_map,
++ (GDestroyNotify) video_negotiation_map_free);
+ }
+- g_list_free_full (negotiation_map,
+- (GDestroyNotify) video_negotiation_map_free);
+- }
+
+- port_def.format.video.nFrameWidth = info->width;
+- if (port_def.nBufferAlignment)
+- port_def.format.video.nStride =
+- (info->width + port_def.nBufferAlignment - 1) &
+- (~(port_def.nBufferAlignment - 1));
+- else
+- {
+- if (klass->cdata.hacks & GST_OMX_HACK_RENESAS_ENCMC_STRIDE_ALIGN)
++ port_def.format.video.nFrameWidth = info->width;
++ if (port_def.nBufferAlignment)
++ port_def.format.video.nStride =
++ (info->width + port_def.nBufferAlignment - 1) &
++ (~(port_def.nBufferAlignment - 1));
++ else
+ {
+- switch (port_def.format.video.eColorFormat) {
+- case OMX_COLOR_FormatYUV420Planar: {
+- /*Renesas encode MC only support following strides*/
+- if (info->width <= 256)
+- port_def.format.video.nStride = 256;
+- else if ((info->width > 256) && (info->width <= 512))
+- port_def.format.video.nStride = 512;
+- else if ((info->width > 512) && (info->width <= 1024))
+- port_def.format.video.nStride = 1024;
+- else
+- port_def.format.video.nStride = 2048;
++ if (klass->cdata.hacks & GST_OMX_HACK_RENESAS_ENCMC_STRIDE_ALIGN)
++ {
++ switch (port_def.format.video.eColorFormat) {
++ case OMX_COLOR_FormatYUV420Planar: {
++ /*Renesas encode MC only support following strides*/
++ if (info->width <= 256)
++ port_def.format.video.nStride = 256;
++ else if ((info->width > 256) && (info->width <= 512))
++ port_def.format.video.nStride = 512;
++ else if ((info->width > 512) && (info->width <= 1024))
++ port_def.format.video.nStride = 1024;
++ else
++ port_def.format.video.nStride = 2048;
++ break;
++ }
++ case OMX_COLOR_FormatYUV420SemiPlanar:
++ port_def.format.video.nStride = ((info->width + 127) & ~ 127); /* Align 128 */
+ break;
+- }
+- case OMX_COLOR_FormatYUV420SemiPlanar:
+- port_def.format.video.nStride = ((info->width + 127) & ~ 127); /* Align 128 */
+- break;
+- default:
++ default:
++ port_def.format.video.nStride = GST_ROUND_UP_4 (info->width); /* Safe (?) default */
++ break;
++ }
++ } else {
+ port_def.format.video.nStride = GST_ROUND_UP_4 (info->width); /* Safe (?) default */
+- break;
+ }
+- } else {
+- port_def.format.video.nStride = GST_ROUND_UP_4 (info->width); /* Safe (?) default */
+ }
+- }
+
+- port_def.format.video.nFrameHeight = info->height;
+- port_def.format.video.nSliceHeight = info->height;
++ port_def.format.video.nFrameHeight = info->height;
++ port_def.format.video.nSliceHeight = info->height;
+
+- switch (port_def.format.video.eColorFormat) {
+- case OMX_COLOR_FormatYUV420Planar:
+- case OMX_COLOR_FormatYUV420PackedPlanar:
+- port_def.nBufferSize =
+- (port_def.format.video.nStride * port_def.format.video.nFrameHeight) +
+- 2 * ((port_def.format.video.nStride / 2) *
+- ((port_def.format.video.nFrameHeight + 1) / 2));
+- break;
+-
+- case OMX_COLOR_FormatYUV420SemiPlanar:
+- port_def.nBufferSize =
+- (port_def.format.video.nStride * port_def.format.video.nFrameHeight) +
+- (port_def.format.video.nStride *
+- ((port_def.format.video.nFrameHeight + 1) / 2));
+- break;
++ switch (port_def.format.video.eColorFormat) {
++ case OMX_COLOR_FormatYUV420Planar:
++ case OMX_COLOR_FormatYUV420PackedPlanar:
++ port_def.nBufferSize =
++ (port_def.format.video.nStride * port_def.format.video.nFrameHeight) +
++ 2 * ((port_def.format.video.nStride / 2) *
++ ((port_def.format.video.nFrameHeight + 1) / 2));
++ break;
+
+- default:
+- g_assert_not_reached ();
+- }
++ case OMX_COLOR_FormatYUV420SemiPlanar:
++ port_def.nBufferSize =
++ (port_def.format.video.nStride * port_def.format.video.nFrameHeight) +
++ (port_def.format.video.nStride *
++ ((port_def.format.video.nFrameHeight + 1) / 2));
++ break;
+
+- if (info->fps_n == 0) {
+- port_def.format.video.xFramerate = 0;
+- } else {
+- if (!(klass->cdata.hacks & GST_OMX_HACK_VIDEO_FRAMERATE_INTEGER))
+- port_def.format.video.xFramerate = (info->fps_n << 16) / (info->fps_d);
+- else
+- port_def.format.video.xFramerate = (info->fps_n) / (info->fps_d);
+- }
++ default:
++ g_assert_not_reached ();
++ }
+
+- GST_DEBUG_OBJECT (self, "Setting inport port definition");
+- if (gst_omx_port_update_port_definition (self->enc_in_port,
+- &port_def) != OMX_ErrorNone)
+- return FALSE;
++ if (info->fps_n == 0) {
++ port_def.format.video.xFramerate = 0;
++ } else {
++ if (!(klass->cdata.hacks & GST_OMX_HACK_VIDEO_FRAMERATE_INTEGER))
++ port_def.format.video.xFramerate = (info->fps_n << 16) / (info->fps_d);
++ else
++ port_def.format.video.xFramerate = (info->fps_n) / (info->fps_d);
++ }
+
+- if (klass->set_format) {
+- if (!klass->set_format (self, self->enc_in_port, state)) {
+- GST_ERROR_OBJECT (self, "Subclass failed to set the new format");
++ GST_DEBUG_OBJECT (self, "Setting inport port definition");
++ if (gst_omx_port_update_port_definition (self->enc_in_port,
++ &port_def) != OMX_ErrorNone)
+ return FALSE;
++
++ if (klass->set_format) {
++ if (!klass->set_format (self, self->enc_in_port, state)) {
++ GST_ERROR_OBJECT (self, "Subclass failed to set the new format");
++ return FALSE;
++ }
+ }
+- }
+
+- GST_DEBUG_OBJECT (self, "Updating outport port definition");
+- if (gst_omx_port_update_port_definition (self->enc_out_port,
+- NULL) != OMX_ErrorNone)
+- return FALSE;
++ GST_DEBUG_OBJECT (self, "Updating outport port definition");
++ if (gst_omx_port_update_port_definition (self->enc_out_port,
++ NULL) != OMX_ErrorNone)
++ return FALSE;
++ }
+
+ GST_DEBUG_OBJECT (self, "Enabling component");
+ if (needs_disable) {
+@@ -1299,11 +1342,13 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder,
+ return FALSE;
+
+ /* Need to allocate buffers to reach Idle state */
+- if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone)
+- return FALSE;
++ if (!self->in_port_pool) {
++ if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone)
++ return FALSE;
++ }
+
+ /* Allocate for output port */
+- if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone)
++ if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone)
+ return FALSE;
+ if (gst_omx_component_get_state (self->enc,
+ GST_CLOCK_TIME_NONE) != OMX_StateIdle)
+@@ -1349,7 +1394,9 @@ gst_omx_video_enc_reset (GstVideoEncoder * encoder, gboolean hard)
+
+ self = GST_OMX_VIDEO_ENC (encoder);
+
+- GST_DEBUG_OBJECT (self, "Resetting encoder");
++ GST_DEBUG_OBJECT (self, "Resetting encoder %s", hard ? "(hard)" : "");
++
++ return TRUE;
+
+ gst_omx_port_set_flushing (self->enc_in_port, 5 * GST_SECOND, TRUE);
+ gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE);
+@@ -1739,11 +1786,50 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder,
+ gst_omx_error_to_string (err), err);
+ }
+
+- /* Copy the buffer content in chunks of size as requested
+- * by the port */
+- if (!gst_omx_video_enc_fill_buffer (self, frame->input_buffer, buf)) {
+- gst_omx_port_release_buffer (port, buf);
+- goto buffer_fill_error;
++ if (self->in_port_pool) {
++ GstMapInfo in_info;
++ gint count = 0;
++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (self->in_port_pool);
++
++ /* Compare input buffer with buffer got from port to get target data for
++ * encoder
++ */
++ if (!pool->deactivated) {
++ if (!gst_buffer_map (frame->input_buffer, &in_info, GST_MAP_READ)) {
++ GST_ERROR_OBJECT (self, "Can not map input buffer");
++ gst_omx_port_release_buffer (port, buf);
++ goto flow_error;
++ }
++
++ if (buf->omx_buf->pBuffer != in_info.data) {
++ gst_omx_port_release_buffer (port, buf);
++ do {
++ acq_ret = gst_omx_port_acquire_buffer (port, &buf);
++ if (acq_ret != GST_OMX_ACQUIRE_BUFFER_OK) {
++ GST_ERROR_OBJECT (self, "Can acquire buffer from input port");
++ return GST_FLOW_ERROR;
++ }
++ if (buf->omx_buf->pBuffer != in_info.data)
++ gst_omx_port_release_buffer (port, buf);
++ count += 1;
++ } while (buf->omx_buf->pBuffer != in_info.data
++ && count < port->port_def.nBufferCountActual * 3);
++ }
++ if (count == port->port_def.nBufferCountActual * 3) {
++ GST_ERROR_OBJECT (self,
++ "Can not get target OMXBuffer after 3 times searching");
++ goto flow_error;
++ }
++ buf->omx_buf->nFilledLen = in_info.size;
++ gst_buffer_unmap (frame->input_buffer, &in_info);
++ }
++ } else {
++ /* Copy the buffer content in chunks of size as requested
++ * by the port */
++ if (!gst_omx_video_enc_fill_buffer (self, frame->input_buffer, buf)) {
++ gst_omx_port_release_buffer (port, buf);
++ goto buffer_fill_error;
++ }
+ }
+
+ timestamp = frame->pts;
+@@ -1920,11 +2006,203 @@ static gboolean
+ gst_omx_video_enc_propose_allocation (GstVideoEncoder * encoder,
+ GstQuery * query)
+ {
+- gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
++ GstOMXVideoEnc *self;
++ GstOMXVideoEncClass *klass;
++
++ self = GST_OMX_VIDEO_ENC (encoder);
++ klass = GST_OMX_VIDEO_ENC_GET_CLASS (encoder);
++
++ GST_DEBUG_OBJECT (self, "gst_omx_video_enc_propose_allocation");
++ if (self->no_copy == TRUE) {
++ /* Allocate buffers and propose them to upstream */
++ GstCaps *caps;
++ GstVideoInfo info;
++ guint size;
++ OMX_PARAM_PORTDEFINITIONTYPE port_def;
++ guint max, min;
++
++ gst_omx_port_get_port_definition (self->enc_in_port, &port_def);
++
++ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
++
++ gst_query_parse_allocation (query, &caps, NULL);
++
++ if (caps == NULL)
++ return FALSE;
++
++ if (!gst_video_info_from_caps (&info, caps))
++ return FALSE;
++
++ size = GST_VIDEO_INFO_SIZE (&info);
++
++ if (gst_omx_component_get_state (self->enc,
++ GST_CLOCK_TIME_NONE) != OMX_StateLoaded)
++ return FALSE;
++ switch (info.finfo->format) {
++ case GST_VIDEO_FORMAT_I420:
++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
++ break;
++ case GST_VIDEO_FORMAT_NV12:
++ case GST_VIDEO_FORMAT_NV16:
++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
++ break;
++ default:
++ GST_ERROR_OBJECT (self, "Unsupported format %s",
++ gst_video_format_to_string (info.finfo->format));
++ return FALSE;
++ break;
++ }
++ port_def.format.video.nFrameWidth = info.width;
++ if (klass->cdata.hacks & GST_OMX_HACK_RENESAS_ENCMC_STRIDE_ALIGN) {
++ switch (port_def.format.video.eColorFormat) {
++ case OMX_COLOR_FormatYUV420Planar:
++ port_def.format.video.nStride = GST_ROUND_UP_64 (info.width);
++ break;
++ case OMX_COLOR_FormatYUV420SemiPlanar:
++ port_def.format.video.nStride = GST_ROUND_UP_32 (info.width);
++ break;
++ default:
++ break;
++ }
++ } else
++ port_def.format.video.nStride = GST_ROUND_UP_4 (info.width); /* safe (?) default */
++ port_def.format.video.nFrameHeight = info.height;
++ port_def.format.video.nSliceHeight = info.height;
++ switch (port_def.format.video.eColorFormat) {
++ case OMX_COLOR_FormatYUV420Planar:
++ case OMX_COLOR_FormatYUV420PackedPlanar:
++ port_def.nBufferSize =
++ (port_def.format.video.nStride *
++ port_def.format.video.nFrameHeight) +
++ 2 * ((port_def.format.video.nStride / 2) *
++ ((port_def.format.video.nFrameHeight + 1) / 2));
++ break;
++
++ case OMX_COLOR_FormatYUV420SemiPlanar:
++ port_def.nBufferSize =
++ (port_def.format.video.nStride *
++ port_def.format.video.nFrameHeight) +
++ (port_def.format.video.nStride *
++ ((port_def.format.video.nFrameHeight + 1) / 2));
++ break;
+
+- return
+- GST_VIDEO_ENCODER_CLASS
+- (gst_omx_video_enc_parent_class)->propose_allocation (encoder, query);
++ default:
++ g_assert_not_reached ();
++ }
++ if (info.fps_n == 0) {
++ port_def.format.video.xFramerate = 0;
++ } else {
++ port_def.format.video.xFramerate = (info.fps_n) / (info.fps_d);
++ }
++
++ if (gst_omx_port_update_port_definition (self->enc_in_port,
++ &port_def) != OMX_ErrorNone)
++ return FALSE;
++
++ if (klass->set_format) {
++ if (!klass->set_format (self, self->enc_in_port, self->input_state)) {
++ GST_ERROR_OBJECT (self, "Subclass failed to set the new format");
++ return FALSE;
++ }
++ }
++ GST_DEBUG_OBJECT (self, "Updating outport port definition");
++ if (gst_omx_port_update_port_definition (self->enc_out_port,
++ NULL) != OMX_ErrorNone)
++ return FALSE;
++
++ if (self->target_bitrate != 0xffffffff) {
++ OMX_VIDEO_PARAM_BITRATETYPE config;
++ OMX_ERRORTYPE err;
++
++ GST_OMX_INIT_STRUCT (&config);
++ config.nPortIndex = self->enc_out_port->index;
++ /* Get default value of eControlRate to avoid setting an invalid value to it */
++ err = gst_omx_component_get_parameter (self->enc,
++ OMX_IndexParamVideoBitrate, &config);
++ if (err != OMX_ErrorNone)
++ GST_ERROR_OBJECT (self,
++ "Fail to get parameter of video bitrate: %s (0x%08x)",
++ gst_omx_error_to_string (err), err);
++
++ config.nTargetBitrate = self->target_bitrate;
++ if (self->control_rate != 0xffffffff)
++ config.eControlRate = self->control_rate;
++
++ err = gst_omx_component_set_parameter (self->enc,
++ OMX_IndexParamVideoBitrate, &config);
++ if (err != OMX_ErrorNone)
++ GST_ERROR_OBJECT (self, "Failed to set bitrate parameter: %s (0x%08x)",
++ gst_omx_error_to_string (err), err);
++ }
++
++ if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone)
++ return FALSE;
++
++ if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone)
++ return FALSE;
++
++ if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone)
++ return FALSE;
++
++ GST_DEBUG_OBJECT (self, " gst_query_get_n_allocation_pools = %d",
++ gst_query_get_n_allocation_pools (query));
++ if (gst_query_get_n_allocation_pools (query) == 0) {
++ GstStructure *structure;
++ GstAllocator *allocator = NULL;
++ GstAllocationParams params = { 0, };
++
++ if (gst_query_get_n_allocation_params (query) > 0)
++ gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
++ else
++ gst_query_add_allocation_param (query, allocator, &params);
++
++ self->in_port_pool = gst_omx_buffer_pool_new (GST_ELEMENT_CAST (self),
++ self->enc, self->enc_in_port);
++
++ structure = gst_buffer_pool_get_config (self->in_port_pool);
++ gst_buffer_pool_config_set_params (structure, caps,
++ self->enc_in_port->port_def.nBufferSize,
++ self->enc_in_port->port_def.nBufferCountActual,
++ self->enc_in_port->port_def.nBufferCountActual);
++
++ GST_DEBUG_OBJECT (self, " add allocator");
++ gst_buffer_pool_config_get_params (structure, &caps, NULL, &min, &max);
++ gst_buffer_pool_config_set_allocator (structure, allocator, &params);
++
++ if (allocator)
++ gst_object_unref (allocator);
++
++ if (!gst_buffer_pool_set_config (self->in_port_pool, structure)) {
++ GST_ERROR_OBJECT (self, "failed to set config");
++ gst_object_unref (self->in_port_pool);
++ return FALSE;
++ }
++
++ GST_OMX_BUFFER_POOL (self->in_port_pool)->allocating = TRUE;
++
++ /* Wait for all buffers allocate */
++ GST_DEBUG_OBJECT (self, "Activating pool");
++ while (!gst_buffer_pool_set_active (self->in_port_pool, TRUE)) {
++ }
++
++ GST_OMX_BUFFER_POOL (self->in_port_pool)->allocating = FALSE;
++
++ gst_query_add_allocation_pool (query, self->in_port_pool, size,
++ port_def.nBufferCountActual, port_def.nBufferCountActual);
++
++ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
++
++ }
++
++ return TRUE;
++
++ } else {
++ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
++
++ return
++ GST_VIDEO_ENCODER_CLASS
++ (gst_omx_video_enc_parent_class)->propose_allocation (encoder, query);
++ }
+ }
+
+ static GstCaps *
+diff --git a/omx/gstomxvideoenc.h b/omx/gstomxvideoenc.h
+index e266537..7f80dca 100644
+--- a/omx/gstomxvideoenc.h
++++ b/omx/gstomxvideoenc.h
+@@ -44,6 +44,7 @@ G_BEGIN_DECLS
+
+ typedef struct _GstOMXVideoEnc GstOMXVideoEnc;
+ typedef struct _GstOMXVideoEncClass GstOMXVideoEncClass;
++typedef struct _GstOMXVideoEncPrivate GstOMXVideoEncPrivate;
+
+ struct _GstOMXVideoEnc
+ {
+@@ -53,6 +54,8 @@ struct _GstOMXVideoEnc
+ GstOMXComponent *enc;
+ GstOMXPort *enc_in_port, *enc_out_port;
+
++ GstBufferPool *in_port_pool, *out_port_pool;
++
+ /* < private > */
+ GstVideoCodecState *input_state;
+ /* TRUE if the component is configured and saw
+@@ -78,6 +81,14 @@ struct _GstOMXVideoEnc
+ guint32 quant_b_frames;
+
+ GstFlowReturn downstream_flow_ret;
++
++ /* Set TRUE to use GstBuffer of Bufferpool to transfer data to
++ * downstream
++ */
++ gboolean no_copy;
++
++ /* need? */
++ GstOMXVideoEncPrivate *priv;
+ };
+
+ struct _GstOMXVideoEncClass
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0004-Export-a-first-dmabuf-file-descriptor-with-the-whole.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0004-Export-a-first-dmabuf-file-descriptor-with-the-whole.patch
new file mode 100644
index 0000000..6aa5d48
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0004-Export-a-first-dmabuf-file-descriptor-with-the-whole.patch
@@ -0,0 +1,65 @@
+From 1fe52cec8fec530a79eb3ab9f313bb860ec109be Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 1 Sep 2016 18:44:49 +0300
+Subject: [PATCH 04/10] Export a first dmabuf file descriptor with the whole
+ size
+
+This patch exports a dmabuf file descriptor from the head of Y plane
+to the end of the buffer so that mapping the whole plane as
+contiguous memory is available.
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ omx/gstomxbufferpool.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c
+index 2585a72..b9fa769 100644
+--- a/omx/gstomxbufferpool.c
++++ b/omx/gstomxbufferpool.c
+@@ -399,6 +399,7 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self,
+ gint i;
+ gint page_size;
+ guint phys_addr = 0;
++ guint phys_size = 0;
+
+ new_buf = gst_buffer_new ();
+ page_size = getpagesize ();
+@@ -410,13 +411,15 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self,
+ OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res =
+ (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf->omx_buf->pOutputPortPrivate;
+ phys_addr = decode_res->pvPhysImageAddressY;
++ phys_size = (guint) omx_buf->omx_buf->nAllocLen;
+ } else if (GST_IS_OMX_VIDEO_ENC (self->element)) {
+ /* private data is a physical address of HW buffer */
+ phys_addr = (guint) omx_buf->omx_buf->pInputPortPrivate;
++ phys_size = (guint) omx_buf->omx_buf->nAllocLen;
+ }
+
+- if (phys_addr == 0) {
+- GST_ERROR_OBJECT (self, "Invalid phys addr for OMX buffer");
++ if ((phys_addr == 0) || (phys_size == 0)) {
++ GST_ERROR_OBJECT (self, "Invalid phys range for OMX buffer");
+ return NULL;
+ }
+
+@@ -428,8 +431,14 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self,
+ /* Calculate offset between physical address and page boundary */
+ page_offset[i] = plane_addr & (page_size - 1);
+
+- plane_size[i] = stride[i] *
+- GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i);
++ /* Export a dmabuf file descriptor from the head of Y plane to
++ * the end of the buffer so that mapping the whole plane as
++ * contiguous memory is available. */
++ if (i == 0)
++ plane_size[i] = phys_size;
++ else
++ plane_size[i] = stride[i] *
++ GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i);
+
+ /* When downstream plugins do mapping from dmabuf fd it requires
+ * mapping from boundary page and size align for page size so
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch
new file mode 100644
index 0000000..c6b5c36
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch
@@ -0,0 +1,75 @@
+From c4e86a58041cd4408d283444dcba6f532a80697c Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 1 Sep 2016 17:33:44 +0300
+Subject: [PATCH 05/10] gssomxbufferpool: add exported flag
+
+This flag indicates that buffer are used outside of OMX component
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ omx/gstomx.c | 1 +
+ omx/gstomx.h | 5 +++++
+ omx/gstomxbufferpool.c | 5 ++++-
+ 3 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/omx/gstomx.c b/omx/gstomx.c
+index c018e72..5a916dc 100644
+--- a/omx/gstomx.c
++++ b/omx/gstomx.c
+@@ -1663,6 +1663,7 @@ gst_omx_port_allocate_buffers_unlocked (GstOMXPort * port,
+ buf = g_slice_new0 (GstOMXBuffer);
+ buf->port = port;
+ buf->used = FALSE;
++ buf->exported = FALSE;
+ buf->settings_cookie = port->settings_cookie;
+ g_ptr_array_add (port->buffers, buf);
+
+diff --git a/omx/gstomx.h b/omx/gstomx.h
+index 84980f3..27cb2a9 100644
+--- a/omx/gstomx.h
++++ b/omx/gstomx.h
+@@ -279,6 +279,11 @@ struct _GstOMXBuffer {
+ */
+ gboolean used;
+
++ /* TRUE if the buffer exported outside the component,
++ * i.e. someone acquired this buffer
++ */
++ gboolean exported;
++
+ /* Cookie of the settings when this buffer was allocated */
+ gint settings_cookie;
+
+diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c
+index b9fa769..1e0a14c 100644
+--- a/omx/gstomxbufferpool.c
++++ b/omx/gstomxbufferpool.c
+@@ -695,7 +695,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+ pool->enc_buffer_index = 0;
+
+ count += 1;
+- } while (omx_buf->used == TRUE &&
++ } while (omx_buf->exported == TRUE &&
+ count < pool->port->port_def.nBufferCountActual * 3);
+
+ if (count == pool->port->port_def.nBufferCountActual * 3) {
+@@ -703,6 +703,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+ GST_ERROR_OBJECT (pool,
+ "Can not acquire buffer after 3 times searching");
+ } else {
++ omx_buf->exported = TRUE;
+ *buffer = buf;
+ ret = GST_FLOW_OK;
+ }
+@@ -731,6 +732,8 @@ gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
+ omx_buf =
+ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer),
+ gst_omx_buffer_data_quark);
++ if (GST_IS_OMX_VIDEO_ENC (pool->element))
++ omx_buf->exported = FALSE;
+ if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) {
+ /* Release back to the port, can be filled again */
+ err = gst_omx_port_release_buffer (pool->port, omx_buf);
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch
new file mode 100644
index 0000000..93966b2
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch
@@ -0,0 +1,46 @@
+From 078a91a917a7b81cfcf523ac23b1c3e154506ef9 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 1 Sep 2016 17:43:35 +0300
+Subject: [PATCH 06/10] gstomxbufferpool: create dmabuf for input port
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ omx/gstomxbufferpool.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c
+index 1e0a14c..d86f9d8 100644
+--- a/omx/gstomxbufferpool.c
++++ b/omx/gstomxbufferpool.c
+@@ -518,6 +518,7 @@ gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool,
+
+ pool->need_copy = FALSE;
+ } else {
++ gboolean dmabuf = FALSE;
+ GstMemory *mem;
+ const guint nstride = pool->port->port_def.format.video.nStride;
+ const guint nslice = pool->port->port_def.format.video.nSliceHeight;
+@@ -552,8 +553,17 @@ gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool,
+
+ if (GST_IS_OMX_VIDEO_DEC (pool->element) &&
+ GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf == TRUE &&
+- (omx_buf->omx_buf->pOutputPortPrivate)) {
+-#if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT)
++ (omx_buf->omx_buf->pOutputPortPrivate))
++ dmabuf = TRUE;
++
++
++ if (GST_IS_OMX_VIDEO_ENC (pool->element) &&
++ GST_OMX_VIDEO_ENC (pool->element)->no_copy == TRUE &&
++ (omx_buf->omx_buf->pInputPortPrivate))
++ dmabuf = TRUE;
++
++ if (dmabuf) {
++#if defined (HAVE_MMNGRBUF)
+ if (pool->allocator)
+ gst_object_unref (pool->allocator);
+ pool->allocator = gst_dmabuf_allocator_new ();
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch
new file mode 100644
index 0000000..ed9da8d
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch
@@ -0,0 +1,140 @@
+From 4abd8ac4f18f5baef5a23c7defdb12469192f9c5 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 1 Sep 2016 17:51:30 +0300
+Subject: [PATCH 07/10] gstomxbufferpool: add helper to get omxbuffer from
+ gstomxbuffer
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ omx/gstomxbufferpool.c | 21 +++++++++++++++------
+ omx/gstomxbufferpool.h | 2 ++
+ omx/gstomxvideoenc.c | 26 ++++++++++++++++----------
+ 3 files changed, 33 insertions(+), 16 deletions(-)
+
+diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c
+index d86f9d8..eb2fe9d 100644
+--- a/omx/gstomxbufferpool.c
++++ b/omx/gstomxbufferpool.c
+@@ -361,6 +361,17 @@ wrong_video_caps:
+ }
+ }
+
++GstOMXBuffer *gst_omx_buffer_get_omxbuffer (GstBuffer * buffer)
++{
++ GstOMXBuffer *omx_buf;
++
++ omx_buf =
++ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer),
++ gst_omx_buffer_data_quark);
++
++ return omx_buf;
++}
++
+ #if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT)
+ static gboolean
+ gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool,
+@@ -697,9 +708,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
+ buf = g_ptr_array_index (pool->buffers, pool->enc_buffer_index);
+ g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
+
+- omx_buf =
+- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buf),
+- gst_omx_buffer_data_quark);
++ omx_buf = gst_omx_buffer_get_omxbuffer(buf);
+ pool->enc_buffer_index++;
+ if (pool->enc_buffer_index == pool->port->port_def.nBufferCountActual)
+ pool->enc_buffer_index = 0;
+@@ -739,11 +748,11 @@ gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
+ g_assert (pool->component && pool->port);
+
+ if (!pool->allocating && !pool->deactivated) {
+- omx_buf =
+- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer),
+- gst_omx_buffer_data_quark);
++ omx_buf = gst_omx_buffer_get_omxbuffer(buffer);
++
+ if (GST_IS_OMX_VIDEO_ENC (pool->element))
+ omx_buf->exported = FALSE;
++
+ if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) {
+ /* Release back to the port, can be filled again */
+ err = gst_omx_port_release_buffer (pool->port, omx_buf);
+diff --git a/omx/gstomxbufferpool.h b/omx/gstomxbufferpool.h
+index 09cab8d..0c6f18b 100644
+--- a/omx/gstomxbufferpool.h
++++ b/omx/gstomxbufferpool.h
+@@ -101,6 +101,8 @@ GType gst_omx_buffer_pool_get_type (void);
+
+ GstBufferPool *gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, GstOMXPort * port);
+
++GstOMXBuffer *gst_omx_buffer_get_omxbuffer (GstBuffer * buffer);
++
+ G_END_DECLS
+
+ #endif /* __GST_OMX_BUFFER_POOL_H__ */
+diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c
+index b36c46e..e96ff28 100644
+--- a/omx/gstomxvideoenc.c
++++ b/omx/gstomxvideoenc.c
+@@ -1786,7 +1786,8 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder,
+ gst_omx_error_to_string (err), err);
+ }
+
+- if (self->in_port_pool) {
++ if ((self->in_port_pool) &&
++ (frame->input_buffer->pool == self->in_port_pool)) {
+ GstMapInfo in_info;
+ gint count = 0;
+ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (self->in_port_pool);
+@@ -1795,13 +1796,15 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder,
+ * encoder
+ */
+ if (!pool->deactivated) {
+- if (!gst_buffer_map (frame->input_buffer, &in_info, GST_MAP_READ)) {
+- GST_ERROR_OBJECT (self, "Can not map input buffer");
+- gst_omx_port_release_buffer (port, buf);
+- goto flow_error;
++ GstOMXBuffer *omx_buf;
++
++ omx_buf = gst_omx_buffer_get_omxbuffer(frame->input_buffer);
++ if (!omx_buf) {
++ GST_ERROR_OBJECT (self, "Can not get OMXBuffer from GstBuffer");
++ return GST_FLOW_ERROR;
+ }
+
+- if (buf->omx_buf->pBuffer != in_info.data) {
++ if (buf != omx_buf) {
+ gst_omx_port_release_buffer (port, buf);
+ do {
+ acq_ret = gst_omx_port_acquire_buffer (port, &buf);
+@@ -1809,10 +1812,10 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder,
+ GST_ERROR_OBJECT (self, "Can acquire buffer from input port");
+ return GST_FLOW_ERROR;
+ }
+- if (buf->omx_buf->pBuffer != in_info.data)
++ if (buf != omx_buf)
+ gst_omx_port_release_buffer (port, buf);
+ count += 1;
+- } while (buf->omx_buf->pBuffer != in_info.data
++ } while (buf != omx_buf
+ && count < port->port_def.nBufferCountActual * 3);
+ }
+ if (count == port->port_def.nBufferCountActual * 3) {
+@@ -1820,8 +1823,11 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder,
+ "Can not get target OMXBuffer after 3 times searching");
+ goto flow_error;
+ }
+- buf->omx_buf->nFilledLen = in_info.size;
+- gst_buffer_unmap (frame->input_buffer, &in_info);
++ GST_DEBUG_OBJECT (self, "found target OMXBuffer %p", buf);
++ //buf->omx_buf->nFilledLen = gst_buffer_get_size (frame->input_buffer);
++ buf->omx_buf->nFilledLen = buf->omx_buf->nAllocLen - buf->omx_buf->nOffset;
++ GST_DEBUG_OBJECT (self, "set nFilledLen = %d", buf->omx_buf->nFilledLen);
++
+ }
+ } else {
+ /* Copy the buffer content in chunks of size as requested
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch
new file mode 100644
index 0000000..8f97e7f
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch
@@ -0,0 +1,47 @@
+From d1025433f05ebeb1a790abefa5cfc48455bf441c Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 1 Sep 2016 17:57:10 +0300
+Subject: [PATCH 08/10] gstomxenc: do not allocate output buffers two times
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ omx/gstomxvideoenc.c | 17 ++++++++++-------
+ 1 file changed, 10 insertions(+), 7 deletions(-)
+
+diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c
+index e96ff28..19a0eb9 100644
+--- a/omx/gstomxvideoenc.c
++++ b/omx/gstomxvideoenc.c
+@@ -1338,18 +1338,21 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder,
+ if (gst_omx_port_mark_reconfigured (self->enc_in_port) != OMX_ErrorNone)
+ return FALSE;
+ } else {
+- if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone)
+- return FALSE;
+-
+- /* Need to allocate buffers to reach Idle state */
++ /* if is not done in propose_allocation */
+ if (!self->in_port_pool) {
++ if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone)
++ return FALSE;
++
++ /* Need to allocate buffers to reach Idle state */
++ /* Allocate for input port */
+ if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone)
+ return FALSE;
++
++ /* Allocate for output port */
++ if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone)
++ return FALSE;
+ }
+
+- /* Allocate for output port */
+- if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone)
+- return FALSE;
+ if (gst_omx_component_get_state (self->enc,
+ GST_CLOCK_TIME_NONE) != OMX_StateIdle)
+ return FALSE;
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch
new file mode 100644
index 0000000..8404acd
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch
@@ -0,0 +1,113 @@
+From 0a9f0aa8271b0fc18c7e9781e3d0bc215ba2fd02 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 1 Sep 2016 17:58:33 +0300
+Subject: [PATCH 09/10] gstomxenc: move encoder disable code to separate
+ function
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ omx/gstomxvideoenc.c | 79 ++++++++++++++++++++++++++++----------------------
+ 1 file changed, 45 insertions(+), 34 deletions(-)
+
+diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c
+index 19a0eb9..6720648 100644
+--- a/omx/gstomxvideoenc.c
++++ b/omx/gstomxvideoenc.c
+@@ -1147,6 +1147,49 @@ gst_omx_video_enc_get_supported_colorformats (GstOMXVideoEnc * self)
+ }
+
+ static gboolean
++gst_omx_video_enc_disable(GstVideoEncoder * encoder)
++{
++ GstOMXVideoEnc *self;
++
++ self = GST_OMX_VIDEO_ENC (encoder);
++
++ GST_DEBUG_OBJECT (self, "Need to disable and drain encoder");
++ gst_omx_video_enc_drain (self, FALSE);
++ gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE);
++
++ /* Wait until the srcpad loop is finished,
++ * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks
++ * caused by using this lock from inside the loop function */
++ GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
++ gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder));
++ GST_VIDEO_ENCODER_STREAM_LOCK (self);
++
++ if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_buffers_released (self->enc_in_port,
++ 5 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_buffers_released (self->enc_out_port,
++ 1 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_enabled (self->enc_in_port,
++ 1 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++ if (gst_omx_port_wait_enabled (self->enc_out_port,
++ 1 * GST_SECOND) != OMX_ErrorNone)
++ return FALSE;
++
++ GST_DEBUG_OBJECT (self, "Encoder drained and disabled");
++ return TRUE;
++}
++
++static gboolean
+ gst_omx_video_enc_set_format (GstVideoEncoder * encoder,
+ GstVideoCodecState * state)
+ {
+@@ -1176,41 +1219,9 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder,
+ * we have to disable the port and re-allocate all buffers. If no real
+ * format change happened we can just exit here.
+ */
+- if (needs_disable) {
+- GST_DEBUG_OBJECT (self, "Need to disable and drain encoder");
+- gst_omx_video_enc_drain (self, FALSE);
+- gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE);
+-
+- /* Wait until the srcpad loop is finished,
+- * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks
+- * caused by using this lock from inside the loop function */
+- GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
+- gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder));
+- GST_VIDEO_ENCODER_STREAM_LOCK (self);
+-
+- if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone)
++ if (needs_disable)
++ if (!gst_omx_video_enc_disable(encoder))
+ return FALSE;
+- if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_buffers_released (self->enc_in_port,
+- 5 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_buffers_released (self->enc_out_port,
+- 1 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_enabled (self->enc_in_port,
+- 1 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+- if (gst_omx_port_wait_enabled (self->enc_out_port,
+- 1 * GST_SECOND) != OMX_ErrorNone)
+- return FALSE;
+-
+- GST_DEBUG_OBJECT (self, "Encoder drained and disabled");
+- }
+
+ negotiation_map = gst_omx_video_enc_get_supported_colorformats (self);
+ if (!negotiation_map) {
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch
new file mode 100644
index 0000000..bd2b91c
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch
@@ -0,0 +1,186 @@
+From 58d8ea72ec78cb17cf75c82c67a69e9bd383c3b3 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 1 Sep 2016 20:09:03 +0300
+Subject: [PATCH 10/10] omxvideodec: support creating buffers using sink
+
+Used for zero-copy output to wayland/weston
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ omx/gstomxbufferpool.c | 107 +++++++++++++++++++++++++++++++++++++++++++++---
+ omx/gstomxvideodec.c | 11 ++++-
+ 2 files changed, 111 insertions(+), 7 deletions(-)
+
+diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c
+index eb2fe9d..60b25ef 100644
+--- a/omx/gstomxbufferpool.c
++++ b/omx/gstomxbufferpool.c
+@@ -372,6 +372,73 @@ GstOMXBuffer *gst_omx_buffer_get_omxbuffer (GstBuffer * buffer)
+ return omx_buf;
+ }
+
++#ifdef HAVE_MMNGRBUF
++static GstBuffer *
++gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool,
++ gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gpointer plane_buf[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES])
++{
++ GstQuery *query;
++ GValue val = { 0, };
++ GstStructure *structure;
++ const GValue *value;
++ GstBuffer *buffer;
++ GArray *dmabuf_array;
++ GArray *stride_array;
++ GArray *planebuf_array;
++ gint n_planes;
++ gint i;
++
++ g_value_init (&val, G_TYPE_POINTER);
++ g_value_set_pointer (&val, (gpointer) pool->allocator);
++
++ dmabuf_array = g_array_new (FALSE, FALSE, sizeof (gint));
++ stride_array = g_array_new (FALSE, FALSE, sizeof (gint));
++ planebuf_array = g_array_new (FALSE, FALSE, sizeof (gpointer));
++
++ n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info);
++ for (i = 0; i < n_planes; i++) {
++ g_array_append_val (dmabuf_array, dmabuf_fd[i]);
++ g_array_append_val (stride_array, stride[i]);
++ g_array_append_val (planebuf_array, plane_buf[i]);
++ }
++
++ structure = gst_structure_new ("videosink_buffer_creation_request",
++ "width", G_TYPE_INT, pool->port->port_def.format.video.nFrameWidth,
++ "height", G_TYPE_INT, pool->port->port_def.format.video.nFrameHeight,
++ "stride", G_TYPE_ARRAY, stride_array,
++ "dmabuf", G_TYPE_ARRAY, dmabuf_array,
++ "planebuf", G_TYPE_ARRAY, planebuf_array,
++ "allocator", G_TYPE_POINTER, &val,
++ "format", G_TYPE_STRING,
++ gst_video_format_to_string (pool->video_info.finfo->format),
++ "n_planes", G_TYPE_INT, n_planes, NULL);
++
++ query = gst_query_new_custom (GST_QUERY_CUSTOM, structure);
++
++ GST_DEBUG_OBJECT (pool, "send a videosink_buffer_creation_request query");
++
++ if (!gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (pool->element), query)) {
++ GST_ERROR_OBJECT (pool, "videosink_buffer_creation_request query failed");
++ return NULL;
++ }
++
++ value = gst_structure_get_value (structure, "buffer");
++ buffer = gst_value_get_buffer (value);
++ if (buffer == NULL) {
++ GST_ERROR_OBJECT (pool,
++ "could not get a buffer from videosink_buffer_creation query");
++ return NULL;
++ }
++
++ gst_query_unref (query);
++
++ g_array_free (dmabuf_array, TRUE);
++ g_array_free (stride_array, TRUE);
++
++ return buffer;
++}
++#endif
++
+ #if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT)
+ static gboolean
+ gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool,
+@@ -406,6 +473,7 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self,
+ gint plane_size_ext[GST_VIDEO_MAX_PLANES];
+ gint dmabuf_id[GST_VIDEO_MAX_PLANES];
+ gint page_offset[GST_VIDEO_MAX_PLANES];
++ gint plane_buf[GST_VIDEO_MAX_PLANES];
+ GstBuffer *new_buf;
+ gint i;
+ gint page_size;
+@@ -450,6 +518,7 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self,
+ else
+ plane_size[i] = stride[i] *
+ GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i);
++ plane_buf[i] = omx_buf->omx_buf->pBuffer + offset[i];
+
+ /* When downstream plugins do mapping from dmabuf fd it requires
+ * mapping from boundary page and size align for page size so
+@@ -472,14 +541,40 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self,
+ gst_buffer_append_memory (new_buf, mem);
+ }
+
+- g_ptr_array_add (self->buffers, new_buf);
+- gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE,
+- GST_VIDEO_INFO_FORMAT (&self->video_info),
+- GST_VIDEO_INFO_WIDTH (&self->video_info),
+- GST_VIDEO_INFO_HEIGHT (&self->video_info),
+- GST_VIDEO_INFO_N_PLANES (&self->video_info), offset, stride);
++ if (self->vsink_buf_req_supported) {
++ new_buf = gst_omx_buffer_pool_request_videosink_buffer_creation (self,
++ dmabuf_fd, plane_buf, stride);
++ if (!new_buf) {
++ GST_ERROR_OBJECT (self, "creating dmabuf using videosink failed");
++ goto err;
++ }
++ new_buf->pool = self;
++ } else {
++ new_buf = gst_buffer_new ();
++ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->video_info); i++) {
++ GstMemory *mem;
++ /* Set offset's information */
++ mem = gst_dmabuf_allocator_alloc (self->allocator, dmabuf_fd[i],
++ plane_size_ext[i]);
++ mem->offset = page_offset[i];
++ mem->size = plane_size[i];
++ gst_buffer_append_memory (new_buf, mem);
++ }
++
++ gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE,
++ GST_VIDEO_INFO_FORMAT (&self->video_info),
++ GST_VIDEO_INFO_WIDTH (&self->video_info),
++ GST_VIDEO_INFO_HEIGHT (&self->video_info),
++ GST_VIDEO_INFO_N_PLANES (&self->video_info), offset, stride);
++ }
+
++ GST_ERROR_OBJECT (self, "got buffer %p from pool %p",
++ new_buf, new_buf->pool);
++ g_ptr_array_add (self->buffers, new_buf);
+ return new_buf;
++
++err:
++ return NULL;
+ }
+ #endif
+
+diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c
+index 25b6b30..44b706a 100644
+--- a/omx/gstomxvideodec.c
++++ b/omx/gstomxvideodec.c
+@@ -2350,6 +2350,8 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
+ &GST_OMX_BUFFER_POOL (self->out_port_pool)->vsink_buf_req_supported);
+ gst_object_unref (pool);
+ update_pool = TRUE;
++ GST_ERROR_OBJECT (self, "vsink_buf_req_supported %d",
++ GST_OMX_BUFFER_POOL (self->out_port_pool)->vsink_buf_req_supported);
+ }
+
+ /* Set pool parameters to our own configuration */
+@@ -2372,7 +2374,14 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
+ }
+
+ GST_OMX_BUFFER_POOL (self->out_port_pool)->allocating = TRUE;
+- gst_buffer_pool_set_active (self->out_port_pool, TRUE);
++ /* This now allocates all the buffers */
++ if (!gst_buffer_pool_set_active (self->out_port_pool, TRUE)) {
++ GST_INFO_OBJECT (self, "Failed to activate internal pool");
++ gst_object_unref (self->out_port_pool);
++ self->out_port_pool = NULL;
++ } else {
++ GST_OMX_BUFFER_POOL (self->out_port_pool)->allocating = FALSE;
++ }
+
+ /* This video buffer pool created below will not be used, just setting to
+ * the gstvideodecoder class through a query, because it is
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf
new file mode 100644
index 0000000..375e201
--- /dev/null
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf
@@ -0,0 +1,62 @@
+[omxh263dec]
+type-name=GstOMXH263Dec
+core-name=/usr/local/lib/libomxr_core.so
+component-name=OMX.RENESAS.VIDEO.DECODER.H263
+rank=512
+in-port-index=0
+out-port-index=1
+hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio
+
+[omxh264dec]
+type-name=GstOMXH264Dec
+core-name=/usr/local/lib/libomxr_core.so
+component-name=OMX.RENESAS.VIDEO.DECODER.H264
+rank=512
+in-port-index=0
+out-port-index=1
+hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio
+
+[omxmpeg2videodec]
+type-name=GstOMXMPEG2VideoDec
+core-name=/usr/local/lib/libomxr_core.so
+component-name=OMX.RENESAS.VIDEO.DECODER.MPEG2
+rank=512
+in-port-index=0
+out-port-index=1
+hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio
+
+[omxmpeg4videodec]
+type-name=GstOMXMPEG4VideoDec
+core-name=/usr/local/lib/libomxr_core.so
+component-name=OMX.RENESAS.VIDEO.DECODER.MPEG4
+rank=512
+in-port-index=0
+out-port-index=1
+hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio
+
+[omxvc1videodec]
+type-name=GstOMXVC1VideoDec
+core-name=/usr/local/lib/libomxr_core.so
+component-name=OMX.RENESAS.VIDEO.DECODER.VC1
+rank=512
+in-port-index=0
+out-port-index=1
+hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio
+
+[omxaacdec]
+type-name=GstOMXAACDec
+core-name=/usr/local/lib/libomxr_core.so
+component-name=OMX.RENESAS.AUDIO.DECODER.AAC
+rank=256
+in-port-index=0
+out-port-index=1
+hacks=
+
+[omxh264enc]
+type-name=GstOMXH264Enc
+core-name=/usr/local/lib/libomxr_core.so
+component-name=OMX.RENESAS.VIDEO.ENCODER.H264
+rank=256
+in-port-index=0
+out-port-index=1
+hacks=renesas-encmc-stride-align
diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend
index 95706e5..426f0c4 100644
--- a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend
+++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend
@@ -1,20 +1,24 @@
require ../../include/gles-control.inc
SRC_URI_rcar-gen2 = "git://github.com/renesas-devel/gst-omx.git;protocol=git;branch=RCAR-GEN2/1.0.0"
-SRCREV_rcar-gen2 = "05563465faad99243ee2dd30547e3075eb8cf5e3"
+SRCREV_rcar-gen2 = "${@'e0a23fb50ec211a8058eac223847bbcc574fb343' \
+ if '1' in '${USE_GLES_WAYLAND}' else '05563465faad99243ee2dd30547e3075eb8cf5e3'}"
-LIC_FILES_CHKSUM_remove_rcar-gen2 = " file://omx/gstomx.h;beginline=1;endline=21;md5=5c8e1fca32704488e76d2ba9ddfa935f"
+LIC_FILES_CHKSUM_remove_rcar-gen2 = " file://omx/gstomx.h;beginline=1;endline=21;md5=5c8e1fca32704488e76d2ba9ddfa935f"
LIC_FILES_CHKSUM_append_rcar-gen2 = " file://omx/gstomx.h;beginline=1;endline=22;md5=17e5f2943dace9e5cde4a8587a31e8f9"
S = "${WORKDIR}/git"
-do_configure_prepend() {
+do_configure() {
cd ${S}
./autogen.sh --noconfigure
cd ${B}
+ oe_runconf
}
DEPENDS_append_rcar-gen2 = " omx-user-module mmngrbuf-user-module"
-EXTRA_OECONF_append_rcar-gen2 = " --with-omx-target=rcar --enable-experimental \
+EXTRA_OECONF_append_rcar-gen2 = " \
+ --with-omx-target=rcar --enable-experimental \
+ '${@'--enable-nv12-page-alignment' if '${USE_GLES_WAYLAND}' == '1' else ''}' \
'${@'--disable-dmabuf' if '${USE_GLES}' == '0' and '${USE_WAYLAND}' == '1' else ''}'"
# Overwrite do_install[postfuncs] += " set_omx_core_name "
@@ -22,6 +26,23 @@ EXTRA_OECONF_append_rcar-gen2 = " --with-omx-target=rcar --enable-experimental \
revert_omx_core_name() {
sed -i -e "s;^core-name=.*;core-name=/usr/local/lib/libomxr_core.so;" "${D}/etc/xdg/gstomx.conf"
}
+
REVERT_OMX_CORE_NAME = ""
REVERT_OMX_CORE_NAME_rcar-gen2 = "revert_omx_core_name"
do_install[postfuncs] += "${REVERT_OMX_CORE_NAME}"
+
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI_append = " \
+ file://0001-omx-videodec-add-planebuf-to-allocation-request.patch \
+ file://0002-Fixed-memory-corruption-and-bad-access.patch \
+ file://0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch \
+ file://0004-Export-a-first-dmabuf-file-descriptor-with-the-whole.patch \
+ file://0005-gssomxbufferpool-add-exported-flag.patch \
+ file://0006-gstomxbufferpool-create-dmabuf-for-input-port.patch \
+ file://0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch \
+ file://0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch \
+ file://0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch \
+ file://0010-omxvideodec-support-creating-buffers-using-sink.patch \
+ file://gstomx.conf \
+"
diff --git a/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb b/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb
index 688c6b7..8fa557f 100644
--- a/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb
+++ b/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb
@@ -23,6 +23,10 @@ MULTIMEDIA_PACKAGES ="\
libmemcpy \
"
+MULTIMEDIA_PACKAGES_append = " \
+ ${@ "vsp2-kernel-module" if "${USE_GLES_WAYLAND}" == "1" else "" } \
+"
+
RDEPENDS_packagegroup-rcar-gen2-multimedia = "\
${@ "${MULTIMEDIA_PACKAGES}" if "${USE_MULTIMEDIA}" == "1" else "" } \
media-ctl \