v4l2-ctl: add streaming between two video devices using dmabuf
authorHans Verkuil <hans.verkuil@cisco.com>
Mon, 24 Feb 2014 14:57:42 +0000 (15:57 +0100)
committerHans Verkuil <hans.verkuil@cisco.com>
Mon, 24 Feb 2014 15:41:02 +0000 (16:41 +0100)
Implement support to streaming from one device to another, either using
mmap -> dmabuf, dmabuf -> mmap or userptr -> userptr. In all three
cases there is no need to copy from one buffer to another.

Eventually all combinations should be possible, but that requires more
work (a copy function) and the code could use some serious refactoring
before doing that.

The basic idea is to specify the video output device using --out-device.
And if you want one of the two sides to use dmabuf for streaming, then
specify --stream(-out)-dmabuf:

v4l2-ctl -d /dev/video0 -e /dev/video1 --stream-mmap=3 --stream-out-dmabuf

It will try to export the dmabuf file descriptors for 3 (the number given
to --stream-mmap) buffers and use those for the output stream buffers.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
utils/v4l2-ctl/v4l2-ctl-common.cpp
utils/v4l2-ctl/v4l2-ctl-streaming.cpp
utils/v4l2-ctl/v4l2-ctl.cpp
utils/v4l2-ctl/v4l2-ctl.h

index fe570b0..055a92d 100644 (file)
@@ -59,6 +59,9 @@ void common_usage(void)
               "  -D, --info         show driver info [VIDIOC_QUERYCAP]\n"
               "  -d, --device=<dev> use device <dev> instead of /dev/video0\n"
               "                     if <dev> starts with a digit, then /dev/video<dev> is used\n"
+              "  -e, --out-device=<dev> use device <dev> for output streams instead of the\n"
+              "                     default device as set with --device\n"
+              "                     if <dev> starts with a digit, then /dev/video<dev> is used\n"
               "  -h, --help         display this help message\n"
               "  --help-all         all options\n"
               "  --help-io          input/output options\n"
index 70aaebf..d920b28 100644 (file)
@@ -57,6 +57,8 @@ void streaming_usage(void)
               "  --stream-user=<count>\n"
               "                     capture video using user pointers [VIDIOC_(D)QBUF]\n"
               "                     count: the number of buffers to allocate. The default is 3.\n"
+              "  --stream-dmabuf    capture video using dmabuf [VIDIOC_(D)QBUF]\n"
+              "                     Requires a corresponding --stream-out-mmap option.\n"
               "  --stream-from=<file> stream from this file. The default is to generate a pattern.\n"
               "                     If <file> is '-', then the data is read from stdin.\n"
               "  --stream-loop      loop when the end of the file we are streaming from is reached.\n"
@@ -69,6 +71,9 @@ void streaming_usage(void)
               "  --stream-out-user=<count>\n"
               "                     output video using user pointers [VIDIOC_(D)QBUF]\n"
               "                     count: the number of buffers to allocate. The default is 3.\n"
+              "  --stream-out-dmabuf\n"
+              "                     output video using dmabuf [VIDIOC_(D)QBUF]\n"
+              "                     Requires a corresponding --stream-mmap option.\n"
               "  --list-buffers     list all video buffers [VIDIOC_QUERYBUF]\n"
               "  --list-buffers-out list all video output buffers [VIDIOC_QUERYBUF]\n"
               "  --list-buffers-vbi list all VBI buffers [VIDIOC_QUERYBUF]\n"
