[PATCH 6/6] clients: add simple-v4l-dmabuf client

Derek Foreman derekf at osg.samsung.com
Thu Dec 3 13:42:19 PST 2015


On 24/11/15 01:28 PM, Emmanuel Gil Peyrot wrote:
> This client opens a V4L2 device, usually exposed as /dev/videoN, and
> retrieves its frames as dmabuf for later import into the compositor.
> 
> It supports both single- and multi-planar devices, and any format
> exposed by the V4L2 device the Wayland compositor accepts.
> 
> This client never changes the v4l2 settings, use `v4l2-ctl -c` if you
> want to change those.
> 
> Signed-off-by: Emmanuel Gil Peyrot <emmanuel.peyrot at collabora.com>
> 
> Maniphest Tasks: T90
> 
> Differential Revision: https://phabricator.freedesktop.org/D339
> ---
>  .gitignore                  |   1 +
>  Makefile.am                 |  15 +
>  clients/simple-dmabuf-v4l.c | 939 ++++++++++++++++++++++++++++++++++++++++++++
>  configure.ac                |  14 +
>  4 files changed, 969 insertions(+)
>  create mode 100644 clients/simple-dmabuf-v4l.c
> 
> diff --git a/.gitignore b/.gitignore
> index 719054d..dfbb4d3 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -67,6 +67,7 @@ weston-presentation-shm
>  weston-resizor
>  weston-scaler
>  weston-simple-dmabuf-intel
> +weston-simple-dmabuf-v4l
>  weston-simple-egl
>  weston-simple-shm
>  weston-simple-touch
> diff --git a/Makefile.am b/Makefile.am
> index 1eb66ae..31e4216 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -538,6 +538,21 @@ weston_simple_dmabuf_intel_LDADD = $(SIMPLE_DMABUF_INTEL_CLIENT_LIBS) libshared.
>  BUILT_SOURCES += protocol/linux-dmabuf-unstable-v1-client-protocol.h
>  endif
>  
> +if BUILD_SIMPLE_DMABUF_V4L_CLIENT
> +demo_clients += weston-simple-dmabuf-v4l
> +weston_simple_dmabuf_v4l_SOURCES = clients/simple-dmabuf-v4l.c
> +nodist_weston_simple_dmabuf_v4l_SOURCES =		\
> +	protocol/xdg-shell-unstable-v5-protocol.c		\
> +	protocol/xdg-shell-unstable-v5-client-protocol.h	\
> +	protocol/fullscreen-shell-unstable-v1-protocol.c	\
> +	protocol/fullscreen-shell-unstable-v1-client-protocol.h \
> +	protocol/linux-dmabuf-unstable-v1-protocol.c \
> +	protocol/linux-dmabuf-unstable-v1-client-protocol.h
> +weston_simple_dmabuf_v4l_CFLAGS = $(AM_CFLAGS) $(SIMPLE_DMABUF_V4L_CLIENT_CFLAGS)
> +weston_simple_dmabuf_v4l_LDADD = $(SIMPLE_DMABUF_V4L_CLIENT_LIBS) libshared.la
> +BUILT_SOURCES += protocol/linux-dmabuf-client-protocol.h
> +endif
> +
>  noinst_LTLIBRARIES += libtoytoolkit.la
>  
>  libtoytoolkit_la_SOURCES =				\
> diff --git a/clients/simple-dmabuf-v4l.c b/clients/simple-dmabuf-v4l.c
> new file mode 100644
> index 0000000..4899b05
> --- /dev/null
> +++ b/clients/simple-dmabuf-v4l.c
> @@ -0,0 +1,939 @@
> +/*
> + * Copyright © 2015 Collabora Ltd.
> + *
> + * 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.
> + */
> +
> +#include <config.h>
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <assert.h>
> +#include <unistd.h>
> +#include <sys/mman.h>
> +#include <signal.h>
> +#include <fcntl.h>
> +
> +#include <drm_fourcc.h>
> +
> +#include <stropts.h>
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <linux/videodev2.h>
> +#include <linux/input.h>
> +
> +#include <wayland-client.h>
> +#include "xdg-shell-unstable-v5-client-protocol.h"
> +#include "fullscreen-shell-unstable-v1-client-protocol.h"
> +#include "linux-dmabuf-unstable-v1-client-protocol.h"
> +
> +#define CLEAR(x) memset(&(x), 0, sizeof(x))
> +
> +static int
> +xioctl(int fh, int request, void *arg)
> +{
> +	int r;
> +
> +	do {
> +		r = ioctl(fh, request, arg);
> +	} while (r == -1 && errno == EINTR);
> +
> +	return r;
> +}
> +
> +static uint32_t
> +parse_format(const char fmt[4])
> +{
> +	/* FIXME: this won’t work on big-endian. */
> +	return fourcc_code(fmt[0], fmt[1], fmt[2], fmt[3]);
> +}
> +
> +static const char *
> +dump_format(uint32_t *format)
> +{
> +	/* FIXME: this won’t work properly on big-endian. */
> +	return (const char *)format;
> +}

