From b17a973f141cb6f5c1d2f79b3aac9a68aac02679 Mon Sep 17 00:00:00 2001 From: Mantas Pucka Date: Wed, 11 Mar 2015 13:50:54 +0200 Subject: [PATCH] mjpg-streamer: fix compatibility with new kernels (>=3.16) 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 --- multimedia/mjpg-streamer/Makefile | 2 +- ...used-variable-from-struct-v4l2_buffe.patch | 87 +++++++ ...-leaking-data-via-struct-v4l2_buffer.patch | 242 ++++++++++++++++++ 3 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 multimedia/mjpg-streamer/patches/040-Buffer-the-bytesused-variable-from-struct-v4l2_buffe.patch create mode 100644 multimedia/mjpg-streamer/patches/041-Stop-leaking-data-via-struct-v4l2_buffer.patch diff --git a/multimedia/mjpg-streamer/Makefile b/multimedia/mjpg-streamer/Makefile index d2e1537d9f..0a297d4e67 100644 --- a/multimedia/mjpg-streamer/Makefile +++ b/multimedia/mjpg-streamer/Makefile @@ -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 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 index 0000000000..a228f420c9 --- /dev/null +++ b/multimedia/mjpg-streamer/patches/040-Buffer-the-bytesused-variable-from-struct-v4l2_buffe.patch @@ -0,0 +1,87 @@ +From 19202b54698b343a0207d7e213448e32b8e58fc3 Mon Sep 17 00:00:00 2001 +From: Olliver Schinagl +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 +--- + 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 index 0000000000..4f45da5d6b --- /dev/null +++ b/multimedia/mjpg-streamer/patches/041-Stop-leaking-data-via-struct-v4l2_buffer.patch @@ -0,0 +1,242 @@ +From 11b28b36a8711b53658e8bbc50435595522f91ba Mon Sep 17 00:00:00 2001 +From: Olliver Schinagl +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 +--- + 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 + #include + #include ++#include + + #include /* for videodev2.h */ + #include +@@ -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 + -- 2.30.2