diff options
author | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-06-20 05:30:55 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-06-20 05:30:55 -0300 |
commit | 4eb13325ec2fde1a2c4516c2fbb4a33f5b9162f2 (patch) | |
tree | 0bb310b3d0197c01dcef61eff679ac549f2218d9 /v4l2-apps/test/pixfmt-test.c | |
parent | bd150f373b325df62dd8e55a4c188874be6056f5 (diff) | |
download | mediapointer-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.c | 1780 |
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; +} |