summaryrefslogtreecommitdiff
path: root/v4l2-apps/test/pixfmt-test.c
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@infradead.org>2007-06-20 05:30:55 -0300
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-06-20 05:30:55 -0300
commit4eb13325ec2fde1a2c4516c2fbb4a33f5b9162f2 (patch)
tree0bb310b3d0197c01dcef61eff679ac549f2218d9 /v4l2-apps/test/pixfmt-test.c
parentbd150f373b325df62dd8e55a4c188874be6056f5 (diff)
downloadmediapointer-dvb-s2-4eb13325ec2fde1a2c4516c2fbb4a33f5b9162f2.tar.gz
mediapointer-dvb-s2-4eb13325ec2fde1a2c4516c2fbb4a33f5b9162f2.tar.bz2
Add another testing tool to v4l2-apps/test
From: Mauro Carvalho Chehab <mchehab@infradead.org> Michael Schimek gladly donated us a newer testing tool, capable of identify other API non-compliances, called pixfmt-test. Thanks Michael for this contribution. Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'v4l2-apps/test/pixfmt-test.c')
-rw-r--r--v4l2-apps/test/pixfmt-test.c1780
1 files changed, 1780 insertions, 0 deletions
diff --git a/v4l2-apps/test/pixfmt-test.c b/v4l2-apps/test/pixfmt-test.c
new file mode 100644
index 000000000..c8047e842
--- /dev/null
+++ b/v4l2-apps/test/pixfmt-test.c
@@ -0,0 +1,1780 @@
+/*
+ V4L2 pixfmt test
+
+ Copyright (C) 2007 Michael H. Schimek <mschimek@gmx.at>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <getopt.h> /* getopt_long() */
+
+#include <fcntl.h> /* low-level i/o */
+#include <unistd.h>
+#include <errno.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>
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xutil.h>
+
+#undef MAX
+#define MAX(x, y) ({ \
+ __typeof__ (x) _x = (x); \
+ __typeof__ (y) _y = (y); \
+ (void)(&_x == &_y); /* alert when type mismatch */ \
+ (_x > _y) ? _x : _y; \
+})
+
+#define N_ELEMENTS(array) (sizeof (array) / sizeof ((array)[0]))
+#define CLEAR(var) memset (&(var), 0, sizeof (var))
+
+typedef enum {
+ /* Packed RGB formats. */
+
+ /* in memory */
+ BGRA8888_LE = 1, /* bbbbbbbb gggggggg rrrrrrrr aaaaaaaa */
+ BGRA8888_BE, /* aaaaaaaa rrrrrrrr gggggggg bbbbbbbb */
+ RGBA8888_LE, /* rrrrrrrr gggggggg bbbbbbbb aaaaaaaa */
+ RGBA8888_BE, /* aaaaaaaa bbbbbbbb gggggggg rrrrrrrr */
+
+ BGR888_LE, /* bbbbbbbb gggggggg rrrrrrrr */
+ BGR888_BE, /* rrrrrrrr gggggggg bbbbbbbb */
+
+ BGR565_LE, /* gggbbbbb rrrrrggg */
+ BGR565_BE, /* rrrrrggg gggbbbbb */
+ RGB565_LE, /* gggrrrrr bbbbbggg */
+ RGB565_BE, /* bbbbbggg gggrrrrr */
+
+ BGRA5551_LE, /* gggbbbbb arrrrrgg */
+ BGRA5551_BE, /* arrrrrgg gggbbbbb */
+ RGBA5551_LE, /* gggrrrrr abbbbbgg */
+ RGBA5551_BE, /* abbbbbgg gggrrrrr */
+
+ ABGR1555_LE, /* ggbbbbba rrrrrggg */
+ ABGR1555_BE, /* rrrrrggg ggbbbbba */
+ ARGB1555_LE, /* ggrrrrra bbbbbggg */
+ ARGB1555_BE, /* bbbbbggg ggrrrrra */
+
+ BGRA4444_LE, /* ggggbbbb aaaarrrr */
+ BGRA4444_BE, /* aaaarrrr ggggbbbb */
+ RGBA4444_LE, /* ggggrrrr aaaabbbb */
+ RGBA4444_BE, /* aaaabbbb ggggrrrr */
+
+ ABGR4444_LE, /* bbbbaaaa rrrrgggg */
+ ABGR4444_BE, /* rrrrgggg bbbbaaaa */
+ ARGB4444_LE, /* rrrraaaa bbbbgggg */
+ ARGB4444_BE, /* bbbbgggg rrrraaaa */
+
+ BGR233, /* rrrgggbb */
+ RGB332, /* bbgggrrr */
+
+ /* Bayer formats. */
+
+ BGGR, /* bbbbbbbb gggggggg */
+ /* gggggggg rrrrrrrr */
+ GBRG, /* gggggggg bbbbbbbb */
+ /* rrrrrrrr gggggggg */
+ RGGB, /* rrrrrrrr gggggggg */
+ /* gggggggg bbbbbbbb */
+ GRBG, /* gggggggg rrrrrrrr */
+ /* bbbbbbbb gggggggg */
+} pixfmt;
+
+/* A pixfmt set would be nicer, but I doubt all
+ YUV and RGB formats will fit in 64 bits. */
+typedef enum {
+ PACKED_RGB = (1 << 0),
+ BAYER = (1 << 1)
+} pixfmt_class;
+
+typedef enum {
+ LE = 1,
+ BE
+} byte_order;
+
+typedef struct {
+ /* Our name for this format. */
+ const char * name;
+
+ /* V4L2's name "V4L2_PIX_FMT_..." or NULL. */
+ const char * v4l2_fourcc_name;
+
+ /* Our ID for this format. */
+ pixfmt pixfmt;
+
+ /* Same pixfmt with opposite byte order.
+ Applies only to packed RGB formats. */
+ pixfmt pixfmt_opposite_byte_order;
+
+ /* Same pixfmt with red and blue bits swapped.
+ Applies only to RGB formats. */
+ pixfmt pixfmt_swap_red_blue;
+
+ /* Same pixfmt with alpha bits at the other end.
+ Applies only to packed RGB formats. */
+ pixfmt pixfmt_opposite_alpha;
+
+ pixfmt_class pixfmt_class;
+
+ /* V4L2's FOURCC or 0. */
+ uint32_t v4l2_fourcc;
+
+ /* LE or BE. Applies only to packed RGB formats. */
+ byte_order byte_order;
+
+ /* Applies only to RGB formats. */
+ uint8_t bits_per_pixel;
+
+ /* Number of blue, green and red bits per pixel.
+ Applies only to RGB formats. */
+ uint8_t color_depth;
+
+ /* Blue, green, red, alpha bit masks.
+ Applies only to packed RGB formats. */
+ uint32_t mask[4];
+
+ /* Number of blue, green, red, alpha bits.
+ Applies only to packed RGB formats. */
+ uint8_t n_bits[4];
+
+ /* Number of zero bits above the blue, green, red, alpha MSB.
+ E.g. 0x80001234 -> 0, 0x00000001 -> 31, 0 -> 32.
+ Applies only to packed RGB formats. */
+ uint8_t shr[4];
+
+} pixel_format;
+
+/* Population count in 32 bit constant, e.g. 0x70F -> 7. */
+#define PC32b(m) ((m) - (((m) >> 1) & 0x55555555))
+#define PC32a(m) ((PC32b (m) & 0x33333333) + ((PC32b (m) >> 2) & 0x33333333))
+#define PC32(m) ((((uint64_t)((PC32a (m) & 0x0F0F0F0F) \
+ + ((PC32a (m) >> 4) & 0x0F0F0F0F)) \
+ * 0x01010101) >> 24) & 0xFF)
+
+/* Find first set bit in 32 bit constant, see man 3 ffs(). */
+#define FFS2(m) ((m) & 0x2 ? 2 : (m))
+#define FFS4(m) ((m) & 0xC ? 2 + FFS2 ((m) >> 2) : FFS2 (m))
+#define FFS8(m) ((m) & 0xF0 ? 4 + FFS4 ((m) >> 4) : FFS4 (m))
+#define FFS16(m) ((m) & 0xFF00 ? 8 + FFS8 ((m) >> 8) : FFS8 (m))
+#define FFS32(m) ((m) & 0xFFFF0000 ? 16 + FFS16 ((m) >> 16) : FFS16 (m))
+
+#define PF_RGB(tn, vn, pf, pfxbo, pfxrb, pfxa, vpf, bo, b, g, r, a) \
+ [pf] = { \
+ .name = tn, \
+ .v4l2_fourcc_name = (0 == vpf) ? NULL : vn, \
+ .pixfmt = pf, \
+ .pixfmt_opposite_byte_order = pfxbo, \
+ .pixfmt_swap_red_blue = pfxrb, \
+ .pixfmt_opposite_alpha = pfxa, \
+ .pixfmt_class = PACKED_RGB, \
+ .v4l2_fourcc = vpf, \
+ .byte_order = bo, \
+ .bits_per_pixel = PC32 ((b) | (g) | (r) | (a)), \
+ .color_depth = PC32 ((b) | (g) | (r)), \
+ .mask = { b, g, r, a }, \
+ .n_bits = { PC32 (b), PC32 (g), PC32 (r), PC32 (a) }, \
+ .shr = { 32 - FFS32 (b), 32 - FFS32 (g), \
+ 32 - FFS32 (r), 32 - FFS32 (a) } \
+ }
+
+#define PF_RGB8(pf, pfxrb, vpf, b, g, r, a) \
+ PF_RGB (# pf, # vpf, pf, pf, pfxrb, 0, vpf, LE, b, g, r, a)
+
+#define PF_RGB16(fmt, bo, pfxrb, pfxa, vpf, b, g, r, a) \
+ PF_RGB (# fmt "_" # bo, # vpf, \
+ fmt ## _ ## bo, \
+ (bo == LE) ? fmt ## _ ## BE : fmt ## _ ## LE, \
+ pfxrb, pfxa, vpf, bo, b, g, r, a)
+
+#define PF_RGB24 PF_RGB16
+#define PF_RGB32 PF_RGB16
+
+#define PF_BAYER(pf, pfxrb, vpf) \
+ [pf] = { \
+ .name = # pf, \
+ .v4l2_fourcc_name = (0 == vpf) ? NULL : # vpf, \
+ .pixfmt = pf, \
+ .pixfmt_opposite_byte_order = pf, \
+ .pixfmt_swap_red_blue = pfxrb, \
+ .pixfmt_opposite_alpha = pf, \
+ .pixfmt_class = BAYER, \
+ .v4l2_fourcc = vpf, \
+ .byte_order = LE, \
+ .bits_per_pixel = 8, \
+ .color_depth = 24 /* sort of */ \
+ }
+
+static const pixel_format
+pixel_formats [] = {
+ PF_RGB32 (BGRA8888, LE, RGBA8888_LE, RGBA8888_BE,
+ V4L2_PIX_FMT_BGR32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000),
+ PF_RGB32 (BGRA8888, BE, RGBA8888_BE, RGBA8888_LE,
+ V4L2_PIX_FMT_RGB32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000),
+ PF_RGB32 (RGBA8888, LE, BGRA8888_LE, BGRA8888_BE,
+ 0, 0xFF0000, 0xFF00, 0xFF, 0xFF000000),
+ PF_RGB32 (RGBA8888, BE, BGRA8888_BE, BGRA8888_LE,
+ 0, 0xFF0000, 0xFF00, 0xFF, 0xFF000000),
+
+ PF_RGB24 (BGR888, LE, BGR888_BE, 0,
+ V4L2_PIX_FMT_BGR24, 0xFF, 0xFF00, 0xFF0000, 0),
+ PF_RGB24 (BGR888, BE, BGR888_LE, 0,
+ V4L2_PIX_FMT_RGB24, 0xFF, 0xFF00, 0xFF0000, 0),
+
+ PF_RGB16 (BGR565, LE, RGB565_LE, 0,
+ V4L2_PIX_FMT_RGB565, 0x001F, 0x07E0, 0xF800, 0),
+ PF_RGB16 (BGR565, BE, RGB565_BE, 0,
+ V4L2_PIX_FMT_RGB565X, 0x001F, 0x07E0, 0xF800, 0),
+ PF_RGB16 (RGB565, LE, BGR565_LE, 0, 0, 0xF800, 0x07E0, 0x001F, 0),
+ PF_RGB16 (RGB565, BE, BGR565_BE, 0, 0, 0xF800, 0x07E0, 0x001F, 0),
+
+ PF_RGB16 (BGRA5551, LE, RGBA5551_LE, ABGR1555_LE,
+ V4L2_PIX_FMT_RGB555, 0x001F, 0x03E0, 0x7C00, 0x8000),
+ PF_RGB16 (BGRA5551, BE, RGBA5551_BE, ABGR1555_BE,
+ V4L2_PIX_FMT_RGB555X, 0x001F, 0x03E0, 0x7C00, 0x8000),
+ PF_RGB16 (RGBA5551, LE, BGRA5551_LE, ARGB1555_LE,
+ 0, 0x7C00, 0x03E0, 0x001F, 0x8000),
+ PF_RGB16 (RGBA5551, BE, BGRA5551_BE, ARGB1555_BE,
+ 0, 0x7C00, 0x03E0, 0x001F, 0x8000),
+
+ PF_RGB16 (ABGR1555, LE, ARGB1555_LE, BGRA5551_LE,
+ 0, 0x003E, 0x07C0, 0xF800, 0x0001),
+ PF_RGB16 (ABGR1555, BE, ARGB1555_BE, BGRA5551_BE,
+ 0, 0x003E, 0x07C0, 0xF800, 0x0001),
+ PF_RGB16 (ARGB1555, LE, ABGR1555_LE, RGBA5551_LE,
+ 0, 0xF800, 0x07C0, 0x003E, 0x0001),
+ PF_RGB16 (ARGB1555, BE, ABGR1555_BE, RGBA5551_BE,
+ 0, 0xF800, 0x07C0, 0x003E, 0x0001),
+
+ PF_RGB16 (BGRA4444, LE, RGBA4444_LE, ABGR4444_LE,
+ V4L2_PIX_FMT_RGB444, 0x000F, 0x00F0, 0x0F00, 0xF000),
+ PF_RGB16 (BGRA4444, BE, RGBA4444_BE, ABGR4444_BE,
+ 0, 0x000F, 0x00F0, 0x0F00, 0xF000),
+ PF_RGB16 (RGBA4444, LE, BGRA4444_LE, ARGB4444_LE,
+ 0, 0x0F00, 0x00F0, 0x000F, 0xF000),
+ PF_RGB16 (RGBA4444, BE, BGRA4444_BE, ARGB4444_BE,
+ 0, 0x0F00, 0x00F0, 0x000F, 0xF000),
+
+ PF_RGB16 (ABGR4444, LE, ARGB4444_LE, BGRA4444_LE,
+ 0, 0x00F0, 0x0F00, 0xF000, 0x000F),
+ PF_RGB16 (ABGR4444, BE, ARGB4444_BE, BGRA4444_BE,
+ 0, 0x00F0, 0x0F00, 0xF000, 0x000F),
+ PF_RGB16 (ARGB4444, LE, ABGR4444_LE, RGBA4444_LE,
+ 0, 0xF000, 0x0F00, 0x00F0, 0x000F),
+ PF_RGB16 (ARGB4444, BE, ABGR4444_BE, RGBA4444_BE,
+ 0, 0xF000, 0x0F00, 0x00F0, 0x000F),
+
+ PF_RGB8 (BGR233, RGB332,
+ V4L2_PIX_FMT_RGB332, 0x03, 0x1C, 0xE0, 0),
+ PF_RGB8 (RGB332, BGR233,
+ 0, 0xE0, 0x1C, 0x03, 0),
+
+ PF_BAYER (BGGR, RGGB, V4L2_PIX_FMT_SBGGR8),
+ PF_BAYER (RGGB, BGGR, 0),
+ PF_BAYER (GBRG, GRBG, 0),
+ PF_BAYER (GRBG, GBRG, 0),
+};
+
+static const pixel_format *
+find_v4l2_fourcc (uint32_t fourcc)
+{
+ const pixel_format *pf;
+
+ for (pf = pixel_formats;
+ pf < pixel_formats + N_ELEMENTS (pixel_formats); ++pf) {
+ if (fourcc == pf->v4l2_fourcc)
+ return pf;
+ }
+
+ return NULL;
+}
+
+static const pixel_format *
+next_converter (const pixel_format * pf)
+{
+ const pixel_format *next_pf;
+
+ if (NULL == pf)
+ pf = pixel_formats;
+ else
+ pf = pixel_formats + pf->pixfmt;
+
+ next_pf = pf;
+
+ for (;;) {
+ if (++next_pf >= pixel_formats + N_ELEMENTS (pixel_formats))
+ next_pf = pixel_formats;
+
+ if (next_pf == pf)
+ break;
+
+ if (0 == next_pf->pixfmt)
+ continue;
+
+ if (pf->pixfmt_class == next_pf->pixfmt_class
+ && pf->bits_per_pixel == next_pf->bits_per_pixel)
+ break;
+ }
+
+ return next_pf;
+}
+
+typedef enum {
+ IO_METHOD_READ = 1,
+ IO_METHOD_MMAP,
+} io_methods;
+
+typedef struct {
+ void * start;
+ size_t length;
+} io_buffer;
+
+#define VERSION "1.0"
+
+static const char * my_name;
+
+static const char * dev_name = "/dev/video";
+
+static int fd;
+static v4l2_std_id std_id;
+static io_methods io_method;
+static struct v4l2_format fmt;
+static io_buffer * buffers;
+static unsigned int n_buffers;
+
+static Display * display;
+static int screen;
+static Window window;
+static GC gc;
+static Atom xa_delete_window;
+
+static XImage * ximage;
+static const pixel_format * ximage_pf;
+
+static void
+error_exit (const char * templ,
+ ...)
+{
+ va_list ap;
+
+ fprintf (stderr, "%s: ", my_name);
+ va_start (ap, templ);
+ vfprintf (stderr, templ, ap);
+ va_end (ap);
+
+ exit (EXIT_FAILURE);
+}
+
+static void
+errno_exit (const char * s)
+{
+ error_exit ("%s error %d, %s\n",
+ s, errno, strerror (errno));
+}
+
+static void
+write_rgb_pixel (uint8_t * dst,
+ const pixel_format * dst_pf,
+ unsigned int b,
+ unsigned int g,
+ unsigned int r)
+{
+ unsigned int dst_pixel;
+
+ dst_pixel = ((b << 24) >> dst_pf->shr[0]) & dst_pf->mask[0];
+ dst_pixel |= ((g << 24) >> dst_pf->shr[1]) & dst_pf->mask[1];
+ dst_pixel |= ((r << 24) >> dst_pf->shr[2]) & dst_pf->mask[2];
+
+ switch (dst_pf->byte_order * 256 + dst_pf->bits_per_pixel) {
+ case LE * 256 + 32:
+ dst[3] = dst_pixel >> 24;
+ /* fall through */
+ case LE * 256 + 24:
+ dst[2] = dst_pixel >> 16;
+ case LE * 256 + 16:
+ dst[1] = dst_pixel >> 8;
+ case LE * 256 + 8:
+ dst[0] = dst_pixel;
+ break;
+
+ case BE * 256 + 32:
+ *dst++ = dst_pixel >> 24;
+ case BE * 256 + 24:
+ *dst++ = dst_pixel >> 16;
+ case BE * 256 + 16:
+ *dst++ = dst_pixel >> 8;
+ case BE * 256 + 8:
+ *dst = dst_pixel;
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+}
+
+static void
+convert_bayer_image (uint8_t * dst,
+ const pixel_format * dst_pf,
+ unsigned long dst_bpl,
+ const uint8_t * src,
+ const pixel_format * src_pf,
+ unsigned long src_bpl,
+ unsigned int width,
+ unsigned int height)
+{
+ unsigned long dst_padding;
+ unsigned int tile;
+ unsigned int y;
+
+ assert (PACKED_RGB == dst_pf->pixfmt_class);
+ assert (BAYER == src_pf->pixfmt_class);
+
+ assert (width >= 2 && 0 == (width & 1));
+ assert (height >= 2 && 0 == (height & 1));
+
+ dst_padding = dst_bpl - width * (dst_pf->bits_per_pixel >> 3);
+ assert ((long) dst_padding >= 0);
+
+ switch (src_pf->pixfmt) {
+ case BGGR:
+ tile = 0;
+ break;
+
+ case GBRG:
+ tile = 1;
+ break;
+
+ case RGGB:
+ tile = 2;
+ break;
+
+ case GRBG:
+ tile = 3;
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+
+ for (y = 0; y < height; ++y) {
+ const uint8_t *srcm;
+ const uint8_t *srcp;
+ int x;
+
+ srcm = srcp = src - src_bpl;
+
+ if (0 == y)
+ srcm += src_bpl * 2;
+
+ if (y != height - 1)
+ srcp += src_bpl * 2;
+
+ for (x = 0; x < width; ++x) {
+ int xm, xp;
+
+ xm = (((0 == x) - 1) | 1) + x;
+ xp = (((x != width - 1) - 1) | 1) + x;
+
+ switch (tile) {
+ case 0: /* BG
+ GR */
+ write_rgb_pixel (dst, dst_pf,
+ /* b */ src[x],
+ /* g */ (src[xm] +
+ src[xp] +
+ srcm[x] +
+ srcp[x] + 2) >> 2,
+ /* r */ (srcm[xm] +
+ srcm[xp] +
+ srcp[xm] +
+ srcp[xp] + 2) >> 2);
+ break;
+
+ case 1: /* GB
+ RG */
+ write_rgb_pixel (dst, dst_pf,
+ /* b */ (src[xm] +
+ src[xp] + 1) >> 1,
+ /* g */ src[x],
+ /* r */ (srcm[x] +
+ srcp[x] + 1) >> 1);
+ break;
+
+ case 2: /* GR
+ BG */
+ write_rgb_pixel (dst, dst_pf,
+ /* b */ (srcm[x] +
+ srcp[x] + 1) >> 1,
+ /* g */ src[x],
+ /* r */ (src[xm] +
+ src[xp] + 1) >> 1);
+ break;
+
+ case 3: /* RG
+ GB */
+ write_rgb_pixel (dst, dst_pf,
+ /* b */ (srcm[xm] +
+ srcm[xp] +
+ srcp[xm] +
+ srcp[xp] + 2) >> 2,
+ /* g */ (src[xm] +
+ src[xp] +
+ srcm[x] +
+ srcp[x] + 2) >> 2,
+ /* r */ src[x]);
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+
+ tile ^= 1;
+
+ dst += dst_pf->bits_per_pixel >> 3;
+ }
+
+ tile ^= 2;
+
+ dst += dst_padding;
+ src += src_bpl;
+ }
+}
+
+static void
+convert_packed_rgb_pixel (uint8_t * dst,
+ const pixel_format * dst_pf,
+ const uint8_t * src,
+ const pixel_format * src_pf)
+{
+ uint32_t dst_pixel;
+ uint32_t src_pixel;
+ unsigned int i;
+
+ src_pixel = 0;
+
+ switch (src_pf->byte_order * 256 + src_pf->bits_per_pixel) {
+ case LE * 256 + 32:
+ src_pixel = src[3] << 24;
+ /* fall through */
+ case LE * 256 + 24:
+ src_pixel |= src[2] << 16;
+ case LE * 256 + 16:
+ src_pixel |= src[1] << 8;
+ case LE * 256 + 8:
+ src_pixel |= src[0];
+ break;
+
+ case BE * 256 + 32:
+ src_pixel = *src++ << 24;
+ case BE * 256 + 24:
+ src_pixel |= *src++ << 16;
+ case BE * 256 + 16:
+ src_pixel |= *src++ << 8;
+ case BE * 256 + 8:
+ src_pixel |= *src;
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+
+ dst_pixel = 0;
+
+ for (i = 0; i < 3; ++i) {
+ unsigned int c;
+
+ c = (src_pixel & src_pf->mask[i]) << src_pf->shr[i];
+
+ /* XXX Check if CPU supports only signed right shift. */
+ c |= c >> src_pf->n_bits[i];
+ c |= c >> (src_pf->n_bits[i] * 2);
+
+ dst_pixel |= (c >> dst_pf->shr[i]) & dst_pf->mask[i];
+ }
+
+ switch (dst_pf->byte_order * 256 + dst_pf->bits_per_pixel) {
+ case LE * 256 + 32:
+ dst[3] = dst_pixel >> 24;
+ /* fall through */
+ case LE * 256 + 24:
+ dst[2] = dst_pixel >> 16;
+ case LE * 256 + 16:
+ dst[1] = dst_pixel >> 8;
+ case LE * 256 + 8:
+ dst[0] = dst_pixel;
+ break;
+
+ case BE * 256 + 32:
+ *dst++ = dst_pixel >> 24;
+ case BE * 256 + 24:
+ *dst++ = dst_pixel >> 16;
+ case BE * 256 + 16:
+ *dst++ = dst_pixel >> 8;
+ case BE * 256 + 8:
+ *dst = dst_pixel;
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+}
+
+static void
+convert_rgb_image (uint8_t * dst,
+ const pixel_format * dst_pf,
+ unsigned long dst_bpl,
+ const uint8_t * src,
+ const pixel_format * src_pf,
+ unsigned long src_bpl,
+ unsigned int width,
+ unsigned int height)
+{
+ unsigned long dst_padding;
+ unsigned long src_padding;
+
+ assert (PACKED_RGB == dst_pf->pixfmt_class);
+
+ if (BAYER == src_pf->pixfmt_class) {
+ convert_bayer_image (dst, dst_pf, dst_bpl,
+ src, src_pf, src_bpl,
+ width, height);
+ return;
+ }
+
+ assert (width > 0);
+ assert (height > 0);
+
+ dst_padding = dst_bpl - width * (dst_pf->bits_per_pixel >> 3);
+ src_padding = src_bpl - width * (src_pf->bits_per_pixel >> 3);
+
+ assert ((long)(dst_padding | src_padding) >= 0);
+
+ do {
+ unsigned int count = width;
+
+ do {
+ convert_packed_rgb_pixel (dst, dst_pf, src, src_pf);
+
+ dst += dst_pf->bits_per_pixel >> 3;
+ src += src_pf->bits_per_pixel >> 3;
+ } while (--count > 0);
+
+ dst += dst_padding;
+ src += src_padding;
+ } while (--height > 0);
+}
+
+typedef enum {
+ NEXT_FORMAT = 1,
+ NEXT_CONVERTER
+} my_event;
+
+static my_event
+x_event (void)
+{
+ while (XPending (display)) {
+ XEvent event;
+ int key;
+
+ XNextEvent (display, &event);
+
+ switch (event.type) {
+ case KeyPress:
+ key = XLookupKeysym (&event.xkey, 0);
+
+ switch (key) {
+ case 'n':
+ return NEXT_FORMAT;
+
+ case 'c':
+ if (event.xkey.state & ControlMask)
+ exit (EXIT_SUCCESS);
+ return NEXT_CONVERTER;
+
+ case 'q':
+ exit (EXIT_SUCCESS);
+
+ default:
+ break;
+ }
+
+ break;
+
+ case ClientMessage:
+ /* We requested only delete_window messages. */
+ exit (EXIT_SUCCESS);
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static XImage *
+create_ximage (const pixel_format ** pf,
+ unsigned int width,
+ unsigned int height)
+{
+ XImage *ximage;
+ unsigned int image_size;
+ unsigned int i;
+
+ assert (NULL != display);
+
+ ximage = XCreateImage (display,
+ DefaultVisual (display, screen),
+ DefaultDepth (display, screen),
+ ZPixmap,
+ /* offset */ 0,
+ /* data */ NULL,
+ width,
+ height,
+ /* bitmap_pad (n/a) */ 8,
+ /* bytes_per_line: auto */ 0);
+ if (NULL == ximage) {
+ error_exit ("Cannot allocate XImage.\n");
+ }
+
+ for (i = 0; i < N_ELEMENTS (pixel_formats); ++i) {
+ if (PACKED_RGB != pixel_formats[i].pixfmt_class)
+ continue;
+ if ((LSBFirst == ximage->byte_order)
+ != (LE == pixel_formats[i].byte_order))
+ continue;
+ if (ximage->bits_per_pixel
+ != pixel_formats[i].bits_per_pixel)
+ continue;
+ if (ximage->blue_mask != pixel_formats[i].mask[0])
+ continue;
+ if (ximage->green_mask != pixel_formats[i].mask[1])
+ continue;
+ if (ximage->red_mask != pixel_formats[i].mask[2])
+ continue;
+ break;
+ }
+
+ if (i >= N_ELEMENTS (pixel_formats)) {
+ error_exit ("Unknown XImage pixel format "
+ "(bpp=%u %s b=0x%08x g=0x%08x r=0x%08x).\n",
+ ximage->bits_per_pixel,
+ (LSBFirst == ximage->byte_order) ?
+ "LSBFirst" : "MSBFirst",
+ ximage->blue_mask,
+ ximage->green_mask,
+ ximage->red_mask);
+ }
+
+ if (NULL != pf)
+ *pf = pixel_formats + i;
+
+ image_size = (ximage->bytes_per_line * ximage->height);
+
+ ximage->data = malloc (image_size);
+ if (NULL == ximage->data) {
+ error_exit ("Cannot allocate XImage data (%u bytes).\n",
+ image_size);
+ exit (EXIT_FAILURE);
+ }
+
+ return ximage;
+}
+
+static void
+resize_window (unsigned int image_width,
+ unsigned int image_height,
+ unsigned int text_width,
+ unsigned int text_height)
+{
+ assert (0 != window);
+
+ XResizeWindow (display, window,
+ MAX (image_width, text_width),
+ image_height + text_height);
+
+ if (NULL != ximage) {
+ free (ximage->data);
+ ximage->data = NULL;
+
+ XDestroyImage (ximage);
+ }
+
+ ximage = create_ximage (&ximage_pf, image_width, image_height);
+}
+
+static const char *
+pixel_format_bit_string (const pixel_format * pf)
+{
+ static char buf[64];
+ char *d;
+ unsigned int i;
+
+ if (PACKED_RGB != pf->pixfmt_class)
+ return NULL;
+
+ d = buf;
+
+ for (i = 0; i < pf->bits_per_pixel; i += 8) {
+ unsigned int ii;
+ int j;
+
+ if (0 != i)
+ *d++ = ' ';
+
+ ii = i;
+ if (BE == pf->byte_order)
+ ii = pf->bits_per_pixel - i - 8;
+
+ for (j = 7; j >= 0; --j) {
+ unsigned int k;
+
+ for (k = 0; k < 4; ++k) {
+ if (pf->mask[k] & (1 << (ii + j))) {
+ *d++ = "bgra"[k];
+ break;
+ }
+ }
+ }
+ }
+
+ *d = 0;
+
+ return buf;
+}
+
+static void
+display_image (const uint8_t * image,
+ uint32_t v4l2_fourcc,
+ const pixel_format * image_pf,
+ unsigned long image_bpl,
+ unsigned int image_width,
+ unsigned int image_height)
+{
+ XWindowAttributes wa;
+ XFontStruct *font;
+ unsigned int text_height;
+ XTextItem xti;
+ const char *v4l2_fourcc_name;
+ unsigned int i;
+
+ assert (NULL != ximage);
+
+ if (!XGetWindowAttributes (display, window, &wa)) {
+ error_exit ("Cannot determine current X11 window size.\n");
+ }
+
+ font = XQueryFont (display, XGContextFromGC (gc));
+ text_height = font->max_bounds.ascent + font->max_bounds.descent;
+
+ if (image_width > ximage->width
+ || image_width != wa.width
+ || image_height > ximage->height
+ || image_height + text_height != wa.height) {
+ resize_window (image_width,
+ image_height,
+ /* text_width */ image_width,
+ text_height);
+ }
+
+ convert_rgb_image ((uint8_t *) ximage->data,
+ ximage_pf,
+ ximage->bytes_per_line,
+ image,
+ image_pf,
+ image_bpl,
+ image_width,
+ image_height);
+
+ XPutImage (display,
+ window,
+ gc,
+ ximage,
+ /* src_x */ 0,
+ /* src_y */ 0,
+ /* dst_x */ 0,
+ /* dst_y */ 0,
+ /* width */ image_width,
+ /* height */ image_height);
+
+
+ XSetForeground (display, gc, XBlackPixel (display, screen));
+
+ XFillRectangle (display,
+ window,
+ gc,
+ /* x */ 0,
+ /* y */ image_height,
+ wa.width,
+ text_height);
+
+ XSetForeground (display, gc, XWhitePixel (display, screen));
+
+ v4l2_fourcc_name = "?";
+
+ for (i = 0; i < N_ELEMENTS (pixel_formats); ++i) {
+ if (v4l2_fourcc == pixel_formats[i].v4l2_fourcc) {
+ v4l2_fourcc_name = pixel_formats[i].v4l2_fourcc_name;
+ break;
+ }
+ }
+
+
+ CLEAR (xti);
+
+ if (PACKED_RGB == image_pf->pixfmt_class) {
+ xti.nchars = asprintf (&xti.chars,
+ "Format %s, converter %s (%s)",
+ v4l2_fourcc_name,
+ image_pf->name,
+ pixel_format_bit_string (image_pf));
+ } else {
+ xti.nchars = asprintf (&xti.chars,
+ "Format %s, converter %s",
+ v4l2_fourcc_name,
+ image_pf->name);
+ }
+
+ if (xti.nchars < 0) {
+ error_exit ("Cannot allocate text buffer.\n");
+ }
+
+ XDrawText (display, window, gc,
+ /* x */ 4,
+ /* y */ image_height + font->max_bounds.ascent,
+ &xti,
+ /* n_items */ 1);
+}
+
+static void
+open_window (unsigned int width,
+ unsigned int height)
+{
+ GC default_gc;
+ XFontStruct *font;
+ unsigned int text_height;
+
+ display = XOpenDisplay (NULL);
+ if (NULL == display) {
+ error_exit ("Cannot open X11 display.\n");
+ }
+
+ screen = DefaultScreen (display);
+
+ default_gc = XDefaultGC (display, screen);
+ font = XQueryFont (display, XGContextFromGC (default_gc));
+ text_height = font->max_bounds.ascent + font->max_bounds.descent;
+
+ window = XCreateSimpleWindow (display,
+ RootWindow (display, screen),
+ /* x */ 0,
+ /* y */ 0,
+ width,
+ height + text_height,
+ /* border width */ 2,
+ /* foreground */
+ XWhitePixel (display, screen),
+ /* background */
+ XBlackPixel (display, screen));
+ if (0 == window) {
+ error_exit ("Cannot open X11 window.\n");
+ }
+
+ gc = XCreateGC (display, window,
+ /* valuemask */ 0,
+ /* values */ NULL);
+
+ XSetFunction (display, gc, GXcopy);
+ XSetFillStyle (display, gc, FillSolid);
+
+ ximage = create_ximage (&ximage_pf, width, height);
+
+ XSelectInput (display, window,
+ (KeyPressMask |
+ ExposureMask |
+ StructureNotifyMask));
+
+ xa_delete_window = XInternAtom (display, "WM_DELETE_WINDOW",
+ /* only_if_exists */ False);
+
+ XSetWMProtocols (display, window, &xa_delete_window, /* count */ 1);
+
+ XStoreName (display, window,
+ "V4L2 Pixfmt Test - "
+ "Press [n] for next format, [c] for next converter");
+
+ XMapWindow (display, window);
+
+ XSync (display, /* discard all events */ False);
+}
+
+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 bool
+read_and_display_frame (const pixel_format * conv_pf)
+{
+ struct v4l2_buffer buf;
+
+ switch (io_method) {
+ case IO_METHOD_READ:
+ if (-1 == read (fd, buffers[0].start, buffers[0].length)) {
+ switch (errno) {
+ case EAGAIN:
+ return false;
+
+ default:
+ errno_exit ("read");
+ }
+ }
+
+ display_image (buffers[0].start,
+ fmt.fmt.pix.pixelformat,
+ conv_pf,
+ fmt.fmt.pix.bytesperline,
+ fmt.fmt.pix.width,
+ fmt.fmt.pix.height);
+
+ 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 false;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+
+ /* fall through */
+
+ default:
+ errno_exit ("VIDIOC_DQBUF");
+ }
+ }
+
+ assert (buf.index < n_buffers);
+
+ display_image (buffers[buf.index].start,
+ fmt.fmt.pix.pixelformat,
+ conv_pf,
+ fmt.fmt.pix.bytesperline,
+ fmt.fmt.pix.width,
+ fmt.fmt.pix.height);
+
+ if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
+ errno_exit ("VIDIOC_QBUF");
+
+ break;
+ }
+
+ return true;
+}
+
+static void
+wait_for_next_frame (void)
+{
+ for (;;) {
+ struct timeval timeout;
+ fd_set fds;
+ int r;
+
+ FD_ZERO (&fds);
+ FD_SET (fd, &fds);
+
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ r = select (fd + 1, &fds, NULL, NULL, &timeout);
+
+ if (-1 == r) {
+ if (EINTR == errno)
+ continue;
+
+ errno_exit ("select");
+ } else if (0 == r) {
+ error_exit ("select timeout.\n");
+ } else {
+ break;
+ }
+ }
+}
+
+static void
+flush_capture_queue (void)
+{
+ struct v4l2_buffer buf;
+
+ for (;;) {
+ switch (io_method) {
+ case IO_METHOD_READ:
+ /* Nothing to do. */
+ return;
+
+ 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;
+
+ default:
+ errno_exit ("VIDIOC_DQBUF");
+ }
+ }
+
+ if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
+ errno_exit ("VIDIOC_QBUF");
+
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+ }
+}
+
+static void
+capture_loop (void)
+{
+ const pixel_format *conv_pf;
+
+ conv_pf = find_v4l2_fourcc (fmt.fmt.pix.pixelformat);
+ assert (NULL != conv_pf);
+
+ for (;;) {
+ /* Remove images from the capture queue if
+ we can't display them fast enough. */
+ flush_capture_queue ();
+
+ do {
+ wait_for_next_frame ();
+ } while (!read_and_display_frame (conv_pf));
+
+ switch (x_event ()) {
+ case NEXT_CONVERTER:
+ conv_pf = next_converter (conv_pf);
+ break;
+
+ case NEXT_FORMAT:
+ return;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void
+stop_capturing (void)
+{
+ enum v4l2_buf_type type;
+
+ switch (io_method) {
+ case IO_METHOD_READ:
+ /* Nothing to do. */
+ break;
+
+ case IO_METHOD_MMAP:
+ 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_method) {
+ 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;
+
+ default:
+ assert (0);
+ break;
+ }
+}
+
+static void
+free_io_buffers (void)
+{
+ unsigned int i;
+
+ switch (io_method) {
+ 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;
+
+ default:
+ assert (0);
+ break;
+ }
+
+ free (buffers);
+ buffers = NULL;
+}
+
+static void
+init_read_io (unsigned int buffer_size)
+{
+ buffers = calloc (1, sizeof (*buffers));
+
+ if (NULL == buffers) {
+ error_exit ("Cannot allocate capture buffer (%u bytes).\n",
+ sizeof (*buffers));
+ }
+
+ buffers[0].length = buffer_size;
+ buffers[0].start = malloc (buffer_size);
+
+ if (NULL == buffers[0].start) {
+ error_exit ("Cannot allocate capture buffer (%u bytes).\n",
+ buffer_size);
+ }
+}
+
+static void
+init_mmap_io (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) {
+ error_exit ("%s does not support "
+ "memory mapping.\n", dev_name);
+ } else {
+ errno_exit ("VIDIOC_REQBUFS");
+ }
+ }
+
+ if (req.count < 2) {
+ error_exit ("Insufficient buffer memory on %s.\n",
+ dev_name);
+ }
+
+ buffers = calloc (req.count, sizeof (*buffers));
+ if (NULL == buffers) {
+ error_exit ("Cannot allocate capture buffer (%u bytes).\n",
+ req.count * sizeof (*buffers));
+ }
+
+ 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
+mainloop (void)
+{
+ bool checked_formats[N_ELEMENTS (pixel_formats)];
+
+ CLEAR (checked_formats);
+
+ for (;;) {
+ const pixel_format *pf;
+ const pixel_format *actual_pf;
+ unsigned int width;
+ unsigned int height;
+ unsigned int min_bpl;
+ unsigned int min_size;
+ unsigned int i;
+
+ for (i = 0; i < N_ELEMENTS (pixel_formats); ++i) {
+ if (checked_formats[i])
+ continue;
+ checked_formats[i] = true;
+ if (0 != pixel_formats[i].v4l2_fourcc)
+ break;
+ }
+
+ if (i >= N_ELEMENTS (pixel_formats))
+ return; /* all done */
+
+ pf = pixel_formats + i;
+
+ CLEAR (fmt);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ height = 480;
+ if (std_id & V4L2_STD_625_50)
+ height = 576;
+
+ width = height * 4 / 3;
+
+ fmt.fmt.pix.width = width;
+ fmt.fmt.pix.height = height;
+
+ fmt.fmt.pix.pixelformat = pf->v4l2_fourcc;
+
+ fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) {
+ if (EINVAL != errno) {
+ errno_exit ("VIDIOC_S_FMT");
+ }
+
+ fprintf (stderr, "Format %s %ux%u "
+ "not supported by driver.\n",
+ pf->v4l2_fourcc_name,
+ width,
+ height);
+
+ continue;
+ }
+
+ actual_pf = find_v4l2_fourcc (fmt.fmt.pix.pixelformat);
+ if (0 == actual_pf) {
+ fprintf (stderr,
+ "Requested pixelformat %s, driver "
+ "returned unknown pixelformat 0x%08x.\n",
+ pf->v4l2_fourcc_name,
+ fmt.fmt.pix.pixelformat);
+ continue;
+ } else if (pf != actual_pf) {
+ /* Some drivers change pixelformat. */
+ checked_formats[actual_pf->pixfmt] = true;
+ pf = actual_pf;
+ }
+
+ min_bpl = (fmt.fmt.pix.width * pf->bits_per_pixel) >> 3;
+
+ if (fmt.fmt.pix.bytesperline < min_bpl) {
+ error_exit ("Driver returned fmt.pix.pixelformat=%s "
+ "width=%u height=%u bytesperline=%u. "
+ "Expected bytesperline >= %u.\n",
+ pf->v4l2_fourcc_name,
+ fmt.fmt.pix.width,
+ fmt.fmt.pix.height,
+ fmt.fmt.pix.bytesperline,
+ min_bpl);
+ continue;
+ }
+
+ min_size = (fmt.fmt.pix.height - 1)
+ * MAX (min_bpl, fmt.fmt.pix.bytesperline)
+ + min_bpl;
+
+ if (fmt.fmt.pix.sizeimage < min_size) {
+ error_exit ("Driver returned fmt.pix.pixelformat=%s "
+ "width=%u height=%u bytesperline=%u "
+ "size=%u. Expected size >= %u.\n",
+ pf->v4l2_fourcc_name,
+ fmt.fmt.pix.width,
+ fmt.fmt.pix.height,
+ fmt.fmt.pix.bytesperline,
+ fmt.fmt.pix.sizeimage,
+ min_size);
+ continue;
+ }
+
+ if (0 == window) {
+ open_window (fmt.fmt.pix.width,
+ fmt.fmt.pix.height);
+ }
+
+ switch (io_method) {
+ case IO_METHOD_READ:
+ init_read_io (fmt.fmt.pix.sizeimage);
+ break;
+
+ case IO_METHOD_MMAP:
+ init_mmap_io ();
+ break;
+ }
+
+ start_capturing ();
+
+ capture_loop ();
+
+ stop_capturing ();
+
+ free_io_buffers ();
+ }
+}
+
+static void
+init_device (void)
+{
+ struct v4l2_capability cap;
+ struct v4l2_cropcap cropcap;
+ struct v4l2_crop crop;
+
+ if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
+ if (EINVAL == errno) {
+ error_exit ("%s is not a V4L2 device.\n");
+ } else {
+ errno_exit ("VIDIOC_QUERYCAP");
+ }
+ }
+
+ switch (io_method) {
+ case 0:
+ if (cap.capabilities & V4L2_CAP_STREAMING) {
+ io_method = IO_METHOD_MMAP;
+ } else if (cap.capabilities & V4L2_CAP_READWRITE) {
+ io_method = IO_METHOD_READ;
+ } else {
+ error_exit ("%s does not support reading or "
+ "streaming.\n");
+ }
+
+ break;
+
+ case IO_METHOD_READ:
+ if (0 == (cap.capabilities & V4L2_CAP_READWRITE)) {
+ error_exit ("%s does not support read i/o.\n");
+ }
+
+ break;
+
+ case IO_METHOD_MMAP:
+ if (0 == (cap.capabilities & V4L2_CAP_STREAMING)) {
+ error_exit ("%s does not support streaming i/o.\n");
+ }
+
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+
+ CLEAR (cropcap);
+
+ cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
+ crop.type = cropcap.type;
+ crop.c = cropcap.defrect; /* reset to default */
+
+ /* Errors ignored. */
+ xioctl (fd, VIDIOC_S_CROP, &crop);
+ } else {
+ /* Errors ignored. */
+ }
+
+ if (-1 == xioctl (fd, VIDIOC_G_STD, &std_id))
+ errno_exit ("VIDIOC_G_STD");
+}
+
+static void
+open_device (void)
+{
+ struct stat st;
+
+ if (-1 == stat (dev_name, &st)) {
+ error_exit ("Cannot identify '%s'. %s.\n",
+ dev_name, strerror (errno));
+ }
+
+ if (!S_ISCHR (st.st_mode)) {
+ error_exit ("%s is not a device file.\n", dev_name);
+ }
+
+ fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
+
+ if (-1 == fd) {
+ error_exit ("Cannot open %s. %s.\n",
+ dev_name, strerror (errno));
+ }
+}
+
+static void
+self_test (void)
+{
+ const pixel_format *pf;
+
+ assert (0 == pixel_formats[0].pixfmt);
+ assert (N_ELEMENTS (pixel_formats) > 0);
+
+ for (pf = pixel_formats + 1;
+ pf < pixel_formats + N_ELEMENTS (pixel_formats); ++pf) {
+ const pixel_format *pf2;
+ unsigned int i;
+
+#define pf_assert(expr) \
+do { \
+ if (!(expr)) { \
+ error_exit ("Assertion %s failed in " \
+ "pixel_format[%d = %s].\n", \
+ #expr, (int)(pf - pixel_formats), \
+ pf->name ? pf->name : "?"); \
+ } \
+} while (0)
+
+ pf_assert (0 != pf->pixfmt);
+ pf_assert (NULL != pf->name);
+
+ pf_assert ((0 == pf->v4l2_fourcc)
+ == (NULL == pf->v4l2_fourcc_name));
+
+ pf_assert (0 != pf->pixfmt_swap_red_blue);
+
+ pf_assert (LE == pf->byte_order
+ || BE == pf->byte_order);
+
+ pf_assert (PACKED_RGB == pf->pixfmt_class
+ || BAYER == pf->pixfmt_class);
+
+ if (PACKED_RGB == pf->pixfmt_class) {
+ pf_assert (pf->color_depth == (pf->n_bits[0] +
+ pf->n_bits[1] +
+ pf->n_bits[2]));
+
+ pf_assert (pf->bits_per_pixel == (pf->n_bits[0] +
+ pf->n_bits[1] +
+ pf->n_bits[2] +
+ pf->n_bits[3]));
+
+ pf_assert (0 != pf->pixfmt_opposite_byte_order);
+
+ if (0 != pf->mask[3]) /* has alpha */
+ pf_assert (0 != pf->pixfmt_opposite_alpha);
+ else
+ pf_assert (0 == pf->pixfmt_opposite_alpha);
+
+ for (i = 0; i < N_ELEMENTS (pf->mask); ++i) {
+ pf_assert (pf->n_bits[i] + pf->shr[i] <= 32);
+ pf_assert (pf->mask[i]
+ == (((1 << pf->n_bits[i]) - 1)
+ << (32 - pf->n_bits[i]
+ - pf->shr[i])));
+ }
+ }
+
+ for (pf2 = pf + 1;
+ pf2 < pixel_formats + N_ELEMENTS (pixel_formats);
+ ++pf2) {
+ if (pf->pixfmt == pf2->pixfmt
+ || 0 == strcmp (pf->name, pf2->name)) {
+ error_exit ("Assertion failure: pixfmt "
+ "%u (%s) twice in "
+ "pixel_formats[] table.\n",
+ pf->pixfmt, pf->name);
+ }
+
+ if (0 != pf->v4l2_fourcc
+ && 0 != pf2->v4l2_fourcc
+ && (pf->v4l2_fourcc == pf2->v4l2_fourcc
+ || 0 == strcmp (pf->v4l2_fourcc_name,
+ pf2->v4l2_fourcc_name))) {
+ error_exit ("Assertion failure: V4L2 "
+ "fourcc 0x%08x (%s) twice in "
+ "pixel_formats[] table.\n",
+ pf->v4l2_fourcc,
+ pf->v4l2_fourcc_name);
+ }
+ }
+
+#undef pf_assert
+
+ }
+
+ /* XXX Should also test the converters here. */
+}
+
+static void
+usage (FILE * fp,
+ int argc,
+ char ** argv)
+{
+ fprintf (fp, "\
+V4L2 pixfmt test " VERSION "\n\
+Copyright (C) 2007 Michael H. Schimek\n\
+This program is licensed under GPL 2 or later. NO WARRANTIES.\n\n\
+Usage: %s [options]\n\n\
+Options:\n\
+-d | --device name Video device name [%s]\n\
+-h | --help Print this message\n\
+-m | --mmap Use memory mapped buffers (auto)\n\
+-r | --read Use read() calls (auto)\n\
+",
+ my_name, dev_name);
+}
+
+static const char short_options [] = "d:hmr";
+
+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' },
+ { "usage", no_argument, NULL, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int
+main (int argc,
+ char ** argv)
+{
+ my_name = argv[0];
+
+ self_test ();
+
+ 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_method = IO_METHOD_MMAP;
+ break;
+
+ case 'r':
+ io_method = IO_METHOD_READ;
+ break;
+
+ default:
+ usage (stderr, argc, argv);
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ open_device ();
+
+ init_device ();
+
+ mainloop ();
+
+ exit (EXIT_SUCCESS);
+
+ return 0;
+}