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