@@ -243,16 +248,39 @@ void streaming_cmd(int ch, char *optarg)
 
 class buffers {
 public:
-       buffers(bool is_output, bool is_mmap)
+       buffers(bool is_output)
        {
                type = is_output ? vidout_buftype : vidcap_buftype;
-               memory = is_mmap ? V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
-               if (is_output)
-                       is_mplane = capabilities & (V4L2_CAP_VIDEO_M2M_MPLANE |
-                                                   V4L2_CAP_VIDEO_OUTPUT_MPLANE);
-               else
+               if (is_output) {
+                       if (options[OptStreamOutMmap])
+                               memory = V4L2_MEMORY_MMAP;
+                       else if (options[OptStreamOutUser])
+                               memory = V4L2_MEMORY_USERPTR;
+                       else if (options[OptStreamOutDmaBuf])
+                               memory = V4L2_MEMORY_DMABUF;
+                       is_mplane = out_capabilities & (V4L2_CAP_VIDEO_M2M_MPLANE |
+                                                       V4L2_CAP_VIDEO_OUTPUT_MPLANE);
+               } else {
+                       if (options[OptStreamMmap])
+                               memory = V4L2_MEMORY_MMAP;
+                       else if (options[OptStreamUser])
+                               memory = V4L2_MEMORY_USERPTR;
+                       else if (options[OptStreamDmaBuf])
+                               memory = V4L2_MEMORY_DMABUF;
                        is_mplane = capabilities & (V4L2_CAP_VIDEO_M2M_MPLANE |
                                                    V4L2_CAP_VIDEO_CAPTURE_MPLANE);
+               }
+               for (int i = 0; i < VIDEO_MAX_FRAME; i++)
+                       for (int p = 0; p < VIDEO_MAX_PLANES; p++)
+                               fds[i][p] = -1;
+               num_planes = is_mplane ? 0 : 1;
+       }
+       ~buffers()
+       {
+               for (int i = 0; i < VIDEO_MAX_FRAME; i++)
+                       for (int p = 0; p < VIDEO_MAX_PLANES; p++)
+                               if (fds[i][p] != -1)
+                                       close(fds[i][p]);
        }
 
 public:
@@ -263,6 +291,7 @@ public:
        unsigned num_planes;
        struct v4l2_plane planes[VIDEO_MAX_FRAME][VIDEO_MAX_PLANES];
        void *bufs[VIDEO_MAX_FRAME][VIDEO_MAX_PLANES];
+       int fds[VIDEO_MAX_FRAME][VIDEO_MAX_PLANES];
 
        int reqbufs(int fd, unsigned buf_count)
        {
@@ -276,8 +305,46 @@ public:
                err = doioctl(fd, VIDIOC_REQBUFS, &reqbufs);
                if (err >= 0)
                        bcount = reqbufs.count;
+               if (is_mplane) {
+                       struct v4l2_plane planes[VIDEO_MAX_PLANES];
+                       struct v4l2_buffer buf;
+
+                       memset(&buf, 0, sizeof(buf));
+                       memset(planes, 0, sizeof(planes));
+                       buf.type = type;
+                       buf.memory = memory;
+                       buf.m.planes = planes;
+                       buf.length = VIDEO_MAX_PLANES;
+                       err = doioctl(fd, VIDIOC_QUERYBUF, &buf);
+                       if (err)
+                               return err;
+                       num_planes = buf.length;
+               }
                return err;
        }
+
+       int expbufs(int fd, unsigned type)
+       {
+               struct v4l2_exportbuffer expbuf;
+               unsigned i, p;
+               int err;
+
+               memset(&expbuf, 0, sizeof(expbuf));
+               for (i = 0; i < bcount; i++) {
+                       for (p = 0; p < num_planes; p++) {
+                               expbuf.type = type;
+                               expbuf.index = i;
+                               expbuf.plane = p;
+                               expbuf.flags = O_RDWR;
+                               err = doioctl(fd, VIDIOC_EXPBUF, &expbuf);
+                               if (err < 0)
+                                       return err;
+                               if (err >= 0)
+                                       fds[i][p] = expbuf.fd;
+                       }
+               }
+               return 0;
+       }
 };
 
 static bool fill_buffer_from_file(buffers &b, unsigned idx, FILE *fin)
@@ -320,7 +387,6 @@ static int do_setup_cap_buffers(int fd, buffers &b)
                        return -1;
 
                if (b.is_mplane) {
-                       b.num_planes = buf.length;
                        for (unsigned j = 0; j < b.num_planes; j++) {
                                struct v4l2_plane &p = b.planes[i][j];
 
@@ -331,11 +397,20 @@ static int do_setup_cap_buffers(int fd, buffers &b)
                                                          fd, planes[j].m.mem_offset);
 
                                        if (b.bufs[i][j] == MAP_FAILED) {
-                                               fprintf(stderr, "mmap failed\n");
+                                               fprintf(stderr, "mmap plane %u failed\n", j);
                                                return -1;
                                        }
-                               }
-                               else {
+                               } else if (b.memory == V4L2_MEMORY_DMABUF) {
+                                       b.bufs[i][j] = test_mmap(NULL, p.length,
+                                                         PROT_READ | PROT_WRITE, MAP_SHARED,
+                                                         b.fds[i][j], 0);
+
+                                       if (b.bufs[i][j] == MAP_FAILED) {
+                                               fprintf(stderr, "dmabuf mmap plane %u failed\n", j);
+                                               return -1;
+                                       }
+                                       planes[j].m.fd = b.fds[i][j];
+                               } else {
                                        b.bufs[i][j] = calloc(1, p.length);
                                        planes[j].m.userptr = (unsigned long)b.bufs[i][j];
                                }
@@ -344,7 +419,6 @@ static int do_setup_cap_buffers(int fd, buffers &b)
                else {
                        struct v4l2_plane &p = b.planes[i][0];
 
-                       b.num_planes = 1;
                        p.length = buf.length;
                        if (b.memory == V4L2_MEMORY_MMAP) {
                                b.bufs[i][0] = test_mmap(NULL, p.length,
@@ -354,8 +428,17 @@ static int do_setup_cap_buffers(int fd, buffers &b)
                                        fprintf(stderr, "mmap failed\n");
                                        return -1;
                                }
-                       }
-                       else {
+                       } else if (b.memory == V4L2_MEMORY_DMABUF) {
+                               b.bufs[i][0] = test_mmap(NULL, p.length,
+                                               PROT_READ | PROT_WRITE, MAP_SHARED,
+                                               b.fds[i][0], 0);
+
+                               if (b.bufs[i][0] == MAP_FAILED) {
+                                       fprintf(stderr, "dmabuf mmap failed\n");
+                                       return -1;
+                               }
+                               buf.m.fd = b.fds[i][0];
+                       } else {
                                b.bufs[i][0] = calloc(1, p.length);
                                buf.m.userptr = (unsigned long)b.bufs[i][0];
                        }
@@ -366,17 +449,14 @@ static int do_setup_cap_buffers(int fd, buffers &b)
        return 0;
 }
 
