diff options
Diffstat (limited to 'meta-rcar-gen2')
36 files changed, 8559 insertions, 394 deletions
diff --git a/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch b/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch index 846527b..0c48b09 100644 --- a/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch +++ b/meta-rcar-gen2/recipes-bsp/u-boot/u-boot/0004-uboot-porter-board-support.patch @@ -403,9 +403,9 @@ index 0000000..e6ded08 + u8 val; + + i2c_init(CONFIG_SYS_I2C_SPEED, 0); -+ i2c_read(0x58, 0x13, 1, &val, 1); ++ i2c_read(0x5a, 0x13, 1, &val, 1); + val |= 0x02; -+ i2c_write(0x58, 0x13, 1, &val, 1); ++ i2c_write(0x5a, 0x13, 1, &val, 1); +} + +enum { diff --git a/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb b/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb index 329e972..b0ddcaf 100644 --- a/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb +++ b/meta-rcar-gen2/recipes-graphics/wayland/libgbm.bb @@ -7,6 +7,10 @@ LIC_FILES_CHKSUM = "file://gbm.c;beginline=4;endline=22;md5=5cdaac262c876e98e477 SRCREV = "d5a58c689932d42add1301c5cd323da5244374af" SRC_URI = "git://github.com/thayama/libgbm;protocol=git;branch=master" +SRC_URI_append = " \ + file://0001-Add-gbm_bo_get_fd-function.patch \ +" + S = "${WORKDIR}/git" COMPATIBLE_MACHINE = "(r8a7790|r8a7791|r8a7793|r8a7794)" diff --git a/meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch b/meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch new file mode 100644 index 0000000..e688cde --- /dev/null +++ b/meta-rcar-gen2/recipes-graphics/wayland/libgbm/0001-Add-gbm_bo_get_fd-function.patch @@ -0,0 +1,90 @@ +From e6d4594481a2c7d9625d1f4abf898cd461c30c42 Mon Sep 17 00:00:00 2001 +From: Grigory Kletsko <grigory.kletsko@cogentembedded.com> +Date: Wed, 2 Nov 2016 16:17:16 +0300 +Subject: [PATCH] Add gbm_bo_get_fd() function + +--- + backend_kms.c | 8 ++++++++ + gbm.c | 15 +++++++++++++++ + gbm.h | 3 +++ + gbmint.h | 1 + + 4 files changed, 27 insertions(+) + +diff --git a/backend_kms.c b/backend_kms.c +index cfee3b0..3fc1d66 100644 +--- a/backend_kms.c ++++ b/backend_kms.c +@@ -339,6 +339,13 @@ static int gbm_kms_surface_has_free_buffers(struct gbm_surface *_surface) + return ((!surface->bo[0]->locked) || (!surface->bo[1]->locked)); + } + ++static int gbm_kms_bo_get_fd(struct gbm_bo *_bo) ++{ ++ struct gbm_kms_bo *bo = (struct gbm_kms_bo*)_bo; ++ ++ return bo->fd; ++} ++ + struct gbm_device kms_gbm_device = { + .name = "kms", + +@@ -349,6 +356,7 @@ struct gbm_device kms_gbm_device = { + .bo_import = gbm_kms_bo_import, + .bo_write = gbm_kms_bo_write, + .bo_destroy = gbm_kms_bo_destroy, ++ .bo_get_fd = gbm_kms_bo_get_fd, + + .surface_create = gbm_kms_surface_create, + .surface_lock_front_buffer = gbm_kms_surface_lock_front_buffer, +diff --git a/gbm.c b/gbm.c +index c58576d..458fac0 100644 +--- a/gbm.c ++++ b/gbm.c +@@ -470,3 +470,18 @@ gbm_surface_has_free_buffers(struct gbm_surface *surf) + { + return surf->gbm->surface_has_free_buffers(surf); + } ++ ++/** Get a DMA-BUF file descriptor for the buffer object ++ * ++ * This function creates a DMA-BUF (also known as PRIME) file descriptor ++ * handle for the buffer object. Eeach call to gbm_bo_get_fd() returns a new ++ * file descriptor and the caller is responsible for closing the file ++ * descriptor. ++ * \param bo The buffer object ++ * \return Returns a file descriptor referring to the underlying buffer ++ */ ++GBM_EXPORT int ++gbm_bo_get_fd(struct gbm_bo *bo) ++{ ++ return bo->gbm->bo_get_fd(bo); ++} +diff --git a/gbm.h b/gbm.h +index 9d2a030..ad92935 100644 +--- a/gbm.h ++++ b/gbm.h +@@ -285,6 +285,9 @@ gbm_surface_has_free_buffers(struct gbm_surface *surface); + void + gbm_surface_destroy(struct gbm_surface *surface); + ++int ++gbm_bo_get_fd(struct gbm_bo *bo); ++ + #ifdef __cplusplus + } + #endif +diff --git a/gbmint.h b/gbmint.h +index a467bea..70a8d4a 100644 +--- a/gbmint.h ++++ b/gbmint.h +@@ -70,6 +70,7 @@ struct gbm_device { + void *buffer, uint32_t usage); + int (*bo_write)(struct gbm_bo *bo, const void *buf, size_t data); + void (*bo_destroy)(struct gbm_bo *bo); ++ int (*bo_get_fd)(struct gbm_bo *_bo); + + struct gbm_surface *(*surface_create)(struct gbm_device *gbm, + uint32_t width, uint32_t height, +-- +2.7.4 + diff --git a/meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch b/meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch deleted file mode 100644 index cd8bc55..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/wayland/disable-macro-checks-not-used-for-scanner.patch +++ /dev/null @@ -1,50 +0,0 @@ -disable macro checks not used for scanner - -We only build wayland-native for the scanner, so disable the bits we don't -actually need. This avoid build issue on older distro such as Centos 5.x: -| error: 'O_CLOEXEC' undeclared (first use in this function) -| error: sys/timerfd.h: No such file or directory -| error: 'CLOCK_MONOTONIC' undeclared (first use in this function) -| error: 'TFD_CLOEXEC' undeclared (first use in this function) -| error: 'SFD_CLOEXEC' undeclared (first use in this function) - -Upstream-Status: Pending - -Signed-off-by: Ting Liu <b28495@freescale.com> ---- - configure.ac | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) - -diff --git a/configure.ac b/configure.ac ---- a/configure.ac -+++ b/configure.ac -@@ -41,16 +41,16 @@ AC_SUBST(GCC_CFLAGS) - - AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate]) - --AC_CHECK_DECL(SFD_CLOEXEC,[], -- [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland")], -- [[#include <sys/signalfd.h>]]) --AC_CHECK_DECL(TFD_CLOEXEC,[], -- [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland")], -- [[#include <sys/timerfd.h>]]) --AC_CHECK_DECL(CLOCK_MONOTONIC,[], -- [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland")], -- [[#include <time.h>]]) --AC_CHECK_HEADERS([execinfo.h]) -+##AC_CHECK_DECL(SFD_CLOEXEC,[], -+# [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland")], -+# [[#include <sys/signalfd.h>]]) -+#AC_CHECK_DECL(TFD_CLOEXEC,[], -+# [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland")], -+# [[#include <sys/timerfd.h>]]) -+#AC_CHECK_DECL(CLOCK_MONOTONIC,[], -+# [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland")], -+# [[#include <time.h>]]) -+#AC_CHECK_HEADERS([execinfo.h]) - - AC_ARG_ENABLE([scanner], - [AC_HELP_STRING([--disable-scanner], --- -1.8.3.2 - diff --git a/meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb b/meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb deleted file mode 100644 index b1ae59e..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/wayland_1.5.0.bb +++ /dev/null @@ -1,41 +0,0 @@ -SUMMARY = "Wayland, a protocol between a compositor and clients" -DESCRIPTION = "Wayland is a protocol for a compositor to talk to its clients \ -as well as a C library implementation of that protocol. The compositor can be \ -a standalone display server running on Linux kernel modesetting and evdev \ -input devices, an X application, or a wayland client itself. The clients can \ -be traditional applications, X servers (rootless or fullscreen) or other \ -display servers." -HOMEPAGE = "http://wayland.freedesktop.org" -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://COPYING;md5=1d4476a7d98dd5691c53d4d43a510c72 \ - file://src/wayland-server.c;endline=21;md5=079ae21dbf98ada52ec23744851b0a5c" - -SRC_URI = "http://wayland.freedesktop.org/releases/${BPN}-${PV}.tar.xz" -SRC_URI[md5sum] = "1d882776b27329b91d2d500b6d66dd1d" -SRC_URI[sha256sum] = "0069e1e9af888b3e05384380ad8cc6c976ea3e81d08ba19b7675ce1d693a41b5" -SRC_URI_append_class-native = " \ - file://disable-macro-checks-not-used-for-scanner.patch \ - " -EXTRA_OECONF_class-native = "--disable-documentation --enable-scanner" - -inherit autotools pkgconfig - -# We need wayland-native for the wayland-scanner utility -BBCLASSEXTEND = "native" - -DEPENDS_class-native = "expat-native libffi-native" -DEPENDS = "expat libffi wayland-native" - -EXTRA_OECONF = "--disable-documentation --disable-scanner" - -# Wayland installs a M4 macro for other projects to use, which uses the target -# pkg-config to find files. Replace pkg-config with pkg-config-native. -do_install_append_class-native() { - sed -e 's,PKG_CHECK_MODULES(.*),,g' \ - -e 's,$PKG_CONFIG,pkg-config-native,g' \ - -i ${D}/${datadir}/aclocal/wayland-scanner.m4 -} - -sysroot_stage_all_append_class-target () { - cp ${STAGING_DATADIR_NATIVE}/aclocal/wayland-scanner.m4 ${SYSROOT_DESTDIR}/${datadir}/aclocal/ -} diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch new file mode 100644 index 0000000..51c2671 --- /dev/null +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/0001-Add-virtual-output-support.patch @@ -0,0 +1,391 @@ +From f26e3ef1f484319a6c803158af16050574363587 Mon Sep 17 00:00:00 2001 +From: Grigory Kletsko <grigory.kletsko@cogentembedded.com> +Date: Wed, 2 Nov 2016 17:14:43 +0300 +Subject: [PATCH 1/2] Add virtual output support + +To enable virtual output set "virtual" property in core section +to desirable number of virtual outputs. Then add settings to +each virtual output in output sections. Name of the outputs +will be virtual1, virtual2... etc. + +Signed-off-by: Grigory Kletsko <grigory.kletsko@cogentembedded.com> +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + src/compositor-drm.c | 329 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 329 insertions(+) + +diff --git a/src/compositor-drm.c b/src/compositor-drm.c +index ab493ad..09611a4 100644 +--- a/src/compositor-drm.c ++++ b/src/compositor-drm.c +@@ -193,6 +193,11 @@ struct drm_output { + + struct vaapi_recorder *recorder; + struct wl_listener recorder_frame_listener; ++ ++ /* not real output device */ ++ int virtual; ++ /* Timer for updating frame */ ++ struct wl_event_source *virtual_finish_frame_timer; + }; + + /* +@@ -1337,6 +1342,33 @@ drm_output_destroy(struct weston_output *output_base) + free(output); + } + ++static void ++virtual_output_destroy(struct weston_output *output_base) ++{ ++ struct drm_output *output = (struct drm_output *) output_base; ++ struct drm_backend *c = ++ (struct drm_backend *) output->base.compositor; ++ ++ c->crtc_allocator &= ~(1 << output->crtc_id); ++ c->connector_allocator &= ~(1 << output->connector_id); ++ ++ if (c->use_pixman) { ++ drm_output_fini_pixman(output); ++ } else { ++ gl_renderer->output_destroy(output_base); ++ gbm_surface_destroy(output->surface); ++ } ++ ++ weston_plane_release(&output->fb_plane); ++ weston_plane_release(&output->cursor_plane); ++ ++ weston_output_destroy(&output->base); ++ ++ ++ wl_event_source_remove(output->virtual_finish_frame_timer); ++ free(output); ++} ++ + /** + * Find the closest-matching mode for a given target + * +@@ -2459,6 +2491,289 @@ err_free: + } + + static void ++virtual_output_start_repaint_loop(struct weston_output *output) ++{ ++ struct timespec now; ++ ++ weston_compositor_read_presentation_clock(output->compositor, &now); ++ weston_output_finish_frame(output, &now, PRESENTATION_FEEDBACK_INVALID); ++} ++ ++ ++static int ++virtual_output_repaint(struct weston_output *output_base, ++ pixman_region32_t *damage) ++{ ++ struct drm_output *output = (struct drm_output *) output_base; ++ struct timespec ts; ++ uint32_t msec_next; ++ uint32_t msec_current; ++ ++ msec_next = (output->base.frame_time + 1000000UL / output->base.current_mode->refresh) ; ++ ++ if (output->destroy_pending) ++ return -1; ++ ++ if (!output->next) ++ drm_output_render(output, damage); ++ if (!output->next) ++ return -1; ++ ++ drm_output_set_cursor(output); ++ ++ output->page_flip_pending = 1; ++ ++ weston_compositor_read_presentation_clock(output_base->compositor, &ts); ++ ++ msec_current = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; ++ ++ /* ++ * If we somehow late with updating frame, then fireup timer immediately (1 msec) ++ */ ++ wl_event_source_timer_update(output->virtual_finish_frame_timer, (msec_next > msec_current) ? ++ msec_next - msec_current : 1); ++ ++ return 0; ++} ++ ++static int ++virtual_finish_frame_handler(void *data) ++{ ++ struct drm_output *output = (struct drm_output *) data; ++ struct timespec ts; ++ ++ /* We don't set page_flip_pending on start_repaint_loop, in that case ++ * we just want to page flip to the current buffer to get an accurate ++ * timestamp */ ++ if (output->page_flip_pending) { ++ drm_output_release_fb(output, output->current); ++ output->current = output->next; ++ output->next = NULL; ++ } ++ ++ output->page_flip_pending = 0; ++ ++ if (output->destroy_pending) ++ drm_output_destroy(&output->base); ++ else if (!output->vblank_pending) { ++ weston_compositor_read_presentation_clock(output->base.compositor, &ts); ++ ++ weston_output_finish_frame(&output->base, &ts, ++ PRESENTATION_FEEDBACK_INVALID); ++ ++ /* We can't call this from frame_notify, because the output's ++ * repaint needed flag is cleared just after that */ ++ if (output->recorder) ++ weston_output_schedule_repaint(&output->base); ++ } ++ ++ return 1; ++} ++ ++/* ++ * Virtual output connector that could be used for simulating output ++ * device for clients and/or streaming of video ++ */ ++static int ++create_output_for_virtual_connector(struct drm_backend *ec, ++ int x, int y, struct udev_device *drm_device) ++{ ++ struct wl_event_loop *loop; ++ static int virtual_id = 1; /* as other outputs numbered */ ++ struct drm_output *output; ++ struct drm_mode *drm_mode, *next, *configured; ++ struct weston_mode *m; ++ struct weston_config_section *section; ++ drmModeModeInfo modeline; ++ int i, width, height, scale, fps; ++ int recorded_output; ++ char name[32], *s; ++ enum output_config config; ++ uint32_t transform; ++ ++ output = zalloc(sizeof *output); ++ if (output == NULL) ++ return -1; ++ ++ output->base.subpixel = WL_OUTPUT_SUBPIXEL_NONE; //drm_subpixel_to_wayland(connector->subpixel); ++ output->base.make = "CogentEmbedded,Inc"; ++ output->base.serial_number = ""; ++ wl_list_init(&output->base.mode_list); ++ ++ snprintf(name, 32, "virtual%d", virtual_id++); ++ output->base.name = strdup(name); ++ ++ section = weston_config_get_section(ec->compositor->config, "output", "name", ++ output->base.name); ++ ++ weston_config_section_get_bool(section, "recorder", &recorded_output, 0); ++ if (recorded_output) { ++ char model[64]; ++ char *ip; ++ int port; ++ ++ weston_config_section_get_string(section, "ip", &ip, "<nil>"); ++ weston_config_section_get_int(section, "port", &port, -1); ++ snprintf(model, 64, "Virtual RTP %s:%d", ip, port); ++ output->base.model = strdup(model); ++ } else { ++ output->base.model = "Virtual Display"; ++ } ++ ++ weston_config_section_get_string(section, "mode", &s, "preferred"); ++ if (strcmp(s, "off") == 0) ++ config = OUTPUT_CONFIG_OFF; ++ else if (sscanf(s, "%dx%d@%d", &width, &height, &fps) == 3) ++ config = OUTPUT_CONFIG_MODE; ++ else if (parse_modeline(s, &modeline) == 0) ++ config = OUTPUT_CONFIG_MODELINE; ++ else { ++ weston_log("Invalid mode \"%s\" for output %s\n", ++ s, output->base.name); ++ width = 1280; ++ height = 720; ++ fps = 60; ++ config = OUTPUT_CONFIG_MODE; ++ } ++ free(s); ++ ++ weston_config_section_get_int(section, "scale", &scale, 1); ++ weston_config_section_get_string(section, "transform", &s, "normal"); ++ if (weston_parse_transform(s, &transform) < 0) ++ weston_log("Invalid transform \"%s\" for output %s\n", ++ s, output->base.name); ++ free(s); ++ ++ if (get_gbm_format_from_section(section, ++ ec->format, ++ &output->format) == -1) ++ output->format = ec->format; ++ ++ weston_config_section_get_string(section, "seat", &s, ""); ++ setup_output_seat_constraint(ec, &output->base, s); ++ free(s); ++ ++ output->pipe = i; ++ ec->crtc_allocator |= (1 << output->crtc_id); ++ output->connector_id = 0; ++ ec->connector_allocator |= (1 << output->connector_id); ++ ++ /* this is virtual output */ ++ output->virtual = 1; ++ ++ ++ output->original_crtc = NULL; ++ output->dpms_prop = NULL; ++ ++ /* set static mode */ ++ if (1) { ++ /* TODO: calculate proper mode settings to get desirable framerate */ ++ drmModeModeInfo static_drm_mode = { ++ width * height * fps, ++ width, 0, 0, width, width, ++ height, 0, 0, height, height, ++ fps * 1000, ++ 0, //flags ++ 0, //type ++ "virtual" ++ }; ++ ++ drm_mode = drm_output_add_mode(output, &static_drm_mode); ++ if (!drm_mode) ++ goto err_free; ++ ++ drm_mode->base.refresh = fps * 1000; ++ } ++ ++ if (config == OUTPUT_CONFIG_OFF) { ++ weston_log("Disabling output %s\n", output->base.name); ++ drmModeSetCrtc(ec->drm.fd, output->crtc_id, ++ 0, 0, 0, 0, 0, NULL); ++ goto err_free; ++ } ++ ++ wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) { ++ if (config == OUTPUT_CONFIG_MODE && ++ width == drm_mode->base.width && ++ height == drm_mode->base.height) ++ configured = drm_mode; ++ } ++ ++ output->base.current_mode = &configured->base; ++ ++ if (output->base.current_mode == NULL) { ++ weston_log("no available modes for %s\n", output->base.name); ++ goto err_free; ++ } ++ ++ output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT; ++ ++ weston_output_init(&output->base, ec->compositor, x, y, ++ 100, 100 * height / width, /* FIXME: calculate proper mm_width and mm_height */ ++ transform, scale); ++ ++ if (ec->use_pixman) { ++ if (drm_output_init_pixman(output, ec) < 0) { ++ weston_log("Failed to init output pixman state\n"); ++ goto err_output; ++ } ++ } else if (drm_output_init_egl(output, ec) < 0) { ++ weston_log("Failed to init output gl state\n"); ++ goto err_output; ++ } ++ ++ output->backlight = NULL; ++ ++ weston_compositor_add_output(ec->compositor, &output->base); ++ ++ output->base.connection_internal = 1; ++ ++ loop = wl_display_get_event_loop(ec->compositor->wl_display); ++ output->virtual_finish_frame_timer = wl_event_loop_add_timer(loop, virtual_finish_frame_handler, output); ++ ++ output->base.start_repaint_loop = virtual_output_start_repaint_loop; ++ output->base.repaint = virtual_output_repaint; ++ output->base.destroy = virtual_output_destroy; ++ output->base.assign_planes = NULL; ++ output->base.set_backlight = NULL; ++ output->base.set_dpms = NULL; ++ output->base.switch_mode = drm_output_switch_mode; ++ ++ output->base.gamma_size = 0; ++ output->base.set_gamma = drm_output_set_gamma; ++ ++ weston_plane_init(&output->cursor_plane, ec->compositor, 0, 0); ++ weston_plane_init(&output->fb_plane, ec->compositor, 0, 0); ++ ++ weston_compositor_stack_plane(ec->compositor, &output->cursor_plane, NULL); ++ weston_compositor_stack_plane(ec->compositor, &output->fb_plane, ++ &ec->compositor->primary_plane); ++ ++ weston_log("Output %s, ()\n", ++ output->base.name); ++ wl_list_for_each(m, &output->base.mode_list, link) ++ weston_log_continue(STAMP_SPACE "mode %dx%d@%.1f\n", ++ m->width, m->height, m->refresh / 1000.0); ++ ++ return 0; ++ ++err_output: ++ weston_output_destroy(&output->base); ++err_free: ++ wl_list_for_each_safe(drm_mode, next, &output->base.mode_list, ++ base.link) { ++ wl_list_remove(&drm_mode->base.link); ++ free(drm_mode); ++ } ++ ++ ec->crtc_allocator &= ~(1 << output->crtc_id); ++ ec->connector_allocator &= ~(1 << output->connector_id); ++ free(output); ++ ++ return -1; ++} ++ ++static void + create_sprites(struct drm_backend *b) + { + struct drm_sprite *sprite; +@@ -2531,10 +2846,12 @@ static int + create_outputs(struct drm_backend *b, uint32_t option_connector, + struct udev_device *drm_device) + { ++ struct weston_config_section *section; + drmModeConnector *connector; + drmModeRes *resources; + int i; + int x = 0, y = 0; ++ int virtual; + + resources = drmModeGetResources(b->drm.fd); + if (!resources) { +@@ -2580,6 +2897,18 @@ create_outputs(struct drm_backend *b, uint32_t option_connector, + drmModeFreeConnector(connector); + } + ++ section = weston_config_get_section(b->compositor->config, "core", NULL, NULL); ++ weston_config_section_get_int(section, "virtual", &virtual, 0); ++ ++ for (i = 0; i < virtual; i++) { ++ if (create_output_for_virtual_connector(b, x, y, ++ drm_device) < 0) ++ continue; ++ x += container_of(b->compositor->output_list.prev, ++ struct weston_output, ++ link)->width; ++ } ++ + if (wl_list_empty(&b->compositor->output_list)) { + weston_log("No currently active connector found.\n"); + drmModeFreeResources(resources); +-- +2.7.4 + diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch new file mode 100644 index 0000000..2683afe --- /dev/null +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/0002-Get-DMA-fd-on-bo.patch @@ -0,0 +1,33 @@ +From 6c320c1319f0ef0888d9a9be614c13e5756a3228 Mon Sep 17 00:00:00 2001 +From: Grigory Kletsko <grigory.kletsko@cogentembedded.com> +Date: Wed, 2 Nov 2016 17:16:29 +0300 +Subject: [PATCH 2/2] Get DMA fd on bo + +--- + src/compositor-drm.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/compositor-drm.c b/src/compositor-drm.c +index 09611a4..85c7d0a 100644 +--- a/src/compositor-drm.c ++++ b/src/compositor-drm.c +@@ -143,6 +143,7 @@ struct drm_fb { + struct drm_output *output; + uint32_t fb_id, stride, handle, size; + int fd; ++ int dmafd; + int is_client_buffer; + struct weston_buffer_reference buffer_ref; + +@@ -411,6 +412,8 @@ drm_fb_get_from_bo(struct gbm_bo *bo, + goto err_free; + } + ++ fb->dmafd = gbm_bo_get_fd(bo); ++ + gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback); + + return fb; +-- +2.7.4 + diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch new file mode 100644 index 0000000..e6eccc6 --- /dev/null +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch @@ -0,0 +1,4183 @@ +From 73a18d5178129591b0c58d0f0150ce0a62d7c39a Mon Sep 17 00:00:00 2001 +From: Grigory Kletsko <grigory.kletsko@cogentembedded.com> +Date: Wed, 2 Nov 2016 17:36:41 +0300 +Subject: [PATCH] Add gst-recorder for h264 output streaming + +To use gst-recorder run weston with arg --gst-record. Add +recorder=true property to output section of one of the outputs +(real or virtual). Use properties ip, port to set host of RTP +stream. Use property bitrate to set desirable maximum h264 bitrate. + +Signed-off-by: Grigory Kletsko <grigory.kletsko@cogentembedded.com> +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + Makefile.am | 17 + + configure.ac | 11 + + src/compositor-drm.c | 202 ++++++- + src/gst-recorder.c | 1222 +++++++++++++++++++++++++++++++++++++++++ + src/gst-recorder.h | 58 ++ + src/media-ctl/libmediactl.c | 955 ++++++++++++++++++++++++++++++++ + src/media-ctl/libv4l2subdev.c | 759 +++++++++++++++++++++++++ + src/media-ctl/mediactl-priv.h | 64 +++ + src/media-ctl/mediactl.h | 423 ++++++++++++++ + src/media-ctl/tools.h | 32 ++ + src/media-ctl/v4l2subdev.h | 258 +++++++++ + 11 files changed, 3999 insertions(+), 2 deletions(-) + create mode 100644 src/gst-recorder.c + create mode 100644 src/gst-recorder.h + create mode 100644 src/media-ctl/libmediactl.c + create mode 100644 src/media-ctl/libv4l2subdev.c + create mode 100644 src/media-ctl/mediactl-priv.h + create mode 100644 src/media-ctl/mediactl.h + create mode 100644 src/media-ctl/tools.h + create mode 100644 src/media-ctl/v4l2subdev.h + +diff --git a/Makefile.am b/Makefile.am +index 62719c9..1624e7c 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -267,6 +267,23 @@ drm_backend_la_SOURCES += src/vaapi-recorder.c src/vaapi-recorder.h + drm_backend_la_LIBADD += $(LIBVA_LIBS) + drm_backend_la_CFLAGS += $(LIBVA_CFLAGS) + endif ++ ++if ENABLE_GST_RECORDER ++drm_backend_la_SOURCES += \ ++ src/gst-recorder.c \ ++ src/gst-recorder.h \ ++ src/v4l2-device.h \ ++ src/media-ctl/libmediactl.c \ ++ src/media-ctl/libv4l2subdev.c \ ++ src/media-ctl/mediactl-priv.h \ ++ src/media-ctl/mediactl.h \ ++ src/media-ctl/tools.h \ ++ src/media-ctl/v4l2subdev.h ++drm_backend_la_LIBADD += $(LIBVA_LIBS) \ ++ -lgstallocators-1.0 \ ++ -lgstvideo-1.0 ++drm_backend_la_CFLAGS += $(LIBVA_CFLAGS) ++endif + endif + + if ENABLE_WAYLAND_COMPOSITOR +diff --git a/configure.ac b/configure.ac +index b8fd67f..5040813 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -321,6 +321,17 @@ if test x$enable_vaapi_recorder != xno; then + fi + AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$have_libva" = xyes) + ++AC_ARG_ENABLE(gst-recorder, [ --enable-gst-recorder],, ++ enable_gst_recorder=yes) ++if test x$enable_gst_recorder != xno; then ++ AC_DEFINE([BUILD_GST_RECORDER], [1], [Build the gst recorder]) ++ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0 >= 1.0]) ++ ++ CPPFLAGS="$CPPFLAGS $GSTREAMER_CFLAGS" ++ LIBS="$LIBS $GSTREAMER_LIBS -lgstapp-1.0" ++fi ++AM_CONDITIONAL(ENABLE_GST_RECORDER, test "x$enable_gst_recorder" != xno) ++ + + AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes) + if test x$have_jpeglib = xyes; then +diff --git a/src/compositor-drm.c b/src/compositor-drm.c +index 85c7d0a..4dd0dfa 100644 +--- a/src/compositor-drm.c ++++ b/src/compositor-drm.c +@@ -55,6 +55,7 @@ + #include "libinput-seat.h" + #include "launcher-util.h" + #include "vaapi-recorder.h" ++#include "gst-recorder.h" + #include "presentation_timing-server-protocol.h" + #include "linux-dmabuf.h" + +@@ -124,6 +125,8 @@ struct drm_backend { + + int use_pixman; + ++ int enable_recorder; ++ + uint32_t prev_state; + + struct udev_input input; +@@ -192,7 +195,12 @@ struct drm_output { + int current_image; + pixman_region32_t previous_damage; + ++#ifdef BUILD_VAAPI_RECORDER + struct vaapi_recorder *recorder; ++#endif ++#ifdef BUILD_GST_RECORDER ++ struct gst_recorder *recorder; ++#endif + struct wl_listener recorder_frame_listener; + + /* not real output device */ +@@ -230,6 +238,7 @@ struct drm_parameters { + int connector; + int tty; + int use_pixman; ++ int enable_recorder; + const char *seat_id; + }; + +@@ -2140,6 +2149,23 @@ parse_modeline(const char *s, drmModeModeInfo *mode) + return 0; + } + ++static int parse_crop_rect(const char *s, struct v4l2_rect* crop) ++{ ++ crop->left = 0; ++ crop->top = 0; ++ crop->width = 0; ++ crop->height = 0; ++ ++ if (sscanf(s, "%dx%d@%dx%d", ++ &crop->width, ++ &crop->height, ++ &crop->top, ++ &crop->left) != 4) ++ return -1; ++ ++ return 0; ++} ++ + static void + setup_output_seat_constraint(struct drm_backend *b, + struct weston_output *output, +@@ -3320,7 +3346,170 @@ recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, + recorder_destroy(output); + } + } +-#else ++#endif ++ ++#ifdef BUILD_GST_RECORDER ++static void ++recorder_destroy(struct drm_backend *c, struct drm_output *output) ++{ ++ wl_list_remove(&output->recorder_frame_listener.link); ++ ++ gst_recorder_destroy(output->recorder); ++ output->recorder = NULL; ++ ++ output->base.disable_planes--; ++ ++ weston_log("[gst recorder] done\n"); ++} ++ ++static void ++recorder_frame_notify(struct wl_listener *listener, void *data) ++{ ++ int ret = 0; ++ struct drm_output *output; ++ struct drm_backend *c; ++ ++ ++ output = container_of(listener, struct drm_output, ++ recorder_frame_listener); ++ c = (struct drm_backend *) output->base.compositor; ++ ++ if (!output->recorder) { ++ weston_log("%s: output have no recorder enabled\n", ++ output->base.name); ++ return; ++ } ++ ++ if (!output->current) { ++ weston_log("%s: frame notify while current frame == NULL\n", ++ output->base.name); ++ return; ++ } ++ ++ if (output->current->dmafd) { ++ ret = gst_recorder_frame_dmafd(output->recorder, output->current->dmafd, ++ output->current->stride); ++ } ++ ++ if (ret < 0) { ++ weston_log("[gst recorder] aborted: %m\n"); ++ recorder_destroy(c, output); ++ } ++} ++ ++static int ++recorder_enable(struct drm_backend *c, struct drm_output *output) ++{ ++ int enable_recorder = 0; ++ struct gst_recorder_settings *settings; ++ struct weston_config_section *section; ++ char* s; ++ struct v4l2_rect crop = { .width = output->base.current_mode->width, ++ .height = output->base.current_mode->height, ++ .top = 0, ++ .left = 0 }; ++ ++ if (output->recorder) ++ return -1; ++ ++ section = weston_config_get_section(c->compositor->config, "output", "name", ++ output->base.name); ++ ++ weston_config_section_get_bool(section, "recorder", &enable_recorder, 0); ++ ++ if (!enable_recorder) ++ return 0; ++ ++ /* TODO: add support for NV16 or NV12 */ ++ if (output->format != GBM_FORMAT_XRGB8888) { ++ weston_log("[gst recorder] %s: " ++ "output format not supported\n", output->base.name); ++ return -1; ++ } ++ ++ settings = malloc(sizeof(* settings)); ++ weston_config_section_get_string(section, "ip", &settings->ip, NULL); ++ if (!settings->ip) ++ goto err; ++ weston_config_section_get_int(section, "port", &settings->port, -1); ++ /* default gives about 16 Mbit/s at 1280x720@60FPS */ ++ weston_config_section_get_int(section, "bitrate", &settings->bitrate, 300000); ++ ++ settings->width = output->base.current_mode->width; ++ settings->height = output->base.current_mode->height; ++ ++ settings->crop = crop; ++ ++ weston_config_section_get_string(section, "crop", &s, NULL); ++ if (s) { ++ if (parse_crop_rect(s, &settings->crop)) { ++ weston_log("[gst recorder] %s:" ++ " failed to parse crop parameter\n", ++ output->base.name); ++ goto err; ++ } ++ } ++ ++ ++ output->recorder = ++ gst_recorder_create(settings); ++ if (!output->recorder) { ++ weston_log("[gst recorder] %s:" ++ " failed to create gst recorder\n", ++ output->base.name); ++ goto err; ++ } ++ ++ output->base.disable_planes++; ++ ++ output->recorder_frame_listener.notify = recorder_frame_notify; ++ wl_signal_add(&output->base.frame_signal, ++ &output->recorder_frame_listener); ++ ++ weston_output_schedule_repaint(&output->base); ++ ++ weston_log("[gst recorder] %s:" ++ " recorder initialized\n", ++ output->base.name); ++ ++ return 0; ++err: ++ weston_log("[gst recorder] %s:" ++ " invalid settings\n", ++ output->base.name); ++ free(settings); ++ return -1; ++} ++ ++static void ++recorders_enable(struct drm_backend *c) ++{ ++ struct drm_output *output; ++ ++ wl_list_for_each(output, &c->compositor->output_list, base.link) { ++ recorder_enable(c, output); ++ } ++} ++ ++static void ++recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, ++ void *data) ++{ ++ struct drm_backend *c = data; ++ struct drm_output *output; ++ ++ /* fix this */ ++ wl_list_for_each(output, &c->compositor->output_list, base.link) { ++ if (!output->recorder) ++ recorder_enable(c, output); ++ else ++ recorder_destroy(c, output); ++ } ++} ++#endif ++ ++#if !defined(BUILD_VAAPI_RECORDER) && !defined(BUILD_GST_RECORDER) ++ + static void + recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, + void *data) +@@ -3422,7 +3611,8 @@ drm_backend_create(struct weston_compositor *compositor, + goto err_base; + + b->use_pixman = param->use_pixman; +- ++ b->enable_recorder = param->enable_recorder; ++ + /* Check if we run drm-backend using weston-launch */ + compositor->launcher = weston_launcher_connect(compositor, param->tty, + param->seat_id, true); +@@ -3465,6 +3655,10 @@ drm_backend_create(struct weston_compositor *compositor, + } + } + ++#ifdef BUILD_GST_RECORDER ++ gst_recorder_init(); ++#endif ++ + b->base.destroy = drm_destroy; + b->base.restore = drm_restore; + +@@ -3529,6 +3723,9 @@ drm_backend_create(struct weston_compositor *compositor, + recorder_binding, b); + weston_compositor_add_debug_binding(compositor, KEY_W, + renderer_switch_binding, b); ++ /* enable GST recording-streaming */ ++ if (b->enable_recorder) ++ recorders_enable(b); + + if (compositor->renderer->import_dmabuf) { + if (linux_dmabuf_setup(compositor) < 0) +@@ -3576,6 +3773,7 @@ backend_init(struct weston_compositor *compositor, int *argc, char *argv[], + { WESTON_OPTION_INTEGER, "tty", 0, ¶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 <stdlib.h> ++#include <stdint.h> ++#include <string.h> ++#include <unistd.h> ++#include <assert.h> ++#include <errno.h> ++ ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <sys/mman.h> ++#include <sys/ioctl.h> ++#include <fcntl.h> ++ ++#include <pthread.h> ++ ++#include "compositor.h" ++#include "gst-recorder.h" ++ ++/* VSP includes */ ++#include <mediactl/mediactl.h> ++#include <mediactl/v4l2subdev.h> ++ ++/* Gstreamer includes */ ++#include <gst/gst.h> ++#include <gst/app/gstappsrc.h> ++#include <gst/rtp/gstrtpbuffer.h> ++ ++#include <gst/video/video.h> ++#include <gst/video/gstvideometa.h> ++#include <gst/video/gstvideopool.h> ++#include <gst/allocators/gstdmabuf.h> ++ ++struct vsp_data; ++ ++#define DEFAULT_FPS 60 ++ ++typedef enum _vsp_port_n { ++ VSP_PORT_INPUT = 0, ++ VSP_PORT_INPUT0 = VSP_PORT_INPUT, ++ VSP_PORT_INPUT1, ++ VSP_PORT_INPUT2, ++ VSP_PORT_INPUT3, ++ VSP_PORT_OUTPUT ++} vsp_port_n; ++ ++struct gst_recorder { ++ struct gst_recorder_settings *set; ++ int frame_count; ++ int input_count; ++ ++ int error; ++ int destroying; ++ pthread_t worker_thread; ++ pthread_mutex_t mutex; ++ pthread_cond_t input_cond; ++ ++ struct { ++ int valid; ++ int prime_fd, stride; ++ } input; ++ ++ /* GLib */ ++ GMainContext *gcontext; ++ /* Gstreamer stuff */ ++ GstElement *pipeline; ++ /* AppSrc */ ++ GstAppSrc *appsrc; ++ /* ...and source pad */ ++ GstPad *appsrc_pad; ++ /* OMX encoder buffer pool */ ++ GstBufferPool *omx_pool; ++ /* bus */ ++ GstBus *bus; ++ /* timestamp */ ++ GstClockTime timestamp; ++ uint32_t ts_last_frame; ++ /* to be removed */ ++ guint callback_tag; ++ ++ struct vsp_data *vsp; ++}; ++ ++/******************************************************************************* ++ * VSP related code ++ ******************************************************************************/ ++ ++#define VSP_OUTPUT_BUFFERS_PLANE 2 ++ ++/* #define VSP_OUTPUT_NV16 1 */ ++ ++/* ...number of input/output pads (WPF1-3 are not implemented) */ ++#define VSP_PADS_NUM 1 ++ ++/******************************************************************************* ++ * Local types definition ++ ******************************************************************************/ ++ ++struct vsp_media_pad ++{ ++ struct media_pad *infmt_pad; ++ struct media_pad *outfmt_pad; ++ struct media_entity *entity; ++ int fd; ++}; ++ ++typedef struct vsp_media_pad vsp_media_pad_t; ++ ++struct vsp_data ++{ ++ /* ...media device */ ++ struct media_device *media; ++ ++ /* ...VSP input/output pads */ ++ vsp_media_pad_t input, output; ++ ++ /* mutex */ ++ pthread_mutex_t mutex; ++ ++ /* user count */ ++ int users; ++}; ++ ++struct vsp_data *vsp_g = NULL; ++ ++/* ...type declarations */ ++typedef struct vsp_data vsp_data_t; ++ ++static int ++gst_recorder_process_dmafd(struct gst_recorder *r, int fd, int stride); ++ ++/* ...module initialization (a bit of salami) */ ++static vsp_data_t * ++vsp_init(const char *devname) ++{ ++ vsp_data_t *vsp; ++ struct media_device *media; ++ const struct media_device_info *info; ++ const char *dev; ++ char buf[256]; ++ char *endp, *p; ++ struct media_entity *entity; ++ ++ /* ...create data structure */ ++ if ((vsp = malloc(sizeof(*vsp))) == NULL) ++ { ++ weston_log("failed to allocate memory"); ++ errno = ENOMEM; ++ return NULL; ++ } ++ memset(vsp, 0, sizeof(*vsp)); ++ ++ pthread_mutex_init(&vsp->mutex, NULL); ++ ++ /* ...create media device */ ++ if ((vsp->media = media = media_device_new(devname)) == NULL) ++ { ++ weston_log("failed to open device '%s'\n", devname); ++ goto error; ++ } ++ else if ((errno = -media_device_enumerate(media)) != 0) ++ { ++ weston_log("failed to enumerate device '%s'\n", devname); ++ goto error_media; ++ } ++ else if ((info = media_get_info(media)) == NULL) ++ { ++ weston_log("failed to get media info data\n"); ++ goto error_media; ++ } ++ else ++ { ++ dev = ((p = strchr(info->bus_info, ':')) ? p + 1 : info->bus_info); ++ weston_log("open media device: %s (%s)\n", info->bus_info, dev); ++ } ++ ++ /* ...reset links */ ++ if (media_reset_links(media) != 0) ++ { ++ weston_log("failed to reset media device\n"); ++ goto error_media; ++ } ++ ++ /* ...setup RPF.0:1 -> WPF.0:0 link */ ++ snprintf(buf, sizeof(buf), "'%s rpf.0':1 -> '%s wpf.0':0 [1]", dev, dev); ++ if (media_parse_setup_link(media, buf, &endp) != 0) ++ { ++ weston_log("failed to setup link '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ /* ...setup WPF.0:1 -> WPF.0 output link */ ++ snprintf(buf, sizeof(buf), "'%s wpf.0':1 -> '%s wpf.0 output':0 [1]", dev, dev); ++ if (media_parse_setup_link(media, buf, &endp) != 0) ++ { ++ weston_log("failed to setup link '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ /* ...specify input/output-format of RPF pad */ ++ snprintf(buf, sizeof(buf), "'%s rpf.0':0", dev); ++ if ((vsp->input.infmt_pad = media_parse_pad(media, buf, NULL)) == NULL) ++ { ++ weston_log("failed to parse pad '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ snprintf(buf, sizeof(buf), "'%s rpf.0':1", dev); ++ if ((vsp->input.outfmt_pad = media_parse_pad(media, buf, NULL)) == NULL) ++ { ++ weston_log("failed to parse pad '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ snprintf(buf, sizeof(buf), "%s rpf.0", dev); ++ if ((vsp->input.entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL) ++ { ++ weston_log("failed to parse entity '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ /* ...get input file-descriptor */ ++ snprintf(buf, sizeof(buf), "%s rpf.0 input", dev); ++ if ((entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL) ++ { ++ weston_log("entity '%s' not found\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ else if (v4l2_subdev_open(entity) != 0) ++ { ++ weston_log("failed to open subdev '%s'\n", buf); ++ goto error_media; ++ } ++ else if ((vsp->input.fd = open(media_entity_get_devname(entity), O_RDWR/* | O_NONBLOCK*/)) < 0) ++ { ++ weston_log("failed to open device '%s'\n", media_entity_get_devname(entity)); ++ goto error_media; ++ } ++ else ++ { ++ weston_log("input pad setup ('%s':'%s')\n", buf, media_entity_get_devname(entity)); ++ } ++ ++ /* ...specify input/output formats of WPF pad */ ++ snprintf(buf, sizeof(buf), "'%s wpf.0':0", dev); ++ if ((vsp->output.infmt_pad = media_parse_pad(media, buf, NULL)) == NULL) ++ { ++ weston_log("failed to parse pad '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ snprintf(buf, sizeof(buf), "'%s wpf.0':1", dev); ++ if ((vsp->output.outfmt_pad = media_parse_pad(media, buf, NULL)) == NULL) ++ { ++ weston_log("failed to parse pad '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ snprintf(buf, sizeof(buf), "%s wpf.0", dev); ++ if ((vsp->output.entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL) ++ { ++ weston_log("failed to parse entity '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ ++ /* ...get a file descriptor for the output */ ++ snprintf(buf, sizeof(buf), "%s wpf.0 output", dev); ++ if ((entity = media_get_entity_by_name(media, buf, strlen(buf))) == NULL) ++ { ++ weston_log("failed to get entity '%s'\n", buf); ++ errno = EINVAL; ++ goto error_media; ++ } ++ else if (v4l2_subdev_open(entity) != 0) ++ { ++ weston_log("failed to open subdev '%s'\n", buf); ++ goto error_media; ++ } ++ else if ((vsp->output.fd = open(media_entity_get_devname(entity), O_RDWR | O_NONBLOCK)) < 0) ++ { ++ weston_log("failed to open device '%s'\n", media_entity_get_devname(entity)); ++ goto error_media; ++ } ++ else ++ { ++ weston_log("output pad setup (%s:%s)\n", buf, media_entity_get_devname(entity)); ++ } ++ ++ weston_log("vsp-device '%s' created\n", devname); ++ ++ return vsp; ++ ++error_media: ++ /* ...destroy media device and all associated structures */ ++ media_device_unref(vsp->media); ++ ++error: ++ /* ...destroy data structure */ ++ free(vsp); ++ return NULL; ++} ++ ++static void ++vsp_deinit(vsp_data_t *vsp) ++{ ++ /* ...destroy media device and all associated structures */ ++ media_device_unref(vsp->media); ++ ++ /* ...destroy data structure */ ++ free(vsp); ++} ++ ++/* ...set V4L2 device format */ ++static int ++vsp_set_format(int fd, struct v4l2_format *fmt) ++{ ++ __u32 w, h, c; ++ ++ /* ...set format */ ++ if (ioctl(fd, VIDIOC_S_FMT, fmt) < 0) { ++ weston_log("format set (fd=%d) failed: %d\n", ++ fd, errno); ++ } ++ ++ /* ...it could get changed */ ++ w = fmt->fmt.pix_mp.width; ++ h = fmt->fmt.pix_mp.height; ++ c = fmt->fmt.pix_mp.pixelformat; ++ /* weston_log("VSP: format set (fd=%d): %u*%u ('%c%c%c%c')\n", */ ++ /* fd, w, h, (__u8)c, (__u8)(c >> 8), (__u8)(c >> 16), (__u8)(c >> 24)); */ ++ ++ return 0; ++} ++ ++/* ...start streaming on specific V4L2 device */ ++static int ++vsp_streaming_enable(vsp_data_t *vsp, vsp_port_n port, int enable) ++{ ++ vsp_media_pad_t *pad = (port == VSP_PORT_INPUT) ? &vsp->input : &vsp->output; ++ int fd = pad->fd; ++ int type = (port == VSP_PORT_INPUT) ? ++ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ ++ return ioctl(fd, (enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type); ++} ++ ++/* ...prepare VSP filter for operation */ ++static int ++vsp_set_formats(vsp_data_t *vsp, int width, int height, struct v4l2_rect* crop) ++{ ++ vsp_media_pad_t *input = &vsp->input, *output = &vsp->output; ++ struct v4l2_mbus_framefmt mfmt = { .width = width, .height = height }; ++ struct v4l2_format format; ++ ++ /* ...configure RPF input pads; specify pixel format and size (one of YUV variants) */ ++ mfmt.width = width; ++ mfmt.height = height; ++ mfmt.code = V4L2_MBUS_FMT_ARGB8888_1X32; ++ if (v4l2_subdev_set_format(input->infmt_pad->entity, ++ &mfmt, input->infmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) { ++ weston_log("VSP: input pad in format set failed: %d\n", errno); ++ return -1; ++ } ++ ++ /* set a crop paramters */ ++ if (v4l2_subdev_set_selection(input->infmt_pad->entity, crop, input->infmt_pad->index, ++ V4L2_SEL_TGT_CROP, V4L2_SUBDEV_FORMAT_ACTIVE)) { ++ weston_log("set crop parameter failed: %dx%d@(%d,%d).\n", ++ crop->width, crop->height, crop->left, crop->top); ++ return -1; ++ } ++ ++ /* ...output is NV12 or NV16 or I420*/ ++ mfmt.width = crop->width; ++ mfmt.height = crop->height; ++ mfmt.code = V4L2_MBUS_FMT_AYUV8_1X32; ++ if (v4l2_subdev_set_format(input->outfmt_pad->entity, ++ &mfmt, input->outfmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) { ++ weston_log("VSP: input pad out format set failed: %d\n", errno); ++ return -1; ++ } ++ ++ /* ...specify input format */ ++ memset(&format, 0, sizeof(format)); ++ format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ format.fmt.pix_mp.width = /* crop-> */width; ++ format.fmt.pix_mp.height = /* crop-> */height; ++ format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_ABGR32; ++ format.fmt.pix_mp.num_planes = 1; ++ /* ...set input port format */ ++ if (vsp_set_format(input->fd, &format) < 0) { ++ weston_log("VSP: input set format failed: %d\n", errno); ++ return -1; ++ } ++ ++ /* ...both input and output are ARGB8888 always (now effective) */ ++ if (v4l2_subdev_set_format(output->infmt_pad->entity, ++ &mfmt, output->infmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) { ++ weston_log("VSP: output pad in format set failed: %d\n", errno); ++ return -1; ++ } ++ ++ /* ...specify cropping area, probably? - tbd */ ++ if (v4l2_subdev_set_format(output->outfmt_pad->entity, ++ &mfmt, output->outfmt_pad->index, V4L2_SUBDEV_FORMAT_ACTIVE) < 0) { ++ weston_log("VSP: output pad in format set failed: %d\n", errno); ++ return -1; ++ } ++ ++ /* ...setup output pads */ ++ memset(&format, 0, sizeof(format)); ++ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ format.fmt.pix_mp.width = crop->width; ++ format.fmt.pix_mp.height = crop->height; ++#ifdef VSP_OUTPUT_NV16 ++ format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV16M; ++#else ++ format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; ++#endif ++ format.fmt.pix_mp.num_planes = VSP_OUTPUT_BUFFERS_PLANE; ++ /* ...set output buffer format */ ++ if (vsp_set_format(output->fd, &format) < 0) { ++ weston_log("VSP: output set format failed: %d\n", errno); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++vsp_request_buffers(vsp_data_t *vsp, vsp_port_n port, unsigned int num) ++{ ++ vsp_media_pad_t *pad = (port == VSP_PORT_INPUT) ? &vsp->input : &vsp->output; ++ struct v4l2_requestbuffers reqbuf; ++ int fd = pad->fd; ++ ++ /* ...input buffers are DMA-fd, output buffers allocated by kernel */ ++ memset(&reqbuf, 0, sizeof(reqbuf)); ++ reqbuf.type = (port == VSP_PORT_INPUT) ? ++ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ reqbuf.memory = V4L2_MEMORY_DMABUF; ++ reqbuf.count = num; ++ if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) { ++ weston_log("VSP: %s REQBUFS failed: %d\n", ++ (port == VSP_PORT_INPUT) ? "input" : "output", errno); ++ return -1; ++ } ++ ++ if (reqbuf.count != num) { ++ weston_log("VSP: %s failed to request %d (!= %d) bufs\n", ++ (port == VSP_PORT_INPUT) ? "input" : "output", num, reqbuf.count); ++ return -1; ++ } ++ return 0; ++} ++ ++ ++/* ...enqueue dmafd buffer */ ++static int ++vsp_input_buffer_queue_dmafd(vsp_data_t *vsp, int i, int dmafd) ++{ ++ vsp_media_pad_t *pad = &vsp->input; ++ struct v4l2_buffer buf; ++ struct v4l2_plane planes[1]; ++ ++ /* ...set buffer parameters */ ++ memset(&buf, 0, sizeof(buf)); ++ memset(planes, 0, sizeof(planes)); ++ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ buf.memory = V4L2_MEMORY_DMABUF; ++ buf.index = i; ++ buf.m.planes = planes; ++ buf.length = 1; ++ buf.m.planes[0].m.fd = dmafd; ++ ++ /* ...submit buffer */ ++ if (ioctl(pad->fd, VIDIOC_QBUF, &buf) < 0) { ++ weston_log("VSP: input dmafd (%d) buffer (%i) queue failed: %d\n", ++ dmafd, i, errno); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ...dequeue dmafd buffer */ ++static int ++vsp_input_buffer_dequeue_dmafd(vsp_data_t *vsp) ++{ ++ vsp_media_pad_t *pad = &vsp->input; ++ struct v4l2_buffer buf; ++ struct v4l2_plane planes[1]; ++ ++ /* ...set buffer parameters */ ++ memset(&buf, 0, sizeof(buf)); ++ memset(planes, 0, sizeof(planes)); ++ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ buf.memory = V4L2_MEMORY_DMABUF; ++ buf.m.planes = planes; ++ buf.length = 1; ++ ++ if (ioctl(pad->fd, VIDIOC_DQBUF, &buf) < 0) { ++ weston_log("VSP: input dmafd buffer de-queue failed: %d\n", errno); ++ return -1; ++ } ++ ++ /* ...return buffer index */ ++ return buf.index; ++} ++ ++/* ...enqueue output buffer */ ++static int ++vsp_output_buffer_queue_dmafd(vsp_data_t *vsp, int i, int dmafd[]) ++{ ++ vsp_media_pad_t *pad = &vsp->output; ++ struct v4l2_plane planes[2]; ++ struct v4l2_buffer buf; ++ ++ /* ...set buffer parameters (single-plane ARGB always) */ ++ memset(&buf, 0, sizeof(buf)); ++ memset(planes, 0, sizeof(planes)); ++ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ buf.memory = V4L2_MEMORY_DMABUF; ++ buf.index = i; ++ buf.m.planes = planes; ++ buf.length = VSP_OUTPUT_BUFFERS_PLANE; ++ buf.m.planes[0].m.fd = dmafd[0]; ++ buf.m.planes[1].m.fd = dmafd[1]; ++ ++ /* ...submit buffer */ ++ if (ioctl(pad->fd, VIDIOC_QBUF, &buf) < 0) { ++ weston_log("VSP: output dmafd queue failed: %d\n", errno); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ...dequeue output buffer */ ++static int ++vsp_output_buffer_dequeue_dmafd(vsp_data_t *vsp) ++{ ++ vsp_media_pad_t *pad = &vsp->output; ++ struct v4l2_buffer buf; ++ struct v4l2_plane planes[2]; ++ ++ /* ...set buffer parameters */ ++ memset(&buf, 0, sizeof(buf)); ++ memset(planes, 0, sizeof(planes)); ++ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ buf.memory = V4L2_MEMORY_DMABUF; ++ buf.m.planes = planes; ++ buf.length = VSP_OUTPUT_BUFFERS_PLANE; ++ ++ if (ioctl(pad->fd, VIDIOC_DQBUF, &buf) < 0) { ++ weston_log("VSP: output dmafd de-queue failed: %d\n", errno); ++ return -1; ++ } ++ ++ /* ...return dequeue buffer index */ ++ return buf.index; ++} ++ ++/* ...get capturing interface file descriptor */ ++static int ++vsp_capture_fd(vsp_data_t *vsp) ++{ ++ return vsp->output.fd; ++} ++ ++/******************************************************************************* ++ * Gstreamer stuff ++ ******************************************************************************/ ++ ++static void ++print_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data) ++{ ++ int i, num; ++ ++ num = gst_tag_list_get_tag_size (list, tag); ++ for (i = 0; i < num; ++i) { ++ const GValue *val; ++ ++ /* Note: when looking for specific tags, use the gst_tag_list_get_xyz() API, ++ * we only use the GValue approach here because it is more generic */ ++ val = gst_tag_list_get_value_index (list, tag, i); ++ if (G_VALUE_HOLDS_STRING (val)) { ++ weston_log("\t%20s : %s\n", tag, g_value_get_string (val)); ++ } else if (G_VALUE_HOLDS_UINT (val)) { ++ weston_log("\t%20s : %u\n", tag, g_value_get_uint (val)); ++ } else if (G_VALUE_HOLDS_DOUBLE (val)) { ++ weston_log("\t%20s : %g\n", tag, g_value_get_double (val)); ++ } else if (G_VALUE_HOLDS_BOOLEAN (val)) { ++ weston_log("\t%20s : %s\n", tag, ++ (g_value_get_boolean (val)) ? "true" : "false"); ++ } else if (GST_VALUE_HOLDS_BUFFER (val)) { ++ GstBuffer *buf = gst_value_get_buffer (val); ++ guint buffer_size = gst_buffer_get_size (buf); ++ ++ weston_log("\t%20s : buffer of size %u\n", tag, buffer_size); ++ } else if (GST_VALUE_HOLDS_DATE_TIME (val)) { ++ GstDateTime *dt = g_value_get_boxed (val); ++ gchar *dt_str = gst_date_time_to_iso8601_string (dt); ++ ++ weston_log("\t%20s : %s\n", tag, dt_str); ++ g_free (dt_str); ++ } else { ++ weston_log("\t%20s : tag of type '%s'\n", tag, G_VALUE_TYPE_NAME (val)); ++ } ++ } ++} ++ ++static gboolean ++gst_bus_callback(GstBus *bus, GstMessage *message, gpointer user_data) ++{ ++ GTimeVal time; ++ struct gst_recorder *r = user_data; ++ ++ if (!r->pipeline) { ++ weston_log("gst_pipeline: unexpected gst bus callback event, while pipeline==null\n"); ++ return TRUE; ++ } ++ ++ g_get_current_time(&time); ++ ++ switch (GST_MESSAGE_TYPE(message)) ++ { ++ case GST_MESSAGE_QOS: ++ { ++ GstFormat format; ++ guint64 processed; ++ guint64 dropped; ++ ++ gst_message_parse_qos_stats (message, &format, &processed, &dropped); ++ weston_log("gst_pipeline: qos from: %s processed %lld, dropped %lld\n", ++ GST_OBJECT_NAME (message->src), ++ processed, dropped); ++ } ++ break; ++ case GST_MESSAGE_STREAM_STATUS: ++ { ++ const GValue *val; ++ ++ val = gst_message_get_stream_status_object (message); ++ weston_log("gst_pipeline: stream status type %s, value %p\n", ++ G_VALUE_TYPE_NAME(val), ++ g_value_get_object(val)); ++ } ++ break; ++ case GST_MESSAGE_TAG: ++ { ++ GstTagList *tags = NULL; ++ ++ gst_message_parse_tag (message, &tags); ++ weston_log("gst_pipeline: tag from element %s:\n", ++ GST_OBJECT_NAME (message->src)); ++ gst_tag_list_foreach (tags, print_one_tag, NULL); ++ weston_log("\n"); ++ } ++ break; ++ case GST_MESSAGE_STATE_CHANGED: ++ { ++ GstState oldstate, newstate; ++ ++ gst_message_parse_state_changed(message, &oldstate, &newstate, NULL); ++ weston_log("gst_pipeline: element %s changed state from %s to %s.\n", ++ GST_OBJECT_NAME (message->src), ++ gst_element_state_get_name (oldstate), ++ gst_element_state_get_name (newstate)); ++ ++ /* if gstreamer become ready */ ++ if ((GST_MESSAGE_SRC(message) == GST_OBJECT(r->appsrc)) && ++ (newstate == GST_STATE_PAUSED)) { ++ weston_log("gst_pipeline: pipeline ready\n"); ++ } ++ } ++ break; ++ case GST_MESSAGE_ERROR: ++ { ++ GError *err; ++ gchar *debug_info; ++ gst_message_parse_error(message, &err, &debug_info); ++ weston_log("gst_pipeline: error received from element %s: %s\n", ++ GST_OBJECT_NAME(message->src), err->message); ++ weston_log("gst_pipeline: debugging information: %s\n", ++ debug_info ? debug_info : "none"); ++ g_clear_error (&err); ++ g_free (debug_info); ++ } ++ break; ++ case GST_MESSAGE_WARNING: ++ { ++ GError *err; ++ gchar *debug_info; ++ gst_message_parse_warning(message, &err, &debug_info); ++ weston_log("gst_pipeline: warning received from element %s: %s\n", ++ GST_OBJECT_NAME(message->src), err->message); ++ weston_log("gst_pipeline: debugging information: %s\n", ++ debug_info ? debug_info : "none"); ++ g_clear_error (&err); ++ g_free (debug_info); ++ } ++ break; ++ default: ++ weston_log("gst_pipeline: %s from %s\n", ++ GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME (message->src)); ++ break; ++ } ++ return TRUE; ++} ++ ++static void * ++worker_thread_function(void *data) ++{ ++ GstMessage *msg; ++ struct gst_recorder *r = data; ++ ++ pthread_mutex_lock(&r->mutex); ++ ++ while (!r->destroying) { ++ if (!r->input.valid) ++ pthread_cond_wait(&r->input_cond, &r->mutex); ++ ++ /* If the thread is awaken by destroy_worker_thread(), ++ * there might not be valid input */ ++ if (!r->input.valid) ++ continue; ++ ++ /* TODO: move it to separate thread? */ ++ g_main_context_iteration(r->gcontext, FALSE); ++ ++ do { ++ msg = gst_bus_pop_filtered(r->bus, ++ GST_MESSAGE_ANY); ++ if (msg) { ++ gst_bus_callback(r->bus, msg, r); ++ } ++ } while (msg); ++ ++ /* check input */ ++ gst_recorder_process_dmafd(r, r->input.prime_fd, r->input.stride); ++ ++ r->input.valid = 0; ++ } ++ ++ pthread_mutex_unlock(&r->mutex); ++ ++ return NULL; ++} ++ ++static int ++setup_worker_thread(struct gst_recorder *r) ++{ ++ r->gcontext = g_main_context_new(); ++ pthread_mutex_init(&r->mutex, NULL); ++ pthread_cond_init(&r->input_cond, NULL); ++ pthread_create(&r->worker_thread, NULL, worker_thread_function, r); ++ ++ return 1; ++} ++ ++static void ++destroy_worker_thread(struct gst_recorder *r) ++{ ++ /* Make sure the worker thread finishes */ ++ r->destroying = 1; ++ ++ pthread_cond_signal(&r->input_cond); ++ ++ pthread_join(r->worker_thread, NULL); ++ ++ pthread_mutex_destroy(&r->mutex); ++ pthread_cond_destroy(&r->input_cond); ++} ++ ++void weston_debug_function(GstDebugCategory* category, GstDebugLevel level, ++ const gchar* file, const char* function, ++ gint line, GObject* object, GstDebugMessage* message, ++ gpointer data) ++{ ++ weston_log("[GST]:%s %s:%d %s\n", file, function, line, gst_debug_message_get(message)); ++} ++ ++void ++gst_recorder_init(void) ++{ ++ gst_init(NULL, 0); ++ ++ /* VSP init */ ++ vsp_g = vsp_init("/dev/media0"); ++ if (!vsp_g) ++ weston_log("[gst recorder] VSP init failed"); ++} ++ ++static int ++gst_recorder_find_omx_pool(struct gst_recorder *r) ++{ ++ int ret = 0; ++ GstCaps *caps; ++ GstQuery *query; ++ GstBufferPool *pool; ++ GstStructure *config; ++ guint size, min, max; ++ ++ caps = gst_caps_new_simple ( "video/x-raw", ++#ifdef VSP_OUTPUT_NV16 ++ "format", G_TYPE_STRING, "NV16", ++#else ++ "format", G_TYPE_STRING, "NV12", ++#endif ++ "width", G_TYPE_INT, r->set->crop.width, ++ "height", G_TYPE_INT, r->set->crop.height, ++ "framerate", GST_TYPE_FRACTION, 0, DEFAULT_FPS, ++ NULL); ++ ++ /* find a pool for the negotiated caps now */ ++ query = gst_query_new_allocation (caps, TRUE); ++ ++ if (!gst_pad_peer_query (r->appsrc_pad, query)) { ++ /* query failed, not a problem, we use the query defaults */ ++ weston_log("allocation query failed"); ++ ret = -1; ++ goto err; ++ } ++ ++ weston_log("goot %d pools\n", gst_query_get_n_allocation_pools (query)); ++ if (gst_query_get_n_allocation_pools (query) > 0) { ++ /* we got configuration from our peer, parse them */ ++ gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); ++ weston_log(" pool settings size %d, min %d, max %d\n", size, min, max); ++ } else { ++ weston_log("no pool queried\n"); ++ ret = -1; ++ goto err; ++ } ++ ++ config = gst_buffer_pool_get_config (pool); ++ gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); ++ gst_buffer_pool_config_set_params (config, caps, size, min, max); ++ gst_buffer_pool_set_config (pool, config); ++ ++ /* and activate */ ++ gst_buffer_pool_set_active (pool, TRUE); ++ ++ r->omx_pool = pool; ++ ++err: ++ gst_query_unref (query); ++ return ret; ++} ++ ++static int ++gst_recorder_omx_buffer_acquire(struct gst_recorder *r, GstBuffer **ret_buf, int fd[]) ++{ ++ unsigned int i; ++ GstFlowReturn ret; ++ GstBuffer *buf; ++ guint n_mem; ++ GstMemory *mem; ++ ++ ret = gst_buffer_pool_acquire_buffer(r->omx_pool, &buf, NULL); ++ if (ret != GST_FLOW_OK) { ++ weston_log("OMX buffer acquire failed\n"); ++ return -1; ++ } ++ ++ n_mem = gst_buffer_n_memory(buf); ++ if (n_mem < 1) { ++ weston_log("Buffer with no mem!\n"); ++ goto err_release; ++ } ++ ++ for (i = 0; i < n_mem; i++) { ++ mem = gst_buffer_peek_memory (buf, i); ++ if (!gst_is_dmabuf_memory (mem)) { ++ weston_log("Mem not dmabuf\n"); ++ goto err_release; ++ } ++ fd[i] = gst_dmabuf_memory_get_fd (mem); ++ } ++ ++ *ret_buf = buf; ++ ++ return 0; ++err_release: ++ gst_buffer_pool_release_buffer(r->omx_pool, buf); ++ return -1; ++} ++ ++static int ++gst_recorder_omx_buffer_release(struct gst_recorder *r, GstBuffer *buf) ++{ ++ gst_buffer_pool_release_buffer(r->omx_pool, buf); ++ ++ return 0; ++} ++ ++struct gst_recorder * ++gst_recorder_create(struct gst_recorder_settings *settings) ++{ ++ struct gst_recorder *r; ++ char gst_pipe[1024]; ++ char *ptr = gst_pipe; ++ GError *perror = NULL; ++ ++ weston_log("gst_recorder_create (%dx%d) crop %dx%d at %d,%d\n", ++ settings->width, settings->height, settings->crop.width, ++ settings->crop.height, settings->crop.top, settings->crop.left); ++ ++ if (!vsp_g) { ++ weston_log("gst_recorder_create: no VSP\n"); ++ return NULL; ++ } ++ ++ r = calloc(1, sizeof *r); ++ if (!r) ++ return NULL; ++ memset(r, 0, sizeof *r); ++ ++ r->set = settings; ++ r->timestamp = 0; ++ ++ r->vsp = vsp_g; ++ vsp_g->users++; ++ ++ /* GST init */ ++ /* source (GST_FORMAT_BYTES) */ ++ ptr += sprintf(ptr, ++ "appsrc name=src ! "); ++ ++ /* omx */ ++ ptr += sprintf(ptr, ++ "omxh264enc target-bitrate=%d control-rate=2 name=my_encoder ! " ++ "video/x-h264,width=%d,height=%d ! ", ++ r->set->bitrate, r->set->crop.width, r->set->crop.height); ++ ++ /* rtp payloader */ ++ ptr += sprintf(ptr, ++ "rtph264pay config-interval=1 name=my_h264pay ! queue ! "); ++ ++ /* usp sink */ ++ ptr += sprintf(ptr, ++ "udpsink host=%s ", r->set->ip); ++ if (r->set->port > 0) ++ ptr += sprintf(ptr, ++ " port=%d name=my_udpsink", r->set->port); ++ ++ weston_log("gst_pipeline: starting: %s\n", gst_pipe); ++ ++ /* launch */ ++ r->pipeline = gst_parse_launch (gst_pipe, &perror); ++ if (!r->pipeline) { ++ weston_log("gst_pipeline: can not start pipeline: %s\n", perror->message); ++ goto err_gst; ++ } ++ ++ /* get appsrc */ ++ r->appsrc = gst_bin_get_by_name(GST_BIN (r->pipeline), "src"); ++ if (!r->appsrc) { ++ weston_log("gst_pipeline: can not get appsrc\n"); ++ goto err_gst; ++ } ++ ++ /* get bus */ ++ r->bus = gst_pipeline_get_bus (GST_PIPELINE(r->pipeline)); ++ if (!r->bus) { ++ weston_log("gst_pipeline: can not get bus\n"); ++ goto err_gst; ++ } ++ ++ setup_worker_thread(r); ++ ++ /* setup caps */ ++ g_object_set(G_OBJECT(r->appsrc), "caps", ++ gst_caps_new_simple ( "video/x-raw", ++#ifdef VSP_OUTPUT_NV16 ++ "format", G_TYPE_STRING, "NV16", ++#else ++ "format", G_TYPE_STRING, "NV12", ++#endif ++ "width", G_TYPE_INT, r->set->crop.width, ++ "height", G_TYPE_INT, r->set->crop.height, ++ "framerate", GST_TYPE_FRACTION, 0, DEFAULT_FPS, ++ NULL), NULL); ++ ++ r->appsrc_pad = gst_element_get_static_pad(GST_ELEMENT_CAST(r->appsrc), "src"); ++ if (!r->appsrc_pad) ++ weston_log("Failed to get src0 pad of appsrc\n"); ++ ++ /* set playing */ ++ if (gst_element_set_state (r->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { ++ weston_log("gst_pipeline: can not change state to PLAYING\n"); ++ goto err_gst; ++ } ++ ++ if (gst_recorder_find_omx_pool(r) != 0) { ++ weston_log("failed to find OMX buffer pool\n"); ++ goto err_gst_stop; ++ } ++ ++ weston_log("gst_recorder_create done\n"); ++ ++ return r; ++ ++err_gst_stop: ++ gst_element_set_state (r->pipeline, GST_STATE_NULL); ++ destroy_worker_thread(r); ++err_gst: ++ free(r->pipeline); ++ free(r); ++ ++ return NULL; ++} ++ ++void ++gst_recorder_destroy(struct gst_recorder *r) ++{ ++ r->vsp->users--; ++ ++ if (r->pipeline) { ++ gst_element_set_state (r->pipeline, GST_STATE_NULL); ++ gst_object_unref(r->omx_pool); ++ ++ destroy_worker_thread(r); ++ ++ gst_object_unref(GST_OBJECT(r->bus)); ++ gst_object_unref (r->pipeline); ++ r->pipeline = NULL; ++ } ++ free(r); ++} ++ ++static int ++gst_recorder_set_timestamp(struct gst_recorder *r, GstBuffer *buffer) ++{ ++ uint32_t cur_time = weston_compositor_get_time(); ++ ++ if (r->timestamp == 0) { ++ /* first frame assume around DEFAULT_FPS FPS */ ++ GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, DEFAULT_FPS); ++ } else { ++ uint32_t delta = cur_time - r->ts_last_frame; ++ /* delta in mS */ ++ GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(delta, GST_SECOND, 1000); ++ } ++ ++ r->timestamp += GST_BUFFER_DURATION(buffer); ++ GST_BUFFER_PTS(buffer) = r->timestamp; ++ GST_BUFFER_DTS(buffer) = r->timestamp; ++ ++ r->ts_last_frame = cur_time; ++ ++ return 0; ++} ++ ++ ++static int ++gst_recorder_process_dmafd(struct gst_recorder *r, int fd, int stride) ++{ ++ int ret; ++ GstBuffer *buf; ++ int omx_fd[2]; ++ ++ /* get GST buffer */ ++ if (gst_recorder_omx_buffer_acquire(r, &buf, omx_fd) < 0) { ++ weston_log("VSP: can not acquire GST buffer, dropping frame\n"); ++ return 0; ++ } ++ ++ pthread_mutex_lock(&r->vsp->mutex); ++ /* setup vsp */ ++ if (vsp_set_formats(r->vsp, r->set->width, r->set->height, &r->set->crop) < 0) { ++ weston_log("VSP: format set failed\n"); ++ goto err; ++ } ++ ++ /* input */ ++ if (vsp_request_buffers(r->vsp, VSP_PORT_INPUT, 1) < 0) { ++ weston_log("VSP: input buffer allocation failed\n"); ++ goto err_vsp; ++ } ++ ++ /* output */ ++ if (vsp_request_buffers(r->vsp, VSP_PORT_OUTPUT, 1) < 0) { ++ weston_log("VSP: output buffer allocation failed\n"); ++ goto err_vsp; ++ } ++ ++ /* queue output biffer */ ++ if (vsp_output_buffer_queue_dmafd(r->vsp, 0, omx_fd) < 0) { ++ weston_log("can not queue OMX buffer %d to VSP\n", 0); ++ gst_recorder_omx_buffer_release(r, buf); ++ goto err_vsp; ++ } ++ ++ /* queue input vsp buffer */ ++ if (vsp_input_buffer_queue_dmafd(r->vsp, 0, fd) < 0) { ++ weston_log("VSP: failed to queue input buffer\n"); ++ goto err_vsp; ++ } ++ ++ /* start input */ ++ if (vsp_streaming_enable(r->vsp, VSP_PORT_INPUT, 1) < 0) { ++ weston_log("VSP: failed to start input\n"); ++ goto err_vsp; ++ } ++ ++ /* start output */ ++ if (vsp_streaming_enable(r->vsp, VSP_PORT_OUTPUT, 1) < 0) { ++ weston_log("VSP: failed to start output\n"); ++ goto err_vsp; ++ } ++ ++ /* dequeue input (do we need this?) */ ++ if (vsp_input_buffer_dequeue_dmafd(r->vsp) < 0) { ++ weston_log("VSP: failed to dequeue input buffer\n"); ++ /* don't care */ ++ } ++ ++ /* dequeue output */ ++ if (vsp_output_buffer_dequeue_dmafd(r->vsp) < 0) { ++ weston_log("VSP: failed to dequeu output buffer\n"); ++ gst_recorder_omx_buffer_release(r, buf); ++ /* fall through */ ++ } else { ++ /* set timestamp */ ++ gst_recorder_set_timestamp(r, buf); ++ ++ ret = gst_app_src_push_buffer(r->appsrc, buf); ++ r->frame_count++; ++ ++ if (ret != GST_FLOW_OK) { ++ /* some error, stop sending data */ ++ weston_log("gst_pipeline: some error %d\n", ret); ++ } ++ ++ } ++ /* stop input */ ++ vsp_streaming_enable(r->vsp, VSP_PORT_INPUT, 0); ++ /* stop output */ ++ vsp_streaming_enable(r->vsp, VSP_PORT_OUTPUT, 0); ++ ++ /* deinit */ ++ vsp_request_buffers(r->vsp, VSP_PORT_INPUT, 0); ++ vsp_request_buffers(r->vsp, VSP_PORT_OUTPUT, 0); ++ ++ pthread_mutex_unlock(&r->vsp->mutex); ++ return 0; ++ ++err_vsp: ++ /* drop gst buffer */ ++ /* finish vsp here */ ++err: ++ pthread_mutex_unlock(&r->vsp->mutex); ++ return -1; ++} ++ ++int ++gst_recorder_frame_dmafd(struct gst_recorder *r, int fd, int stride) ++{ ++ int ret = 0; ++ ++ pthread_mutex_lock(&r->mutex); ++ ++ if (r->error) { ++ errno = r->error; ++ ret = -1; ++ goto unlock; ++ } ++ ++ /* The mutex is never released while encoding, so this point should ++ * never be reached if input.valid is true. */ ++ assert(!r->input.valid); ++ ++ r->input.prime_fd = fd; ++ r->input.stride = stride; ++ r->input.valid = 1; ++ pthread_cond_signal(&r->input_cond); ++ ++unlock: ++ pthread_mutex_unlock(&r->mutex); ++ ++ return 0; ++} +diff --git a/src/gst-recorder.h b/src/gst-recorder.h +new file mode 100644 +index 0000000..e1c53ff +--- /dev/null ++++ b/src/gst-recorder.h +@@ -0,0 +1,58 @@ ++/* ++ * Copyright © 2016 Cogent Embedded Inc ++ * ++ * Permission to use, copy, modify, distribute, and sell this software and ++ * its documentation for any purpose is hereby granted without fee, provided ++ * that the above copyright notice appear in all copies and that both that ++ * copyright notice and this permission notice appear in supporting ++ * documentation, and that the name of the copyright holders not be used in ++ * advertising or publicity pertaining to distribution of the software ++ * without specific, written prior permission. The copyright holders make ++ * no representations about the suitability of this software for any ++ * purpose. It is provided "as is" without express or implied warranty. ++ * ++ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS ++ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND ++ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY ++ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ++ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF ++ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN ++ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#ifndef _GST_RECORDER_H_ ++#define _GST_RECORDER_H_ ++ ++/* VSP includes */ ++#include <mediactl/mediactl.h> ++#include <mediactl/v4l2subdev.h> ++ ++struct gst_recorder; ++ ++struct gst_recorder_settings { ++ int width; ++ int height; ++ int bitrate; ++ char *ip; ++ int port; ++ int latency_test; ++ int refresh_ratio; ++ ++ /* Cropping */ ++ struct v4l2_rect crop; ++}; ++ ++void ++gst_recorder_init(void); ++struct gst_recorder * ++gst_recorder_create(struct gst_recorder_settings *settings); ++void ++gst_recorder_destroy(struct gst_recorder *r); ++int ++gst_recorder_frame(struct gst_recorder *r, int fd, int stride); ++int ++gst_recorder_frame_mmap(struct gst_recorder *r, void *data, int stride); ++int ++gst_recorder_frame_dmafd(struct gst_recorder *r, int fd, int stride); ++ ++#endif /* _GST_RECORDER_H_ */ +diff --git a/src/media-ctl/libmediactl.c b/src/media-ctl/libmediactl.c +new file mode 100644 +index 0000000..f15b1a3 +--- /dev/null ++++ b/src/media-ctl/libmediactl.c +@@ -0,0 +1,955 @@ ++/* ++ * Media controller interface library ++ * ++ * Copyright (C) 2010-2014 Ideas on board SPRL ++ * ++ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include "config.h" ++ ++#include <sys/ioctl.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++ ++#include <ctype.h> ++#include <errno.h> ++#include <fcntl.h> ++#include <stdbool.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <unistd.h> ++ ++#include <linux/media.h> ++#include <linux/videodev2.h> ++ ++#include "mediactl.h" ++#include "mediactl-priv.h" ++#include "tools.h" ++ ++/* ----------------------------------------------------------------------------- ++ * Graph access ++ */ ++ ++struct media_pad *media_entity_remote_source(struct media_pad *pad) ++{ ++ unsigned int i; ++ ++ if (!(pad->flags & MEDIA_PAD_FL_SINK)) ++ return NULL; ++ ++ for (i = 0; i < pad->entity->num_links; ++i) { ++ struct media_link *link = &pad->entity->links[i]; ++ ++ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) ++ continue; ++ ++ if (link->sink == pad) ++ return link->source; ++ } ++ ++ return NULL; ++} ++ ++struct media_entity *media_get_entity_by_name(struct media_device *media, ++ const char *name, size_t length) ++{ ++ unsigned int i; ++ ++ /* A match is impossible if the entity name is longer than the maximum ++ * size we can get from the kernel. ++ */ ++ if (length >= FIELD_SIZEOF(struct media_entity_desc, name)) ++ return NULL; ++ ++ for (i = 0; i < media->entities_count; ++i) { ++ struct media_entity *entity = &media->entities[i]; ++ ++ if (strncmp(entity->info.name, name, length) == 0 && ++ entity->info.name[length] == '\0') ++ return entity; ++ } ++ ++ return NULL; ++} ++ ++struct media_entity *media_get_entity_by_id(struct media_device *media, ++ __u32 id) ++{ ++ bool next = id & MEDIA_ENT_ID_FLAG_NEXT; ++ unsigned int i; ++ ++ id &= ~MEDIA_ENT_ID_FLAG_NEXT; ++ ++ for (i = 0; i < media->entities_count; ++i) { ++ struct media_entity *entity = &media->entities[i]; ++ ++ if ((entity->info.id == id && !next) || ++ (entity->info.id > id && next)) ++ return entity; ++ } ++ ++ return NULL; ++} ++ ++unsigned int media_get_entities_count(struct media_device *media) ++{ ++ return media->entities_count; ++} ++ ++struct media_entity *media_get_entity(struct media_device *media, unsigned int index) ++{ ++ if (index >= media->entities_count) ++ return NULL; ++ ++ return &media->entities[index]; ++} ++ ++const struct media_pad *media_entity_get_pad(struct media_entity *entity, unsigned int index) ++{ ++ if (index >= entity->info.pads) ++ return NULL; ++ ++ return &entity->pads[index]; ++} ++ ++unsigned int media_entity_get_links_count(struct media_entity *entity) ++{ ++ return entity->num_links; ++} ++ ++const struct media_link *media_entity_get_link(struct media_entity *entity, unsigned int index) ++{ ++ if (index >= entity->num_links) ++ return NULL; ++ ++ return &entity->links[index]; ++} ++ ++const char *media_entity_get_devname(struct media_entity *entity) ++{ ++ return entity->devname[0] ? entity->devname : NULL; ++} ++ ++struct media_entity *media_get_default_entity(struct media_device *media, ++ unsigned int type) ++{ ++ switch (type) { ++ case MEDIA_ENT_T_DEVNODE_V4L: ++ return media->def.v4l; ++ case MEDIA_ENT_T_DEVNODE_FB: ++ return media->def.fb; ++ case MEDIA_ENT_T_DEVNODE_ALSA: ++ return media->def.alsa; ++ case MEDIA_ENT_T_DEVNODE_DVB: ++ return media->def.dvb; ++ } ++ ++ return NULL; ++} ++ ++const struct media_device_info *media_get_info(struct media_device *media) ++{ ++ return &media->info; ++} ++ ++const char *media_get_devnode(struct media_device *media) ++{ ++ return media->devnode; ++} ++ ++const struct media_entity_desc *media_entity_get_info(struct media_entity *entity) ++{ ++ return &entity->info; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Open/close ++ */ ++ ++static int media_device_open(struct media_device *media) ++{ ++ int ret; ++ ++ if (media->fd != -1) ++ return 0; ++ ++ media_dbg(media, "Opening media device %s\n", media->devnode); ++ ++ media->fd = open(media->devnode, O_RDWR); ++ if (media->fd < 0) { ++ ret = -errno; ++ media_dbg(media, "%s: Can't open media device %s\n", ++ __func__, media->devnode); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void media_device_close(struct media_device *media) ++{ ++ if (media->fd != -1) { ++ close(media->fd); ++ media->fd = -1; ++ } ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Link setup ++ */ ++ ++int media_setup_link(struct media_device *media, ++ struct media_pad *source, ++ struct media_pad *sink, ++ __u32 flags) ++{ ++ struct media_link *link; ++ struct media_link_desc ulink; ++ unsigned int i; ++ int ret; ++ ++ ret = media_device_open(media); ++ if (ret < 0) ++ goto done; ++ ++ for (i = 0; i < source->entity->num_links; i++) { ++ link = &source->entity->links[i]; ++ ++ if (link->source->entity == source->entity && ++ link->source->index == source->index && ++ link->sink->entity == sink->entity && ++ link->sink->index == sink->index) ++ break; ++ } ++ ++ if (i == source->entity->num_links) { ++ media_dbg(media, "%s: Link not found\n", __func__); ++ ret = -ENOENT; ++ goto done; ++ } ++ ++ /* source pad */ ++ ulink.source.entity = source->entity->info.id; ++ ulink.source.index = source->index; ++ ulink.source.flags = MEDIA_PAD_FL_SOURCE; ++ ++ /* sink pad */ ++ ulink.sink.entity = sink->entity->info.id; ++ ulink.sink.index = sink->index; ++ ulink.sink.flags = MEDIA_PAD_FL_SINK; ++ ++ ulink.flags = flags | (link->flags & MEDIA_LNK_FL_IMMUTABLE); ++ ++ ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink); ++ if (ret == -1) { ++ ret = -errno; ++ media_dbg(media, "%s: Unable to setup link (%s)\n", ++ __func__, strerror(errno)); ++ goto done; ++ } ++ ++ link->flags = ulink.flags; ++ link->twin->flags = ulink.flags; ++ ++ ret = 0; ++ ++done: ++ return ret; ++} ++ ++int media_reset_links(struct media_device *media) ++{ ++ unsigned int i, j; ++ int ret; ++ ++ for (i = 0; i < media->entities_count; ++i) { ++ struct media_entity *entity = &media->entities[i]; ++ ++ for (j = 0; j < entity->num_links; j++) { ++ struct media_link *link = &entity->links[j]; ++ ++ if (link->flags & MEDIA_LNK_FL_IMMUTABLE || ++ link->source->entity != entity) ++ continue; ++ ++ ret = media_setup_link(media, link->source, link->sink, ++ link->flags & ~MEDIA_LNK_FL_ENABLED); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Entities, pads and links enumeration ++ */ ++ ++static struct media_link *media_entity_add_link(struct media_entity *entity) ++{ ++ if (entity->num_links >= entity->max_links) { ++ struct media_link *links = entity->links; ++ unsigned int max_links = entity->max_links * 2; ++ unsigned int i; ++ ++ links = realloc(links, max_links * sizeof *links); ++ if (links == NULL) ++ return NULL; ++ ++ for (i = 0; i < entity->num_links; ++i) ++ links[i].twin->twin = &links[i]; ++ ++ entity->max_links = max_links; ++ entity->links = links; ++ } ++ ++ return &entity->links[entity->num_links++]; ++} ++ ++static int media_enum_links(struct media_device *media) ++{ ++ __u32 id; ++ int ret = 0; ++ ++ for (id = 1; id <= media->entities_count; id++) { ++ struct media_entity *entity = &media->entities[id - 1]; ++ struct media_links_enum links; ++ unsigned int i; ++ ++ links.entity = entity->info.id; ++ links.pads = calloc(entity->info.pads, sizeof(struct media_pad_desc)); ++ links.links = calloc(entity->info.links, sizeof(struct media_link_desc)); ++ ++ if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) { ++ ret = -errno; ++ media_dbg(media, ++ "%s: Unable to enumerate pads and links (%s).\n", ++ __func__, strerror(errno)); ++ free(links.pads); ++ free(links.links); ++ return ret; ++ } ++ ++ for (i = 0; i < entity->info.pads; ++i) { ++ entity->pads[i].entity = entity; ++ entity->pads[i].index = links.pads[i].index; ++ entity->pads[i].flags = links.pads[i].flags; ++ } ++ ++ for (i = 0; i < entity->info.links; ++i) { ++ struct media_link_desc *link = &links.links[i]; ++ struct media_link *fwdlink; ++ struct media_link *backlink; ++ struct media_entity *source; ++ struct media_entity *sink; ++ ++ source = media_get_entity_by_id(media, link->source.entity); ++ sink = media_get_entity_by_id(media, link->sink.entity); ++ ++ if (source == NULL || sink == NULL) { ++ media_dbg(media, ++ "WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n", ++ id, i, link->source.entity, ++ link->source.index, ++ link->sink.entity, ++ link->sink.index); ++ ret = -EINVAL; ++ } else { ++ fwdlink = media_entity_add_link(source); ++ fwdlink->source = &source->pads[link->source.index]; ++ fwdlink->sink = &sink->pads[link->sink.index]; ++ fwdlink->flags = link->flags; ++ ++ backlink = media_entity_add_link(sink); ++ backlink->source = &source->pads[link->source.index]; ++ backlink->sink = &sink->pads[link->sink.index]; ++ backlink->flags = link->flags; ++ ++ fwdlink->twin = backlink; ++ backlink->twin = fwdlink; ++ } ++ } ++ ++ free(links.pads); ++ free(links.links); ++ } ++ ++ return ret; ++} ++ ++#ifdef HAVE_LIBUDEV ++ ++#include <libudev.h> ++ ++static inline int media_udev_open(struct udev **udev) ++{ ++ *udev = udev_new(); ++ if (*udev == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++static inline void media_udev_close(struct udev *udev) ++{ ++ if (udev != NULL) ++ udev_unref(udev); ++} ++ ++static int media_get_devname_udev(struct udev *udev, ++ struct media_entity *entity) ++{ ++ struct udev_device *device; ++ dev_t devnum; ++ const char *p; ++ int ret = -ENODEV; ++ ++ if (udev == NULL) ++ return -EINVAL; ++ ++ devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor); ++ media_dbg(entity->media, "looking up device: %u:%u\n", ++ major(devnum), minor(devnum)); ++ device = udev_device_new_from_devnum(udev, 'c', devnum); ++ if (device) { ++ p = udev_device_get_devnode(device); ++ if (p) { ++ strncpy(entity->devname, p, sizeof(entity->devname)); ++ entity->devname[sizeof(entity->devname) - 1] = '\0'; ++ } ++ ret = 0; ++ } ++ ++ udev_device_unref(device); ++ ++ return ret; ++} ++ ++#else /* HAVE_LIBUDEV */ ++ ++struct udev; ++ ++static inline int media_udev_open(struct udev **udev) { return 0; } ++ ++static inline void media_udev_close(struct udev *udev) { } ++ ++static inline int media_get_devname_udev(struct udev *udev, ++ struct media_entity *entity) ++{ ++ return -ENOTSUP; ++} ++ ++#endif /* HAVE_LIBUDEV */ ++ ++static int media_get_devname_sysfs(struct media_entity *entity) ++{ ++ struct stat devstat; ++ char devname[32]; ++ char sysname[32]; ++ char target[1024]; ++ char *p; ++ int ret; ++ ++ sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major, ++ entity->info.v4l.minor); ++ ret = readlink(sysname, target, sizeof(target) - 1); ++ if (ret < 0) ++ return -errno; ++ ++ target[ret] = '\0'; ++ p = strrchr(target, '/'); ++ if (p == NULL) ++ return -EINVAL; ++ ++ sprintf(devname, "/dev/%s", p + 1); ++ ret = stat(devname, &devstat); ++ if (ret < 0) ++ return -errno; ++ ++ /* Sanity check: udev might have reordered the device nodes. ++ * Make sure the major/minor match. We should really use ++ * libudev. ++ */ ++ if (major(devstat.st_rdev) == entity->info.v4l.major && ++ minor(devstat.st_rdev) == entity->info.v4l.minor) ++ strcpy(entity->devname, devname); ++ ++ return 0; ++} ++ ++static int media_enum_entities(struct media_device *media) ++{ ++ struct media_entity *entity; ++ struct udev *udev; ++ unsigned int size; ++ __u32 id; ++ int ret; ++ ++ ret = media_udev_open(&udev); ++ if (ret < 0) ++ media_dbg(media, "Can't get udev context\n"); ++ ++ for (id = 0, ret = 0; ; id = entity->info.id) { ++ size = (media->entities_count + 1) * sizeof(*media->entities); ++ media->entities = realloc(media->entities, size); ++ ++ entity = &media->entities[media->entities_count]; ++ memset(entity, 0, sizeof(*entity)); ++ entity->fd = -1; ++ entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT; ++ entity->media = media; ++ ++ ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info); ++ if (ret < 0) { ++ ret = errno != EINVAL ? -errno : 0; ++ break; ++ } ++ ++ /* Number of links (for outbound links) plus number of pads (for ++ * inbound links) is a good safe initial estimate of the total ++ * number of links. ++ */ ++ entity->max_links = entity->info.pads + entity->info.links; ++ ++ entity->pads = malloc(entity->info.pads * sizeof(*entity->pads)); ++ entity->links = malloc(entity->max_links * sizeof(*entity->links)); ++ if (entity->pads == NULL || entity->links == NULL) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ media->entities_count++; ++ ++ if (entity->info.flags & MEDIA_ENT_FL_DEFAULT) { ++ switch (entity->info.type) { ++ case MEDIA_ENT_T_DEVNODE_V4L: ++ media->def.v4l = entity; ++ break; ++ case MEDIA_ENT_T_DEVNODE_FB: ++ media->def.fb = entity; ++ break; ++ case MEDIA_ENT_T_DEVNODE_ALSA: ++ media->def.alsa = entity; ++ break; ++ case MEDIA_ENT_T_DEVNODE_DVB: ++ media->def.dvb = entity; ++ break; ++ } ++ } ++ ++ /* Find the corresponding device name. */ ++ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE && ++ media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) ++ continue; ++ ++ /* Try to get the device name via udev */ ++ if (!media_get_devname_udev(udev, entity)) ++ continue; ++ ++ /* Fall back to get the device name via sysfs */ ++ media_get_devname_sysfs(entity); ++ } ++ ++ media_udev_close(udev); ++ return ret; ++} ++ ++int media_device_enumerate(struct media_device *media) ++{ ++ int ret; ++ ++ if (media->entities) ++ return 0; ++ ++ ret = media_device_open(media); ++ if (ret < 0) ++ return ret; ++ ++ ret = ioctl(media->fd, MEDIA_IOC_DEVICE_INFO, &media->info); ++ if (ret < 0) { ++ ret = -errno; ++ media_dbg(media, "%s: Unable to retrieve media device " ++ "information for device %s (%s)\n", __func__, ++ media->devnode, strerror(errno)); ++ goto done; ++ } ++ ++ media_dbg(media, "Enumerating entities\n"); ++ ++ ret = media_enum_entities(media); ++ if (ret < 0) { ++ media_dbg(media, ++ "%s: Unable to enumerate entities for device %s (%s)\n", ++ __func__, media->devnode, strerror(-ret)); ++ goto done; ++ } ++ ++ media_dbg(media, "Found %u entities\n", media->entities_count); ++ media_dbg(media, "Enumerating pads and links\n"); ++ ++ ret = media_enum_links(media); ++ if (ret < 0) { ++ media_dbg(media, ++ "%s: Unable to enumerate pads and linksfor device %s\n", ++ __func__, media->devnode); ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Create/destroy ++ */ ++ ++static void media_debug_default(void *ptr, ...) ++{ ++} ++ ++void media_debug_set_handler(struct media_device *media, ++ void (*debug_handler)(void *, ...), ++ void *debug_priv) ++{ ++ if (debug_handler) { ++ media->debug_handler = debug_handler; ++ media->debug_priv = debug_priv; ++ } else { ++ media->debug_handler = media_debug_default; ++ media->debug_priv = NULL; ++ } ++} ++ ++static struct media_device *__media_device_new(void) ++{ ++ struct media_device *media; ++ ++ media = calloc(1, sizeof(*media)); ++ if (media == NULL) ++ return NULL; ++ ++ media->fd = -1; ++ media->refcount = 1; ++ ++ media_debug_set_handler(media, NULL, NULL); ++ ++ return media; ++} ++ ++struct media_device *media_device_new(const char *devnode) ++{ ++ struct media_device *media; ++ ++ media = __media_device_new(); ++ if (media == NULL) ++ return NULL; ++ ++ media->devnode = strdup(devnode); ++ if (media->devnode == NULL) { ++ media_device_unref(media); ++ return NULL; ++ } ++ ++ return media; ++} ++ ++struct media_device *media_device_new_emulated(struct media_device_info *info) ++{ ++ struct media_device *media; ++ ++ media = __media_device_new(); ++ if (media == NULL) ++ return NULL; ++ ++ media->info = *info; ++ ++ return media; ++} ++ ++struct media_device *media_device_ref(struct media_device *media) ++{ ++ media->refcount++; ++ return media; ++} ++ ++void media_device_unref(struct media_device *media) ++{ ++ unsigned int i; ++ ++ media->refcount--; ++ if (media->refcount > 0) ++ return; ++ ++ for (i = 0; i < media->entities_count; ++i) { ++ struct media_entity *entity = &media->entities[i]; ++ ++ free(entity->pads); ++ free(entity->links); ++ if (entity->fd != -1) ++ close(entity->fd); ++ } ++ ++ free(media->entities); ++ free(media->devnode); ++ free(media); ++} ++ ++int media_device_add_entity(struct media_device *media, ++ const struct media_entity_desc *desc, ++ const char *devnode) ++{ ++ struct media_entity **defent = NULL; ++ struct media_entity *entity; ++ unsigned int size; ++ ++ size = (media->entities_count + 1) * sizeof(*media->entities); ++ entity = realloc(media->entities, size); ++ if (entity == NULL) ++ return -ENOMEM; ++ ++ media->entities = entity; ++ media->entities_count++; ++ ++ entity = &media->entities[media->entities_count - 1]; ++ memset(entity, 0, sizeof *entity); ++ ++ entity->fd = -1; ++ entity->media = media; ++ strncpy(entity->devname, devnode, sizeof entity->devname); ++ entity->devname[sizeof entity->devname - 1] = '\0'; ++ ++ entity->info.id = 0; ++ entity->info.type = desc->type; ++ entity->info.flags = 0; ++ memcpy(entity->info.name, desc->name, sizeof entity->info.name); ++ ++ switch (entity->info.type) { ++ case MEDIA_ENT_T_DEVNODE_V4L: ++ defent = &media->def.v4l; ++ entity->info.v4l = desc->v4l; ++ break; ++ case MEDIA_ENT_T_DEVNODE_FB: ++ defent = &media->def.fb; ++ entity->info.fb = desc->fb; ++ break; ++ case MEDIA_ENT_T_DEVNODE_ALSA: ++ defent = &media->def.alsa; ++ entity->info.alsa = desc->alsa; ++ break; ++ case MEDIA_ENT_T_DEVNODE_DVB: ++ defent = &media->def.dvb; ++ entity->info.dvb = desc->dvb; ++ break; ++ } ++ ++ if (desc->flags & MEDIA_ENT_FL_DEFAULT) { ++ entity->info.flags |= MEDIA_ENT_FL_DEFAULT; ++ if (defent) ++ *defent = entity; ++ } ++ ++ return 0; ++} ++ ++struct media_pad *media_parse_pad(struct media_device *media, ++ const char *p, char **endp) ++{ ++ unsigned int entity_id, pad; ++ struct media_entity *entity; ++ char *end; ++ ++ /* endp can be NULL. To avoid spreading NULL checks across the function, ++ * set endp to &end in that case. ++ */ ++ if (endp == NULL) ++ endp = &end; ++ ++ for (; isspace(*p); ++p); ++ ++ if (*p == '"' || *p == '\'') { ++ for (end = (char *)p + 1; *end && *end != '"' && *end != '\''; ++end); ++ if (*end != '"' && *end != '\'') { ++ media_dbg(media, "missing matching '\"'\n"); ++ *endp = end; ++ return NULL; ++ } ++ ++ entity = media_get_entity_by_name(media, p + 1, end - p - 1); ++ if (entity == NULL) { ++ media_dbg(media, "no such entity \"%.*s\"\n", end - p - 1, p + 1); ++ *endp = (char *)p + 1; ++ return NULL; ++ } ++ ++ ++end; ++ } else { ++ entity_id = strtoul(p, &end, 10); ++ entity = media_get_entity_by_id(media, entity_id); ++ if (entity == NULL) { ++ media_dbg(media, "no such entity %d\n", entity_id); ++ *endp = (char *)p; ++ return NULL; ++ } ++ } ++ for (; isspace(*end); ++end); ++ ++ if (*end != ':') { ++ media_dbg(media, "Expected ':'\n", *end); ++ *endp = end; ++ return NULL; ++ } ++ ++ for (p = end + 1; isspace(*p); ++p); ++ ++ pad = strtoul(p, &end, 10); ++ ++ if (pad >= entity->info.pads) { ++ media_dbg(media, "No pad '%d' on entity \"%s\". Maximum pad number is %d\n", ++ pad, entity->info.name, entity->info.pads - 1); ++ *endp = (char *)p; ++ return NULL; ++ } ++ ++ for (p = end; isspace(*p); ++p); ++ *endp = (char *)p; ++ ++ return &entity->pads[pad]; ++} ++ ++struct media_link *media_parse_link(struct media_device *media, ++ const char *p, char **endp) ++{ ++ struct media_link *link; ++ struct media_pad *source; ++ struct media_pad *sink; ++ unsigned int i; ++ char *end; ++ ++ source = media_parse_pad(media, p, &end); ++ if (source == NULL) { ++ *endp = end; ++ return NULL; ++ } ++ ++ if (end[0] != '-' || end[1] != '>') { ++ *endp = end; ++ media_dbg(media, "Expected '->'\n"); ++ return NULL; ++ } ++ ++ p = end + 2; ++ ++ sink = media_parse_pad(media, p, &end); ++ if (sink == NULL) { ++ *endp = end; ++ return NULL; ++ } ++ ++ *endp = end; ++ ++ for (i = 0; i < source->entity->num_links; i++) { ++ link = &source->entity->links[i]; ++ ++ if (link->source == source && link->sink == sink) ++ return link; ++ } ++ ++ media_dbg(media, "No link between \"%s\":%d and \"%s\":%d\n", ++ source->entity->info.name, source->index, ++ sink->entity->info.name, sink->index); ++ return NULL; ++} ++ ++int media_parse_setup_link(struct media_device *media, ++ const char *p, char **endp) ++{ ++ struct media_link *link; ++ __u32 flags; ++ char *end; ++ ++ link = media_parse_link(media, p, &end); ++ if (link == NULL) { ++ media_dbg(media, ++ "%s: Unable to parse link\n", __func__); ++ *endp = end; ++ return -EINVAL; ++ } ++ ++ p = end; ++ if (*p++ != '[') { ++ media_dbg(media, "Unable to parse link flags: expected '['.\n"); ++ *endp = (char *)p - 1; ++ return -EINVAL; ++ } ++ ++ flags = strtoul(p, &end, 10); ++ for (p = end; isspace(*p); p++); ++ if (*p++ != ']') { ++ media_dbg(media, "Unable to parse link flags: expected ']'.\n"); ++ *endp = (char *)p - 1; ++ return -EINVAL; ++ } ++ ++ for (; isspace(*p); p++); ++ *endp = (char *)p; ++ ++ media_dbg(media, ++ "Setting up link %u:%u -> %u:%u [%u]\n", ++ link->source->entity->info.id, link->source->index, ++ link->sink->entity->info.id, link->sink->index, ++ flags); ++ ++ return media_setup_link(media, link->source, link->sink, flags); ++} ++ ++void media_print_streampos(struct media_device *media, const char *p, ++ const char *end) ++{ ++ int pos; ++ ++ pos = end - p + 1; ++ ++ if (pos < 0) ++ pos = 0; ++ if (pos > strlen(p)) ++ pos = strlen(p); ++ ++ media_dbg(media, "\n"); ++ media_dbg(media, " %s\n", p); ++ media_dbg(media, " %*s\n", pos, "^"); ++} ++ ++int media_parse_setup_links(struct media_device *media, const char *p) ++{ ++ char *end; ++ int ret; ++ ++ do { ++ ret = media_parse_setup_link(media, p, &end); ++ if (ret < 0) { ++ media_print_streampos(media, p, end); ++ return ret; ++ } ++ ++ p = end + 1; ++ } while (*end == ','); ++ ++ return *end ? -EINVAL : 0; ++} +diff --git a/src/media-ctl/libv4l2subdev.c b/src/media-ctl/libv4l2subdev.c +new file mode 100644 +index 0000000..4ede4fa +--- /dev/null ++++ b/src/media-ctl/libv4l2subdev.c +@@ -0,0 +1,759 @@ ++/* ++ * V4L2 subdev interface library ++ * ++ * Copyright (C) 2010-2014 Ideas on board SPRL ++ * ++ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <sys/ioctl.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++ ++#include <ctype.h> ++#include <errno.h> ++#include <fcntl.h> ++#include <stdbool.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <unistd.h> ++ ++#include <linux/v4l2-subdev.h> ++ ++#include "mediactl.h" ++#include "mediactl-priv.h" ++#include "tools.h" ++#include "v4l2subdev.h" ++ ++int v4l2_subdev_open(struct media_entity *entity) ++{ ++ if (entity->fd != -1) ++ return 0; ++ ++ entity->fd = open(entity->devname, O_RDWR); ++ if (entity->fd == -1) { ++ int ret = -errno; ++ media_dbg(entity->media, ++ "%s: Failed to open subdev device node %s\n", __func__, ++ entity->devname); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++void v4l2_subdev_close(struct media_entity *entity) ++{ ++ close(entity->fd); ++ entity->fd = -1; ++} ++ ++int v4l2_subdev_get_format(struct media_entity *entity, ++ struct v4l2_mbus_framefmt *format, unsigned int pad, ++ enum v4l2_subdev_format_whence which) ++{ ++ struct v4l2_subdev_format fmt; ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(&fmt, 0, sizeof(fmt)); ++ fmt.pad = pad; ++ fmt.which = which; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt); ++ if (ret < 0) ++ return -errno; ++ ++ *format = fmt.format; ++ return 0; ++} ++ ++int v4l2_subdev_set_format(struct media_entity *entity, ++ struct v4l2_mbus_framefmt *format, unsigned int pad, ++ enum v4l2_subdev_format_whence which) ++{ ++ struct v4l2_subdev_format fmt; ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(&fmt, 0, sizeof(fmt)); ++ fmt.pad = pad; ++ fmt.which = which; ++ fmt.format = *format; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FMT, &fmt); ++ if (ret < 0) ++ return -errno; ++ ++ *format = fmt.format; ++ return 0; ++} ++ ++int v4l2_subdev_get_selection(struct media_entity *entity, ++ struct v4l2_rect *rect, unsigned int pad, unsigned int target, ++ enum v4l2_subdev_format_whence which) ++{ ++ union { ++ struct v4l2_subdev_selection sel; ++ struct v4l2_subdev_crop crop; ++ } u; ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(&u.sel, 0, sizeof(u.sel)); ++ u.sel.pad = pad; ++ u.sel.target = target; ++ u.sel.which = which; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_SELECTION, &u.sel); ++ if (ret >= 0) { ++ *rect = u.sel.r; ++ return 0; ++ } ++ if (errno != ENOTTY || target != V4L2_SEL_TGT_CROP) ++ return -errno; ++ ++ memset(&u.crop, 0, sizeof(u.crop)); ++ u.crop.pad = pad; ++ u.crop.which = which; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_CROP, &u.crop); ++ if (ret < 0) ++ return -errno; ++ ++ *rect = u.crop.rect; ++ return 0; ++} ++ ++int v4l2_subdev_set_selection(struct media_entity *entity, ++ struct v4l2_rect *rect, unsigned int pad, unsigned int target, ++ enum v4l2_subdev_format_whence which) ++{ ++ union { ++ struct v4l2_subdev_selection sel; ++ struct v4l2_subdev_crop crop; ++ } u; ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(&u.sel, 0, sizeof(u.sel)); ++ u.sel.pad = pad; ++ u.sel.target = target; ++ u.sel.which = which; ++ u.sel.r = *rect; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_SELECTION, &u.sel); ++ if (ret >= 0) { ++ *rect = u.sel.r; ++ return 0; ++ } ++ if (errno != ENOTTY || target != V4L2_SEL_TGT_CROP) ++ return -errno; ++ ++ memset(&u.crop, 0, sizeof(u.crop)); ++ u.crop.pad = pad; ++ u.crop.which = which; ++ u.crop.rect = *rect; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_CROP, &u.crop); ++ if (ret < 0) ++ return -errno; ++ ++ *rect = u.crop.rect; ++ return 0; ++} ++ ++#if 0 ++int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity, ++ struct v4l2_dv_timings_cap *caps) ++{ ++ unsigned int pad = caps->pad; ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(caps, 0, sizeof(*caps)); ++ caps->pad = pad; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_DV_TIMINGS_CAP, caps); ++ if (ret < 0) ++ return -errno; ++ ++ return 0; ++} ++ ++int v4l2_subdev_query_dv_timings(struct media_entity *entity, ++ struct v4l2_dv_timings *timings) ++{ ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(timings, 0, sizeof(*timings)); ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_QUERY_DV_TIMINGS, timings); ++ if (ret < 0) ++ return -errno; ++ ++ return 0; ++} ++ ++int v4l2_subdev_get_dv_timings(struct media_entity *entity, ++ struct v4l2_dv_timings *timings) ++{ ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(timings, 0, sizeof(*timings)); ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_DV_TIMINGS, timings); ++ if (ret < 0) ++ return -errno; ++ ++ return 0; ++} ++ ++int v4l2_subdev_set_dv_timings(struct media_entity *entity, ++ struct v4l2_dv_timings *timings) ++{ ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_DV_TIMINGS, timings); ++ if (ret < 0) ++ return -errno; ++ ++ return 0; ++} ++#endif ++ ++int v4l2_subdev_get_frame_interval(struct media_entity *entity, ++ struct v4l2_fract *interval) ++{ ++ struct v4l2_subdev_frame_interval ival; ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(&ival, 0, sizeof(ival)); ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival); ++ if (ret < 0) ++ return -errno; ++ ++ *interval = ival.interval; ++ return 0; ++} ++ ++int v4l2_subdev_set_frame_interval(struct media_entity *entity, ++ struct v4l2_fract *interval) ++{ ++ struct v4l2_subdev_frame_interval ival; ++ int ret; ++ ++ ret = v4l2_subdev_open(entity); ++ if (ret < 0) ++ return ret; ++ ++ memset(&ival, 0, sizeof(ival)); ++ ival.interval = *interval; ++ ++ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival); ++ if (ret < 0) ++ return -errno; ++ ++ *interval = ival.interval; ++ return 0; ++} ++ ++static int v4l2_subdev_parse_format(struct media_device *media, ++ struct v4l2_mbus_framefmt *format, ++ const char *p, char **endp) ++{ ++ enum v4l2_mbus_pixelcode code; ++ unsigned int width, height; ++ char *end; ++ ++ /* ++ * Compatibility with the old syntax: consider space as valid ++ * separator between the media bus pixel code and the size. ++ */ ++ for (; isspace(*p); ++p); ++ for (end = (char *)p; ++ *end != '/' && *end != ' ' && *end != '\0'; ++end); ++ ++ code = v4l2_subdev_string_to_pixelcode(p, end - p); ++ if (code == (enum v4l2_mbus_pixelcode)-1) { ++ media_dbg(media, "Invalid pixel code '%.*s'\n", end - p, p); ++ return -EINVAL; ++ } ++ ++ p = end + 1; ++ width = strtoul(p, &end, 10); ++ if (*end != 'x') { ++ media_dbg(media, "Expected 'x'\n"); ++ return -EINVAL; ++ } ++ ++ p = end + 1; ++ height = strtoul(p, &end, 10); ++ *endp = end; ++ ++ memset(format, 0, sizeof(*format)); ++ format->width = width; ++ format->height = height; ++ format->code = code; ++ ++ return 0; ++} ++ ++static int v4l2_subdev_parse_rectangle(struct media_device *media, ++ struct v4l2_rect *r, const char *p, ++ char **endp) ++{ ++ char *end; ++ ++ if (*p++ != '(') { ++ media_dbg(media, "Expected '('\n"); ++ *endp = (char *)p - 1; ++ return -EINVAL; ++ } ++ ++ r->left = strtoul(p, &end, 10); ++ if (*end != ',') { ++ media_dbg(media, "Expected ','\n"); ++ *endp = end; ++ return -EINVAL; ++ } ++ ++ p = end + 1; ++ r->top = strtoul(p, &end, 10); ++ if (*end++ != ')') { ++ media_dbg(media, "Expected ')'\n"); ++ *endp = end - 1; ++ return -EINVAL; ++ } ++ if (*end != '/') { ++ media_dbg(media, "Expected '/'\n"); ++ *endp = end; ++ return -EINVAL; ++ } ++ ++ p = end + 1; ++ r->width = strtoul(p, &end, 10); ++ if (*end != 'x') { ++ media_dbg(media, "Expected 'x'\n"); ++ *endp = end; ++ return -EINVAL; ++ } ++ ++ p = end + 1; ++ r->height = strtoul(p, &end, 10); ++ *endp = end; ++ ++ return 0; ++} ++ ++static int v4l2_subdev_parse_frame_interval(struct media_device *media, ++ struct v4l2_fract *interval, ++ const char *p, char **endp) ++{ ++ char *end; ++ ++ for (; isspace(*p); ++p); ++ ++ interval->numerator = strtoul(p, &end, 10); ++ ++ for (p = end; isspace(*p); ++p); ++ if (*p++ != '/') { ++ media_dbg(media, "Expected '/'\n"); ++ *endp = (char *)p - 1; ++ return -EINVAL; ++ } ++ ++ for (; isspace(*p); ++p); ++ interval->denominator = strtoul(p, &end, 10); ++ ++ *endp = end; ++ return 0; ++} ++ ++/* ++ * The debate over whether this function should be named icanhasstr() instead ++ * has been strong and heated. If you feel like this would be an important ++ * change, patches are welcome (or not). ++ */ ++static bool strhazit(const char *str, const char **p) ++{ ++ int len = strlen(str); ++ ++ if (strncmp(str, *p, len)) ++ return false; ++ ++ for (*p += len; isspace(**p); ++*p); ++ return true; ++} ++ ++static struct media_pad *v4l2_subdev_parse_pad_format( ++ struct media_device *media, struct v4l2_mbus_framefmt *format, ++ struct v4l2_rect *crop, struct v4l2_rect *compose, ++ struct v4l2_fract *interval, const char *p, char **endp) ++{ ++ struct media_pad *pad; ++ bool first; ++ char *end; ++ int ret; ++ ++ for (; isspace(*p); ++p); ++ ++ pad = media_parse_pad(media, p, &end); ++ if (pad == NULL) { ++ *endp = end; ++ return NULL; ++ } ++ ++ for (p = end; isspace(*p); ++p); ++ if (*p++ != '[') { ++ media_dbg(media, "Expected '['\n"); ++ *endp = (char *)p - 1; ++ return NULL; ++ } ++ ++ for (first = true; ; first = false) { ++ for (; isspace(*p); p++); ++ ++ /* ++ * Backward compatibility: if the first property starts with an ++ * uppercase later, process it as a format description. ++ */ ++ if (strhazit("fmt:", &p) || (first && isupper(*p))) { ++ ret = v4l2_subdev_parse_format(media, format, p, &end); ++ if (ret < 0) { ++ *endp = end; ++ return NULL; ++ } ++ ++ p = end; ++ continue; ++ } ++ ++ /* ++ * Backward compatibility: crop rectangles can be specified ++ * implicitly without the 'crop:' property name. ++ */ ++ if (strhazit("crop:", &p) || *p == '(') { ++ ret = v4l2_subdev_parse_rectangle(media, crop, p, &end); ++ if (ret < 0) { ++ *endp = end; ++ return NULL; ++ } ++ ++ p = end; ++ continue; ++ } ++ ++ if (strhazit("compose:", &p)) { ++ ret = v4l2_subdev_parse_rectangle(media, compose, p, &end); ++ if (ret < 0) { ++ *endp = end; ++ return NULL; ++ } ++ ++ for (p = end; isspace(*p); p++); ++ continue; ++ } ++ ++ if (*p == '@') { ++ ret = v4l2_subdev_parse_frame_interval(media, interval, ++p, &end); ++ if (ret < 0) { ++ *endp = end; ++ return NULL; ++ } ++ ++ p = end; ++ continue; ++ } ++ ++ break; ++ } ++ ++ if (*p != ']') { ++ media_dbg(media, "Expected ']'\n"); ++ *endp = (char *)p; ++ return NULL; ++ } ++ ++ *endp = (char *)p + 1; ++ return pad; ++} ++ ++static int set_format(struct media_pad *pad, ++ struct v4l2_mbus_framefmt *format) ++{ ++ int ret; ++ ++ if (format->width == 0 || format->height == 0) ++ return 0; ++ ++ media_dbg(pad->entity->media, ++ "Setting up format %s %ux%u on pad %s/%u\n", ++ v4l2_subdev_pixelcode_to_string(format->code), ++ format->width, format->height, ++ pad->entity->info.name, pad->index); ++ ++ ret = v4l2_subdev_set_format(pad->entity, format, pad->index, ++ V4L2_SUBDEV_FORMAT_ACTIVE); ++ if (ret < 0) { ++ media_dbg(pad->entity->media, ++ "Unable to set format: %s (%d)\n", ++ strerror(-ret), ret); ++ return ret; ++ } ++ ++ media_dbg(pad->entity->media, ++ "Format set: %s %ux%u\n", ++ v4l2_subdev_pixelcode_to_string(format->code), ++ format->width, format->height); ++ ++ return 0; ++} ++ ++static int set_selection(struct media_pad *pad, unsigned int target, ++ struct v4l2_rect *rect) ++{ ++ int ret; ++ ++ if (rect->left == -1 || rect->top == -1) ++ return 0; ++ ++ media_dbg(pad->entity->media, ++ "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u\n", ++ target, rect->left, rect->top, rect->width, rect->height, ++ pad->entity->info.name, pad->index); ++ ++ ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index, ++ target, V4L2_SUBDEV_FORMAT_ACTIVE); ++ if (ret < 0) { ++ media_dbg(pad->entity->media, ++ "Unable to set selection rectangle: %s (%d)\n", ++ strerror(-ret), ret); ++ return ret; ++ } ++ ++ media_dbg(pad->entity->media, ++ "Selection rectangle set: (%u,%u)/%ux%u\n", ++ rect->left, rect->top, rect->width, rect->height); ++ ++ return 0; ++} ++ ++static int set_frame_interval(struct media_entity *entity, ++ struct v4l2_fract *interval) ++{ ++ int ret; ++ ++ if (interval->numerator == 0) ++ return 0; ++ ++ media_dbg(entity->media, ++ "Setting up frame interval %u/%u on entity %s\n", ++ interval->numerator, interval->denominator, ++ entity->info.name); ++ ++ ret = v4l2_subdev_set_frame_interval(entity, interval); ++ if (ret < 0) { ++ media_dbg(entity->media, ++ "Unable to set frame interval: %s (%d)", ++ strerror(-ret), ret); ++ return ret; ++ } ++ ++ media_dbg(entity->media, "Frame interval set: %u/%u\n", ++ interval->numerator, interval->denominator); ++ ++ return 0; ++} ++ ++ ++static int v4l2_subdev_parse_setup_format(struct media_device *media, ++ const char *p, char **endp) ++{ ++ struct v4l2_mbus_framefmt format = { 0, 0, 0 }; ++ struct media_pad *pad; ++ struct v4l2_rect crop = { -1, -1, -1, -1 }; ++ struct v4l2_rect compose = crop; ++ struct v4l2_fract interval = { 0, 0 }; ++ unsigned int i; ++ char *end; ++ int ret; ++ ++ pad = v4l2_subdev_parse_pad_format(media, &format, &crop, &compose, ++ &interval, p, &end); ++ if (pad == NULL) { ++ media_print_streampos(media, p, end); ++ media_dbg(media, "Unable to parse format\n"); ++ return -EINVAL; ++ } ++ ++ if (pad->flags & MEDIA_PAD_FL_SINK) { ++ ret = set_format(pad, &format); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = set_selection(pad, V4L2_SEL_TGT_CROP, &crop); ++ if (ret < 0) ++ return ret; ++ ++ ret = set_selection(pad, V4L2_SEL_TGT_COMPOSE, &compose); ++ if (ret < 0) ++ return ret; ++ ++ if (pad->flags & MEDIA_PAD_FL_SOURCE) { ++ ret = set_format(pad, &format); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = set_frame_interval(pad->entity, &interval); ++ if (ret < 0) ++ return ret; ++ ++ ++ /* If the pad is an output pad, automatically set the same format on ++ * the remote subdev input pads, if any. ++ */ ++ if (pad->flags & MEDIA_PAD_FL_SOURCE) { ++ for (i = 0; i < pad->entity->num_links; ++i) { ++ struct media_link *link = &pad->entity->links[i]; ++ struct v4l2_mbus_framefmt remote_format; ++ ++ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) ++ continue; ++ ++ if (link->source == pad && ++ link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) { ++ remote_format = format; ++ set_format(link->sink, &remote_format); ++ } ++ } ++ } ++ ++ *endp = end; ++ return 0; ++} ++ ++int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p) ++{ ++ char *end; ++ int ret; ++ ++ do { ++ ret = v4l2_subdev_parse_setup_format(media, p, &end); ++ if (ret < 0) ++ return ret; ++ ++ p = end + 1; ++ } while (*end == ','); ++ ++ return *end ? -EINVAL : 0; ++} ++ ++static struct { ++ const char *name; ++ enum v4l2_mbus_pixelcode code; ++} mbus_formats[] = { ++ { "Y8", V4L2_MBUS_FMT_Y8_1X8}, ++ { "Y10", V4L2_MBUS_FMT_Y10_1X10 }, ++ { "Y12", V4L2_MBUS_FMT_Y12_1X12 }, ++ { "YUYV", V4L2_MBUS_FMT_YUYV8_1X16 }, ++ { "YUYV1_5X8", V4L2_MBUS_FMT_YUYV8_1_5X8 }, ++ { "YUYV2X8", V4L2_MBUS_FMT_YUYV8_2X8 }, ++ { "UYVY", V4L2_MBUS_FMT_UYVY8_1X16 }, ++ { "UYVY1_5X8", V4L2_MBUS_FMT_UYVY8_1_5X8 }, ++ { "UYVY2X8", V4L2_MBUS_FMT_UYVY8_2X8 }, ++ { "SBGGR8", V4L2_MBUS_FMT_SBGGR8_1X8 }, ++ { "SGBRG8", V4L2_MBUS_FMT_SGBRG8_1X8 }, ++ { "SGRBG8", V4L2_MBUS_FMT_SGRBG8_1X8 }, ++ { "SRGGB8", V4L2_MBUS_FMT_SRGGB8_1X8 }, ++ { "SBGGR10", V4L2_MBUS_FMT_SBGGR10_1X10 }, ++ { "SGBRG10", V4L2_MBUS_FMT_SGBRG10_1X10 }, ++ { "SGRBG10", V4L2_MBUS_FMT_SGRBG10_1X10 }, ++ { "SRGGB10", V4L2_MBUS_FMT_SRGGB10_1X10 }, ++ { "SBGGR10_DPCM8", V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 }, ++ { "SGBRG10_DPCM8", V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 }, ++ { "SGRBG10_DPCM8", V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 }, ++ { "SRGGB10_DPCM8", V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 }, ++ { "SBGGR12", V4L2_MBUS_FMT_SBGGR12_1X12 }, ++ { "SGBRG12", V4L2_MBUS_FMT_SGBRG12_1X12 }, ++ { "SGRBG12", V4L2_MBUS_FMT_SGRBG12_1X12 }, ++ { "SRGGB12", V4L2_MBUS_FMT_SRGGB12_1X12 }, ++ { "AYUV32", V4L2_MBUS_FMT_AYUV8_1X32 }, ++ { "ARGB32", V4L2_MBUS_FMT_ARGB8888_1X32 }, ++}; ++ ++const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { ++ if (mbus_formats[i].code == code) ++ return mbus_formats[i].name; ++ } ++ ++ return "unknown"; ++} ++ ++enum v4l2_mbus_pixelcode v4l2_subdev_string_to_pixelcode(const char *string, ++ unsigned int length) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { ++ if (strncmp(mbus_formats[i].name, string, length) == 0) ++ break; ++ } ++ ++ if (i == ARRAY_SIZE(mbus_formats)) ++ return (enum v4l2_mbus_pixelcode)-1; ++ ++ return mbus_formats[i].code; ++} +diff --git a/src/media-ctl/mediactl-priv.h b/src/media-ctl/mediactl-priv.h +new file mode 100644 +index 0000000..a0d3a55 +--- /dev/null ++++ b/src/media-ctl/mediactl-priv.h +@@ -0,0 +1,64 @@ ++/* ++ * Media controller interface library ++ * ++ * Copyright (C) 2010-2014 Ideas on board SPRL ++ * ++ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __MEDIA_PRIV_H__ ++#define __MEDIA_PRIV_H__ ++ ++#include <linux/media.h> ++ ++#include "mediactl.h" ++ ++struct media_entity { ++ struct media_device *media; ++ struct media_entity_desc info; ++ struct media_pad *pads; ++ struct media_link *links; ++ unsigned int max_links; ++ unsigned int num_links; ++ ++ char devname[32]; ++ int fd; ++}; ++ ++struct media_device { ++ int fd; ++ int refcount; ++ char *devnode; ++ ++ struct media_device_info info; ++ struct media_entity *entities; ++ unsigned int entities_count; ++ ++ void (*debug_handler)(void *, ...); ++ void *debug_priv; ++ ++ struct { ++ struct media_entity *v4l; ++ struct media_entity *fb; ++ struct media_entity *alsa; ++ struct media_entity *dvb; ++ } def; ++}; ++ ++#define media_dbg(media, ...) \ ++ (media)->debug_handler((media)->debug_priv, __VA_ARGS__) ++ ++#endif /* __MEDIA_PRIV_H__ */ +diff --git a/src/media-ctl/mediactl.h b/src/media-ctl/mediactl.h +new file mode 100644 +index 0000000..77ac182 +--- /dev/null ++++ b/src/media-ctl/mediactl.h +@@ -0,0 +1,423 @@ ++/* ++ * Media controller interface library ++ * ++ * Copyright (C) 2010-2014 Ideas on board SPRL ++ * ++ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __MEDIA_H__ ++#define __MEDIA_H__ ++ ++#include <linux/media.h> ++ ++struct media_link { ++ struct media_pad *source; ++ struct media_pad *sink; ++ struct media_link *twin; ++ __u32 flags; ++ __u32 padding[3]; ++}; ++ ++struct media_pad { ++ struct media_entity *entity; ++ __u32 index; ++ __u32 flags; ++ __u32 padding[3]; ++}; ++ ++struct media_device; ++struct media_entity; ++ ++/** ++ * @brief Create a new media device. ++ * @param devnode - device node path. ++ * ++ * Create a media device instance for the given device node and return it. The ++ * device node is not accessed by this function, device node access errors will ++ * not be caught and reported here. The media device needs to be enumerated ++ * before it can be accessed, see media_device_enumerate(). ++ * ++ * Media devices are reference-counted, see media_device_ref() and ++ * media_device_unref() for more information. ++ * ++ * @return A pointer to the new media device or NULL if memory cannot be ++ * allocated. ++ */ ++struct media_device *media_device_new(const char *devnode); ++ ++/** ++ * @brief Create a new emulated media device. ++ * @param info - device information. ++ * ++ * Emulated media devices are userspace-only objects not backed by a kernel ++ * media device. They are created for ALSA and V4L2 devices that are not ++ * associated with a media controller device. ++ * ++ * Only device query functions are available for media devices. Enumerating or ++ * setting up links is invalid. ++ * ++ * @return A pointer to the new media device or NULL if memory cannot be ++ * allocated. ++ */ ++struct media_device *media_device_new_emulated(struct media_device_info *info); ++ ++/** ++ * @brief Take a reference to the device. ++ * @param media - device instance. ++ * ++ * Media devices are reference-counted. Taking a reference to a device prevents ++ * it from being freed until all references are released. The reference count is ++ * initialized to 1 when the device is created. ++ * ++ * @return A pointer to @a media. ++ */ ++struct media_device *media_device_ref(struct media_device *media); ++ ++/** ++ * @brief Release a reference to the device. ++ * @param media - device instance. ++ * ++ * Release a reference to the media device. When the reference count reaches 0 ++ * this function frees the device. ++ */ ++void media_device_unref(struct media_device *media); ++ ++/** ++ * @brief Add an entity to an existing media device ++ * @param media - device instance. ++ * @param desc - description of the entity to be added ++ * @param devnode - device node corresponding to the entity ++ * ++ * Entities are usually created and added to media devices automatically when ++ * the media device is enumerated through the media controller API. However, ++ * when an emulated media device (thus not backed with a kernel-side media ++ * controller device) is created, entities need to be manually added. ++ * ++ * Entities can also be manually added to a successfully enumerated media device ++ * to group several functions provided by separate kernel devices. The most ++ * common use case is to group the audio and video functions of a USB webcam in ++ * a single media device. Those functions are exposed through separate USB ++ * interfaces and handled through unrelated kernel drivers, they must thus be ++ * manually added to the same media device. ++ * ++ * This function adds a new entity to the given media device and initializes it ++ * from the given entity description and device node name. Only the following ++ * fields of the description are copied over to the new entity: ++ * ++ * - type ++ * - flags (MEDIA_ENT_FL_DEFAULT only) ++ * - name ++ * - v4l, fb, alsa or dvb (depending on the device type) ++ * ++ * All other fields of the newly created entity id are initialized to 0, ++ * including the entity ID. ++ * ++ * @return Zero on success or -ENOMEM if memory cannot be allocated. ++ */ ++int media_device_add_entity(struct media_device *media, ++ const struct media_entity_desc *desc, ++ const char *devnode); ++ ++/** ++ * @brief Set a handler for debug messages. ++ * @param media - device instance. ++ * @param debug_handler - debug message handler ++ * @param debug_priv - first argument to debug message handler ++ * ++ * Set a handler for debug messages that will be called whenever ++ * debugging information is to be printed. The handler expects an ++ * fprintf-like function. ++ */ ++void media_debug_set_handler( ++ struct media_device *media, void (*debug_handler)(void *, ...), ++ void *debug_priv); ++ ++/** ++ * @brief Enumerate the device topology ++ * @param media - device instance. ++ * ++ * Enumerate the media device entities, pads and links. Calling this function is ++ * mandatory before accessing the media device contents. ++ * ++ * @return Zero on success or a negative error code on failure. ++ */ ++int media_device_enumerate(struct media_device *media); ++ ++/** ++ * @brief Locate the pad at the other end of a link. ++ * @param pad - sink pad at one end of the link. ++ * ++ * Locate the source pad connected to @a pad through an enabled link. As only one ++ * link connected to a sink pad can be enabled at a time, the connected source ++ * pad is guaranteed to be unique. ++ * ++ * @return A pointer to the connected source pad, or NULL if all links connected ++ * to @a pad are disabled. Return NULL also if @a pad is not a sink pad. ++ */ ++struct media_pad *media_entity_remote_source(struct media_pad *pad); ++ ++/** ++ * @brief Get information about a media entity ++ * @param entity - media entity. ++ * ++ * The information structure is owned by the media entity object and will be ++ * freed when the object is destroyed. ++ * ++ * @return A pointer to the media entity information ++ */ ++const struct media_entity_desc *media_entity_get_info(struct media_entity *entity); ++ ++/** ++ * @brief Get an entity pad ++ * @param entity - media entity. ++ * @param index - pad index. ++ * ++ * This function returns a pointer to the pad object identified by its index ++ * for the given entity. If the pad index is out of bounds it will return NULL. ++ * ++ * @return A pointer to the pad ++ */ ++const struct media_pad *media_entity_get_pad(struct media_entity *entity, ++ unsigned int index); ++ ++/** ++ * @brief Get the number of links ++ * @param entity - media entity. ++ * ++ * This function returns the total number of links that originate from or arrive ++ * at the the media entity. ++ * ++ * @return The number of links for the entity ++ */ ++unsigned int media_entity_get_links_count(struct media_entity *entity); ++ ++/** ++ * @brief Get an entity link ++ * @param entity - media entity. ++ * @param index - link index. ++ * ++ * This function returns a pointer to the link object identified by its index ++ * for the given entity. If the link index is out of bounds it will return NULL. ++ * ++ * @return A pointer to the link ++ */ ++const struct media_link *media_entity_get_link(struct media_entity *entity, ++ unsigned int index); ++ ++/** ++ * @brief Get the device node name for an entity ++ * @param entity - media entity. ++ * ++ * This function returns the full path and name to the device node corresponding ++ * to the given entity. ++ * ++ * @return A pointer to the device node name or NULL if the entity has no ++ * associated device node ++ */ ++const char *media_entity_get_devname(struct media_entity *entity); ++ ++/** ++ * @brief Get the type of an entity. ++ * @param entity - the entity. ++ * ++ * @return The type of @a entity. ++ */ ++static inline unsigned int media_entity_type(struct media_entity *entity) ++{ ++ return media_entity_get_info(entity)->type & MEDIA_ENT_TYPE_MASK; ++} ++ ++/** ++ * @brief Find an entity by its name. ++ * @param media - media device. ++ * @param name - entity name. ++ * @param length - size of @a name. ++ * ++ * Search for an entity with a name equal to @a name. ++ * ++ * @return A pointer to the entity if found, or NULL otherwise. ++ */ ++struct media_entity *media_get_entity_by_name(struct media_device *media, ++ const char *name, size_t length); ++ ++/** ++ * @brief Find an entity by its ID. ++ * @param media - media device. ++ * @param id - entity ID. ++ * ++ * This function searches for an entity based on its ID using an exact match or ++ * next ID method based on the given @a id. If @a id is ORed with ++ * MEDIA_ENT_ID_FLAG_NEXT, the function will return the entity with the smallest ++ * ID larger than @a id. Otherwise it will return the entity with an ID equal to ++ * @a id. ++ * ++ * @return A pointer to the entity if found, or NULL otherwise. ++ */ ++struct media_entity *media_get_entity_by_id(struct media_device *media, ++ __u32 id); ++ ++/** ++ * @brief Get the number of entities ++ * @param media - media device. ++ * ++ * This function returns the total number of entities in the media device. If ++ * entities haven't been enumerated yet it will return 0. ++ * ++ * @return The number of entities in the media device ++ */ ++unsigned int media_get_entities_count(struct media_device *media); ++ ++/** ++ * @brief Get the entities ++ * @param media - media device. ++ * ++ * This function returns a pointer to the array of entities for the media ++ * device. If entities haven't been enumerated yet it will return NULL. ++ * ++ * The array of entities is owned by the media device object and will be freed ++ * when the media object is destroyed. ++ * ++ * @return A pointer to an array of entities ++ */ ++struct media_entity *media_get_entity(struct media_device *media, unsigned int index); ++ ++/** ++ * @brief Get the default entity for a given type ++ * @param media - media device. ++ * @param type - entity type. ++ * ++ * This function returns the default entity of the requested type. @a type must ++ * be one of ++ * ++ * MEDIA_ENT_T_DEVNODE_V4L ++ * MEDIA_ENT_T_DEVNODE_FB ++ * MEDIA_ENT_T_DEVNODE_ALSA ++ * MEDIA_ENT_T_DEVNODE_DVB ++ * ++ * @return A pointer to the default entity for the type if it exists, or NULL ++ * otherwise. ++ */ ++struct media_entity *media_get_default_entity(struct media_device *media, ++ unsigned int type); ++ ++/** ++ * @brief Get the media device information ++ * @param media - media device. ++ * ++ * The information structure is owned by the media device object and will be freed ++ * when the media object is destroyed. ++ * ++ * @return A pointer to the media device information ++ */ ++const struct media_device_info *media_get_info(struct media_device *media); ++ ++/** ++ * @brief Get the media device node name ++ * @param media - media device. ++ * ++ * The device node name string is owned by the media device object and will be ++ * freed when the media object is destroyed. ++ * ++ * @return A pointer to the media device node name ++ */ ++const char *media_get_devnode(struct media_device *media); ++ ++/** ++ * @brief Configure a link. ++ * @param media - media device. ++ * @param source - source pad at the link origin. ++ * @param sink - sink pad at the link target. ++ * @param flags - configuration flags. ++ * ++ * Locate the link between @a source and @a sink, and configure it by applying ++ * the new @a flags. ++ * ++ * Only the MEDIA_LINK_FLAG_ENABLED flag is writable. ++ * ++ * @return 0 on success, -1 on failure: ++ * -ENOENT: link not found ++ * - other error codes returned by MEDIA_IOC_SETUP_LINK ++ */ ++int media_setup_link(struct media_device *media, ++ struct media_pad *source, struct media_pad *sink, ++ __u32 flags); ++ ++/** ++ * @brief Reset all links to the disabled state. ++ * @param media - media device. ++ * ++ * Disable all links in the media device. This function is usually used after ++ * opening a media device to reset all links to a known state. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int media_reset_links(struct media_device *media); ++ ++/** ++ * @brief Parse string to a pad on the media device. ++ * @param media - media device. ++ * @param p - input string ++ * @param endp - pointer to string where parsing ended ++ * ++ * Parse NULL terminated string describing a pad and return its struct ++ * media_pad instance. ++ * ++ * @return Pointer to struct media_pad on success, NULL on failure. ++ */ ++struct media_pad *media_parse_pad(struct media_device *media, ++ const char *p, char **endp); ++ ++/** ++ * @brief Parse string to a link on the media device. ++ * @param media - media device. ++ * @param p - input string ++ * @param endp - pointer to p where parsing ended ++ * ++ * Parse NULL terminated string p describing a link and return its struct ++ * media_link instance. ++ * ++ * @return Pointer to struct media_link on success, NULL on failure. ++ */ ++struct media_link *media_parse_link(struct media_device *media, ++ const char *p, char **endp); ++ ++/** ++ * @brief Parse string to a link on the media device and set it up. ++ * @param media - media device. ++ * @param p - input string ++ * ++ * Parse NULL terminated string p describing a link and its configuration ++ * and configure the link. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int media_parse_setup_link(struct media_device *media, ++ const char *p, char **endp); ++ ++/** ++ * @brief Parse string to link(s) on the media device and set it up. ++ * @param media - media device. ++ * @param p - input string ++ * ++ * Parse NULL terminated string p describing link(s) separated by ++ * commas (,) and configure the link(s). ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int media_parse_setup_links(struct media_device *media, const char *p); ++ ++#endif +diff --git a/src/media-ctl/tools.h b/src/media-ctl/tools.h +new file mode 100644 +index 0000000..815534c +--- /dev/null ++++ b/src/media-ctl/tools.h +@@ -0,0 +1,32 @@ ++/* ++ * Media controller test application ++ * ++ * Copyright (C) 2010-2014 Ideas on board SPRL ++ * ++ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __TOOLS_H__ ++#define __TOOLS_H__ ++ ++#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) ++#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) ++ ++void media_print_streampos(struct media_device *media, const char *p, ++ const char *end); ++ ++#endif /* __TOOLS_H__ */ ++ +diff --git a/src/media-ctl/v4l2subdev.h b/src/media-ctl/v4l2subdev.h +new file mode 100644 +index 0000000..1cb53ff +--- /dev/null ++++ b/src/media-ctl/v4l2subdev.h +@@ -0,0 +1,258 @@ ++/* ++ * V4L2 subdev interface library ++ * ++ * Copyright (C) 2010-2014 Ideas on board SPRL ++ * ++ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __SUBDEV_H__ ++#define __SUBDEV_H__ ++ ++#include <linux/v4l2-subdev.h> ++ ++struct media_entity; ++ ++/** ++ * @brief Open a sub-device. ++ * @param entity - sub-device media entity. ++ * ++ * Open the V4L2 subdev device node associated with @a entity. The file ++ * descriptor is stored in the media_entity structure. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_open(struct media_entity *entity); ++ ++/** ++ * @brief Close a sub-device. ++ * @param entity - sub-device media entity. ++ * ++ * Close the V4L2 subdev device node associated with the @a entity and opened by ++ * a previous call to v4l2_subdev_open() (either explicit or implicit). ++ */ ++void v4l2_subdev_close(struct media_entity *entity); ++ ++/** ++ * @brief Retrieve the format on a pad. ++ * @param entity - subdev-device media entity. ++ * @param format - format to be filled. ++ * @param pad - pad number. ++ * @param which - identifier of the format to get. ++ * ++ * Retrieve the current format on the @a entity @a pad and store it in the ++ * @a format structure. ++ * ++ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try format stored ++ * in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the current ++ * active format. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_get_format(struct media_entity *entity, ++ struct v4l2_mbus_framefmt *format, unsigned int pad, ++ enum v4l2_subdev_format_whence which); ++ ++/** ++ * @brief Set the format on a pad. ++ * @param entity - subdev-device media entity. ++ * @param format - format. ++ * @param pad - pad number. ++ * @param which - identifier of the format to set. ++ * ++ * Set the format on the @a entity @a pad to @a format. The driver is allowed to ++ * modify the requested format, in which case @a format is updated with the ++ * modifications. ++ * ++ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try format stored in the ++ * file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the device with an ++ * active format. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_set_format(struct media_entity *entity, ++ struct v4l2_mbus_framefmt *format, unsigned int pad, ++ enum v4l2_subdev_format_whence which); ++ ++/** ++ * @brief Retrieve a selection rectangle on a pad. ++ * @param entity - subdev-device media entity. ++ * @param r - rectangle to be filled. ++ * @param pad - pad number. ++ * @param target - selection target ++ * @param which - identifier of the format to get. ++ * ++ * Retrieve the @a target selection rectangle on the @a entity @a pad ++ * and store it in the @a rect structure. ++ * ++ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try ++ * selection rectangle stored in the file handle, or ++ * V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the current active selection ++ * rectangle. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_get_selection(struct media_entity *entity, ++ struct v4l2_rect *rect, unsigned int pad, unsigned int target, ++ enum v4l2_subdev_format_whence which); ++ ++/** ++ * @brief Set a selection rectangle on a pad. ++ * @param entity - subdev-device media entity. ++ * @param rect - crop rectangle. ++ * @param pad - pad number. ++ * @param target - selection target ++ * @param which - identifier of the format to set. ++ * ++ * Set the @a target selection rectangle on the @a entity @a pad to @a ++ * rect. The driver is allowed to modify the requested rectangle, in ++ * which case @a rect is updated with the modifications. ++ * ++ * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try crop rectangle ++ * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the ++ * device with an active crop rectangle. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_set_selection(struct media_entity *entity, ++ struct v4l2_rect *rect, unsigned int pad, unsigned int target, ++ enum v4l2_subdev_format_whence which); ++ ++/** ++ * @brief Query the digital video capabilities of a pad. ++ * @param entity - subdev-device media entity. ++ * @param cap - capabilities to be filled. ++ * ++ * Retrieve the digital video capabilities of the @a entity pad specified by ++ * @a cap.pad and store it in the @a cap structure. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity, ++ struct v4l2_dv_timings_cap *caps); ++ ++/** ++ * @brief Query the digital video timings of a sub-device ++ * @param entity - subdev-device media entity. ++ * @param timings timings to be filled. ++ * ++ * Retrieve the detected digital video timings for the currently selected input ++ * of @a entity and store them in the @a timings structure. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_query_dv_timings(struct media_entity *entity, ++ struct v4l2_dv_timings *timings); ++ ++/** ++ * @brief Get the current digital video timings of a sub-device ++ * @param entity - subdev-device media entity. ++ * @param timings timings to be filled. ++ * ++ * Retrieve the current digital video timings for the currently selected input ++ * of @a entity and store them in the @a timings structure. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_get_dv_timings(struct media_entity *entity, ++ struct v4l2_dv_timings *timings); ++ ++/** ++ * @brief Set the digital video timings of a sub-device ++ * @param entity - subdev-device media entity. ++ * @param timings timings to be set. ++ * ++ * Set the digital video timings of @a entity to @a timings. The driver is ++ * allowed to modify the requested format, in which case @a timings is updated ++ * with the modifications. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_set_dv_timings(struct media_entity *entity, ++ struct v4l2_dv_timings *timings); ++ ++/** ++ * @brief Retrieve the frame interval on a sub-device. ++ * @param entity - subdev-device media entity. ++ * @param interval - frame interval to be filled. ++ * ++ * Retrieve the current frame interval on subdev @a entity and store it in the ++ * @a interval structure. ++ * ++ * Frame interval retrieving is usually supported only on devices at the ++ * beginning of video pipelines, such as sensors. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++ ++int v4l2_subdev_get_frame_interval(struct media_entity *entity, ++ struct v4l2_fract *interval); ++ ++/** ++ * @brief Set the frame interval on a sub-device. ++ * @param entity - subdev-device media entity. ++ * @param interval - frame interval. ++ * ++ * Set the frame interval on subdev @a entity to @a interval. The driver is ++ * allowed to modify the requested frame interval, in which case @a interval is ++ * updated with the modifications. ++ * ++ * Frame interval setting is usually supported only on devices at the beginning ++ * of video pipelines, such as sensors. ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_set_frame_interval(struct media_entity *entity, ++ struct v4l2_fract *interval); ++ ++/** ++ * @brief Parse a string and apply format, crop and frame interval settings. ++ * @param media - media device. ++ * @param p - input string ++ * @param endp - pointer to string p where parsing ended (return) ++ * ++ * Parse string @a p and apply format, crop and frame interval settings to a ++ * subdev pad specified in @a p. @a endp will be written a pointer where ++ * parsing of @a p ended. ++ * ++ * Format strings are separeted by commas (,). ++ * ++ * @return 0 on success, or a negative error code on failure. ++ */ ++int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p); ++ ++/** ++ * @brief Convert media bus pixel code to string. ++ * @param code - input string ++ * ++ * Convert media bus pixel code @a code to a human-readable string. ++ * ++ * @return A pointer to a string on success, NULL on failure. ++ */ ++const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code); ++ ++/** ++ * @brief Parse string to media bus pixel code. ++ * @param string - input string ++ * @param lenght - length of the string ++ * ++ * Parse human readable string @a string to an media bus pixel code. ++ * ++ * @return media bus pixelcode on success, -1 on failure. ++ */ ++enum v4l2_mbus_pixelcode v4l2_subdev_string_to_pixelcode(const char *string, ++ unsigned int length); ++#endif +-- +2.7.4 + diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/disable-wayland-scanner-pkg-check.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/disable-wayland-scanner-pkg-check.patch deleted file mode 100644 index 062da5c..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/disable-wayland-scanner-pkg-check.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: weston-1.5.0/configure.ac -=================================================================== ---- weston-1.5.0.orig/configure.ac -+++ weston-1.5.0/configure.ac -@@ -503,7 +503,7 @@ if test x$wayland_scanner = x; then - AC_MSG_ERROR([wayland-scanner is needed to compile weston]) - fi - --PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner) -+#PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner) - - AC_CONFIG_FILES([Makefile src/version.h src/weston.pc]) - diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch deleted file mode 100644 index 35e6d6f..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-lcms-explicitly-configurable.patch +++ /dev/null @@ -1,43 +0,0 @@ -weston-1.5.0/configure.ac: make lcms explicitly configurable - -The lcms package is outside of openembedded-core, so make it -explicitly configurable. Make it deterministic, so that if lcms -dependencies are missing, autoconf throws a fatal error. Follow -upstream style to make it more likely to be merged. - -Upstream-Status: Pending - -Index: weston-1.5.0/configure.ac -=================================================================== ---- weston-1.5.0.orig/configure.ac -+++ weston-1.5.0/configure.ac -@@ -491,12 +491,24 @@ AC_ARG_ENABLE(demo-clients-install, - enable_demo_clients_install=no) - AM_CONDITIONAL(INSTALL_DEMO_CLIENTS, [test "x$enable_demo_clients_install" = "xyes"]) - --PKG_CHECK_MODULES(LCMS, lcms2, -- [have_lcms=yes], [have_lcms=no]) --if test "x$have_lcms" = xyes; then -- AC_DEFINE(HAVE_LCMS, 1, [Have lcms support]) -+AC_ARG_ENABLE(lcms, -+ AS_HELP_STRING([--disable-lcms], -+ [Disable lcms support]),, -+ enable_lcms=auto) -+AM_CONDITIONAL(HAVE_LCMS, [test "x$enable_lcms" = xyes]) -+if test "x$enable_lcms" != "xno"; then -+ PKG_CHECK_MODULES(LCMS, -+ lcms2, -+ [have_lcms=yes], -+ [have_lcms=no]) -+ if test "x$have_lcms" = "xno" -a "x$enable_lcms" = "xyes"; then -+ AC_MSG_ERROR([lcms support explicitly requested, but lcms couldn't be found]) -+ fi -+ if test "x$have_lcms" = "xyes"; then -+ enable_lcms=yes -+ AC_DEFINE(HAVE_LCMS, 1, [Have lcms support]) -+ fi - fi --AM_CONDITIONAL(HAVE_LCMS, [test "x$have_lcms" = xyes]) - - AC_PATH_PROG([wayland_scanner], [wayland-scanner]) - if test x$wayland_scanner = x; then diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch deleted file mode 100644 index ad07d4f..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/make-libwebp-explicitly-configurable.patch +++ /dev/null @@ -1,37 +0,0 @@ - -The libwebp package is outside of openembedded-core, so make it -explicitly configurable. Make it deterministic, so that if libwebp -dependencies are missing, autoconf throws a fatal error. - -Upstream-Status: Pending - -Index: weston-1.5.0/configure.ac -=================================================================== ---- weston-1.5.0.orig/configure.ac -+++ weston-1.5.0/configure.ac -@@ -268,9 +268,22 @@ fi - - PKG_CHECK_MODULES(PIXMAN, [pixman-1]) - PKG_CHECK_MODULES(PNG, [libpng]) --PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no]) --AS_IF([test "x$have_webp" = "xyes"], -- [AC_DEFINE([HAVE_WEBP], [1], [Have webp])]) -+AC_ARG_ENABLE(webp, -+ AS_HELP_STRING([--disable-webp], -+ [Disable libwebp support]),, -+ enable_webp=auto) -+AM_CONDITIONAL(HAVE_WEBP, [test "x$enable_webp" = xyes]) -+AS_IF([test "x$enable_webp" != "xno"], -+ PKG_CHECK_MODULES(WEBP, -+ [libwebp], -+ [have_webp=yes], -+ [have_webp=no]) -+ AS_IF([test "x$have_webp" = "xno" -a "x$enable_webp" = "xyes"], -+ AC_MSG_ERROR([libwebp support explicitly request, but lipwebp could not be found])) -+ AS_IF([test "x$have_webp" = "xyes"], -+ [enable_webp=yes] -+ [AC_DEFINE([HAVE_WEBP], [1], [Have webp])]) -+) - - AC_ARG_ENABLE(vaapi-recorder, [ --enable-vaapi-recorder],, - enable_vaapi_recorder=auto) diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch deleted file mode 100644 index 9bf5752..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston-workaround-to-avoid-segfault-on-hotplug-event.patch +++ /dev/null @@ -1,81 +0,0 @@ -From be437c6b27a2c6b83b2e60e30e5f0cdcbda28ad0 Mon Sep 17 00:00:00 2001 -From: Yannick Gicquel <yannick.gicquel@iot.bzh> -Date: Fri, 6 Nov 2015 09:01:43 +0100 -Subject: [PATCH] weston: workaround to avoid segfault on hotplug event - -When user unplug/replug a monitor, HDMI link for example, -weston segfault while calling gbm_surface_destroy from -drm_output_destroy function. - -This workaround basically disable the output destroy -mechanism on unplug/replug event callbacks. It also force -the compositor to do a pageflip in order to refresh the -output when it is connected back. - -All this workaround is under "KEEP_OUTPUTS_ON_HOTPLUG" -compilation flag, which can be disable if required. - -Signed-off-by: Yannick Gicquel <yannick.gicquel@iot.bzh> -Signed-off-by: Manuel Bachmann <mbc@iot.bzh> ---- - src/compositor-drm.c | 22 ++++++++++++++++++++++ - 1 file changed, 22 insertions(+) - -diff --git a/src/compositor-drm.c b/src/compositor-drm.c -index 2087c93..21b7776 100644 ---- a/src/compositor-drm.c -+++ b/src/compositor-drm.c -@@ -56,6 +56,12 @@ - #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 - #endif - -+/* This allow weston to keep outputs when re-attached after -+ * a previous unplug event. Comment line below to fallback -+ * to initial behavior. -+ */ -+#define KEEP_OUTPUTS_ON_HOTPLUG -+ - static int option_current_mode = 0; - - enum output_config { -@@ -2383,6 +2389,11 @@ create_outputs(struct drm_compositor *ec, uint32_t option_connector, - return 0; - } - -+#if defined(KEEP_OUTPUTS_ON_HOTPLUG) -+static void -+drm_compositor_set_modes(struct drm_compositor *compositor); -+#endif -+ - static void - update_outputs(struct drm_compositor *ec, struct udev_device *drm_device) - { -@@ -2443,14 +2454,25 @@ update_outputs(struct drm_compositor *ec, struct udev_device *drm_device) - disconnects &= ~(1 << output->connector_id); - weston_log("connector %d disconnected\n", - output->connector_id); -+#if !defined(KEEP_OUTPUTS_ON_HOTPLUG) - drm_output_destroy(&output->base); -+#endif - } - } - } - -+#if defined(KEEP_OUTPUTS_ON_HOTPLUG) -+ /* This refresh the display when connector is plugged back */ -+ wl_list_for_each_safe(output, next, &ec->base.output_list, -+ base.link) { -+ /* FIXME: following is required only on 'connected' event. */ -+ drm_compositor_set_modes(ec); -+ } -+#else - /* FIXME: handle zero outputs, without terminating */ - if (ec->connector_allocator == 0) - wl_display_terminate(ec->base.wl_display); -+#endif - } - - static int --- -1.9.1 - diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop deleted file mode 100644 index 1086ae8..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.desktop +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Type=Application -Name=Weston -Comment=Wayland Compostitor -Exec=weston -Icon=weston -Terminal=false -Categories=Utility; diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png Binary files differdeleted file mode 100644 index ea8b7e0..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.png +++ /dev/null diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service new file mode 100644 index 0000000..db0fd29 --- /dev/null +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston/weston.service @@ -0,0 +1,13 @@ +[Unit] +Description=Weston reference Wayland compositor +Conflicts=getty@tty1.service +After=dbus.service rc.pvr.service + +[Service] +ExecStartPre=/bin/rm -rf /home/root/.cache/gstreamer-1.0 +ExecStart=/usr/bin/weston-launch -u root -- --idle-time=4294967 --gst-record +ExecStop=/usr/bin/killall -s KILL weston +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bb b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bb deleted file mode 100644 index 4a8584f..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bb +++ /dev/null @@ -1,87 +0,0 @@ -SUMMARY = "Weston, a Wayland compositor" -DESCRIPTION = "Weston is the reference implementation of a Wayland compositor" -HOMEPAGE = "http://wayland.freedesktop.org" -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://COPYING;md5=275efac2559a224527bd4fd593d38466 \ - file://src/compositor.c;endline=23;md5=aa98a8db03480fe7d500d0b1f4b8850c" - -SRC_URI = "http://wayland.freedesktop.org/releases/${BPN}-${PV}.tar.xz \ - file://weston.png \ - file://weston.desktop \ - file://disable-wayland-scanner-pkg-check.patch \ - file://make-lcms-explicitly-configurable.patch \ - file://make-libwebp-explicitly-configurable.patch \ -" -SRC_URI[md5sum] = "8eb40d230efc2411f083c20656534780" -SRC_URI[sha256sum] = "06388ba04ac79aa72d685cc1a8e646ddb2b8cfe11fcc742294f9addac48b7684" - -inherit autotools pkgconfig useradd - -DEPENDS = "libxkbcommon gdk-pixbuf pixman cairo glib-2.0 jpeg" -DEPENDS += "wayland virtual/egl pango" - -EXTRA_OECONF = "--enable-setuid-install \ - --disable-xwayland \ - --enable-simple-clients \ - --enable-clients \ - --enable-demo-clients-install \ - --disable-libunwind \ - --disable-rpi-compositor \ - --disable-rdp-compositor \ - " - - -PACKAGECONFIG ??= "${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'kms fbdev wayland egl', '', d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'x11', '', d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'pam', 'launch', '', d)} \ - " -# -# Compositor choices -# -# Weston on KMS -PACKAGECONFIG[kms] = "--enable-drm-compositor,--disable-drm-compositor,drm udev virtual/mesa mtdev" -# Weston on Wayland (nested Weston) -PACKAGECONFIG[wayland] = "--enable-wayland-compositor,--disable-wayland-compositor,virtual/mesa" -# Weston on X11 -PACKAGECONFIG[x11] = "--enable-x11-compositor,--disable-x11-compositor,virtual/libx11 libxcb libxcb libxcursor cairo" -# Headless Weston -PACKAGECONFIG[headless] = "--enable-headless-compositor,--disable-headless-compositor" -# Weston on framebuffer -PACKAGECONFIG[fbdev] = "--enable-fbdev-compositor,--disable-fbdev-compositor,udev mtdev" -# weston-launch -PACKAGECONFIG[launch] = "--enable-weston-launch,--disable-weston-launch,libpam drm" -# VA-API desktop recorder -PACKAGECONFIG[vaapi] = "--enable-vaapi-recorder,--disable-vaapi-recorder,libva" -# Weston with EGL support -PACKAGECONFIG[egl] = "--enable-egl --enable-simple-egl-clients,--disable-egl --disable-simple-egl-clients,virtual/egl" -# Weston with cairo glesv2 support -PACKAGECONFIG[cairo-glesv2] = "--with-cairo-glesv2,--with-cairo=image,cairo" -# Weston with lcms support -PACKAGECONFIG[lcms] = "--enable-lcms,--disable-lcms,lcms" -# Weston with webp support -PACKAGECONFIG[webp] = "--enable-webp,--disable-webp,libwebp" - -do_install_append() { - # Weston doesn't need the .la files to load modules, so wipe them - rm -f ${D}/${libdir}/weston/*.la - - # If X11, ship a desktop file to launch it - if [ "${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'x11', '', d)}" = "x11" ]; then - install -d ${D}${datadir}/applications - install ${WORKDIR}/weston.desktop ${D}${datadir}/applications - - install -d ${D}${datadir}/icons/hicolor/48x48/apps - install ${WORKDIR}/weston.png ${D}${datadir}/icons/hicolor/48x48/apps - fi -} - -PACKAGES += "${PN}-examples" - -FILES_${PN} = "${bindir}/weston ${bindir}/weston-terminal ${bindir}/weston-info ${bindir}/weston-launch ${bindir}/wcap-decode ${libexecdir} ${datadir}" -FILES_${PN}-examples = "${bindir}/*" - -RDEPENDS_${PN} += "xkeyboard-config" -RRECOMMENDS_${PN} = "liberation-fonts" - -USERADD_PACKAGES = "${PN}" -GROUPADD_PARAM_${PN} = "--system weston-launch" diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend deleted file mode 100644 index 6a18dba..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.5.0.bbappend +++ /dev/null @@ -1,26 +0,0 @@ -require ../../include/gles-control.inc - -PACKAGECONFIG_rcar-gen2 := "${@'${PACKAGECONFIG}'.replace('x11', '')}" - -PACKAGECONFIG_append_rcar-gen2 = " \ - ${@base_conditional('USE_GLES', '1', '', 'fbdev', d)}" -DEPENDS_append_rcar-gen2 = " \ - ${@base_conditional('USE_GLES', '1', 'gles-user-module', '', d)}" -EXTRA_OECONF_append_rcar-gen2 = " \ - ${@base_conditional('USE_GLES', '1', '--enable-v4l2', \ - '--disable-xwayland-test WESTON_NATIVE_BACKEND=fbdev-backend.so', d)}" - -SRCREV_rcar-gen2 = "${@'decdc587ceb147d22eb989a3334e627208030f69' \ - if '1' in '${USE_GLES}' else '00781bcf518f6bab0d08e6962630b0994e8bf632'}" -SRC_URI_rcar-gen2 = "git://github.com/renesas-devel/weston.git;protocol=git;branch=RCAR-GEN2/1.5.0/gl-fallback \ - file://weston.desktop \ - file://weston.png \ - file://disable-wayland-scanner-pkg-check.patch \ - file://make-lcms-explicitly-configurable.patch \ - file://make-libwebp-explicitly-configurable.patch \ - file://weston-workaround-to-avoid-segfault-on-hotplug-event.patch \ -" -S = "${WORKDIR}/git" - -RDEPENDS_${PN}_append_rcar-gen2 = " \ - ${@base_conditional('USE_GLES', '1', 'media-ctl', '', d)}" diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend new file mode 100644 index 0000000..33efb49 --- /dev/null +++ b/meta-rcar-gen2/recipes-graphics/wayland/weston_1.9.0.bbappend @@ -0,0 +1,11 @@ +EXTRA_OECONF += "--enable-gst-recorder " +DEPENDS += "media-ctl gstreamer1.0 gstreamer1.0-plugins-base libjpeg-turbo" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI_append = " \ + file://0001-Add-virtual-output-support.patch \ + file://0002-Get-DMA-fd-on-bo.patch \ + file://0003-Add-gst-recorder-for-h264-output-streaming.patch \ + file://weston.service \ +" diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch new file mode 100644 index 0000000..7bfff9a --- /dev/null +++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch @@ -0,0 +1,29 @@ +From 21b85faf93b8028cfa477279354bedab93dcea04 Mon Sep 17 00:00:00 2001 +From: Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com> +Date: Wed, 21 Dec 2016 11:27:34 +0900 +Subject: [PATCH] ASoC: ak4642: Replace mdelay function to msleep + +Replace mdelay to msleep to avoid busy loop on ak4642_lout_event(). +Otherwise, sometimes playback doesn't work correctly when pulseaudio was used. + +Signed-off-by: Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com> +--- + sound/soc/codecs/ak4642.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c +index 40500cd..0205ae1 100644 +--- a/sound/soc/codecs/ak4642.c ++++ b/sound/soc/codecs/ak4642.c +@@ -186,7 +186,7 @@ static int ak4642_lout_event(struct snd_soc_dapm_widget *w, + break; + case SND_SOC_DAPM_POST_PMU: /* after widget power up */ + /* Power save mode OFF */ +- mdelay(popup_wait); ++ msleep(popup_wait); + snd_soc_update_bits(codec, SG_SL2, LOPS, 0); + break; + case SND_SOC_DAPM_PRE_PMD: /* before widget power down */ +-- +2.9.2 + diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch index 13fc68b..e452f0e 100644 --- a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch +++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0008-Porter-board-support.patch @@ -1322,7 +1322,7 @@ index 0000000..d481ecd + +/* POWER IC */ +static struct i2c_board_info poweric_i2c[] = { -+ { I2C_BOARD_INFO("da9063", 0x58), }, ++ { I2C_BOARD_INFO("da9063", 0x5a), }, +}; + +static void porter_restart(char mode, const char *cmd) diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch new file mode 100644 index 0000000..e7a143f --- /dev/null +++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/9999-Backport-fix-for-CVE-2016-5195.patch @@ -0,0 +1,68 @@ +From 8003e1524789537680204d44d5bf7a82561f8ba3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan-Simon=20M=C3=B6ller?= <jsmoeller@linuxfoundation.org> +Date: Fri, 4 Nov 2016 20:58:46 +0100 +Subject: [PATCH] Backport fix for CVE-2016-5195 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Jan-Simon Möller <jsmoeller@linuxfoundation.org> +--- + include/linux/mm.h | 1 + + mm/memory.c | 14 ++++++++++++-- + 2 files changed, 13 insertions(+), 2 deletions(-) + +diff --git a/include/linux/mm.h b/include/linux/mm.h +index 3bf21c3..263b405 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1702,6 +1702,7 @@ static inline struct page *follow_page(struct vm_area_struct *vma, + #define FOLL_HWPOISON 0x100 /* check page is hwpoisoned */ + #define FOLL_NUMA 0x200 /* force NUMA hinting page fault */ + #define FOLL_MIGRATION 0x400 /* wait for page to replace migration entry */ ++#define FOLL_COW 0x4000 /* internal GUP flag */ + + typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr, + void *data); +diff --git a/mm/memory.c b/mm/memory.c +index 48aa275..3a3f316 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -1462,6 +1462,16 @@ int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address, + } + EXPORT_SYMBOL_GPL(zap_vma_ptes); + ++/* ++ * FOLL_FORCE can write to even unwritable pte's, but only ++ * after we've gone through a COW cycle and they are dirty. ++ */ ++static inline bool can_follow_write_pte(pte_t pte, unsigned int flags) ++{ ++ return pte_write(pte) || ++ ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte)); ++} ++ + /** + * follow_page_mask - look up a page descriptor from a user-virtual address + * @vma: vm_area_struct mapping @address +@@ -1569,7 +1579,7 @@ split_fallthrough: + } + if ((flags & FOLL_NUMA) && pte_numa(pte)) + goto no_page; +- if ((flags & FOLL_WRITE) && !pte_write(pte)) ++ if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) + goto unlock; + + page = vm_normal_page(vma, address, pte); +@@ -1876,7 +1886,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, + */ + if ((ret & VM_FAULT_WRITE) && + !(vma->vm_flags & VM_WRITE)) +- foll_flags &= ~FOLL_WRITE; ++ foll_flags |= FOLL_COW; + + cond_resched(); + } +-- +2.1.4 + diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb index 4a3e515..e8e80f2 100644 --- a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb +++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb @@ -17,6 +17,7 @@ SRC_URI = "${RENESAS_BACKPORTS_URL};protocol=git;branch=bsp/v3.10.31-ltsi/rcar-g file://0001-arm-koelsch-Add-vmalloc-384M-to-bootargs-of-DTS.patch \ file://0001-arm-alt-Add-vmalloc-384M-to-bootargs-of-DTS.patch \ file://0001-arm-gose-Add-vmalloc-384M-to-bootargs-of-DTS.patch \ + file://0001-ASoC-ak4642-Replace-mdelay-function-to-msleep.patch \ " SRC_URI_append_porter = " \ @@ -156,6 +157,9 @@ SRC_URI_append_smack = " \ SRC_URI_append_porter = " file://can-rcar.cfg" +# Backport fix for CVE-2016-5195 +SRC_URI_append = " file://9999-Backport-fix-for-CVE-2016-5195.patch " + S = "${WORKDIR}/git" KERNEL_DEFCONFIG = "shmobile_defconfig" diff --git a/meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb b/meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb new file mode 100644 index 0000000..a87314a --- /dev/null +++ b/meta-rcar-gen2/recipes-kernel/vspm-module/vsp2-kernel-module.bb @@ -0,0 +1,58 @@ +require ../../include/rcar-gen2-modules-common.inc + +LICENSE = "GPLv2&MIT" +LIC_FILES_CHKSUM = " \ + file://GPL-COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263 \ + file://MIT-COPYING;md5=fea016ce2bdf2ec10080f69e9381d378 \ +" + +DEPENDS = "linux-renesas vspm-kernel-module" +PN = "vsp2-kernel-module" +PR = "r0" + +SRCREV = "c231aff0fba0a2c559968098e5573050a1aa336d" +SRC_URI = " \ + git://github.com/renesas-devel/vsp2driver.git;protocol=git;branch=RCAR-GEN2/1.0.0 \ +" +S = "${WORKDIR}/git" + +do_configure[noexec] = "1" +do_compile() { + export VSP2_VSPMDIR=${KERNELSRC}/include + export VSP2_VSPMSYMVERS=vspm.symvers + cd ${S}/drv + make all ARCH=arm +} + +do_install() { + # Create destination folder + mkdir -p ${D}/lib/modules/${KERNEL_VERSION}/extra/ ${D}/usr/src/kernel/include/ + + # Copy kernel module + cp -f ${S}/drv/vsp2.ko ${D}/lib/modules/${KERNEL_VERSION}/extra/ + + # Copy shared library for reference from other modules + cp -f ${S}/drv/Module.symvers ${D}/usr/src/kernel/include/vsp2.symvers + cp -f ${S}/drv/Module.symvers ${KERNELSRC}/include/vsp2.symvers +} + +PACKAGES = "\ + ${PN} \ + ${PN}-dev \ +" + +FILES_${PN} = " \ + /lib/modules/${KERNEL_VERSION}/extra/vsp2.ko \ + ${sysconfdir}/* \ +" + +FILES_${PN}-dev = " \ + /usr/src/kernel/include/vsp2.symvers \ +" +RPROVIDES_${PN} += "vsp2-kernel-module" +INHIBIT_PACKAGE_DEBUG_SPLIT = "1" + +ALLOW_EMPTY_kernel-module-vsp2 = "1" + +# Autoload VSP2Driver +KERNEL_MODULE_AUTOLOAD = "vsp2" diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch new file mode 100644 index 0000000..d863414 --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0001-omx-videodec-add-planebuf-to-allocation-request.patch @@ -0,0 +1,85 @@ +diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c +index c5b69ab..647ac88 100644 +--- a/omx/gstomxvideodec.c ++++ b/omx/gstomxvideodec.c +@@ -581,7 +581,7 @@ gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) + #ifdef HAVE_MMNGRBUF + static GstBuffer * + gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool, +- gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES]) ++ gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gpointer plane_buf[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES]) + { + GstQuery *query; + GValue val = { 0, }; +@@ -590,6 +590,7 @@ gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool, + GstBuffer *buffer; + GArray *dmabuf_array; + GArray *stride_array; ++ GArray *planebuf_array; + gint n_planes; + gint i; + +@@ -598,11 +599,13 @@ gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool, + + dmabuf_array = g_array_new (FALSE, FALSE, sizeof (gint)); + stride_array = g_array_new (FALSE, FALSE, sizeof (gint)); ++ planebuf_array = g_array_new (FALSE, FALSE, sizeof (gpointer)); + + n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info); + for (i = 0; i < n_planes; i++) { + g_array_append_val (dmabuf_array, dmabuf_fd[i]); + g_array_append_val (stride_array, stride[i]); ++ g_array_append_val (planebuf_array, plane_buf[i]); + } + + structure = gst_structure_new ("videosink_buffer_creation_request", +@@ -610,6 +613,7 @@ gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool, + "height", G_TYPE_INT, pool->port->port_def.format.video.nFrameHeight, + "stride", G_TYPE_ARRAY, stride_array, + "dmabuf", G_TYPE_ARRAY, dmabuf_array, ++ "planebuf", G_TYPE_ARRAY, planebuf_array, + "allocator", G_TYPE_POINTER, &val, + "format", G_TYPE_STRING, + gst_video_format_to_string (pool->video_info.finfo->format), +@@ -704,6 +708,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + gint i; + gint dmabuf_fd[GST_VIDEO_MAX_PLANES]; + gint plane_size[GST_VIDEO_MAX_PLANES]; ++ gpointer plane_buf[GST_VIDEO_MAX_PLANES]; + guint phys_addr; + OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res = + (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf-> +@@ -730,6 +735,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + + plane_size[0] = vmeta->stride[0] * + GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, 0); ++ plane_buf[0] = omx_buf->omx_buf->pBuffer; + + /* Export dmabuf file descriptors from second and subsequent planes */ + n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info); +@@ -737,6 +743,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + phys_addr = (guint) decode_res->pvPhysImageAddressY + vmeta->offset[i]; + plane_size[i] = vmeta->stride[i] * + GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, i); ++ plane_buf[i] = omx_buf->omx_buf->pBuffer + vmeta->offset[i]; + + if (!gst_omx_buffer_pool_export_dmabuf (pool, phys_addr, plane_size[i], + page_size, &vdbuf_data->id_export[i], &dmabuf_fd[i])) { +@@ -747,7 +754,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + + if (pool->vsink_buf_req_supported) + new_buf = gst_omx_buffer_pool_request_videosink_buffer_creation (pool, +- dmabuf_fd, vmeta->stride); ++ dmabuf_fd, plane_buf, vmeta->stride); + else { + GstVideoMeta *new_meta; + +@@ -1947,6 +1954,8 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) + goto caps_failed; + } + ++ /* ...force clearing of reconfiguration flag to prevent subsequent buffers allocation */ ++ gst_pad_check_reconfigure(GST_VIDEO_DECODER_SRC_PAD(self)); + gst_video_codec_state_unref (state); + + GST_VIDEO_DECODER_STREAM_UNLOCK (self); diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch new file mode 100644 index 0000000..354a7a9 --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0002-Fixed-memory-corruption-and-bad-access.patch @@ -0,0 +1,65 @@ +From 3e528cda6fb9d0da2be52e18a305096cf6e37528 Mon Sep 17 00:00:00 2001 +From: Andrey Vostrikov <andrey.vostrikov@cogentembedded.com> +Date: Sat, 18 Jul 2015 15:52:59 +0300 +Subject: [PATCH] Fixed memory corruption and bad access + +--- + omx/gstomxh264dec.c | 4 ++-- + omx/gstomxvideodec.c | 12 ++++++------ + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/omx/gstomxh264dec.c b/omx/gstomxh264dec.c +index 7799e69..5509cc0 100644 +--- a/omx/gstomxh264dec.c ++++ b/omx/gstomxh264dec.c +@@ -104,7 +104,7 @@ gst_omx_h264_dec_retrieve_sps_pps (GstOMXH264Dec * self, guint8 * data) + + sps_num = ptr[5] & 0x1f; /* reserved(3bit) + numOfSequenceParameterSets(uint 5bit) */ + +- sps_size_list = g_malloc (sps_num); ++ sps_size_list = g_malloc (sps_num * sizeof (guint)); + if (!sps_size_list) { + GST_ERROR_OBJECT (self, "failed g_malloc"); + return NULL; +@@ -119,7 +119,7 @@ gst_omx_h264_dec_retrieve_sps_pps (GstOMXH264Dec * self, guint8 * data) + } + + pps_num = *ptr++; /* numOfPictureParameterSets (unint 8bit) */ +- pps_size_list = g_malloc (pps_num); ++ pps_size_list = g_malloc (pps_num * sizeof (guint)); + if (!pps_size_list) { + GST_ERROR_OBJECT (self, "failed g_malloc"); + g_free (sps_size_list); +diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c +index c5b69ab..4a9706f 100644 +--- a/omx/gstomxvideodec.c ++++ b/omx/gstomxvideodec.c +@@ -563,10 +563,12 @@ gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) + gst_omx_buffer_data_quark); + #ifdef HAVE_MMNGRBUF + if (self->use_dmabuf) { +- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data; +- for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) +- if (vdbuf_data->id_export[i] >= 0) +- mmngr_export_end_in_user (vdbuf_data->id_export[i]); ++ vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data; ++ if (vdbuf_data) { ++ for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) ++ if (vdbuf_data->id_export[i] >= 0) ++ mmngr_export_end_in_user (vdbuf_data->id_export[i]); ++ } + } + #endif + g_slice_free (GstOMXVideoDecBufferData, omx_buf->private_data); +@@ -1684,8 +1686,6 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self) + + static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *release) + { +- gint i; +- + if (!release) + return; + +-- +2.1.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch new file mode 100644 index 0000000..23a3dfe --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch @@ -0,0 +1,2759 @@ +From dcf585068bbb591431a999656359627cccd38716 Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Thu, 25 Aug 2016 18:37:44 +0300 +Subject: [PATCH 03/10] omxvideoenc: export dmafd buffer through own buffer + pool + + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + configure.ac | 17 + + omx/Makefile.am | 1 + + omx/gstomxbufferpool.c | 845 +++++++++++++++++++++++++++++++++++++++++++ + omx/gstomxbufferpool.h | 106 ++++++ + omx/gstomxvideodec.c | 924 +----------------------------------------------- + omx/gstomxvideoenc.c | 578 ++++++++++++++++++++++-------- + omx/gstomxvideoenc.h | 11 + + 7 files changed, 1420 insertions(+), 1062 deletions(-) + create mode 100644 omx/gstomxbufferpool.c + create mode 100644 omx/gstomxbufferpool.h + +diff --git a/configure.ac b/configure.ac +index 6aae527..57f5ae9 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -245,6 +245,23 @@ AC_CHECK_LIB([mmngrbuf], [mmngr_export_start_in_user], + ]) + fi + ++dnl check OMXR_Extension_video.h ++AC_CHECK_HEADER([OMXR_Extension_video.h], ++ [AC_DEFINE(HAVE_VIDEOR_EXT, 1, [Define if you have OMXR_Extension_video.h header])], ++ [], ++ [AC_INCLUDES_DEFAULT]) ++dnl check OMXR_Extension_vecmn.h ++AC_CHECK_HEADER([OMXR_Extension_vecmn.h], ++ [AC_DEFINE(HAVE_VIDEOENC_EXT, 1, [Define if you have OMXR_Extension_vecmn.h header])], ++ [], ++ [AC_INCLUDES_DEFAULT]) ++ ++dnl check OMXR_Extension_vdcmn.h ++AC_CHECK_HEADER([OMXR_Extension_vdcmn.h], ++ [AC_DEFINE(HAVE_VIDEODEC_EXT, 1, [Define if you have OMXR_Extension_vdcmn.h header])], ++ [], ++ [AC_INCLUDES_DEFAULT]) ++ + dnl check page alignment option for NV12 planes + AC_ARG_ENABLE([nv12-page-alignment], + [AS_HELP_STRING([--enable-nv12-page-alignment], +diff --git a/omx/Makefile.am b/omx/Makefile.am +index 3ec6173..3619281 100644 +--- a/omx/Makefile.am ++++ b/omx/Makefile.am +@@ -12,6 +12,7 @@ endif + + libgstomx_la_SOURCES = \ + gstomx.c \ ++ gstomxbufferpool.c \ + gstomxvideodec.c \ + gstomxvideoenc.c \ + gstomxaudioenc.c \ +diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c +new file mode 100644 +index 0000000..2585a72 +--- /dev/null ++++ b/omx/gstomxbufferpool.c +@@ -0,0 +1,845 @@ ++/* ++ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. ++ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd. ++ * Copyright (C) 2013, Collabora Ltd. ++ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk> ++ * Copyright (C) 2015, Renesas Electronics Corporation ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation ++ * version 2.1 of the License. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "gstomxbufferpool.h" ++#include "gstomxvideodec.h" ++#include "gstomxvideoenc.h" ++#include "gst/allocators/gstdmabuf.h" ++#ifdef HAVE_MMNGRBUF ++#include "mmngr_buf_user_public.h" ++#endif ++#ifdef HAVE_VIDEODEC_EXT ++#include "OMXR_Extension_vdcmn.h" ++#endif ++#ifdef HAVE_VIDEOENC_EXT ++#include "OMXR_Extension_vecmn.h" ++#endif ++#include <unistd.h> /* getpagesize() */ ++ ++/** ++ * GST_ROUND_UP_N: ++ * @num: integrer value to round up ++ * @align: a power of two to round up to ++ * ++ * Rounds an integer value up to the next multiple of @align. @align MUST be a ++ * power of two. ++ */ ++#define GST_ROUND_UP_N(num,align) ((((num) + ((align) - 1)) & ~((align) - 1))) ++ ++GST_DEBUG_CATEGORY_STATIC (gst_omx_buffer_pool_debug_category); ++#define GST_CAT_DEFAULT gst_omx_buffer_pool_debug_category ++ ++typedef struct _GstOMXMemory GstOMXMemory; ++typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator; ++typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass; ++ ++struct _GstOMXMemory ++{ ++ GstMemory mem; ++ ++ GstOMXBuffer *buf; ++}; ++ ++struct _GstOMXMemoryAllocator ++{ ++ GstAllocator parent; ++}; ++ ++struct _GstOMXMemoryAllocatorClass ++{ ++ GstAllocatorClass parent_class; ++}; ++ ++#define GST_OMX_MEMORY_TYPE "openmax" ++ ++static GstMemory * ++gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size, ++ GstAllocationParams * params) ++{ ++ g_assert_not_reached (); ++ return NULL; ++} ++ ++static void ++gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem) ++{ ++ GstOMXMemory *omem = (GstOMXMemory *) mem; ++ ++ /* TODO: We need to remember which memories are still used ++ * so we can wait until everything is released before allocating ++ * new memory ++ */ ++ ++ g_slice_free (GstOMXMemory, omem); ++} ++ ++static gpointer ++gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) ++{ ++ GstOMXMemory *omem = (GstOMXMemory *) mem; ++ ++ return omem->buf->omx_buf->pBuffer + omem->mem.offset; ++} ++ ++static void ++gst_omx_memory_unmap (GstMemory * mem) ++{ ++} ++ ++static GstMemory * ++gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size) ++{ ++ g_assert_not_reached (); ++ return NULL; ++} ++ ++GType gst_omx_memory_allocator_get_type (void); ++G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator, ++ GST_TYPE_ALLOCATOR); ++ ++#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type()) ++#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR)) ++ ++static void ++gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass) ++{ ++ GstAllocatorClass *allocator_class; ++ ++ allocator_class = (GstAllocatorClass *) klass; ++ ++ allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy; ++ allocator_class->free = gst_omx_memory_allocator_free; ++} ++ ++static void ++gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator) ++{ ++ GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); ++ ++ alloc->mem_type = GST_OMX_MEMORY_TYPE; ++ alloc->mem_map = gst_omx_memory_map; ++ alloc->mem_unmap = gst_omx_memory_unmap; ++ alloc->mem_share = gst_omx_memory_share; ++ ++ /* default copy & is_span */ ++ ++ GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); ++} ++ ++static GstMemory * ++gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags, ++ GstOMXBuffer * buf) ++{ ++ GstOMXMemory *mem; ++ gint align; ++ ++ /* FIXME: We don't allow sharing because we need to know ++ * when the memory becomes unused and can only then put ++ * it back to the pool. Which is done in the pool's release ++ * function ++ */ ++ flags |= GST_MEMORY_FLAG_NO_SHARE; ++ ++ /* GStreamer uses a bitmask for the alignment while ++ * OMX uses the alignment itself. So we have to convert ++ * here */ ++ align = buf->port->port_def.nBufferAlignment; ++ if (align > 0) ++ align -= 1; ++ if (((align + 1) & align) != 0) { ++ GST_WARNING ("Invalid alignment that is not a power of two: %u", ++ (guint) buf->port->port_def.nBufferAlignment); ++ align = 0; ++ } ++ ++ mem = g_slice_new (GstOMXMemory); ++ /* the shared memory is always readonly */ ++ gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL, ++ buf->omx_buf->nAllocLen, align, 0, buf->omx_buf->nAllocLen); ++ ++ mem->buf = buf; ++ ++ return GST_MEMORY_CAST (mem); ++} ++ ++/* Buffer pool for the buffers of an OpenMAX port. ++ * ++ * This pool is only used if we either passed buffers from another ++ * pool to the OMX port or provide the OMX buffers directly to other ++ * elements. ++ * ++ * ++ * A buffer is in the pool if it is currently owned by the port, ++ * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside ++ * the pool after it was taken from the port after it was handled ++ * by the port, i.e. {Empty,Fill}BufferDone. ++ * ++ * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated ++ * by someone else and (temporarily) passed to this pool ++ * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of ++ * the buffer will be overriden, and restored in free_buffer(). Other ++ * buffers are just freed there. ++ * ++ * The pool always has a fixed number of minimum and maximum buffers ++ * and these are allocated while starting the pool and released afterwards. ++ * They correspond 1:1 to the OMX buffers of the port, which are allocated ++ * before the pool is started. ++ * ++ * Acquiring a buffer from this pool happens after the OMX buffer has ++ * been acquired from the port. gst_buffer_pool_acquire_buffer() is ++ * supposed to return the buffer that corresponds to the OMX buffer. ++ * ++ * For buffers provided to upstream, the buffer will be passed to ++ * the component manually when it arrives and then unreffed. If the ++ * buffer is released before reaching the component it will be just put ++ * back into the pool as if EmptyBufferDone has happened. If it was ++ * passed to the component, it will be back into the pool when it was ++ * released and EmptyBufferDone has happened. ++ * ++ * For buffers provided to downstream, the buffer will be returned ++ * back to the component (OMX_FillThisBuffer()) when it is released. ++ */ ++ ++static GQuark gst_omx_buffer_data_quark = 0; ++ ++#define DEBUG_INIT \ ++ GST_DEBUG_CATEGORY_INIT (gst_omx_buffer_pool_debug_category, "omxbufferpool", 0, \ ++ "debug category for gst-omx buffer pool base class"); ++ ++G_DEFINE_TYPE_WITH_CODE (GstOMXBufferPool, gst_omx_buffer_pool, ++ GST_TYPE_BUFFER_POOL, DEBUG_INIT); ++ ++static void gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, ++ GstBuffer * buffer); ++ ++static gboolean ++gst_omx_buffer_pool_start (GstBufferPool * bpool) ++{ ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ ++ /* Only allow to start the pool if we still are attached ++ * to a component and port */ ++ GST_OBJECT_LOCK (pool); ++ if (!pool->component || !pool->port) { ++ GST_OBJECT_UNLOCK (pool); ++ return FALSE; ++ } ++ GST_OBJECT_UNLOCK (pool); ++ ++ return ++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool); ++} ++ ++static gboolean ++gst_omx_buffer_pool_stop (GstBufferPool * bpool) ++{ ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ gint i = 0; ++ ++ /* When not using the default GstBufferPool::GstAtomicQueue then ++ * GstBufferPool::free_buffer is not called while stopping the pool ++ * (because the queue is empty) */ ++ for (i = 0; i < pool->buffers->len; i++) ++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer ++ (bpool, g_ptr_array_index (pool->buffers, i)); ++ ++ /* Remove any buffers that are there */ ++ g_ptr_array_set_size (pool->buffers, 0); ++ ++ if (pool->caps) ++ gst_caps_unref (pool->caps); ++ pool->caps = NULL; ++ ++ pool->add_videometa = FALSE; ++ ++ return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool); ++} ++ ++static const gchar ** ++gst_omx_buffer_pool_get_options (GstBufferPool * bpool) ++{ ++ static const gchar *raw_video_options[] = ++ { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL }; ++ static const gchar *options[] = { NULL }; ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ ++ GST_OBJECT_LOCK (pool); ++ if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo ++ && pool->port->port_def.format.video.eCompressionFormat == ++ OMX_VIDEO_CodingUnused) { ++ GST_OBJECT_UNLOCK (pool); ++ return raw_video_options; ++ } ++ GST_OBJECT_UNLOCK (pool); ++ ++ return options; ++} ++ ++static gboolean ++gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) ++{ ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ GstCaps *caps; ++ ++ GST_OBJECT_LOCK (pool); ++ ++ if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) ++ goto wrong_config; ++ ++ if (caps == NULL) ++ goto no_caps; ++ ++ if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo ++ && pool->port->port_def.format.video.eCompressionFormat == ++ OMX_VIDEO_CodingUnused) { ++ GstVideoInfo info; ++ ++ /* now parse the caps from the config */ ++ if (!gst_video_info_from_caps (&info, caps)) ++ goto wrong_video_caps; ++ ++ /* enable metadata based on config of the pool */ ++ pool->add_videometa = ++ gst_buffer_pool_config_has_option (config, ++ GST_BUFFER_POOL_OPTION_VIDEO_META); ++ ++ pool->video_info = info; ++ } ++ ++ if (pool->caps) ++ gst_caps_unref (pool->caps); ++ pool->caps = gst_caps_ref (caps); ++ ++ GST_OBJECT_UNLOCK (pool); ++ ++ return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config ++ (bpool, config); ++ ++ /* ERRORS */ ++wrong_config: ++ { ++ GST_OBJECT_UNLOCK (pool); ++ GST_WARNING_OBJECT (pool, "invalid config"); ++ return FALSE; ++ } ++no_caps: ++ { ++ GST_OBJECT_UNLOCK (pool); ++ GST_WARNING_OBJECT (pool, "no caps in config"); ++ return FALSE; ++ } ++wrong_video_caps: ++ { ++ GST_OBJECT_UNLOCK (pool); ++ GST_WARNING_OBJECT (pool, ++ "failed getting geometry from caps %" GST_PTR_FORMAT, caps); ++ return FALSE; ++ } ++} ++ ++#if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT) ++static gboolean ++gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool, ++ guint phys_addr, gint size, gint * id_export, gint * dmabuf_fd) ++{ ++ gint res; ++ ++ res = ++ mmngr_export_start_in_user (id_export, ++ (gsize) size, phys_addr, dmabuf_fd); ++ if (res != R_MM_OK) { ++ GST_ERROR_OBJECT (pool, ++ "mmngr_export_start_in_user failed (phys_addr:0x%08x)", phys_addr); ++ return FALSE; ++ } ++ GST_DEBUG_OBJECT (pool, ++ "Export dmabuf:%d id_export:%d (phys_addr:0x%08x)", *dmabuf_fd, ++ *id_export, phys_addr); ++ ++ return TRUE; ++} ++ ++/* This function will create a GstBuffer contain dmabuf_fd of decoded ++ * video got from Media Component ++ */ ++static GstBuffer * ++gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self, ++ GstOMXBuffer * omx_buf, gint * stride, gsize * offset) ++{ ++ gint dmabuf_fd[GST_VIDEO_MAX_PLANES]; ++ gint plane_size[GST_VIDEO_MAX_PLANES]; ++ gint plane_size_ext[GST_VIDEO_MAX_PLANES]; ++ gint dmabuf_id[GST_VIDEO_MAX_PLANES]; ++ gint page_offset[GST_VIDEO_MAX_PLANES]; ++ GstBuffer *new_buf; ++ gint i; ++ gint page_size; ++ guint phys_addr = 0; ++ ++ new_buf = gst_buffer_new (); ++ page_size = getpagesize (); ++ ++ GST_DEBUG_OBJECT (self, "Creating dmabuf mem pBuffer=%p", ++ omx_buf->omx_buf->pBuffer); ++ ++ if (GST_IS_OMX_VIDEO_DEC (self->element)) { ++ OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res = ++ (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf->omx_buf->pOutputPortPrivate; ++ phys_addr = decode_res->pvPhysImageAddressY; ++ } else if (GST_IS_OMX_VIDEO_ENC (self->element)) { ++ /* private data is a physical address of HW buffer */ ++ phys_addr = (guint) omx_buf->omx_buf->pInputPortPrivate; ++ } ++ ++ if (phys_addr == 0) { ++ GST_ERROR_OBJECT (self, "Invalid phys addr for OMX buffer"); ++ return NULL; ++ } ++ ++ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->video_info); i++) { ++ guint plane_addr = 0; ++ GstMemory *mem; ++ ++ plane_addr = phys_addr + offset[i]; ++ /* Calculate offset between physical address and page boundary */ ++ page_offset[i] = plane_addr & (page_size - 1); ++ ++ plane_size[i] = stride[i] * ++ GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i); ++ ++ /* When downstream plugins do mapping from dmabuf fd it requires ++ * mapping from boundary page and size align for page size so ++ * memory for plane must increase to handle for this case */ ++ plane_size_ext[i] = GST_ROUND_UP_N (plane_size[i] + page_offset[i], ++ page_size); ++ ++ if (!gst_omx_buffer_pool_export_dmabuf (self, plane_addr, ++ plane_size_ext[i], &dmabuf_id[i], &dmabuf_fd[i])) { ++ GST_ERROR_OBJECT (self, "dmabuf exporting failed"); ++ return NULL; ++ } ++ ++ g_array_append_val (self->id_array, dmabuf_id[i]); ++ /* Set offset's information */ ++ mem = gst_dmabuf_allocator_alloc (self->allocator, dmabuf_fd[i], ++ plane_size_ext[i]); ++ mem->offset = page_offset[i]; ++ mem->size = plane_size[i]; ++ gst_buffer_append_memory (new_buf, mem); ++ } ++ ++ g_ptr_array_add (self->buffers, new_buf); ++ gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE, ++ GST_VIDEO_INFO_FORMAT (&self->video_info), ++ GST_VIDEO_INFO_WIDTH (&self->video_info), ++ GST_VIDEO_INFO_HEIGHT (&self->video_info), ++ GST_VIDEO_INFO_N_PLANES (&self->video_info), offset, stride); ++ ++ return new_buf; ++} ++#endif ++ ++static GstFlowReturn ++gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, ++ GstBuffer ** buffer, GstBufferPoolAcquireParams * params) ++{ ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ GstBuffer *buf; ++ GstOMXBuffer *omx_buf; ++ ++ g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR); ++ ++ omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index); ++ g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR); ++ ++ if (pool->other_pool) { ++ guint i, n; ++ ++ buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); ++ g_assert (pool->other_pool == buf->pool); ++ gst_object_replace ((GstObject **) & buf->pool, NULL); ++ ++ n = gst_buffer_n_memory (buf); ++ for (i = 0; i < n; i++) { ++ GstMemory *mem = gst_buffer_peek_memory (buf, i); ++ ++ /* FIXME: We don't allow sharing because we need to know ++ * when the memory becomes unused and can only then put ++ * it back to the pool. Which is done in the pool's release ++ * function ++ */ ++ GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE); ++ } ++ ++ if (pool->add_videometa) { ++ GstVideoMeta *meta; ++ ++ meta = gst_buffer_get_video_meta (buf); ++ if (!meta) { ++ gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE, ++ GST_VIDEO_INFO_FORMAT (&pool->video_info), ++ GST_VIDEO_INFO_WIDTH (&pool->video_info), ++ GST_VIDEO_INFO_HEIGHT (&pool->video_info)); ++ } ++ } ++ ++ pool->need_copy = FALSE; ++ } else { ++ GstMemory *mem; ++ const guint nstride = pool->port->port_def.format.video.nStride; ++ const guint nslice = pool->port->port_def.format.video.nSliceHeight; ++ gsize offset[GST_VIDEO_MAX_PLANES] = { 0, }; ++ gint stride[GST_VIDEO_MAX_PLANES] = { nstride, 0, }; ++ ++ switch (GST_VIDEO_INFO_FORMAT (&pool->video_info)) { ++ case GST_VIDEO_FORMAT_ABGR: ++ case GST_VIDEO_FORMAT_ARGB: ++ case GST_VIDEO_FORMAT_RGB16: ++ case GST_VIDEO_FORMAT_BGR16: ++ case GST_VIDEO_FORMAT_YUY2: ++ case GST_VIDEO_FORMAT_UYVY: ++ case GST_VIDEO_FORMAT_YVYU: ++ case GST_VIDEO_FORMAT_GRAY8: ++ break; ++ case GST_VIDEO_FORMAT_I420: ++ stride[1] = nstride / 2; ++ offset[1] = offset[0] + stride[0] * nslice; ++ stride[2] = nstride / 2; ++ offset[2] = offset[1] + (stride[1] * nslice / 2); ++ break; ++ case GST_VIDEO_FORMAT_NV12: ++ case GST_VIDEO_FORMAT_NV16: ++ stride[1] = nstride; ++ offset[1] = offset[0] + stride[0] * nslice; ++ break; ++ default: ++ g_assert_not_reached (); ++ break; ++ } ++ ++ if (GST_IS_OMX_VIDEO_DEC (pool->element) && ++ GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf == TRUE && ++ (omx_buf->omx_buf->pOutputPortPrivate)) { ++#if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT) ++ if (pool->allocator) ++ gst_object_unref (pool->allocator); ++ pool->allocator = gst_dmabuf_allocator_new (); ++ buf = gst_omx_buffer_pool_create_buffer_contain_dmabuf (pool, ++ omx_buf, (gint *) (&stride), (gsize *) (&offset)); ++ if (!buf) { ++ GST_ERROR_OBJECT (pool, "Can not create buffer contain dmabuf"); ++ return GST_FLOW_ERROR; ++ } ++#else ++ GST_ELEMENT_ERROR (pool->element, STREAM, FAILED, (NULL), ++ ("dmabuf mode is invalid now due to not have MMNGR_BUF or MC does not support getting physical address")); ++ return GST_FLOW_ERROR; ++#endif ++ } else { ++ if (GST_IS_OMX_VIDEO_ENC (pool->element) && ++ pool->port->port_def.eDir == OMX_DirInput) ++ /* Propose actual area of encoder to upstream */ ++ mem = gst_memory_new_wrapped (0, omx_buf->omx_buf->pBuffer, ++ omx_buf->omx_buf->nAllocLen, 0, 0, NULL, NULL); ++ else ++ mem = gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf); ++ ++ buf = gst_buffer_new (); ++ gst_buffer_append_memory (buf, mem); ++ g_ptr_array_add (pool->buffers, buf); ++ if (pool->add_videometa) { ++ pool->need_copy = FALSE; ++ } else { ++ GstVideoInfo info; ++ gboolean need_copy = FALSE; ++ gint i; ++ ++ gst_video_info_init (&info); ++ gst_video_info_set_format (&info, ++ GST_VIDEO_INFO_FORMAT (&pool->video_info), ++ GST_VIDEO_INFO_WIDTH (&pool->video_info), ++ GST_VIDEO_INFO_HEIGHT (&pool->video_info)); ++ ++ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&pool->video_info); i++) { ++ if (info.stride[i] != stride[i] || info.offset[i] != offset[i]) { ++ need_copy = TRUE; ++ break; ++ } ++ } ++ ++ pool->need_copy = need_copy; ++ } ++ ++ if (pool->need_copy || pool->add_videometa) { ++ /* We always add the videometa. It's the job of the user ++ * to copy the buffer if pool->need_copy is TRUE ++ */ ++ gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, ++ GST_VIDEO_INFO_FORMAT (&pool->video_info), ++ GST_VIDEO_INFO_WIDTH (&pool->video_info), ++ GST_VIDEO_INFO_HEIGHT (&pool->video_info), ++ GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride); ++ } ++ } ++ } ++ ++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), ++ gst_omx_buffer_data_quark, omx_buf, NULL); ++ ++ *buffer = buf; ++ ++ pool->current_buffer_index++; ++ ++ return GST_FLOW_OK; ++} ++ ++static void ++gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) ++{ ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ ++ /* If the buffers belong to another pool, restore them now */ ++ GST_OBJECT_LOCK (pool); ++ if (pool->other_pool) { ++ gst_object_replace ((GstObject **) & buffer->pool, ++ (GstObject *) pool->other_pool); ++ } ++ GST_OBJECT_UNLOCK (pool); ++ ++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer), ++ gst_omx_buffer_data_quark, NULL, NULL); ++ ++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool, ++ buffer); ++} ++ ++static GstFlowReturn ++gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, ++ GstBuffer ** buffer, GstBufferPoolAcquireParams * params) ++{ ++ GstFlowReturn ret; ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ ++ if (pool->port->port_def.eDir == OMX_DirOutput) { ++ GstBuffer *buf; ++ ++ g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR); ++ ++ buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); ++ g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); ++ *buffer = buf; ++ ret = GST_FLOW_OK; ++ ++ /* If it's our own memory we have to set the sizes */ ++ if ((!pool->other_pool) && ++ ((GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf) == FALSE)) { ++ GstMemory *mem = gst_buffer_peek_memory (*buffer, 0); ++ ++ g_assert (mem ++ && g_strcmp0 (mem->allocator->mem_type, GST_OMX_MEMORY_TYPE) == 0); ++ mem->size = ((GstOMXMemory *) mem)->buf->omx_buf->nFilledLen; ++ mem->offset = ((GstOMXMemory *) mem)->buf->omx_buf->nOffset; ++ } ++ } else { ++ if (GST_IS_OMX_VIDEO_ENC (pool->element)) { ++ GstBuffer *buf; ++ GstOMXBuffer *omx_buf; ++ gint count = 0; ++ ++ /* Search on number of OMXBuffer of port to find available GstBuffer ++ * (emptied OMXBuffer) to propose to upstream. If after 3 times searching, ++ * can not find target GstBuffer, return flow error ++ */ ++ do { ++ buf = g_ptr_array_index (pool->buffers, pool->enc_buffer_index); ++ g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); ++ ++ omx_buf = ++ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buf), ++ gst_omx_buffer_data_quark); ++ pool->enc_buffer_index++; ++ if (pool->enc_buffer_index == pool->port->port_def.nBufferCountActual) ++ pool->enc_buffer_index = 0; ++ ++ count += 1; ++ } while (omx_buf->used == TRUE && ++ count < pool->port->port_def.nBufferCountActual * 3); ++ ++ if (count == pool->port->port_def.nBufferCountActual * 3) { ++ ret = GST_FLOW_ERROR; ++ GST_ERROR_OBJECT (pool, ++ "Can not acquire buffer after 3 times searching"); ++ } else { ++ *buffer = buf; ++ ret = GST_FLOW_OK; ++ } ++ } else { ++ /* Acquire any buffer that is available to be filled by upstream */ ++ ret = ++ GST_BUFFER_POOL_CLASS ++ (gst_omx_buffer_pool_parent_class)->acquire_buffer (bpool, buffer, ++ params); ++ } ++ } ++ ++ return ret; ++} ++ ++static void ++gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) ++{ ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); ++ OMX_ERRORTYPE err; ++ GstOMXBuffer *omx_buf; ++ ++ g_assert (pool->component && pool->port); ++ ++ if (!pool->allocating && !pool->deactivated) { ++ omx_buf = ++ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), ++ gst_omx_buffer_data_quark); ++ if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) { ++ /* Release back to the port, can be filled again */ ++ err = gst_omx_port_release_buffer (pool->port, omx_buf); ++ if (err != OMX_ErrorNone) { ++ GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL), ++ ("Failed to relase output buffer to component: %s (0x%08x)", ++ gst_omx_error_to_string (err), err)); ++ } ++ } else if (!omx_buf->used) { ++ /* TODO: Implement. ++ * ++ * If not used (i.e. was not passed to the component) this should do ++ * the same as EmptyBufferDone. ++ * If it is used (i.e. was passed to the component) this should do ++ * nothing until EmptyBufferDone. ++ * ++ * EmptyBufferDone should release the buffer to the pool so it can ++ * be allocated again ++ * ++ * Needs something to call back here in EmptyBufferDone, like keeping ++ * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which ++ * would ensure that the buffer is always unused when this is called. ++ */ ++ if (GST_OMX_VIDEO_ENC (pool->element)->no_copy == FALSE) { ++ g_assert_not_reached (); ++ GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer ++ (bpool, buffer); ++ } ++ } ++ } ++} ++ ++static void ++gst_omx_buffer_pool_finalize (GObject * object) ++{ ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object); ++ ++#ifdef HAVE_MMNGRBUF ++ if (GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf) { ++ gint i; ++ gint dmabuf_id; ++ ++ for (i = 0; i < pool->id_array->len; i++) { ++ dmabuf_id = g_array_index (pool->id_array, gint, i); ++ if (dmabuf_id >= 0) { ++ GST_DEBUG_OBJECT (pool, "mmngr_export_end_in_user (%d)", dmabuf_id); ++ mmngr_export_end_in_user (dmabuf_id); ++ } else { ++ GST_WARNING_OBJECT (pool, "Invalid dmabuf_id"); ++ } ++ } ++ } ++ g_array_free (pool->id_array, TRUE); ++#endif ++ ++ if (pool->element) ++ gst_object_unref (pool->element); ++ pool->element = NULL; ++ ++ if (pool->buffers) ++ g_ptr_array_unref (pool->buffers); ++ pool->buffers = NULL; ++ ++ if (pool->other_pool) ++ gst_object_unref (pool->other_pool); ++ pool->other_pool = NULL; ++ ++ if (pool->allocator) ++ gst_object_unref (pool->allocator); ++ pool->allocator = NULL; ++ ++ if (pool->caps) ++ gst_caps_unref (pool->caps); ++ pool->caps = NULL; ++ ++ G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object); ++} ++ ++static void ++gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass) ++{ ++ GObjectClass *gobject_class = (GObjectClass *) klass; ++ GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; ++ ++ gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData"); ++ ++ gobject_class->finalize = gst_omx_buffer_pool_finalize; ++ gstbufferpool_class->start = gst_omx_buffer_pool_start; ++ gstbufferpool_class->stop = gst_omx_buffer_pool_stop; ++ gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options; ++ gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config; ++ gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer; ++ gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer; ++ gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer; ++ gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer; ++} ++ ++static void ++gst_omx_buffer_pool_init (GstOMXBufferPool * pool) ++{ ++ pool->buffers = g_ptr_array_new (); ++ pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL); ++#ifdef HAVE_MMNGRBUF ++ pool->id_array = g_array_new (FALSE, FALSE, sizeof (gint)); ++#endif ++ pool->enc_buffer_index = 0; ++} ++ ++GstBufferPool * ++gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, ++ GstOMXPort * port) ++{ ++ GstOMXBufferPool *pool; ++ ++ pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL); ++ pool->element = gst_object_ref (element); ++ pool->component = component; ++ pool->port = port; ++ ++ return GST_BUFFER_POOL (pool); ++} +diff --git a/omx/gstomxbufferpool.h b/omx/gstomxbufferpool.h +new file mode 100644 +index 0000000..09cab8d +--- /dev/null ++++ b/omx/gstomxbufferpool.h +@@ -0,0 +1,106 @@ ++/* ++ * Copyright 2014 Advanced Micro Devices, Inc. ++ * Author: Christian König <christian.koenig@amd.com> ++ * Copyright (C) 2015, Renesas Electronics Corporation ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation ++ * version 2.1 of the License. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++#ifndef __GST_OMX_BUFFER_POOL_H__ ++#define __GST_OMX_BUFFER_POOL_H__ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include <gst/gst.h> ++#include <gst/video/gstvideometa.h> ++#include <gst/video/gstvideopool.h> ++ ++#include "gstomx.h" ++ ++G_BEGIN_DECLS ++ ++#define GST_TYPE_OMX_BUFFER_POOL \ ++ (gst_omx_buffer_pool_get_type()) ++#define GST_OMX_BUFFER_POOL(obj) \ ++ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_BUFFER_POOL,GstOMXBufferPool)) ++#define GST_IS_OMX_BUFFER_POOL(obj) \ ++ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_BUFFER_POOL)) ++ ++typedef struct _GstOMXBufferPool GstOMXBufferPool; ++typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass; ++ ++struct _GstOMXBufferPool ++{ ++ GstVideoBufferPool parent; ++ ++ GstElement *element; ++ ++ GstCaps *caps; ++ gboolean add_videometa; ++ gboolean need_copy; ++ GstVideoInfo video_info; ++ ++ /* Owned by element, element has to stop this pool before ++ * it destroys component or port */ ++ GstOMXComponent *component; ++ GstOMXPort *port; ++ ++ /* For handling OpenMAX allocated memory */ ++ GstAllocator *allocator; ++ ++ /* Set from outside this pool */ ++ /* TRUE if we're currently allocating all our buffers */ ++ gboolean allocating; ++ /* TRUE if the pool is not used anymore */ ++ gboolean deactivated; ++ ++ /* For populating the pool from another one */ ++ GstBufferPool *other_pool; ++ GPtrArray *buffers; ++ ++ /* Used during acquire for output ports to ++ * specify which buffer has to be retrieved ++ * and during alloc, which buffer has to be ++ * wrapped ++ */ ++ gint current_buffer_index; ++ ++ /* Used during acquire for input port */ ++ gint enc_buffer_index; ++#ifdef HAVE_MMNGRBUF ++ /* Array use to contain dma_id. It is used in export_end dmabuf area */ ++ GArray *id_array; ++#endif ++ ++ /* TRUE if the downstream buffer pool can handle ++ "videosink_buffer_creation_request" query */ ++ gboolean vsink_buf_req_supported; ++}; ++ ++struct _GstOMXBufferPoolClass ++{ ++ GstVideoBufferPoolClass parent_class; ++}; ++ ++GType gst_omx_buffer_pool_get_type (void); ++ ++GstBufferPool *gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, GstOMXPort * port); ++ ++G_END_DECLS ++ ++#endif /* __GST_OMX_BUFFER_POOL_H__ */ +diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c +index 837c623..25b6b30 100644 +--- a/omx/gstomxvideodec.c ++++ b/omx/gstomxvideodec.c +@@ -32,6 +32,7 @@ + #include <unistd.h> /* getpagesize() */ + + #include "gstomxvideodec.h" ++#include "gstomxbufferpool.h" + + #ifdef HAVE_MMNGRBUF + #include "gst/allocators/gstdmabuf.h" +@@ -42,898 +43,6 @@ + GST_DEBUG_CATEGORY_STATIC (gst_omx_video_dec_debug_category); + #define GST_CAT_DEFAULT gst_omx_video_dec_debug_category + +-typedef struct _GstOMXMemory GstOMXMemory; +-typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator; +-typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass; +- +-struct _GstOMXMemory +-{ +- GstMemory mem; +- +- GstOMXBuffer *buf; +-}; +- +-struct _GstOMXMemoryAllocator +-{ +- GstAllocator parent; +-}; +- +-struct _GstOMXMemoryAllocatorClass +-{ +- GstAllocatorClass parent_class; +-}; +- +-/* User data and function for release OMX buffer in no-copy mode */ +-struct GstOMXBufferCallback +-{ +- GstOMXPort * out_port; +- GstOMXBuffer * buf; +-}; +- +-#define GST_OMX_MEMORY_TYPE "openmax" +-#define DEFAULT_FRAME_PER_SECOND 30 +- +-static GstMemory * +-gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size, +- GstAllocationParams * params) +-{ +- g_assert_not_reached (); +- return NULL; +-} +- +-static void +-gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem) +-{ +- GstOMXMemory *omem = (GstOMXMemory *) mem; +- +- /* TODO: We need to remember which memories are still used +- * so we can wait until everything is released before allocating +- * new memory +- */ +- +- g_slice_free (GstOMXMemory, omem); +-} +- +-static gpointer +-gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) +-{ +- GstOMXMemory *omem = (GstOMXMemory *) mem; +- +- return omem->buf->omx_buf->pBuffer; +-} +- +-static void +-gst_omx_memory_unmap (GstMemory * mem) +-{ +-} +- +-static GstMemory * +-gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size) +-{ +- g_assert_not_reached (); +- return NULL; +-} +- +-GType gst_omx_memory_allocator_get_type (void); +-G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator, +- GST_TYPE_ALLOCATOR); +- +-#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type()) +-#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR)) +- +-static void +-gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass) +-{ +- GstAllocatorClass *allocator_class; +- +- allocator_class = (GstAllocatorClass *) klass; +- +- allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy; +- allocator_class->free = gst_omx_memory_allocator_free; +-} +- +-static void +-gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator) +-{ +- GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); +- +- alloc->mem_type = GST_OMX_MEMORY_TYPE; +- alloc->mem_map = gst_omx_memory_map; +- alloc->mem_unmap = gst_omx_memory_unmap; +- alloc->mem_share = gst_omx_memory_share; +- +- /* default copy & is_span */ +- +- GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +-} +- +-#ifndef HAVE_MMNGRBUF +-static GstMemory * +-gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags, +- GstOMXBuffer * buf, gsize offset, gsize size) +-{ +- GstOMXMemory *mem; +- +- /* FIXME: We don't allow sharing because we need to know +- * when the memory becomes unused and can only then put +- * it back to the pool. Which is done in the pool's release +- * function +- */ +- flags |= GST_MEMORY_FLAG_NO_SHARE; +- +- mem = g_slice_new (GstOMXMemory); +- /* the shared memory is always readonly */ +- gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL, +- buf->omx_buf->nAllocLen, buf->port->port_def.nBufferAlignment, +- offset, size); +- +- mem->buf = buf; +- +- return GST_MEMORY_CAST (mem); +-} +-#endif +- +-/* Buffer pool for the buffers of an OpenMAX port. +- * +- * This pool is only used if we either passed buffers from another +- * pool to the OMX port or provide the OMX buffers directly to other +- * elements. +- * +- * +- * A buffer is in the pool if it is currently owned by the port, +- * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside +- * the pool after it was taken from the port after it was handled +- * by the port, i.e. {Empty,Fill}BufferDone. +- * +- * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated +- * by someone else and (temporarily) passed to this pool +- * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of +- * the buffer will be overriden, and restored in free_buffer(). Other +- * buffers are just freed there. +- * +- * The pool always has a fixed number of minimum and maximum buffers +- * and these are allocated while starting the pool and released afterwards. +- * They correspond 1:1 to the OMX buffers of the port, which are allocated +- * before the pool is started. +- * +- * Acquiring a buffer from this pool happens after the OMX buffer has +- * been acquired from the port. gst_buffer_pool_acquire_buffer() is +- * supposed to return the buffer that corresponds to the OMX buffer. +- * +- * For buffers provided to upstream, the buffer will be passed to +- * the component manually when it arrives and then unreffed. If the +- * buffer is released before reaching the component it will be just put +- * back into the pool as if EmptyBufferDone has happened. If it was +- * passed to the component, it will be back into the pool when it was +- * released and EmptyBufferDone has happened. +- * +- * For buffers provided to downstream, the buffer will be returned +- * back to the component (OMX_FillThisBuffer()) when it is released. +- */ +- +-static GQuark gst_omx_buffer_data_quark = 0; +- +-#define GST_OMX_BUFFER_POOL(pool) ((GstOMXBufferPool *) pool) +-typedef struct _GstOMXBufferPool GstOMXBufferPool; +-typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass; +- +-typedef struct _GstOMXVideoDecBufferData GstOMXVideoDecBufferData; +- +-struct _GstOMXBufferPool +-{ +- GstVideoBufferPool parent; +- +- GstElement *element; +- +- GstCaps *caps; +- gboolean add_videometa; +- GstVideoInfo video_info; +- +- /* Owned by element, element has to stop this pool before +- * it destroys component or port */ +- GstOMXComponent *component; +- GstOMXPort *port; +- +- /* For handling OpenMAX allocated memory */ +- GstAllocator *allocator; +- +- /* Set from outside this pool */ +- /* TRUE if we're currently allocating all our buffers */ +- gboolean allocating; +- +- /* TRUE if the pool is not used anymore */ +- gboolean deactivated; +- +- /* For populating the pool from another one */ +- GstBufferPool *other_pool; +- GPtrArray *buffers; +- +- /* Used during acquire for output ports to +- * specify which buffer has to be retrieved +- * and during alloc, which buffer has to be +- * wrapped +- */ +- gint current_buffer_index; +- +- /* TRUE if the downstream buffer pool can handle +- "videosink_buffer_creation_request" query */ +- gboolean vsink_buf_req_supported; +-}; +- +-struct _GstOMXBufferPoolClass +-{ +- GstVideoBufferPoolClass parent_class; +-}; +- +-struct _GstOMXVideoDecBufferData +-{ +- gboolean already_acquired; +- +-#ifdef HAVE_MMNGRBUF +- gint id_export[GST_VIDEO_MAX_PLANES]; +-#endif +-}; +- +-GType gst_omx_buffer_pool_get_type (void); +- +-G_DEFINE_TYPE (GstOMXBufferPool, gst_omx_buffer_pool, GST_TYPE_BUFFER_POOL); +- +-static void gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, +- GstBuffer * buffer); +- +-static gboolean +-gst_omx_buffer_pool_start (GstBufferPool * bpool) +-{ +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- +- /* Only allow to start the pool if we still are attached +- * to a component and port */ +- GST_OBJECT_LOCK (pool); +- if (!pool->component || !pool->port) { +- GST_OBJECT_UNLOCK (pool); +- return FALSE; +- } +- GST_OBJECT_UNLOCK (pool); +- +- return +- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool); +-} +- +-static gboolean +-gst_omx_buffer_pool_stop (GstBufferPool * bpool) +-{ +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- gint i = 0; +- +- /* When not using the default GstBufferPool::GstAtomicQueue then +- * GstBufferPool::free_buffer is not called while stopping the pool +- * (because the queue is empty) */ +- for (i = 0; i < pool->buffers->len; i++) +- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer +- (bpool, g_ptr_array_index (pool->buffers, i)); +- +- /* Remove any buffers that are there */ +- g_ptr_array_set_size (pool->buffers, 0); +- +- if (pool->caps) +- gst_caps_unref (pool->caps); +- pool->caps = NULL; +- +- pool->add_videometa = FALSE; +- +- return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool); +-} +- +-static const gchar ** +-gst_omx_buffer_pool_get_options (GstBufferPool * bpool) +-{ +- static const gchar *raw_video_options[] = +- { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL }; +- static const gchar *options[] = { NULL }; +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- +- GST_OBJECT_LOCK (pool); +- if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo +- && pool->port->port_def.format.video.eCompressionFormat == +- OMX_VIDEO_CodingUnused) { +- GST_OBJECT_UNLOCK (pool); +- return raw_video_options; +- } +- GST_OBJECT_UNLOCK (pool); +- +- return options; +-} +- +-static gboolean +-gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) +-{ +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- GstCaps *caps; +- +- GST_OBJECT_LOCK (pool); +- +- if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) +- goto wrong_config; +- +- if (caps == NULL) +- goto no_caps; +- +- if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo +- && pool->port->port_def.format.video.eCompressionFormat == +- OMX_VIDEO_CodingUnused) { +- GstVideoInfo info; +- +- /* now parse the caps from the config */ +- if (!gst_video_info_from_caps (&info, caps)) +- goto wrong_video_caps; +- +- /* enable metadata based on config of the pool */ +- pool->add_videometa = +- gst_buffer_pool_config_has_option (config, +- GST_BUFFER_POOL_OPTION_VIDEO_META); +- +- pool->video_info = info; +- } +- +- if (pool->caps) +- gst_caps_unref (pool->caps); +- pool->caps = gst_caps_ref (caps); +- +- GST_OBJECT_UNLOCK (pool); +- +- return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config +- (bpool, config); +- +- /* ERRORS */ +-wrong_config: +- { +- GST_OBJECT_UNLOCK (pool); +- GST_WARNING_OBJECT (pool, "invalid config"); +- return FALSE; +- } +-no_caps: +- { +- GST_OBJECT_UNLOCK (pool); +- GST_WARNING_OBJECT (pool, "no caps in config"); +- return FALSE; +- } +-wrong_video_caps: +- { +- GST_OBJECT_UNLOCK (pool); +- GST_WARNING_OBJECT (pool, +- "failed getting geometry from caps %" GST_PTR_FORMAT, caps); +- return FALSE; +- } +-} +- +-static GstFlowReturn +-gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, +- GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +-{ +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- GstBuffer *buf; +- GstOMXBuffer *omx_buf; +- GstOMXVideoDec *self; +- self = GST_OMX_VIDEO_DEC (pool->element); +- +- g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR); +- +- omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index); +- g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR); +- +- if (pool->other_pool) { +- guint i, n; +- +- buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); +- g_assert (pool->other_pool == buf->pool); +- gst_object_replace ((GstObject **) & buf->pool, NULL); +- +- n = gst_buffer_n_memory (buf); +- for (i = 0; i < n; i++) { +- GstMemory *mem = gst_buffer_peek_memory (buf, i); +- +- /* FIXME: We don't allow sharing because we need to know +- * when the memory becomes unused and can only then put +- * it back to the pool. Which is done in the pool's release +- * function +- */ +- GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE); +- } +- +- if (pool->add_videometa) { +- GstVideoMeta *meta; +- +- meta = gst_buffer_get_video_meta (buf); +- if (!meta) { +- gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE, +- GST_VIDEO_INFO_FORMAT (&pool->video_info), +- GST_VIDEO_INFO_WIDTH (&pool->video_info), +- GST_VIDEO_INFO_HEIGHT (&pool->video_info)); +- } +- } +- } else { +- gsize offset[4] = { 0, }; +- gint stride[4] = { 0, }; +- gsize plane_size[4] = { 0, }; +-#ifndef HAVE_MMNGRBUF +- guint n_planes; +-#endif +- gint i; +- GstOMXVideoDecBufferData *vdbuf_data; +- +- switch (pool->video_info.finfo->format) { +- case GST_VIDEO_FORMAT_I420: +- offset[0] = 0; +- stride[0] = pool->port->port_def.format.video.nStride; +- offset[1] = stride[0] * pool->port->port_def.format.video.nSliceHeight; +- stride[1] = pool->port->port_def.format.video.nStride / 2; +- offset[2] = +- offset[1] + +- stride[1] * (pool->port->port_def.format.video.nSliceHeight / 2); +- stride[2] = pool->port->port_def.format.video.nStride / 2; +- plane_size[0] = pool->port->port_def.format.video.nStride * +- pool->port->port_def.format.video.nFrameHeight; +- plane_size[1] = plane_size[2] = plane_size[0] / 4; +- +-#ifndef HAVE_MMNGRBUF +- n_planes = 3; +-#endif +- break; +- case GST_VIDEO_FORMAT_NV12: +- offset[0] = 0; +- stride[0] = pool->port->port_def.format.video.nStride; +- offset[1] = stride[0] * pool->port->port_def.format.video.nSliceHeight; +- stride[1] = pool->port->port_def.format.video.nStride; +- plane_size[0] = pool->port->port_def.format.video.nStride * +- pool->port->port_def.format.video.nFrameHeight; +- plane_size[1] = plane_size[0] / 2; +- +-#ifndef HAVE_MMNGRBUF +- n_planes = 2; +-#endif +- break; +- default: +- g_assert_not_reached (); +- break; +- } +- +- buf = gst_buffer_new (); +- +-#ifndef HAVE_MMNGRBUF +- if (self->use_dmabuf == FALSE) +- for (i = 0; i < n_planes; i++) +- gst_buffer_append_memory (buf, +- gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf, +- offset[i], plane_size[i])); +-#endif +- +- g_ptr_array_add (pool->buffers, buf); +- +- if (pool->add_videometa) +- gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, +- GST_VIDEO_INFO_FORMAT (&pool->video_info), +- GST_VIDEO_INFO_WIDTH (&pool->video_info), +- GST_VIDEO_INFO_HEIGHT (&pool->video_info), +- GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride); +- +- /* Initialize an already_acquired flag */ +- vdbuf_data = g_slice_new (GstOMXVideoDecBufferData); +- vdbuf_data->already_acquired = FALSE; +-#ifdef HAVE_MMNGRBUF +- if (self->use_dmabuf) +- for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) +- vdbuf_data->id_export[i] = -1; +-#endif +- +- omx_buf->private_data = (void *) vdbuf_data; +- } +- +- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), +- gst_omx_buffer_data_quark, omx_buf, NULL); +- +- *buffer = buf; +- +- pool->current_buffer_index++; +- +- return GST_FLOW_OK; +-} +- +-static void +-gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) +-{ +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- GstOMXBuffer *omx_buf; +- GstOMXVideoDec *self; +- self = GST_OMX_VIDEO_DEC (pool->element); +-#ifdef HAVE_MMNGRBUF +- GstOMXVideoDecBufferData *vdbuf_data; +- gint i; +-#endif +- +- /* If the buffers belong to another pool, restore them now */ +- GST_OBJECT_LOCK (pool); +- if (pool->other_pool) { +- gst_object_replace ((GstObject **) & buffer->pool, +- (GstObject *) pool->other_pool); +- } +- GST_OBJECT_UNLOCK (pool); +- +- omx_buf = +- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), +- gst_omx_buffer_data_quark); +-#ifdef HAVE_MMNGRBUF +- if (self->use_dmabuf) { +- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data; +- if (vdbuf_data) { +- for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) +- if (vdbuf_data->id_export[i] >= 0) +- mmngr_export_end_in_user (vdbuf_data->id_export[i]); +- } +- } +-#endif +- g_slice_free (GstOMXVideoDecBufferData, omx_buf->private_data); +- +- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer), +- gst_omx_buffer_data_quark, NULL, NULL); +- +- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool, +- buffer); +-} +- +-#ifdef HAVE_MMNGRBUF +-static GstBuffer * +-gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool, +- gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gpointer plane_buf[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES]) +-{ +- GstQuery *query; +- GValue val = { 0, }; +- GstStructure *structure; +- const GValue *value; +- GstBuffer *buffer; +- GArray *dmabuf_array; +- GArray *stride_array; +- GArray *planebuf_array; +- gint n_planes; +- gint i; +- +- g_value_init (&val, G_TYPE_POINTER); +- g_value_set_pointer (&val, (gpointer) pool->allocator); +- +- dmabuf_array = g_array_new (FALSE, FALSE, sizeof (gint)); +- stride_array = g_array_new (FALSE, FALSE, sizeof (gint)); +- planebuf_array = g_array_new (FALSE, FALSE, sizeof (gpointer)); +- +- n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info); +- for (i = 0; i < n_planes; i++) { +- g_array_append_val (dmabuf_array, dmabuf_fd[i]); +- g_array_append_val (stride_array, stride[i]); +- g_array_append_val (planebuf_array, plane_buf[i]); +- } +- +- structure = gst_structure_new ("videosink_buffer_creation_request", +- "width", G_TYPE_INT, pool->port->port_def.format.video.nFrameWidth, +- "height", G_TYPE_INT, pool->port->port_def.format.video.nFrameHeight, +- "stride", G_TYPE_ARRAY, stride_array, +- "dmabuf", G_TYPE_ARRAY, dmabuf_array, +- "planebuf", G_TYPE_ARRAY, planebuf_array, +- "allocator", G_TYPE_POINTER, &val, +- "format", G_TYPE_STRING, +- gst_video_format_to_string (pool->video_info.finfo->format), +- "n_planes", G_TYPE_INT, n_planes, NULL); +- +- query = gst_query_new_custom (GST_QUERY_CUSTOM, structure); +- +- GST_DEBUG_OBJECT (pool, "send a videosink_buffer_creation_request query"); +- +- if (!gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (pool->element), query)) { +- GST_ERROR_OBJECT (pool, "videosink_buffer_creation_request query failed"); +- return NULL; +- } +- +- value = gst_structure_get_value (structure, "buffer"); +- buffer = gst_value_get_buffer (value); +- if (buffer == NULL) { +- GST_ERROR_OBJECT (pool, +- "could not get a buffer from videosink_buffer_creation query"); +- return NULL; +- } +- +- gst_query_unref (query); +- +- g_array_free (dmabuf_array, TRUE); +- g_array_free (stride_array, TRUE); +- +- return buffer; +-} +-#endif +- +-#ifdef HAVE_MMNGRBUF +-static gboolean +-gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool, +- guint phys_addr, gint size, gint boundary, gint * id_export, +- gint * dmabuf_fd) +-{ +- gint res; +- +- res = +- mmngr_export_start_in_user (id_export, +- (size + boundary - 1) & ~(boundary - 1), (unsigned long) phys_addr, +- dmabuf_fd); +- if (res != R_MM_OK) { +- GST_ERROR_OBJECT (pool, +- "mmngr_export_start_in_user failed (phys_addr:0x%08x)", phys_addr); +- return FALSE; +- } +- GST_DEBUG_OBJECT (pool, +- "Export dmabuf:%d id_export:%d (phys_addr:0x%08x)", *dmabuf_fd, +- *id_export, phys_addr); +- +- return TRUE; +-} +-#endif +- +-static GstFlowReturn +-gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, +- GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +-{ +- GstFlowReturn ret; +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- GstOMXVideoDec *self; +- self = GST_OMX_VIDEO_DEC (pool->element); +- +- if (pool->port->port_def.eDir == OMX_DirOutput) { +- GstBuffer *buf; +- GstOMXBuffer *omx_buf; +- GstOMXVideoDecBufferData *vdbuf_data; +-#ifdef HAVE_MMNGRBUF +- guint n_mem; +-#endif +- +- g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR); +- +- buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); +- g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); +- +- omx_buf = +- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buf), +- gst_omx_buffer_data_quark); +- +- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data; +-#ifdef HAVE_MMNGRBUF +- if (self->use_dmabuf) +- { +- n_mem = gst_buffer_n_memory (buf); +- if (n_mem == 0) { +- GstBuffer *new_buf; +- GstVideoMeta *vmeta; +- gint n_planes; +- gint i; +- gint dmabuf_fd[GST_VIDEO_MAX_PLANES]; +- gint plane_size[GST_VIDEO_MAX_PLANES]; +- gpointer plane_buf[GST_VIDEO_MAX_PLANES]; +- guint phys_addr; +- OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res = +- (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf-> +- omx_buf->pOutputPortPrivate; +- gint page_size; +- +- GST_DEBUG_OBJECT (pool, "Create dmabuf mem pBuffer=%p", +- omx_buf->omx_buf->pBuffer); +- +- vmeta = gst_buffer_get_video_meta (buf); +- +- phys_addr = (guint) decode_res->pvPhysImageAddressY; +- page_size = getpagesize (); +- +- /* Export a dmabuf file descriptor from the head of Y plane to +- * the end of the buffer so that mapping the whole plane as +- * contiguous memory is available. */ +- if (!gst_omx_buffer_pool_export_dmabuf (pool, phys_addr, +- pool->port->port_def.nBufferSize, page_size, +- &vdbuf_data->id_export[0], &dmabuf_fd[0])) { +- GST_ERROR_OBJECT (pool, "dmabuf exporting failed"); +- return GST_FLOW_ERROR; +- } +- +- plane_size[0] = vmeta->stride[0] * +- GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, 0); +- plane_buf[0] = omx_buf->omx_buf->pBuffer; +- +- /* Export dmabuf file descriptors from second and subsequent planes */ +- n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info); +- for (i = 1; i < n_planes; i++) { +- phys_addr = (guint) decode_res->pvPhysImageAddressY + vmeta->offset[i]; +- plane_size[i] = vmeta->stride[i] * +- GST_VIDEO_INFO_COMP_HEIGHT (&pool->video_info, i); +- plane_buf[i] = omx_buf->omx_buf->pBuffer + vmeta->offset[i]; +- +- if (!gst_omx_buffer_pool_export_dmabuf (pool, phys_addr, plane_size[i], +- page_size, &vdbuf_data->id_export[i], &dmabuf_fd[i])) { +- GST_ERROR_OBJECT (pool, "dmabuf exporting failed"); +- return GST_FLOW_ERROR; +- } +- } +- +- if (pool->vsink_buf_req_supported) +- new_buf = gst_omx_buffer_pool_request_videosink_buffer_creation (pool, +- dmabuf_fd, plane_buf, vmeta->stride); +- else { +- GstVideoMeta *new_meta; +- +- new_buf = gst_buffer_new (); +- for (i = 0; i < n_planes; i++) +- gst_buffer_append_memory (new_buf, +- gst_dmabuf_allocator_alloc (pool->allocator, dmabuf_fd[i], +- plane_size[i])); +- +- gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE, +- GST_VIDEO_INFO_FORMAT (&pool->video_info), +- GST_VIDEO_INFO_WIDTH (&pool->video_info), +- GST_VIDEO_INFO_HEIGHT (&pool->video_info), +- GST_VIDEO_INFO_N_PLANES (&pool->video_info), vmeta->offset, +- vmeta->stride); +- +- new_meta = gst_buffer_get_video_meta (new_buf); +- /* To avoid detaching meta data when a buffer returns +- to the buffer pool */ +- GST_META_FLAG_SET (new_meta, GST_META_FLAG_POOLED); +- } +- +- g_ptr_array_remove_index (pool->buffers, pool->current_buffer_index); +- +- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), +- gst_omx_buffer_data_quark, NULL, NULL); +- +- gst_buffer_unref (buf); +- +- gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (new_buf), +- gst_omx_buffer_data_quark, omx_buf, NULL); +- +- g_ptr_array_add (pool->buffers, new_buf); +- +- *buffer = new_buf; +- } else +- *buffer = buf; +- } else +-#endif +- *buffer = buf; +- +- vdbuf_data->already_acquired = TRUE; +- +- ret = GST_FLOW_OK; +- } else { +- /* Acquire any buffer that is available to be filled by upstream */ +- ret = +- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer +- (bpool, buffer, params); +- } +- +- return ret; +-} +- +-static void +-gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) +-{ +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); +- OMX_ERRORTYPE err; +- GstOMXBuffer *omx_buf; +- +- g_assert (pool->component && pool->port); +- +- if (pool->allocating && !pool->deactivated) { +- GstOMXVideoDecBufferData *vdbuf_data; +- +- omx_buf = +- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), +- gst_omx_buffer_data_quark); +- +- vdbuf_data = (GstOMXVideoDecBufferData *) omx_buf->private_data; +- +- if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used && +- vdbuf_data->already_acquired) { +- /* Release back to the port, can be filled again */ +- err = gst_omx_port_release_buffer (pool->port, omx_buf); +- if (err != OMX_ErrorNone) { +- GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL), +- ("Failed to relase output buffer to component: %s (0x%08x)", +- gst_omx_error_to_string (err), err)); +- } +- vdbuf_data->already_acquired = FALSE; +- } else if (pool->port->port_def.eDir == OMX_DirInput && !omx_buf->used) { +- /* TODO: Implement. +- * +- * If not used (i.e. was not passed to the component) this should do +- * the same as EmptyBufferDone. +- * If it is used (i.e. was passed to the component) this should do +- * nothing until EmptyBufferDone. +- * +- * EmptyBufferDone should release the buffer to the pool so it can +- * be allocated again +- * +- * Needs something to call back here in EmptyBufferDone, like keeping +- * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which +- * would ensure that the buffer is always unused when this is called. +- */ +- g_assert_not_reached (); +- GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer +- (bpool, buffer); +- } +- } +-} +- +-static void +-gst_omx_buffer_pool_finalize (GObject * object) +-{ +- GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object); +- +- if (pool->element) +- gst_object_unref (pool->element); +- pool->element = NULL; +- +- if (pool->buffers) +- g_ptr_array_unref (pool->buffers); +- pool->buffers = NULL; +- +- if (pool->other_pool) +- gst_object_unref (pool->other_pool); +- pool->other_pool = NULL; +- +- if (pool->allocator) +- gst_object_unref (pool->allocator); +- pool->allocator = NULL; +- +- if (pool->caps) +- gst_caps_unref (pool->caps); +- pool->caps = NULL; +- +- G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object); +-} +- +-static void +-gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass) +-{ +- GObjectClass *gobject_class = (GObjectClass *) klass; +- GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; +- +- gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData"); +- +- gobject_class->finalize = gst_omx_buffer_pool_finalize; +- gstbufferpool_class->start = gst_omx_buffer_pool_start; +- gstbufferpool_class->stop = gst_omx_buffer_pool_stop; +- gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options; +- gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config; +- gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer; +- gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer; +- gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer; +- gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer; +-} +- +-static void +-gst_omx_buffer_pool_init (GstOMXBufferPool * pool) +-{ +- pool->buffers = g_ptr_array_new (); +-#ifdef HAVE_MMNGRBUF +- pool->allocator = gst_dmabuf_allocator_new (); +-#else +- pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL); +-#endif +-} +- +-static GstBufferPool * +-gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, +- GstOMXPort * port) +-{ +- GstOMXBufferPool *pool; +- +- pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL); +- pool->element = gst_object_ref (element); +- pool->component = component; +- pool->port = port; +- pool->vsink_buf_req_supported = FALSE; +- +- return GST_BUFFER_POOL (pool); +-} +- + typedef struct _BufferIdentification BufferIdentification; + struct _BufferIdentification + { +@@ -979,8 +88,9 @@ static void gst_omx_video_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + static void gst_omx_video_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); ++/* + static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *); +- ++*/ + enum + { + PROP_0, +@@ -999,6 +109,9 @@ enum + G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstOMXVideoDec, gst_omx_video_dec, + GST_TYPE_VIDEO_DECODER, DEBUG_INIT); + ++/* Default fps for input files that does not support fps */ ++#define DEFAULT_FRAME_PER_SECOND 30 ++ + static gsize + gst_omx_video_dec_copy_frame (GstOMXVideoDec * self, GstBuffer * inbuf, + guint offset, GstOMXBuffer * outbuf) +@@ -1691,6 +804,7 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self) + return err; + } + ++/* + static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *release) + { + if (!release) +@@ -1702,6 +816,7 @@ static void GstOMXBufCallbackfunc (struct GstOMXBufferCallback *release) + + g_free (release); + } ++*/ + + static GstBuffer * + gst_omx_video_dec_create_buffer_from_omx_output (GstOMXVideoDec * self, +@@ -1762,7 +877,7 @@ gst_omx_video_dec_create_buffer_from_omx_output (GstOMXVideoDec * self, + GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, sliceheigh); + used_size = stride[i] * + GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, height); +- ++#if 0 + if (i == 0) { + struct GstOMXBufferCallback *release; + release = g_malloc (sizeof(struct GstOMXBufferCallback)); +@@ -1774,6 +889,7 @@ gst_omx_video_dec_create_buffer_from_omx_output (GstOMXVideoDec * self, + plane_size, 0, used_size, release, GstOMXBufCallbackfunc); + } + else ++#endif + /* Only release OMX buffer one time. Do not add callback + * function to other planes + * (These planes are from same OMX buffer) */ +@@ -2042,15 +1158,7 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) + + n = port->buffers->len; + for (i = 0; i < n; i++) { +- GstBuffer *outbuf; +- GstOMXBuffer *tmp; +- +- outbuf = +- g_ptr_array_index (GST_OMX_BUFFER_POOL (self-> +- out_port_pool)->buffers, i); +- tmp = +- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (outbuf), +- gst_omx_buffer_data_quark); ++ GstOMXBuffer *tmp = g_ptr_array_index (port->buffers, i); + + if (tmp == buf) + break; +@@ -2084,15 +1192,7 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) + + n = port->buffers->len; + for (i = 0; i < n; i++) { +- GstBuffer *outbuf; +- GstOMXBuffer *tmp; +- +- outbuf = +- g_ptr_array_index (GST_OMX_BUFFER_POOL (self-> +- out_port_pool)->buffers, i); +- tmp = +- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (outbuf), +- gst_omx_buffer_data_quark); ++ GstOMXBuffer *tmp = g_ptr_array_index (port->buffers, i); + + if (tmp == buf) + break; +diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c +index cec44cb..b36c46e 100644 +--- a/omx/gstomxvideoenc.c ++++ b/omx/gstomxvideoenc.c +@@ -28,6 +28,11 @@ + #include <string.h> + + #include "gstomxvideoenc.h" ++#include "gstomxbufferpool.h" ++#ifdef HAVE_MMNGRBUF ++#include "mmngr_buf_user_public.h" ++#endif ++#include "gst/allocators/gstdmabuf.h" + + GST_DEBUG_CATEGORY_STATIC (gst_omx_video_enc_debug_category); + #define GST_CAT_DEFAULT gst_omx_video_enc_debug_category +@@ -68,6 +73,13 @@ buffer_identification_free (BufferIdentification * id) + g_slice_free (BufferIdentification, id); + } + ++/* Used in dmabuf mode */ ++struct _GstOMXVideoEncPrivate ++{ ++ /* Array contain id when using mmngrbuf to import fd */ ++ GArray *id_array; ++}; ++ + /* prototypes */ + static void gst_omx_video_enc_finalize (GObject * object); + static void gst_omx_video_enc_set_property (GObject * object, guint prop_id, +@@ -109,7 +121,8 @@ enum + PROP_TARGET_BITRATE, + PROP_QUANT_I_FRAMES, + PROP_QUANT_P_FRAMES, +- PROP_QUANT_B_FRAMES ++ PROP_QUANT_B_FRAMES, ++ PROP_NO_COPY + }; + + /* FIXME: Better defaults */ +@@ -175,6 +188,11 @@ gst_omx_video_enc_class_init (GstOMXVideoEncClass * klass) + 0, G_MAXUINT, GST_OMX_VIDEO_ENC_QUANT_B_FRAMES_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); ++ g_object_class_install_property (gobject_class, PROP_NO_COPY, ++ g_param_spec_boolean ("no-copy", "Propose buffer to upstream", ++ "Whether or not to use no copy method", ++ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | ++ GST_PARAM_MUTABLE_READY)); + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_omx_video_enc_change_state); +@@ -210,7 +228,11 @@ gst_omx_video_enc_init (GstOMXVideoEnc * self) + self->quant_i_frames = GST_OMX_VIDEO_ENC_QUANT_I_FRAMES_DEFAULT; + self->quant_p_frames = GST_OMX_VIDEO_ENC_QUANT_P_FRAMES_DEFAULT; + self->quant_b_frames = GST_OMX_VIDEO_ENC_QUANT_B_FRAMES_DEFAULT; +- ++ self->no_copy = TRUE; ++ self->priv = ++ G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_OMX_VIDEO_ENC, ++ GstOMXVideoEncPrivate); ++ self->priv->id_array = g_array_new (FALSE, FALSE, sizeof (gint)); + g_mutex_init (&self->drain_lock); + g_cond_init (&self->drain_cond); + } +@@ -413,6 +435,16 @@ gst_omx_video_enc_finalize (GObject * object) + g_mutex_clear (&self->drain_lock); + g_cond_clear (&self->drain_cond); + ++#ifdef HAVE_MMNGRBUF ++ if (self->priv->id_array->len > 0) { ++ gint i; ++ for (i = 0; i < self->priv->id_array->len; i++) ++ mmngr_import_end_in_user (g_array_index (self->priv->id_array, gint, ++ i)); ++ } ++#endif; ++ g_array_free (self->priv->id_array, TRUE); ++ + G_OBJECT_CLASS (gst_omx_video_enc_parent_class)->finalize (object); + } + +@@ -453,6 +485,9 @@ gst_omx_video_enc_set_property (GObject * object, guint prop_id, + case PROP_QUANT_B_FRAMES: + self->quant_b_frames = g_value_get_uint (value); + break; ++ case PROP_NO_COPY: ++ self->no_copy = g_value_get_boolean (value); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -481,6 +516,9 @@ gst_omx_video_enc_get_property (GObject * object, guint prop_id, GValue * value, + case PROP_QUANT_B_FRAMES: + g_value_set_uint (value, self->quant_b_frames); + break; ++ case PROP_NO_COPY: ++ g_value_set_boolean (value, self->no_copy); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -1125,163 +1163,168 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, + GST_DEBUG_OBJECT (self, "Setting new format %s", + gst_video_format_to_string (info->finfo->format)); + +- gst_omx_port_get_port_definition (self->enc_in_port, &port_def); +- +- needs_disable = +- gst_omx_component_get_state (self->enc, +- GST_CLOCK_TIME_NONE) != OMX_StateLoaded; +- /* If the component is not in Loaded state and a real format change happens +- * we have to disable the port and re-allocate all buffers. If no real +- * format change happened we can just exit here. ++ /* If there is inport pool, it means that OMXBuffer has already allocated on ++ * propose_allocation. Do not allocate OMXBuffer on set_format + */ +- if (needs_disable) { +- GST_DEBUG_OBJECT (self, "Need to disable and drain encoder"); +- gst_omx_video_enc_drain (self, FALSE); +- gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); +- +- /* Wait until the srcpad loop is finished, +- * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks +- * caused by using this lock from inside the loop function */ +- GST_VIDEO_ENCODER_STREAM_UNLOCK (self); +- gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder)); +- GST_VIDEO_ENCODER_STREAM_LOCK (self); +- +- if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_buffers_released (self->enc_in_port, +- 5 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_buffers_released (self->enc_out_port, +- 1 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_enabled (self->enc_in_port, +- 1 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_enabled (self->enc_out_port, +- 1 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- +- GST_DEBUG_OBJECT (self, "Encoder drained and disabled"); +- } ++ if (!self->in_port_pool) { ++ gst_omx_port_get_port_definition (self->enc_in_port, &port_def); ++ ++ needs_disable = ++ gst_omx_component_get_state (self->enc, ++ GST_CLOCK_TIME_NONE) != OMX_StateLoaded; ++ /* If the component is not in Loaded state and a real format change happens ++ * we have to disable the port and re-allocate all buffers. If no real ++ * format change happened we can just exit here. ++ */ ++ if (needs_disable) { ++ GST_DEBUG_OBJECT (self, "Need to disable and drain encoder"); ++ gst_omx_video_enc_drain (self, FALSE); ++ gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); ++ ++ /* Wait until the srcpad loop is finished, ++ * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks ++ * caused by using this lock from inside the loop function */ ++ GST_VIDEO_ENCODER_STREAM_UNLOCK (self); ++ gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder)); ++ GST_VIDEO_ENCODER_STREAM_LOCK (self); + +- negotiation_map = gst_omx_video_enc_get_supported_colorformats (self); +- if (!negotiation_map) { +- /* Fallback */ +- switch (info->finfo->format) { +- case GST_VIDEO_FORMAT_I420: +- port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; +- break; +- case GST_VIDEO_FORMAT_NV16: +- case GST_VIDEO_FORMAT_NV12: +- port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar; +- break; +- default: +- GST_ERROR_OBJECT (self, "Unsupported format %s", +- gst_video_format_to_string (info->finfo->format)); ++ if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) + return FALSE; +- break; ++ if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_buffers_released (self->enc_in_port, ++ 5 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_buffers_released (self->enc_out_port, ++ 1 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_enabled (self->enc_in_port, ++ 1 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_enabled (self->enc_out_port, ++ 1 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ ++ GST_DEBUG_OBJECT (self, "Encoder drained and disabled"); + } +- } else { +- for (l = negotiation_map; l; l = l->next) { +- VideoNegotiationMap *m = l->data; + +- if (m->format == info->finfo->format) { +- port_def.format.video.eColorFormat = m->type; +- break; ++ negotiation_map = gst_omx_video_enc_get_supported_colorformats (self); ++ if (!negotiation_map) { ++ /* Fallback */ ++ switch (info->finfo->format) { ++ case GST_VIDEO_FORMAT_I420: ++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; ++ break; ++ case GST_VIDEO_FORMAT_NV16: ++ case GST_VIDEO_FORMAT_NV12: ++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar; ++ break; ++ default: ++ GST_ERROR_OBJECT (self, "Unsupported format %s", ++ gst_video_format_to_string (info->finfo->format)); ++ return FALSE; ++ break; ++ } ++ } else { ++ for (l = negotiation_map; l; l = l->next) { ++ VideoNegotiationMap *m = l->data; ++ ++ if (m->format == info->finfo->format) { ++ port_def.format.video.eColorFormat = m->type; ++ break; ++ } + } ++ g_list_free_full (negotiation_map, ++ (GDestroyNotify) video_negotiation_map_free); + } +- g_list_free_full (negotiation_map, +- (GDestroyNotify) video_negotiation_map_free); +- } + +- port_def.format.video.nFrameWidth = info->width; +- if (port_def.nBufferAlignment) +- port_def.format.video.nStride = +- (info->width + port_def.nBufferAlignment - 1) & +- (~(port_def.nBufferAlignment - 1)); +- else +- { +- if (klass->cdata.hacks & GST_OMX_HACK_RENESAS_ENCMC_STRIDE_ALIGN) ++ port_def.format.video.nFrameWidth = info->width; ++ if (port_def.nBufferAlignment) ++ port_def.format.video.nStride = ++ (info->width + port_def.nBufferAlignment - 1) & ++ (~(port_def.nBufferAlignment - 1)); ++ else + { +- switch (port_def.format.video.eColorFormat) { +- case OMX_COLOR_FormatYUV420Planar: { +- /*Renesas encode MC only support following strides*/ +- if (info->width <= 256) +- port_def.format.video.nStride = 256; +- else if ((info->width > 256) && (info->width <= 512)) +- port_def.format.video.nStride = 512; +- else if ((info->width > 512) && (info->width <= 1024)) +- port_def.format.video.nStride = 1024; +- else +- port_def.format.video.nStride = 2048; ++ if (klass->cdata.hacks & GST_OMX_HACK_RENESAS_ENCMC_STRIDE_ALIGN) ++ { ++ switch (port_def.format.video.eColorFormat) { ++ case OMX_COLOR_FormatYUV420Planar: { ++ /*Renesas encode MC only support following strides*/ ++ if (info->width <= 256) ++ port_def.format.video.nStride = 256; ++ else if ((info->width > 256) && (info->width <= 512)) ++ port_def.format.video.nStride = 512; ++ else if ((info->width > 512) && (info->width <= 1024)) ++ port_def.format.video.nStride = 1024; ++ else ++ port_def.format.video.nStride = 2048; ++ break; ++ } ++ case OMX_COLOR_FormatYUV420SemiPlanar: ++ port_def.format.video.nStride = ((info->width + 127) & ~ 127); /* Align 128 */ + break; +- } +- case OMX_COLOR_FormatYUV420SemiPlanar: +- port_def.format.video.nStride = ((info->width + 127) & ~ 127); /* Align 128 */ +- break; +- default: ++ default: ++ port_def.format.video.nStride = GST_ROUND_UP_4 (info->width); /* Safe (?) default */ ++ break; ++ } ++ } else { + port_def.format.video.nStride = GST_ROUND_UP_4 (info->width); /* Safe (?) default */ +- break; + } +- } else { +- port_def.format.video.nStride = GST_ROUND_UP_4 (info->width); /* Safe (?) default */ + } +- } + +- port_def.format.video.nFrameHeight = info->height; +- port_def.format.video.nSliceHeight = info->height; ++ port_def.format.video.nFrameHeight = info->height; ++ port_def.format.video.nSliceHeight = info->height; + +- switch (port_def.format.video.eColorFormat) { +- case OMX_COLOR_FormatYUV420Planar: +- case OMX_COLOR_FormatYUV420PackedPlanar: +- port_def.nBufferSize = +- (port_def.format.video.nStride * port_def.format.video.nFrameHeight) + +- 2 * ((port_def.format.video.nStride / 2) * +- ((port_def.format.video.nFrameHeight + 1) / 2)); +- break; +- +- case OMX_COLOR_FormatYUV420SemiPlanar: +- port_def.nBufferSize = +- (port_def.format.video.nStride * port_def.format.video.nFrameHeight) + +- (port_def.format.video.nStride * +- ((port_def.format.video.nFrameHeight + 1) / 2)); +- break; ++ switch (port_def.format.video.eColorFormat) { ++ case OMX_COLOR_FormatYUV420Planar: ++ case OMX_COLOR_FormatYUV420PackedPlanar: ++ port_def.nBufferSize = ++ (port_def.format.video.nStride * port_def.format.video.nFrameHeight) + ++ 2 * ((port_def.format.video.nStride / 2) * ++ ((port_def.format.video.nFrameHeight + 1) / 2)); ++ break; + +- default: +- g_assert_not_reached (); +- } ++ case OMX_COLOR_FormatYUV420SemiPlanar: ++ port_def.nBufferSize = ++ (port_def.format.video.nStride * port_def.format.video.nFrameHeight) + ++ (port_def.format.video.nStride * ++ ((port_def.format.video.nFrameHeight + 1) / 2)); ++ break; + +- if (info->fps_n == 0) { +- port_def.format.video.xFramerate = 0; +- } else { +- if (!(klass->cdata.hacks & GST_OMX_HACK_VIDEO_FRAMERATE_INTEGER)) +- port_def.format.video.xFramerate = (info->fps_n << 16) / (info->fps_d); +- else +- port_def.format.video.xFramerate = (info->fps_n) / (info->fps_d); +- } ++ default: ++ g_assert_not_reached (); ++ } + +- GST_DEBUG_OBJECT (self, "Setting inport port definition"); +- if (gst_omx_port_update_port_definition (self->enc_in_port, +- &port_def) != OMX_ErrorNone) +- return FALSE; ++ if (info->fps_n == 0) { ++ port_def.format.video.xFramerate = 0; ++ } else { ++ if (!(klass->cdata.hacks & GST_OMX_HACK_VIDEO_FRAMERATE_INTEGER)) ++ port_def.format.video.xFramerate = (info->fps_n << 16) / (info->fps_d); ++ else ++ port_def.format.video.xFramerate = (info->fps_n) / (info->fps_d); ++ } + +- if (klass->set_format) { +- if (!klass->set_format (self, self->enc_in_port, state)) { +- GST_ERROR_OBJECT (self, "Subclass failed to set the new format"); ++ GST_DEBUG_OBJECT (self, "Setting inport port definition"); ++ if (gst_omx_port_update_port_definition (self->enc_in_port, ++ &port_def) != OMX_ErrorNone) + return FALSE; ++ ++ if (klass->set_format) { ++ if (!klass->set_format (self, self->enc_in_port, state)) { ++ GST_ERROR_OBJECT (self, "Subclass failed to set the new format"); ++ return FALSE; ++ } + } +- } + +- GST_DEBUG_OBJECT (self, "Updating outport port definition"); +- if (gst_omx_port_update_port_definition (self->enc_out_port, +- NULL) != OMX_ErrorNone) +- return FALSE; ++ GST_DEBUG_OBJECT (self, "Updating outport port definition"); ++ if (gst_omx_port_update_port_definition (self->enc_out_port, ++ NULL) != OMX_ErrorNone) ++ return FALSE; ++ } + + GST_DEBUG_OBJECT (self, "Enabling component"); + if (needs_disable) { +@@ -1299,11 +1342,13 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, + return FALSE; + + /* Need to allocate buffers to reach Idle state */ +- if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) +- return FALSE; ++ if (!self->in_port_pool) { ++ if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) ++ return FALSE; ++ } + + /* Allocate for output port */ +- if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) ++ if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_component_get_state (self->enc, + GST_CLOCK_TIME_NONE) != OMX_StateIdle) +@@ -1349,7 +1394,9 @@ gst_omx_video_enc_reset (GstVideoEncoder * encoder, gboolean hard) + + self = GST_OMX_VIDEO_ENC (encoder); + +- GST_DEBUG_OBJECT (self, "Resetting encoder"); ++ GST_DEBUG_OBJECT (self, "Resetting encoder %s", hard ? "(hard)" : ""); ++ ++ return TRUE; + + gst_omx_port_set_flushing (self->enc_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); +@@ -1739,11 +1786,50 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, + gst_omx_error_to_string (err), err); + } + +- /* Copy the buffer content in chunks of size as requested +- * by the port */ +- if (!gst_omx_video_enc_fill_buffer (self, frame->input_buffer, buf)) { +- gst_omx_port_release_buffer (port, buf); +- goto buffer_fill_error; ++ if (self->in_port_pool) { ++ GstMapInfo in_info; ++ gint count = 0; ++ GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (self->in_port_pool); ++ ++ /* Compare input buffer with buffer got from port to get target data for ++ * encoder ++ */ ++ if (!pool->deactivated) { ++ if (!gst_buffer_map (frame->input_buffer, &in_info, GST_MAP_READ)) { ++ GST_ERROR_OBJECT (self, "Can not map input buffer"); ++ gst_omx_port_release_buffer (port, buf); ++ goto flow_error; ++ } ++ ++ if (buf->omx_buf->pBuffer != in_info.data) { ++ gst_omx_port_release_buffer (port, buf); ++ do { ++ acq_ret = gst_omx_port_acquire_buffer (port, &buf); ++ if (acq_ret != GST_OMX_ACQUIRE_BUFFER_OK) { ++ GST_ERROR_OBJECT (self, "Can acquire buffer from input port"); ++ return GST_FLOW_ERROR; ++ } ++ if (buf->omx_buf->pBuffer != in_info.data) ++ gst_omx_port_release_buffer (port, buf); ++ count += 1; ++ } while (buf->omx_buf->pBuffer != in_info.data ++ && count < port->port_def.nBufferCountActual * 3); ++ } ++ if (count == port->port_def.nBufferCountActual * 3) { ++ GST_ERROR_OBJECT (self, ++ "Can not get target OMXBuffer after 3 times searching"); ++ goto flow_error; ++ } ++ buf->omx_buf->nFilledLen = in_info.size; ++ gst_buffer_unmap (frame->input_buffer, &in_info); ++ } ++ } else { ++ /* Copy the buffer content in chunks of size as requested ++ * by the port */ ++ if (!gst_omx_video_enc_fill_buffer (self, frame->input_buffer, buf)) { ++ gst_omx_port_release_buffer (port, buf); ++ goto buffer_fill_error; ++ } + } + + timestamp = frame->pts; +@@ -1920,11 +2006,203 @@ static gboolean + gst_omx_video_enc_propose_allocation (GstVideoEncoder * encoder, + GstQuery * query) + { +- gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); ++ GstOMXVideoEnc *self; ++ GstOMXVideoEncClass *klass; ++ ++ self = GST_OMX_VIDEO_ENC (encoder); ++ klass = GST_OMX_VIDEO_ENC_GET_CLASS (encoder); ++ ++ GST_DEBUG_OBJECT (self, "gst_omx_video_enc_propose_allocation"); ++ if (self->no_copy == TRUE) { ++ /* Allocate buffers and propose them to upstream */ ++ GstCaps *caps; ++ GstVideoInfo info; ++ guint size; ++ OMX_PARAM_PORTDEFINITIONTYPE port_def; ++ guint max, min; ++ ++ gst_omx_port_get_port_definition (self->enc_in_port, &port_def); ++ ++ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); ++ ++ gst_query_parse_allocation (query, &caps, NULL); ++ ++ if (caps == NULL) ++ return FALSE; ++ ++ if (!gst_video_info_from_caps (&info, caps)) ++ return FALSE; ++ ++ size = GST_VIDEO_INFO_SIZE (&info); ++ ++ if (gst_omx_component_get_state (self->enc, ++ GST_CLOCK_TIME_NONE) != OMX_StateLoaded) ++ return FALSE; ++ switch (info.finfo->format) { ++ case GST_VIDEO_FORMAT_I420: ++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; ++ break; ++ case GST_VIDEO_FORMAT_NV12: ++ case GST_VIDEO_FORMAT_NV16: ++ port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar; ++ break; ++ default: ++ GST_ERROR_OBJECT (self, "Unsupported format %s", ++ gst_video_format_to_string (info.finfo->format)); ++ return FALSE; ++ break; ++ } ++ port_def.format.video.nFrameWidth = info.width; ++ if (klass->cdata.hacks & GST_OMX_HACK_RENESAS_ENCMC_STRIDE_ALIGN) { ++ switch (port_def.format.video.eColorFormat) { ++ case OMX_COLOR_FormatYUV420Planar: ++ port_def.format.video.nStride = GST_ROUND_UP_64 (info.width); ++ break; ++ case OMX_COLOR_FormatYUV420SemiPlanar: ++ port_def.format.video.nStride = GST_ROUND_UP_32 (info.width); ++ break; ++ default: ++ break; ++ } ++ } else ++ port_def.format.video.nStride = GST_ROUND_UP_4 (info.width); /* safe (?) default */ ++ port_def.format.video.nFrameHeight = info.height; ++ port_def.format.video.nSliceHeight = info.height; ++ switch (port_def.format.video.eColorFormat) { ++ case OMX_COLOR_FormatYUV420Planar: ++ case OMX_COLOR_FormatYUV420PackedPlanar: ++ port_def.nBufferSize = ++ (port_def.format.video.nStride * ++ port_def.format.video.nFrameHeight) + ++ 2 * ((port_def.format.video.nStride / 2) * ++ ((port_def.format.video.nFrameHeight + 1) / 2)); ++ break; ++ ++ case OMX_COLOR_FormatYUV420SemiPlanar: ++ port_def.nBufferSize = ++ (port_def.format.video.nStride * ++ port_def.format.video.nFrameHeight) + ++ (port_def.format.video.nStride * ++ ((port_def.format.video.nFrameHeight + 1) / 2)); ++ break; + +- return +- GST_VIDEO_ENCODER_CLASS +- (gst_omx_video_enc_parent_class)->propose_allocation (encoder, query); ++ default: ++ g_assert_not_reached (); ++ } ++ if (info.fps_n == 0) { ++ port_def.format.video.xFramerate = 0; ++ } else { ++ port_def.format.video.xFramerate = (info.fps_n) / (info.fps_d); ++ } ++ ++ if (gst_omx_port_update_port_definition (self->enc_in_port, ++ &port_def) != OMX_ErrorNone) ++ return FALSE; ++ ++ if (klass->set_format) { ++ if (!klass->set_format (self, self->enc_in_port, self->input_state)) { ++ GST_ERROR_OBJECT (self, "Subclass failed to set the new format"); ++ return FALSE; ++ } ++ } ++ GST_DEBUG_OBJECT (self, "Updating outport port definition"); ++ if (gst_omx_port_update_port_definition (self->enc_out_port, ++ NULL) != OMX_ErrorNone) ++ return FALSE; ++ ++ if (self->target_bitrate != 0xffffffff) { ++ OMX_VIDEO_PARAM_BITRATETYPE config; ++ OMX_ERRORTYPE err; ++ ++ GST_OMX_INIT_STRUCT (&config); ++ config.nPortIndex = self->enc_out_port->index; ++ /* Get default value of eControlRate to avoid setting an invalid value to it */ ++ err = gst_omx_component_get_parameter (self->enc, ++ OMX_IndexParamVideoBitrate, &config); ++ if (err != OMX_ErrorNone) ++ GST_ERROR_OBJECT (self, ++ "Fail to get parameter of video bitrate: %s (0x%08x)", ++ gst_omx_error_to_string (err), err); ++ ++ config.nTargetBitrate = self->target_bitrate; ++ if (self->control_rate != 0xffffffff) ++ config.eControlRate = self->control_rate; ++ ++ err = gst_omx_component_set_parameter (self->enc, ++ OMX_IndexParamVideoBitrate, &config); ++ if (err != OMX_ErrorNone) ++ GST_ERROR_OBJECT (self, "Failed to set bitrate parameter: %s (0x%08x)", ++ gst_omx_error_to_string (err), err); ++ } ++ ++ if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone) ++ return FALSE; ++ ++ if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) ++ return FALSE; ++ ++ if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) ++ return FALSE; ++ ++ GST_DEBUG_OBJECT (self, " gst_query_get_n_allocation_pools = %d", ++ gst_query_get_n_allocation_pools (query)); ++ if (gst_query_get_n_allocation_pools (query) == 0) { ++ GstStructure *structure; ++ GstAllocator *allocator = NULL; ++ GstAllocationParams params = { 0, }; ++ ++ if (gst_query_get_n_allocation_params (query) > 0) ++ gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶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 <andrey.gusakov@cogentembedded.com> +Date: Thu, 1 Sep 2016 18:44:49 +0300 +Subject: [PATCH 04/10] Export a first dmabuf file descriptor with the whole + size + +This patch exports a dmabuf file descriptor from the head of Y plane +to the end of the buffer so that mapping the whole plane as +contiguous memory is available. + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + omx/gstomxbufferpool.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c +index 2585a72..b9fa769 100644 +--- a/omx/gstomxbufferpool.c ++++ b/omx/gstomxbufferpool.c +@@ -399,6 +399,7 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self, + gint i; + gint page_size; + guint phys_addr = 0; ++ guint phys_size = 0; + + new_buf = gst_buffer_new (); + page_size = getpagesize (); +@@ -410,13 +411,15 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self, + OMXR_MC_VIDEO_DECODERESULTTYPE *decode_res = + (OMXR_MC_VIDEO_DECODERESULTTYPE *) omx_buf->omx_buf->pOutputPortPrivate; + phys_addr = decode_res->pvPhysImageAddressY; ++ phys_size = (guint) omx_buf->omx_buf->nAllocLen; + } else if (GST_IS_OMX_VIDEO_ENC (self->element)) { + /* private data is a physical address of HW buffer */ + phys_addr = (guint) omx_buf->omx_buf->pInputPortPrivate; ++ phys_size = (guint) omx_buf->omx_buf->nAllocLen; + } + +- if (phys_addr == 0) { +- GST_ERROR_OBJECT (self, "Invalid phys addr for OMX buffer"); ++ if ((phys_addr == 0) || (phys_size == 0)) { ++ GST_ERROR_OBJECT (self, "Invalid phys range for OMX buffer"); + return NULL; + } + +@@ -428,8 +431,14 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self, + /* Calculate offset between physical address and page boundary */ + page_offset[i] = plane_addr & (page_size - 1); + +- plane_size[i] = stride[i] * +- GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i); ++ /* Export a dmabuf file descriptor from the head of Y plane to ++ * the end of the buffer so that mapping the whole plane as ++ * contiguous memory is available. */ ++ if (i == 0) ++ plane_size[i] = phys_size; ++ else ++ plane_size[i] = stride[i] * ++ GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i); + + /* When downstream plugins do mapping from dmabuf fd it requires + * mapping from boundary page and size align for page size so +-- +1.7.10.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch new file mode 100644 index 0000000..c6b5c36 --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0005-gssomxbufferpool-add-exported-flag.patch @@ -0,0 +1,75 @@ +From c4e86a58041cd4408d283444dcba6f532a80697c Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Thu, 1 Sep 2016 17:33:44 +0300 +Subject: [PATCH 05/10] gssomxbufferpool: add exported flag + +This flag indicates that buffer are used outside of OMX component + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + omx/gstomx.c | 1 + + omx/gstomx.h | 5 +++++ + omx/gstomxbufferpool.c | 5 ++++- + 3 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/omx/gstomx.c b/omx/gstomx.c +index c018e72..5a916dc 100644 +--- a/omx/gstomx.c ++++ b/omx/gstomx.c +@@ -1663,6 +1663,7 @@ gst_omx_port_allocate_buffers_unlocked (GstOMXPort * port, + buf = g_slice_new0 (GstOMXBuffer); + buf->port = port; + buf->used = FALSE; ++ buf->exported = FALSE; + buf->settings_cookie = port->settings_cookie; + g_ptr_array_add (port->buffers, buf); + +diff --git a/omx/gstomx.h b/omx/gstomx.h +index 84980f3..27cb2a9 100644 +--- a/omx/gstomx.h ++++ b/omx/gstomx.h +@@ -279,6 +279,11 @@ struct _GstOMXBuffer { + */ + gboolean used; + ++ /* TRUE if the buffer exported outside the component, ++ * i.e. someone acquired this buffer ++ */ ++ gboolean exported; ++ + /* Cookie of the settings when this buffer was allocated */ + gint settings_cookie; + +diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c +index b9fa769..1e0a14c 100644 +--- a/omx/gstomxbufferpool.c ++++ b/omx/gstomxbufferpool.c +@@ -695,7 +695,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + pool->enc_buffer_index = 0; + + count += 1; +- } while (omx_buf->used == TRUE && ++ } while (omx_buf->exported == TRUE && + count < pool->port->port_def.nBufferCountActual * 3); + + if (count == pool->port->port_def.nBufferCountActual * 3) { +@@ -703,6 +703,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + GST_ERROR_OBJECT (pool, + "Can not acquire buffer after 3 times searching"); + } else { ++ omx_buf->exported = TRUE; + *buffer = buf; + ret = GST_FLOW_OK; + } +@@ -731,6 +732,8 @@ gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) + omx_buf = + gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), + gst_omx_buffer_data_quark); ++ if (GST_IS_OMX_VIDEO_ENC (pool->element)) ++ omx_buf->exported = FALSE; + if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) { + /* Release back to the port, can be filled again */ + err = gst_omx_port_release_buffer (pool->port, omx_buf); +-- +1.7.10.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch new file mode 100644 index 0000000..93966b2 --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0006-gstomxbufferpool-create-dmabuf-for-input-port.patch @@ -0,0 +1,46 @@ +From 078a91a917a7b81cfcf523ac23b1c3e154506ef9 Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Thu, 1 Sep 2016 17:43:35 +0300 +Subject: [PATCH 06/10] gstomxbufferpool: create dmabuf for input port + + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + omx/gstomxbufferpool.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c +index 1e0a14c..d86f9d8 100644 +--- a/omx/gstomxbufferpool.c ++++ b/omx/gstomxbufferpool.c +@@ -518,6 +518,7 @@ gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, + + pool->need_copy = FALSE; + } else { ++ gboolean dmabuf = FALSE; + GstMemory *mem; + const guint nstride = pool->port->port_def.format.video.nStride; + const guint nslice = pool->port->port_def.format.video.nSliceHeight; +@@ -552,8 +553,17 @@ gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, + + if (GST_IS_OMX_VIDEO_DEC (pool->element) && + GST_OMX_VIDEO_DEC (pool->element)->use_dmabuf == TRUE && +- (omx_buf->omx_buf->pOutputPortPrivate)) { +-#if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT) ++ (omx_buf->omx_buf->pOutputPortPrivate)) ++ dmabuf = TRUE; ++ ++ ++ if (GST_IS_OMX_VIDEO_ENC (pool->element) && ++ GST_OMX_VIDEO_ENC (pool->element)->no_copy == TRUE && ++ (omx_buf->omx_buf->pInputPortPrivate)) ++ dmabuf = TRUE; ++ ++ if (dmabuf) { ++#if defined (HAVE_MMNGRBUF) + if (pool->allocator) + gst_object_unref (pool->allocator); + pool->allocator = gst_dmabuf_allocator_new (); +-- +1.7.10.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch new file mode 100644 index 0000000..ed9da8d --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch @@ -0,0 +1,140 @@ +From 4abd8ac4f18f5baef5a23c7defdb12469192f9c5 Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Thu, 1 Sep 2016 17:51:30 +0300 +Subject: [PATCH 07/10] gstomxbufferpool: add helper to get omxbuffer from + gstomxbuffer + + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + omx/gstomxbufferpool.c | 21 +++++++++++++++------ + omx/gstomxbufferpool.h | 2 ++ + omx/gstomxvideoenc.c | 26 ++++++++++++++++---------- + 3 files changed, 33 insertions(+), 16 deletions(-) + +diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c +index d86f9d8..eb2fe9d 100644 +--- a/omx/gstomxbufferpool.c ++++ b/omx/gstomxbufferpool.c +@@ -361,6 +361,17 @@ wrong_video_caps: + } + } + ++GstOMXBuffer *gst_omx_buffer_get_omxbuffer (GstBuffer * buffer) ++{ ++ GstOMXBuffer *omx_buf; ++ ++ omx_buf = ++ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), ++ gst_omx_buffer_data_quark); ++ ++ return omx_buf; ++} ++ + #if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT) + static gboolean + gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool, +@@ -697,9 +708,7 @@ gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + buf = g_ptr_array_index (pool->buffers, pool->enc_buffer_index); + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + +- omx_buf = +- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buf), +- gst_omx_buffer_data_quark); ++ omx_buf = gst_omx_buffer_get_omxbuffer(buf); + pool->enc_buffer_index++; + if (pool->enc_buffer_index == pool->port->port_def.nBufferCountActual) + pool->enc_buffer_index = 0; +@@ -739,11 +748,11 @@ gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) + g_assert (pool->component && pool->port); + + if (!pool->allocating && !pool->deactivated) { +- omx_buf = +- gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), +- gst_omx_buffer_data_quark); ++ omx_buf = gst_omx_buffer_get_omxbuffer(buffer); ++ + if (GST_IS_OMX_VIDEO_ENC (pool->element)) + omx_buf->exported = FALSE; ++ + if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) { + /* Release back to the port, can be filled again */ + err = gst_omx_port_release_buffer (pool->port, omx_buf); +diff --git a/omx/gstomxbufferpool.h b/omx/gstomxbufferpool.h +index 09cab8d..0c6f18b 100644 +--- a/omx/gstomxbufferpool.h ++++ b/omx/gstomxbufferpool.h +@@ -101,6 +101,8 @@ GType gst_omx_buffer_pool_get_type (void); + + GstBufferPool *gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, GstOMXPort * port); + ++GstOMXBuffer *gst_omx_buffer_get_omxbuffer (GstBuffer * buffer); ++ + G_END_DECLS + + #endif /* __GST_OMX_BUFFER_POOL_H__ */ +diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c +index b36c46e..e96ff28 100644 +--- a/omx/gstomxvideoenc.c ++++ b/omx/gstomxvideoenc.c +@@ -1786,7 +1786,8 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, + gst_omx_error_to_string (err), err); + } + +- if (self->in_port_pool) { ++ if ((self->in_port_pool) && ++ (frame->input_buffer->pool == self->in_port_pool)) { + GstMapInfo in_info; + gint count = 0; + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (self->in_port_pool); +@@ -1795,13 +1796,15 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, + * encoder + */ + if (!pool->deactivated) { +- if (!gst_buffer_map (frame->input_buffer, &in_info, GST_MAP_READ)) { +- GST_ERROR_OBJECT (self, "Can not map input buffer"); +- gst_omx_port_release_buffer (port, buf); +- goto flow_error; ++ GstOMXBuffer *omx_buf; ++ ++ omx_buf = gst_omx_buffer_get_omxbuffer(frame->input_buffer); ++ if (!omx_buf) { ++ GST_ERROR_OBJECT (self, "Can not get OMXBuffer from GstBuffer"); ++ return GST_FLOW_ERROR; + } + +- if (buf->omx_buf->pBuffer != in_info.data) { ++ if (buf != omx_buf) { + gst_omx_port_release_buffer (port, buf); + do { + acq_ret = gst_omx_port_acquire_buffer (port, &buf); +@@ -1809,10 +1812,10 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, + GST_ERROR_OBJECT (self, "Can acquire buffer from input port"); + return GST_FLOW_ERROR; + } +- if (buf->omx_buf->pBuffer != in_info.data) ++ if (buf != omx_buf) + gst_omx_port_release_buffer (port, buf); + count += 1; +- } while (buf->omx_buf->pBuffer != in_info.data ++ } while (buf != omx_buf + && count < port->port_def.nBufferCountActual * 3); + } + if (count == port->port_def.nBufferCountActual * 3) { +@@ -1820,8 +1823,11 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, + "Can not get target OMXBuffer after 3 times searching"); + goto flow_error; + } +- buf->omx_buf->nFilledLen = in_info.size; +- gst_buffer_unmap (frame->input_buffer, &in_info); ++ GST_DEBUG_OBJECT (self, "found target OMXBuffer %p", buf); ++ //buf->omx_buf->nFilledLen = gst_buffer_get_size (frame->input_buffer); ++ buf->omx_buf->nFilledLen = buf->omx_buf->nAllocLen - buf->omx_buf->nOffset; ++ GST_DEBUG_OBJECT (self, "set nFilledLen = %d", buf->omx_buf->nFilledLen); ++ + } + } else { + /* Copy the buffer content in chunks of size as requested +-- +1.7.10.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch new file mode 100644 index 0000000..8f97e7f --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch @@ -0,0 +1,47 @@ +From d1025433f05ebeb1a790abefa5cfc48455bf441c Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Thu, 1 Sep 2016 17:57:10 +0300 +Subject: [PATCH 08/10] gstomxenc: do not allocate output buffers two times + + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + omx/gstomxvideoenc.c | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c +index e96ff28..19a0eb9 100644 +--- a/omx/gstomxvideoenc.c ++++ b/omx/gstomxvideoenc.c +@@ -1338,18 +1338,21 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, + if (gst_omx_port_mark_reconfigured (self->enc_in_port) != OMX_ErrorNone) + return FALSE; + } else { +- if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone) +- return FALSE; +- +- /* Need to allocate buffers to reach Idle state */ ++ /* if is not done in propose_allocation */ + if (!self->in_port_pool) { ++ if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone) ++ return FALSE; ++ ++ /* Need to allocate buffers to reach Idle state */ ++ /* Allocate for input port */ + if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) + return FALSE; ++ ++ /* Allocate for output port */ ++ if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) ++ return FALSE; + } + +- /* Allocate for output port */ +- if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) +- return FALSE; + if (gst_omx_component_get_state (self->enc, + GST_CLOCK_TIME_NONE) != OMX_StateIdle) + return FALSE; +-- +1.7.10.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch new file mode 100644 index 0000000..8404acd --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch @@ -0,0 +1,113 @@ +From 0a9f0aa8271b0fc18c7e9781e3d0bc215ba2fd02 Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Thu, 1 Sep 2016 17:58:33 +0300 +Subject: [PATCH 09/10] gstomxenc: move encoder disable code to separate + function + + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + omx/gstomxvideoenc.c | 79 ++++++++++++++++++++++++++++---------------------- + 1 file changed, 45 insertions(+), 34 deletions(-) + +diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c +index 19a0eb9..6720648 100644 +--- a/omx/gstomxvideoenc.c ++++ b/omx/gstomxvideoenc.c +@@ -1147,6 +1147,49 @@ gst_omx_video_enc_get_supported_colorformats (GstOMXVideoEnc * self) + } + + static gboolean ++gst_omx_video_enc_disable(GstVideoEncoder * encoder) ++{ ++ GstOMXVideoEnc *self; ++ ++ self = GST_OMX_VIDEO_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "Need to disable and drain encoder"); ++ gst_omx_video_enc_drain (self, FALSE); ++ gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); ++ ++ /* Wait until the srcpad loop is finished, ++ * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks ++ * caused by using this lock from inside the loop function */ ++ GST_VIDEO_ENCODER_STREAM_UNLOCK (self); ++ gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder)); ++ GST_VIDEO_ENCODER_STREAM_LOCK (self); ++ ++ if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_buffers_released (self->enc_in_port, ++ 5 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_buffers_released (self->enc_out_port, ++ 1 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_enabled (self->enc_in_port, ++ 1 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ if (gst_omx_port_wait_enabled (self->enc_out_port, ++ 1 * GST_SECOND) != OMX_ErrorNone) ++ return FALSE; ++ ++ GST_DEBUG_OBJECT (self, "Encoder drained and disabled"); ++ return TRUE; ++} ++ ++static gboolean + gst_omx_video_enc_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state) + { +@@ -1176,41 +1219,9 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, + * we have to disable the port and re-allocate all buffers. If no real + * format change happened we can just exit here. + */ +- if (needs_disable) { +- GST_DEBUG_OBJECT (self, "Need to disable and drain encoder"); +- gst_omx_video_enc_drain (self, FALSE); +- gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); +- +- /* Wait until the srcpad loop is finished, +- * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks +- * caused by using this lock from inside the loop function */ +- GST_VIDEO_ENCODER_STREAM_UNLOCK (self); +- gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder)); +- GST_VIDEO_ENCODER_STREAM_LOCK (self); +- +- if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) ++ if (needs_disable) ++ if (!gst_omx_video_enc_disable(encoder)) + return FALSE; +- if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_buffers_released (self->enc_in_port, +- 5 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_buffers_released (self->enc_out_port, +- 1 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_enabled (self->enc_in_port, +- 1 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- if (gst_omx_port_wait_enabled (self->enc_out_port, +- 1 * GST_SECOND) != OMX_ErrorNone) +- return FALSE; +- +- GST_DEBUG_OBJECT (self, "Encoder drained and disabled"); +- } + + negotiation_map = gst_omx_video_enc_get_supported_colorformats (self); + if (!negotiation_map) { +-- +1.7.10.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch new file mode 100644 index 0000000..bd2b91c --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/0010-omxvideodec-support-creating-buffers-using-sink.patch @@ -0,0 +1,186 @@ +From 58d8ea72ec78cb17cf75c82c67a69e9bd383c3b3 Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Thu, 1 Sep 2016 20:09:03 +0300 +Subject: [PATCH 10/10] omxvideodec: support creating buffers using sink + +Used for zero-copy output to wayland/weston + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + omx/gstomxbufferpool.c | 107 +++++++++++++++++++++++++++++++++++++++++++++--- + omx/gstomxvideodec.c | 11 ++++- + 2 files changed, 111 insertions(+), 7 deletions(-) + +diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c +index eb2fe9d..60b25ef 100644 +--- a/omx/gstomxbufferpool.c ++++ b/omx/gstomxbufferpool.c +@@ -372,6 +372,73 @@ GstOMXBuffer *gst_omx_buffer_get_omxbuffer (GstBuffer * buffer) + return omx_buf; + } + ++#ifdef HAVE_MMNGRBUF ++static GstBuffer * ++gst_omx_buffer_pool_request_videosink_buffer_creation (GstOMXBufferPool * pool, ++ gint dmabuf_fd[GST_VIDEO_MAX_PLANES], gpointer plane_buf[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES]) ++{ ++ GstQuery *query; ++ GValue val = { 0, }; ++ GstStructure *structure; ++ const GValue *value; ++ GstBuffer *buffer; ++ GArray *dmabuf_array; ++ GArray *stride_array; ++ GArray *planebuf_array; ++ gint n_planes; ++ gint i; ++ ++ g_value_init (&val, G_TYPE_POINTER); ++ g_value_set_pointer (&val, (gpointer) pool->allocator); ++ ++ dmabuf_array = g_array_new (FALSE, FALSE, sizeof (gint)); ++ stride_array = g_array_new (FALSE, FALSE, sizeof (gint)); ++ planebuf_array = g_array_new (FALSE, FALSE, sizeof (gpointer)); ++ ++ n_planes = GST_VIDEO_INFO_N_PLANES (&pool->video_info); ++ for (i = 0; i < n_planes; i++) { ++ g_array_append_val (dmabuf_array, dmabuf_fd[i]); ++ g_array_append_val (stride_array, stride[i]); ++ g_array_append_val (planebuf_array, plane_buf[i]); ++ } ++ ++ structure = gst_structure_new ("videosink_buffer_creation_request", ++ "width", G_TYPE_INT, pool->port->port_def.format.video.nFrameWidth, ++ "height", G_TYPE_INT, pool->port->port_def.format.video.nFrameHeight, ++ "stride", G_TYPE_ARRAY, stride_array, ++ "dmabuf", G_TYPE_ARRAY, dmabuf_array, ++ "planebuf", G_TYPE_ARRAY, planebuf_array, ++ "allocator", G_TYPE_POINTER, &val, ++ "format", G_TYPE_STRING, ++ gst_video_format_to_string (pool->video_info.finfo->format), ++ "n_planes", G_TYPE_INT, n_planes, NULL); ++ ++ query = gst_query_new_custom (GST_QUERY_CUSTOM, structure); ++ ++ GST_DEBUG_OBJECT (pool, "send a videosink_buffer_creation_request query"); ++ ++ if (!gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (pool->element), query)) { ++ GST_ERROR_OBJECT (pool, "videosink_buffer_creation_request query failed"); ++ return NULL; ++ } ++ ++ value = gst_structure_get_value (structure, "buffer"); ++ buffer = gst_value_get_buffer (value); ++ if (buffer == NULL) { ++ GST_ERROR_OBJECT (pool, ++ "could not get a buffer from videosink_buffer_creation query"); ++ return NULL; ++ } ++ ++ gst_query_unref (query); ++ ++ g_array_free (dmabuf_array, TRUE); ++ g_array_free (stride_array, TRUE); ++ ++ return buffer; ++} ++#endif ++ + #if defined (HAVE_MMNGRBUF) && defined (HAVE_VIDEODEC_EXT) + static gboolean + gst_omx_buffer_pool_export_dmabuf (GstOMXBufferPool * pool, +@@ -406,6 +473,7 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self, + gint plane_size_ext[GST_VIDEO_MAX_PLANES]; + gint dmabuf_id[GST_VIDEO_MAX_PLANES]; + gint page_offset[GST_VIDEO_MAX_PLANES]; ++ gint plane_buf[GST_VIDEO_MAX_PLANES]; + GstBuffer *new_buf; + gint i; + gint page_size; +@@ -450,6 +518,7 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self, + else + plane_size[i] = stride[i] * + GST_VIDEO_INFO_COMP_HEIGHT (&self->video_info, i); ++ plane_buf[i] = omx_buf->omx_buf->pBuffer + offset[i]; + + /* When downstream plugins do mapping from dmabuf fd it requires + * mapping from boundary page and size align for page size so +@@ -472,14 +541,40 @@ gst_omx_buffer_pool_create_buffer_contain_dmabuf (GstOMXBufferPool * self, + gst_buffer_append_memory (new_buf, mem); + } + +- g_ptr_array_add (self->buffers, new_buf); +- gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE, +- GST_VIDEO_INFO_FORMAT (&self->video_info), +- GST_VIDEO_INFO_WIDTH (&self->video_info), +- GST_VIDEO_INFO_HEIGHT (&self->video_info), +- GST_VIDEO_INFO_N_PLANES (&self->video_info), offset, stride); ++ if (self->vsink_buf_req_supported) { ++ new_buf = gst_omx_buffer_pool_request_videosink_buffer_creation (self, ++ dmabuf_fd, plane_buf, stride); ++ if (!new_buf) { ++ GST_ERROR_OBJECT (self, "creating dmabuf using videosink failed"); ++ goto err; ++ } ++ new_buf->pool = self; ++ } else { ++ new_buf = gst_buffer_new (); ++ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->video_info); i++) { ++ GstMemory *mem; ++ /* Set offset's information */ ++ mem = gst_dmabuf_allocator_alloc (self->allocator, dmabuf_fd[i], ++ plane_size_ext[i]); ++ mem->offset = page_offset[i]; ++ mem->size = plane_size[i]; ++ gst_buffer_append_memory (new_buf, mem); ++ } ++ ++ gst_buffer_add_video_meta_full (new_buf, GST_VIDEO_FRAME_FLAG_NONE, ++ GST_VIDEO_INFO_FORMAT (&self->video_info), ++ GST_VIDEO_INFO_WIDTH (&self->video_info), ++ GST_VIDEO_INFO_HEIGHT (&self->video_info), ++ GST_VIDEO_INFO_N_PLANES (&self->video_info), offset, stride); ++ } + ++ GST_ERROR_OBJECT (self, "got buffer %p from pool %p", ++ new_buf, new_buf->pool); ++ g_ptr_array_add (self->buffers, new_buf); + return new_buf; ++ ++err: ++ return NULL; + } + #endif + +diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c +index 25b6b30..44b706a 100644 +--- a/omx/gstomxvideodec.c ++++ b/omx/gstomxvideodec.c +@@ -2350,6 +2350,8 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query) + &GST_OMX_BUFFER_POOL (self->out_port_pool)->vsink_buf_req_supported); + gst_object_unref (pool); + update_pool = TRUE; ++ GST_ERROR_OBJECT (self, "vsink_buf_req_supported %d", ++ GST_OMX_BUFFER_POOL (self->out_port_pool)->vsink_buf_req_supported); + } + + /* Set pool parameters to our own configuration */ +@@ -2372,7 +2374,14 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query) + } + + GST_OMX_BUFFER_POOL (self->out_port_pool)->allocating = TRUE; +- gst_buffer_pool_set_active (self->out_port_pool, TRUE); ++ /* This now allocates all the buffers */ ++ if (!gst_buffer_pool_set_active (self->out_port_pool, TRUE)) { ++ GST_INFO_OBJECT (self, "Failed to activate internal pool"); ++ gst_object_unref (self->out_port_pool); ++ self->out_port_pool = NULL; ++ } else { ++ GST_OMX_BUFFER_POOL (self->out_port_pool)->allocating = FALSE; ++ } + + /* This video buffer pool created below will not be used, just setting to + * the gstvideodecoder class through a query, because it is +-- +1.7.10.4 + diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf new file mode 100644 index 0000000..375e201 --- /dev/null +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx/gstomx.conf @@ -0,0 +1,62 @@ +[omxh263dec] +type-name=GstOMXH263Dec +core-name=/usr/local/lib/libomxr_core.so +component-name=OMX.RENESAS.VIDEO.DECODER.H263 +rank=512 +in-port-index=0 +out-port-index=1 +hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio + +[omxh264dec] +type-name=GstOMXH264Dec +core-name=/usr/local/lib/libomxr_core.so +component-name=OMX.RENESAS.VIDEO.DECODER.H264 +rank=512 +in-port-index=0 +out-port-index=1 +hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio + +[omxmpeg2videodec] +type-name=GstOMXMPEG2VideoDec +core-name=/usr/local/lib/libomxr_core.so +component-name=OMX.RENESAS.VIDEO.DECODER.MPEG2 +rank=512 +in-port-index=0 +out-port-index=1 +hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio + +[omxmpeg4videodec] +type-name=GstOMXMPEG4VideoDec +core-name=/usr/local/lib/libomxr_core.so +component-name=OMX.RENESAS.VIDEO.DECODER.MPEG4 +rank=512 +in-port-index=0 +out-port-index=1 +hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio + +[omxvc1videodec] +type-name=GstOMXVC1VideoDec +core-name=/usr/local/lib/libomxr_core.so +component-name=OMX.RENESAS.VIDEO.DECODER.VC1 +rank=512 +in-port-index=0 +out-port-index=1 +hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1;no-component-role;default-pix-aspect-ratio + +[omxaacdec] +type-name=GstOMXAACDec +core-name=/usr/local/lib/libomxr_core.so +component-name=OMX.RENESAS.AUDIO.DECODER.AAC +rank=256 +in-port-index=0 +out-port-index=1 +hacks= + +[omxh264enc] +type-name=GstOMXH264Enc +core-name=/usr/local/lib/libomxr_core.so +component-name=OMX.RENESAS.VIDEO.ENCODER.H264 +rank=256 +in-port-index=0 +out-port-index=1 +hacks=renesas-encmc-stride-align diff --git a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend index 95706e5..426f0c4 100644 --- a/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend +++ b/meta-rcar-gen2/recipes-multimedia/gstreamer/gstreamer1.0-omx_1.0.0.bbappend @@ -1,20 +1,24 @@ require ../../include/gles-control.inc SRC_URI_rcar-gen2 = "git://github.com/renesas-devel/gst-omx.git;protocol=git;branch=RCAR-GEN2/1.0.0" -SRCREV_rcar-gen2 = "05563465faad99243ee2dd30547e3075eb8cf5e3" +SRCREV_rcar-gen2 = "${@'e0a23fb50ec211a8058eac223847bbcc574fb343' \ + if '1' in '${USE_GLES_WAYLAND}' else '05563465faad99243ee2dd30547e3075eb8cf5e3'}" -LIC_FILES_CHKSUM_remove_rcar-gen2 = " file://omx/gstomx.h;beginline=1;endline=21;md5=5c8e1fca32704488e76d2ba9ddfa935f" +LIC_FILES_CHKSUM_remove_rcar-gen2 = " file://omx/gstomx.h;beginline=1;endline=21;md5=5c8e1fca32704488e76d2ba9ddfa935f" LIC_FILES_CHKSUM_append_rcar-gen2 = " file://omx/gstomx.h;beginline=1;endline=22;md5=17e5f2943dace9e5cde4a8587a31e8f9" S = "${WORKDIR}/git" -do_configure_prepend() { +do_configure() { cd ${S} ./autogen.sh --noconfigure cd ${B} + oe_runconf } DEPENDS_append_rcar-gen2 = " omx-user-module mmngrbuf-user-module" -EXTRA_OECONF_append_rcar-gen2 = " --with-omx-target=rcar --enable-experimental \ +EXTRA_OECONF_append_rcar-gen2 = " \ + --with-omx-target=rcar --enable-experimental \ + '${@'--enable-nv12-page-alignment' if '${USE_GLES_WAYLAND}' == '1' else ''}' \ '${@'--disable-dmabuf' if '${USE_GLES}' == '0' and '${USE_WAYLAND}' == '1' else ''}'" # Overwrite do_install[postfuncs] += " set_omx_core_name " @@ -22,6 +26,23 @@ EXTRA_OECONF_append_rcar-gen2 = " --with-omx-target=rcar --enable-experimental \ revert_omx_core_name() { sed -i -e "s;^core-name=.*;core-name=/usr/local/lib/libomxr_core.so;" "${D}/etc/xdg/gstomx.conf" } + REVERT_OMX_CORE_NAME = "" REVERT_OMX_CORE_NAME_rcar-gen2 = "revert_omx_core_name" do_install[postfuncs] += "${REVERT_OMX_CORE_NAME}" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI_append = " \ + file://0001-omx-videodec-add-planebuf-to-allocation-request.patch \ + file://0002-Fixed-memory-corruption-and-bad-access.patch \ + file://0003-omxvideoenc-export-dmafd-buffer-through-own-buffer-p.patch \ + file://0004-Export-a-first-dmabuf-file-descriptor-with-the-whole.patch \ + file://0005-gssomxbufferpool-add-exported-flag.patch \ + file://0006-gstomxbufferpool-create-dmabuf-for-input-port.patch \ + file://0007-gstomxbufferpool-add-helper-to-get-omxbuffer-from-gs.patch \ + file://0008-gstomxenc-do-not-allocate-output-buffers-two-times.patch \ + file://0009-gstomxenc-move-encoder-disable-code-to-separate-func.patch \ + file://0010-omxvideodec-support-creating-buffers-using-sink.patch \ + file://gstomx.conf \ +" diff --git a/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb b/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb index 688c6b7..8fa557f 100644 --- a/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb +++ b/meta-rcar-gen2/recipes-multimedia/packagegroups/packagegroup-rcar-gen2-multimedia.bb @@ -23,6 +23,10 @@ MULTIMEDIA_PACKAGES ="\ libmemcpy \ " +MULTIMEDIA_PACKAGES_append = " \ + ${@ "vsp2-kernel-module" if "${USE_GLES_WAYLAND}" == "1" else "" } \ +" + RDEPENDS_packagegroup-rcar-gen2-multimedia = "\ ${@ "${MULTIMEDIA_PACKAGES}" if "${USE_MULTIMEDIA}" == "1" else "" } \ media-ctl \ |