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