[FFmpeg-devel] [PATCH] v4l2: add libv4l2 support.

Clément Bœsch ubitux at gmail.com
Fri Nov 4 20:11:40 CET 2011


On Sun, Oct 30, 2011 at 03:57:41AM +0100, Clément Bœsch wrote:
> ---
> Here is a patch to support old webcams with exotic colorspaces or insane
> drivers using userland libv4l2 (see for instance
> http://hansdegoede.livejournal.com/3636.html).
> 
> It seems to work with my personnal crazy webcam, but more tests are welcome.
> 
> I wanted to make the support optional at runtime, but it raises at least one
> issue: the context has to be transmitted to all subfunctions, which is not
> easily possible in some cases such as mmap_release_buffer() callback. So ATM,
> if you build FFmpeg with --enable-libv4l2, then deal with it, you have to use
> it...
[...]

New version attached. I didn't change the strerror() to be consistent with
the rest of the code (it can be changed later), but the rest should be
fixed.

Still, the runtime support is not present for the issue I raised in the
previous mail. But if there is no objection I'd like to push this version
in a few days.

-- 
Clément B.
-------------- next part --------------
From 185715adab6fdbb2c39b455c89f199044a3d981f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= <ubitux at gmail.com>
Date: Sun, 30 Oct 2011 03:21:55 +0100
Subject: [PATCH] v4l2: add libv4l2 support.

---
 configure          |    5 +++
 libavdevice/v4l2.c |   79 ++++++++++++++++++++++++++++++++++------------------
 2 files changed, 57 insertions(+), 27 deletions(-)

diff --git a/configure b/configure
index 279e416..a617b10 100755
--- a/configure
+++ b/configure
@@ -187,6 +187,7 @@ External library support:
   --enable-libstagefright-h264  enable H.264 decoding via libstagefright [no]
   --enable-libtheora       enable Theora encoding via libtheora [no]
   --enable-libutvideo      enable Ut Video decoding via libutvideo [no]
+  --enable-libv4l2         enable libv4l2/v4l-utils [no]
   --enable-libvo-aacenc    enable AAC encoding via libvo-aacenc [no]
   --enable-libvo-amrwbenc  enable AMR-WB encoding via libvo-amrwbenc [no]
   --enable-libvorbis       enable Vorbis encoding via libvorbis,
@@ -1022,6 +1023,7 @@ CONFIG_LIST="
     libstagefright_h264
     libtheora
     libutvideo
+    libv4l2
     libvo_aacenc
     libvo_amrwbenc
     libvorbis
@@ -1568,6 +1570,7 @@ jack_indev_deps="jack_jack_h sem_timedwait"
 lavfi_indev_deps="avfilter"
 libcdio_indev_deps="libcdio"
 libdc1394_indev_deps="libdc1394"
+libv4l2_indev_deps="libv4l2"
 openal_indev_deps="openal"
 oss_indev_deps_any="soundcard_h sys_soundcard_h"
 oss_outdev_deps_any="soundcard_h sys_soundcard_h"
@@ -3053,6 +3056,7 @@ enabled libstagefright_h264  && require_cpp libstagefright_h264 "binder/ProcessS
     media/stagefright/OMXClient.h media/stagefright/OMXCodec.h" android::OMXClient -lstagefright -lmedia -lutils -lbinder
 enabled libtheora  && require  libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg
 enabled libutvideo    && require_cpp utvideo "stdint.h stdlib.h utvideo/utvideo.h utvideo/Codec.h" 'CCodec*' -lutvideo -lstdc++
+enabled libv4l2    && require_pkg_config libv4l2 libv4l2.h v4l2_ioctl
 enabled libvo_aacenc && require libvo_aacenc vo-aacenc/voAAC.h voGetAACEncAPI -lvo-aacenc
 enabled libvo_amrwbenc && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc
 enabled libvorbis  && require  libvorbis vorbis/vorbisenc.h vorbis_info_init -lvorbisenc -lvorbis -logg
@@ -3381,6 +3385,7 @@ echo "libspeex enabled          ${libspeex-no}"
 echo "libstagefright-h264 enabled    ${libstagefright_h264-no}"
 echo "libtheora enabled         ${libtheora-no}"
 echo "libutvideo enabled        ${libutvideo-no}"
+echo "libv4l2 enabled           ${libv4l2-no}"
 echo "libvo-aacenc support      ${libvo_aacenc-no}"
 echo "libvo-amrwbenc support    ${libvo_amrwbenc-no}"
 echo "libvorbis enabled         ${libvorbis-no}"
diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c
index e327cb0..85666f5 100644
--- a/libavdevice/v4l2.c
+++ b/libavdevice/v4l2.c
@@ -52,6 +52,18 @@
 #include "libavutil/pixdesc.h"
 #include "libavutil/avstring.h"
 
+#if CONFIG_LIBV4L2
+#include <libv4l2.h>
+#else
+#define v4l2_open   open
+#define v4l2_close  close
+#define v4l2_dup    dup
+#define v4l2_ioctl  ioctl
+#define v4l2_read   read
+#define v4l2_mmap   mmap
+#define v4l2_munmap munmap
+#endif
+
 static const int desired_video_buffers = 256;
 
 enum io_method {
@@ -113,36 +125,49 @@ static int device_open(AVFormatContext *ctx, uint32_t *capabilities)
 {
     struct v4l2_capability cap;
     int fd;
+#if CONFIG_LIBV4L2
+    int fd_libv4l;
+#endif
     int res, err;
     int flags = O_RDWR;
 
     if (ctx->flags & AVFMT_FLAG_NONBLOCK) {
         flags |= O_NONBLOCK;
     }
-    fd = open(ctx->filename, flags, 0);
+    fd = v4l2_open(ctx->filename, flags, 0);
     if (fd < 0) {
         av_log(ctx, AV_LOG_ERROR, "Cannot open video device %s : %s\n",
                  ctx->filename, strerror(errno));
         return AVERROR(errno);
     }
+#if CONFIG_LIBV4L2
+    fd_libv4l = v4l2_fd_open(fd, 0);
+    if (fd < 0) {
+        err = AVERROR(errno);
+        av_log(ctx, AV_LOG_ERROR, "Cannot open video device with libv4l neither %s : %s\n",
+               ctx->filename, strerror(errno));
+        return err;
+    }
+    fd = fd_libv4l;
+#endif
 
-    res = ioctl(fd, VIDIOC_QUERYCAP, &cap);
+    res = v4l2_ioctl(fd, VIDIOC_QUERYCAP, &cap);
     // ENOIOCTLCMD definition only availble on __KERNEL__
     if (res < 0 && ((err = errno) == 515)) {
         av_log(ctx, AV_LOG_ERROR, "QUERYCAP not implemented, probably V4L device but not supporting V4L2\n");
-        close(fd);
+        v4l2_close(fd);
 
         return AVERROR(515);
     }
     if (res < 0) {
         av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QUERYCAP): %s\n",
                  strerror(errno));
-        close(fd);
+        v4l2_close(fd);
         return AVERROR(err);
     }
     if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
         av_log(ctx, AV_LOG_ERROR, "Not a video capture device\n");
-        close(fd);
+        v4l2_close(fd);
         return AVERROR(ENODEV);
     }
     *capabilities = cap.capabilities;
