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 <mantas@8devices.com>
This commit is contained in:
Mantas Pucka 2015-03-11 13:50:54 +02:00
parent 58245f257f
commit b17a973f14
3 changed files with 330 additions and 1 deletions

View 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

View File

@ -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

View File

@ -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