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(×tamp, 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(¶ms, 0, sizeof(v4l2_streamparm));
794 params.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
795
796 if (x_ioctl(fd, VIDIOC_G_PARM, ¶ms) >= 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