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)) {