4f45da5d6b4946f73fae642c145249c73fe33d7a
[librecmc/librecmc-fossil.git] /
1 From 11b28b36a8711b53658e8bbc50435595522f91ba Mon Sep 17 00:00:00 2001
2 From: Olliver Schinagl <o.schinagl@ultimaker.com>
3 Date: Wed, 29 Oct 2014 11:21:16 +0100
4 Subject: [PATCH 2/7] Stop leaking data via struct v4l2_buffer
5
6 Before the 3.16 kernel, the v4l2_buffer was leaking data and violating
7 its own spec. Since 3.16 this has been corrected and after calling the
8 QBUF ioctl, the struct gets cleaned up.
9
10 Right now, input_uvc assumes the buffer is valid at all times. This no
11 longer being true, this patch removes the v4l2_buffer from the vdIn
12 struct. Certain values are still needed outside of this buffer however,
13 the length buffer in the buffer array 'mem' and the timestamp. These are
14 now stored in the vdIn struct.
15
16 All of this is still somewhat hackish, as a) the processing of the image
17 should really be done inside the uvcGrab function between the queuing
18 and dequeing of the buffers (or separate that into 3 functions, deq, q
19 and process and call them from input_uvc). b) we are still copying the
20 image using memcpy, which is something we don't really want and defeats
21 the purpose of using a mmap in the first place. Changing this however
22 requires some heavier re-architecting and in the end, may still not be avoided.
23
24 More information about this bug and change can be found on the
25 linux-media mailing list[0] with the title uvcvideo fails on 3.16 and
26 3.17 kernels.
27
28 [0] http://www.spinics.net/lists/linux-media/msg81515.html
29
30 Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
31 ---
32  plugins/input_uvc/input_uvc.c |  6 ++--
33  plugins/input_uvc/v4l2uvc.c   | 64 +++++++++++++++++++++++--------------------
34  plugins/input_uvc/v4l2uvc.h   |  4 ++-
35  3 files changed, 41 insertions(+), 33 deletions(-)
36
37 diff --git a/plugins/input_uvc/input_uvc.c b/plugins/input_uvc/input_uvc.c
38 index 64f66cb..64ef56c 100644
39 --- a/plugins/input_uvc/input_uvc.c
40 +++ b/plugins/input_uvc/input_uvc.c
41 @@ -500,8 +500,8 @@ void *cam_thread(void *arg)
42          if (pcontext->videoIn->soft_framedrop == 1) {
43              unsigned long last = pglobal->in[pcontext->id].timestamp.tv_sec * 1000 +
44                                  (pglobal->in[pcontext->id].timestamp.tv_usec/1000); // convert to ms
45 -            unsigned long current = pcontext->videoIn->buf.timestamp.tv_sec * 1000 +
46 -                                    pcontext->videoIn->buf.timestamp.tv_usec/1000; // convert to ms
47 +            unsigned long current = pcontext->videoIn->tmptimestamp.tv_sec * 1000 +
48 +                                    pcontext->videoIn->tmptimestamp.tv_usec/1000; // convert to ms
49  
50              // if the requested time did not esplashed skip the frame
51              if ((current - last) < pcontext->videoIn->frame_period_time) {
52 @@ -543,7 +543,7 @@ void *cam_thread(void *arg)
53  #endif
54  
55          /* copy this frame's timestamp to user space */
56 -        pglobal->in[pcontext->id].timestamp = pcontext->videoIn->buf.timestamp;
57 +        pglobal->in[pcontext->id].timestamp = pcontext->videoIn->tmptimestamp;
58  
59          /* signal fresh_frame */
60          pthread_cond_broadcast(&pglobal->in[pcontext->id].db_update);
61 diff --git a/plugins/input_uvc/v4l2uvc.c b/plugins/input_uvc/v4l2uvc.c
62 index d11510c..7ec5eec 100644
63 --- a/plugins/input_uvc/v4l2uvc.c
64 +++ b/plugins/input_uvc/v4l2uvc.c
65 @@ -217,6 +217,9 @@ static int init_v4l2(struct vdIn *vd)
66  {
67      int i;
68      int ret = 0;
69 +    struct v4l2_buffer buf;
70 +
71 +
72      if((vd->fd = OPEN_VIDEO(vd->videodevice, O_RDWR)) == -1) {
73          perror("ERROR opening V4L interface");
74          DBG("errno: %d", errno);
75 @@ -375,26 +378,27 @@ static int init_v4l2(struct vdIn *vd)
76       * map the buffers
77       */
78      for(i = 0; i < NB_BUFFER; i++) {
79 -        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
80 -        vd->buf.index = i;
81 -        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
82 -        vd->buf.memory = V4L2_MEMORY_MMAP;
83 -        ret = xioctl(vd->fd, VIDIOC_QUERYBUF, &vd->buf);
84 +        memset(&buf, 0, sizeof(struct v4l2_buffer));
85 +        buf.index = i;
86 +        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
87 +        buf.memory = V4L2_MEMORY_MMAP;
88 +        ret = xioctl(vd->fd, VIDIOC_QUERYBUF, &buf);
89          if(ret < 0) {
90              perror("Unable to query buffer");
91              goto fatal;
92          }
93  
94          if(debug)
95 -            fprintf(stderr, "length: %u offset: %u\n", vd->buf.length, vd->buf.m.offset);
96 +            fprintf(stderr, "length: %u offset: %u\n", buf.length, buf.m.offset);
97  
98          vd->mem[i] = mmap(0 /* start anywhere */ ,
99 -                          vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
100 -                          vd->buf.m.offset);
101 +                          buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
102 +                          buf.m.offset);
103          if(vd->mem[i] == MAP_FAILED) {
104              perror("Unable to map buffer");
105              goto fatal;
106          }
107 +       vd->memlength[i] = buf.length;
108          if(debug)
109              fprintf(stderr, "Buffer mapped at address %p.\n", vd->mem[i]);
110      }
111 @@ -403,11 +407,11 @@ static int init_v4l2(struct vdIn *vd)
112       * Queue the buffers.
113       */
114      for(i = 0; i < NB_BUFFER; ++i) {
115 -        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
116 -        vd->buf.index = i;
117 -        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
118 -        vd->buf.memory = V4L2_MEMORY_MMAP;
119 -        ret = xioctl(vd->fd, VIDIOC_QBUF, &vd->buf);
120 +        memset(&buf, 0, sizeof(struct v4l2_buffer));
121 +        buf.index = i;
122 +        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
123 +        buf.memory = V4L2_MEMORY_MMAP;
124 +        ret = xioctl(vd->fd, VIDIOC_QBUF, &buf);
125          if(ret < 0) {
126              perror("Unable to queue buffer");
127              goto fatal;;
128 @@ -499,17 +503,18 @@ int memcpy_picture(unsigned char *out, unsigned char *buf, int size)
129  int uvcGrab(struct vdIn *vd)
130  {
131  #define HEADERFRAME1 0xaf
132 +    struct v4l2_buffer buf;
133      int ret;
134  
135      if(vd->streamingState == STREAMING_OFF) {
136          if(video_enable(vd))
137              goto err;
138      }
139 -    memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
140 -    vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
141 -    vd->buf.memory = V4L2_MEMORY_MMAP;
142 +    memset(&buf, 0, sizeof(struct v4l2_buffer));
143 +    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
144 +    buf.memory = V4L2_MEMORY_MMAP;
145  
146 -    ret = xioctl(vd->fd, VIDIOC_DQBUF, &vd->buf);
147 +    ret = xioctl(vd->fd, VIDIOC_DQBUF, &buf);
148      if(ret < 0) {
149          perror("Unable to dequeue buffer");
150          goto err;
151 @@ -517,33 +522,34 @@ int uvcGrab(struct vdIn *vd)
152  
153      switch(vd->formatIn) {
154      case V4L2_PIX_FMT_MJPEG:
155 -        if(vd->buf.bytesused <= HEADERFRAME1) {
156 +        if(buf.bytesused <= HEADERFRAME1) {
157              /* Prevent crash
158                                                          * on empty image */
159              fprintf(stderr, "Ignoring empty buffer ...\n");
160              return 0;
161          }
162  
163 -        /* memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
164 +        /* memcpy(vd->tmpbuffer, vd->mem[buf.index], buf.bytesused);
165  
166 -        memcpy (vd->tmpbuffer, vd->mem[vd->buf.index], HEADERFRAME1);
167 +        memcpy (vd->tmpbuffer, vd->mem[buf.index], HEADERFRAME1);
168          memcpy (vd->tmpbuffer + HEADERFRAME1, dht_data, sizeof(dht_data));
169 -        memcpy (vd->tmpbuffer + HEADERFRAME1 + sizeof(dht_data), vd->mem[vd->buf.index] + HEADERFRAME1, (vd->buf.bytesused - HEADERFRAME1));
170 +        memcpy (vd->tmpbuffer + HEADERFRAME1 + sizeof(dht_data), vd->mem[buf.index] + HEADERFRAME1, (buf.bytesused - HEADERFRAME1));
171          */
172  
173 -        memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
174 -       vd->tmpbytesused = vd->buf.bytesused;
175 +        memcpy(vd->tmpbuffer, vd->mem[buf.index], buf.bytesused);
176 +       vd->tmpbytesused = buf.bytesused;
177 +       vd->tmptimestamp = buf.timestamp;
178  
179          if(debug)
180 -            fprintf(stderr, "bytes in used %d \n", vd->buf.bytesused);
181 +            fprintf(stderr, "bytes in used %d \n", buf.bytesused);
182          break;
183      case V4L2_PIX_FMT_RGB565:
184      case V4L2_PIX_FMT_YUYV:
185      case V4L2_PIX_FMT_RGB24:
186 -        if(vd->buf.bytesused > vd->framesizeIn)
187 -            memcpy(vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->framesizeIn);
188 +        if(buf.bytesused > vd->framesizeIn)
189 +            memcpy(vd->framebuffer, vd->mem[buf.index], (size_t) vd->framesizeIn);
190          else
191 -            memcpy(vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->buf.bytesused);
192 +            memcpy(vd->framebuffer, vd->mem[buf.index], (size_t) buf.bytesused);
193          break;
194  
195      default:
196 @@ -551,7 +557,7 @@ int uvcGrab(struct vdIn *vd)
197          break;
198      }
199  
200 -    ret = xioctl(vd->fd, VIDIOC_QBUF, &vd->buf);
201 +    ret = xioctl(vd->fd, VIDIOC_QBUF, &buf);
202      if(ret < 0) {
203          perror("Unable to requeue buffer");
204          goto err;
205 @@ -947,7 +953,7 @@ int setResolution(struct vdIn *vd, int width, int height)
206          DBG("Unmap buffers\n");
207          int i;
208          for(i = 0; i < NB_BUFFER; i++)
209 -            munmap(vd->mem[i], vd->buf.length);
210 +            munmap(vd->mem[i], vd->memlength[i]);
211  
212          if(CLOSE_VIDEO(vd->fd) == 0) {
213              DBG("Device closed successfully\n");
214 diff --git a/plugins/input_uvc/v4l2uvc.h b/plugins/input_uvc/v4l2uvc.h
215 index 2c7c8ba..e625957 100644
216 --- a/plugins/input_uvc/v4l2uvc.h
217 +++ b/plugins/input_uvc/v4l2uvc.h
218 @@ -35,6 +35,7 @@
219  #include <sys/ioctl.h>
220  #include <sys/mman.h>
221  #include <sys/select.h>
222 +#include <sys/time.h>
223  
224  #include <linux/types.h>          /* for videodev2.h */
225  #include <linux/videodev2.h>
226 @@ -79,11 +80,12 @@ struct vdIn {
227      char *pictName;
228      struct v4l2_capability cap;
229      struct v4l2_format fmt;
230 -    struct v4l2_buffer buf;
231      struct v4l2_requestbuffers rb;
232      void *mem[NB_BUFFER];
233 +    int memlength[NB_BUFFER];
234      unsigned char *tmpbuffer;
235      int tmpbytesused;
236 +    struct timeval tmptimestamp;
237      unsigned char *framebuffer;
238      streaming_state streamingState;
239      int grabmethod;
240 -- 
241 1.9.1
242