diff options
author | Kristian Høgsberg <krh@bitplanet.net> | 2012-07-04 00:28:42 -0400 |
---|---|---|
committer | Kristian Høgsberg <krh@bitplanet.net> | 2012-07-04 00:28:42 -0400 |
commit | 1a722068d20f4d58ee457d4ba93d540a7d3cde45 (patch) | |
tree | b3c62b5192835ca907ecefb29f3e84ddd0bf19ff |
initial simple-yuv commit
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | simple-yuv.c | 411 | ||||
-rw-r--r-- | wayland-drm-client-protocol.h | 213 | ||||
-rw-r--r-- | wayland-drm-protocol.c | 72 | ||||
-rw-r--r-- | webcam.c | 717 |
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; +} |