1 /**************************************************************************
2 * Copyright (C) 2000-2019 by Johan Maes *
3 * on4qz@telenet.be *
4 * http://users.telenet.be/on4qz *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
20 * *
21 * *
22 * This software contains parts of the following softwares *
23 * videoCapture.cpp -- Kapture *
24 * *
25 * Copyright (C) 2006-2009 *
26 * Detlev Casanova (detlev.casanova@gmail.com) *
27 * *
28 * qv4l2: a control panel controlling v4l2 devices. *
29 * *
30 * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl> * *
31 ***************************************************************************/
32
33 #include "videocapture.h"
34 #include "configparams.h"
35 #include "appglobal.h"
36 #include <stdio.h>
37 #include <string.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/mman.h>
43 #include <sys/select.h>
44 #include <libv4l2.h>
45 #include <libv4lconvert.h>
46
47 #include <QtGui>
48 #include <QApplication>
49 #include <QMainWindow>
50 #include <QImage>
51 #include <QPixmap>
52
53 #include <QLabel>
54 #include <QSize>
55
56
57
videoCapture()58 videoCapture::videoCapture()
59 {
60 dev = 0;
61 opened = false;
62 allocated = false;
63 localImage=nullptr;
64 numBuffers=2;
65
66 }
67
~videoCapture()68 videoCapture::~videoCapture()
69 {
70 if(localImage!=nullptr) delete localImage;
71 close();
72 }
73
close()74 void videoCapture::close()
75 {
76 if(!opened) return;
77
78 v4l2_close(dev);
79 opened = false;
80 allocated = false;
81 }
82
open(QString videoDev)83 bool videoCapture::open(QString videoDev)
84 {
85 videoDevice=videoDev;
86 if (opened) return true;
87 addToLog("opening Videocapture device",LOGCAM);
88
89 dev = v4l2_open(videoDevice.toLatin1().data(), O_RDWR);
90 if (dev < 0)
91 {
92 addToLog(QString("Error opening %1, %2").arg(videoDevice).arg(errno),LOGCAM);
93 return false;
94 }
95 // dumpCaps(cap);
96 opened = true;
97 return opened;
98 }
99
100
init(int pixelFormat,int width,int height)101 bool videoCapture::init(int pixelFormat,int width,int height)
102 {
103
104 getFormat(srcFmt);
105 srcFmt.fmt.pix.pixelformat=static_cast<uint>(pixelFormat);
106 srcFmt.fmt.pix.width=static_cast<uint>(width);
107 srcFmt.fmt.pix.height=static_cast<uint>(height);
108 // srcFmt.fmt.pix.width=320;
109 // srcFmt.fmt.pix.height=240;
110 setFormat(srcFmt);
111 dstFmt=srcFmt;
112 qFmt=checkConversionNeeded();
113 if(qFmt==QImage::Format_Invalid)
114 {
115 needsConversion=true;
116 convertData = v4lconvert_create(dev);
117 dstFmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB24;
118 qFmt=QImage::Format_RGB888;
119 }
120 else
121 {
122 needsConversion=false;
123 }
124
125 if(needsConversion)
126 {
127 v4lconvert_try_format(convertData, &dstFmt, &srcFmt);
128 getFormat(srcFmt); //restore srcFmt
129 }
130
131 if(localImage) delete localImage;
132 localImage=new QImage( static_cast<int>(dstFmt.fmt.pix.width),static_cast<int>(dstFmt.fmt.pix.height),qFmt);
133 if (!allocated)
134 {
135 if(allocateBuffers(numBuffers)>=0)
136 {
137 allocated=true;
138 }
139 else
140 {
141 return false;
142 }
143 }
144 else
145 {
146 allocateBuffers(0); // deallocate buffers
147 if(allocateBuffers(numBuffers)>=0)
148 {
149 allocated=true;
150 }
151 else
152 {
153 return false;
154 }
155 }
156 return true;
157 }
158
allocateBuffers(uint numBufs)159 int videoCapture::allocateBuffers(uint numBufs)
160 {
161 int ret;
162 memset(&rb, 0, sizeof rb);
163 rb.count = static_cast<uint>(numBufs);
164 rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
165 rb.memory = V4L2_MEMORY_MMAP;
166
167 ret = v4l2_ioctl(dev, VIDIOC_REQBUFS, &rb);
168 if (ret < 0)
169 {
170 addToLog(QString("Unable to allocate buffers for %1, %2").arg(videoDevice).arg(errno),LOGCAM);
171 }
172 return ret;
173 }
174
175
setFormat(v4l2_format & fmt)176 bool videoCapture::setFormat(v4l2_format &fmt)
177 {
178 addToLog("setFormat",LOGCAM);
179 // memset(&fmt, 0, sizeof fmt);
180 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
181 // fmt.fmt.pix.width = width;
182 // fmt.fmt.pix.height = height;
183 fmt.fmt.pix.field = V4L2_FIELD_ANY;
184 // fmt.fmt.pix.pixelformat = pixelformat;
185 // fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
186 // fmt.fmt.pix.pixelformat =V4L2_PIX_FMT_RGB24;
187 fmt.fmt.pix.bytesperline=0;
188 if (v4l2_ioctl(dev, VIDIOC_S_FMT, &fmt) < 0)
189 {
190 addToLog(QString("Error while setting format , %1").arg(strerror(errno)),LOGCAM);
191 return false;
192 }
193 getFormat(fmt);
194 return true;
195 }
196
getFormat(v4l2_format & fmt)197 bool videoCapture::getFormat(v4l2_format &fmt)
198 {
199 addToLog("getFormat",LOGCAM);
200 memset(&fmt, 0, sizeof fmt);
201 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
202 if (v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) < 0)
203 {
204 addToLog(QString("Error while getting format , %1").arg(errno),LOGCAM);
205 return false;
206 }
207 return true;
208 }
209
210
211
getFrame()212 int videoCapture::getFrame()
213 {
214 int ret = 0;
215 // Dequeue a buffer.
216 errorString.clear();
217
218 ret = v4l2_ioctl(dev, VIDIOC_DQBUF, &buf);
219 //addToLog(QString("Dequeue buffer %1").arg(buf.index),LOGCAM);
220 if(ret==EAGAIN)
221 {
222 return 0;
223 }
224 else if (ret < 0)
225 {
226 addToLog(QString("Unable to dequeue buffer , %1").arg(strerror(errno)),LOGCAM);
227 errorString=QString("Unable to dequeue buffer , %1").arg(strerror(errno));
228 return -1;
229 }
230
231 convert(mem[buf.index]);
232
233 // Requeue the buffer.
234 ret = v4l2_ioctl(dev, VIDIOC_QBUF, &buf);
235 //addToLog(QString("Requeue buffer %1").arg(buf.index),LOGCAM);
236 if (ret < 0)
237 {
238 addToLog(QString("Unable to requeue buffer %1").arg(errno),LOGCAM);
239 return -1;
240 }
241 return 1;
242 }
243
244
245
convert(unsigned char * src)246 bool videoCapture::convert(unsigned char *src)
247 {
248 int result=0;
249 // if (localImage!=NULL) delete localImage;
250 // localImage=new QImage( dstFmt.fmt.pix.width,dstFmt.fmt.pix.height,qFmt);
251 if(needsConversion)
252 {
253 result=v4lconvert_convert(convertData,&srcFmt,&dstFmt,
254 src,static_cast<int>(srcFmt.fmt.pix.sizeimage),
255 localImage->bits(),static_cast<int>(dstFmt.fmt.pix.sizeimage));
256 }
257 else
258 {
259 #if (QT_VERSION < QT_VERSION_CHECK(5,10,0))
260 memcpy(localImage->bits(),src,static_cast<uint>(localImage->byteCount()));
261 #else
262 memcpy(localImage->bits(),src,static_cast<uint>(localImage->sizeInBytes()));
263 #endif
264 }
265 if (result<0) return false;
266 return true;
267 }
268
269
currentWidth(v4l2_format fmt) const270 int videoCapture::currentWidth(v4l2_format fmt) const
271 {
272 return (static_cast<int>(fmt.fmt.pix.width));
273 }
274
currentHeight(v4l2_format fmt) const275 int videoCapture::currentHeight(v4l2_format fmt) const
276 {
277 return (static_cast<int>(fmt.fmt.pix.height));
278 }
279
currentPixelFormat(v4l2_format fmt) const280 int videoCapture::currentPixelFormat(v4l2_format fmt) const
281 {
282 return (static_cast<int>( fmt.fmt.pix.pixelformat));
283 }
284
285
286
287
288
289
captureStop()290 bool videoCapture::captureStop()
291 {
292 int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
293 int ret;
294
295 if(!streaming) return false;
296 ret = v4l2_ioctl(dev, VIDIOC_STREAMOFF, &type);
297 if (ret < 0)
298 {
299 addToLog(QString("Unable to stop capture, %1").arg(errno),LOGCAM);
300 return false;
301 }
302
303 streaming = false;
304 return true;
305 }
306
captureStart()307 bool videoCapture::captureStart()
308 {
309 uint i;
310 int ret;
311 if (!opened) return false;
312 addToLog("captureStart",LOGCAM);
313
314 //Allocate buffers
315
316 if(!mmapped)
317 {
318
319 // Map the buffers.
320 memset(&buf, 0, sizeof buf);
321 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
322 buf.memory = V4L2_MEMORY_MMAP;
323 for (i = 0; i < numBuffers; i++)
324 {
325 buf.index = static_cast<uint>(i);
326 ret = v4l2_ioctl(dev, VIDIOC_QUERYBUF, &buf);
327 if (ret < 0)
328 {
329 addToLog(QString("Unable to query buffer %1").arg(ret),LOGCAM);
330 return false;
331 }
332 mem[i] = static_cast<uchar *>(v4l2_mmap(nullptr, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, dev, buf.m.offset));
333 if (mem[i] == MAP_FAILED)
334 {
335 addToLog(QString("Unable to map buffers %1").arg(ret),LOGCAM);
336 return false;
337 }
338 bufLength = buf.length;
339 mmapped = true;
340 }
341
342 // Queue the buffers
343
344 for (i = 0; i < numBuffers; i++)
345 {
346 buf.index = i;
347 ret = v4l2_ioctl(dev, VIDIOC_QBUF, &buf);
348 if (ret < 0)
349 {
350 addToLog(QString("Unable to queue buffer %1").arg(errno),LOGCAM);
351 return false;
352 }
353 }
354 }
355
356 // Start streaming.
357 ret = v4l2_ioctl(dev, VIDIOC_STREAMON, &buf.type);
358 if (ret < 0)
359 {
360 addToLog(QString("Unable to start capture %1").arg(errno),LOGCAM);
361 return false;
362 }
363 streaming = true;
364 return true;
365 }
366
stopStreaming()367 bool videoCapture::stopStreaming()
368 {
369 uint i;
370 bool ok=true;
371 if(!streaming) return false;
372 for(i=0;i<numBuffers;i++)
373 {
374 if (v4l2_munmap(mem[i], bufLength) == -1)
375 {
376 addToLog(QString("videoCapture::stopStreaming : munmap %1 failed. errno = %2").arg(i).arg(errno),LOGCAM);
377 ok=false;
378 }
379
380 }
381 if(ok) mmapped = false;
382
383 if(captureStop())
384 {
385 streaming = false;
386 addToLog(" * Succesful Stopped",LOGCAM);
387 }
388 return true;
389 }
390
391
392
startSnapshots()393 bool videoCapture::startSnapshots()
394 {
395 if(!opened) return false;
396 if(!captureStart()) return false;
397 return true;
398 }
399
checkConversionNeeded()400 enum QImage::Format videoCapture::checkConversionNeeded ()
401 {
402 QImage::Format qfmt;
403 switch(srcFmt.fmt.pix.pixelformat)
404 {
405 case V4L2_PIX_FMT_BGR32: qfmt=QImage::Format_RGB32; break;
406 case V4L2_PIX_FMT_RGB24: qfmt=QImage::Format_RGB888 ; break;
407 case V4L2_PIX_FMT_RGB565: qfmt=QImage::Format_RGB16 ; break;
408 case V4L2_PIX_FMT_RGB555: qfmt=QImage::Format_RGB555 ; break;
409 case V4L2_PIX_FMT_RGB444: qfmt=QImage::Format_RGB444 ; break;
410 default: qfmt=QImage::Format_Invalid;
411 }
412 return qfmt;
413 }
414
415
dumpCaps(v4l2_capability & cap)416 void videoCapture::dumpCaps(v4l2_capability &cap)
417 {
418 if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) addToLog("The device supports the Video Capture interface",LOGCAM);
419 if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) addToLog("The device supports the Video Output interface",LOGCAM);
420 if (cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) addToLog("The device supports the Video Overlay interface",LOGCAM);
421 if (cap.capabilities & V4L2_CAP_VBI_CAPTURE) addToLog("The device supports the Raw VBI Capture interface",LOGCAM);
422 if (cap.capabilities & V4L2_CAP_VBI_OUTPUT) addToLog("The device supports the Raw VBI Output interface",LOGCAM);
423 if (cap.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) addToLog("The device supports the Sliced VBI Capture interface",LOGCAM);
424 if (cap.capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) addToLog("The device supports the Sliced VBI Output interface",LOGCAM);
425 if (cap.capabilities & V4L2_CAP_RDS_CAPTURE) addToLog("[to be defined]",LOGCAM);
426 #ifdef V4L2_CAP_VIDEO_OUTPUT_OVERLAY
427 if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) addToLog("The device supports the Video Output Overlay (OSD) interface",LOGCAM);
428 #endif
429 if (cap.capabilities & V4L2_CAP_TUNER) addToLog("The device has some sort of tuner or modulator",LOGCAM);
430 if (cap.capabilities & V4L2_CAP_AUDIO) addToLog("The device has audio inputs or outputs.",LOGCAM);
431 if (cap.capabilities & V4L2_CAP_READWRITE) addToLog("The device supports the read() and/or write() I/O methods.",LOGCAM);
432 if (cap.capabilities & V4L2_CAP_ASYNCIO) addToLog("The device supports the asynchronous I/O methods.",LOGCAM);
433 if (cap.capabilities & V4L2_CAP_STREAMING) addToLog("The device supports the streaming I/O method.",LOGCAM);
434 }
435
436
437
438