1 /* Webcamoid, webcam capture application.
2  * Copyright (C) 2016  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 <QtConcurrent>
21 #include <ak.h>
22 #include <akfrac.h>
23 #include <akcaps.h>
24 #include <akpacket.h>
25 #include <libuvc/libuvc.h>
26 
27 #include "capturelibuvc.h"
28 #include "usbglobals.h"
29 #include "usbids.h"
30 
31 Q_GLOBAL_STATIC(UsbIds, usbIds)
32 
33 #define TIME_OUT 500
34 
35 #define PROCESSING_UNIT 0
36 #define CAMERA_TERMINAL 1
37 
38 class UvcControl
39 {
40     public:
41         int controlType;
42         uint8_t selector;
43         QString description;
44         QString type;
45         bool signd;
46         QStringList menu;
47 
48         inline static const QVector<UvcControl> &controls();
49         inline static const UvcControl *bySelector(int controlType,
50                                                    uint8_t selector);
51         inline static QVector<uint8_t> allSelectors(int controlType);
52 };
53 
54 Q_GLOBAL_STATIC(UsbGlobals, usbGlobals)
55 
56 using PixFmtToStrMap = QMap<uvc_frame_format, QString>;
57 using FourccToStrMap = QMap<QString, QString>;
58 
59 class CaptureLibUVCPrivate
60 {
61     public:
62         CaptureLibUVC *self;
63         QString m_device;
64         QList<int> m_streams;
65         QMap<quint32, QString> m_devices;
66         QMap<QString, QString> m_descriptions;
67         QMap<QString, QVariantList> m_devicesCaps;
68         QMap<QString, QVariantList> m_imageControls;
69         QMap<QString, QVariantList> m_cameraControls;
70         QString m_curDevice;
71         AkPacket m_curPacket;
72         uvc_context_t *m_uvcContext {nullptr};
73         uvc_device_handle_t *m_deviceHnd {nullptr};
74         QThreadPool m_threadPool;
75         QWaitCondition m_packetNotReady;
76         QMutex m_mutex;
77         qint64 m_id {-1};
78         AkFrac m_fps;
79 
80         explicit CaptureLibUVCPrivate(CaptureLibUVC *self);
81         QVariantList controlsList(uvc_device_handle_t *deviceHnd,
82                                   uint8_t unit,
83                                   uint8_t control,
84                                   int controlType) const;
85         int setControls(uvc_device_handle_t *deviceHnd,
86                         uint8_t unit,
87                         uint8_t control,
88                         int controlType,
89                         const QVariantMap &values);
90         static void frameCallback(struct uvc_frame *frame, void *userData);
91         QString fourccToStr(const uint8_t *format) const;
92         void updateDevices();
93         inline static const PixFmtToStrMap &pixFmtToStr();
94         inline static const FourccToStrMap &v4l2FourccToStr();
95 };
96 
CaptureLibUVC(QObject * parent)97 CaptureLibUVC::CaptureLibUVC(QObject *parent):
98     Capture(parent)
99 {
100     this->d = new CaptureLibUVCPrivate(this);
101     auto uvcError = uvc_init(&this->d->m_uvcContext, usbGlobals->context());
102 
103     if (uvcError != UVC_SUCCESS) {
104         qDebug() << "CaptureLibUVC:" << uvc_strerror(uvcError);
105 
106         return;
107     }
108 
109     QObject::connect(usbGlobals,
110                      &UsbGlobals::devicesUpdated,
111                      [this] () {
112                         this->d->updateDevices();
113                      });
114     this->d->updateDevices();
115 }
116 
~CaptureLibUVC()117 CaptureLibUVC::~CaptureLibUVC()
118 {
119     if (this->d->m_uvcContext)
120         uvc_exit(this->d->m_uvcContext);
121 
122     delete this->d;
123 }
124 
webcams() const125 QStringList CaptureLibUVC::webcams() const
126 {
127     return this->d->m_devices.values();
128 }
129 
device() const130 QString CaptureLibUVC::device() const
131 {
132     return this->d->m_device;
133 }
134 
streams()135 QList<int> CaptureLibUVC::streams()
136 {
137     if (!this->d->m_streams.isEmpty())
138         return this->d->m_streams;
139 
140     QVariantList caps = this->caps(this->d->m_device);
141 
142     if (caps.isEmpty())
143         return QList<int>();
144 
145     return QList<int> {0};
146 }
147 
listTracks(const QString & mimeType)148 QList<int> CaptureLibUVC::listTracks(const QString &mimeType)
149 {
150     if (mimeType != "video/x-raw"
151         && !mimeType.isEmpty())
152         return QList<int>();
153 
154     QVariantList caps = this->caps(this->d->m_device);
155     QList<int> streams;
156 
157     for (int i = 0; i < caps.count(); i++)
158         streams << i;
159 
160     return streams;
161 }
162 
ioMethod() const163 QString CaptureLibUVC::ioMethod() const
164 {
165     return QString();
166 }
167 
nBuffers() const168 int CaptureLibUVC::nBuffers() const
169 {
170     return 0;
171 }
172 
description(const QString & webcam) const173 QString CaptureLibUVC::description(const QString &webcam) const
174 {
175     return this->d->m_descriptions.value(webcam);
176 }
177 
caps(const QString & webcam) const178 QVariantList CaptureLibUVC::caps(const QString &webcam) const
179 {
180     return this->d->m_devicesCaps.value(webcam);
181 }
182 
capsDescription(const AkCaps & caps) const183 QString CaptureLibUVC::capsDescription(const AkCaps &caps) const
184 {
185     if (caps.mimeType() != "video/unknown")
186         return QString();
187 
188     AkFrac fps = caps.property("fps").toString();
189 
190     return QString("%1, %2x%3, %4 FPS")
191                 .arg(caps.property("fourcc").toString())
192                 .arg(caps.property("width").toString())
193                 .arg(caps.property("height").toString())
194                 .arg(qRound(fps.value()));
195 }
196 
imageControls() const197 QVariantList CaptureLibUVC::imageControls() const
198 {
199     return this->d->m_imageControls.value(this->d->m_device);
200 }
201 
setImageControls(const QVariantMap & imageControls)202 bool CaptureLibUVC::setImageControls(const QVariantMap &imageControls)
203 {
204     QVariantMap imageControlsDiff;
205 
206     for (auto &control: this->imageControls()) {
207         auto params = control.toList();
208         auto ctrlName = params[0].toString();
209 
210         if (imageControls.contains(ctrlName)
211             && imageControls[ctrlName] != params[6]) {
212             imageControlsDiff[ctrlName] = imageControls[ctrlName];
213         }
214     }
215 
216     if (imageControlsDiff.isEmpty())
217         return false;
218 
219     uvc_device_handle_t *deviceHnd = nullptr;
220 
221     if (this->d->m_deviceHnd) {
222         deviceHnd = this->d->m_deviceHnd;
223     } else {
224         auto deviceVP = this->d->m_devices.key(this->d->m_device);
225         auto vendorId = deviceVP >> 16;
226         auto productId = deviceVP & 0xFFFF;
227 
228         uvc_device_t *device = nullptr;
229         auto error = uvc_find_device(this->d->m_uvcContext,
230                                      &device,
231                                      int(vendorId),
232                                      int(productId),
233                                      nullptr);
234 
235         if (error != UVC_SUCCESS)
236             return false;
237 
238         error = uvc_open(device, &deviceHnd);
239         uvc_unref_device(device);
240 
241         if (error != UVC_SUCCESS)
242             return false;
243     }
244 
245     for (auto pu = uvc_get_processing_units(deviceHnd); pu; pu = pu->next) {
246         for (auto &control: UvcControl::allSelectors(PROCESSING_UNIT))
247             if (pu->bmControls & control) {
248                 this->d->setControls(deviceHnd,
249                                      pu->bUnitID,
250                                      control,
251                                      PROCESSING_UNIT,
252                                      imageControlsDiff);
253             }
254     }
255 
256     if (!this->d->m_deviceHnd)
257         uvc_close(deviceHnd);
258 
259     QVariantList controls;
260 
261     for (const auto &control: this->d->m_imageControls.value(this->d->m_device)) {
262         auto controlParams = control.toList();
263         auto controlName = controlParams[0].toString();
264 
265         if (imageControlsDiff.contains(controlName))
266             controlParams[6] = imageControlsDiff[controlName];
267 
268         controls << QVariant(controlParams);
269     }
270 
271     this->d->m_imageControls[this->d->m_device] = controls;
272     emit this->imageControlsChanged(imageControlsDiff);
273 
274     return true;
275 }
276 
resetImageControls()277 bool CaptureLibUVC::resetImageControls()
278 {
279     QVariantMap controls;
280 
281     for (auto &control: this->imageControls()) {
282         auto params = control.toList();
283         controls[params[0].toString()] = params[5].toInt();
284     }
285 
286     return this->setImageControls(controls);
287 }
288 
cameraControls() const289 QVariantList CaptureLibUVC::cameraControls() const
290 {
291     return this->d->m_cameraControls.value(this->d->m_device);
292 }
293 
setCameraControls(const QVariantMap & cameraControls)294 bool CaptureLibUVC::setCameraControls(const QVariantMap &cameraControls)
295 {
296     QVariantMap cameraControlsDiff;
297 
298     for (auto &control: this->cameraControls()) {
299         auto params = control.toList();
300         auto ctrlName = params[0].toString();
301 
302         if (cameraControls.contains(ctrlName)
303             && cameraControls[ctrlName] != params[6]) {
304             cameraControlsDiff[ctrlName] = cameraControls[ctrlName];
305         }
306     }
307 
308     if (cameraControlsDiff.isEmpty())
309         return false;
310 
311     uvc_device_handle_t *deviceHnd = nullptr;
312 
313     if (this->d->m_deviceHnd) {
314         deviceHnd = this->d->m_deviceHnd;
315     } else {
316         auto deviceVP = this->d->m_devices.key(this->d->m_device);
317         auto vendorId = deviceVP >> 16;
318         auto productId = deviceVP & 0xFFFF;
319 
320         uvc_device_t *device = nullptr;
321         auto error = uvc_find_device(this->d->m_uvcContext,
322                                      &device,
323                                      int(vendorId),
324                                      int(productId),
325                                      nullptr);
326 
327         if (error != UVC_SUCCESS)
328             return false;
329 
330         error = uvc_open(device, &deviceHnd);
331         uvc_unref_device(device);
332 
333         if (error != UVC_SUCCESS)
334             return false;
335     }
336 
337     for (auto ca = uvc_get_input_terminals(deviceHnd); ca; ca = ca->next) {
338         for (auto &control: UvcControl::allSelectors(CAMERA_TERMINAL))
339             if (ca->bmControls & control) {
340                 this->d->setControls(deviceHnd,
341                                      ca->bTerminalID,
342                                      control,
343                                      CAMERA_TERMINAL,
344                                      cameraControlsDiff);
345             }
346     }
347 
348     if (!this->d->m_deviceHnd)
349         uvc_close(deviceHnd);
350 
351     QVariantList controls;
352 
353     for (const auto &control: this->d->m_cameraControls.value(this->d->m_device)) {
354         auto controlParams = control.toList();
355         auto controlName = controlParams[0].toString();
356 
357         if (cameraControlsDiff.contains(controlName))
358             controlParams[6] = cameraControlsDiff[controlName];
359 
360         controls << QVariant(controlParams);
361     }
362 
363     this->d->m_cameraControls[this->d->m_device] = controls;
364     emit this->cameraControlsChanged(cameraControlsDiff);
365 
366     return true;
367 }
368 
resetCameraControls()369 bool CaptureLibUVC::resetCameraControls()
370 {
371     QVariantMap controls;
372 
373     for (auto &control: this->cameraControls()) {
374         auto params = control.toList();
375         controls[params[0].toString()] = params[5].toInt();
376     }
377 
378     return this->setCameraControls(controls);
379 }
380 
readFrame()381 AkPacket CaptureLibUVC::readFrame()
382 {
383     this->d->m_mutex.lock();
384 
385     if (!this->d->m_curPacket)
386         if (!this->d->m_packetNotReady.wait(&this->d->m_mutex, TIME_OUT)) {
387             this->d->m_mutex.unlock();
388 
389             return {};
390         }
391 
392     auto packet = this->d->m_curPacket;
393     this->d->m_curPacket = {};
394 
395     this->d->m_mutex.unlock();
396 
397     return packet;
398 }
399 
uvcId(quint16 vendorId,quint16 productId) const400 QString CaptureLibUVC::uvcId(quint16 vendorId, quint16 productId) const
401 {
402     return QString("USB\\VID_v%1&PID_d%2")
403             .arg(vendorId, 4, 16, QChar('0'))
404             .arg(productId, 4, 16, QChar('0'));
405 }
406 
CaptureLibUVCPrivate(CaptureLibUVC * self)407 CaptureLibUVCPrivate::CaptureLibUVCPrivate(CaptureLibUVC *self):
408     self(self)
409 {
410 
411 }
412 
controlsList(uvc_device_handle_t * deviceHnd,uint8_t unit,uint8_t control,int controlType) const413 QVariantList CaptureLibUVCPrivate::controlsList(uvc_device_handle_t *deviceHnd,
414                                                 uint8_t unit,
415                                                 uint8_t control,
416                                                 int controlType) const
417 {
418     auto selector = UvcControl::bySelector(controlType, control);
419     int min = 0;
420     int max = 0;
421     int step = 0;
422     int defaultValue = 0;
423     int value = 0;
424 
425     if (selector->type == "integer") {
426         if (selector->signd) {
427             int16_t val = 0;
428 
429             if (uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_CUR) < 0)
430                 return {};
431 
432             value = val;
433             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_MIN);
434             min = val;
435             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_MAX);
436             max = val;
437             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_RES);
438             step = val;
439             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_DEF);
440             defaultValue = val;
441         } else {
442             uint16_t val = 0;
443 
444             if (uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_CUR) < 0)
445                 return {};
446 
447             value = val;
448             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint16_t), UVC_GET_MIN);
449             min = val;
450             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint16_t), UVC_GET_MAX);
451             max = val;
452             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint16_t), UVC_GET_RES);
453             step = val;
454             uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint16_t), UVC_GET_DEF);
455             defaultValue = val;
456         }
457     } else if (selector->type == "boolean") {
458         uint8_t val = false;
459 
460         if (uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_CUR) < 0)
461             return {};
462 
463         value = val;
464         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_MIN);
465         min = val;
466         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_MAX);
467         max = val;
468         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_RES);
469         step = val;
470         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_DEF);
471         defaultValue = val;
472     } else if (selector->type == "menu") {
473         uint8_t val = 0;
474 
475         if (uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(int16_t), UVC_GET_CUR) < 0)
476             return {};
477 
478         value = val;
479         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_MIN);
480         min = val;
481         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_MAX);
482         max = val;
483         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_RES);
484         step = val;
485         uvc_get_ctrl(deviceHnd, unit, control, &val, sizeof(uint8_t), UVC_GET_DEF);
486         defaultValue = val;
487     }
488 
489     return QVariantList {
490         selector->description,
491         selector->type,
492         min,
493         max,
494         step,
495         defaultValue,
496         value,
497         selector->menu
498     };
499 }
500 
setControls(uvc_device_handle_t * deviceHnd,uint8_t unit,uint8_t control,int controlType,const QVariantMap & values)501 int CaptureLibUVCPrivate::setControls(uvc_device_handle_t *deviceHnd,
502                                       uint8_t unit,
503                                       uint8_t control,
504                                       int controlType,
505                                       const QVariantMap &values)
506 {
507     auto selector = UvcControl::bySelector(controlType,
508                                            control);
509 
510     if (!values.contains(selector->description))
511         return -1;
512 
513     int result = -1;
514 
515     if (selector->type == "integer") {
516         if (selector->signd) {
517             auto val = int16_t(values[selector->description].toInt());
518             result = uvc_set_ctrl(deviceHnd,
519                                   unit,
520                                   control,
521                                   &val,
522                                   sizeof(int16_t));
523         } else {
524             auto val = uint16_t(values[selector->description].toUInt());
525             result = uvc_set_ctrl(deviceHnd,
526                                   unit,
527                                   control,
528                                   &val,
529                                   sizeof(uint16_t));
530         }
531     } else if (selector->type == "boolean") {
532         uint8_t val = values[selector->description].toBool();
533         result = uvc_set_ctrl(deviceHnd,
534                               unit,
535                               control,
536                               &val,
537                               sizeof(uint8_t));
538     } else if (selector->type == "menu") {
539         auto val = uint8_t(values[selector->description].toUInt());
540         result = uvc_set_ctrl(deviceHnd,
541                               unit,
542                               control,
543                               &val,
544                               sizeof(uint8_t));
545     }
546 
547     return result;
548 }
549 
frameCallback(uvc_frame * frame,void * userData)550 void CaptureLibUVCPrivate::frameCallback(uvc_frame *frame, void *userData)
551 {
552     if (!frame || !userData)
553         return;
554 
555     auto self = reinterpret_cast<CaptureLibUVCPrivate *>(userData);
556 
557     self->m_mutex.lock();
558 
559     AkCaps caps("video/unknown");
560     caps.setProperty("fourcc", CaptureLibUVCPrivate::pixFmtToStr().value(frame->frame_format));
561     caps.setProperty("width", frame->width);
562     caps.setProperty("height", frame->height);
563     caps.setProperty("fps", self->m_fps.toString());
564 
565     QByteArray buffer(reinterpret_cast<const char *>(frame->data),
566                        int(frame->data_bytes));
567 
568     auto pts = qint64(QTime::currentTime().msecsSinceStartOfDay()
569                       * self->m_fps.value() / 1e3);
570 
571     AkPacket packet(caps);
572     packet.setBuffer(buffer);
573     packet.setPts(pts);
574     packet.setTimeBase(self->m_fps.invert());
575     packet.setIndex(0);
576     packet.setId(self->m_id);
577 
578     self->m_curPacket = packet;
579     self->m_packetNotReady.wakeAll();
580     self->m_mutex.unlock();
581 }
582 
fourccToStr(const uint8_t * format) const583 QString CaptureLibUVCPrivate::fourccToStr(const uint8_t *format) const
584 {
585     char fourcc[5];
586     memcpy(fourcc, format, sizeof(quint32));
587     fourcc[4] = 0;
588 
589     return QString(fourcc);
590 }
591 
updateDevices()592 void CaptureLibUVCPrivate::updateDevices()
593 {
594     if (!this->m_uvcContext)
595         return;
596 
597     decltype(this->m_devices) devicesList;
598     decltype(this->m_descriptions) descriptions;
599     decltype(this->m_devicesCaps) devicesCaps;
600     decltype(this->m_imageControls) imageControls;
601     decltype(this->m_cameraControls) cameraControls;
602 
603     uvc_device_t **devices = nullptr;
604     auto error = uvc_get_device_list(this->m_uvcContext, &devices);
605 
606     if (error != UVC_SUCCESS) {
607         qDebug() << "CaptureLibUVC:" << uvc_strerror(error);
608 
609         goto updateDevices_failed;
610     }
611 
612     for (int i = 0; devices[i] != nullptr; i++) {
613         uvc_device_descriptor_t *descriptor = nullptr;
614         error = uvc_get_device_descriptor(devices[i], &descriptor);
615 
616         if (error != UVC_SUCCESS) {
617             qDebug() << "CaptureLibUVC:" << uvc_strerror(error);
618 
619             continue;
620         }
621 
622         auto deviceId = self->uvcId(descriptor->idVendor,
623                                     descriptor->idProduct);
624         uvc_device_handle_t *deviceHnd = nullptr;
625 
626         if (this->m_deviceHnd && this->m_curDevice == deviceId)
627             deviceHnd = this->m_deviceHnd;
628         else {
629             error = uvc_open(devices[i], &deviceHnd);
630 
631             if (error != UVC_SUCCESS) {
632                 qDebug() << "CaptureLibUVC:" << uvc_strerror(error);
633                 uvc_free_device_descriptor(descriptor);
634 
635                 continue;
636             }
637         }
638 
639         auto formatDescription = uvc_get_format_descs(deviceHnd);
640 
641         if (!formatDescription) {
642             qDebug() << "CaptureLibUVC: Can't read format description";
643 
644             if (!this->m_deviceHnd || this->m_curDevice != deviceId)
645                 uvc_close(deviceHnd);
646 
647             uvc_free_device_descriptor(descriptor);
648 
649             continue;
650         }
651 
652         auto description =
653                 usbIds->description(descriptor->idVendor, descriptor->idProduct);
654 
655         if (description.isEmpty()) {
656             if (QString(descriptor->manufacturer).isEmpty())
657                 description += QString("Vendor 0x%1")
658                                .arg(descriptor->idVendor, 4, 16, QChar('0'));
659             else
660                 description += QString(descriptor->manufacturer);
661 
662             description += ", ";
663 
664             if (QString(descriptor->product).isEmpty())
665                 description += QString("Product 0x%1")
666                                .arg(descriptor->idProduct, 4, 16, QChar('0'));
667             else
668                 description += QString(descriptor->product);
669         }
670 
671         devicesList[quint32((descriptor->idVendor << 16)
672                             | descriptor->idProduct)] = deviceId;
673         descriptions[deviceId] = description;
674         devicesCaps[deviceId] = QVariantList();
675         AkCaps videoCaps;
676         videoCaps.setMimeType("video/unknown");
677 
678         for (; formatDescription; formatDescription = formatDescription->next) {
679             auto fourCC = this->fourccToStr(formatDescription->fourccFormat);
680             fourCC = CaptureLibUVCPrivate::v4l2FourccToStr().value(fourCC,
681                                                                    fourCC);
682 
683             if (std::find(CaptureLibUVCPrivate::pixFmtToStr().cbegin(),
684                           CaptureLibUVCPrivate::pixFmtToStr().cend(),
685                           fourCC) == CaptureLibUVCPrivate::pixFmtToStr().cend())
686                 continue;
687 
688             videoCaps.setProperty("fourcc", fourCC);
689 
690             for (auto description = formatDescription->frame_descs;
691                  description;
692                  description = description->next) {
693                 videoCaps.setProperty("width", description->wWidth);
694                 videoCaps.setProperty("height", description->wHeight);
695 
696                 if (description->intervals) {
697                     int prevInterval = 0;
698 
699                     for (auto interval = description->intervals; interval && *interval; interval++) {
700                         auto fps = AkFrac(100e5, *interval);
701                         auto fpsValue = qRound(fps.value());
702 
703                         if (prevInterval != fpsValue) {
704                             videoCaps.setProperty("fps", fps.toString());
705                             devicesCaps[deviceId] << QVariant::fromValue(videoCaps);
706                         }
707 
708                         prevInterval = fpsValue;
709                     }
710                 } else if (description->dwFrameIntervalStep > 0
711                          && description->dwMinFrameInterval != description->dwMaxFrameInterval) {
712                     int prevInterval = 0;
713 
714                     for (auto interval = description->dwMinFrameInterval;
715                          interval <= description->dwMaxFrameInterval;
716                          interval += description->dwFrameIntervalStep) {
717                         auto fps = AkFrac(100e5, interval);
718                         auto fpsValue = qRound(fps.value());
719 
720                         if (prevInterval != fpsValue) {
721                             videoCaps.setProperty("fps", fps.toString());
722                             devicesCaps[deviceId] << QVariant::fromValue(videoCaps);
723                         }
724 
725                         prevInterval = fpsValue;
726                     }
727                 } else {
728                     auto fps = AkFrac(100e5, description->dwDefaultFrameInterval);
729                     videoCaps.setProperty("fps", fps.toString());
730                     devicesCaps[deviceId] << QVariant::fromValue(videoCaps);
731                 }
732             }
733         }
734 
735         QVariantList deviceControls;
736 
737         for (auto pu = uvc_get_processing_units(deviceHnd); pu; pu = pu->next) {
738             for (auto &control: UvcControl::allSelectors(PROCESSING_UNIT))
739                 if (pu->bmControls & control) {
740                     auto controls =
741                             this->controlsList(deviceHnd,
742                                                pu->bUnitID,
743                                                control,
744                                                PROCESSING_UNIT);
745 
746                     if (!controls.isEmpty())
747                         deviceControls << QVariant(controls);
748                 }
749         }
750 
751         imageControls[deviceId] = deviceControls;
752         deviceControls.clear();
753 
754         for (auto ca = uvc_get_input_terminals(deviceHnd); ca; ca = ca->next) {
755             for (auto &control: UvcControl::allSelectors(CAMERA_TERMINAL))
756                 if (ca->bmControls & control) {
757                     auto controls =
758                             this->controlsList(deviceHnd,
759                                                ca->bTerminalID,
760                                                control,
761                                                CAMERA_TERMINAL);
762 
763                     if (!controls.isEmpty())
764                         deviceControls << QVariant(controls);
765                 }
766         }
767 
768         cameraControls[deviceId] = deviceControls;
769 
770         if (!this->m_deviceHnd || this->m_curDevice != deviceId)
771             uvc_close(deviceHnd);
772 
773         uvc_free_device_descriptor(descriptor);
774     }
775 
776 updateDevices_failed:
777     if (devices)
778         uvc_free_device_list(devices, 1);
779 
780     if (devicesCaps.isEmpty()) {
781         devicesList.clear();
782         descriptions.clear();
783         imageControls.clear();
784         cameraControls.clear();
785     }
786 
787     this->m_descriptions = descriptions;
788     this->m_devicesCaps = devicesCaps;
789     this->m_imageControls = imageControls;
790     this->m_cameraControls = cameraControls;
791 
792     if (this->m_devices != devicesList) {
793         this->m_devices = devicesList;
794         emit self->webcamsChanged(this->m_devices.values());
795     }
796 }
797 
pixFmtToStr()798 const PixFmtToStrMap &CaptureLibUVCPrivate::pixFmtToStr()
799 {
800     static const PixFmtToStrMap &pixFmtToStr {
801         {UVC_FRAME_FORMAT_YUYV  , "YUY2"  },
802         {UVC_FRAME_FORMAT_UYVY  , "UYVY"  },
803         {UVC_FRAME_FORMAT_RGB  ,  "RGB"   },
804         {UVC_FRAME_FORMAT_BGR  ,  "BGR"   },
805         {UVC_FRAME_FORMAT_MJPEG , "MJPG"  },
806         {UVC_FRAME_FORMAT_GRAY8 , "GRAY8" },
807         {UVC_FRAME_FORMAT_GRAY16, "GRAY16"},
808         {UVC_FRAME_FORMAT_BY8   , "BY8"   },
809         {UVC_FRAME_FORMAT_BA81  , "SBGGR8"},
810         {UVC_FRAME_FORMAT_SGRBG8, "SGRBG8"},
811         {UVC_FRAME_FORMAT_SGBRG8, "SGBRG8"},
812         {UVC_FRAME_FORMAT_SRGGB8, "SRGGB8"},
813         {UVC_FRAME_FORMAT_SBGGR8, "SBGGR8"},
814     };
815 
816     return pixFmtToStr;
817 }
818 
v4l2FourccToStr()819 const FourccToStrMap &CaptureLibUVCPrivate::v4l2FourccToStr()
820 {
821     static const FourccToStrMap fourccToStr {
822         {"RGB3", "RGB24" },
823         {"BGR3", "BGR24" },
824         {"Y800", "GRAY8" },
825         {"Y16 ", "GRAY16"},
826         {"BA81", "SBGGR8"},
827         {"GRBG", "SGRBG8"},
828         {"GBRG", "SGBRG8"},
829         {"RGGB", "SRGGB8"},
830         {"BGGR", "SBGGR8"},
831     };
832 
833     return fourccToStr;
834 }
835 
init()836 bool CaptureLibUVC::init()
837 {
838     if (this->d->m_devices.isEmpty() || this->d->m_device.isEmpty())
839         return false;
840 
841     QList<int> streams = this->streams();
842 
843     if (streams.isEmpty()) {
844         qDebug() << "CaptureLibUVC: No streams available.";
845 
846         return false;
847     }
848 
849     auto deviceVP = this->d->m_devices.key(this->d->m_device);
850     auto vendorId = deviceVP >> 16;
851     auto productId = deviceVP & 0xFFFF;
852 
853     uvc_device_t *device = nullptr;
854     auto error = uvc_find_device(this->d->m_uvcContext,
855                                  &device,
856                                  int(vendorId),
857                                  int(productId),
858                                  nullptr);
859 
860     if (error != UVC_SUCCESS) {
861         qDebug() << "CaptureLibUVC:" << uvc_strerror(error);
862 
863         return false;
864     }
865 
866     error = uvc_open(device, &this->d->m_deviceHnd);
867     uvc_unref_device(device);
868 
869     if (error != UVC_SUCCESS) {
870         qDebug() << "CaptureLibUVC:" << uvc_strerror(error);
871 
872         return false;
873     }
874 
875     QVariantList supportedCaps = this->caps(this->d->m_device);
876     AkCaps caps = supportedCaps[streams[0]].value<AkCaps>();
877     int fps = qRound(AkFrac(caps.property("fps").toString()).value());
878 
879     uvc_stream_ctrl_t streamCtrl;
880     error = uvc_get_stream_ctrl_format_size(this->d->m_deviceHnd,
881                                             &streamCtrl,
882                                             CaptureLibUVCPrivate::pixFmtToStr().key(caps.property("fourcc").toString()),
883                                             caps.property("width").toInt(),
884                                             caps.property("height").toInt(),
885                                             fps);
886 
887     if (error != UVC_SUCCESS) {
888         qDebug() << "CaptureLibUVC:" << uvc_strerror(error);
889 
890         goto init_failed;
891     }
892 
893     error = uvc_start_streaming(this->d->m_deviceHnd,
894                                 &streamCtrl,
895                                 this->d->frameCallback,
896                                 this->d,
897                                 0);
898 
899     if (error != UVC_SUCCESS) {
900         qDebug() << "CaptureLibUVC:" << uvc_strerror(error);
901 
902         goto init_failed;
903     }
904 
905     this->d->m_curDevice = this->d->m_device;
906     this->d->m_id = Ak::id();
907     this->d->m_fps = AkFrac(fps, 1);
908 
909     return true;
910 
911 init_failed:
912     uvc_close(this->d->m_deviceHnd);
913     this->d->m_deviceHnd = nullptr;
914 
915     return false;
916 }
917 
uninit()918 void CaptureLibUVC::uninit()
919 {
920     this->d->m_mutex.lock();
921 
922     if (this->d->m_deviceHnd) {
923         uvc_stop_streaming(this->d->m_deviceHnd);
924         uvc_close(this->d->m_deviceHnd);
925         this->d->m_deviceHnd = nullptr;
926     }
927 
928     this->d->m_curPacket = AkPacket();
929     this->d->m_curDevice.clear();
930     this->d->m_id = -1;
931     this->d->m_fps = AkFrac();
932     this->d->m_mutex.unlock();
933 }
934 
setDevice(const QString & device)935 void CaptureLibUVC::setDevice(const QString &device)
936 {
937     if (this->d->m_device == device)
938         return;
939 
940     this->d->m_device = device;
941     emit this->deviceChanged(device);
942 }
943 
setStreams(const QList<int> & streams)944 void CaptureLibUVC::setStreams(const QList<int> &streams)
945 {
946     if (streams.isEmpty())
947         return;
948 
949     int stream = streams[0];
950 
951     if (stream < 0)
952         return;
953 
954     auto supportedCaps = this->caps(this->d->m_device);
955 
956     if (stream >= supportedCaps.length())
957         return;
958 
959     QList<int> inputStreams {stream};
960 
961     if (this->streams() == inputStreams)
962         return;
963 
964     this->d->m_streams = inputStreams;
965     emit this->streamsChanged(inputStreams);
966 }
967 
setIoMethod(const QString & ioMethod)968 void CaptureLibUVC::setIoMethod(const QString &ioMethod)
969 {
970     Q_UNUSED(ioMethod)
971 }
972 
setNBuffers(int nBuffers)973 void CaptureLibUVC::setNBuffers(int nBuffers)
974 {
975     Q_UNUSED(nBuffers)
976 }
977 
resetDevice()978 void CaptureLibUVC::resetDevice()
979 {
980     this->setDevice("");
981 }
982 
resetStreams()983 void CaptureLibUVC::resetStreams()
984 {
985     auto supportedCaps = this->caps(this->d->m_device);
986     QList<int> streams;
987 
988     if (!supportedCaps.isEmpty())
989         streams << 0;
990 
991     this->setStreams(streams);
992 }
993 
resetIoMethod()994 void CaptureLibUVC::resetIoMethod()
995 {
996 }
997 
resetNBuffers()998 void CaptureLibUVC::resetNBuffers()
999 {
1000 }
1001 
reset()1002 void CaptureLibUVC::reset()
1003 {
1004     this->resetStreams();
1005     this->resetImageControls();
1006     this->resetCameraControls();
1007 }
1008 
controls()1009 const QVector<UvcControl> &UvcControl::controls()
1010 {
1011     static const QVector<UvcControl> controls {
1012         // Processing Units
1013         {PROCESSING_UNIT, UVC_PU_CONTROL_UNDEFINED                     , ""                              , ""       , false, {}},
1014         {PROCESSING_UNIT, UVC_PU_BACKLIGHT_COMPENSATION_CONTROL        , "Backlight Compensation"        , "integer", false, {}},
1015         {PROCESSING_UNIT, UVC_PU_BRIGHTNESS_CONTROL                    , "Brightness"                    , "integer", true , {}},
1016         {PROCESSING_UNIT, UVC_PU_CONTRAST_CONTROL                      , "Contrast"                      , "integer", false, {}},
1017         {PROCESSING_UNIT, UVC_PU_GAIN_CONTROL                          , "Gain"                          , "integer", false, {}},
1018         {PROCESSING_UNIT, UVC_PU_POWER_LINE_FREQUENCY_CONTROL          , "Power Line Frequency"          , "menu"   , false, {"Disabled",
1019                                                                                                                               "50 Hz",
1020                                                                                                                               "60 Hz",
1021                                                                                                                               "Auto"}},
1022         {PROCESSING_UNIT, UVC_PU_HUE_CONTROL                           , "Hue"                           , "integer", true , {}},
1023         {PROCESSING_UNIT, UVC_PU_SATURATION_CONTROL                    , "Saturation"                    , "integer", false, {}},
1024         {PROCESSING_UNIT, UVC_PU_SHARPNESS_CONTROL                     , "Sharpness"                     , "integer", false, {}},
1025         {PROCESSING_UNIT, UVC_PU_GAMMA_CONTROL                         , "Gamma"                         , "integer", false, {}},
1026         {PROCESSING_UNIT, UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL     , "White Balance Temperature"     , "integer", false, {}},
1027         {PROCESSING_UNIT, UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, "White Balance Temperature Auto", "boolean", false, {}},
1028         //{PROCESSING_UNIT, UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL       , "White Balance Component"       , "integer", false, {}},
1029         {PROCESSING_UNIT, UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL  , "White Balance Component Auto"  , "boolean", false, {}},
1030         {PROCESSING_UNIT, UVC_PU_DIGITAL_MULTIPLIER_CONTROL            , "Digital Multiplier"            , "integer", false, {}},
1031         {PROCESSING_UNIT, UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL      , "Digital Multiplier Limit"      , "integer", false, {}},
1032         {PROCESSING_UNIT, UVC_PU_HUE_AUTO_CONTROL                      , "Hue Auto"                      , "boolean", false, {}},
1033         {PROCESSING_UNIT, UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL         , "Analog Video Standard"         , "menu"   , false, {"None",
1034                                                                                                                               "NTSC - 525/60",
1035                                                                                                                               "PAL - 625/50",
1036                                                                                                                               "SECAM - 625/50",
1037                                                                                                                               "NTSC - 625/50",
1038                                                                                                                               "PAL - 525/60"}},
1039         {PROCESSING_UNIT, UVC_PU_ANALOG_LOCK_STATUS_CONTROL            , "Analog Lock Status"            , "menu"   , false, {"Locked",
1040                                                                                                                               "Unlocked"}},
1041         {PROCESSING_UNIT, UVC_PU_CONTRAST_AUTO_CONTROL                 , "Contrast Auto"                 , "boolean", false, {}},
1042 
1043         // Camera Terminals
1044         {CAMERA_TERMINAL, UVC_CT_CONTROL_UNDEFINED                     , ""                              , ""       , false, {}},
1045         {CAMERA_TERMINAL, UVC_CT_SCANNING_MODE_CONTROL                 , "Scanning Mode"                 , "boolean", false, {}},
1046         //{CAMERA_TERMINAL, UVC_CT_AE_MODE_CONTROL                       , "AE Mode"                       , "", false, {}},
1047         //{CAMERA_TERMINAL, UVC_CT_AE_PRIORITY_CONTROL                   , "AE Priority"                   , "", false, {}},
1048         //{CAMERA_TERMINAL, UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL        , "Exposure Time Absolute"        , "", false, {}},
1049         //{CAMERA_TERMINAL, UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL        , "Exposure Time Relative"        , "", false, {}},
1050         {CAMERA_TERMINAL, UVC_CT_FOCUS_ABSOLUTE_CONTROL                , "Focus Absolute"                , "integer", false, {}},
1051         //{CAMERA_TERMINAL, UVC_CT_FOCUS_RELATIVE_CONTROL                , "Focus Relative"                , "", false, {}},
1052         {CAMERA_TERMINAL, UVC_CT_FOCUS_AUTO_CONTROL                    , "Focus Auto"                    , "boolean", false, {}},
1053         {CAMERA_TERMINAL, UVC_CT_IRIS_ABSOLUTE_CONTROL                 , "Iris Absolute"                 , "integer", false, {}},
1054         //{CAMERA_TERMINAL, UVC_CT_IRIS_RELATIVE_CONTROL                 , "Iris Relative"                 , "", false, {}},
1055         {CAMERA_TERMINAL, UVC_CT_ZOOM_ABSOLUTE_CONTROL                 , "Zoom Absolute"                 , "integer", false, {}},
1056         //{CAMERA_TERMINAL, UVC_CT_ZOOM_RELATIVE_CONTROL                 , "Zoom Relative"                 , "", false, {}},
1057         //{CAMERA_TERMINAL, UVC_CT_PANTILT_ABSOLUTE_CONTROL              , "Pantilt Absolute"              , "", false, {}},
1058         //{CAMERA_TERMINAL, UVC_CT_PANTILT_RELATIVE_CONTROL              , "Pantilt Relative"              , "", false, {}},
1059         {CAMERA_TERMINAL, UVC_CT_ROLL_ABSOLUTE_CONTROL                 , "Roll Absolute"                 , "integer", true, {}},
1060         //{CAMERA_TERMINAL, UVC_CT_ROLL_RELATIVE_CONTROL                 , "Roll Relative"                 , "", false, {}},
1061         {CAMERA_TERMINAL, UVC_CT_PRIVACY_CONTROL                       , "Privacy"                       , "boolean", false, {}},
1062         {CAMERA_TERMINAL, UVC_CT_FOCUS_SIMPLE_CONTROL                  , "Focus Simple"                  , "menu"   , false, {"Full Range",
1063                                                                                                                               "Macro",
1064                                                                                                                               "People",
1065                                                                                                                               "Scene"}},
1066         //{CAMERA_TERMINAL, UVC_CT_DIGITAL_WINDOW_CONTROL                , "Digital Window"                , "", false, {}},
1067         //{CAMERA_TERMINAL, UVC_CT_REGION_OF_INTEREST_CONTROL            , "Region of Interest"            , "", false, {}}
1068     };
1069 
1070     return controls;
1071 }
1072 
bySelector(int controlType,uint8_t selector)1073 const UvcControl *UvcControl::bySelector(int controlType, uint8_t selector)
1074 {
1075     for (auto &control: controls())
1076         if (control.controlType == controlType
1077             && control.selector == selector)
1078             return &control;
1079 
1080     // Returns default for control type.
1081     for (auto &control: controls())
1082         if (control.controlType == controlType)
1083             return &control;
1084 
1085     return &controls().first();
1086 }
1087 
allSelectors(int controlType)1088 QVector<uint8_t> UvcControl::allSelectors(int controlType)
1089 {
1090     QVector<uint8_t> selectors;
1091 
1092     for (int i = 1; i < controls().size(); i++)
1093         if (controls()[i].controlType == controlType)
1094             selectors << controls()[i].selector;
1095 
1096     return selectors;
1097 }
1098 
1099 #include "moc_capturelibuvc.cpp"
1100