summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@bitplanet.net>2012-07-04 00:28:42 -0400
committerKristian Høgsberg <krh@bitplanet.net>2012-07-04 00:28:42 -0400
commit1a722068d20f4d58ee457d4ba93d540a7d3cde45 (patch)
treeb3c62b5192835ca907ecefb29f3e84ddd0bf19ff
initial simple-yuv commit
-rw-r--r--Makefile11
-rw-r--r--simple-yuv.c411
-rw-r--r--wayland-drm-client-protocol.h213
-rw-r--r--wayland-drm-protocol.c72
-rw-r--r--webcam.c717
5 files changed, 1424 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9cd8332
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+CFLAGS = $(shell pkg-config --cflags wayland-client libdrm_intel) -Wall -g
+LDFLAGS = $(shell pkg-config --libs wayland-client libdrm_intel)
+
+all : simple-yuv webcam
+
+simple-yuv : simple-yuv.o wayland-drm-protocol.o
+
+webcam : webcam.o
+
+clean :
+ rm *.o simple-yuv webcam
diff --git a/simple-yuv.c b/simple-yuv.c
new file mode 100644
index 0000000..4a06ce0
--- /dev/null
+++ b/simple-yuv.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2010 Intel Corporation
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <signal.h>
+
+#include <libdrm/intel_bufmgr.h>
+#include <xf86drm.h>
+#include <wayland-client.h>
+
+#include "wayland-drm-client-protocol.h"
+
+#define CAP 1
+
+struct display {
+ struct wl_display *display;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_drm *drm;
+ drm_intel_bufmgr *bufmgr;
+ int fd;
+ int authenticated;
+ uint32_t formats[10];
+ int format_count;
+ uint32_t mask;
+};
+
+struct buffer {
+ drm_intel_bo *bo;
+ struct wl_buffer *buffer;
+ uint8_t *y;
+ uint8_t *u;
+ uint8_t *v;
+};
+
+
+struct window {
+ struct display *display;
+ int width, height;
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+ struct wl_callback *callback;
+
+ struct buffer *front, *back;
+};
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ handle_ping,
+ handle_configure,
+ handle_popup_done
+};
+
+static void
+read_frame(struct window *window)
+{
+ struct buffer *tmp;
+ int size = window->width * window->height;
+
+ fread(window->back->y, 1, size, stdin);
+ fread(window->back->u, 1, size / 4, stdin);
+ fread(window->back->v, 1, size / 4, stdin);
+
+ wl_surface_attach(window->surface, window->back->buffer, 0, 0);
+ wl_surface_damage(window->surface,
+ 0, 0, window->width, window->height);
+
+ tmp = window->back;
+ window->back = window->front;
+ window->front = tmp;
+}
+
+static void
+paint_pixels(struct window *window, int width, int height, uint32_t time)
+{
+ const int halfh = height / 2;
+ const int halfw = width / 2;
+ int ir, or;
+ uint8_t *y = window->back->y;
+ uint8_t *u = window->back->u;
+ uint8_t *v = window->back->v;
+ int i, x;
+
+ /* squared radii thresholds */
+ or = (halfw < halfh ? halfw : halfh) - 8;
+ ir = or - 32;
+ or *= or;
+ ir *= ir;
+
+ for (i = 0; i < height; i++) {
+ int x;
+ int y2 = (i - halfh) * (i - halfh);
+
+ for (x = 0; x < width; x++) {
+ uint32_t v;
+
+ /* squared distance from center */
+ int r2 = (x - halfw) * (x - halfw) + y2;
+
+ if (r2 < ir)
+ v = (r2 / 32 + time / 64) * 0x0080401;
+ else if (r2 < or)
+ v = (i + time / 32) * 0x0080401;
+ else
+ v = (x + time / 16) * 0x0080401;
+ v &= 0x00ffffff;
+
+ *y++ = v;
+ }
+ }
+
+ for (i = 0; i < halfh; i++) {
+ for (x = 0; x < halfw; x++) {
+ *u++ = i > x + 30 ? 200 : 10;
+ *v++ = i > x - 30 ? 150 : 50;
+ }
+ }
+}
+
+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 *tmp;
+
+ paint_pixels(window, window->width, window->height, time);
+ wl_surface_attach(window->surface, window->back->buffer, 0, 0);
+ wl_surface_damage(window->surface,
+ 0, 0, window->width, window->height);
+
+ tmp = window->back;
+ window->back = window->front;
+ window->front = tmp;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ window->callback = wl_surface_frame(window->surface);
+ wl_callback_add_listener(window->callback, &frame_listener, window);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ redraw
+};
+
+static inline int
+align(int value, int size)
+{
+ return (value + size - 1) & ~(size - 1);
+}
+
+struct buffer *
+create_buffer(struct window *window)
+{
+ struct display *display = window->display;
+ struct buffer *buffer;
+ int size, offset1, offset2;
+ uint32_t name;
+
+ buffer = malloc(sizeof *buffer);
+
+ offset1 = align(window->width * window->height, 4096);
+ offset2 = align(offset1 + window->width * window->height, 4096);
+
+ size = align(offset2 + window->width * window->height, 4096);
+ buffer->bo =
+ drm_intel_bo_alloc(display->bufmgr, "simple-yuv", size, 0);
+ drm_intel_gem_bo_map_gtt(buffer->bo);
+
+ drm_intel_bo_flink(buffer->bo, &name);
+
+ buffer->buffer =
+ wl_drm_create_planar_buffer(display->drm, name,
+ window->width, window->height,
+ WL_BUFFER_FORMAT_YUV420,
+ 0, window->width,
+ offset1, window->width / 2,
+ offset2, window->width / 2);
+
+ buffer->y = buffer->bo->virtual;
+ buffer->u = buffer->y + offset1;
+ buffer->v = buffer->y + offset2;
+
+ return buffer;
+}
+
+static struct window *
+create_window(struct display *display, int width, int height)
+{
+ struct window *window;
+ int i;
+
+ for (i = 0; i < display->format_count; i++) {
+ if (display->formats[i] == WL_BUFFER_FORMAT_YUV420)
+ break;
+ }
+
+ if (i == display->format_count) {
+ fprintf(stderr,
+ "format WL_BUFFER_FORMAT_ARGB8888 not supported\n");
+ return NULL;
+ }
+
+ window = malloc(sizeof *window);
+
+ window->callback = NULL;
+ window->display = display;
+ window->width = width;
+ window->height = height;
+ window->surface = wl_compositor_create_surface(display->compositor);
+ window->shell_surface = wl_shell_get_shell_surface(display->shell,
+ window->surface);
+
+ if (window->shell_surface)
+ wl_shell_surface_add_listener(window->shell_surface,
+ &shell_surface_listener, window);
+
+ wl_shell_surface_set_toplevel(window->shell_surface);
+
+ window->front = create_buffer(window);
+ window->back = create_buffer(window);
+
+ return window;
+}
+
+static void
+destroy_window(struct window *window)
+{
+ if (window->callback)
+ wl_callback_destroy(window->callback);
+
+ wl_shell_surface_destroy(window->shell_surface);
+ wl_surface_destroy(window->surface);
+ free(window);
+}
+
+static void
+drm_handle_device(void *data, struct wl_drm *drm, const char *device)
+{
+ struct display *d = data;
+ drm_magic_t magic;
+
+ d->fd = open(device, O_RDWR | O_CLOEXEC);
+ if (d->fd == -1) {
+ fprintf(stderr, "could not open %s: %m\n", device);
+ exit(-1);
+ }
+
+ drmGetMagic(d->fd, &magic);
+ wl_drm_authenticate(d->drm, magic);
+}
+
+static void
+drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
+{
+ struct display *d = data;
+
+ d->formats[d->format_count++] = format;
+}
+
+static void
+drm_handle_authenticated(void *data, struct wl_drm *drm)
+{
+ struct display *d = data;
+
+ d->authenticated = 1;
+
+ d->bufmgr = drm_intel_bufmgr_gem_init(d->fd, 4096);
+}
+
+static const struct wl_drm_listener drm_listener = {
+ drm_handle_device,
+ drm_handle_format,
+ drm_handle_authenticated
+};
+
+static void
+display_handle_global(struct wl_display *display, uint32_t id,
+ const char *interface, uint32_t version, void *data)
+{
+ struct display *d = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor =
+ wl_display_bind(display, id, &wl_compositor_interface);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ d->shell = wl_display_bind(display, id, &wl_shell_interface);
+ } else if (strcmp(interface, "wl_drm") == 0) {
+ d->drm = wl_display_bind(display, id, &wl_drm_interface);
+ wl_drm_add_listener(d->drm, &drm_listener, d);
+ }
+}
+
+static struct display *
+create_display(void)
+{
+ struct display *display;
+
+ display = malloc(sizeof *display);
+ memset(display, 0, sizeof *display);
+ display->display = wl_display_connect(NULL);
+ assert(display->display);
+
+ wl_display_add_global_listener(display->display,
+ display_handle_global, display);
+
+ return display;
+}
+
+static void
+destroy_display(struct display *display)
+{
+ if (display->drm)
+ wl_drm_destroy(display->drm);
+
+ if (display->shell)
+ wl_shell_destroy(display->shell);
+
+ if (display->compositor)
+ wl_compositor_destroy(display->compositor);
+
+ wl_display_flush(display->display);
+ wl_display_disconnect(display->display);
+ free(display);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct display *display;
+ struct window *window;
+ int test_pattern = 1;
+
+ if (argc == 2 && strcmp(argv[1], "-") == 0)
+ test_pattern = 0;
+
+ display = create_display();
+ while (!display->authenticated)
+ wl_display_roundtrip(display->display);
+
+ if (test_pattern) {
+ window = create_window(display, 512, 512);
+ redraw(window, NULL, 0);
+ } else {
+ window = create_window(display, 640, 480);
+ }
+
+ while (1) {
+ if (test_pattern) {
+ wl_display_flush(display->display);
+ wl_display_iterate(display->display,
+ WL_DISPLAY_READABLE);
+ } else {
+ read_frame(window);
+ wl_display_flush(display->display);
+ wl_display_roundtrip(display->display);
+ }
+
+ }
+
+ destroy_window(window);
+ destroy_display(display);
+
+ return 0;
+}
diff --git a/wayland-drm-client-protocol.h b/wayland-drm-client-protocol.h
new file mode 100644
index 0000000..7ddb614
--- /dev/null
+++ b/wayland-drm-client-protocol.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2010-2011 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that\n 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 DRM_CLIENT_PROTOCOL_H
+#define DRM_CLIENT_PROTOCOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+#include "wayland-util.h"
+
+struct wl_client;
+struct wl_resource;
+
+struct wl_drm;
+
+extern const struct wl_interface wl_drm_interface;
+
+#ifndef WL_DRM_ERROR_ENUM
+#define WL_DRM_ERROR_ENUM
+enum wl_drm_error {
+ WL_DRM_ERROR_AUTHENTICATE_FAIL = 0,
+ WL_DRM_ERROR_INVALID_FORMAT = 1,
+ WL_DRM_ERROR_INVALID_NAME = 2,
+};
+#endif /* WL_DRM_ERROR_ENUM */
+
+#ifndef WL_DRM_FORMAT_ENUM
+#define WL_DRM_FORMAT_ENUM
+enum wl_drm_format {
+ WL_DRM_FORMAT_C8 = 0x20203843,
+ WL_DRM_FORMAT_RGB332 = 0x38424752,
+ WL_DRM_FORMAT_BGR233 = 0x38524742,
+ WL_DRM_FORMAT_XRGB4444 = 0x32315258,
+ WL_DRM_FORMAT_XBGR4444 = 0x32314258,
+ WL_DRM_FORMAT_RGBX4444 = 0x32315852,
+ WL_DRM_FORMAT_BGRX4444 = 0x32315842,
+ WL_DRM_FORMAT_ARGB4444 = 0x32315241,
+ WL_DRM_FORMAT_ABGR4444 = 0x32314241,
+ WL_DRM_FORMAT_RGBA4444 = 0x32314152,
+ WL_DRM_FORMAT_BGRA4444 = 0x32314142,
+ WL_DRM_FORMAT_XRGB1555 = 0x35315258,
+ WL_DRM_FORMAT_XBGR1555 = 0x35314258,
+ WL_DRM_FORMAT_RGBX5551 = 0x35315852,
+ WL_DRM_FORMAT_BGRX5551 = 0x35315842,
+ WL_DRM_FORMAT_ARGB1555 = 0x35315241,
+ WL_DRM_FORMAT_ABGR1555 = 0x35314241,
+ WL_DRM_FORMAT_RGBA5551 = 0x35314152,
+ WL_DRM_FORMAT_BGRA5551 = 0x35314142,
+ WL_DRM_FORMAT_RGB565 = 0x36314752,
+ WL_DRM_FORMAT_BGR565 = 0x36314742,
+ WL_DRM_FORMAT_RGB888 = 0x34324752,
+ WL_DRM_FORMAT_BGR888 = 0x34324742,
+ WL_DRM_FORMAT_XRGB8888 = 0x34325258,
+ WL_DRM_FORMAT_XBGR8888 = 0x34324258,
+ WL_DRM_FORMAT_RGBX8888 = 0x34325852,
+ WL_DRM_FORMAT_BGRX8888 = 0x34325842,
+ WL_DRM_FORMAT_ARGB8888 = 0x34325241,
+ WL_DRM_FORMAT_ABGR8888 = 0x34324241,
+ WL_DRM_FORMAT_RGBA8888 = 0x34324152,
+ WL_DRM_FORMAT_BGRA8888 = 0x34324142,
+ WL_DRM_FORMAT_XRGB2101010 = 0x30335258,
+ WL_DRM_FORMAT_XBGR2101010 = 0x30334258,
+ WL_DRM_FORMAT_RGBX1010102 = 0x30335852,
+ WL_DRM_FORMAT_BGRX1010102 = 0x30335842,
+ WL_DRM_FORMAT_ARGB2101010 = 0x30335241,
+ WL_DRM_FORMAT_ABGR2101010 = 0x30334241,
+ WL_DRM_FORMAT_RGBA1010102 = 0x30334152,
+ WL_DRM_FORMAT_BGRA1010102 = 0x30334142,
+ WL_DRM_FORMAT_YUYV = 0x56595559,
+ WL_DRM_FORMAT_YVYU = 0x55595659,
+ WL_DRM_FORMAT_UYVY = 0x59565955,
+ WL_DRM_FORMAT_VYUY = 0x59555956,
+ WL_DRM_FORMAT_AYUV = 0x56555941,
+ WL_DRM_FORMAT_NV12 = 0x3231564e,
+ WL_DRM_FORMAT_NV21 = 0x3132564e,
+ WL_DRM_FORMAT_NV16 = 0x3631564e,
+ WL_DRM_FORMAT_NV61 = 0x3136564e,
+ WL_DRM_FORMAT_YUV410 = 0x39565559,
+ WL_DRM_FORMAT_YVU410 = 0x39555659,
+ WL_DRM_FORMAT_YUV411 = 0x31315559,
+ WL_DRM_FORMAT_YVU411 = 0x31315659,
+ WL_DRM_FORMAT_YUV420 = 0x32315559,
+ WL_DRM_FORMAT_YVU420 = 0x32315659,
+ WL_DRM_FORMAT_YUV422 = 0x36315559,
+ WL_DRM_FORMAT_YVU422 = 0x36315659,
+ WL_DRM_FORMAT_YUV444 = 0x34325559,
+ WL_DRM_FORMAT_YVU444 = 0x34325659,
+};
+#endif /* WL_DRM_FORMAT_ENUM */
+
+struct wl_drm_listener {
+ /**
+ * device - (none)
+ * @name: (none)
+ */
+ void (*device)(void *data,
+ struct wl_drm *wl_drm,
+ const char *name);
+ /**
+ * format - (none)
+ * @format: (none)
+ */
+ void (*format)(void *data,
+ struct wl_drm *wl_drm,
+ uint32_t format);
+ /**
+ * authenticated - (none)
+ */
+ void (*authenticated)(void *data,
+ struct wl_drm *wl_drm);
+};
+
+static inline int
+wl_drm_add_listener(struct wl_drm *wl_drm,
+ const struct wl_drm_listener *listener, void *data)
+{
+ return wl_proxy_add_listener((struct wl_proxy *) wl_drm,
+ (void (**)(void)) listener, data);
+}
+
+#define WL_DRM_AUTHENTICATE 0
+#define WL_DRM_CREATE_BUFFER 1
+#define WL_DRM_CREATE_PLANAR_BUFFER 2
+
+static inline void
+wl_drm_set_user_data(struct wl_drm *wl_drm, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) wl_drm, user_data);
+}
+
+static inline void *
+wl_drm_get_user_data(struct wl_drm *wl_drm)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) wl_drm);
+}
+
+static inline void
+wl_drm_destroy(struct wl_drm *wl_drm)
+{
+ wl_proxy_destroy((struct wl_proxy *) wl_drm);
+}
+
+static inline void
+wl_drm_authenticate(struct wl_drm *wl_drm, uint32_t id)
+{
+ wl_proxy_marshal((struct wl_proxy *) wl_drm,
+ WL_DRM_AUTHENTICATE, id);
+}
+
+static inline struct wl_buffer *
+wl_drm_create_buffer(struct wl_drm *wl_drm, uint32_t name, int32_t width, int32_t height, uint32_t stride, uint32_t format)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_create((struct wl_proxy *) wl_drm,
+ &wl_buffer_interface);
+ if (!id)
+ return NULL;
+
+ wl_proxy_marshal((struct wl_proxy *) wl_drm,
+ WL_DRM_CREATE_BUFFER, id, name, width, height, stride, format);
+
+ return (struct wl_buffer *) id;
+}
+
+static inline struct wl_buffer *
+wl_drm_create_planar_buffer(struct wl_drm *wl_drm, uint32_t name, int32_t width, int32_t height, uint32_t format, int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1, int32_t offset2, int32_t stride2)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_create((struct wl_proxy *) wl_drm,
+ &wl_buffer_interface);
+ if (!id)
+ return NULL;
+
+ wl_proxy_marshal((struct wl_proxy *) wl_drm,
+ WL_DRM_CREATE_PLANAR_BUFFER, id, name, width, height, format, offset0, stride0, offset1, stride1, offset2, stride2);
+
+ return (struct wl_buffer *) id;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/wayland-drm-protocol.c b/wayland-drm-protocol.c
new file mode 100644
index 0000000..98e98ef
--- /dev/null
+++ b/wayland-drm-protocol.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2010-2011 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that\n 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 <stdlib.h>
+#include <stdint.h>
+#include "wayland-util.h"
+
+extern const struct wl_interface wl_buffer_interface;
+extern const struct wl_interface wl_buffer_interface;
+
+static const struct wl_interface *types[] = {
+ NULL,
+ &wl_buffer_interface,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &wl_buffer_interface,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static const struct wl_message wl_drm_requests[] = {
+ { "authenticate", "u", types + 0 },
+ { "create_buffer", "nuiiuu", types + 1 },
+ { "create_planar_buffer", "nuiiuiiiiii", types + 7 },
+};
+
+static const struct wl_message wl_drm_events[] = {
+ { "device", "s", types + 0 },
+ { "format", "u", types + 0 },
+ { "authenticated", "", types + 0 },
+};
+
+WL_EXPORT const struct wl_interface wl_drm_interface = {
+ "wl_drm", 1,
+ ARRAY_LENGTH(wl_drm_requests), wl_drm_requests,
+ ARRAY_LENGTH(wl_drm_events), wl_drm_events,
+};
+
diff --git a/webcam.c b/webcam.c
new file mode 100644
index 0000000..a396eb8
--- /dev/null
+++ b/webcam.c
@@ -0,0 +1,717 @@
+/*
+ * V4L2 video capture example
+ *
+ * This program can be used and distributed without restrictions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <getopt.h> /* getopt_long() */
+
+#include <fcntl.h> /* low-level i/o */
+#include <unistd.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#include <asm/types.h> /* for videodev2.h */
+
+#include <linux/videodev2.h>
+
+#define CLEAR(x) memset (&(x), 0, sizeof (x))
+
+typedef enum {
+ IO_METHOD_READ,
+ IO_METHOD_MMAP,
+ IO_METHOD_USERPTR,
+} io_method;
+
+struct buffer {
+ void * start;
+ size_t length;
+};
+
+static char * dev_name = NULL;
+static io_method io = IO_METHOD_MMAP;
+static int fd = -1;
+struct buffer * buffers = NULL;
+static unsigned int n_buffers = 0;
+
+static void
+errno_exit (const char * s)
+{
+ fprintf (stderr, "%s error %d, %s\n",
+ s, errno, strerror (errno));
+
+ exit (EXIT_FAILURE);
+}
+
+static int
+xioctl (int fd,
+ int request,
+ void * arg)
+{
+ int r;
+
+ do r = ioctl (fd, request, arg);
+ while (-1 == r && EINTR == errno);
+
+ return r;
+}
+
+static inline int
+align(int value, int size)
+{
+ return (value + size - 1) & ~(size - 1);
+}
+
+static void
+process_image (const void * gah)
+{
+#define SIZE (640 * 480)
+ char buf[SIZE], *d;
+ const char *p;
+ int i, j;
+
+ access("/dev/null", 0);
+
+ p = gah;
+ for (i = 0; i < SIZE; i++)
+ buf[i] = p[i * 2];
+
+ fwrite(buf, 1, SIZE, stdout);
+
+ d = buf;
+ for (i = 0; i < 480; i += 2) {
+ p = (char *) gah + i * 640 * 2;
+ for (j = 0; j < 640 * 2; j += 4)
+ *d++ = p[j + 1];
+ }
+ fwrite(buf, 1, SIZE / 4, stdout);
+
+ d = buf;
+ for (i = 0; i < 480; i += 2) {
+ p = (char *) gah + i * 640 * 2;
+ for (j = 0; j < 640 * 2; j += 4)
+ *d++ = p[j + 3];
+ }
+ fwrite(buf, 1, SIZE / 4, stdout);
+ fflush (stdout);
+}
+
+static int
+read_frame (void)
+{
+ struct v4l2_buffer buf;
+ unsigned int i;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ if (-1 == read (fd, buffers[0].start, buffers[0].length)) {
+ switch (errno) {
+ case EAGAIN:
+ return 0;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+
+ /* fall through */
+
+ default:
+ errno_exit ("read");
+ }
+ }
+
+ process_image (buffers[0].start);
+
+ break;
+
+ case IO_METHOD_MMAP:
+ CLEAR (buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
+ switch (errno) {
+ case EAGAIN:
+ return 0;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+
+ /* fall through */
+
+ default:
+ errno_exit ("VIDIOC_DQBUF");
+ }
+ }
+
+ assert (buf.index < n_buffers);
+
+ process_image (buffers[buf.index].start);
+
+ if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
+ errno_exit ("VIDIOC_QBUF");
+
+ break;
+
+ case IO_METHOD_USERPTR:
+ CLEAR (buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
+ switch (errno) {
+ case EAGAIN:
+ return 0;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+
+ /* fall through */
+
+ default:
+ errno_exit ("VIDIOC_DQBUF");
+ }
+ }
+
+ for (i = 0; i < n_buffers; ++i)
+ if (buf.m.userptr == (unsigned long) buffers[i].start
+ && buf.length == buffers[i].length)
+ break;
+
+ assert (i < n_buffers);
+
+ process_image ((void *) buf.m.userptr);
+
+ if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
+ errno_exit ("VIDIOC_QBUF");
+
+ break;
+ }
+
+ return 1;
+}
+
+static void
+mainloop (void)
+{
+ while (1) {
+ for (;;) {
+ fd_set fds;
+ struct timeval tv;
+ int r;
+
+ FD_ZERO (&fds);
+ FD_SET (fd, &fds);
+
+ /* Timeout. */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+
+ r = select (fd + 1, &fds, NULL, NULL, &tv);
+
+ if (-1 == r) {
+ if (EINTR == errno)
+ continue;
+
+ errno_exit ("select");
+ }
+
+ if (0 == r) {
+ fprintf (stderr, "select timeout\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (read_frame ())
+ break;
+
+ /* EAGAIN - continue select loop. */
+ }
+ }
+}
+
+static void
+stop_capturing (void)
+{
+ enum v4l2_buf_type type;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ /* Nothing to do. */
+ break;
+
+ case IO_METHOD_MMAP:
+ case IO_METHOD_USERPTR:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
+ errno_exit ("VIDIOC_STREAMOFF");
+
+ break;
+ }
+}
+
+static void
+start_capturing (void)
+{
+ unsigned int i;
+ enum v4l2_buf_type type;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ /* Nothing to do. */
+ break;
+
+ case IO_METHOD_MMAP:
+ for (i = 0; i < n_buffers; ++i) {
+ struct v4l2_buffer buf;
+
+ CLEAR (buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = i;
+
+ if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
+ errno_exit ("VIDIOC_QBUF");
+ }
+
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
+ errno_exit ("VIDIOC_STREAMON");
+
+ break;
+
+ case IO_METHOD_USERPTR:
+ for (i = 0; i < n_buffers; ++i) {
+ struct v4l2_buffer buf;
+
+ CLEAR (buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.index = i;
+ buf.m.userptr = (unsigned long) buffers[i].start;
+ buf.length = buffers[i].length;
+
+ if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
+ errno_exit ("VIDIOC_QBUF");
+ }
+
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
+ errno_exit ("VIDIOC_STREAMON");
+
+ break;
+ }
+}
+
+static void
+uninit_device (void)
+{
+ unsigned int i;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ free (buffers[0].start);
+ break;
+
+ case IO_METHOD_MMAP:
+ for (i = 0; i < n_buffers; ++i)
+ if (-1 == munmap (buffers[i].start, buffers[i].length))
+ errno_exit ("munmap");
+ break;
+
+ case IO_METHOD_USERPTR:
+ for (i = 0; i < n_buffers; ++i)
+ free (buffers[i].start);
+ break;
+ }
+
+ free (buffers);
+}
+
+static void
+init_read (unsigned int buffer_size)
+{
+ buffers = calloc (1, sizeof (*buffers));
+
+ if (!buffers) {
+ fprintf (stderr, "Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+
+ buffers[0].length = buffer_size;
+ buffers[0].start = malloc (buffer_size);
+
+ if (!buffers[0].start) {
+ fprintf (stderr, "Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+}
+
+static void
+init_mmap (void)
+{
+ struct v4l2_requestbuffers req;
+
+ CLEAR (req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
+ if (EINVAL == errno) {
+ fprintf (stderr, "%s does not support "
+ "memory mapping\n", dev_name);
+ exit (EXIT_FAILURE);
+ } else {
+ errno_exit ("VIDIOC_REQBUFS");
+ }
+ }
+
+ if (req.count < 2) {
+ fprintf (stderr, "Insufficient buffer memory on %s\n",
+ dev_name);
+ exit (EXIT_FAILURE);
+ }
+
+ buffers = calloc (req.count, sizeof (*buffers));
+
+ if (!buffers) {
+ fprintf (stderr, "Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+
+ for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
+ struct v4l2_buffer buf;
+
+ CLEAR (buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = n_buffers;
+
+ if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
+ errno_exit ("VIDIOC_QUERYBUF");
+
+ buffers[n_buffers].length = buf.length;
+ buffers[n_buffers].start =
+ mmap (NULL /* start anywhere */,
+ buf.length,
+ PROT_READ | PROT_WRITE /* required */,
+ MAP_SHARED /* recommended */,
+ fd, buf.m.offset);
+
+ if (MAP_FAILED == buffers[n_buffers].start)
+ errno_exit ("mmap");
+ }
+}
+
+static void
+init_userp (unsigned int buffer_size)
+{
+ struct v4l2_requestbuffers req;
+ unsigned int page_size;
+
+ page_size = getpagesize ();
+ buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);
+
+ CLEAR (req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
+ if (EINVAL == errno) {
+ fprintf (stderr, "%s does not support "
+ "user pointer i/o\n", dev_name);
+ exit (EXIT_FAILURE);
+ } else {
+ errno_exit ("VIDIOC_REQBUFS");
+ }
+ }
+
+ buffers = calloc (4, sizeof (*buffers));
+
+ if (!buffers) {
+ fprintf (stderr, "Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+
+ for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
+ buffers[n_buffers].length = buffer_size;
+ buffers[n_buffers].start = memalign (/* boundary */ page_size,
+ buffer_size);
+
+ if (!buffers[n_buffers].start) {
+ fprintf (stderr, "Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+ }
+}
+
+static void
+init_device (void)
+{
+ struct v4l2_capability cap;
+ struct v4l2_cropcap cropcap;
+ struct v4l2_crop crop;
+ struct v4l2_format fmt;
+ unsigned int min;
+
+ if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
+ if (EINVAL == errno) {
+ fprintf (stderr, "%s is no V4L2 device\n",
+ dev_name);
+ exit (EXIT_FAILURE);
+ } else {
+ errno_exit ("VIDIOC_QUERYCAP");
+ }
+ }
+
+ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+ fprintf (stderr, "%s is no video capture device\n",
+ dev_name);
+ exit (EXIT_FAILURE);
+ }
+
+ switch (io) {
+ case IO_METHOD_READ:
+ if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
+ fprintf (stderr, "%s does not support read i/o\n",
+ dev_name);
+ exit (EXIT_FAILURE);
+ }
+
+ break;
+
+ case IO_METHOD_MMAP:
+ case IO_METHOD_USERPTR:
+ if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
+ fprintf (stderr, "%s does not support streaming i/o\n",
+ dev_name);
+ exit (EXIT_FAILURE);
+ }
+
+ break;
+ }
+
+
+ struct v4l2_frmivalenum frmival;
+
+ frmival.index = 0;
+ frmival.width = 640;
+ frmival.height = 480;
+ frmival.pixel_format = V4L2_PIX_FMT_YUYV;
+ if (xioctl (fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) < 0) {
+ fprintf(stderr, "VIDIOC_ENUM_FRAMEINTERVALS error %m\n");
+ } else {
+ fprintf(stderr, "type %d %d/%d\n",
+ frmival.type,
+ frmival.discrete.numerator,
+ frmival.discrete.denominator);
+ }
+
+
+ CLEAR (cropcap);
+
+ cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ crop.c = cropcap.defrect; /* reset to default */
+
+ if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {
+ switch (errno) {
+ case EINVAL:
+ /* Cropping not supported. */
+ break;
+ default:
+ /* Errors ignored. */
+ break;
+ }
+ }
+ } else {
+ /* Errors ignored. */
+ }
+
+
+ CLEAR (fmt);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = 640;
+ fmt.fmt.pix.height = 480;
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
+ errno_exit ("VIDIOC_S_FMT");
+
+ /* Note VIDIOC_S_FMT may change width and height. */
+
+ /* Buggy driver paranoia. */
+ min = fmt.fmt.pix.width * 2;
+ if (fmt.fmt.pix.bytesperline < min)
+ fmt.fmt.pix.bytesperline = min;
+ min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
+ if (fmt.fmt.pix.sizeimage < min)
+ fmt.fmt.pix.sizeimage = min;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ init_read (fmt.fmt.pix.sizeimage);
+ break;
+
+ case IO_METHOD_MMAP:
+ init_mmap ();
+ break;
+
+ case IO_METHOD_USERPTR:
+ init_userp (fmt.fmt.pix.sizeimage);
+ break;
+ }
+}
+
+static void
+close_device (void)
+{
+ if (-1 == close (fd))
+ errno_exit ("close");
+
+ fd = -1;
+}
+
+static void
+open_device (void)
+{
+ struct stat st;
+
+ if (-1 == stat (dev_name, &st)) {
+ fprintf (stderr, "Cannot identify '%s': %d, %s\n",
+ dev_name, errno, strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ if (!S_ISCHR (st.st_mode)) {
+ fprintf (stderr, "%s is no device\n", dev_name);
+ exit (EXIT_FAILURE);
+ }
+
+ fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
+
+ if (-1 == fd) {
+ fprintf (stderr, "Cannot open '%s': %d, %s\n",
+ dev_name, errno, strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+}
+
+static void
+usage (FILE * fp,
+ int argc,
+ char ** argv)
+{
+ fprintf (fp,
+ "Usage: %s [options]\n\n"
+ "Options:\n"
+ "-d | --device name Video device name [/dev/video]\n"
+ "-h | --help Print this message\n"
+ "-m | --mmap Use memory mapped buffers\n"
+ "-r | --read Use read() calls\n"
+ "-u | --userp Use application allocated buffers\n"
+ "",
+ argv[0]);
+}
+
+static const char short_options [] = "d:hmru";
+
+static const struct option
+long_options [] = {
+ { "device", required_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "mmap", no_argument, NULL, 'm' },
+ { "read", no_argument, NULL, 'r' },
+ { "userp", no_argument, NULL, 'u' },
+ { 0, 0, 0, 0 }
+};
+
+int
+main (int argc,
+ char ** argv)
+{
+ dev_name = "/dev/video";
+
+ for (;;) {
+ int index;
+ int c;
+
+ c = getopt_long (argc, argv,
+ short_options, long_options,
+ &index);
+
+ if (-1 == c)
+ break;
+
+ switch (c) {
+ case 0: /* getopt_long() flag */
+ break;
+
+ case 'd':
+ dev_name = optarg;
+ break;
+
+ case 'h':
+ usage (stdout, argc, argv);
+ exit (EXIT_SUCCESS);
+
+ case 'm':
+ io = IO_METHOD_MMAP;
+ break;
+
+ case 'r':
+ io = IO_METHOD_READ;
+ break;
+
+ case 'u':
+ io = IO_METHOD_USERPTR;
+ break;
+
+ default:
+ usage (stderr, argc, argv);
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ open_device ();
+
+ init_device ();
+
+ start_capturing ();
+
+ mainloop ();
+
+ stop_capturing ();
+
+ uninit_device ();
+
+ close_device ();
+
+ exit (EXIT_SUCCESS);
+
+ return 0;
+}