I was in the middle of testing a uvideo(4) mmap queue diff when I
noticed that our video(1) tool doesn't support the mmap method to grab
frames.  This diff adds it and also makes mmap the default method.

Some test reports with different uvideo(4) devices would be welcome.
It would be good if you could switch between different resolutions and
also regression test the read method.  E.g.:

        $ video
        $ video -s 800x600
        $ video -s 1600x1200
        $ video -g
        $ video -g -s 1024x768
        ...

Thanks,
Marcus


Index: video.1
===================================================================
RCS file: /cvs/xenocara/app/video/video.1,v
retrieving revision 1.11
diff -u -p -u -p -r1.11 video.1
--- video.1     30 Nov 2014 01:40:26 -0000      1.11
+++ video.1     30 May 2016 07:48:15 -0000
@@ -25,7 +25,7 @@
 .Sh SYNOPSIS
 .Nm
 .Bk -words
-.Op Fl \&Rv
+.Op Fl \&gRv
 .Op Fl a Ar adaptor
 .Op Fl e Ar encoding
 .Op Fl f Ar file
@@ -101,6 +101,11 @@ will be used by default.
 device from which frames will be read.
 The default is
 .Pa /dev/video .
+.It Fl g
+Use
+.Xr read 2
+method to grab frames instead of
+.Xr mmap 2 .
 .It Fl i Ar input
 File from which frames will be read.
 If
Index: video.c
===================================================================
RCS file: /cvs/xenocara/app/video/video.c,v
retrieving revision 1.12
diff -u -p -u -p -r1.12 video.c
--- video.c     23 Oct 2014 07:36:06 -0000      1.12
+++ video.c     30 May 2016 07:48:15 -0000
@@ -20,6 +20,7 @@
 #include <sys/videoio.h>
 #include <sys/time.h>
 #include <sys/limits.h>
+#include <sys/mman.h>
 
 #include <err.h>
 #include <errno.h>