-static int do_setup_out_buffers(int fd, buffers &b, FILE *fin)
+static int do_setup_out_buffers(int fd, buffers &b, FILE *fin, bool qbuf)
 {
        struct v4l2_format fmt;
        memset(&fmt, 0, sizeof(fmt));
        fmt.type = b.type;
        doioctl(fd, VIDIOC_G_FMT, &fmt);
 
-       if (!precalculate_bars(fmt.fmt.pix.pixelformat, stream_pat)) {
-               fprintf(stderr, "unsupported pixelformat\n");
-               return -1;
-       }
+       bool can_fill = precalculate_bars(fmt.fmt.pix.pixelformat, stream_pat);
 
        for (unsigned i = 0; i < b.bcount; i++) {
                struct v4l2_plane planes[VIDEO_MAX_PLANES];
@@ -395,7 +475,6 @@ static int do_setup_out_buffers(int fd, buffers &b, FILE *fin)
                        return -1;
 
                if (b.is_mplane) {
-                       b.num_planes = buf.length;
                        for (unsigned j = 0; j < b.num_planes; j++) {
                                struct v4l2_plane &p = b.planes[i][j];
 
@@ -407,11 +486,20 @@ static int do_setup_out_buffers(int fd, buffers &b, FILE *fin)
                                                          fd, planes[j].m.mem_offset);
 
                                        if (b.bufs[i][j] == MAP_FAILED) {
-                                               fprintf(stderr, "mmap failed\n");
+                                               fprintf(stderr, "mmap output plane %u failed\n", j);
                                                return -1;
                                        }
-                               }
-                               else {
+                               } else if (b.memory == V4L2_MEMORY_DMABUF) {
+                                       b.bufs[i][j] = test_mmap(NULL, p.length,
+                                                         PROT_READ | PROT_WRITE, MAP_SHARED,
+                                                         b.fds[i][j], 0);
+
+                                       if (b.bufs[i][j] == MAP_FAILED) {
+                                               fprintf(stderr, "dmabuf mmap output plane %u failed\n", j);
+                                               return -1;
+                                       }
+                                       planes[j].m.fd = b.fds[i][j];
+                               } else {
                                        b.bufs[i][j] = calloc(1, p.length);
                                        planes[j].m.userptr = (unsigned long)b.bufs[i][j];
                                }
@@ -421,7 +509,6 @@ static int do_setup_out_buffers(int fd, buffers &b, FILE *fin)
                                fill_buffer_from_file(b, buf.index, fin);
                }
                else {
-                       b.num_planes = 1;
                        b.planes[i][0].length = buf.length;
                        buf.bytesused = buf.length;
                        if (b.memory == V4L2_MEMORY_MMAP) {
@@ -429,21 +516,33 @@ static int do_setup_out_buffers(int fd, buffers &b, FILE *fin)
                                                  PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
 
                                if (b.bufs[i][0] == MAP_FAILED) {
-                                       fprintf(stderr, "mmap failed\n");
+                                       fprintf(stderr, "mmap output failed\n");
                                        return -1;
                                }
-                       }
-                       else {
+                       } else if (b.memory == V4L2_MEMORY_DMABUF) {
+                               b.bufs[i][0] = test_mmap(NULL, buf.length,
+                                               PROT_READ | PROT_WRITE, MAP_SHARED,
+                                               b.fds[i][0], 0);
+
+                               if (b.bufs[i][0] == MAP_FAILED) {
+                                       fprintf(stderr, "dmabuf mmap output failed\n");
+                                       return -1;
+                               }
+                               buf.m.fd = b.fds[i][0];
+                       } else {
                                b.bufs[i][0] = calloc(1, buf.length);
                                buf.m.userptr = (unsigned long)b.bufs[i][0];
                        }
                        if (!fin || !fill_buffer_from_file(b, buf.index, fin))
-                               fill_buffer(b.bufs[i][0], &fmt.fmt.pix);
+                               if (can_fill)
+                                       fill_buffer(b.bufs[i][0], &fmt.fmt.pix);
+               }
+               if (qbuf) {
+                       if (V4L2_TYPE_IS_OUTPUT(buf.type))
+                               setTimeStamp(buf);
+                       if (doioctl(fd, VIDIOC_QBUF, &buf))
+                               return -1;
                }
-               if (V4L2_TYPE_IS_OUTPUT(buf.type))
-                       setTimeStamp(buf);
-               if (doioctl(fd, VIDIOC_QBUF, &buf))
-                       return -1;
        }
        return 0;
 }
@@ -452,15 +551,15 @@ static void do_release_buffers(buffers &b)
 {
        for (unsigned i = 0; i < b.bcount; i++) {
                for (unsigned j = 0; j < b.num_planes; j++) {
-                       if (b.memory == V4L2_MEMORY_MMAP)
-                               munmap(b.bufs[i][j], b.planes[i][j].length);
-                       else
+                       if (b.memory == V4L2_MEMORY_USERPTR)
                                free(b.bufs[i][j]);
+                       else
+                               munmap(b.bufs[i][j], b.planes[i][j].length);
                }
        }
 }
 
