diff options
author | 2017-03-02 09:17:45 +0100 | |
---|---|---|
committer | 2017-03-21 14:36:40 +0000 | |
commit | 975d053dec34c40142725fdbc7d75d597d51920e (patch) | |
tree | 43c59b25f7bdc2540424ce3eb5bb8f0a7ce50e2a /meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch | |
parent | 8fff09a2d58e7f21f9ee2da001601d36f18b2281 (diff) |
Remove fix Add-gst-recorder for gen2
The patches for gst-recorder are not compliant with weston 1.11.
Bug-AGL: SPEC-473
* Remove both weston.service and weston_%.bbappend
because all lines in these files only for supporting
to share screen using H.264.
Bug-AGL: SPEC-471
Bug-AGL: SPEC-411
Change-Id: I09ad20f5eadcc269aceb2d6de75cc84d00a07bb9
Signed-off-by: Ronan Le Martret <ronan.lemartret@iot.bzh>
Diffstat (limited to 'meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch')
-rw-r--r-- | meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch | 4183 |
1 files changed, 0 insertions, 4183 deletions
diff --git a/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch b/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch deleted file mode 100644 index e6eccc6..0000000 --- a/meta-rcar-gen2/recipes-graphics/wayland/weston/0003-Add-gst-recorder-for-h264-output-streaming.patch +++ /dev/null @@ -1,4183 +0,0 @@ -From 73a18d5178129591b0c58d0f0150ce0a62d7c39a Mon Sep 17 00:00:00 2001 -From: Grigory Kletsko <grigory.kletsko@cogentembedded.com> -Date: Wed, 2 Nov 2016 17:36:41 +0300 -Subject: [PATCH] Add gst-recorder for h264 output streaming - -To use gst-recorder run weston with arg --gst-record. Add -recorder=true property to output section of one of the outputs -(real or virtual). Use properties ip, port to set host of RTP -stream. Use property bitrate to set desirable maximum h264 bitrate. - -Signed-off-by: Grigory Kletsko <grigory.kletsko@cogentembedded.com> -Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> ---- - Makefile.am | 17 + - configure.ac | 11 + - src/compositor-drm.c | 202 ++++++- - src/gst-recorder.c | 1222 +++++++++++++++++++++++++++++++++++++++++ - src/gst-recorder.h | 58 ++ - src/media-ctl/libmediactl.c | 955 ++++++++++++++++++++++++++++++++ - src/media-ctl/libv4l2subdev.c | 759 +++++++++++++++++++++++++ - src/media-ctl/mediactl-priv.h | 64 +++ - src/media-ctl/mediactl.h | 423 ++++++++++++++ - src/media-ctl/tools.h | 32 ++ - src/media-ctl/v4l2subdev.h | 258 +++++++++ - 11 files changed, 3999 insertions(+), 2 deletions(-) - create mode 100644 src/gst-recorder.c - create mode 100644 src/gst-recorder.h - create mode 100644 src/media-ctl/libmediactl.c - create mode 100644 src/media-ctl/libv4l2subdev.c - create mode 100644 src/media-ctl/mediactl-priv.h - create mode 100644 src/media-ctl/mediactl.h - create mode 100644 src/media-ctl/tools.h - create mode 100644 src/media-ctl/v4l2subdev.h - -diff --git a/Makefile.am b/Makefile.am -index 62719c9..1624e7c 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -267,6 +267,23 @@ drm_backend_la_SOURCES += src/vaapi-recorder.c src/vaapi-recorder.h - drm_backend_la_LIBADD += $(LIBVA_LIBS) - drm_backend_la_CFLAGS += $(LIBVA_CFLAGS) - endif -+ -+if ENABLE_GST_RECORDER -+drm_backend_la_SOURCES += \ -+ src/gst-recorder.c \ -+ src/gst-recorder.h \ -+ src/v4l2-device.h \ -+ src/media-ctl/libmediactl.c \ -+ src/media-ctl/libv4l2subdev.c \ -+ src/media-ctl/mediactl-priv.h \ -+ src/media-ctl/mediactl.h \ -+ src/media-ctl/tools.h \ -+ src/media-ctl/v4l2subdev.h -+drm_backend_la_LIBADD += $(LIBVA_LIBS) \ -+ -lgstallocators-1.0 \ -+ -lgstvideo-1.0 -+drm_backend_la_CFLAGS += $(LIBVA_CFLAGS) -+endif - endif - - if ENABLE_WAYLAND_COMPOSITOR -diff --git a/configure.ac b/configure.ac -index b8fd67f..5040813 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -321,6 +321,17 @@ if test x$enable_vaapi_recorder != xno; then - fi - AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$have_libva" = xyes) - -+AC_ARG_ENABLE(gst-recorder, [ --enable-gst-recorder],, -+ enable_gst_recorder=yes) -+if test x$enable_gst_recorder != xno; then -+ AC_DEFINE([BUILD_GST_RECORDER], [1], [Build the gst recorder]) -+ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0 >= 1.0]) -+ -+ CPPFLAGS="$CPPFLAGS $GSTREAMER_CFLAGS" -+ LIBS="$LIBS $GSTREAMER_LIBS -lgstapp-1.0" -+fi -+AM_CONDITIONAL(ENABLE_GST_RECORDER, test "x$enable_gst_recorder" != xno) -+ - - AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes) - if test x$have_jpeglib = xyes; then -diff --git a/src/compositor-drm.c b/src/compositor-drm.c -index 85c7d0a..4dd0dfa 100644 ---- a/src/compositor-drm.c -+++ b/src/compositor-drm.c -@@ -55,6 +55,7 @@ - #include "libinput-seat.h" - #include "launcher-util.h" - #include "vaapi-recorder.h" -+#include "gst-recorder.h" - #include "presentation_timing-server-protocol.h" - #include "linux-dmabuf.h" - -@@ -124,6 +125,8 @@ struct drm_backend { - - int use_pixman; - -+ int enable_recorder; -+ - uint32_t prev_state; - - struct udev_input input; -@@ -192,7 +195,12 @@ struct drm_output { - int current_image; - pixman_region32_t previous_damage; - -+#ifdef BUILD_VAAPI_RECORDER - struct vaapi_recorder *recorder; -+#endif -+#ifdef BUILD_GST_RECORDER -+ struct gst_recorder *recorder; -+#endif - struct wl_listener recorder_frame_listener; - - /* not real output device */ -@@ -230,6 +238,7 @@ struct drm_parameters { - int connector; - int tty; - int use_pixman; -+ int enable_recorder; - const char *seat_id; - }; - -@@ -2140,6 +2149,23 @@ parse_modeline(const char *s, drmModeModeInfo *mode) - return 0; - } - -+static int parse_crop_rect(const char *s, struct v4l2_rect* crop) -+{ -+ crop->left = 0; -+ crop->top = 0; -+ crop->width = 0; -+ crop->height = 0; -+ -+ if (sscanf(s, "%dx%d@%dx%d", -+ &crop->width, -+ &crop->height, -+ &crop->top, -+ &crop->left) != 4) -+ return -1; -+ -+ return 0; -+} -+ - static void - setup_output_seat_constraint(struct drm_backend *b, - struct weston_output *output, -@@ -3320,7 +3346,170 @@ recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, - recorder_destroy(output); - } - } --#else -+#endif -+ -+#ifdef BUILD_GST_RECORDER -+static void -+recorder_destroy(struct drm_backend *c, struct drm_output *output) -+{ -+ wl_list_remove(&output->recorder_frame_listener.link); -+ -+ gst_recorder_destroy(output->recorder); -+ output->recorder = NULL; -+ -+ output->base.disable_planes--; -+ -+ weston_log("[gst recorder] done\n"); -+} -+ -+static void -+recorder_frame_notify(struct wl_listener *listener, void *data) -+{ -+ int ret = 0; -+ struct drm_output *output; -+ struct drm_backend *c; -+ -+ -+ output = container_of(listener, struct drm_output, -+ recorder_frame_listener); -+ c = (struct drm_backend *) output->base.compositor; -+ -+ if (!output->recorder) { -+ weston_log("%s: output have no recorder enabled\n", -+ output->base.name); -+ return; -+ } -+ -+ if (!output->current) { -+ weston_log("%s: frame notify while current frame == NULL\n", -+ output->base.name); -+ return; -+ } -+ -+ if (output->current->dmafd) { -+ ret = gst_recorder_frame_dmafd(output->recorder, output->current->dmafd, -+ output->current->stride); -+ } -+ -+ if (ret < 0) { -+ weston_log("[gst recorder] aborted: %m\n"); -+ recorder_destroy(c, output); -+ } -+} -+ -+static int -+recorder_enable(struct drm_backend *c, struct drm_output *output) -+{ -+ int enable_recorder = 0; -+ struct gst_recorder_settings *settings; -+ struct weston_config_section *section; -+ char* s; -+ struct v4l2_rect crop = { .width = output->base.current_mode->width, -+ .height = output->base.current_mode->height, -+ .top = 0, -+ .left = 0 }; -+ -+ if (output->recorder) -+ return -1; -+ -+ section = weston_config_get_section(c->compositor->config, "output", "name", -+ output->base.name); -+ -+ weston_config_section_get_bool(section, "recorder", &enable_recorder, 0); -+ -+ if (!enable_recorder) -+ return 0; -+ -+ /* TODO: add support for NV16 or NV12 */ -+ if (output->format != GBM_FORMAT_XRGB8888) { -+ weston_log("[gst recorder] %s: " -+ "output format not supported\n", output->base.name); -+ return -1; -+ } -+ -+ settings = malloc(sizeof(* settings)); -+ weston_config_section_get_string(section, "ip", &settings->ip, NULL); -+ if (!settings->ip) -+ goto err; -+ weston_config_section_get_int(section, "port", &settings->port, -1); -+ /* default gives about 16 Mbit/s at 1280x720@60FPS */ -+ weston_config_section_get_int(section, "bitrate", &settings->bitrate, 300000); -+ -+ settings->width = output->base.current_mode->width; -+ settings->height = output->base.current_mode->height; -+ -+ settings->crop = crop; -+ -+ weston_config_section_get_string(section, "crop", &s, NULL); -+ if (s) { -+ if (parse_crop_rect(s, &settings->crop)) { -+ weston_log("[gst recorder] %s:" -+ " failed to parse crop parameter\n", -+ output->base.name); -+ goto err; -+ } -+ } -+ -+ -+ output->recorder = -+ gst_recorder_create(settings); -+ if (!output->recorder) { -+ weston_log("[gst recorder] %s:" -+ " failed to create gst recorder\n", -+ output->base.name); -+ goto err; -+ } -+ -+ output->base.disable_planes++; -+ -+ output->recorder_frame_listener.notify = recorder_frame_notify; -+ wl_signal_add(&output->base.frame_signal, -+ &output->recorder_frame_listener); -+ -+ weston_output_schedule_repaint(&output->base); -+ -+ weston_log("[gst recorder] %s:" -+ " recorder initialized\n", -+ output->base.name); -+ -+ return 0; -+err: -+ weston_log("[gst recorder] %s:" -+ " invalid settings\n", -+ output->base.name); -+ free(settings); -+ return -1; -+} -+ -+static void -+recorders_enable(struct drm_backend *c) -+{ -+ struct drm_output *output; -+ -+ wl_list_for_each(output, &c->compositor->output_list, base.link) { -+ recorder_enable(c, output); -+ } -+} -+ -+static void -+recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, -+ void *data) -+{ -+ struct drm_backend *c = data; -+ struct drm_output *output; -+ -+ /* fix this */ -+ wl_list_for_each(output, &c->compositor->output_list, base.link) { -+ if (!output->recorder) -+ recorder_enable(c, output); -+ else -+ recorder_destroy(c, output); -+ } -+} -+#endif -+ -+#if !defined(BUILD_VAAPI_RECORDER) && !defined(BUILD_GST_RECORDER) -+ - static void - recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, - void *data) -@@ -3422,7 +3611,8 @@ drm_backend_create(struct weston_compositor *compositor, - goto err_base; - - b->use_pixman = param->use_pixman; -- -+ b->enable_recorder = param->enable_recorder; -+ - /* Check if we run drm-backend using weston-launch */ - compositor->launcher = weston_launcher_connect(compositor, param->tty, - param->seat_id, true); -@@ -3465,6 +3655,10 @@ drm_backend_create(struct weston_compositor *compositor, - } - } - -+#ifdef BUILD_GST_RECORDER -+ gst_recorder_init(); -+#endif -+ - b->base.destroy = drm_destroy; - b->base.restore = drm_restore; - -@@ -3529,6 +3723,9 @@ drm_backend_create(struct weston_compositor *compositor, - recorder_binding, b); - weston_compositor_add_debug_binding(compositor, KEY_W, - renderer_switch_binding, b); -+ /* enable GST recording-streaming */ -+ if (b->enable_recorder) -+ recorders_enable(b); - - if (compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(compositor) < 0) -@@ -3576,6 +3773,7 @@ backend_init(struct weston_compositor *compositor, int *argc, char *argv[], - { WESTON_OPTION_INTEGER, "tty", 0, ¶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 - |