On Mon, May 30, 2016 at 10:49:10AM +0200, Theo Buehler wrote:

> On Mon, May 30, 2016 at 10:02:23AM +0200, Marcus Glocker wrote:
> > 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
> > 
> 
> A quick test of the built-in camera of my T420, shows no regression when
> directly compared to output of video(1) without your patch.  I tested
> each of the supported frame sizes and rates for about 10 seconds both
> with and without -g.

Thanks for testing!  Attached an updated diff after more feedback from
Patrick Keshishian; Fix frame buffer processing order to dequeue,
process buffer, requeue to avoid a potential buffer overwriting by
driver before we can copy away the buffer.  And added munmap(2) once done.


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 12:24:06 -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 12:24:06 -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,11 @@ 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 mmap_stop(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 +213,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 +675,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 +1242,85 @@ 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
+mmap_stop(struct video *vid)
+{
+       int i, r, type;
+
+       /* stop video stream */
+       r = ioctl(vid->dev.fd, VIDIOC_STREAMOFF, &type);
+       if (r == -1) {
+               warn("ioctl STREAMOFF");
+               return 0;
+       }
+
+       /* unmap the buffers */
+       for (i = 0; i < MMAP_NUM_BUFS; i++) {
+               r = munmap(vid->mmap_buffer[i], vid->bpf);
+               if (r == -1) {
+                       warn("munmap");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+int
 setup(struct video *vid)
 {
        if (vid->mode & M_IN_FILE) {
@@ -1315,6 +1405,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;
+       }
+
+       /* copy frame buffer */
+       if (buf.length > vid->bpf)
+               return 0;
+       memcpy(vid->frame_buffer, vid->mmap_buffer[buf.index], buf.length);
+
+       /* 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;
+       }
+
        return 1;
 }
 
@@ -1431,11 +1558,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 +1691,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 +1708,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)
+                       mmap_stop(vid);
                close(vid->dev.fd);
+       }
 
        if (vid->iofile_fd >= 0)
                close(vid->iofile_fd);
@@ -1608,9 +1745,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 +1766,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