From 70f81fc7c3e93cb1653311738d6014de38673008 Mon Sep 17 00:00:00 2001 From: Jan-Simon Möller Date: Fri, 4 Nov 2016 21:20:14 +0100 Subject: Backport fix for CVE-2016-5195 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I8da95aa22cf2ba90419a897a6f5880a9451b987e Signed-off-by: Jan-Simon Möller --- .../9999-Backport-fix-for-CVE-2016-5195.patch | 68 ++++++++++++++++++++++ .../recipes-kernel/linux/linux-renesas_3.10.bb | 3 + 2 files changed, 71 insertions(+) create mode 100644 meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch 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?= +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 +--- + 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 4ef7e25..010e362 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 @@ -156,6 +156,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" -- cgit 1.2.3-korg From abe0801504f08b6e178bbf07f594f93d233ae5a6 Mon Sep 17 00:00:00 2001 From: Ronan Date: Fri, 21 Oct 2016 11:43:18 +0200 Subject: remove wayland&weston backport Change-Id: I970a9a6dc467590ce10d328c34804083923dbd68 Signed-off-by: Ronan --- ...disable-macro-checks-not-used-for-scanner.patch | 50 ------------ .../recipes-graphics/wayland/wayland_1.5.0.bb | 41 ---------- .../weston/disable-wayland-scanner-pkg-check.patch | 13 --- .../weston/make-lcms-explicitly-configurable.patch | 43 ---------- .../make-libwebp-explicitly-configurable.patch | 37 --------- ...around-to-avoid-segfault-on-hotplug-event.patch | 81 ------------------- .../recipes-graphics/wayland/weston/weston.desktop | 9 --- .../recipes-graphics/wayland/weston/weston.png | Bin 2383 -> 0 bytes .../recipes-graphics/wayland/weston_1.5.0.bb | 87 --------------------- .../recipes-graphics/wayland/weston_1.5.0.bbappend | 26 ------ 10 files changed, 387 deletions(-) delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/disable-wayland-scanner-pkg-check.patch delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bb delete mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend 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 ---- - 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 ]]) --AC_CHECK_DECL(TFD_CLOEXEC,[], -- [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland")], -- [[#include ]]) --AC_CHECK_DECL(CLOCK_MONOTONIC,[], -- [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland")], -- [[#include ]]) --AC_CHECK_HEADERS([execinfo.h]) -+##AC_CHECK_DECL(SFD_CLOEXEC,[], -+# [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland")], -+# [[#include ]]) -+#AC_CHECK_DECL(TFD_CLOEXEC,[], -+# [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland")], -+# [[#include ]]) -+#AC_CHECK_DECL(CLOCK_MONOTONIC,[], -+# [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland")], -+# [[#include ]]) -+#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/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 -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 -Signed-off-by: Manuel Bachmann ---- - 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 Binary files a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png and /dev/null differ 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)}" -- cgit 1.2.3-korg From b3036a9d0d264f1958e68fbf99efbb8fb9bd9487 Mon Sep 17 00:00:00 2001 From: Anton Gerasimov Date: Tue, 8 Nov 2016 18:28:09 +0100 Subject: Change DA9063 I2C address to 0x5A since we are using high-speed interface Change-Id: I8b4031223a8e645c210ee7c86de8cd44913976cd Signed-off-by: Anton Gerasimov --- .../recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch | 4 ++-- .../linux/linux-renesas/0008-Porter-board-support.patch | 2 +- 2 files changed, 3 insertions(+), 3 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-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) -- cgit 1.2.3-korg From 55899bf739f36f1e692c42d9219020720e836e73 Mon Sep 17 00:00:00 2001 From: Harunobu Kurokawa Date: Tue, 15 Nov 2016 19:05:57 +0900 Subject: rcar-gen2: vsp2-kernel-module: Add recipe This is wrapper module to use vspm-kernel-module via V4L2 I/F. In order to use this module, should be use the followings. $modprobe -a vsp2 vspm This commit is based 001ff9ff913d15b3f795bbb4c60e287fb4f780b8 in git://git.yoctoproject.org/meta-renesas. Signed-off-by: Tomohiro Komagata Signed-off-by: Takeshi Saito Change-Id: I17c824e8305cae67410b4fc809de3158664a7436 Signed-off-by: Harunobu Kurokawa --- .../vspm-module/vsp2-kernel-module.bb | 58 ++++++++++++++++++++++ .../packagegroup-rcar-gen2-multimedia.bb | 4 ++ 2 files changed, 62 insertions(+) create mode 100644 meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb 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/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 \ -- cgit 1.2.3-korg From baeac5c376980853149a85eab528a60f28d74bcf Mon Sep 17 00:00:00 2001 From: Grigory Kletsko Date: Wed, 2 Nov 2016 17:41:54 +0300 Subject: patch libgbm to retrieve DMA fd for gbm_surface This patch enables handling buffer object as file descriptor This patch will be used for meter cluster demo for CES2017 Change-Id: Ib83d69ba887ef9b5b03e99708aedf9b2a97f4d8a Signed-off-by: Grigory Kletsko --- meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb | 4 + .../libgbm/0001-Add-gbm_bo_get_fd-function.patch | 90 ++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch 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 +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 + -- cgit 1.2.3-korg From 284877cb63856744f2af7145d722d2ce9c6d9cb8 Mon Sep 17 00:00:00 2001 From: Grigory Kletsko Date: Wed, 2 Nov 2016 17:43:18 +0300 Subject: Add sharing screen support to Weston via h.264 This patch enables sharing screen support between IVI and meter cluster for CES2017 demo using weston. h264 streaming data on the surface can be sent to the different soc via rtp protocol. Change-Id: Ie4d00e369039e57a19940284a7d82aba684a4bf6 Signed-off-by: Grigory Kletsko --- .../weston/0001-Add-virtual-output-support.patch | 391 ++ .../wayland/weston/0002-Get-DMA-fd-on-bo.patch | 33 + ...dd-gst-recorder-for-h264-output-streaming.patch | 4183 ++++++++++++++++++++ .../recipes-graphics/wayland/weston/weston.service | 12 + .../recipes-graphics/wayland/weston_1.9.0.bbappend | 11 + 5 files changed, 4630 insertions(+) create mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch create mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch create mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch create mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service create mode 100644 meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend 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 +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 +Signed-off-by: Andrey Gusakov +--- + 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, ""); ++ 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 +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 +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 +Signed-off-by: Andrey Gusakov +--- + 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, ¶m.tty }, + { WESTON_OPTION_BOOLEAN, "current-mode", 0, &option_current_mode }, + { WESTON_OPTION_BOOLEAN, "use-pixman", 0, ¶m.use_pixman }, ++ { WESTON_OPTION_BOOLEAN, "gst-record", 0, ¶m.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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "compositor.h" ++#include "gst-recorder.h" ++ ++/* VSP includes */ ++#include ++#include ++ ++/* Gstreamer includes */ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 ++#include ++ ++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 ++ * ++ * 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 . ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 ++ ++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 ++ * ++ * 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 . ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 ++ * ++ * 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 . ++ */ ++ ++#ifndef __MEDIA_PRIV_H__ ++#define __MEDIA_PRIV_H__ ++ ++#include ++ ++#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 ++ * ++ * 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 . ++ */ ++ ++#ifndef __MEDIA_H__ ++#define __MEDIA_H__ ++ ++#include ++ ++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 ++ * ++ * 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 . ++ */ ++ ++#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 ++ * ++ * 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 . ++ */ ++ ++#ifndef __SUBDEV_H__ ++#define __SUBDEV_H__ ++ ++#include ++ ++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/weston.service b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service new file mode 100644 index 0000000..4e75b68 --- /dev/null +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service @@ -0,0 +1,12 @@ +[Unit] +Description=Weston reference Wayland compositor +Conflicts=getty@tty1.service +After=dbus.service rc.pvr.service + +[Service] +ExecStart=/usr/bin/weston-launch -u root -- --idle-time=4294967 --gst-record +ExecStop=/usr/bin/killall -s KILL weston +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend new file mode 100644 index 0000000..db4ec27 --- /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" + +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 \ +" \ No newline at end of file -- cgit 1.2.3-korg From d21c6598eff91f6ff739e340f708b13a566f9b57 Mon Sep 17 00:00:00 2001 From: Grigory Kletsko Date: Wed, 2 Nov 2016 17:42:27 +0300 Subject: patch to gst to add export of DMA fd of input port This patch adds h264 decode plugin using DMA for gstreamer. This plugin is supposed to be used to encode video data for CES2017 cluster demo. Change-Id: I19e3595aac8943ce3f348764aca66d2b4132cf28 Signed-off-by: Grigory Kletsko --- ...deodec-add-planebuf-to-allocation-request.patch | 85 + ...02-Fixed-memory-corruption-and-bad-access.patch | 65 + ...-export-dmafd-buffer-through-own-buffer-p.patch | 2759 ++++++++++++++++++++ ...rst-dmabuf-file-descriptor-with-the-whole.patch | 65 + .../0005-gssomxbufferpool-add-exported-flag.patch | 75 + ...mxbufferpool-create-dmabuf-for-input-port.patch | 46 + ...rpool-add-helper-to-get-omxbuffer-from-gs.patch | 140 + ...-do-not-allocate-output-buffers-two-times.patch | 47 + ...ove-encoder-disable-code-to-separate-func.patch | 113 + ...eodec-support-creating-buffers-using-sink.patch | 186 ++ .../gstreamer/gstreamer1.0-omx/gstomx.conf | 62 + .../gstreamer/gstreamer1.0-omx_1.0.0.bbappend | 29 +- 12 files changed, 3668 insertions(+), 4 deletions(-) create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0004-Export-a-first-dmabuf-file-descriptor-with-the-whole.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch create mode 100644 meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf 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 +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 +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 +--- + 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 , Collabora Ltd. ++ * Copyright (C) 2013, Collabora Ltd. ++ * Author: Sebastian Dröge ++ * 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 /* 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 ++ * 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 ++#include ++#include ++ ++#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 /* 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 + + #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, ¶ms); ++ else ++ gst_query_add_allocation_param (query, allocator, ¶ms); ++ ++ 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, ¶ms); ++ ++ 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 +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 +--- + 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 +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 +--- + 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 +Date: Thu, 1 Sep 2016 17:43:35 +0300 +Subject: [PATCH 06/10] gstomxbufferpool: create dmabuf for input port + + +Signed-off-by: Andrey Gusakov +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 \ +" -- cgit 1.2.3-korg From a45baaeac41ef0975315aa10a13cd3dc1d0c531f Mon Sep 17 00:00:00 2001 From: Yannick Gicquel Date: Wed, 21 Dec 2016 16:48:48 +0100 Subject: weston: fix missing dependencies This commit fix missing dependencies. Please refer to following: https://jira.automotivelinux.org/browse/SPEC-373 Bug-AGL: SPEC-373 Change-Id: I0e7e616ee2392e2ddc7aabc55379533c6ae5682d Signed-off-by: Yannick Gicquel Signed-off-by: Jan-Simon Moeller --- meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index db4ec27..33efb49 100644 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend @@ -1,5 +1,5 @@ EXTRA_OECONF += "--enable-gst-recorder " -DEPENDS += "media-ctl" +DEPENDS += "media-ctl gstreamer1.0 gstreamer1.0-plugins-base libjpeg-turbo" FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" @@ -8,4 +8,4 @@ SRC_URI_append = " \ file://0002-Get-DMA-fd-on-bo.patch \ file://0003-Add-gst-recorder-for-h264-output-streaming.patch \ file://weston.service \ -" \ No newline at end of file +" -- cgit 1.2.3-korg From bfdf1a0cfd9c8afbdf472345269037fb75f5c432 Mon Sep 17 00:00:00 2001 From: Harunobu Kurokawa Date: Wed, 21 Dec 2016 23:10:36 +0900 Subject: rcar-gen2: linux-renesas: Add audio patch to fix SPEC-365 Fix the issue that pulseaudio sometimes doesn't work correctly. Bug-AGL: SPEC-365 Change-Id: I30420d80691212a072d7604be6a3833c680bce1e Signed-off-by: Harunobu Kurokawa --- ...-ak4642-Replace-mdelay-function-to-msleep.patch | 29 ++++++++++++++++++++++ .../recipes-kernel/linux/linux-renesas_3.10.bb | 1 + 2 files changed, 30 insertions(+) create mode 100644 meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch 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 +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 +--- + 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_3.10.bb b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb index 010e362..4d8e332 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 = " \ -- cgit 1.2.3-korg From 2420779506844c8b8cf2f50d8f08b622dc6468f6 Mon Sep 17 00:00:00 2001 From: Toshi Umemura Date: Mon, 19 Dec 2016 17:31:24 +0900 Subject: Add exec to remove gst cache in weston.service When weston in IVI starts to send encoded display image to meter cluster, it also starts to use gstreamer to encode in CES2017 demo. If cache for gstreamer remains, weston sometimes failes to start. To avoid this, this patch adds ExecStartPre to remove the cache as a workaround. Change-Id: I0f3f90bbfbf9033452d27c1cc3ea358e0e2880f3 Signed-off-by: ynakamura --- meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service | 1 + 1 file changed, 1 insertion(+) diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service index 4e75b68..db0fd29 100644 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service @@ -4,6 +4,7 @@ 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 -- cgit 1.2.3-korg