mjpg-streamer: fix compatibility with new kernels (>=3.16)
authorMantas Pucka <mantas@8devices.com>
Wed, 11 Mar 2015 11:50:54 +0000 (13:50 +0200)
committerMantas Pucka <mantas@8devices.com>
Wed, 11 Mar 2015 14:53:02 +0000 (16:53 +0200)
input_uvc was broken with new kernel update

patch source: https://github.com/oliv3r/mjpg-streamer

Tested on ar71xx with Logitech C170

Signed-off-by: Mantas Pucka <mantas@8devices.com>
multimedia/mjpg-streamer/Makefile
multimedia/mjpg-streamer/patches/040-Buffer-the-bytesused-variable-from-struct-v4l2_buffe.patch [new file with mode: 0644]
multimedia/mjpg-streamer/patches/041-Stop-leaking-data-via-struct-v4l2_buffer.patch [new file with mode: 0644]

index d2e1537d9ff9a778fa74f4b2335cc9cdf314a8ac..0a297d4e67f69cde33d89dca5a7a904517cabcce 100644 (file)
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
 PKG_NAME:=mjpg-streamer
 PKG_REV:=182
 PKG_VERSION:=r$(PKG_REV)
-PKG_RELEASE:=5
+PKG_RELEASE:=6
 PKG_MAINTAINER:=Roger D <rogerdammit@gmail.com>
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).1.tar.bz2
diff --git a/multimedia/mjpg-streamer/patches/040-Buffer-the-bytesused-variable-from-struct-v4l2_buffe.patch b/multimedia/mjpg-streamer/patches/040-Buffer-the-bytesused-variable-from-struct-v4l2_buffe.patch
new file mode 100644 (file)
index 0000000..a228f42
--- /dev/null
@@ -0,0 +1,87 @@
+From 19202b54698b343a0207d7e213448e32b8e58fc3 Mon Sep 17 00:00:00 2001
+From: Olliver Schinagl <o.schinagl@ultimaker.com>
+Date: Wed, 29 Oct 2014 09:34:41 +0100
+Subject: [PATCH 1/7] Buffer the bytesused variable from struct v4l2_buffer
+
+Starting with kernel versions 3.16, (DE)Queing of buffers has been fixed
+after it was leaking data for ages. in the struct v4l2_buffer is the
+bytesused element which indicates the size of the buffer. This however
+gets cleared whenever the buffer gets requeued and is thus no longer
+valid.
+
+This patch copies the bytesused variable so it is available until the
+next frame captured again.
+
+Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
+---
+ plugins/input_uvc/input_uvc.c | 6 +++---
+ plugins/input_uvc/v4l2uvc.c   | 2 ++
+ plugins/input_uvc/v4l2uvc.h   | 1 +
+ 3 files changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/plugins/input_uvc/input_uvc.c b/plugins/input_uvc/input_uvc.c
+index e6c74fd..64f66cb 100644
+--- a/plugins/input_uvc/input_uvc.c
++++ b/plugins/input_uvc/input_uvc.c
+@@ -482,7 +482,7 @@ void *cam_thread(void *arg)
+             exit(EXIT_FAILURE);
+         }
+-        //DBG("received frame of size: %d from plugin: %d\n", pcontext->videoIn->buf.bytesused, pcontext->id);
++        //DBG("received frame of size: %d from plugin: %d\n", pcontext->videoIn->tmpbytesused, pcontext->id);
+         /*
+          * Workaround for broken, corrupted frames:
+@@ -491,7 +491,7 @@ void *cam_thread(void *arg)
+          * For example a VGA (640x480) webcam picture is normally >= 8kByte large,
+          * corrupted frames are smaller.
+          */
+-        if(pcontext->videoIn->buf.bytesused < minimum_size) {
++        if(pcontext->videoIn->tmpbytesused < minimum_size) {
+             DBG("dropping too small frame, assuming it as broken\n");
+             continue;
+         }
+@@ -529,7 +529,7 @@ void *cam_thread(void *arg)
+         } else {
+         #endif
+             //DBG("copying frame from input: %d\n", (int)pcontext->id);
+-            pglobal->in[pcontext->id].size = memcpy_picture(pglobal->in[pcontext->id].buf, pcontext->videoIn->tmpbuffer, pcontext->videoIn->buf.bytesused);
++            pglobal->in[pcontext->id].size = memcpy_picture(pglobal->in[pcontext->id].buf, pcontext->videoIn->tmpbuffer, pcontext->videoIn->tmpbytesused);
+         #ifndef NO_LIBJPEG
+         }
+         #endif
+diff --git a/plugins/input_uvc/v4l2uvc.c b/plugins/input_uvc/v4l2uvc.c
+index c5a5aa4..d11510c 100644
+--- a/plugins/input_uvc/v4l2uvc.c
++++ b/plugins/input_uvc/v4l2uvc.c
+@@ -532,6 +532,7 @@ int uvcGrab(struct vdIn *vd)
+         */
+         memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
++      vd->tmpbytesused = vd->buf.bytesused;
+         if(debug)
+             fprintf(stderr, "bytes in used %d \n", vd->buf.bytesused);
+@@ -570,6 +571,7 @@ int close_v4l2(struct vdIn *vd)
+     if(vd->tmpbuffer)
+         free(vd->tmpbuffer);
+     vd->tmpbuffer = NULL;
++    vd->tmpbytesused = 0;
+     free(vd->framebuffer);
+     vd->framebuffer = NULL;
+     free(vd->videodevice);
+diff --git a/plugins/input_uvc/v4l2uvc.h b/plugins/input_uvc/v4l2uvc.h
+index 022c57e..2c7c8ba 100644
+--- a/plugins/input_uvc/v4l2uvc.h
++++ b/plugins/input_uvc/v4l2uvc.h
+@@ -83,6 +83,7 @@ struct vdIn {
+     struct v4l2_requestbuffers rb;
+     void *mem[NB_BUFFER];
+     unsigned char *tmpbuffer;
++    int tmpbytesused;
+     unsigned char *framebuffer;
+     streaming_state streamingState;
+     int grabmethod;
+-- 
+1.9.1
+
diff --git a/multimedia/mjpg-streamer/patches/041-Stop-leaking-data-via-struct-v4l2_buffer.patch b/multimedia/mjpg-streamer/patches/041-Stop-leaking-data-via-struct-v4l2_buffer.patch
new file mode 100644 (file)
index 0000000..4f45da5
--- /dev/null
@@ -0,0 +1,242 @@
+From 11b28b36a8711b53658e8bbc50435595522f91ba Mon Sep 17 00:00:00 2001
+From: Olliver Schinagl <o.schinagl@ultimaker.com>
+Date: Wed, 29 Oct 2014 11:21:16 +0100
+Subject: [PATCH 2/7] Stop leaking data via struct v4l2_buffer
+
+Before the 3.16 kernel, the v4l2_buffer was leaking data and violating
+its own spec. Since 3.16 this has been corrected and after calling the
+QBUF ioctl, the struct gets cleaned up.
+
+Right now, input_uvc assumes the buffer is valid at all times. This no
+longer being true, this patch removes the v4l2_buffer from the vdIn
+struct. Certain values are still needed outside of this buffer however,
+the length buffer in the buffer array 'mem' and the timestamp. These are
+now stored in the vdIn struct.
+
+All of this is still somewhat hackish, as a) the processing of the image
+should really be done inside the uvcGrab function between the queuing
+and dequeing of the buffers (or separate that into 3 functions, deq, q
+and process and call them from input_uvc). b) we are still copying the
+image using memcpy, which is something we don't really want and defeats
+the purpose of using a mmap in the first place. Changing this however
+requires some heavier re-architecting and in the end, may still not be avoided.
+
+More information about this bug and change can be found on the
+linux-media mailing list[0] with the title uvcvideo fails on 3.16 and
+3.17 kernels.
+
+[0] http://www.spinics.net/lists/linux-media/msg81515.html
+
+Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
+---
+ plugins/input_uvc/input_uvc.c |  6 ++--
+ plugins/input_uvc/v4l2uvc.c   | 64 +++++++++++++++++++++++--------------------
+ plugins/input_uvc/v4l2uvc.h   |  4 ++-
+ 3 files changed, 41 insertions(+), 33 deletions(-)
+
+diff --git a/plugins/input_uvc/input_uvc.c b/plugins/input_uvc/input_uvc.c
+index 64f66cb..64ef56c 100644
+--- a/plugins/input_uvc/input_uvc.c
++++ b/plugins/input_uvc/input_uvc.c
+@@ -500,8 +500,8 @@ void *cam_thread(void *arg)
+         if (pcontext->videoIn->soft_framedrop == 1) {
+             unsigned long last = pglobal->in[pcontext->id].timestamp.tv_sec * 1000 +
+                                 (pglobal->in[pcontext->id].timestamp.tv_usec/1000); // convert to ms
+-            unsigned long current = pcontext->videoIn->buf.timestamp.tv_sec * 1000 +
+-                                    pcontext->videoIn->buf.timestamp.tv_usec/1000; // convert to ms
++            unsigned long current = pcontext->videoIn->tmptimestamp.tv_sec * 1000 +
++                                    pcontext->videoIn->tmptimestamp.tv_usec/1000; // convert to ms
+             // if the requested time did not esplashed skip the frame
+             if ((current - last) < pcontext->videoIn->frame_period_time) {
+@@ -543,7 +543,7 @@ void *cam_thread(void *arg)
+ #endif
+         /* copy this frame's timestamp to user space */
+-        pglobal->in[pcontext->id].timestamp = pcontext->videoIn->buf.timestamp;
++        pglobal->in[pcontext->id].timestamp = pcontext->videoIn->tmptimestamp;
+         /* signal fresh_frame */
+         pthread_cond_broadcast(&pglobal->in[pcontext->id].db_update);
+diff --git a/plugins/input_uvc/v4l2uvc.c b/plugins/input_uvc/v4l2uvc.c
+index d11510c..7ec5eec 100644
+--- a/plugins/input_uvc/v4l2uvc.c
++++ b/plugins/input_uvc/v4l2uvc.c
+@@ -217,6 +217,9 @@ static int init_v4l2(struct vdIn *vd)
+ {
+     int i;
+     int ret = 0;
++    struct v4l2_buffer buf;
++
++
+     if((vd->fd = OPEN_VIDEO(vd->videodevice, O_RDWR)) == -1) {
+         perror("ERROR opening V4L interface");
+         DBG("errno: %d", errno);
+@@ -375,26 +378,27 @@ static int init_v4l2(struct vdIn *vd)
+      * map the buffers
+      */
+     for(i = 0; i < NB_BUFFER; i++) {
+-        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
+-        vd->buf.index = i;
+-        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+-        vd->buf.memory = V4L2_MEMORY_MMAP;
+-        ret = xioctl(vd->fd, VIDIOC_QUERYBUF, &vd->buf);
++        memset(&buf, 0, sizeof(struct v4l2_buffer));
++        buf.index = i;
++        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++        buf.memory = V4L2_MEMORY_MMAP;
++        ret = xioctl(vd->fd, VIDIOC_QUERYBUF, &buf);
+         if(ret < 0) {
+             perror("Unable to query buffer");
+             goto fatal;
+         }
+         if(debug)
+-            fprintf(stderr, "length: %u offset: %u\n", vd->buf.length, vd->buf.m.offset);
++            fprintf(stderr, "length: %u offset: %u\n", buf.length, buf.m.offset);
+         vd->mem[i] = mmap(0 /* start anywhere */ ,
+-                          vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
+-                          vd->buf.m.offset);
++                          buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
++                          buf.m.offset);
+         if(vd->mem[i] == MAP_FAILED) {
+             perror("Unable to map buffer");
+             goto fatal;
+         }
++      vd->memlength[i] = buf.length;
+         if(debug)
+             fprintf(stderr, "Buffer mapped at address %p.\n", vd->mem[i]);
+     }
+@@ -403,11 +407,11 @@ static int init_v4l2(struct vdIn *vd)
+      * Queue the buffers.
+      */
+     for(i = 0; i < NB_BUFFER; ++i) {
+-        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
+-        vd->buf.index = i;
+-        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+-        vd->buf.memory = V4L2_MEMORY_MMAP;
+-        ret = xioctl(vd->fd, VIDIOC_QBUF, &vd->buf);
++        memset(&buf, 0, sizeof(struct v4l2_buffer));
++        buf.index = i;
++        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++        buf.memory = V4L2_MEMORY_MMAP;
++        ret = xioctl(vd->fd, VIDIOC_QBUF, &buf);
+         if(ret < 0) {
+             perror("Unable to queue buffer");
+             goto fatal;;
+@@ -499,17 +503,18 @@ int memcpy_picture(unsigned char *out, unsigned char *buf, int size)
+ int uvcGrab(struct vdIn *vd)
+ {
+ #define HEADERFRAME1 0xaf
++    struct v4l2_buffer buf;
+     int ret;
+     if(vd->streamingState == STREAMING_OFF) {
+         if(video_enable(vd))
+             goto err;
+     }
+-    memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
+-    vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+-    vd->buf.memory = V4L2_MEMORY_MMAP;
++    memset(&buf, 0, sizeof(struct v4l2_buffer));
++    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++    buf.memory = V4L2_MEMORY_MMAP;
+-    ret = xioctl(vd->fd, VIDIOC_DQBUF, &vd->buf);
++    ret = xioctl(vd->fd, VIDIOC_DQBUF, &buf);
+     if(ret < 0) {
+         perror("Unable to dequeue buffer");
+         goto err;
+@@ -517,33 +522,34 @@ int uvcGrab(struct vdIn *vd)
+     switch(vd->formatIn) {
+     case V4L2_PIX_FMT_MJPEG:
+-        if(vd->buf.bytesused <= HEADERFRAME1) {
++        if(buf.bytesused <= HEADERFRAME1) {
+             /* Prevent crash
+                                                         * on empty image */
+             fprintf(stderr, "Ignoring empty buffer ...\n");
+             return 0;
+         }
+-        /* memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
++        /* memcpy(vd->tmpbuffer, vd->mem[buf.index], buf.bytesused);
+-        memcpy (vd->tmpbuffer, vd->mem[vd->buf.index], HEADERFRAME1);
++        memcpy (vd->tmpbuffer, vd->mem[buf.index], HEADERFRAME1);
+         memcpy (vd->tmpbuffer + HEADERFRAME1, dht_data, sizeof(dht_data));
+-        memcpy (vd->tmpbuffer + HEADERFRAME1 + sizeof(dht_data), vd->mem[vd->buf.index] + HEADERFRAME1, (vd->buf.bytesused - HEADERFRAME1));
++        memcpy (vd->tmpbuffer + HEADERFRAME1 + sizeof(dht_data), vd->mem[buf.index] + HEADERFRAME1, (buf.bytesused - HEADERFRAME1));
+         */
+-        memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
+-      vd->tmpbytesused = vd->buf.bytesused;
++        memcpy(vd->tmpbuffer, vd->mem[buf.index], buf.bytesused);
++      vd->tmpbytesused = buf.bytesused;
++      vd->tmptimestamp = buf.timestamp;
+         if(debug)
+-            fprintf(stderr, "bytes in used %d \n", vd->buf.bytesused);
++            fprintf(stderr, "bytes in used %d \n", buf.bytesused);
+         break;
+     case V4L2_PIX_FMT_RGB565:
+     case V4L2_PIX_FMT_YUYV:
+     case V4L2_PIX_FMT_RGB24:
+-        if(vd->buf.bytesused > vd->framesizeIn)
+-            memcpy(vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->framesizeIn);
++        if(buf.bytesused > vd->framesizeIn)
++            memcpy(vd->framebuffer, vd->mem[buf.index], (size_t) vd->framesizeIn);
+         else
+-            memcpy(vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->buf.bytesused);
++            memcpy(vd->framebuffer, vd->mem[buf.index], (size_t) buf.bytesused);
+         break;
+     default:
+@@ -551,7 +557,7 @@ int uvcGrab(struct vdIn *vd)
+         break;
+     }
+-    ret = xioctl(vd->fd, VIDIOC_QBUF, &vd->buf);
++    ret = xioctl(vd->fd, VIDIOC_QBUF, &buf);
+     if(ret < 0) {
+         perror("Unable to requeue buffer");
+         goto err;
+@@ -947,7 +953,7 @@ int setResolution(struct vdIn *vd, int width, int height)
+         DBG("Unmap buffers\n");
+         int i;
+         for(i = 0; i < NB_BUFFER; i++)
+-            munmap(vd->mem[i], vd->buf.length);
++            munmap(vd->mem[i], vd->memlength[i]);
+         if(CLOSE_VIDEO(vd->fd) == 0) {
+             DBG("Device closed successfully\n");
+diff --git a/plugins/input_uvc/v4l2uvc.h b/plugins/input_uvc/v4l2uvc.h
+index 2c7c8ba..e625957 100644
+--- a/plugins/input_uvc/v4l2uvc.h
++++ b/plugins/input_uvc/v4l2uvc.h
+@@ -35,6 +35,7 @@
+ #include <sys/ioctl.h>
+ #include <sys/mman.h>
+ #include <sys/select.h>
++#include <sys/time.h>
+ #include <linux/types.h>          /* for videodev2.h */
+ #include <linux/videodev2.h>
+@@ -79,11 +80,12 @@ struct vdIn {
+     char *pictName;
+     struct v4l2_capability cap;
+     struct v4l2_format fmt;
+-    struct v4l2_buffer buf;
+     struct v4l2_requestbuffers rb;
+     void *mem[NB_BUFFER];
++    int memlength[NB_BUFFER];
+     unsigned char *tmpbuffer;
+     int tmpbytesused;
++    struct timeval tmptimestamp;
+     unsigned char *framebuffer;
+     streaming_state streamingState;
+     int grabmethod;
+-- 
+1.9.1
+