On Mon, May 30, 2016 at 02:29:08PM +0200, Marcus Glocker wrote:
> 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.
> 

The same tests went fine with these changes.  Diff reads ok to me, it's
really easy to follow what is happening.

Very minor comments inline.

> 
> 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)");

This should be:
                warnx("%s does not support read(2)", d->path);

> +             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");

Why "mmap ioctl" here?

> +             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");

and here?

> +             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");

Seems odd to print this by default, especially since the program is
rather silent.  You invoke the command with -g, so you requested the
read method, you don't need to be told.  How about only printing it
when -v was given?

>                       break;
>               case 'i':
>                       if (vid.mode & (M_IN_FILE | M_OUT_FILE)) {
> 

Reply via email to