@@ -162,7 +187,7 @@ static int device_init(AVFormatContext *ctx, int *width, int *height, uint32_t p
     fmt.fmt.pix.height = *height;
     fmt.fmt.pix.pixelformat = pix_fmt;
     fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
-    res = ioctl(fd, VIDIOC_S_FMT, &fmt);
+    res = v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt);
     if ((*width != fmt.fmt.pix.width) || (*height != fmt.fmt.pix.height)) {
         av_log(ctx, AV_LOG_INFO, "The V4L2 driver changed the video from %dx%d to %dx%d\n", *width, *height, fmt.fmt.pix.width, fmt.fmt.pix.height);
         *width = fmt.fmt.pix.width;
@@ -182,7 +207,7 @@ static int first_field(int fd)
     int res;
     v4l2_std_id std;
 
-    res = ioctl(fd, VIDIOC_G_STD, &std);
+    res = v4l2_ioctl(fd, VIDIOC_G_STD, &std);
     if (res < 0) {
         return 0;
     }
@@ -245,7 +270,7 @@ static int mmap_init(AVFormatContext *ctx)
     req.count = desired_video_buffers;
     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     req.memory = V4L2_MEMORY_MMAP;
-    res = ioctl(s->fd, VIDIOC_REQBUFS, &req);
+    res = v4l2_ioctl(s->fd, VIDIOC_REQBUFS, &req);
     if (res < 0) {
         if (errno == EINVAL) {
             av_log(ctx, AV_LOG_ERROR, "Device does not support mmap\n");
@@ -278,7 +303,7 @@ static int mmap_init(AVFormatContext *ctx)
         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         buf.memory = V4L2_MEMORY_MMAP;
         buf.index = i;
-        res = ioctl(s->fd, VIDIOC_QUERYBUF, &buf);
+        res = v4l2_ioctl(s->fd, VIDIOC_QUERYBUF, &buf);
         if (res < 0) {
             av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QUERYBUF)\n");
             return AVERROR(errno);
@@ -290,7 +315,7 @@ static int mmap_init(AVFormatContext *ctx)
 
             return -1;
         }
-        s->buf_start[i] = mmap (NULL, buf.length,
+        s->buf_start[i] = v4l2_mmap(NULL, buf.length,
                         PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, buf.m.offset);
         if (s->buf_start[i] == MAP_FAILED) {
             av_log(ctx, AV_LOG_ERROR, "mmap: %s\n", strerror(errno));
@@ -322,7 +347,7 @@ static void mmap_release_buffer(AVPacket *pkt)
     fd = buf_descriptor->fd;
     av_free(buf_descriptor);
 
-    res = ioctl(fd, VIDIOC_QBUF, &buf);
+    res = v4l2_ioctl(fd, VIDIOC_QBUF, &buf);
     if (res < 0) {
         av_log(NULL, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n", strerror(errno));
     }
@@ -341,7 +366,7 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
     buf.memory = V4L2_MEMORY_MMAP;
 
     /* FIXME: Some special treatment might be needed in case of loss of signal... */
-    while ((res = ioctl(s->fd, VIDIOC_DQBUF, &buf)) < 0 && (errno == EINTR));
+    while ((res = v4l2_ioctl(s->fd, VIDIOC_DQBUF, &buf)) < 0 && (errno == EINTR));
     if (res < 0) {
         if (errno == EAGAIN) {
             pkt->size = 0;
@@ -368,7 +393,7 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
          * allocate a buffer for memcopying into it
          */
         av_log(ctx, AV_LOG_ERROR, "Failed to allocate a buffer descriptor\n");
-        res = ioctl(s->fd, VIDIOC_QBUF, &buf);
+        res = v4l2_ioctl(s->fd, VIDIOC_QBUF, &buf);
 
         return AVERROR(ENOMEM);
     }
@@ -397,7 +422,7 @@ static int mmap_start(AVFormatContext *ctx)
         buf.memory = V4L2_MEMORY_MMAP;
         buf.index  = i;
 
-        res = ioctl(s->fd, VIDIOC_QBUF, &buf);
+        res = v4l2_ioctl(s->fd, VIDIOC_QBUF, &buf);
         if (res < 0) {
             av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n", strerror(errno));
             return AVERROR(errno);
@@ -405,7 +430,7 @@ static int mmap_start(AVFormatContext *ctx)
     }
 
     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    res = ioctl(s->fd, VIDIOC_STREAMON, &type);
+    res = v4l2_ioctl(s->fd, VIDIOC_STREAMON, &type);
     if (res < 0) {
         av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_STREAMON): %s\n", strerror(errno));
         return AVERROR(errno);
@@ -423,9 +448,9 @@ static void mmap_close(struct video_data *s)
     /* We do not check for the result, because we could
      * not do anything about it anyway...
      */
-    ioctl(s->fd, VIDIOC_STREAMOFF, &type);
+    v4l2_ioctl(s->fd, VIDIOC_STREAMOFF, &type);
     for (i = 0; i < s->buffers; i++) {
-        munmap(s->buf_start[i], s->buf_len[i]);
+        v4l2_munmap(s->buf_start[i], s->buf_len[i]);
     }
     av_free(s->buf_start);
     av_free(s->buf_len);
@@ -450,14 +475,14 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
 
     /* set tv video input */
     input.index = s->channel;
-    if (ioctl(s->fd, VIDIOC_ENUMINPUT, &input) < 0) {
+    if (v4l2_ioctl(s->fd, VIDIOC_ENUMINPUT, &input) < 0) {
         av_log(s1, AV_LOG_ERROR, "The V4L2 driver ioctl enum input failed:\n");
         return AVERROR(EIO);
     }
 
     av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set input_id: %d, input: %s\n",
             s->channel, input.name);
-    if (ioctl(s->fd, VIDIOC_S_INPUT, &input.index) < 0) {
+    if (v4l2_ioctl(s->fd, VIDIOC_S_INPUT, &input.index) < 0) {
         av_log(s1, AV_LOG_ERROR, "The V4L2 driver ioctl set input(%d) failed\n",
                 s->channel);
         return AVERROR(EIO);
@@ -469,7 +494,7 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
         /* set tv standard */
         for (i = 0;; i++) {
             standard.index = i;
-            ret = ioctl(s->fd, VIDIOC_ENUMSTD, &standard);
+            ret = v4l2_ioctl(s->fd, VIDIOC_ENUMSTD, &standard);
             if (ret < 0 || !av_strcasecmp(standard.name, s->standard))
                 break;
         }
@@ -480,7 +505,7 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
 
         av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set standard: %s, id: %"PRIu64"\n",
                s->standard, (uint64_t)standard.id);
-        if (ioctl(s->fd, VIDIOC_S_STD, &standard.id) < 0) {
+        if (v4l2_ioctl(s->fd, VIDIOC_S_STD, &standard.id) < 0) {
             av_log(s1, AV_LOG_ERROR, "The V4L2 driver ioctl set standard(%s) failed\n",
                    s->standard);
             return AVERROR(EIO);
@@ -492,7 +517,7 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
                framerate_q.den, framerate_q.num);
         tpf->numerator   = framerate_q.den;
         tpf->denominator = framerate_q.num;
-        if (ioctl(s->fd, VIDIOC_S_PARM, &streamparm) != 0) {
+        if (v4l2_ioctl(s->fd, VIDIOC_S_PARM, &streamparm) != 0) {
             av_log(s1, AV_LOG_ERROR,
                    "ioctl set time per frame(%d/%d) failed\n",
                    framerate_q.den, framerate_q.num);
@@ -508,7 +533,7 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
         }
     } else {
         /* if timebase value is not set, read the timebase value from the driver */
-        if (ioctl(s->fd, VIDIOC_G_PARM, &streamparm) != 0) {
+        if (v4l2_ioctl(s->fd, VIDIOC_G_PARM, &streamparm) != 0) {
             av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_PARM): %s\n", strerror(errno));
             return AVERROR(errno);
         }
@@ -590,7 +615,7 @@ static int v4l2_read_header(AVFormatContext *s1, AVFormatParameters *ap)
 
         av_log(s1, AV_LOG_VERBOSE, "Querying the device for the current frame size\n");
         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-        if (ioctl(s->fd, VIDIOC_G_FMT, &fmt) < 0) {
+        if (v4l2_ioctl(s->fd, VIDIOC_G_FMT, &fmt) < 0) {
             av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_FMT): %s\n", strerror(errno));
             res = AVERROR(errno);
             goto out;
@@ -604,7 +629,7 @@ static int v4l2_read_header(AVFormatContext *s1, AVFormatParameters *ap)
     if (desired_format == 0) {
         av_log(s1, AV_LOG_ERROR, "Cannot find a proper format for "
                "codec_id %d, pix_fmt %d.\n", s1->video_codec_id, pix_fmt);
-        close(s->fd);
+        v4l2_close(s->fd);
 
         res = AVERROR(EIO);
         goto out;
@@ -629,7 +654,7 @@ static int v4l2_read_header(AVFormatContext *s1, AVFormatParameters *ap)
         res = read_init(s1);
     }
     if (res < 0) {
-        close(s->fd);
+        v4l2_close(s->fd);
         res = AVERROR(EIO);
         goto out;
     }
@@ -681,7 +706,7 @@ static int v4l2_read_close(AVFormatContext *s1)
         mmap_close(s);
     }
 
-    close(s->fd);
+    v4l2_close(s->fd);
     return 0;
 }
 
-- 
1.7.7.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20111104/099981bd/attachment.asc>


More information about the ffmpeg-devel mailing list