1 /* Webcamoid, webcam capture application.
2  * Copyright (C) 2017  Gonzalo Exequiel Pedone
3  *
4  * Webcamoid is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Webcamoid is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Web-Site: http://webcamoid.github.io/
18  */
19 
20 #include <QDebug>
21 #include <QVariant>
22 #include <QMap>
23 #include <QMutex>
24 #include <QVector>
25 #include <QDir>
26 #include <QFileSystemWatcher>
27 #include <ak.h>
28 #include <akfrac.h>
29 #include <akcaps.h>
30 #include <akpacket.h>
31 #include <sys/mman.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <linux/videodev2.h>
35 
36 #ifdef HAVE_V4LUTILS
37 #include <libv4l2.h>
38 
39 #define x_ioctl v4l2_ioctl
40 #define x_open v4l2_open
41 #define x_close v4l2_close
42 #define x_read v4l2_read
43 #define x_mmap v4l2_mmap
44 #define x_munmap v4l2_munmap
45 #else
46 #include <unistd.h>
47 #include <sys/ioctl.h>
48 
49 #define x_ioctl ioctl
50 #define x_open open
51 #define x_close close
52 #define x_read read
53 #define x_mmap mmap
54 #define x_munmap munmap
55 #endif
56 
57 #include "capturev4l2.h"
58 #include "capturebuffer.h"
59 
60 using V4l2CtrlTypeMap = QMap<v4l2_ctrl_type, QString>;
61 
initV4l2CtrlTypeMap()62 inline V4l2CtrlTypeMap initV4l2CtrlTypeMap()
63 {
64     V4l2CtrlTypeMap ctrlTypeToStr = {
65         // V4L2 controls
66         {V4L2_CTRL_TYPE_INTEGER     , "integer"    },
67         {V4L2_CTRL_TYPE_BOOLEAN     , "boolean"    },
68         {V4L2_CTRL_TYPE_MENU        , "menu"       },
69         {V4L2_CTRL_TYPE_BUTTON      , "button"     },
70         {V4L2_CTRL_TYPE_INTEGER64   , "integer64"  },
71         {V4L2_CTRL_TYPE_CTRL_CLASS  , "ctrlClass"  },
72 #ifdef HAVE_EXTENDEDCONTROLS
73         {V4L2_CTRL_TYPE_STRING      , "string"     },
74         {V4L2_CTRL_TYPE_BITMASK     , "bitmask"    },
75         {V4L2_CTRL_TYPE_INTEGER_MENU, "integerMenu"}
76 #endif
77     };
78 
79     return ctrlTypeToStr;
80 }
81 
82 Q_GLOBAL_STATIC_WITH_ARGS(V4l2CtrlTypeMap, ctrlTypeToStr, (initV4l2CtrlTypeMap()))
83 
84 using IoMethodMap = QMap<CaptureV4L2::IoMethod, QString>;
85 
initIoMethodMap()86 inline IoMethodMap initIoMethodMap()
87 {
88     IoMethodMap ioMethodToStr = {
89         {CaptureV4L2::IoMethodReadWrite  , "readWrite"  },
90         {CaptureV4L2::IoMethodMemoryMap  , "memoryMap"  },
91         {CaptureV4L2::IoMethodUserPointer, "userPointer"}
92     };
93 
94     return ioMethodToStr;
95 }
96 
97 Q_GLOBAL_STATIC_WITH_ARGS(IoMethodMap, ioMethodToStr, (initIoMethodMap()))
98 
99 using FourccToStrMap = QMap<__u32, QString>;
100 
initFourccToStr()101 inline FourccToStrMap initFourccToStr()
102 {
103     FourccToStrMap fourccToStr = {
104         {V4L2_PIX_FMT_RGB332      , "RGB332"      },
105         {V4L2_PIX_FMT_RGB444      , "RGB444"      },
106         {V4L2_PIX_FMT_ARGB444     , "ARGB444"     },
107         {V4L2_PIX_FMT_XRGB444     , "XRGB444"     },
108         {V4L2_PIX_FMT_RGB555      , "RGB555"      },
109         {V4L2_PIX_FMT_ARGB555     , "ARGB555"     },
110         {V4L2_PIX_FMT_XRGB555     , "XRGB555"     },
111         {V4L2_PIX_FMT_RGB565      , "RGB565"      },
112         {V4L2_PIX_FMT_RGB555X     , "RGB555BE"    },
113         {V4L2_PIX_FMT_ARGB555X    , "ARGB555BE"   },
114         {V4L2_PIX_FMT_XRGB555X    , "XRGB555BE"   },
115         {V4L2_PIX_FMT_RGB565X     , "RGB565BE"    },
116         {V4L2_PIX_FMT_BGR666      , "BGR666"      },
117         {V4L2_PIX_FMT_BGR24       , "BGR"         },
118         {V4L2_PIX_FMT_RGB24       , "RGB"         },
119         {V4L2_PIX_FMT_BGR32       , "BGRX"        },
120         {V4L2_PIX_FMT_ABGR32      , "ABGR"        },
121         {V4L2_PIX_FMT_XBGR32      , "XBGR"        },
122         {V4L2_PIX_FMT_RGB32       , "RGBA"        },
123         {V4L2_PIX_FMT_ARGB32      , "ARGB"        },
124         {V4L2_PIX_FMT_XRGB32      , "XRGB"        },
125         {V4L2_PIX_FMT_GREY        , "GRAY8"       },
126         {V4L2_PIX_FMT_Y4          , "GRAY4"       },
127         {V4L2_PIX_FMT_Y6          , "GRAY6"       },
128         {V4L2_PIX_FMT_Y10         , "GRAY10"      },
129         {V4L2_PIX_FMT_Y12         , "GRAY12"      },
130         {V4L2_PIX_FMT_Y16         , "GRAY16"      },
131         {V4L2_PIX_FMT_Y16_BE      , "GRAY16BE"    },
132         {V4L2_PIX_FMT_SBGGR8      , "SBGGR8"      },
133         {V4L2_PIX_FMT_SGBRG8      , "SGBRG8"      },
134         {V4L2_PIX_FMT_SGRBG8      , "SGRBG8"      },
135         {V4L2_PIX_FMT_SRGGB8      , "SRGGB8"      },
136         {V4L2_PIX_FMT_SBGGR10     , "SBGGR10"     },
137         {V4L2_PIX_FMT_SGBRG10     , "SGBRG10"     },
138         {V4L2_PIX_FMT_SGRBG10     , "SGRBG10"     },
139         {V4L2_PIX_FMT_SRGGB10     , "SRGGB10"     },
140         {V4L2_PIX_FMT_SBGGR10P    , "SBGGR10P"    },
141         {V4L2_PIX_FMT_SGBRG10P    , "SGBRG10P"    },
142         {V4L2_PIX_FMT_SGRBG10P    , "SGRBG10P"    },
143         {V4L2_PIX_FMT_SRGGB10P    , "SRGGB10P"    },
144         {V4L2_PIX_FMT_SBGGR10ALAW8, "SBGGR10ALAW8"},
145         {V4L2_PIX_FMT_SGBRG10ALAW8, "SGBRG10ALAW8"},
146         {V4L2_PIX_FMT_SGRBG10ALAW8, "SGRBG10ALAW8"},
147         {V4L2_PIX_FMT_SRGGB10ALAW8, "SRGGB10ALAW8"},
148         {V4L2_PIX_FMT_SBGGR10DPCM8, "SBGGR10DPCM8"},
149         {V4L2_PIX_FMT_SGBRG10DPCM8, "SGBRG10DPCM8"},
150         {V4L2_PIX_FMT_SGRBG10DPCM8, "SGRBG10DPCM8"},
151         {V4L2_PIX_FMT_SRGGB10DPCM8, "SRGGB10DPCM8"},
152         {V4L2_PIX_FMT_SBGGR12     , "SBGGR12"     },
153         {V4L2_PIX_FMT_SGBRG12     , "SGBRG12"     },
154         {V4L2_PIX_FMT_SGRBG12     , "SGRBG12"     },
155         {V4L2_PIX_FMT_SRGGB12     , "SRGGB12"     },
156 #ifdef HAVE_EXTRAFORMATS
157         {V4L2_PIX_FMT_SBGGR12P    , "SBGGR12P"    },
158         {V4L2_PIX_FMT_SGBRG12P    , "SGBRG12P"    },
159         {V4L2_PIX_FMT_SGRBG12P    , "SGRBG12P"    },
160         {V4L2_PIX_FMT_SRGGB12P    , "SRGGB12P"    },
161         {V4L2_PIX_FMT_SBGGR16     , "SBGGR16"     },
162         {V4L2_PIX_FMT_SGBRG16     , "SGBRG16"     },
163         {V4L2_PIX_FMT_SGRBG16     , "SGRBG16"     },
164         {V4L2_PIX_FMT_SRGGB16     , "SRGGB16"     },
165 #endif
166     };
167 
168     return fourccToStr;
169 }
170 
171 Q_GLOBAL_STATIC_WITH_ARGS(FourccToStrMap, v4l2FourccToStr, (initFourccToStr()))
172 
173 class CaptureV4L2Private
174 {
175     public:
176         CaptureV4L2 *self;
177         QString m_device;
178         QList<int> m_streams;
179         QStringList m_devices;
180         QMap<QString, QString> m_descriptions;
181         QMap<QString, QVariantList> m_devicesCaps;
182         QMutex m_controlsMutex;
183         QVariantList m_globalImageControls;
184         QVariantList m_globalCameraControls;
185         QVariantMap m_localImageControls;
186         QVariantMap m_localCameraControls;
187         QFileSystemWatcher *m_fsWatcher {nullptr};
188         AkFrac m_fps;
189         AkFrac m_timeBase;
190         AkCaps m_caps;
191         qint64 m_id {-1};
192         QVector<CaptureBuffer> m_buffers;
193         CaptureV4L2::IoMethod m_ioMethod {CaptureV4L2::IoMethodUnknown};
194         int m_nBuffers {32};
195         int m_fd {-1};
196 
197         explicit CaptureV4L2Private(CaptureV4L2 *self);
198         ~CaptureV4L2Private();
199         QVariantList capsFps(int fd,
200                              const v4l2_fmtdesc &format,
201                              __u32 width,
202                              __u32 height) const;
203         QVariantList caps(int fd) const;
204         void setFps(int fd, const AkFrac &fps);
205         QVariantList controls(int fd, quint32 controlClass) const;
206         bool setControls(int fd,
207                          quint32 controlClass,
208                          const QVariantMap &controls) const;
209         QVariantList queryControl(int handle,
210                                   quint32 controlClass,
211                                   v4l2_queryctrl *queryctrl) const;
212         QMap<QString, quint32> findControls(int handle,
213                                             quint32 controlClass) const;
214         bool initReadWrite(quint32 bufferSize);
215         bool initMemoryMap();
216         bool initUserPointer(quint32 bufferSize);
217         bool startCapture();
218         void stopCapture();
219         QString fourccToStr(quint32 format) const;
220         quint32 strToFourCC(const QString &format) const;
221         AkPacket processFrame(const char *buffer,
222                               size_t bufferSize,
223                               qint64 pts) const;
224         QVariantList imageControls(int fd) const;
225         bool setImageControls(int fd,
226                               const QVariantMap &imageControls) const;
227         QVariantList cameraControls(int fd) const;
228         bool setCameraControls(int fd,
229                                const QVariantMap &cameraControls) const;
230         QVariantMap controlStatus(const QVariantList &controls) const;
231         QVariantMap mapDiff(const QVariantMap &map1,
232                             const QVariantMap &map2) const;
233         inline QStringList v4l2Devices() const;
234         void updateDevices();
235 };
236 
CaptureV4L2(QObject * parent)237 CaptureV4L2::CaptureV4L2(QObject *parent):
238     Capture(parent)
239 {
240     this->d = new CaptureV4L2Private(this);
241 }
242 
~CaptureV4L2()243 CaptureV4L2::~CaptureV4L2()
244 {
245     delete this->d;
246 }
247 
webcams() const248 QStringList CaptureV4L2::webcams() const
249 {
250     return this->d->m_devices;
251 }
252 
device() const253 QString CaptureV4L2::device() const
254 {
255     return this->d->m_device;
256 }
257 
streams()258 QList<int> CaptureV4L2::streams()
259 {
260     if (!this->d->m_streams.isEmpty())
261         return this->d->m_streams;
262 
263     auto caps = this->caps(this->d->m_device);
264 
265     if (caps.isEmpty())
266         return {};
267 
268     return {0};
269 }
270 
listTracks(const QString & mimeType)271 QList<int> CaptureV4L2::listTracks(const QString &mimeType)
272 {
273     if (mimeType != "video/x-raw"
274         && !mimeType.isEmpty())
275         return QList<int>();
276 
277     QVariantList caps = this->caps(this->d->m_device);
278     QList<int> streams;
279 
280     for (int i = 0; i < caps.count(); i++)
281         streams << i;
282 
283     return streams;
284 }
285 
ioMethod() const286 QString CaptureV4L2::ioMethod() const
287 {
288     return ioMethodToStr->value(this->d->m_ioMethod, "any");
289 }
290 
nBuffers() const291 int CaptureV4L2::nBuffers() const
292 {
293     return this->d->m_nBuffers;
294 }
295 
description(const QString & webcam) const296 QString CaptureV4L2::description(const QString &webcam) const
297 {
298     return this->d->m_descriptions.value(webcam);
299 }
300 
caps(const QString & webcam) const301 QVariantList CaptureV4L2::caps(const QString &webcam) const
302 {
303     return this->d->m_devicesCaps.value(webcam);
304 }
305 
capsDescription(const AkCaps & caps) const306 QString CaptureV4L2::capsDescription(const AkCaps &caps) const
307 {
308     if (caps.mimeType() != "video/unknown")
309         return QString();
310 
311     AkFrac fps = caps.property("fps").toString();
312 
313     return QString("%1, %2x%3, %4 FPS")
314                 .arg(caps.property("fourcc").toString(),
315                      caps.property("width").toString(),
316                      caps.property("height").toString())
317                 .arg(qRound(fps.value()));
318 }
319 
imageControls() const320 QVariantList CaptureV4L2::imageControls() const
321 {
322     return this->d->m_globalImageControls;
323 }
324 
setImageControls(const QVariantMap & imageControls)325 bool CaptureV4L2::setImageControls(const QVariantMap &imageControls)
326 {
327     this->d->m_controlsMutex.lock();
328     auto globalImageControls = this->d->m_globalImageControls;
329     this->d->m_controlsMutex.unlock();
330 
331     for (int i = 0; i < globalImageControls.count(); i++) {
332         auto control = globalImageControls[i].toList();
333         auto controlName = control[0].toString();
334 
335         if (imageControls.contains(controlName)) {
336             control[6] = imageControls[controlName];
337             globalImageControls[i] = control;
338         }
339     }
340 
341     this->d->m_controlsMutex.lock();
342 
343     if (this->d->m_globalImageControls == globalImageControls) {
344         this->d->m_controlsMutex.unlock();
345 
346         return false;
347     }
348 
349     this->d->m_globalImageControls = globalImageControls;
350     this->d->m_controlsMutex.unlock();
351 
352     emit this->imageControlsChanged(imageControls);
353 
354     return true;
355 }
356 
resetImageControls()357 bool CaptureV4L2::resetImageControls()
358 {
359     QVariantMap controls;
360 
361     for (auto &control: this->imageControls()) {
362         QVariantList params = control.toList();
363         controls[params[0].toString()] = params[5].toInt();
364     }
365 
366     return this->setImageControls(controls);
367 }
368 
cameraControls() const369 QVariantList CaptureV4L2::cameraControls() const
370 {
371     return this->d->m_globalCameraControls;
372 }
373 
setCameraControls(const QVariantMap & cameraControls)374 bool CaptureV4L2::setCameraControls(const QVariantMap &cameraControls)
375 {
376     this->d->m_controlsMutex.lock();
377     auto globalCameraControls = this->d->m_globalCameraControls;
378     this->d->m_controlsMutex.unlock();
379 
380     for (int i = 0; i < globalCameraControls.count(); i++) {
381         QVariantList control = globalCameraControls[i].toList();
382         QString controlName = control[0].toString();
383 
384         if (cameraControls.contains(controlName)) {
385             control[6] = cameraControls[controlName];
386             globalCameraControls[i] = control;
387         }
388     }
389 
390     this->d->m_controlsMutex.lock();
391 
392     if (this->d->m_globalCameraControls == globalCameraControls) {
393         this->d->m_controlsMutex.unlock();
394 
395         return false;
396     }
397 
398     this->d->m_globalCameraControls = globalCameraControls;
399     this->d->m_controlsMutex.unlock();
400     emit this->cameraControlsChanged(cameraControls);
401 
402     return true;
403 }
404 
resetCameraControls()405 bool CaptureV4L2::resetCameraControls()
406 {
407     QVariantMap controls;
408 
409     for (auto &control: this->cameraControls()) {
410         auto params = control.toList();
411         controls[params[0].toString()] = params[5].toInt();
412     }
413 
414     return this->setCameraControls(controls);
415 }
416 
readFrame()417 AkPacket CaptureV4L2::readFrame()
418 {
419     if (this->d->m_buffers.isEmpty())
420         return AkPacket();
421 
422     if (this->d->m_fd < 0)
423         return AkPacket();
424 
425     this->d->m_controlsMutex.lock();
426     auto imageControls = this->d->controlStatus(this->d->m_globalImageControls);
427     this->d->m_controlsMutex.unlock();
428 
429     if (this->d->m_localImageControls != imageControls) {
430         auto controls = this->d->mapDiff(this->d->m_localImageControls,
431                                          imageControls);
432         this->d->setImageControls(this->d->m_fd, controls);
433         this->d->m_localImageControls = imageControls;
434     }
435 
436     this->d->m_controlsMutex.lock();
437     auto cameraControls = this->d->controlStatus(this->d->m_globalCameraControls);
438     this->d->m_controlsMutex.unlock();
439 
440     if (this->d->m_localCameraControls != cameraControls) {
441         auto controls = this->d->mapDiff(this->d->m_localCameraControls,
442                                          cameraControls);
443         this->d->setCameraControls(this->d->m_fd, controls);
444         this->d->m_localCameraControls = cameraControls;
445     }
446 
447     if (this->d->m_ioMethod == IoMethodReadWrite) {
448         if (x_read(this->d->m_fd,
449                    this->d->m_buffers[0].start,
450                    this->d->m_buffers[0].length) < 0)
451             return AkPacket();
452 
453         timeval timestamp {};
454         gettimeofday(&timestamp, nullptr);
455 
456         auto pts = qint64((timestamp.tv_sec + 1e-6 * timestamp.tv_usec)
457                           * this->d->m_fps.value());
458 
459         return this->d->processFrame(this->d->m_buffers[0].start,
460                                      this->d->m_buffers[0].length,
461                                      pts);
462     }
463 
464     if (this->d->m_ioMethod == IoMethodMemoryMap
465         || this->d->m_ioMethod == IoMethodUserPointer) {
466         v4l2_buffer buffer {};
467         buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
468         buffer.memory = (this->d->m_ioMethod == IoMethodMemoryMap)?
469                             V4L2_MEMORY_MMAP:
470                             V4L2_MEMORY_USERPTR;
471 
472         if (x_ioctl(this->d->m_fd, VIDIOC_DQBUF, &buffer) < 0)
473             return AkPacket();
474 
475         if (buffer.index >= quint32(this->d->m_buffers.size()))
476             return AkPacket();
477 
478         auto pts = qint64((buffer.timestamp.tv_sec
479                            + 1e-6 * buffer.timestamp.tv_usec)
480                           * this->d->m_fps.value());
481 
482         AkPacket packet =
483                 this->d->processFrame(this->d->m_buffers[int(buffer.index)].start,
484                                       buffer.bytesused,
485                                       pts);
486 
487         if (x_ioctl(this->d->m_fd, VIDIOC_QBUF, &buffer) < 0)
488             return AkPacket();
489 
490         return packet;
491     }
492 
493     return AkPacket();
494 }
495 
init()496 bool CaptureV4L2::init()
497 {
498     this->d->m_localImageControls.clear();
499     this->d->m_localCameraControls.clear();
500 
501     // Frames read must be blocking so we does not waste CPU time.
502     this->d->m_fd =
503             x_open(this->d->m_device.toStdString().c_str(),
504                    O_RDWR, // | O_NONBLOCK,
505                    0);
506 
507     if (this->d->m_fd < 0)
508         return false;
509 
510     v4l2_capability capabilities;
511     memset(&capabilities, 0, sizeof(v4l2_capability));
512 
513     if (x_ioctl(this->d->m_fd, VIDIOC_QUERYCAP, &capabilities) < 0) {
514         qDebug() << "VideoCapture: Can't query capabilities.";
515         x_close(this->d->m_fd);
516         this->d->m_fd = -1;
517 
518         return false;
519     }
520 
521     QList<int> streams = this->streams();
522 
523     if (streams.isEmpty()) {
524         qDebug() << "VideoCapture: No streams available.";
525         x_close(this->d->m_fd);
526 
527         return false;
528     }
529 
530     QVariantList supportedCaps = this->caps(this->d->m_device);
531     AkCaps caps = supportedCaps[streams[0]].value<AkCaps>();
532     v4l2_format fmt {};
533     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
534 
535     x_ioctl(this->d->m_fd, VIDIOC_G_FMT, &fmt);
536     auto fourcc = caps.property("fourcc").toString();
537     fmt.fmt.pix.pixelformat =
538             v4l2FourccToStr->key(fourcc,
539                                  this->d->strToFourCC(fourcc));
540     fmt.fmt.pix.width = caps.property("width").toUInt();
541     fmt.fmt.pix.height = caps.property("height").toUInt();
542 
543     if (x_ioctl(this->d->m_fd, VIDIOC_S_FMT, &fmt) < 0) {
544         qDebug() << "VideoCapture: Can't set format:"
545                  << this->d->fourccToStr(fmt.fmt.pix.pixelformat);
546         x_close(this->d->m_fd);
547         this->d->m_fd = -1;
548 
549         return false;
550     }
551 
552     this->d->setFps(this->d->m_fd, caps.property("fps").toString());
553     this->d->m_caps = caps;
554     this->d->m_fps = caps.property("fps").toString();
555     this->d->m_timeBase = this->d->m_fps.invert();
556 
557     if (this->d->m_ioMethod == IoMethodReadWrite
558         && capabilities.capabilities & V4L2_CAP_READWRITE
559         && this->d->initReadWrite(fmt.fmt.pix.sizeimage)) {
560     } else if (this->d->m_ioMethod == IoMethodMemoryMap
561              && capabilities.capabilities & V4L2_CAP_STREAMING
562              && this->d->initMemoryMap()) {
563     } else if (this->d->m_ioMethod == IoMethodUserPointer
564              && capabilities.capabilities & V4L2_CAP_STREAMING
565              && this->d->initUserPointer(fmt.fmt.pix.sizeimage)) {
566     } else
567         this->d->m_ioMethod = IoMethodUnknown;
568 
569     if (this->d->m_ioMethod != IoMethodUnknown)
570         return this->d->startCapture();
571 
572     if (capabilities.capabilities & V4L2_CAP_STREAMING) {
573         if (this->d->initMemoryMap())
574             this->d->m_ioMethod = IoMethodMemoryMap;
575         else if (this->d->initUserPointer(fmt.fmt.pix.sizeimage))
576             this->d->m_ioMethod = IoMethodUserPointer;
577     }
578 
579     if (this->d->m_ioMethod == IoMethodUnknown) {
580         if (capabilities.capabilities & V4L2_CAP_READWRITE
581             && this->d->initReadWrite(fmt.fmt.pix.sizeimage))
582             this->d->m_ioMethod = IoMethodReadWrite;
583         else
584             return false;
585     }
586 
587     return this->d->startCapture();
588 }
589 
uninit()590 void CaptureV4L2::uninit()
591 {
592     this->d->stopCapture();
593 
594     if (!this->d->m_buffers.isEmpty()) {
595         if (this->d->m_ioMethod == IoMethodReadWrite)
596             delete [] this->d->m_buffers[0].start;
597         else if (this->d->m_ioMethod == IoMethodMemoryMap)
598             for (auto &buffer: this->d->m_buffers)
599                 x_munmap(buffer.start, buffer.length);
600         else if (this->d->m_ioMethod == IoMethodUserPointer)
601             for (auto &buffer: this->d->m_buffers)
602                 delete [] buffer.start;
603     }
604 
605     x_close(this->d->m_fd);
606     this->d->m_caps.clear();
607     this->d->m_fps = AkFrac();
608     this->d->m_timeBase = AkFrac();
609     this->d->m_buffers.clear();
610 }
611 
setDevice(const QString & device)612 void CaptureV4L2::setDevice(const QString &device)
613 {
614     if (this->d->m_device == device)
615         return;
616 
617     this->d->m_device = device;
618 
619     if (device.isEmpty()) {
620         this->d->m_controlsMutex.lock();
621         this->d->m_globalImageControls.clear();
622         this->d->m_globalCameraControls.clear();
623         this->d->m_controlsMutex.unlock();
624     } else {
625         this->d->m_controlsMutex.lock();
626         int fd = x_open(device.toStdString().c_str(), O_RDWR | O_NONBLOCK, 0);
627 
628         if (fd >= 0) {
629             this->d->m_globalImageControls = this->d->imageControls(fd);
630             this->d->m_globalCameraControls = this->d->cameraControls(fd);
631             x_close(fd);
632         }
633 
634         this->d->m_controlsMutex.unlock();
635     }
636 
637     this->d->m_controlsMutex.lock();
638     auto imageStatus = this->d->controlStatus(this->d->m_globalImageControls);
639     auto cameraStatus = this->d->controlStatus(this->d->m_globalCameraControls);
640     this->d->m_controlsMutex.unlock();
641 
642     emit this->deviceChanged(device);
643     emit this->imageControlsChanged(imageStatus);
644     emit this->cameraControlsChanged(cameraStatus);
645 }
646 
setStreams(const QList<int> & streams)647 void CaptureV4L2::setStreams(const QList<int> &streams)
648 {
649     if (streams.isEmpty())
650         return;
651 
652     int stream = streams[0];
653 
654     if (stream < 0)
655         return;
656 
657     auto supportedCaps = this->caps(this->d->m_device);
658 
659     if (stream >= supportedCaps.length())
660         return;
661 
662     QList<int> inputStreams {stream};
663 
664     if (this->streams() == inputStreams)
665         return;
666 
667     this->d->m_streams = inputStreams;
668     emit this->streamsChanged(inputStreams);
669 }
670 
setIoMethod(const QString & ioMethod)671 void CaptureV4L2::setIoMethod(const QString &ioMethod)
672 {
673     if (this->d->m_fd >= 0)
674         return;
675 
676     IoMethod ioMethodEnum = ioMethodToStr->key(ioMethod, IoMethodUnknown);
677 
678     if (this->d->m_ioMethod == ioMethodEnum)
679         return;
680 
681     this->d->m_ioMethod = ioMethodEnum;
682     emit this->ioMethodChanged(ioMethod);
683 }
684 
setNBuffers(int nBuffers)685 void CaptureV4L2::setNBuffers(int nBuffers)
686 {
687     if (this->d->m_nBuffers == nBuffers)
688         return;
689 
690     this->d->m_nBuffers = nBuffers;
691     emit this->nBuffersChanged(nBuffers);
692 }
693 
resetDevice()694 void CaptureV4L2::resetDevice()
695 {
696     this->setDevice("");
697 }
698 
resetStreams()699 void CaptureV4L2::resetStreams()
700 {
701     QVariantList supportedCaps = this->caps(this->d->m_device);
702     QList<int> streams;
703 
704     if (!supportedCaps.isEmpty())
705         streams << 0;
706 
707     this->setStreams(streams);
708 }
709 
resetIoMethod()710 void CaptureV4L2::resetIoMethod()
711 {
712     this->setIoMethod("any");
713 }
714 
resetNBuffers()715 void CaptureV4L2::resetNBuffers()
716 {
717     this->setNBuffers(32);
718 }
719 
reset()720 void CaptureV4L2::reset()
721 {
722     this->resetStreams();
723     this->resetImageControls();
724     this->resetCameraControls();
725 }
726 
CaptureV4L2Private(CaptureV4L2 * self)727 CaptureV4L2Private::CaptureV4L2Private(CaptureV4L2 *self):
728     self(self)
729 {
730 #if !defined(FREEBSD_BUG224011_VIDEO0)
731     this->m_fsWatcher = new QFileSystemWatcher({"/dev"}, self);
732     QObject::connect(this->m_fsWatcher,
733                      &QFileSystemWatcher::directoryChanged,
734                      this->self,
735                      [this] () {
736         this->updateDevices();
737     });
738 #endif
739     this->updateDevices();
740 }
741 
~CaptureV4L2Private()742 CaptureV4L2Private::~CaptureV4L2Private()
743 {
744 #if !defined(FREEBSD_BUG224011_VIDEO0)
745     delete this->m_fsWatcher;
746 #endif
747 }
748 
capsFps(int fd,const struct v4l2_fmtdesc & format,__u32 width,__u32 height) const749 QVariantList CaptureV4L2Private::capsFps(int fd,
750                                          const struct v4l2_fmtdesc &format,
751                                          __u32 width,
752                                          __u32 height) const
753 {
754     QVariantList caps;
755     auto fourcc =
756             v4l2FourccToStr->value(format.pixelformat,
757                                    this->fourccToStr(format.pixelformat));
758 
759 #ifdef VIDIOC_ENUM_FRAMEINTERVALS
760     v4l2_frmivalenum frmival {};
761     frmival.pixel_format = format.pixelformat;
762     frmival.width = width;
763     frmival.height = height;
764 
765     for (frmival.index = 0;
766          x_ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0;
767          frmival.index++) {
768         if (!frmival.discrete.numerator
769             || !frmival.discrete.denominator)
770             continue;
771 
772         AkFrac fps;
773 
774         if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
775             fps = AkFrac(frmival.discrete.denominator,
776                          frmival.discrete.numerator);
777         else
778             fps = AkFrac(frmival.stepwise.min.denominator,
779                          frmival.stepwise.max.numerator);
780 
781         AkCaps videoCaps;
782         videoCaps.setMimeType("video/unknown");
783         videoCaps.setProperty("fourcc", fourcc);
784         videoCaps.setProperty("width", width);
785         videoCaps.setProperty("height", height);
786         videoCaps.setProperty("fps", fps.toString());
787         caps << QVariant::fromValue(videoCaps);
788     }
789 
790     if (caps.isEmpty()) {
791 #endif
792         struct v4l2_streamparm params;
793         memset(&params, 0, sizeof(v4l2_streamparm));
794         params.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
795 
796         if (x_ioctl(fd, VIDIOC_G_PARM, &params) >= 0) {
797             AkFrac fps;
798 
799             if (params.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)
800                 fps = AkFrac(params.parm.capture.timeperframe.denominator,
801                              params.parm.capture.timeperframe.numerator);
802             else
803                 fps = AkFrac(30, 1);
804 
805             AkCaps videoCaps;
806             videoCaps.setMimeType("video/unknown");
807             videoCaps.setProperty("fourcc", fourcc);
808             videoCaps.setProperty("width", width);
809             videoCaps.setProperty("height", height);
810             videoCaps.setProperty("fps", fps.toString());
811             caps << QVariant::fromValue(videoCaps);
812         }
813 #ifdef VIDIOC_ENUM_FRAMEINTERVALS
814     }
815 #endif
816 
817     return caps;
818 }
819 
caps(int fd) const820 QVariantList CaptureV4L2Private::caps(int fd) const
821 {
822     QVariantList caps;
823 
824 #ifndef VIDIOC_ENUM_FRAMESIZES
825     v4l2_format fmt;
826     memset(&fmt, 0, sizeof(v4l2_format));
827     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
828     uint width = 0;
829     uint height = 0;
830 
831     // Check if it has at least a default format.
832     if (x_ioctl(fd, VIDIOC_G_FMT, &fmt) >= 0) {
833         width = fmt.fmt.pix.width;
834         height = fmt.fmt.pix.height;
835     }
836 
837     if (width <= 0 || height <= 0)
838         return {};
839 #endif
840 
841     // Enumerate all supported formats.
842     v4l2_fmtdesc fmtdesc;
843     memset(&fmtdesc, 0, sizeof(v4l2_fmtdesc));
844     fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
845 
846     for (fmtdesc.index = 0;
847          x_ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) >= 0;
848          fmtdesc.index++) {
849 #ifdef VIDIOC_ENUM_FRAMESIZES
850         v4l2_frmsizeenum frmsize;
851         memset(&frmsize, 0, sizeof(v4l2_frmsizeenum));
852         frmsize.pixel_format = fmtdesc.pixelformat;
853 
854         // Enumerate frame sizes.
855         for (frmsize.index = 0;
856              x_ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0;
857              frmsize.index++) {
858             if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
859                 caps << this->capsFps(fd,
860                                       fmtdesc,
861                                       frmsize.discrete.width,
862                                       frmsize.discrete.height);
863             } else {
864 #if 0
865                 for (uint height = frmsize.stepwise.min_height;
866                      height < frmsize.stepwise.max_height;
867                      height += frmsize.stepwise.step_height)
868                     for (uint width = frmsize.stepwise.min_width;
869                          width < frmsize.stepwise.max_width;
870                          width += frmsize.stepwise.step_width) {
871                         caps << this->capsFps(fd, fmtdesc, width, height);
872                     }
873 #endif
874             }
875         }
876 #else
877         caps << this->capsFps(fd, fmtdesc, width, height);
878 #endif
879     }
880 
881     return caps;
882 }
883 
setFps(int fd,const AkFrac & fps)884 void CaptureV4L2Private::setFps(int fd, const AkFrac &fps)
885 {
886     v4l2_streamparm streamparm {};
887     streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
888 
889     if (x_ioctl(fd, VIDIOC_G_PARM, &streamparm) >= 0)
890         if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
891             streamparm.parm.capture.timeperframe.numerator = __u32(fps.den());
892             streamparm.parm.capture.timeperframe.denominator = __u32(fps.num());
893             x_ioctl(fd, VIDIOC_S_PARM, &streamparm);
894         }
895 }
896 
controls(int fd,quint32 controlClass) const897 QVariantList CaptureV4L2Private::controls(int fd, quint32 controlClass) const
898 {
899     QVariantList controls;
900 
901     if (fd < 0)
902         return controls;
903 
904     v4l2_queryctrl queryctrl;
905     memset(&queryctrl, 0, sizeof(v4l2_queryctrl));
906     queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
907 
908     while (x_ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl) == 0) {
909         auto control = this->queryControl(fd, controlClass, &queryctrl);
910 
911         if (!control.isEmpty())
912             controls << QVariant(control);
913 
914         queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
915     }
916 
917     if (queryctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL)
918         return controls;
919 
920     for (__u32 id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
921         queryctrl.id = id;
922 
923         if (x_ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl) == 0) {
924             auto control = this->queryControl(fd, controlClass, &queryctrl);
925 
926             if (!control.isEmpty())
927                 controls << QVariant(control);
928         }
929     }
930 
931     for (queryctrl.id = V4L2_CID_PRIVATE_BASE;
932          x_ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl) == 0;
933          queryctrl.id++) {
934         auto control = this->queryControl(fd, controlClass, &queryctrl);
935 
936         if (!control.isEmpty())
937             controls << QVariant(control);
938     }
939 
940     return controls;
941 }
942 
setControls(int fd,quint32 controlClass,const QVariantMap & controls) const943 bool CaptureV4L2Private::setControls(int fd,
944                                      quint32 controlClass,
945                                      const QVariantMap &controls) const
946 {
947     if (fd < 0)
948         return false;
949 
950     auto ctrl2id = this->findControls(fd, controlClass);
951 
952     for (auto it = controls.cbegin(); it != controls.cend(); it++) {
953         if (!ctrl2id.contains(it.key()))
954             continue;
955 
956         v4l2_control ctrl;
957         memset(&ctrl, 0, sizeof(v4l2_control));
958         ctrl.id = ctrl2id[it.key()];
959         ctrl.value = it.value().toInt();
960         x_ioctl(fd, VIDIOC_S_CTRL, &ctrl);
961     }
962 
963     return true;
964 }
965 
queryControl(int handle,quint32 controlClass,v4l2_queryctrl * queryctrl) const966 QVariantList CaptureV4L2Private::queryControl(int handle,
967                                               quint32 controlClass,
968                                               v4l2_queryctrl *queryctrl) const
969 {
970     if (queryctrl->flags & V4L2_CTRL_FLAG_DISABLED)
971         return {};
972 
973     if (V4L2_CTRL_ID2CLASS(queryctrl->id) != controlClass)
974         return {};
975 
976     v4l2_ext_control ext_ctrl;
977     memset(&ext_ctrl, 0, sizeof(v4l2_ext_control));
978     ext_ctrl.id = queryctrl->id;
979 
980     v4l2_ext_controls ctrls;
981     memset(&ctrls, 0, sizeof(v4l2_ext_controls));
982     ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(queryctrl->id);
983     ctrls.count = 1;
984     ctrls.controls = &ext_ctrl;
985 
986     if (V4L2_CTRL_ID2CLASS(queryctrl->id) != V4L2_CTRL_CLASS_USER &&
987         queryctrl->id < V4L2_CID_PRIVATE_BASE) {
988         if (x_ioctl(handle, VIDIOC_G_EXT_CTRLS, &ctrls))
989             return {};
990     } else {
991         v4l2_control ctrl;
992         memset(&ctrl, 0, sizeof(v4l2_control));
993         ctrl.id = queryctrl->id;
994 
995         if (x_ioctl(handle, VIDIOC_G_CTRL, &ctrl))
996             return QVariantList();
997 
998         ext_ctrl.value = ctrl.value;
999     }
1000 
1001     v4l2_querymenu qmenu;
1002     memset(&qmenu, 0, sizeof(v4l2_querymenu));
1003     qmenu.id = queryctrl->id;
1004     QStringList menu;
1005 
1006     if (queryctrl->type == V4L2_CTRL_TYPE_MENU)
1007         for (int i = 0; i < queryctrl->maximum + 1; i++) {
1008             qmenu.index = __u32(i);
1009 
1010             if (x_ioctl(handle, VIDIOC_QUERYMENU, &qmenu))
1011                 continue;
1012 
1013             menu << QString(reinterpret_cast<const char *>(qmenu.name));
1014         }
1015 
1016     auto type = static_cast<v4l2_ctrl_type>(queryctrl->type);
1017 
1018     return QVariantList {
1019         QString(reinterpret_cast<const char *>(queryctrl->name)),
1020         ctrlTypeToStr->value(type),
1021         queryctrl->minimum,
1022         queryctrl->maximum,
1023         queryctrl->step,
1024         queryctrl->default_value,
1025         ext_ctrl.value,
1026         menu
1027     };
1028 }
1029 
findControls(int handle,quint32 controlClass) const1030 QMap<QString, quint32> CaptureV4L2Private::findControls(int handle,
1031                                                         quint32 controlClass) const
1032 {
1033     v4l2_queryctrl qctrl;
1034     memset(&qctrl, 0, sizeof(v4l2_queryctrl));
1035     qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
1036     QMap<QString, quint32> controls;
1037 
1038     while (x_ioctl(handle, VIDIOC_QUERYCTRL, &qctrl) == 0) {
1039         if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
1040             && V4L2_CTRL_ID2CLASS(qctrl.id) == controlClass)
1041             controls[QString(reinterpret_cast<const char *>(qctrl.name))] = qctrl.id;
1042 
1043         qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
1044     }
1045 
1046     if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL)
1047         return controls;
1048 
1049     for (__u32 id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
1050         qctrl.id = id;
1051 
1052         if (x_ioctl(handle, VIDIOC_QUERYCTRL, &qctrl) == 0
1053             && !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
1054             && V4L2_CTRL_ID2CLASS(qctrl.id) == controlClass)
1055             controls[QString(reinterpret_cast<const char *>(qctrl.name))] = qctrl.id;
1056     }
1057 
1058     qctrl.id = V4L2_CID_PRIVATE_BASE;
1059 
1060     while (x_ioctl(handle, VIDIOC_QUERYCTRL, &qctrl) == 0) {
1061         if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
1062             && V4L2_CTRL_ID2CLASS(qctrl.id) == controlClass)
1063             controls[QString(reinterpret_cast<const char *>(qctrl.name))] = qctrl.id;
1064 
1065         qctrl.id++;
1066     }
1067 
1068     return controls;
1069 }
1070 
initReadWrite(quint32 bufferSize)1071 bool CaptureV4L2Private::initReadWrite(quint32 bufferSize)
1072 {
1073     this->m_buffers.resize(1);
1074     this->m_buffers[0].length = bufferSize;
1075     this->m_buffers[0].start = new char[bufferSize];
1076 
1077     if (!this->m_buffers[0].start) {
1078         this->m_buffers.clear();
1079 
1080         return false;
1081     }
1082 
1083     memset(this->m_buffers[0].start, 0, bufferSize);
1084 
1085     return true;
1086 }
1087 
initMemoryMap()1088 bool CaptureV4L2Private::initMemoryMap()
1089 {
1090     v4l2_requestbuffers requestBuffers;
1091     memset(&requestBuffers, 0, sizeof(v4l2_requestbuffers));
1092     requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1093     requestBuffers.memory = V4L2_MEMORY_MMAP;
1094     requestBuffers.count = __u32(this->m_nBuffers);
1095 
1096     if (x_ioctl(this->m_fd, VIDIOC_REQBUFS, &requestBuffers) < 0)
1097         return false;
1098 
1099     if (requestBuffers.count < 1)
1100         return false;
1101 
1102     this->m_buffers.resize(int(requestBuffers.count));
1103     bool error = false;
1104 
1105     for (int i = 0; i < int(requestBuffers.count); i++) {
1106         v4l2_buffer buffer;
1107         memset(&buffer, 0, sizeof(v4l2_buffer));
1108         buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1109         buffer.memory = V4L2_MEMORY_MMAP;
1110         buffer.index = __u32(i);
1111 
1112         if (x_ioctl(this->m_fd, VIDIOC_QUERYBUF, &buffer) < 0) {
1113             error = true;
1114 
1115             break;
1116         }
1117 
1118         this->m_buffers[i].length = buffer.length;
1119         this->m_buffers[i].start =
1120                 reinterpret_cast<char *>(x_mmap(nullptr,
1121                                                 buffer.length,
1122                                                 PROT_READ | PROT_WRITE,
1123                                                 MAP_SHARED,
1124                                                 this->m_fd,
1125                                                 buffer.m.offset));
1126 
1127         if (this->m_buffers[i].start == MAP_FAILED) {
1128             error = true;
1129 
1130             break;
1131         }
1132     }
1133 
1134     if (error) {
1135         for (auto &buffer: this->m_buffers)
1136             x_munmap(buffer.start, buffer.length);
1137 
1138         this->m_buffers.clear();
1139 
1140         return false;
1141     }
1142 
1143     return true;
1144 }
1145 
initUserPointer(quint32 bufferSize)1146 bool CaptureV4L2Private::initUserPointer(quint32 bufferSize)
1147 {
1148     v4l2_requestbuffers requestBuffers;
1149     memset(&requestBuffers, 0, sizeof(v4l2_requestbuffers));
1150     requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1151     requestBuffers.memory = V4L2_MEMORY_USERPTR;
1152     requestBuffers.count = __u32(this->m_nBuffers);
1153 
1154     if (x_ioctl(this->m_fd, VIDIOC_REQBUFS, &requestBuffers) < 0)
1155         return false;
1156 
1157     this->m_buffers.resize(int(requestBuffers.count));
1158     bool error = false;
1159 
1160     for (int i = 0; i < int(requestBuffers.count); i++) {
1161         this->m_buffers[i].length = bufferSize;
1162         this->m_buffers[i].start = new char[bufferSize];
1163 
1164         if (!this->m_buffers[i].start) {
1165             error = true;
1166 
1167             break;
1168         }
1169 
1170         memset(this->m_buffers[i].start, 0, bufferSize);
1171     }
1172 
1173     if (error) {
1174         for (auto &buffer: this->m_buffers)
1175             delete [] buffer.start;
1176 
1177         this->m_buffers.clear();
1178 
1179         return false;
1180     }
1181 
1182     return true;
1183 }
1184 
startCapture()1185 bool CaptureV4L2Private::startCapture()
1186 {
1187     bool error = false;
1188 
1189     if (this->m_ioMethod == CaptureV4L2::IoMethodMemoryMap) {
1190         for (int i = 0; i < this->m_buffers.size(); i++) {
1191             v4l2_buffer buffer;
1192             memset(&buffer, 0, sizeof(v4l2_buffer));
1193             buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1194             buffer.memory = V4L2_MEMORY_MMAP;
1195             buffer.index = __u32(i);
1196 
1197             if (x_ioctl(this->m_fd, VIDIOC_QBUF, &buffer) < 0)
1198                 error = true;
1199         }
1200 
1201         v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1202 
1203         if (x_ioctl(this->m_fd, VIDIOC_STREAMON, &type) < 0)
1204             error = true;
1205     } else if (this->m_ioMethod == CaptureV4L2::IoMethodUserPointer) {
1206         for (int i = 0; i < this->m_buffers.size(); i++) {
1207             v4l2_buffer buffer;
1208             memset(&buffer, 0, sizeof(v4l2_buffer));
1209             buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1210             buffer.memory = V4L2_MEMORY_USERPTR;
1211             buffer.index = __u32(i);
1212             buffer.m.userptr = ulong(this->m_buffers[i].start);
1213             buffer.length = __u32(this->m_buffers[i].length);
1214 
1215             if (x_ioctl(this->m_fd, VIDIOC_QBUF, &buffer) < 0)
1216                 error = true;
1217         }
1218 
1219         v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1220 
1221         if (x_ioctl(this->m_fd, VIDIOC_STREAMON, &type) < 0)
1222             error = true;
1223     }
1224 
1225     if (error)
1226         self->uninit();
1227 
1228     this->m_id = Ak::id();
1229 
1230     return !error;
1231 }
1232 
stopCapture()1233 void CaptureV4L2Private::stopCapture()
1234 {
1235     if (this->m_ioMethod == CaptureV4L2::IoMethodMemoryMap
1236         || this->m_ioMethod == CaptureV4L2::IoMethodUserPointer) {
1237         v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1238         x_ioctl(this->m_fd, VIDIOC_STREAMOFF, &type);
1239     }
1240 }
1241 
fourccToStr(quint32 format) const1242 QString CaptureV4L2Private::fourccToStr(quint32 format) const
1243 {
1244     char fourcc[5];
1245     memcpy(fourcc, &format, sizeof(quint32));
1246     fourcc[4] = 0;
1247 
1248     return QString(fourcc);
1249 }
1250 
strToFourCC(const QString & format) const1251 quint32 CaptureV4L2Private::strToFourCC(const QString &format) const
1252 {
1253     quint32 fourcc;
1254     memcpy(&fourcc, format.toStdString().c_str(), sizeof(quint32));
1255 
1256     return fourcc;
1257 }
1258 
processFrame(const char * buffer,size_t bufferSize,qint64 pts) const1259 AkPacket CaptureV4L2Private::processFrame(const char *buffer,
1260                                           size_t bufferSize,
1261                                           qint64 pts) const
1262 {
1263     AkPacket oPacket(this->m_caps);
1264     oPacket.setBuffer({buffer, int(bufferSize)});
1265     oPacket.setPts(pts);
1266     oPacket.setTimeBase(this->m_timeBase);
1267     oPacket.setIndex(0);
1268     oPacket.setId(this->m_id);
1269 
1270     return oPacket;
1271 }
1272 
imageControls(int fd) const1273 QVariantList CaptureV4L2Private::imageControls(int fd) const
1274 {
1275     return this->controls(fd, V4L2_CTRL_CLASS_USER);
1276 }
1277 
setImageControls(int fd,const QVariantMap & imageControls) const1278 bool CaptureV4L2Private::setImageControls(int fd,
1279                                           const QVariantMap &imageControls) const
1280 {
1281     return this->setControls(fd, V4L2_CTRL_CLASS_USER, imageControls);
1282 }
1283 
cameraControls(int fd) const1284 QVariantList CaptureV4L2Private::cameraControls(int fd) const
1285 {
1286 #ifdef V4L2_CTRL_CLASS_CAMERA
1287     return this->controls(fd, V4L2_CTRL_CLASS_CAMERA);
1288 #else
1289     Q_UNUSED(fd)
1290 
1291     return {};
1292 #endif
1293 }
1294 
setCameraControls(int fd,const QVariantMap & cameraControls) const1295 bool CaptureV4L2Private::setCameraControls(int fd,
1296                                            const QVariantMap &cameraControls) const
1297 {
1298 #ifdef V4L2_CTRL_CLASS_CAMERA
1299     return this->setControls(fd, V4L2_CTRL_CLASS_CAMERA, cameraControls);
1300 #else
1301     Q_UNUSED(fd)
1302     Q_UNUSED(cameraControls)
1303 
1304     return false;
1305 #endif
1306 }
1307 
controlStatus(const QVariantList & controls) const1308 QVariantMap CaptureV4L2Private::controlStatus(const QVariantList &controls) const
1309 {
1310     QVariantMap controlStatus;
1311 
1312     for (auto &control: controls) {
1313         auto params = control.toList();
1314         auto controlName = params[0].toString();
1315         controlStatus[controlName] = params[6];
1316     }
1317 
1318     return controlStatus;
1319 }
1320 
mapDiff(const QVariantMap & map1,const QVariantMap & map2) const1321 QVariantMap CaptureV4L2Private::mapDiff(const QVariantMap &map1,
1322                                         const QVariantMap &map2) const
1323 {
1324     QVariantMap map;
1325 
1326     for (auto it = map2.cbegin(); it != map2.cend(); it++)
1327         if (!map1.contains(it.key())
1328             || map1[it.key()] != it.value()) {
1329             map[it.key()] = it.value();
1330         }
1331 
1332     return map;
1333 }
1334 
v4l2Devices() const1335 QStringList CaptureV4L2Private::v4l2Devices() const
1336 {
1337     QDir devicesDir("/dev");
1338 
1339     return devicesDir.entryList(QStringList() << "video*",
1340                                 QDir::System
1341                                 | QDir::Readable
1342                                 | QDir::Writable
1343                                 | QDir::NoSymLinks
1344                                 | QDir::NoDotAndDotDot
1345                                 | QDir::CaseSensitive,
1346                                 QDir::Name);
1347 }
1348 
updateDevices()1349 void CaptureV4L2Private::updateDevices()
1350 {
1351     decltype(this->m_devices) devices;
1352     decltype(this->m_descriptions) descriptions;
1353     decltype(this->m_devicesCaps) devicesCaps;
1354 
1355     QDir devicesDir("/dev");
1356     auto devicesFiles = this->v4l2Devices();
1357 
1358     for (auto &devicePath: devicesFiles) {
1359         auto fileName = devicesDir.absoluteFilePath(devicePath);
1360         int fd = x_open(fileName.toStdString().c_str(), O_RDWR | O_NONBLOCK, 0);
1361 
1362         if (fd < 0)
1363             continue;
1364 
1365         auto caps = this->caps(fd);
1366 
1367         if (!caps.empty()) {
1368             v4l2_capability capability;
1369             memset(&capability, 0, sizeof(v4l2_capability));
1370             QString description;
1371 
1372             if (x_ioctl(fd, VIDIOC_QUERYCAP, &capability) >= 0)
1373                 description = reinterpret_cast<const char *>(capability.card);
1374 
1375             devices << fileName;
1376             descriptions[fileName] = description;
1377             devicesCaps[fileName] = caps;
1378         }
1379 
1380         x_close(fd);
1381     }
1382 
1383     if (devicesCaps.isEmpty()) {
1384         devices.clear();
1385         descriptions.clear();
1386     }
1387 
1388     this->m_descriptions = descriptions;
1389     this->m_devicesCaps = devicesCaps;
1390 
1391     if (this->m_devices != devices) {
1392 #if !defined(FREEBSD_BUG224011_VIDEO0)
1393         if (!this->m_devices.isEmpty())
1394             this->m_fsWatcher->removePaths(this->m_devices);
1395 #endif
1396 
1397         this->m_devices = devices;
1398 #if !defined(FREEBSD_BUG224011_VIDEO0)
1399         if (!this->m_devices.isEmpty())
1400             this->m_fsWatcher->addPaths(this->m_devices);
1401 #endif
1402         emit self->webcamsChanged(this->m_devices);
1403     }
1404 }
1405 
1406 #include "moc_capturev4l2.cpp"
1407