1*b7041c07Sderaadt /* $OpenBSD: videotest.c,v 1.6 2021/10/24 21:24:20 deraadt Exp $ */
27aa705deSmglocker
37aa705deSmglocker /*
47aa705deSmglocker * Copyright (c) 2010 Marcus Glocker <mglocker@openbsd.org>
57aa705deSmglocker *
67aa705deSmglocker * Permission to use, copy, modify, and distribute this software for any
77aa705deSmglocker * purpose with or without fee is hereby granted, provided that the above
87aa705deSmglocker * copyright notice and this permission notice appear in all copies.
97aa705deSmglocker *
107aa705deSmglocker * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
117aa705deSmglocker * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127aa705deSmglocker * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137aa705deSmglocker * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147aa705deSmglocker * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157aa705deSmglocker * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167aa705deSmglocker * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177aa705deSmglocker */
187aa705deSmglocker
197aa705deSmglocker /*
207aa705deSmglocker * Regression test program for the video(4) interface.
217aa705deSmglocker *
227aa705deSmglocker * TODO:
237aa705deSmglocker * - Add test for VIDIOC_ENUM_FRAMEINTERVALS ioctl.
247aa705deSmglocker * - Add test for VIDIOC_ENUMINPUT ioctl.
257aa705deSmglocker * - Add test for VIDIOC_S_INPUT ioctl.
267aa705deSmglocker * - Add test for VIDIOC_TRY_FMT ioctl.
277aa705deSmglocker * - Add test for VIDIOC_QUERYCTRL ioctl.
287aa705deSmglocker * - Add test for VIDIOC_G_CTRL ioctl.
297aa705deSmglocker * - Add test for VIDIOC_S_CTRL ioctl.
307aa705deSmglocker */
317aa705deSmglocker
327aa705deSmglocker #include <sys/ioctl.h>
337aa705deSmglocker #include <sys/types.h>
34e644765cSmglocker #include <sys/mman.h>
357aa705deSmglocker #include <sys/videoio.h>
367aa705deSmglocker
377aa705deSmglocker #include <err.h>
387aa705deSmglocker #include <errno.h>
397aa705deSmglocker #include <fcntl.h>
407aa705deSmglocker #include <poll.h>
417aa705deSmglocker #include <unistd.h>
427aa705deSmglocker #include <stdio.h>
437aa705deSmglocker #include <stdlib.h>
447aa705deSmglocker #include <string.h>
457aa705deSmglocker
467aa705deSmglocker /*
477aa705deSmglocker * Defines.
487aa705deSmglocker */
497aa705deSmglocker #define DEV_CHECK_NR 128
507aa705deSmglocker #define DEV_PATH "/dev/"
517aa705deSmglocker
527aa705deSmglocker /*
537aa705deSmglocker * Some devices need a hell of time to initialize and stop (e.g. the
547aa705deSmglocker * Logitech Pro 9000). If we don't give them that time, they will stall
557aa705deSmglocker * at some point in the open / close cycle and will require a cold reset
567aa705deSmglocker * (detach / attach) to operate again.
577aa705deSmglocker */
587aa705deSmglocker #define WAIT_INIT 5 /* seconds */
597aa705deSmglocker #define WAIT_STOP 15 /* seconds */
607aa705deSmglocker
617aa705deSmglocker #define POLL_NO 0
627aa705deSmglocker #define POLL_YES 1
637aa705deSmglocker #define POLL_TIMEOUT 2000 /* milliseconds */
647aa705deSmglocker
657aa705deSmglocker #define ACCESS_READ 0
667aa705deSmglocker #define ACCESS_MMAP 1
677aa705deSmglocker
687aa705deSmglocker #define MMAP_QUEUE_NR 4
697aa705deSmglocker
707aa705deSmglocker /*
717aa705deSmglocker * Prototypes.
727aa705deSmglocker */
737aa705deSmglocker int test_ioctl_querycap(int);
747aa705deSmglocker int test_ioctl_g_fmt(int);
757aa705deSmglocker int test_ioctl_enum_fmt(int);
767aa705deSmglocker int test_ioctl_enum_fsizes(int, uint32_t, int);
777aa705deSmglocker int test_capture(char *, char *, int, int);
787aa705deSmglocker int test_capture_read(int, char *, int, int);
797aa705deSmglocker int test_capture_mmap(int, char *, int, int);
807aa705deSmglocker void jpeg_insert_dht(uint8_t *, int, uint8_t *, int *);
817aa705deSmglocker char *print_pixelformat(uint32_t, int);
827aa705deSmglocker
837aa705deSmglocker /*
847aa705deSmglocker * Structures.
857aa705deSmglocker */
867aa705deSmglocker struct frame_buffer {
877aa705deSmglocker uint8_t *buf;
887aa705deSmglocker int len;
897aa705deSmglocker };
907aa705deSmglocker
917aa705deSmglocker struct sizes {
927aa705deSmglocker uint32_t width;
937aa705deSmglocker uint32_t height;
947aa705deSmglocker };
957aa705deSmglocker
967aa705deSmglocker /*
977aa705deSmglocker * Global variables.
987aa705deSmglocker */
997aa705deSmglocker struct fmt_sizes {
1007aa705deSmglocker uint32_t pixelformat;
1017aa705deSmglocker struct sizes s[32];
1027aa705deSmglocker } dev_fmts[8];
1037aa705deSmglocker
1047aa705deSmglocker /*
1057aa705deSmglocker * Main program.
1067aa705deSmglocker */
1077aa705deSmglocker int
main(void)1087aa705deSmglocker main(void)
1097aa705deSmglocker {
1107aa705deSmglocker int i, fd, r;
1117aa705deSmglocker char dev_name[32], dev_full[32];
1127aa705deSmglocker
1137aa705deSmglocker for (i = 0; i < DEV_CHECK_NR; i++) {
1147aa705deSmglocker /* assemble device name and path */
1157aa705deSmglocker snprintf(dev_name, sizeof(dev_name), "video%d", i);
1167aa705deSmglocker snprintf(dev_full, sizeof(dev_full), "%s%s",
1177aa705deSmglocker DEV_PATH, dev_name);
1187aa705deSmglocker
1197aa705deSmglocker /* open video device */
120*b7041c07Sderaadt fd = open(dev_full, O_RDWR);
1217aa705deSmglocker if (fd == -1) {
1227aa705deSmglocker warn("%s", dev_full);
1237aa705deSmglocker break;
1247aa705deSmglocker }
1257aa705deSmglocker
1267aa705deSmglocker /* run some ioctl tests */
1277aa705deSmglocker r = test_ioctl_querycap(fd);
1287aa705deSmglocker if (r == -1)
1297aa705deSmglocker err(1, "ioctl_querycap");
1307aa705deSmglocker
1317aa705deSmglocker r = test_ioctl_g_fmt(fd);
1327aa705deSmglocker if (r == -1)
1337aa705deSmglocker err(1, "ioctl_g_fmt");
1347aa705deSmglocker
1357aa705deSmglocker r = test_ioctl_enum_fmt(fd);
1367aa705deSmglocker if (r == -1)
1377aa705deSmglocker err(1, "ioctl_enum_fmt");
1387aa705deSmglocker
1397aa705deSmglocker /* close video device */
1407aa705deSmglocker close(fd);
1417aa705deSmglocker
1427aa705deSmglocker /* run frame capture tests */
1437aa705deSmglocker r = test_capture(dev_name, dev_full, ACCESS_READ, POLL_NO);
1447aa705deSmglocker if (r == -1)
1457aa705deSmglocker err(1, "test_capture");
1467aa705deSmglocker
1477aa705deSmglocker r = test_capture(dev_name, dev_full, ACCESS_READ, POLL_YES);
1487aa705deSmglocker if (r == -1)
1497aa705deSmglocker err(1, "test_capture");
1507aa705deSmglocker
1517aa705deSmglocker r = test_capture(dev_name, dev_full, ACCESS_MMAP, POLL_NO);
1527aa705deSmglocker if (r == -1)
1537aa705deSmglocker err(1, "test_capture");
1547aa705deSmglocker
1557aa705deSmglocker r = test_capture(dev_name, dev_full, ACCESS_MMAP, POLL_YES);
1567aa705deSmglocker if (r == -1)
1577aa705deSmglocker err(1, "test_capture");
1587aa705deSmglocker }
1597aa705deSmglocker
1607aa705deSmglocker return (0);
1617aa705deSmglocker }
1627aa705deSmglocker
1637aa705deSmglocker int
test_ioctl_querycap(int fd)1647aa705deSmglocker test_ioctl_querycap(int fd)
1657aa705deSmglocker {
1667aa705deSmglocker int r;
1677aa705deSmglocker struct v4l2_capability caps;
1687aa705deSmglocker
1697aa705deSmglocker printf("[ Calling VIDIOC_QUERYCAP ioctl ]\n\n");
1707aa705deSmglocker
1717aa705deSmglocker memset(&caps, 0, sizeof(struct v4l2_capability));
1727aa705deSmglocker r = ioctl(fd, VIDIOC_QUERYCAP, &caps);
1737aa705deSmglocker if (r == -1)
1747aa705deSmglocker return (-1);
1757aa705deSmglocker
1767aa705deSmglocker printf("Driver : %s\n", caps.driver);
1777aa705deSmglocker printf("Card : %s\n", caps.card);
1787aa705deSmglocker printf("Bus Info : %s\n", caps.bus_info);
1797aa705deSmglocker printf("Version : %d\n", caps.version);
1807aa705deSmglocker printf("Capabilities : ");
1817aa705deSmglocker if (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE)
1827aa705deSmglocker printf("CAPTURE ");
1837aa705deSmglocker if (caps.capabilities & V4L2_CAP_STREAMING)
1847aa705deSmglocker printf("STREAMING ");
1857aa705deSmglocker if (caps.capabilities & V4L2_CAP_READWRITE)
1867aa705deSmglocker printf("READWRITE ");
1877aa705deSmglocker printf("\n");
1887aa705deSmglocker
1897aa705deSmglocker printf("\n");
1907aa705deSmglocker
1917aa705deSmglocker return (0);
1927aa705deSmglocker }
1937aa705deSmglocker
1947aa705deSmglocker int
test_ioctl_g_fmt(int fd)1957aa705deSmglocker test_ioctl_g_fmt(int fd)
1967aa705deSmglocker {
1977aa705deSmglocker int r;
1987aa705deSmglocker struct v4l2_format fmt;
1997aa705deSmglocker
2007aa705deSmglocker printf("[ Calling VIDIOC_G_FMT ioctl ]\n\n");
2017aa705deSmglocker
2027aa705deSmglocker memset(&fmt, 0, sizeof(struct v4l2_format));
2037aa705deSmglocker fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2047aa705deSmglocker r = ioctl(fd, VIDIOC_G_FMT, &fmt);
2057aa705deSmglocker if (r == -1)
2067aa705deSmglocker return (r);
2077aa705deSmglocker
2087aa705deSmglocker printf("Current format : %s\n",
2097aa705deSmglocker print_pixelformat(fmt.fmt.pix.pixelformat, 0));
2107aa705deSmglocker printf("Current width : %u pixels\n", fmt.fmt.pix.width);
2117aa705deSmglocker printf("Current height : %u pixels\n", fmt.fmt.pix.height);
2127aa705deSmglocker printf("Current max. framesize : %u bytes\n", fmt.fmt.pix.sizeimage);
2137aa705deSmglocker
2147aa705deSmglocker printf("\n");
2157aa705deSmglocker
2167aa705deSmglocker return (0);
2177aa705deSmglocker }
2187aa705deSmglocker
2197aa705deSmglocker int
test_ioctl_enum_fmt(int fd)2207aa705deSmglocker test_ioctl_enum_fmt(int fd)
2217aa705deSmglocker {
2227aa705deSmglocker int r;
2237aa705deSmglocker struct v4l2_fmtdesc fmtdesc;
2247aa705deSmglocker
2257aa705deSmglocker printf("[ Calling VIDIOC_ENUM_FMT|VIDIOC_ENUM_FRAMESIZES ioctl ]\n\n");
2267aa705deSmglocker
2277aa705deSmglocker memset(&fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
2287aa705deSmglocker fmtdesc.index = 0;
2297aa705deSmglocker fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2307aa705deSmglocker while ((r = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0) {
2317aa705deSmglocker printf("Pixelformat '%s' ", fmtdesc.description);
2327aa705deSmglocker
2337aa705deSmglocker (void)test_ioctl_enum_fsizes(fd, fmtdesc.pixelformat,
2347aa705deSmglocker fmtdesc.index);
2357aa705deSmglocker
2367aa705deSmglocker fmtdesc.index++;
2377aa705deSmglocker }
2387aa705deSmglocker if (errno != EINVAL)
2397aa705deSmglocker return (-1);
2407aa705deSmglocker
2417aa705deSmglocker return (0);
2427aa705deSmglocker }
2437aa705deSmglocker
2447aa705deSmglocker int
test_ioctl_enum_fsizes(int fd,uint32_t pixelformat,int index)2457aa705deSmglocker test_ioctl_enum_fsizes(int fd, uint32_t pixelformat, int index)
2467aa705deSmglocker {
2477aa705deSmglocker int r;
2487aa705deSmglocker struct v4l2_frmsizeenum fsizes;
2497aa705deSmglocker
2507aa705deSmglocker printf("supports following sizes:\n");
2517aa705deSmglocker
2527aa705deSmglocker memset(&fsizes, 0, sizeof(struct v4l2_frmsizeenum));
2537aa705deSmglocker fsizes.index = 0;
2547aa705deSmglocker fsizes.pixel_format = pixelformat;
2557aa705deSmglocker while ((r = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsizes)) == 0) {
2567aa705deSmglocker if (fsizes.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
2577aa705deSmglocker printf("discrete width = %u, height = %u\n",
2587aa705deSmglocker fsizes.discrete.width,
2597aa705deSmglocker fsizes.discrete.height);
2607aa705deSmglocker
2617aa705deSmglocker /* save format and sizes for later use */
2627aa705deSmglocker dev_fmts[index].pixelformat = pixelformat;
2637aa705deSmglocker dev_fmts[index].s[fsizes.index].width =
2647aa705deSmglocker fsizes.discrete.width;
2657aa705deSmglocker dev_fmts[index].s[fsizes.index].height =
2667aa705deSmglocker fsizes.discrete.height;
2677aa705deSmglocker }
2687aa705deSmglocker
2697aa705deSmglocker fsizes.index++;
2707aa705deSmglocker }
2717aa705deSmglocker if (errno != EINVAL)
2727aa705deSmglocker return (-1);
2737aa705deSmglocker
2747aa705deSmglocker printf("\n");
2757aa705deSmglocker
2767aa705deSmglocker return (0);
2777aa705deSmglocker }
2787aa705deSmglocker
2797aa705deSmglocker int
test_capture(char * dev_name,char * dev_full,int access,int use_poll)2807aa705deSmglocker test_capture(char *dev_name, char *dev_full, int access, int use_poll)
2817aa705deSmglocker {
2827aa705deSmglocker ssize_t n1, n2;
2837aa705deSmglocker int fd1, fd2;
2847aa705deSmglocker int i, j, r;
2857aa705deSmglocker int buf_size, img_size, img_len;
2867aa705deSmglocker char filename[64];
2877aa705deSmglocker uint8_t *buf, *img;
2887aa705deSmglocker uint32_t last_pixelformat;
2897aa705deSmglocker struct v4l2_format fmt;
2907aa705deSmglocker
2917aa705deSmglocker fd1 = last_pixelformat = n1 = 0;
2927aa705deSmglocker img = buf = NULL;
2937aa705deSmglocker
2947aa705deSmglocker printf("[ Testing %s access type %s]\n\n",
2957aa705deSmglocker access == ACCESS_READ ? "READ" : "MMAP",
2967aa705deSmglocker use_poll ? "with poll " : "");
2977aa705deSmglocker
2987aa705deSmglocker for (i = 0; i < 8; i++) {
2997aa705deSmglocker /* did we reach end of formats? */
3007aa705deSmglocker if (dev_fmts[i].pixelformat == 0)
3017aa705deSmglocker return (0);
3027aa705deSmglocker
3037aa705deSmglocker /* some devices have duplicate format descriptors */
3047aa705deSmglocker if (last_pixelformat == dev_fmts[i].pixelformat)
3057aa705deSmglocker continue;
3067aa705deSmglocker else
3077aa705deSmglocker last_pixelformat = dev_fmts[i].pixelformat;
3087aa705deSmglocker
3097aa705deSmglocker for (j = 0; j < 32; j++) {
3107aa705deSmglocker /* did we reach end of sizes? */
3117aa705deSmglocker if (dev_fmts[i].s[j].width == 0) {
3127aa705deSmglocker free(buf);
3137aa705deSmglocker buf = NULL;
3147aa705deSmglocker free(img);
3157aa705deSmglocker img = NULL;
3167aa705deSmglocker break;
3177aa705deSmglocker }
3187aa705deSmglocker
3197aa705deSmglocker /* open device */
320*b7041c07Sderaadt fd1 = open(dev_full, O_RDWR);
3217aa705deSmglocker if (fd1 == -1)
3227aa705deSmglocker err(1, "open");
3237aa705deSmglocker sleep(WAIT_INIT); /* let device initialize */
3247aa705deSmglocker
3257aa705deSmglocker /* set format */
3267aa705deSmglocker printf("Set format to %s-%ux%u ... ",
3277aa705deSmglocker print_pixelformat(dev_fmts[i].pixelformat, 0),
3287aa705deSmglocker dev_fmts[i].s[j].width,
3297aa705deSmglocker dev_fmts[i].s[j].height);
3307aa705deSmglocker
3317aa705deSmglocker memset(&fmt, 0, sizeof(struct v4l2_format));
3327aa705deSmglocker fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3337aa705deSmglocker fmt.fmt.pix.width = dev_fmts[i].s[j].width;
3347aa705deSmglocker fmt.fmt.pix.height = dev_fmts[i].s[j].height;
3357aa705deSmglocker fmt.fmt.pix.pixelformat = dev_fmts[i].pixelformat;
3367aa705deSmglocker fmt.fmt.pix.field = V4L2_FIELD_ANY;
3377aa705deSmglocker r = ioctl(fd1, VIDIOC_S_FMT, &fmt);
3387aa705deSmglocker if (r == -1)
3397aa705deSmglocker goto error;
3407aa705deSmglocker
3417aa705deSmglocker printf("results in %u bytes max. framesize.\n",
3427aa705deSmglocker fmt.fmt.pix.sizeimage);
3437aa705deSmglocker
3447aa705deSmglocker /* allocate frame and image buffer */
3457aa705deSmglocker free(buf);
3467aa705deSmglocker buf = NULL;
3477aa705deSmglocker free(img);
3487aa705deSmglocker img = NULL;
3497aa705deSmglocker
3507aa705deSmglocker buf_size = fmt.fmt.pix.sizeimage;
351e3a3b5caStedu buf = calloc(1, buf_size);
3527aa705deSmglocker if (buf == NULL)
3537aa705deSmglocker goto error;
3547aa705deSmglocker
3557aa705deSmglocker img_size = fmt.fmt.pix.sizeimage + 1024;
356e3a3b5caStedu img = calloc(1, img_size);
3577aa705deSmglocker if (img == NULL)
3587aa705deSmglocker goto error;
3597aa705deSmglocker
3607aa705deSmglocker /* get frame */
3617aa705deSmglocker if (access == ACCESS_READ)
3627aa705deSmglocker n1 = test_capture_read(fd1, buf, buf_size,
3637aa705deSmglocker use_poll);
3647aa705deSmglocker else
3657aa705deSmglocker n1 = test_capture_mmap(fd1, buf, buf_size,
3667aa705deSmglocker use_poll);
3677aa705deSmglocker if (n1 == -1)
3687aa705deSmglocker goto error;
3697aa705deSmglocker
3707aa705deSmglocker /*
3717aa705deSmglocker * Convert frame to JPEG image.
3727aa705deSmglocker *
3737aa705deSmglocker * TODO:
3747aa705deSmglocker * For now just MJPEG convertion is supported.
3757aa705deSmglocker */
3767aa705deSmglocker snprintf(filename, sizeof(filename),
3777aa705deSmglocker "%s_img_%s_%ux%u%s%s",
3787aa705deSmglocker dev_name,
3797aa705deSmglocker print_pixelformat(dev_fmts[i].pixelformat, 1),
3807aa705deSmglocker fmt.fmt.pix.width,
3817aa705deSmglocker fmt.fmt.pix.height,
3827aa705deSmglocker access == ACCESS_READ ? "_read" : "_mmap",
3837aa705deSmglocker use_poll ? "_poll" : "");
3847aa705deSmglocker
3857aa705deSmglocker switch (dev_fmts[i].pixelformat) {
3867aa705deSmglocker case V4L2_PIX_FMT_MJPEG:
3877aa705deSmglocker printf("Converting MJPEG to JPEG.\n");
3887aa705deSmglocker
3897aa705deSmglocker /* insert dynamic huffmann table to mjpeg */
3907aa705deSmglocker jpeg_insert_dht(buf, n1, img, &img_len);
3917aa705deSmglocker
3925bcc9d93Smglocker strlcat(filename, ".jpg", sizeof(filename));
3937aa705deSmglocker break;
3947aa705deSmglocker case V4L2_PIX_FMT_YUYV:
3957aa705deSmglocker printf("Convertion for YUYV not supported!\n");
3967aa705deSmglocker
3977aa705deSmglocker img_len = n1;
3987aa705deSmglocker memcpy(img, buf, img_len);
3997aa705deSmglocker
4005bcc9d93Smglocker strlcat(filename, ".raw", sizeof(filename));
4017aa705deSmglocker break;
4027aa705deSmglocker default:
4037aa705deSmglocker break;
4047aa705deSmglocker }
4057aa705deSmglocker
4067aa705deSmglocker /* write image file */
4077aa705deSmglocker fd2 = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
4087aa705deSmglocker if (fd2 == -1)
4097aa705deSmglocker goto error;
4107aa705deSmglocker n2 = write(fd2, img, img_len);
4117aa705deSmglocker if (n2 == -1)
4127aa705deSmglocker goto error;
4137aa705deSmglocker printf("Saving image to '%s'.\n\n", filename);
4147aa705deSmglocker close(fd2);
4157aa705deSmglocker
4167aa705deSmglocker /* shutdown device */
4177aa705deSmglocker close(fd1);
4187aa705deSmglocker sleep(WAIT_STOP); /* let device stop */
4197aa705deSmglocker }
4207aa705deSmglocker }
4217aa705deSmglocker error:
4227aa705deSmglocker free(buf);
4237aa705deSmglocker buf = NULL;
4247aa705deSmglocker free(img);
4257aa705deSmglocker img = NULL;
4267aa705deSmglocker close(fd1);
4277aa705deSmglocker
4287aa705deSmglocker printf("\n");
4297aa705deSmglocker
4307aa705deSmglocker return (-1);
4317aa705deSmglocker }
4327aa705deSmglocker
4337aa705deSmglocker int
test_capture_read(int fd,char * buf,int buf_size,int use_poll)4347aa705deSmglocker test_capture_read(int fd, char *buf, int buf_size, int use_poll)
4357aa705deSmglocker {
4367aa705deSmglocker ssize_t n1;
4377aa705deSmglocker int i, r;
4387aa705deSmglocker struct pollfd pfds[1];
4397aa705deSmglocker
4407aa705deSmglocker n1 = 0;
4417aa705deSmglocker
4427aa705deSmglocker /*
4437aa705deSmglocker * Read frame data.
4447aa705deSmglocker *
4457aa705deSmglocker * Some devices need a while until they start
4467aa705deSmglocker * sending a sane image. Therefore skip the
4477aa705deSmglocker * first few frames.
4487aa705deSmglocker */
4497aa705deSmglocker printf("Reading frame data ... ");
4507aa705deSmglocker
4517aa705deSmglocker for (i = 0; i < 3; i++) {
4527aa705deSmglocker if (use_poll) {
4537aa705deSmglocker pfds[0].fd = fd;
4547aa705deSmglocker pfds[0].events = POLLIN;
4557aa705deSmglocker
4567aa705deSmglocker r = poll(pfds, 1, POLL_TIMEOUT);
4577aa705deSmglocker if (r == -1)
4587aa705deSmglocker return (-1);
4597aa705deSmglocker if (r == 0) {
4607aa705deSmglocker printf("poll timeout (%d seconds)!\n",
4617aa705deSmglocker POLL_TIMEOUT / 1000);
4627aa705deSmglocker continue;
4637aa705deSmglocker }
4647aa705deSmglocker }
4657aa705deSmglocker
4667aa705deSmglocker n1 = read(fd, buf, buf_size);
4677aa705deSmglocker if (n1 == -1)
4687aa705deSmglocker return (-1);
4697aa705deSmglocker }
4707aa705deSmglocker printf("%ld bytes read.\n", n1);
4717aa705deSmglocker
4727aa705deSmglocker return (n1);
4737aa705deSmglocker }
4747aa705deSmglocker
4757aa705deSmglocker int
test_capture_mmap(int fd,char * buf,int buf_size,int use_poll)4767aa705deSmglocker test_capture_mmap(int fd, char *buf, int buf_size, int use_poll)
4777aa705deSmglocker {
4787aa705deSmglocker int i, r, type;
4797aa705deSmglocker struct v4l2_requestbuffers reqbufs;
4807aa705deSmglocker struct v4l2_buffer buffer;
4817aa705deSmglocker struct frame_buffer fbuffer[MMAP_QUEUE_NR];
4827aa705deSmglocker struct pollfd pfds[1];
4837aa705deSmglocker
4847aa705deSmglocker /* request buffers */
4857aa705deSmglocker memset(&reqbufs, 0, sizeof(struct v4l2_requestbuffers));
4867aa705deSmglocker reqbufs.count = MMAP_QUEUE_NR;
4877aa705deSmglocker reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
4887aa705deSmglocker reqbufs.memory = V4L2_MEMORY_MMAP;
4897aa705deSmglocker r = ioctl(fd, VIDIOC_REQBUFS, &reqbufs);
4907aa705deSmglocker if (r == -1)
4917aa705deSmglocker return (-1);
4927aa705deSmglocker
4937aa705deSmglocker /* map the buffers */
4947aa705deSmglocker for (i = 0; i < MMAP_QUEUE_NR; i++) {
4957aa705deSmglocker memset(&buffer, 0, sizeof(struct v4l2_buffer));
4967aa705deSmglocker buffer.index = i;
4977aa705deSmglocker buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
4987aa705deSmglocker buffer.memory = V4L2_MEMORY_MMAP;
4997aa705deSmglocker r = ioctl(fd, VIDIOC_QUERYBUF, &buffer);
5007aa705deSmglocker if (r == -1)
5017aa705deSmglocker return (-1);
5027aa705deSmglocker
5037aa705deSmglocker fbuffer[i].buf =
5047aa705deSmglocker mmap(0, buffer.length, PROT_READ, MAP_SHARED, fd,
5057aa705deSmglocker buffer.m.offset);
5067aa705deSmglocker if (fbuffer[i].buf == MAP_FAILED)
5077aa705deSmglocker return (-1);
5087aa705deSmglocker fbuffer[i].len = buffer.length;
5097aa705deSmglocker }
5107aa705deSmglocker
5117aa705deSmglocker /* queue the buffers */
5127aa705deSmglocker for (i = 0; i < MMAP_QUEUE_NR; i++) {
5137aa705deSmglocker memset(&buffer, 0, sizeof(struct v4l2_buffer));
5147aa705deSmglocker buffer.index = i;
5157aa705deSmglocker buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5167aa705deSmglocker buffer.memory = V4L2_MEMORY_MMAP;
5177aa705deSmglocker r = ioctl(fd, VIDIOC_QBUF, &buffer);
5187aa705deSmglocker if (r == -1)
5197aa705deSmglocker return (-1);
5207aa705deSmglocker }
5217aa705deSmglocker
5227aa705deSmglocker /* turn on stream */
5237aa705deSmglocker type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5247aa705deSmglocker r = ioctl(fd, VIDIOC_STREAMON, &type);
5257aa705deSmglocker if (r == -1)
5267aa705deSmglocker return (-1);
5277aa705deSmglocker
5287aa705deSmglocker /* dequeue buffers */
5297aa705deSmglocker printf("Dequeue frame data ... ");
5307aa705deSmglocker
5317aa705deSmglocker for (i = 0; i < MMAP_QUEUE_NR; i++) {
5327aa705deSmglocker if (use_poll) {
5337aa705deSmglocker pfds[0].fd = fd;
5347aa705deSmglocker pfds[0].events = POLLIN;
5357aa705deSmglocker
5367aa705deSmglocker r = poll(pfds, 1, POLL_TIMEOUT);
5377aa705deSmglocker if (r == -1)
5387aa705deSmglocker return (-1);
5397aa705deSmglocker if (r == 0) {
5407aa705deSmglocker printf("poll timeout (%d seconds)!\n",
5417aa705deSmglocker POLL_TIMEOUT / 1000);
5427aa705deSmglocker return (-1);
5437aa705deSmglocker }
5447aa705deSmglocker }
5457aa705deSmglocker
5467aa705deSmglocker memset(&buffer, 0, sizeof(struct v4l2_buffer));
5477aa705deSmglocker buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5487aa705deSmglocker buffer.memory = V4L2_MEMORY_MMAP;
5497aa705deSmglocker r = ioctl(fd, VIDIOC_DQBUF, &buffer);
5507aa705deSmglocker if (r == -1)
5517aa705deSmglocker return (-1);
5527aa705deSmglocker }
5537aa705deSmglocker printf("%d bytes dequeued.\n", buffer.bytesused);
5547aa705deSmglocker memcpy(buf, fbuffer[buffer.index].buf, buffer.bytesused);
5557aa705deSmglocker
5567aa705deSmglocker /* turn off stream */
5577aa705deSmglocker type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5587aa705deSmglocker r = ioctl(fd, VIDIOC_STREAMOFF, &type);
5597aa705deSmglocker if (r == -1)
5607aa705deSmglocker return (-1);
5617aa705deSmglocker
5627aa705deSmglocker /* unmap buffers */
5637aa705deSmglocker for (i = 0; i < MMAP_QUEUE_NR; i++) {
5647aa705deSmglocker r = munmap(fbuffer[i].buf, fbuffer[i].len);
5657aa705deSmglocker if (r == -1)
5667aa705deSmglocker return (-1);
5677aa705deSmglocker }
5687aa705deSmglocker
5697aa705deSmglocker return (buffer.bytesused);
5707aa705deSmglocker }
5717aa705deSmglocker
5727aa705deSmglocker void
jpeg_insert_dht(uint8_t * src,int src_len,uint8_t * dst,int * dst_len)5737aa705deSmglocker jpeg_insert_dht(uint8_t *src, int src_len, uint8_t *dst, int *dst_len)
5747aa705deSmglocker {
5757aa705deSmglocker int i;
5767aa705deSmglocker uint8_t *p;
5777aa705deSmglocker
5787aa705deSmglocker static unsigned char dht[] = {
5797aa705deSmglocker 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01,
5807aa705deSmglocker 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5817aa705deSmglocker 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
5827aa705deSmglocker 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
5837aa705deSmglocker 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
5847aa705deSmglocker 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
5857aa705deSmglocker 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32,
5867aa705deSmglocker 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52,
5877aa705deSmglocker 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
5887aa705deSmglocker 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
5897aa705deSmglocker 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
5907aa705deSmglocker 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57,
5917aa705deSmglocker 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
5927aa705deSmglocker 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83,
5937aa705deSmglocker 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
5947aa705deSmglocker 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
5957aa705deSmglocker 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
5967aa705deSmglocker 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
5977aa705deSmglocker 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
5987aa705deSmglocker 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8,
5997aa705deSmglocker 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
6007aa705deSmglocker 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
6017aa705deSmglocker 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6027aa705deSmglocker 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
6037aa705deSmglocker 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
6047aa705deSmglocker 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
6057aa705deSmglocker 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
6067aa705deSmglocker 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
6077aa705deSmglocker 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
6087aa705deSmglocker 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25,
6097aa705deSmglocker 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a,
6107aa705deSmglocker 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
6117aa705deSmglocker 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
6127aa705deSmglocker 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
6137aa705deSmglocker 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83,
6147aa705deSmglocker 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
6157aa705deSmglocker 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
6167aa705deSmglocker 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
6177aa705deSmglocker 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
6187aa705deSmglocker 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
6197aa705deSmglocker 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
6207aa705deSmglocker 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
6217aa705deSmglocker };
6227aa705deSmglocker
6237aa705deSmglocker p = src;
6247aa705deSmglocker for (i = 0; i < src_len; i++, p++) {
6257aa705deSmglocker if (*p != 0xff)
6267aa705deSmglocker continue;
6277aa705deSmglocker
6287aa705deSmglocker p++;
6297aa705deSmglocker if (*p == 0xda)
6307aa705deSmglocker break;
6317aa705deSmglocker else
6327aa705deSmglocker i++;
6337aa705deSmglocker }
6347aa705deSmglocker
6357aa705deSmglocker memcpy(dst, src, i);
6367aa705deSmglocker dst += i;
6377aa705deSmglocker memcpy(dst, dht, sizeof(dht));
6387aa705deSmglocker dst += sizeof(dht);
6397aa705deSmglocker src += i;
6407aa705deSmglocker memcpy(dst, src, src_len - i);
6417aa705deSmglocker
6427aa705deSmglocker *dst_len = src_len + sizeof(dht);
6437aa705deSmglocker }
6447aa705deSmglocker
6457aa705deSmglocker char *
print_pixelformat(uint32_t pixelformat,int lowercase)6467aa705deSmglocker print_pixelformat(uint32_t pixelformat, int lowercase)
6477aa705deSmglocker {
6487aa705deSmglocker static char pformat[8];
6497aa705deSmglocker
6507aa705deSmglocker memset(pformat, 0, sizeof(pformat));
6517aa705deSmglocker
6527aa705deSmglocker switch (pixelformat) {
6537aa705deSmglocker case V4L2_PIX_FMT_MJPEG:
6547aa705deSmglocker if (lowercase)
6557aa705deSmglocker memcpy(pformat, "mjpeg", 5);
6567aa705deSmglocker else
6577aa705deSmglocker memcpy(pformat, "MJPEG", 5);
6587aa705deSmglocker break;
6597aa705deSmglocker case V4L2_PIX_FMT_YUYV:
6607aa705deSmglocker if (lowercase)
6617aa705deSmglocker memcpy(pformat, "yuyv", 4);
6627aa705deSmglocker else
6637aa705deSmglocker memcpy(pformat, "YUYV", 4);
6647aa705deSmglocker break;
6657aa705deSmglocker default:
6667aa705deSmglocker memcpy(pformat, "unknown", 7);
6677aa705deSmglocker break;
6687aa705deSmglocker }
6697aa705deSmglocker
6707aa705deSmglocker return (pformat);
6717aa705deSmglocker }
672