Should probably FIX these FIXMEs...

> +struct buffer_format {
> +	int width;
> +	int height;
> +	enum v4l2_buf_type type;
> +	uint32_t format;
> +
> +	unsigned num_planes;
> +	unsigned strides[VIDEO_MAX_PLANES];
> +};
> +
> +struct display {
> +	struct wl_display *display;
> +	struct wl_registry *registry;
> +	struct wl_compositor *compositor;
> +	struct wl_seat *seat;
> +	struct wl_keyboard *keyboard;
> +	struct xdg_shell *shell;
> +	struct zwp_fullscreen_shell_v1 *fshell;
> +	struct zwp_linux_dmabuf_v1 *dmabuf;
> +	int yuyv_format_found;

I guess format_found could be a bool...

> +
> +	int v4l_fd;
> +	struct buffer_format format;
> +	uint32_t drm_format;
> +};
> +
> +struct buffer {
> +	struct wl_buffer *buffer;
> +	struct display *display;
> +	int busy;
> +	int index;
> +
> +	int dmabuf_fds[VIDEO_MAX_PLANES];
> +	int data_offsets[VIDEO_MAX_PLANES];
> +};
> +
> +#define NUM_BUFFERS 3
> +
> +struct window {
> +	struct display *display;
> +	struct wl_surface *surface;
> +	struct xdg_surface *xdg_surface;
> +	struct buffer buffers[NUM_BUFFERS];
> +	struct wl_callback *callback;
> +};
> +
> +static bool running = true;
> +
> +static int
> +queue(struct display *display, struct buffer *buffer)
> +{
> +	struct v4l2_buffer buf;
> +	struct v4l2_plane planes[VIDEO_MAX_PLANES];
> +	unsigned i;
> +
> +	CLEAR(buf);
> +	buf.type = display->format.type;
> +	buf.memory = V4L2_MEMORY_MMAP;
> +	buf.index = buffer->index;
> +
> +	if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		CLEAR(planes);
> +		buf.length = VIDEO_MAX_PLANES;
> +		buf.m.planes = planes;
> +	}
> +
> +	if (xioctl(display->v4l_fd, VIDIOC_QUERYBUF, &buf) == -1) {
> +		perror("VIDIOC_QUERYBUF");
> +		return 0;
> +	}
> +
> +	if (xioctl(display->v4l_fd, VIDIOC_QBUF, &buf) == -1) {
> +		perror("VIDIOC_QBUF");
> +		return 0;
> +	}
> +
> +	if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		if (display->format.num_planes != buf.length) {
> +			fprintf(stderr, "Wrong number of planes returned by "
> +			                "QUERYBUF\n");
> +			return 0;
> +		}
> +
> +		for (i = 0; i < buf.length; ++i)
> +			buffer->data_offsets[i] = buf.m.planes[i].data_offset;
> +	}
> +
> +	return 1;
> +}
> +
> +static void
> +buffer_release(void *data, struct wl_buffer *buffer)
> +{
> +	struct buffer *mybuf = data;
> +
> +	mybuf->busy = 0;
> +
> +	if (!queue(mybuf->display, mybuf))
> +		running = false;
> +}
> +
> +static const struct wl_buffer_listener buffer_listener = {
> +	buffer_release
> +};
> +
> +static int
> +set_format(struct display *display, uint32_t format)
> +{
> +	struct v4l2_format fmt;
> +
> +	CLEAR(fmt);
> +
> +	fmt.type = display->format.type;
> +
> +	if (xioctl(display->v4l_fd, VIDIOC_G_FMT, &fmt) == -1) {
> +		perror("VIDIOC_G_FMT");
> +		return 0;
> +	}
> +
> +	/* No need to set the format if it already is the one we want */
> +	if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
> +	    fmt.fmt.pix.pixelformat == format)
> +		return 1;
> +	if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	    fmt.fmt.pix_mp.pixelformat == format)
> +		return fmt.fmt.pix_mp.num_planes;
> +
> +	if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		fmt.fmt.pix.pixelformat = format;
> +	else
> +		fmt.fmt.pix_mp.pixelformat = format;
> +
> +	if (xioctl(display->v4l_fd, VIDIOC_S_FMT, &fmt) == -1) {
> +		perror("VIDIOC_S_FMT");
> +		return 0;
> +	}
> +
> +	if ((display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
> +	     fmt.fmt.pix.pixelformat != format) ||
> +	    (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	     fmt.fmt.pix_mp.pixelformat != format)) {
> +		fprintf(stderr, "Failed to set format to %.4s\n",
> +		        dump_format(&format));
> +		return 0;
> +	}
> +
> +	if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +		return fmt.fmt.pix_mp.num_planes;
> +
> +	return 1;
> +}
> +
> +static int
> +v4l_connect(struct display *display, const char *dev_name)
> +{
> +	struct v4l2_capability cap;
> +	struct v4l2_requestbuffers req;
> +	int num_planes;
> +
> +	display->v4l_fd = open(dev_name, O_RDWR);
> +	if (display->v4l_fd < 0) {
> +		perror(dev_name);
> +		return 0;
> +	}
> +
> +	if (xioctl(display->v4l_fd, VIDIOC_QUERYCAP, &cap) == -1) {
> +		if (errno == EINVAL) {
> +			fprintf(stderr, "%s is no V4L2 device\n", dev_name);
> +		} else {
> +			perror("VIDIOC_QUERYCAP");
> +		}
> +		return 0;
> +	}
> +
> +	if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
> +		display->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
> +		display->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +	else {
> +		fprintf(stderr, "%s is no video capture device\n", dev_name);
> +		return 0;
> +	}
> +
> +	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
> +		fprintf(stderr, "%s does not support dmabuf i/o\n", dev_name);
> +		return 0;
> +	}
> +
> +	/* Select video input, video standard and tune here */
> +
> +	num_planes = set_format(display, display->format.format);
> +	if (num_planes < 1)
> +		return 0;
> +
> +	CLEAR(req);
> +
> +	req.type = display->format.type;
> +	req.memory = V4L2_MEMORY_MMAP;
> +	req.count = NUM_BUFFERS * num_planes;
> +
> +	if (xioctl(display->v4l_fd, VIDIOC_REQBUFS, &req) == -1) {
> +		if (errno == EINVAL) {
> +			fprintf(stderr, "%s does not support dmabuf\n",
> +			        dev_name);
> +		} else {
> +			perror("VIDIOC_REQBUFS");
> +		}
> +		return 0;
> +	}
> +
> +	if (req.count < NUM_BUFFERS * num_planes) {
> +		fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name);
> +		return 0;
> +	}
> +
> +	printf("Created %d buffers\n", req.count);
> +
> +	return 1;
> +}
> +
> +static void
> +v4l_shutdown(struct display *display)
> +{
> +	close(display->v4l_fd);
> +}
> +
> +static void
> +create_succeeded(void *data,
> +		 struct zwp_linux_buffer_params_v1 *params,
> +		 struct wl_buffer *new_buffer)
> +{
> +	struct buffer *buffer = data;
> +	unsigned i;
> +
> +	buffer->buffer = new_buffer;
> +	wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
> +
> +	zwp_linux_buffer_params_v1_destroy(params);
> +
> +	for (i = 0; i < buffer->display->format.num_planes; ++i)
> +		close(buffer->dmabuf_fds[i]);
> +}
> +
> +static void
> +create_failed(void *data, struct zwp_linux_buffer_params_v1 *params)
> +{
> +	struct buffer *buffer = data;
> +	unsigned i;
> +
> +	buffer->buffer = NULL;
> +
> +	zwp_linux_buffer_params_v1_destroy(params);
> +
> +	for (i = 0; i < buffer->display->format.num_planes; ++i)
> +		close(buffer->dmabuf_fds[i]);
> +
> +	running = false;
> +
> +	fprintf(stderr, "Error: zwp_linux_buffer_params.create failed.\n");
> +}
> +
> +static const struct zwp_linux_buffer_params_v1_listener params_listener = {
> +	create_succeeded,
> +	create_failed
> +};
> +
> +static void
> +create_dmabuf_buffer(struct display *display, struct buffer *buffer)
> +{
> +	struct zwp_linux_buffer_params_v1 *params;
> +	uint64_t modifier;
> +	uint32_t flags;
> +	unsigned i;
> +
> +	modifier = 0;
> +	flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
> +
> +	params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
> +	for (i = 0; i < display->format.num_planes; ++i)
> +		zwp_linux_buffer_params_v1_add(params,
> +		                               buffer->dmabuf_fds[i],
> +		                               i, /* plane_idx */
> +		                               buffer->data_offsets[i], /* offset */
> +		                               display->format.strides[i],
> +		                               modifier >> 32,
> +		                               modifier & 0xffffffff);
> +	zwp_linux_buffer_params_v1_add_listener(params, &params_listener,
> +	                                        buffer);
> +	zwp_linux_buffer_params_v1_create(params,
> +	                                  display->format.width,
> +	                                  display->format.height,
> +	                                  display->drm_format,
> +	                                  flags);
> +}
> +
> +static int
> +buffer_export(struct display *display, int index, int dmafd[])
> +{
> +	struct v4l2_exportbuffer expbuf;
> +	unsigned i;
> +
> +	CLEAR(expbuf);
> +
> +	for (i = 0; i < display->format.num_planes; ++i) {
> +		expbuf.type = display->format.type;
> +		expbuf.index = index;
> +		expbuf.plane = i;
> +		if (xioctl(display->v4l_fd, VIDIOC_EXPBUF, &expbuf) == -1) {
> +			perror("VIDIOC_EXPBUF");
> +			while (i)
> +				close(dmafd[--i]);
> +			return 0;
> +		}
> +		dmafd[i] = expbuf.fd;
> +	}
> +
> +	return 1;
> +}
> +
> +static int
> +queue_initial_buffers(struct display *display,
> +                      struct buffer buffers[NUM_BUFFERS])
> +{
> +	struct buffer *buffer;
> +	int index;
> +
> +	for (index = 0; index < NUM_BUFFERS; ++index) {
> +		buffer = &buffers[index];
> +		buffer->display = display;
> +		buffer->index = index;
> +
> +		if (!queue(display, buffer)) {
> +			fprintf(stderr, "Failed to queue buffer\n");
> +			return 0;
> +		}
> +
> +		assert(!buffer->buffer);
> +		if (!buffer_export(display, index, buffer->dmabuf_fds))
> +			return 0;
> +
> +		create_dmabuf_buffer(display, buffer);
> +	}
> +
> +	return 1;
> +}
> +
> +static int
> +dequeue(struct display *display)
> +{
> +	struct v4l2_buffer buf;
> +	struct v4l2_plane planes[VIDEO_MAX_PLANES];
> +
> +	CLEAR(buf);
> +	buf.type = display->format.type;
> +	buf.memory = V4L2_MEMORY_MMAP;
> +	buf.length = VIDEO_MAX_PLANES;
> +	buf.m.planes = planes;
> +
> +	/* This ioctl is blocking until a buffer is ready to be displayed */
> +	if (xioctl(display->v4l_fd, VIDIOC_DQBUF, &buf) == -1) {
> +		perror("VIDIOC_DQBUF");
> +		return -1;
> +	}
> +
> +	assert(buf.flags & V4L2_BUF_FLAG_DONE);
> +
> +	return buf.index;
> +}
> +
> +static int
> +fill_buffer_format(struct display *display)
> +{
> +	struct v4l2_format fmt;
> +	struct v4l2_pix_format *pix;
> +	struct v4l2_pix_format_mplane *pix_mp;
> +	int i;
> +
> +	CLEAR(fmt);
> +	fmt.type = display->format.type;
> +
> +	/* Preserve original settings as set by v4l2-ctl for example */
> +	if (xioctl(display->v4l_fd, VIDIOC_G_FMT, &fmt) == -1) {
> +		perror("VIDIOC_G_FMT");
> +		return 0;
> +	}
> +
> +	if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +		pix = &fmt.fmt.pix;
> +
> +		printf("%d×%d, %.4s\n", pix->width, pix->height,
> +		       dump_format(&pix->pixelformat));
> +
> +		display->format.num_planes = 1;
> +		display->format.width = pix->width;
> +		display->format.height = pix->height;
> +		display->format.strides[0] = pix->bytesperline;
> +	} else {
> +		pix_mp = &fmt.fmt.pix_mp;
> +
> +		display->format.num_planes = pix_mp->num_planes;
> +		display->format.width = pix_mp->width;
> +		display->format.height = pix_mp->height;
> +
> +		for (i = 0; i < pix_mp->num_planes; ++i)
> +			display->format.strides[i] = pix_mp->plane_fmt[i].bytesperline;
> +
> +		printf("%d×%d, %.4s, %d planes\n",
> +		       pix_mp->width, pix_mp->height,
> +		       dump_format(&pix_mp->pixelformat), pix_mp->num_planes);
> +	}
> +
> +	return 1;
> +}
> +
> +static int
> +v4l_init(struct display *display, struct buffer buffers[NUM_BUFFERS]) {
> +	if (!fill_buffer_format(display)) {
> +		fprintf(stderr, "Failed to fill buffer format\n");
> +		return 0;
> +	}
> +
> +	if (!queue_initial_buffers(display, buffers)) {
> +		fprintf(stderr, "Failed to queue initial buffers\n");
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +static int
> +start_capture(struct display *display)
> +{
> +	int type = display->format.type;
> +
> +	if (xioctl(display->v4l_fd, VIDIOC_STREAMON, &type) == -1) {
> +		perror("VIDIOC_STREAMON");
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +static void
> +handle_configure(void *data, struct xdg_surface *surface,
> +		 int32_t width, int32_t height,
> +		 struct wl_array *states, uint32_t serial)
> +{
> +}
> +
> +static void
> +handle_delete(void *data, struct xdg_surface *xdg_surface)
> +{
> +	running = false;
> +}
> +
> +static const struct xdg_surface_listener xdg_surface_listener = {
> +	handle_configure,
> +	handle_delete,
> +};
> +
> +static struct window *
> +create_window(struct display *display)
> +{
> +	struct window *window;
> +
> +	window = calloc(1, sizeof *window);
> +	if (!window)
> +		return NULL;
> +
> +	window->callback = NULL;
> +	window->display = display;
> +	window->surface = wl_compositor_create_surface(display->compositor);
> +
> +	if (display->shell) {
> +		window->xdg_surface =
> +			xdg_shell_get_xdg_surface(display->shell,
> +			                          window->surface);
> +
> +		assert(window->xdg_surface);
> +
> +		xdg_surface_add_listener(window->xdg_surface,
> +		                         &xdg_surface_listener, window);
> +
> +		xdg_surface_set_title(window->xdg_surface, "simple-dmabuf-v4l");
> +	} else if (display->fshell) {
> +		zwp_fullscreen_shell_v1_present_surface(display->fshell,
> +		                                        window->surface,
> +		                                        ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
> +		                                        NULL);
> +	} else {
> +		assert(0);
> +	}
> +
> +	return window;
> +}
> +
> +static void
> +destroy_window(struct window *window)
> +{
> +	int i;
> +	unsigned j;
> +
> +	if (window->callback)
> +		wl_callback_destroy(window->callback);
> +
> +	if (window->xdg_surface)
> +		xdg_surface_destroy(window->xdg_surface);
> +	wl_surface_destroy(window->surface);
> +
> +	for (i = 0; i < NUM_BUFFERS; i++) {
> +		if (!window->buffers[i].buffer)
> +			continue;
> +
> +		wl_buffer_destroy(window->buffers[i].buffer);
> +		for (j = 0; j < window->display->format.num_planes; ++j)
> +			close(window->buffers[i].dmabuf_fds[j]);
> +	}
> +
> +	v4l_shutdown(window->display);
> +
> +	free(window);
> +}
> +
> +static const struct wl_callback_listener frame_listener;
> +
> +static void
> +redraw(void *data, struct wl_callback *callback, uint32_t time)
> +{
> +	struct window *window = data;
> +	struct buffer *buffer;
> +	int index, num_busy = 0;
> +
> +	/* Check for a deadlock situation where we would block forever trying
> +	 * to dequeue a buffer while all of them are locked by the compositor.
> +	 */
> +	for (index = 0; index < NUM_BUFFERS; ++index)
> +		if (window->buffers[index].busy)
> +			++num_busy;
> +
> +	/* A robust application would just postpone redraw until it has queued
> +	 * a buffer.
> +	 */
> +	assert(num_busy < NUM_BUFFERS);
> +
> +	index = dequeue(window->display);
> +	if (index < 0) {
> +		/* We couldn’t get any buffer out of the camera, exiting. */
> +		running = false;
> +		return;
> +	}
> +
> +	buffer = &window->buffers[index];
> +	assert(!buffer->busy);
> +
> +	wl_surface_attach(window->surface, buffer->buffer, 0, 0);
> +	wl_surface_damage(window->surface, 0, 0,
> +	                  window->display->format.width,
> +	                  window->display->format.height);
> +
> +	if (callback)
> +		wl_callback_destroy(callback);
> +
> +	window->callback = wl_surface_frame(window->surface);
> +	wl_callback_add_listener(window->callback, &frame_listener, window);
> +	wl_surface_commit(window->surface);
> +	buffer->busy = 1;
> +}
> +
> +static const struct wl_callback_listener frame_listener = {
> +	redraw
> +};
> +
> +static void
> +dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
> +              uint32_t format)
> +{
> +	struct display *d = data;
> +
> +	if (format == DRM_FORMAT_YUYV)
> +		d->yuyv_format_found = 1;

I guess the machinery isn't hooked up for this anyway, so it doesn't
really matter, but should this be searching for the format specified on
the command line?

The rest looks fine to me, but I haven't touched v4l2 in a long time, so
I don't feel comfortable giving this a Reviewed-by...

So, if the FIXMEs are fixed...
Acked-by: Derek Foreman <derekf at osg.samsung.com>

Thanks,
Derek
> +}
> +
> +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
> +	dmabuf_format
> +};
> +
> +static void
> +keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
> +                       uint32_t format, int fd, uint32_t size)
> +{
> +	/* Just so we don’t leak the keymap fd */
> +	close(fd);
> +}
> +
> +static void
> +keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
> +                      uint32_t serial, struct wl_surface *surface,
> +                      struct wl_array *keys)
> +{
> +}
> +
> +static void
> +keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
> +                      uint32_t serial, struct wl_surface *surface)
> +{
> +}
> +
> +static void
> +keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
> +                    uint32_t serial, uint32_t time, uint32_t key,
> +                    uint32_t state)
> +{
> +	struct display *d = data;
> +
> +	if (!d->shell)
> +		return;
> +
> +	if (key == KEY_ESC && state)
> +		running = false;
> +}
> +
> +static void
> +keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
> +                          uint32_t serial, uint32_t mods_depressed,
> +                          uint32_t mods_latched, uint32_t mods_locked,
> +                          uint32_t group)
> +{
> +}
> +
> +static const struct wl_keyboard_listener keyboard_listener = {
> +	keyboard_handle_keymap,
> +	keyboard_handle_enter,
> +	keyboard_handle_leave,
> +	keyboard_handle_key,
> +	keyboard_handle_modifiers,
> +};
> +
> +static void
> +seat_handle_capabilities(void *data, struct wl_seat *seat,
> +                         enum wl_seat_capability caps)
> +{
> +	struct display *d = data;
> +
> +	if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
> +		d->keyboard = wl_seat_get_keyboard(seat);
> +		wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
> +	} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
> +		wl_keyboard_destroy(d->keyboard);
> +		d->keyboard = NULL;
> +	}
> +}
> +
> +static const struct wl_seat_listener seat_listener = {
> +	seat_handle_capabilities,
> +};
> +
> +static void
> +xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
> +{
> +	xdg_shell_pong(shell, serial);
> +}
> +
> +static const struct xdg_shell_listener xdg_shell_listener = {
> +	xdg_shell_ping,
> +};
> +
> +#define XDG_VERSION 5 /* The version of xdg-shell that we implement */
> +#ifdef static_assert
> +static_assert(XDG_VERSION == XDG_SHELL_VERSION_CURRENT,
> +              "Interface version doesn't match implementation version");
> +#endif
> +
> +static void
> +registry_handle_global(void *data, struct wl_registry *registry,
> +                       uint32_t id, const char *interface, uint32_t version)
> +{
> +	struct display *d = data;
> +
> +	if (strcmp(interface, "wl_compositor") == 0) {
> +		d->compositor =
> +			wl_registry_bind(registry,
> +			                 id, &wl_compositor_interface, 1);
> +	} else if (strcmp(interface, "wl_seat") == 0) {
> +		d->seat = wl_registry_bind(registry,
> +		                           id, &wl_seat_interface, 1);
> +		wl_seat_add_listener(d->seat, &seat_listener, d);
> +	} else if (strcmp(interface, "xdg_shell") == 0) {
> +		d->shell = wl_registry_bind(registry,
> +		                            id, &xdg_shell_interface, 1);
> +		xdg_shell_use_unstable_version(d->shell, XDG_VERSION);
> +		xdg_shell_add_listener(d->shell, &xdg_shell_listener, d);
> +	} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
> +		d->fshell = wl_registry_bind(registry,
> +		                             id, &zwp_fullscreen_shell_v1_interface,
> +		                             1);
> +	} else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
> +		d->dmabuf = wl_registry_bind(registry,
> +		                             id, &zwp_linux_dmabuf_v1_interface,
> +		                             1);
> +		zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener,
> +		                                 d);
> +	}
> +}
> +
> +static void
> +registry_handle_global_remove(void *data, struct wl_registry *registry,
> +                              uint32_t name)
> +{
> +}
> +
> +static const struct wl_registry_listener registry_listener = {
> +	registry_handle_global,
> +	registry_handle_global_remove
> +};
> +
> +static struct display *
> +create_display(void)
> +{
> +	struct display *display;
> +
> +	display = malloc(sizeof *display);
> +	if (display == NULL) {
> +		fprintf(stderr, "out of memory\n");
> +		exit(1);
> +	}
> +	display->display = wl_display_connect(NULL);
> +	assert(display->display);
> +
> +	/* XXX: fake, because the compositor does not yet advertise anything */
> +	display->yuyv_format_found = 1;
> +
> +	display->registry = wl_display_get_registry(display->display);
> +	wl_registry_add_listener(display->registry,
> +	                         &registry_listener, display);
> +	wl_display_roundtrip(display->display);
> +	if (display->dmabuf == NULL) {
> +		fprintf(stderr, "No zwp_linux_dmabuf global\n");
> +		exit(1);
> +	}
> +
> +	wl_display_roundtrip(display->display);
> +
> +	if (!display->yuyv_format_found) {
> +		fprintf(stderr, "DRM_FORMAT_YUYV not available\n");
> +		exit(1);
> +	}
> +
> +	return display;
> +}
> +
> +static void
> +destroy_display(struct display *display)
> +{
> +	if (display->dmabuf)
> +		zwp_linux_dmabuf_v1_destroy(display->dmabuf);
> +
> +	if (display->shell)
> +		xdg_shell_destroy(display->shell);
> +
> +	if (display->fshell)
> +		zwp_fullscreen_shell_v1_release(display->fshell);
> +
> +	if (display->compositor)
> +		wl_compositor_destroy(display->compositor);
> +
> +	wl_registry_destroy(display->registry);
> +	wl_display_flush(display->display);
> +	wl_display_disconnect(display->display);
> +	free(display);
> +}
> +
> +static void
> +usage(const char *argv0)
> +{
> +	printf("Usage: %s [V4L2 device] [V4L2 format] [DRM format]\n"
> +	       "\n"
> +	       "The default V4L2 device is /dev/video0\n"
> +	       "\n"
> +	       "Both formats are FOURCC values (see http://fourcc.org/)\n"
> +	       "V4L2 formats are defined in <linux/videodev2.h>\n"
> +	       "DRM formats are defined in <libdrm/drm_fourcc.h>\n"
> +	       "The default for both formats is YUYV.\n"
> +	       "If the V4L2 and DRM formats differ, the data is simply "
> +	       "reinterpreted rather than converted.\n", argv0);
> +	exit(0);
> +}
> +
> +static void
> +signal_int(int signum)
> +{
> +	running = false;
> +}
> +
> +int
> +main(int argc, char **argv)
> +{
> +	struct sigaction sigint;
> +	struct display *display;
> +	struct window *window;
> +	const char *v4l_device;
> +	uint32_t v4l_format, drm_format;
> +	int ret = 0;
> +
> +	if (argc < 2) {
> +		v4l_device = "/dev/video0";
> +	} else if (!strcmp(argv[1], "--help")) {
> +		usage(argv[0]);
> +	} else {
> +		v4l_device = argv[1];
> +	}
> +
> +	if (argc < 3)
> +		v4l_format = parse_format("YUYV");
> +	else
> +		v4l_format = parse_format(argv[2]);
> +
> +	if (argc < 4)
> +		drm_format = v4l_format;
> +	else
> +		drm_format = parse_format(argv[3]);
> +
> +	display = create_display();
> +	display->format.format = v4l_format;
> +	display->drm_format = drm_format;
> +
> +	window = create_window(display);
> +	if (!window)
> +		return 1;
> +
> +	if (!v4l_connect(display, v4l_device))
> +		return 1;
> +
> +	if (!v4l_init(display, window->buffers))
> +		return 1;
> +
> +	sigint.sa_handler = signal_int;
> +	sigemptyset(&sigint.sa_mask);
> +	sigint.sa_flags = SA_RESETHAND;
> +	sigaction(SIGINT, &sigint, NULL);
> +
> +	/* Here we retrieve the linux-dmabuf objects, or error */
> +	wl_display_roundtrip(display->display);
> +
> +	/* In case of error, running will be 0 */
> +	if (!running)
> +		return 1;
> +
> +	/* We got all of our buffers, we can start the capture! */
> +	if (!start_capture(display))
> +		return 1;
> +
> +	redraw(window, NULL, 0);
> +
> +	while (running && ret != -1)
> +		ret = wl_display_dispatch(display->display);
> +
> +	fprintf(stderr, "simple-dmabuf-v4l exiting\n");
> +	destroy_window(window);
> +	destroy_display(display);
> +
> +	return 0;
> +}
> diff --git a/configure.ac b/configure.ac
> index 826b30c..07345a7 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -356,6 +356,20 @@ if ! test "x$enable_simple_dmabuf_intel_client" = "xno"; then
>  fi
>  AM_CONDITIONAL(BUILD_SIMPLE_DMABUF_INTEL_CLIENT, test "x$enable_simple_dmabuf_intel_client" = "xyes")
>  
> +AC_ARG_ENABLE(simple-dmabuf-v4l-client,
> +              AS_HELP_STRING([--disable-simple-dmabuf-v4l-client],
> +                             [do not build the simple dmabuf v4l client]),,
> +              enable_simple_dmabuf_v4l_client="auto")
> +if ! test "x$enable_simple_dmabuf_v4l_client" = "xno"; then
> +  PKG_CHECK_MODULES(SIMPLE_DMABUF_V4L_CLIENT, [wayland-client libdrm],
> +		    have_simple_dmabuf_v4l_client=yes, have_simple_dmabuf_v4l_client=no)
> +  if test "x$have_simple_dmabuf_v4l_client" = "xno" -a "x$enable_simple_dmabuf_v4l_client" = "xyes"; then
> +    AC_MSG_ERROR([V4L dmabuf client explicitly enabled, but libdrm couldn't be found])
> +  fi
> +  enable_simple_dmabuf_v4l_client="$have_simple_dmabuf_v4l_client"
> +fi
> +AM_CONDITIONAL(BUILD_SIMPLE_DMABUF_V4L_CLIENT, test "x$enable_simple_dmabuf_v4l_client" = "xyes")
> +
>  AC_ARG_ENABLE(clients, [  --enable-clients],, enable_clients=yes)
>  AM_CONDITIONAL(BUILD_CLIENTS, test x$enable_clients = xyes)
>  if test x$enable_clients = xyes; then
> 



More information about the wayland-devel mailing list