-static int do_handle_cap(int fd, buffers &b, FILE *fout,
+static int do_handle_cap(int fd, buffers &b, FILE *fout, int *index,
                         unsigned &count, unsigned &last, struct timeval &tv_last)
 {
        char ch = '<';
@@ -521,8 +620,10 @@ static int do_handle_cap(int fd, buffers &b, FILE *fout,
                ch = 'B';
        if (verbose)
                print_buffer(stderr, buf);
-       if (test_ioctl(fd, VIDIOC_QBUF, &buf))
+       if (index == NULL && test_ioctl(fd, VIDIOC_QBUF, &buf))
                return -1;
+       if (index)
+               *index = buf.index;
 
        if (!verbose) {
                fprintf(stderr, "%c", ch);
@@ -561,7 +662,7 @@ static int do_handle_cap(int fd, buffers &b, FILE *fout,
        return 0;
 }
 
-static int do_handle_out(int fd, buffers &b, FILE *fin,
+static int do_handle_out(int fd, buffers &b, FILE *fin, struct v4l2_buffer *cap,
                         unsigned &count, unsigned &last, struct timeval &tv_last)
 {
        struct v4l2_plane planes[VIDEO_MAX_PLANES];
@@ -577,25 +678,55 @@ static int do_handle_out(int fd, buffers &b, FILE *fin,
                buf.length = VIDEO_MAX_PLANES;
        }
 
-       ret = test_ioctl(fd, VIDIOC_DQBUF, &buf);
-       if (ret < 0 && errno == EAGAIN)
-               return 0;
+       if (cap) {
+               buf.index = cap->index;
+               ret = test_ioctl(fd, VIDIOC_QUERYBUF, &buf);
+               if (b.is_mplane) {
+                       for (unsigned j = 0; j < b.num_planes; j++) {
+                               unsigned bytesused = cap->m.planes[j].bytesused;
+                               unsigned data_offset = cap->m.planes[j].data_offset;
+
+                               if (b.memory == V4L2_MEMORY_USERPTR) {
+                                       planes[j].m.userptr = (unsigned long)cap->m.planes[j].m.userptr + data_offset;
+                                       planes[j].bytesused = cap->m.planes[j].bytesused - data_offset;
+                                       planes[j].data_offset = 0;
+                               } else if (b.memory == V4L2_MEMORY_DMABUF) {
+                                       planes[j].m.fd = b.fds[cap->index][j];
+                                       planes[j].bytesused = bytesused;
+                                       planes[j].data_offset = data_offset;
+                               }
+                       }
+               }
+               else {
+                       buf.bytesused = cap->bytesused;
+                       if (b.memory == V4L2_MEMORY_USERPTR)
+                               buf.m.userptr = (unsigned long)cap->m.userptr;
+                       else if (b.memory == V4L2_MEMORY_DMABUF)
+                               buf.m.fd = b.fds[cap->index][0];
+               }
+       } else {
+               ret = test_ioctl(fd, VIDIOC_DQBUF, &buf);
+               if (ret < 0 && errno == EAGAIN)
+                       return 0;
+               if (b.is_mplane) {
+                       for (unsigned j = 0; j < buf.length; j++)
+                               buf.m.planes[j].bytesused = buf.m.planes[j].length;
+               } else {
+                       buf.bytesused = buf.length;
+               }
+       }
        if (ret < 0) {
                fprintf(stderr, "%s: failed: %s\n", "VIDIOC_DQBUF", strerror(errno));
                return -1;
        }
        if (fin && !fill_buffer_from_file(b, buf.index, fin))
                return -1;
-       if (b.is_mplane) {
-               for (unsigned j = 0; j < buf.length; j++)
-                       buf.m.planes[j].bytesused = buf.m.planes[j].length;
-       } else {
-               buf.bytesused = buf.length;
-       }
        if (V4L2_TYPE_IS_OUTPUT(buf.type))
                setTimeStamp(buf);
-       if (test_ioctl(fd, VIDIOC_QBUF, &buf))
+       if (test_ioctl(fd, VIDIOC_QBUF, &buf)) {
+               fprintf(stderr, "%s: failed: %s\n", "VIDIOC_QBUF", strerror(errno));
                return -1;
+       }
 
        fprintf(stderr, ">");
        fflush(stderr);
@@ -624,22 +755,67 @@ static int do_handle_out(int fd, buffers &b, FILE *fin,
        return 0;
 }
 
+static int do_handle_out_to_in(int out_fd, int fd, buffers &out, buffers &in)
+{
+       struct v4l2_plane planes[VIDEO_MAX_PLANES];
+       struct v4l2_buffer buf;
+       int ret;
+
+       memset(&buf, 0, sizeof(buf));
+       memset(planes, 0, sizeof(planes));
+       buf.type = out.type;
+       buf.memory = out.memory;
+       if (out.is_mplane) {
+               buf.m.planes = planes;
+               buf.length = VIDEO_MAX_PLANES;
+       }
+
+       do {
+               ret = test_ioctl(out_fd, VIDIOC_DQBUF, &buf);
+       } while (ret < 0 && errno == EAGAIN);
+       if (ret < 0) {
+               fprintf(stderr, "%s: failed: %s\n", "VIDIOC_DQBUF", strerror(errno));
+               return -1;
+       }
+       memset(planes, 0, sizeof(planes));
+       buf.type = in.type;
+       buf.memory = in.memory;
+       if (in.is_mplane) {
+               buf.m.planes = planes;
+               buf.length = VIDEO_MAX_PLANES;
+       }
+       ret = test_ioctl(fd, VIDIOC_QUERYBUF, &buf);
+       if (ret == 0)
+               ret = test_ioctl(fd, VIDIOC_QBUF, &buf);
+       if (ret < 0) {
+               fprintf(stderr, "%s: failed: %s\n", "VIDIOC_QBUF", strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
 static void streaming_set_cap(int fd)
 {
        struct v4l2_event_subscription sub;
        int fd_flags = fcntl(fd, F_GETFL);
-       buffers b(false, options[OptStreamMmap]);
+       buffers b(false);
        bool use_poll = options[OptStreamPoll];
        unsigned count = 0, last = 0;
        struct timeval tv_last;
        bool eos = false;
        FILE *fout = NULL;
 
-       if (!(capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
-           !(capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) {
+       if (!(capabilities & (V4L2_CAP_VIDEO_CAPTURE |
+                             V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+                             V4L2_CAP_VIDEO_M2M |
+                             V4L2_CAP_VIDEO_M2M_MPLANE))) {
                fprintf(stderr, "unsupported stream type\n");
                return;
        }
+       if (options[OptStreamDmaBuf]) {
+               fprintf(stderr, "--stream-dmabuf can only work in combination with --stream-out-mmap\n");
+               return;
+       }
 
        memset(&sub, 0, sizeof(sub));
        sub.type = V4L2_EVENT_EOS;
@@ -700,7 +876,7 @@ static void streaming_set_cap(int fd)
                }
 
                if (FD_ISSET(fd, &read_fds)) {
-                       r  = do_handle_cap(fd, b, fout,
+                       r  = do_handle_cap(fd, b, fout, NULL,
                                           count, last, tv_last);
                        if (r == -1)
                                break;
@@ -720,18 +896,24 @@ done:
 
 static void streaming_set_out(int fd)
 {
-       buffers b(true, options[OptStreamOutMmap]);
+       buffers b(true);
        int fd_flags = fcntl(fd, F_GETFL);
        bool use_poll = options[OptStreamPoll];
        unsigned count = 0, last = 0;
        struct timeval tv_last;
        FILE *fin = NULL;
 
-       if (!(capabilities & V4L2_CAP_VIDEO_OUTPUT) &&
-           !(capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) {
+       if (!(capabilities & (V4L2_CAP_VIDEO_OUTPUT |
+                             V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+                             V4L2_CAP_VIDEO_M2M |
+                             V4L2_CAP_VIDEO_M2M_MPLANE))) {
                fprintf(stderr, "unsupported stream type\n");
                return;
        }
+       if (options[OptStreamOutDmaBuf]) {
+               fprintf(stderr, "--stream-out-dmabuf can only work in combination with --stream-mmap\n");
+               return;
+       }
 
        if (file_out) {
                if (!strcmp(file_out, "-"))
@@ -743,7 +925,7 @@ static void streaming_set_out(int fd)
        if (b.reqbufs(fd, reqbufs_count_out))
                goto done;
 
-       if (do_setup_out_buffers(fd, b, fin))
+       if (do_setup_out_buffers(fd, b, fin, true))
                goto done;
 
        if (doioctl(fd, VIDIOC_STREAMON, &b.type))
@@ -781,7 +963,7 @@ static void streaming_set_out(int fd)
                                goto done;
                        }
                }
-               r = do_handle_out(fd, b, fin,
+               r = do_handle_out(fd, b, fin, NULL,
                                   count, last, tv_last);
                if (r == -1)
                        break;
@@ -812,8 +994,8 @@ static void streaming_set_m2m(int fd)
 {
        int fd_flags = fcntl(fd, F_GETFL);
        bool use_poll = options[OptStreamPoll];
-       buffers in(false, options[OptStreamMmap]);
-       buffers out(true, options[OptStreamOutMmap]);
+       buffers in(false);
+       buffers out(true);
        unsigned count[2] = { 0, 0 };
        unsigned last[2] = { 0, 0 };
        struct timeval tv_last[2];
@@ -823,9 +1005,13 @@ static void streaming_set_m2m(int fd)
        fd_set *ex_fds = &fds[1]; /* for capture */
        fd_set *wr_fds = &fds[2]; /* for output */
 
-       if (!(capabilities & V4L2_CAP_VIDEO_M2M) &&
-           !(capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)) {
-               fprintf(stderr, "unsupported stream type\n");
+       if (!(capabilities & (V4L2_CAP_VIDEO_M2M |
+                             V4L2_CAP_VIDEO_M2M_MPLANE))) {
+               fprintf(stderr, "unsupported m2m stream type\n");
+               return;
+       }
+       if (options[OptStreamDmaBuf] || options[OptStreamOutDmaBuf]) {
+               fprintf(stderr, "--stream-dmabuf or --stream-out-dmabuf not supported for m2m devices\n");
                return;
        }
 
@@ -854,7 +1040,7 @@ static void streaming_set_m2m(int fd)
                goto done;
 
        if (do_setup_cap_buffers(fd, in) ||
-           do_setup_out_buffers(fd, out, file[OUT]))
+           do_setup_out_buffers(fd, out, file[OUT], true))
                goto done;
 
        if (doioctl(fd, VIDIOC_STREAMON, &in.type) ||
@@ -900,7 +1086,7 @@ static void streaming_set_m2m(int fd)
                }
 
                if (rd_fds && FD_ISSET(fd, rd_fds)) {
-                       r  = do_handle_cap(fd, in, file[CAP],
+                       r  = do_handle_cap(fd, in, file[CAP], NULL,
                                           count[CAP], last[CAP], tv_last[CAP]);
                        if (r < 0) {
                                rd_fds = NULL;
@@ -910,7 +1096,7 @@ static void streaming_set_m2m(int fd)
                }
 
                if (wr_fds && FD_ISSET(fd, wr_fds)) {
-                       r  = do_handle_out(fd, out, file[OUT],
+                       r  = do_handle_out(fd, out, file[OUT], NULL,
                                           count[OUT], last[OUT], tv_last[OUT]);
                        if (r < 0)  {
                                wr_fds = NULL;
@@ -928,7 +1114,6 @@ static void streaming_set_m2m(int fd)
                        struct v4l2_event ev;
 
                        while (!ioctl(fd, VIDIOC_DQEVENT, &ev)) {
-
                                if (ev.type != V4L2_EVENT_EOS)
                                        continue;
 
@@ -954,27 +1139,213 @@ done:
                fclose(file[OUT]);
 }
 
-void streaming_set(int fd)
+static void streaming_set_cap2out(int fd, int out_fd)
 {
-       bool do_cap = options[OptStreamMmap] || options[OptStreamUser];
-       bool do_out = options[OptStreamOutMmap] || options[OptStreamOutUser];
+       int fd_flags = fcntl(fd, F_GETFL);
+       bool use_poll = options[OptStreamPoll];
+       bool use_dmabuf = options[OptStreamDmaBuf] || options[OptStreamOutDmaBuf];
+       bool use_userptr = options[OptStreamUser] && options[OptStreamOutUser];
+       buffers in(false);
+       buffers out(true);
+       unsigned count[2] = { 0, 0 };
+       unsigned last[2] = { 0, 0 };
+       struct timeval tv_last[2];
+       FILE *file[2] = {NULL, NULL};
+       fd_set fds;
+       unsigned cnt = 0;
+
+       if (!(capabilities & (V4L2_CAP_VIDEO_CAPTURE |
+                             V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+                             V4L2_CAP_VIDEO_M2M |
+                             V4L2_CAP_VIDEO_M2M_MPLANE))) {
+               fprintf(stderr, "unsupported capture stream type\n");
+               return;
+       } else if (!(out_capabilities & (V4L2_CAP_VIDEO_OUTPUT |
+                                        V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+                                        V4L2_CAP_VIDEO_M2M |
+                                        V4L2_CAP_VIDEO_M2M_MPLANE))) {
+               fprintf(stderr, "unsupported output stream type\n");
+               return;
+       }
+       if (options[OptStreamDmaBuf] && !options[OptStreamOutMmap]) {
+               fprintf(stderr, "--stream-dmabuf can only work in combination with --stream-out-mmap\n");
+               return;
+       }
+       if (options[OptStreamOutDmaBuf] && !options[OptStreamMmap]) {
+               fprintf(stderr, "--stream-out-dmabuf can only work in combination with --stream-mmap\n");
+               return;
+       }
+       if (options[OptStreamDmaBuf])
+               reqbufs_count_cap = reqbufs_count_out;
+       if (options[OptStreamOutDmaBuf])
+               reqbufs_count_out = reqbufs_count_cap;
+       if (!use_dmabuf && !use_userptr) {
+               fprintf(stderr, "Allowed combinations (for now):\n");
+               fprintf(stderr, "\t--stream-mmap and --stream-out-dmabuf\n");
+               fprintf(stderr, "\t--stream-dmabuf and --stream-out-mmap\n");
+               fprintf(stderr, "\t--stream-user and --stream-out-user\n");
+               return;
+       }
+
+       if (file_cap) {
+               if (!strcmp(file_cap, "-"))
+                       file[CAP] = stdout;
+               else
+                       file[CAP] = fopen(file_cap, "w+");
+       }
+
+       if (file_out) {
+               if (!strcmp(file_out, "-"))
+                       file[OUT] = stdin;
+               else
+                       file[OUT] = fopen(file_out, "r");
+       }
+
+       if (in.reqbufs(fd, reqbufs_count_cap) ||
+           out.reqbufs(out_fd, reqbufs_count_out))
+               goto done;
+
+       if (options[OptStreamDmaBuf]) {
+               if (in.expbufs(out_fd, out.type))
+                       goto done;
+       } else if (options[OptStreamOutDmaBuf]) {
+               if (out.expbufs(fd, in.type))
+                       goto done;
+       }
+
+       if (in.num_planes != out.num_planes ||
+           in.is_mplane != out.is_mplane) {
+               fprintf(stderr, "mismatch between number of planes\n");
+               goto done;
+       }
+
+       if (do_setup_cap_buffers(fd, in) ||
+           do_setup_out_buffers(out_fd, out, file[OUT], false))
+               goto done;
+
+       if (doioctl(fd, VIDIOC_STREAMON, &in.type) ||
+           doioctl(out_fd, VIDIOC_STREAMON, &out.type))
+               goto done;
+
+       if (use_poll)
+               fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
+
+       while (1) {
+               struct timeval tv = { use_poll ? 2 : 0, 0 };
+               int r = 0;
 
-       if (do_cap && do_out)
+               FD_ZERO(&fds);
+               FD_SET(fd, &fds);
+
+               if (use_poll)
+                       r = select(fd + 1, &fds, NULL, NULL, &tv);
+
+               if (r == -1) {
+                       if (EINTR == errno)
+                               continue;
+                       fprintf(stderr, "select error: %s\n",
+                                       strerror(errno));
+                       goto done;
+               }
+               if (use_poll && r == 0) {
+                       fprintf(stderr, "select timeout\n");
+                       goto done;
+               }
+
+               if (FD_ISSET(fd, &fds)) {
+                       int index = -1;
+
+                       r = do_handle_cap(fd, in, file[CAP], &index,
+                                          count[CAP], last[CAP], tv_last[CAP]);
+                       if (r)
+                               fprintf(stderr, "handle cap %d\n", r);
+                       if (!r) {
+                               struct v4l2_plane planes[VIDEO_MAX_PLANES];
+                               struct v4l2_buffer buf;
+
+                               memset(&buf, 0, sizeof(buf));
+                               buf.type = in.type;
+                               buf.index = index;
+                               if (in.is_mplane) {
+                                       buf.m.planes = planes;
+                                       buf.length = VIDEO_MAX_PLANES;
+                                       memset(planes, 0, sizeof(planes));
+                               }
+                               if (test_ioctl(fd, VIDIOC_QUERYBUF, &buf))
+                                       break;
+                               r = do_handle_out(out_fd, out, file[OUT], &buf,
+                                          count[OUT], last[OUT], tv_last[OUT]);
+                       }
+                       if (r)
+                               fprintf(stderr, "handle out %d\n", r);
+                       if (!r && cnt++ > 1)
+                               r = do_handle_out_to_in(out_fd, fd, out, in);
+                       if (r)
+                               fprintf(stderr, "handle out2in %d\n", r);
+                       if (r < 0) {
+                               doioctl(fd, VIDIOC_STREAMOFF, &in.type);
+                               doioctl(out_fd, VIDIOC_STREAMOFF, &out.type);
+                               break;
+                       }
+               }
+       }
+
+       fcntl(fd, F_SETFL, fd_flags);
+       fprintf(stderr, "\n");
+
+       do_release_buffers(in);
+       do_release_buffers(out);
+
+done:
+       if (file[CAP] && file[CAP] != stdout)
+               fclose(file[CAP]);
+
+       if (file[OUT] && file[OUT] != stdin)
+               fclose(file[OUT]);
+}
+
+void streaming_set(int fd, int out_fd)
+{
+       int do_cap = options[OptStreamMmap] + options[OptStreamUser] + options[OptStreamDmaBuf];
+       int do_out = options[OptStreamOutMmap] + options[OptStreamOutUser] + options[OptStreamOutDmaBuf];
+
+       if (out_fd < 0) {
+               out_fd = fd;
+               out_capabilities = capabilities;
+       }
+
+       if (do_cap > 1) {
+               fprintf(stderr, "only one of --stream-mmap/user/dmabuf is allowed\n");
+               return;
+       }
+       if (do_out > 1) {
+               fprintf(stderr, "only one of --stream-out-mmap/user/dmabuf is allowed\n");
+               return;
+       }
+
+       if (do_cap && do_out && fd == out_fd)
                streaming_set_m2m(fd);
+       else if (do_cap && do_out)
+               streaming_set_cap2out(fd, out_fd);
        else if (do_cap)
                streaming_set_cap(fd);
        else if (do_out)
                streaming_set_out(fd);
 }
 
-void streaming_list(int fd)
+void streaming_list(int fd, int out_fd)
 {
+       if (out_fd < 0) {
+               out_fd = fd;
+               out_capabilities = capabilities;
+       }
+
        if (options[OptListBuffers]) {
                list_buffers(fd, vidcap_buftype);
        }
 
        if (options[OptListBuffersOut]) {
-               list_buffers(fd, vidout_buftype);
+               list_buffers(out_fd, vidout_buftype);
        }
 
        if (options[OptListBuffersVbi]) {
@@ -986,10 +1357,10 @@ void streaming_list(int fd)
        }
 
        if (options[OptListBuffersVbiOut]) {
-               list_buffers(fd, V4L2_BUF_TYPE_VBI_OUTPUT);
+               list_buffers(out_fd, V4L2_BUF_TYPE_VBI_OUTPUT);
        }
 
        if (options[OptListBuffersSlicedVbiOut]) {
-               list_buffers(fd, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT);
+               list_buffers(out_fd, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT);
        }
 }
index 737d1a6..09a1782 100644 (file)
@@ -59,6 +59,7 @@ static int app_result;
 int verbose;
 
 unsigned capabilities;
+unsigned out_capabilities;
 bool is_multiplanar;
 __u32 vidcap_buftype;
 __u32 vidout_buftype;
@@ -68,6 +69,7 @@ static struct option long_options[] = {
        {"list-audio-outputs", no_argument, 0, OptListAudioOutputs},
        {"all", no_argument, 0, OptAll},
        {"device", required_argument, 0, OptSetDevice},
+       {"out-device", required_argument, 0, OptSetOutDevice},
        {"get-fmt-video", no_argument, 0, OptGetVideoFormat},
        {"set-fmt-video", required_argument, 0, OptSetVideoFormat},
        {"try-fmt-video", required_argument, 0, OptTryVideoFormat},
@@ -196,10 +198,12 @@ static struct option long_options[] = {
        {"stream-to", required_argument, 0, OptStreamTo},
        {"stream-mmap", optional_argument, 0, OptStreamMmap},
        {"stream-user", optional_argument, 0, OptStreamUser},
+       {"stream-dmabuf", no_argument, 0, OptStreamDmaBuf},
        {"stream-from", required_argument, 0, OptStreamFrom},
        {"stream-pattern", required_argument, 0, OptStreamPattern},
        {"stream-out-mmap", optional_argument, 0, OptStreamOutMmap},
        {"stream-out-user", optional_argument, 0, OptStreamOutUser},
+       {"stream-out-dmabuf", no_argument, 0, OptStreamOutDmaBuf},
        {0, 0, 0, 0}
 };
 
@@ -763,10 +767,12 @@ int main(int argc, char **argv)
        int i;
 
        int fd = -1;
+       int out_fd = -1;
 
        /* command args */
        int ch;
        const char *device = "/dev/video0";     /* -d device */
+       const char *out_device = NULL;
        struct v4l2_capability vcap;    /* list_cap */
        __u32 wait_for_event = 0;       /* wait for this event */
        const char *wait_event_id = NULL;
@@ -849,6 +855,15 @@ int main(int argc, char **argv)
                                device = newdev;
                        }
                        break;
+               case OptSetOutDevice:
+                       out_device = optarg;
+                       if (out_device[0] >= '0' && out_device[0] <= '9' && strlen(out_device) <= 3) {
+                               static char newdev[20];
+
+                               sprintf(newdev, "/dev/video%s", out_device);
+                               out_device = newdev;
+                       }
+                       break;
                case OptWaitForEvent:
                        wait_for_event = parse_event(optarg, &wait_event_id);
                        if (wait_for_event == 0)
@@ -903,7 +918,10 @@ int main(int argc, char **argv)
        }
 
        verbose = options[OptVerbose];
-       doioctl(fd, VIDIOC_QUERYCAP, &vcap);
+       if (doioctl(fd, VIDIOC_QUERYCAP, &vcap)) {
+               fprintf(stderr, "%s: not a v4l2 node\n", device);
+               exit(1);
+       }
        capabilities = vcap.capabilities;
        if (capabilities & V4L2_CAP_DEVICE_CAPS)
                capabilities = vcap.device_caps;
@@ -917,6 +935,21 @@ int main(int argc, char **argv)
        vidout_buftype = is_multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
                                          V4L2_BUF_TYPE_VIDEO_OUTPUT;
 
+       if (out_device) {
+               if ((out_fd = test_open(out_device, O_RDWR)) < 0) {
+                       fprintf(stderr, "Failed to open %s: %s\n", out_device,
+                                       strerror(errno));
+                       exit(1);
+               }
+               if (doioctl(out_fd, VIDIOC_QUERYCAP, &vcap)) {
+                       fprintf(stderr, "%s: not a v4l2 node\n", out_device);
+                       exit(1);
+               }
+               out_capabilities = vcap.capabilities;
+               if (out_capabilities & V4L2_CAP_DEVICE_CAPS)
+                       out_capabilities = vcap.device_caps;
+       }
+
        common_process_controls(fd);
 
        if (wait_for_event == V4L2_EVENT_CTRL && wait_event_id)
@@ -995,7 +1028,7 @@ int main(int argc, char **argv)
        overlay_set(fd);
        vbi_set(fd);
        selection_set(fd);
-       streaming_set(fd);
+       streaming_set(fd, out_fd);
        misc_set(fd);
 
        /* Get options */
@@ -1020,7 +1053,7 @@ int main(int argc, char **argv)
        vidout_list(fd);
        overlay_list(fd);
        vbi_list(fd);
-       streaming_list(fd);
+       streaming_list(fd, out_fd);
 
        if (options[OptWaitForEvent]) {
                struct v4l2_event_subscription sub;
@@ -1078,5 +1111,7 @@ int main(int argc, char **argv)
        }
 
        test_close(fd);
+       if (out_device)
+               test_close(out_fd);
        exit(app_result);
 }
index 6abcd38..b976a32 100644 (file)
@@ -15,6 +15,7 @@ enum Option {
        OptSetCtrl = 'c',
        OptSetDevice = 'd',
        OptGetDriverInfo = 'D',
+       OptSetOutDevice = 'e',
        OptGetFreq = 'F',
        OptSetFreq = 'f',
        OptHelp = 'h',
@@ -135,10 +136,12 @@ enum Option {
        OptStreamTo,
        OptStreamMmap,
        OptStreamUser,
+       OptStreamDmaBuf,
        OptStreamFrom,
        OptStreamPattern,
        OptStreamOutMmap,
        OptStreamOutUser,
+       OptStreamOutDmaBuf,
        OptHelpTuner,
        OptHelpIO,
        OptHelpStds,
@@ -155,6 +158,7 @@ enum Option {
 
 extern char options[OptLast];
 extern unsigned capabilities;
+extern unsigned out_capabilities;
 extern bool is_multiplanar;
 extern __u32 vidcap_buftype;
 extern __u32 vidout_buftype;
@@ -271,8 +275,8 @@ void misc_get(int fd);
 // v4l2-ctl-streaming.cpp
 void streaming_usage(void);
 void streaming_cmd(int ch, char *optarg);
-void streaming_set(int fd);
-void streaming_list(int fd);
+void streaming_set(int fd, int out_fd);
+void streaming_list(int fd, int out_fd);
 
 // v4l2-ctl-test-patterns.cpp
 void fill_buffer(void *buffer, struct v4l2_pix_format *pix);