@@ -145,6 +146,9 @@ struct encodings {
 struct video {
        struct xdsp      xdsp;
        struct dev       dev;
+#define MMAP_NUM_BUFS  4
+       uint8_t          mmap_on;
+       void            *mmap_buffer[MMAP_NUM_BUFS];
        uint8_t         *frame_buffer;
        size_t           frame_bufsz;
        uint8_t         *conv_buffer;
@@ -189,8 +193,10 @@ void dev_reset_ctrls(struct video *);
 int parse_size(struct video *);
 int choose_size(struct video *);
 int choose_enc(struct video *);
+int mmap_init(struct video *);
 int setup(struct video *);
 void cleanup(struct video *, int);
+int ioctl_input(struct video *);
 int poll_input(struct video *);
 int grab_frame(struct video *);
 int stream(struct video *);
@@ -206,7 +212,7 @@ extern char *__progname;
 void
 usage(void)
 {
-       fprintf(stderr, "usage: %s [-Rv] "
+       fprintf(stderr, "usage: %s [-gRv] "
            "[-a adaptor] [-e encoding] [-f file] [-i input] [-O output]\n"
            "       %*s [-o output] [-r rate] [-s size]\n", __progname,
            (int)strlen(__progname), "");
@@ -668,10 +674,14 @@ dev_check_caps(struct video *vid)
                warnx("%s is not a capture device", d->path);
                return 0;
        }
-       if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
+       if (!(cap.capabilities & V4L2_CAP_READWRITE) && !vid->mmap_on) {
                warnx("%s does not support read(2)", d->path);
                return 0;
        }
+       if (!(cap.capabilities & V4L2_CAP_STREAMING) && vid->mmap_on) {
+               warnx("%s does not support mmap(2)");
+               return 0;
+       }
        d->buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
        return 1;
@@ -1231,6 +1241,61 @@ choose_enc(struct video *vid)
 }
 
 int
+mmap_init(struct video *vid)
+{
+       struct v4l2_requestbuffers rb;
+       struct v4l2_buffer buf;
+       int i, r, type;
+
+       /* request buffers */
+       rb.count = MMAP_NUM_BUFS;
+       r = ioctl(vid->dev.fd, VIDIOC_REQBUFS, &rb);
+       if (r == -1) {
+               warn("ioctl VIDIOC_REQBUFS");
+               return 0;
+       }
+
+       /* mmap the buffers */
+       for (i = 0; i < MMAP_NUM_BUFS; i++) {
+               buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               buf.memory = V4L2_MEMORY_MMAP;
+               buf.index = i;
+               r = ioctl(vid->dev.fd, VIDIOC_QUERYBUF, &buf);
+               if (r == -1) {
+                       warn("ioctl VIDIOC_QUERYBUF");
+                       return 0;
+               }
+               vid->mmap_buffer[i] = mmap(0, buf.length, PROT_READ,
+                   MAP_SHARED, vid->dev.fd, buf.m.offset);
+               if (vid->mmap_buffer[i] == NULL) {
+                       warn("mmap");
+                       return 0;
+               }
+       }
+
+       /* initial buffer queueing */
+       for (i = 0; i < MMAP_NUM_BUFS; i++) {
+               buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               buf.memory = V4L2_MEMORY_MMAP;
+               buf.index = i;
+               r = ioctl(vid->dev.fd, VIDIOC_QBUF, &buf);
+               if (r == -1) {
+                       warn("ioctl VIDIOC_QBUF");
+                       return 0;
+               }
+       }
+
+       /* start video stream */
+       r = ioctl(vid->dev.fd, VIDIOC_STREAMON, &type);
+       if (r == -1) {
+               warn("ioctl VIDIOC_STREAMON");
+               return 0;
+       }
+
+       return 1;
+}
+
+int
 setup(struct video *vid)
 {
        if (vid->mode & M_IN_FILE) {
@@ -1315,6 +1380,43 @@ setup(struct video *vid)
        if (vid->sz_str && !strcmp(vid->sz_str, "full"))
                resize_window(vid, 1);
 
+       if (vid->mmap_on) {
+               if (!mmap_init(vid))
+                       return 0;
+       }
+
+       return 1;
+}
+
+int
+ioctl_input(struct video *vid)
+{
+       struct v4l2_buffer buf;
+       int r;
+
+       /* dequeue buffer */
+       buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       buf.memory = V4L2_MEMORY_MMAP;
+       r = ioctl(vid->dev.fd, VIDIOC_DQBUF, &buf);
+       if (r == -1) {
+               warn("mmap ioctl VIDIOC_DQBUF");
+               return 0;
+       }
+
+       /* requeue buffer */
+       buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       buf.memory = V4L2_MEMORY_MMAP;
+       r = ioctl(vid->dev.fd, VIDIOC_QBUF, &buf);
+       if (r == -1) {
+               warn("mmap ioctl VIDIOC_QBUF");
+               return 0;
+       }
+
+       /* copy frame buffer */
+       if (buf.length > vid->bpf)
+               return 0;
+       memcpy(vid->frame_buffer, vid->mmap_buffer[buf.index], buf.length);
+
        return 1;
 }
 
@@ -1431,11 +1533,16 @@ stream(struct video *vid)
 
        while (!shutdown) {
                err = 0;
-               ret = poll_input(vid);
+               if (vid->mmap_on) {
+                       if (!(ret = ioctl_input(vid)))
+                               return 0;
+               } else
+                       ret = poll_input(vid);
                if (ret == 1) {
                        if ((vid->mode & M_IN_DEV) ||
                            frames_grabbed - 1 == frames_played) {
-                               ret = grab_frame(vid);
+                               if (!vid->mmap_on)
+                                       ret = grab_frame(vid);
                                if (ret == 1) {
                                        frames_grabbed++;
                                        if (vid->nofps)
@@ -1559,6 +1666,8 @@ stream(struct video *vid)
 __dead void
 cleanup(struct video *vid, int excode)
 {
+       int type;
+
        if (vid->xdsp.xv_image != NULL)
                XFree(vid->xdsp.xv_image);
 
@@ -1574,8 +1683,11 @@ cleanup(struct video *vid, int excode)
        if (vid->xdsp.dpy != NULL)
                XCloseDisplay(vid->xdsp.dpy);
 
-       if (vid->dev.fd >= 0)
+       if (vid->dev.fd >= 0) {
+               if (vid->mmap_on)
+                       (void)ioctl(vid->dev.fd, VIDIOC_STREAMOFF, &type);
                close(vid->dev.fd);
+       }
 
        if (vid->iofile_fd >= 0)
                close(vid->iofile_fd);
@@ -1608,9 +1720,10 @@ main(int argc, char *argv[])
        vid.dev.fd = vid.iofile_fd = -1;
        vid.mode = M_IN_DEV | M_OUT_XV;
        vid.enc = -1;
+       vid.mmap_on = 1; /* mmap method is default */
        wout = 1;
 
-       while ((ch = getopt(argc, argv, "vRa:e:f:i:O:o:r:s:")) != -1) {
+       while ((ch = getopt(argc, argv, "gvRa:e:f:i:O:o:r:s:")) != -1) {
                switch (ch) {
                case 'a':
                        x->cur_adap = strtonum(optarg, 0, 4, &errstr);
@@ -1628,6 +1741,10 @@ main(int argc, char *argv[])
                        break;
                case 'f':
                        snprintf(d->path, sizeof(d->path), optarg);
+                       break;
+               case 'g':
+                       vid.mmap_on = 0;
+                       printf("Using read instead of mmap to grab frames\n");
                        break;
                case 'i':
                        if (vid.mode & (M_IN_FILE | M_OUT_FILE)) {

Reply via email to