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 <QCoreApplication>
21 #include <QSharedPointer>
22 #include <QMap>
23 #include <QSize>
24 #include <QDateTime>
25 #include <QVariant>
26 #include <QMutex>
27 #include <QWaitCondition>
28 #include <ak.h>
29 #include <akfrac.h>
30 #include <akcaps.h>
31 #include <akpacket.h>
32 #include <dshow.h>
33 #include <dbt.h>
34 #include <usbiodef.h>
35
36 #include "capturedshow.h"
37 #include "framegrabber.h"
38
39 #define TIME_BASE 1.0e7
40 #define SOURCE_FILTER_NAME L"Source"
41
42 DEFINE_GUID(CLSID_SampleGrabber, 0xc1f400a0, 0x3f08, 0x11d3, 0x9f, 0x0b, 0x00, 0x60, 0x08, 0x03, 0x9e, 0x37);
43 DEFINE_GUID(CLSID_NullRenderer, 0xc1f400a4, 0x3f08, 0x11d3, 0x9f, 0x0b, 0x00, 0x60, 0x08, 0x03, 0x9e, 0x37);
44
45 Q_CORE_EXPORT HINSTANCE qWinAppInst();
46
operator <(REFGUID guid1,REFGUID guid2)47 __inline bool operator <(REFGUID guid1, REFGUID guid2)
48 {
49 return guid1.Data1 < guid2.Data1;
50 }
51
52 using VideoProcAmpPropertyMap = QMap<VideoProcAmpProperty, QString>;
53
initVideoProcAmpPropertyMap()54 inline VideoProcAmpPropertyMap initVideoProcAmpPropertyMap()
55 {
56 VideoProcAmpPropertyMap vpapToStr = {
57 {VideoProcAmp_Brightness , "Brightness" },
58 {VideoProcAmp_Contrast , "Contrast" },
59 {VideoProcAmp_Hue , "Hue" },
60 {VideoProcAmp_Saturation , "Saturation" },
61 {VideoProcAmp_Sharpness , "Sharpness" },
62 {VideoProcAmp_Gamma , "Gamma" },
63 {VideoProcAmp_ColorEnable , "Color Enable" },
64 {VideoProcAmp_WhiteBalance , "White Balance" },
65 {VideoProcAmp_BacklightCompensation, "Backlight Compensation"},
66 {VideoProcAmp_Gain , "Gain" }
67 };
68
69 return vpapToStr;
70 }
71
72 Q_GLOBAL_STATIC_WITH_ARGS(VideoProcAmpPropertyMap, vpapToStr, (initVideoProcAmpPropertyMap()))
73
74 using CameraControlMap = QMap<CameraControlProperty, QString>;
75
initCameraControlMap()76 inline CameraControlMap initCameraControlMap()
77 {
78 CameraControlMap ccToStr = {
79 {CameraControl_Pan , "Pan" },
80 {CameraControl_Tilt , "Tilt" },
81 {CameraControl_Roll , "Roll" },
82 {CameraControl_Zoom , "Zoom" },
83 {CameraControl_Exposure, "Exposure"},
84 {CameraControl_Iris , "Iris" },
85 {CameraControl_Focus , "Focus" }
86 };
87
88 return ccToStr;
89 }
90
91 Q_GLOBAL_STATIC_WITH_ARGS(CameraControlMap, ccToStr, (initCameraControlMap()))
92
93 using GuidToStrMap = QMap<GUID, QString>;
94
initGuidToStrMap()95 inline GuidToStrMap initGuidToStrMap()
96 {
97 GuidToStrMap guidToStr = {
98 {MEDIASUBTYPE_CLPL , "CLPL" },
99 {MEDIASUBTYPE_YUYV , "YUYV" },
100 {MEDIASUBTYPE_IYUV , "IYUV" },
101 {MEDIASUBTYPE_YVU9 , "YVU9" },
102 {MEDIASUBTYPE_Y411 , "Y411" },
103 {MEDIASUBTYPE_Y41P , "Y41P" },
104 {MEDIASUBTYPE_YUY2 , "YUY2" },
105 {MEDIASUBTYPE_YVYU , "YVYU" },
106 {MEDIASUBTYPE_UYVY , "UYVY" },
107 {MEDIASUBTYPE_Y211 , "Y211" },
108 {MEDIASUBTYPE_CLJR , "CLJR" },
109 {MEDIASUBTYPE_IF09 , "IF09" },
110 {MEDIASUBTYPE_CPLA , "CPLA" },
111 {MEDIASUBTYPE_MJPG , "MJPG" },
112 {MEDIASUBTYPE_TVMJ , "TVMJ" },
113 {MEDIASUBTYPE_WAKE , "WAKE" },
114 {MEDIASUBTYPE_CFCC , "CFCC" },
115 {MEDIASUBTYPE_IJPG , "IJPG" },
116 {MEDIASUBTYPE_Plum , "Plum" },
117 {MEDIASUBTYPE_DVCS , "DVCS" },
118 {MEDIASUBTYPE_DVSD , "DVSD" },
119 {MEDIASUBTYPE_MDVF , "MDVF" },
120 {MEDIASUBTYPE_RGB1 , "RGB1" },
121 {MEDIASUBTYPE_RGB4 , "BGRX" },
122 {MEDIASUBTYPE_RGB8 , "RGB8" },
123 {MEDIASUBTYPE_RGB565 , "RGB565" },
124 {MEDIASUBTYPE_RGB555 , "RGB555" },
125 {MEDIASUBTYPE_RGB24 , "RGB" },
126 {MEDIASUBTYPE_RGB32 , "BGRX" },
127 {MEDIASUBTYPE_ARGB1555 , "ARGB555" },
128 {MEDIASUBTYPE_ARGB4444 , "ARGB4444"},
129 {MEDIASUBTYPE_ARGB32 , "ARGB" },
130 {MEDIASUBTYPE_AYUV , "AYUV" },
131 {MEDIASUBTYPE_AI44 , "AI44" },
132 {MEDIASUBTYPE_IA44 , "IA44" },
133 {MEDIASUBTYPE_RGB32_D3D_DX7_RT , "7R32" },
134 {MEDIASUBTYPE_RGB16_D3D_DX7_RT , "7R16" },
135 {MEDIASUBTYPE_ARGB32_D3D_DX7_RT , "7A88" },
136 {MEDIASUBTYPE_ARGB4444_D3D_DX7_RT, "7A44" },
137 {MEDIASUBTYPE_ARGB1555_D3D_DX7_RT, "7A15" },
138 {MEDIASUBTYPE_RGB32_D3D_DX9_RT , "9R32" },
139 {MEDIASUBTYPE_RGB16_D3D_DX9_RT , "9R16" },
140 {MEDIASUBTYPE_ARGB32_D3D_DX9_RT , "9A88" },
141 {MEDIASUBTYPE_ARGB4444_D3D_DX9_RT, "9A44" },
142 {MEDIASUBTYPE_ARGB1555_D3D_DX9_RT, "9A15" },
143 {MEDIASUBTYPE_YV12 , "YV12" },
144 {MEDIASUBTYPE_NV12 , "NV12" },
145 {MEDIASUBTYPE_IMC1 , "IMC1" },
146 {MEDIASUBTYPE_IMC2 , "IMC2" },
147 {MEDIASUBTYPE_IMC3 , "IMC3" },
148 {MEDIASUBTYPE_IMC4 , "IMC4" },
149 {MEDIASUBTYPE_S340 , "S340" },
150 {MEDIASUBTYPE_S342 , "S342" },
151 {MEDIASUBTYPE_QTRpza , "rpza" },
152 {MEDIASUBTYPE_QTSmc , "smc " },
153 {MEDIASUBTYPE_QTRle , "rle " },
154 {MEDIASUBTYPE_QTJpeg , "jpeg" },
155 {MEDIASUBTYPE_dvsd , "dvsd" },
156 {MEDIASUBTYPE_dvhd , "dvhd" },
157 {MEDIASUBTYPE_dvsl , "dvsl" },
158 {MEDIASUBTYPE_dv25 , "dv25" },
159 {MEDIASUBTYPE_dv50 , "dv50" },
160 {MEDIASUBTYPE_dvh1 , "dvh1" },
161 };
162
163 return guidToStr;
164 }
165
166 Q_GLOBAL_STATIC_WITH_ARGS(GuidToStrMap, guidToStr, (initGuidToStrMap()))
167
168 using IoMethodMap = QMap<CaptureDShow::IoMethod, QString>;
169
initIoMethodMap()170 inline IoMethodMap initIoMethodMap()
171 {
172 IoMethodMap ioMethodToStr = {
173 {CaptureDShow::IoMethodDirectRead, "directRead"},
174 {CaptureDShow::IoMethodGrabSample, "grabSample"},
175 {CaptureDShow::IoMethodGrabBuffer, "grabBuffer"}
176 };
177
178 return ioMethodToStr;
179 }
180
181 Q_GLOBAL_STATIC_WITH_ARGS(IoMethodMap, ioMethodToStr, (initIoMethodMap()))
182
183 using BaseFilterPtr = QSharedPointer<IBaseFilter>;
184 using SampleGrabberPtr = QSharedPointer<ISampleGrabber>;
185 using MonikerPtr = QSharedPointer<IMoniker>;
186 using MonikersMap = QMap<QString, MonikerPtr>;
187 using MediaTypePtr = QSharedPointer<AM_MEDIA_TYPE>;
188 using MediaTypesList = QList<MediaTypePtr>;
189 using PinPtr = QSharedPointer<IPin>;
190 using PinList = QList<PinPtr>;
191
192 class CaptureDShowPrivate
193 {
194 public:
195 CaptureDShow *self;
196 QString m_device;
197 QList<int> m_streams;
198 QStringList m_devices;
199 QMap<QString, QString> m_descriptions;
200 QMap<QString, QVariantList> m_devicesCaps;
201 qint64 m_id {-1};
202 AkFrac m_timeBase;
203 CaptureDShow::IoMethod m_ioMethod {CaptureDShow::IoMethodGrabSample};
204 QMap<QString, QSize> m_resolution;
205 BaseFilterPtr m_webcamFilter;
206 IGraphBuilder *m_graph {nullptr};
207 SampleGrabberPtr m_grabber;
208 FrameGrabber m_frameGrabber;
209 QByteArray m_curBuffer;
210 QMutex m_mutex;
211 QMutex m_controlsMutex;
212 QWaitCondition m_waitCondition;
213 QVariantList m_globalImageControls;
214 QVariantList m_globalCameraControls;
215 QVariantMap m_localImageControls;
216 QVariantMap m_localCameraControls;
217
218 explicit CaptureDShowPrivate(CaptureDShow *self);
219 QString devicePath(IPropertyBag *propertyBag) const;
220 QString deviceDescription(IPropertyBag *propertyBag) const;
221 QVariantList caps(IBaseFilter *baseFilter) const;
222 AkCaps capsFromMediaType(const AM_MEDIA_TYPE *mediaType) const;
223 AkCaps capsFromMediaType(const MediaTypePtr &mediaType) const;
224 HRESULT enumerateCameras(IEnumMoniker **ppEnum) const;
225 MonikersMap listMonikers() const;
226 MonikerPtr findMoniker(const QString &webcam) const;
227 QString monikerDisplayName(IMoniker *moniker) const;
228 IBaseFilter *findFilterP(const QString &webcam) const;
229 BaseFilterPtr findFilter(const QString &webcam) const;
230 MediaTypesList listMediaTypes(IBaseFilter *filter) const;
231 bool isPinConnected(IPin *pPin, bool *ok=nullptr) const;
232 PinPtr findUnconnectedPin(IBaseFilter *pFilter,
233 PIN_DIRECTION PinDir) const;
234 bool connectFilters(IGraphBuilder *pGraph,
235 IBaseFilter *pSrc,
236 IBaseFilter *pDest) const;
237 PinList enumPins(IBaseFilter *filter,
238 PIN_DIRECTION direction) const;
239 static void freeMediaType(AM_MEDIA_TYPE &mediaType);
240 static void deleteMediaType(AM_MEDIA_TYPE *mediaType);
241 QVariantList imageControls(IBaseFilter *filter) const;
242 bool setImageControls(IBaseFilter *filter,
243 const QVariantMap &imageControls) const;
244 QVariantList cameraControls(IBaseFilter *filter) const;
245 bool setCameraControls(IBaseFilter *filter,
246 const QVariantMap &cameraControls) const;
247 QVariantMap controlStatus(const QVariantList &controls) const;
248 QVariantMap mapDiff(const QVariantMap &map1,
249 const QVariantMap &map2) const;
250 void frameReceived(qreal time, const QByteArray &buffer);
251 void updateDevices();
252 };
253
CaptureDShow(QObject * parent)254 CaptureDShow::CaptureDShow(QObject *parent):
255 Capture(parent),
256 QAbstractNativeEventFilter()
257 {
258 this->d = new CaptureDShowPrivate(this);
259 QObject::connect(&this->d->m_frameGrabber,
260 &FrameGrabber::frameReady,
261 [this] (qreal time, const QByteArray &packet) {
262 this->d->frameReceived(time, packet);
263 });
264 qApp->installNativeEventFilter(this);
265 this->d->updateDevices();
266 }
267
~CaptureDShow()268 CaptureDShow::~CaptureDShow()
269 {
270 qApp->removeNativeEventFilter(this);
271 delete this->d;
272 }
273
webcams() const274 QStringList CaptureDShow::webcams() const
275 {
276 return this->d->m_devices;
277 }
278
device() const279 QString CaptureDShow::device() const
280 {
281 return this->d->m_device;
282 }
283
streams()284 QList<int> CaptureDShow::streams()
285 {
286 if (!this->d->m_streams.isEmpty())
287 return this->d->m_streams;
288
289 auto caps = this->caps(this->d->m_device);
290
291 if (caps.isEmpty())
292 return {};
293
294 return {0};
295 }
296
listTracks(const QString & mimeType)297 QList<int> CaptureDShow::listTracks(const QString &mimeType)
298 {
299 if (mimeType != "video/x-raw"
300 && !mimeType.isEmpty())
301 return {};
302
303 auto caps = this->caps(this->d->m_device);
304 QList<int> streams;
305
306 for (int i = 0; i < caps.count(); i++)
307 streams << i;
308
309 return streams;
310 }
311
ioMethod() const312 QString CaptureDShow::ioMethod() const
313 {
314 return ioMethodToStr->value(this->d->m_ioMethod, "any");
315 }
316
nBuffers() const317 int CaptureDShow::nBuffers() const
318 {
319 return 0;
320 }
321
description(const QString & webcam) const322 QString CaptureDShow::description(const QString &webcam) const
323 {
324 return this->d->m_descriptions.value(webcam);
325 }
326
caps(const QString & webcam) const327 QVariantList CaptureDShow::caps(const QString &webcam) const
328 {
329 return this->d->m_devicesCaps.value(webcam);
330 }
331
capsDescription(const AkCaps & caps) const332 QString CaptureDShow::capsDescription(const AkCaps &caps) const
333 {
334 if (caps.mimeType() != "video/unknown")
335 return {};
336
337 AkFrac fps = caps.property("fps").toString();
338
339 return QString("%1, %2x%3, %4 FPS")
340 .arg(caps.property("fourcc").toString())
341 .arg(caps.property("width").toString())
342 .arg(caps.property("height").toString())
343 .arg(qRound(fps.value()));
344 }
345
imageControls() const346 QVariantList CaptureDShow::imageControls() const
347 {
348 return this->d->m_globalImageControls;
349 }
350
setImageControls(const QVariantMap & imageControls)351 bool CaptureDShow::setImageControls(const QVariantMap &imageControls)
352 {
353 this->d->m_controlsMutex.lock();
354 auto globalImageControls = this->d->m_globalImageControls;
355 this->d->m_controlsMutex.unlock();
356
357 for (int i = 0; i < globalImageControls.count(); i++) {
358 auto control = globalImageControls[i].toList();
359 auto controlName = control[0].toString();
360
361 if (imageControls.contains(controlName)) {
362 control[6] = imageControls[controlName];
363 globalImageControls[i] = control;
364 }
365 }
366
367 this->d->m_controlsMutex.lock();
368
369 if (this->d->m_globalImageControls == globalImageControls) {
370 this->d->m_controlsMutex.unlock();
371
372 return false;
373 }
374
375 this->d->m_globalImageControls = globalImageControls;
376 this->d->m_controlsMutex.unlock();
377
378 emit this->imageControlsChanged(imageControls);
379
380 return true;
381 }
382
resetImageControls()383 bool CaptureDShow::resetImageControls()
384 {
385 QVariantMap controls;
386
387 for (auto &control: this->imageControls()) {
388 auto params = control.toList();
389 controls[params[0].toString()] = params[5].toInt();
390 }
391
392 return this->setImageControls(controls);
393 }
394
cameraControls() const395 QVariantList CaptureDShow::cameraControls() const
396 {
397 return this->d->m_globalCameraControls;
398 }
399
setCameraControls(const QVariantMap & cameraControls)400 bool CaptureDShow::setCameraControls(const QVariantMap &cameraControls)
401 {
402 this->d->m_controlsMutex.lock();
403 auto globalCameraControls = this->d->m_globalCameraControls;
404 this->d->m_controlsMutex.unlock();
405
406 for (int i = 0; i < globalCameraControls.count(); i++) {
407 auto control = globalCameraControls[i].toList();
408 auto controlName = control[0].toString();
409
410 if (cameraControls.contains(controlName)) {
411 control[6] = cameraControls[controlName];
412 globalCameraControls[i] = control;
413 }
414 }
415
416 this->d->m_controlsMutex.lock();
417
418 if (this->d->m_globalCameraControls == globalCameraControls) {
419 this->d->m_controlsMutex.unlock();
420
421 return false;
422 }
423
424 this->d->m_globalCameraControls = globalCameraControls;
425 this->d->m_controlsMutex.unlock();
426 emit this->cameraControlsChanged(cameraControls);
427
428 return true;
429 }
430
resetCameraControls()431 bool CaptureDShow::resetCameraControls()
432 {
433 QVariantMap controls;
434
435 for (auto &control: this->cameraControls()) {
436 auto params = control.toList();
437 controls[params[0].toString()] = params[5].toInt();
438 }
439
440 return this->setCameraControls(controls);
441 }
442
readFrame()443 AkPacket CaptureDShow::readFrame()
444 {
445 IBaseFilter *source = nullptr;
446 this->d->m_graph->FindFilterByName(SOURCE_FILTER_NAME, &source);
447
448 if (source) {
449 this->d->m_controlsMutex.lock();
450 auto imageControls = this->d->controlStatus(this->d->m_globalImageControls);
451 this->d->m_controlsMutex.unlock();
452
453 if (this->d->m_localImageControls != imageControls) {
454 auto controls = this->d->mapDiff(this->d->m_localImageControls,
455 imageControls);
456 this->d->setImageControls(source, controls);
457 this->d->m_localImageControls = imageControls;
458 }
459
460 this->d->m_controlsMutex.lock();
461 auto cameraControls = this->d->controlStatus(this->d->m_globalCameraControls);
462 this->d->m_controlsMutex.unlock();
463
464 if (this->d->m_localCameraControls != cameraControls) {
465 auto controls = this->d->mapDiff(this->d->m_localCameraControls,
466 cameraControls);
467 this->d->setCameraControls(source, controls);
468 this->d->m_localCameraControls = cameraControls;
469 }
470
471 source->Release();
472 }
473
474 AM_MEDIA_TYPE mediaType;
475 ZeroMemory(&mediaType, sizeof(AM_MEDIA_TYPE));
476 this->d->m_grabber->GetConnectedMediaType(&mediaType);
477 AkCaps caps = this->d->capsFromMediaType(&mediaType);
478 this->d->freeMediaType(mediaType);
479
480 AkPacket packet;
481 auto timestamp = QDateTime::currentMSecsSinceEpoch();
482 auto pts =
483 qint64(timestamp
484 * this->d->m_timeBase.invert().value()
485 / 1e3);
486
487 if (this->d->m_ioMethod != IoMethodDirectRead) {
488 this->d->m_mutex.lock();
489
490 if (this->d->m_curBuffer.isEmpty())
491 this->d->m_waitCondition.wait(&this->d->m_mutex, 1000);
492
493 if (!this->d->m_curBuffer.isEmpty()) {
494 int bufferSize = this->d->m_curBuffer.size();
495 QByteArray oBuffer(bufferSize, 0);
496 memcpy(oBuffer.data(),
497 this->d->m_curBuffer.constData(),
498 size_t(bufferSize));
499
500 packet = AkPacket(caps);
501 packet.setBuffer(oBuffer);
502 packet.setPts(pts);
503 packet.setTimeBase(this->d->m_timeBase);
504 packet.setIndex(0);
505 packet.setId(this->d->m_id);
506 this->d->m_curBuffer.clear();
507 }
508
509 this->d->m_mutex.unlock();
510 } else {
511 long bufferSize;
512
513 HRESULT hr = this->d->m_grabber->GetCurrentBuffer(&bufferSize, nullptr);
514
515 if (FAILED(hr))
516 return {};
517
518 QByteArray oBuffer(bufferSize, 0);
519 hr = this->d->m_grabber->GetCurrentBuffer(&bufferSize,
520 reinterpret_cast<long *>(oBuffer.data()));
521
522 if (FAILED(hr))
523 return {};
524
525 packet = AkPacket(caps);
526 packet.setBuffer(oBuffer);
527 packet.setPts(pts);
528 packet.setTimeBase(this->d->m_timeBase);
529 packet.setIndex(0);
530 packet.setId(this->d->m_id);
531 }
532
533 return packet;
534 }
535
nativeEventFilter(const QByteArray & eventType,void * message,long * result)536 bool CaptureDShow::nativeEventFilter(const QByteArray &eventType,
537 void *message,
538 long *result)
539 {
540 Q_UNUSED(eventType)
541
542 if (!message)
543 return false;
544
545 auto msg = reinterpret_cast<MSG *>(message);
546
547 if (msg->message == WM_DEVICECHANGE) {
548 switch (msg->wParam) {
549 case DBT_DEVICEARRIVAL:
550 case DBT_DEVICEREMOVECOMPLETE:
551 case DBT_DEVNODES_CHANGED: {
552 this->d->updateDevices();
553
554 if (result)
555 *result = TRUE;
556
557 return true;
558 }
559 default:
560 break;
561 }
562 }
563
564 return false;
565 }
566
CaptureDShowPrivate(CaptureDShow * self)567 CaptureDShowPrivate::CaptureDShowPrivate(CaptureDShow *self):
568 self(self)
569 {
570
571 }
572
devicePath(IPropertyBag * propertyBag) const573 QString CaptureDShowPrivate::devicePath(IPropertyBag *propertyBag) const
574 {
575 VARIANT var;
576 VariantInit(&var);
577 auto hr = propertyBag->Read(L"DevicePath", &var, nullptr);
578 QString devicePath;
579
580 if (SUCCEEDED(hr))
581 devicePath = QString::fromWCharArray(var.bstrVal);
582
583 VariantClear(&var);
584
585 return devicePath;
586 }
587
deviceDescription(IPropertyBag * propertyBag) const588 QString CaptureDShowPrivate::deviceDescription(IPropertyBag *propertyBag) const
589 {
590
591 VARIANT var;
592 VariantInit(&var);
593 auto hr = propertyBag->Read(L"Description", &var, nullptr);
594
595 if (FAILED(hr))
596 hr = propertyBag->Read(L"FriendlyName", &var, nullptr);
597
598 QString description;
599
600 if (SUCCEEDED(hr))
601 description = QString::fromWCharArray(var.bstrVal);
602
603 VariantClear(&var);
604
605 return description;
606 }
607
caps(IBaseFilter * baseFilter) const608 QVariantList CaptureDShowPrivate::caps(IBaseFilter *baseFilter) const
609 {
610 auto pins = this->enumPins(baseFilter, PINDIR_OUTPUT);
611 QVariantList caps;
612
613 for (auto &pin: pins) {
614 IEnumMediaTypes *pEnum = nullptr;
615
616 if (FAILED(pin->EnumMediaTypes(&pEnum)))
617 continue;
618
619 pEnum->Reset();
620 AM_MEDIA_TYPE *mediaType = nullptr;
621
622 while (pEnum->Next(1, &mediaType, nullptr) == S_OK) {
623 if (mediaType->formattype == FORMAT_VideoInfo
624 && mediaType->cbFormat >= sizeof(VIDEOINFOHEADER)
625 && mediaType->pbFormat != nullptr
626 && guidToStr->contains(mediaType->subtype)) {
627 auto videoCaps = this->capsFromMediaType(mediaType);
628
629 if (videoCaps)
630 caps << QVariant::fromValue(videoCaps);
631 }
632
633 this->deleteMediaType(mediaType);
634 }
635
636 pEnum->Release();
637 }
638
639 return caps;
640 }
641
capsFromMediaType(const AM_MEDIA_TYPE * mediaType) const642 AkCaps CaptureDShowPrivate::capsFromMediaType(const AM_MEDIA_TYPE *mediaType) const
643 {
644 if (!mediaType)
645 return {};
646
647 VIDEOINFOHEADER *videoInfoHeader =
648 reinterpret_cast<VIDEOINFOHEADER *>(mediaType->pbFormat);
649 QString fourcc = guidToStr->value(mediaType->subtype);
650
651 if (fourcc.isEmpty())
652 return {};
653
654 AkCaps videoCaps;
655 videoCaps.setMimeType("video/unknown");
656 videoCaps.setProperty("fourcc", fourcc);
657 videoCaps.setProperty("width", int(videoInfoHeader->bmiHeader.biWidth));
658 videoCaps.setProperty("height", int(videoInfoHeader->bmiHeader.biHeight));
659 AkFrac fps(TIME_BASE, videoInfoHeader->AvgTimePerFrame);
660 videoCaps.setProperty("fps", fps.toString());
661
662 return videoCaps;
663 }
664
capsFromMediaType(const MediaTypePtr & mediaType) const665 AkCaps CaptureDShowPrivate::capsFromMediaType(const MediaTypePtr &mediaType) const
666 {
667 return this->capsFromMediaType(mediaType.data());
668 }
669
enumerateCameras(IEnumMoniker ** ppEnum) const670 HRESULT CaptureDShowPrivate::enumerateCameras(IEnumMoniker **ppEnum) const
671 {
672 // Create the System Device Enumerator.
673 ICreateDevEnum *pDevEnum = nullptr;
674 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
675 nullptr,
676 CLSCTX_INPROC_SERVER,
677 IID_ICreateDevEnum,
678 reinterpret_cast<void **>(&pDevEnum));
679
680 if (SUCCEEDED(hr)) {
681 // Create an enumerator for the category.
682 hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
683 ppEnum,
684 0);
685
686 if (hr == S_FALSE)
687 hr = VFW_E_NOT_FOUND;
688
689 pDevEnum->Release();
690 }
691
692 return hr;
693 }
694
listMonikers() const695 MonikersMap CaptureDShowPrivate::listMonikers() const
696 {
697 MonikersMap monikers;
698 IEnumMoniker *pEnum = nullptr;
699 HRESULT hr = this->enumerateCameras(&pEnum);
700
701 if (SUCCEEDED(hr)) {
702 pEnum->Reset();
703 IMoniker *pMoniker = nullptr;
704
705 for (int i = 0; pEnum->Next(1, &pMoniker, nullptr) == S_OK; i++) {
706 IPropertyBag *pPropBag = nullptr;
707 HRESULT hr = pMoniker->BindToStorage(nullptr,
708 nullptr,
709 IID_IPropertyBag,
710 reinterpret_cast<void **>(&pPropBag));
711
712 if (FAILED(hr)) {
713 pMoniker->Release();
714
715 continue;
716 }
717
718 auto devicePath = this->devicePath(pPropBag);
719
720 if (devicePath.isEmpty())
721 devicePath = this->monikerDisplayName(pMoniker);
722
723 monikers[devicePath] =
724 MonikerPtr(pMoniker, [] (IMoniker *moniker) {
725 moniker->Release();
726 });
727 pPropBag->Release();
728 }
729
730 pEnum->Release();
731 }
732
733 return monikers;
734 }
735
findMoniker(const QString & webcam) const736 MonikerPtr CaptureDShowPrivate::findMoniker(const QString &webcam) const
737 {
738 auto monikers = this->listMonikers();
739
740 if (monikers.contains(webcam))
741 return monikers[webcam];
742
743 return {};
744 }
745
monikerDisplayName(IMoniker * moniker) const746 QString CaptureDShowPrivate::monikerDisplayName(IMoniker *moniker) const
747 {
748 IBindCtx *bind_ctx = nullptr;
749
750 if (FAILED(CreateBindCtx(0, &bind_ctx)))
751 return {};
752
753 LPOLESTR olestr = nullptr;
754 moniker->GetDisplayName(bind_ctx, nullptr, &olestr);
755 bind_ctx->Release();
756
757 return QString::fromWCharArray(olestr);
758 }
759
findFilterP(const QString & webcam) const760 IBaseFilter *CaptureDShowPrivate::findFilterP(const QString &webcam) const
761 {
762 auto moniker = this->findMoniker(webcam);
763
764 if (!moniker)
765 return nullptr;
766
767 IBaseFilter *filter = nullptr;
768 HRESULT hr = moniker->BindToObject(nullptr,
769 nullptr,
770 IID_IBaseFilter,
771 reinterpret_cast<void **>(&filter));
772
773 if (FAILED(hr))
774 return nullptr;
775
776 return filter;
777 }
778
findFilter(const QString & webcam) const779 BaseFilterPtr CaptureDShowPrivate::findFilter(const QString &webcam) const
780 {
781 auto filter = this->findFilterP(webcam);
782
783 if (!filter)
784 return {};
785
786 return BaseFilterPtr(filter, [] (IBaseFilter *filter) {
787 filter->Release();
788 });
789 }
790
listMediaTypes(IBaseFilter * filter) const791 MediaTypesList CaptureDShowPrivate::listMediaTypes(IBaseFilter *filter) const
792 {
793 auto pins = this->enumPins(filter, PINDIR_OUTPUT);
794 MediaTypesList mediaTypes;
795
796 for (auto &pin: pins) {
797 IEnumMediaTypes *pEnum = nullptr;
798
799 if (FAILED(pin->EnumMediaTypes(&pEnum)))
800 continue;
801
802 pEnum->Reset();
803 AM_MEDIA_TYPE *mediaType = nullptr;
804
805 while (pEnum->Next(1, &mediaType, nullptr) == S_OK)
806 if (mediaType->formattype == FORMAT_VideoInfo
807 && mediaType->cbFormat >= sizeof(VIDEOINFOHEADER)
808 && mediaType->pbFormat != nullptr
809 && guidToStr->contains(mediaType->subtype)) {
810 mediaTypes << MediaTypePtr(mediaType, this->deleteMediaType);
811 } else {
812 this->deleteMediaType(mediaType);
813 }
814
815 pEnum->Release();
816 }
817
818 return mediaTypes;
819 }
820
isPinConnected(IPin * pPin,bool * ok) const821 bool CaptureDShowPrivate::isPinConnected(IPin *pPin, bool *ok) const
822 {
823 IPin *pTmp = nullptr;
824 HRESULT hr = pPin->ConnectedTo(&pTmp);
825
826 if (ok)
827 *ok = true;
828
829 if (hr == VFW_E_NOT_CONNECTED)
830 return false;
831
832 if (FAILED(hr)) {
833 if (ok)
834 *ok = false;
835
836 return false;
837 }
838
839 if (!pTmp)
840 return false;
841
842 pTmp->Release();
843
844 return true;
845 }
846
findUnconnectedPin(IBaseFilter * pFilter,PIN_DIRECTION PinDir) const847 PinPtr CaptureDShowPrivate::findUnconnectedPin(IBaseFilter *pFilter,
848 PIN_DIRECTION PinDir) const
849 {
850 IEnumPins *pEnum = nullptr;
851
852 if (FAILED(pFilter->EnumPins(&pEnum)))
853 return {};
854
855 pEnum->Reset();
856 PinPtr matchedPin;
857 IPin *pPin = nullptr;
858
859 while (pEnum->Next(1, &pPin, nullptr) == S_OK) {
860 PIN_DIRECTION pinDir;
861
862 if (FAILED(pPin->QueryDirection(&pinDir))
863 || pinDir != PinDir)
864 continue;
865
866 bool ok = false;
867 bool connected = this->isPinConnected(pPin, &ok);
868
869 if (!ok || connected)
870 continue;
871
872 matchedPin = PinPtr(pPin, [] (IPin *pin) {
873 pin->Release();
874 });
875 pPin->AddRef();
876
877 break;
878 }
879
880 pEnum->Release();
881
882 return matchedPin;
883 }
884
connectFilters(IGraphBuilder * pGraph,IBaseFilter * pSrc,IBaseFilter * pDest) const885 bool CaptureDShowPrivate::connectFilters(IGraphBuilder *pGraph,
886 IBaseFilter *pSrc,
887 IBaseFilter *pDest) const
888 {
889 // Find source pin.
890 PinPtr srcPin = this->findUnconnectedPin(pSrc, PINDIR_OUTPUT);
891
892 if (!srcPin)
893 return false;
894
895 // Find dest pin.
896 PinPtr dstPin = this->findUnconnectedPin(pDest, PINDIR_INPUT);
897
898 if (!dstPin)
899 return false;
900
901 if (FAILED(pGraph->Connect(srcPin.data(), dstPin.data())))
902 return false;
903
904 return true;
905 }
906
enumPins(IBaseFilter * filter,PIN_DIRECTION direction) const907 PinList CaptureDShowPrivate::enumPins(IBaseFilter *filter,
908 PIN_DIRECTION direction) const
909 {
910 if (!filter)
911 return PinList();
912
913 PinList pinList;
914 IEnumPins *enumPins = nullptr;
915
916 if (SUCCEEDED(filter->EnumPins(&enumPins))) {
917 enumPins->Reset();
918 IPin *pin = nullptr;
919
920 while (enumPins->Next(1, &pin, nullptr) == S_OK) {
921 PIN_DIRECTION pinDir;
922
923 if (SUCCEEDED(pin->QueryDirection(&pinDir))
924 && pinDir == direction) {
925 pinList << PinPtr(pin, [] (IPin *pin) {
926 pin->Release();
927 });
928
929 continue;
930 }
931
932 pin->Release();
933 }
934
935 enumPins->Release();
936 }
937
938 return pinList;
939 }
940
freeMediaType(AM_MEDIA_TYPE & mediaType)941 void CaptureDShowPrivate::freeMediaType(AM_MEDIA_TYPE &mediaType)
942 {
943 if (mediaType.cbFormat) {
944 CoTaskMemFree(PVOID(mediaType.pbFormat));
945 mediaType.cbFormat = 0;
946 mediaType.pbFormat = nullptr;
947 }
948
949 if (mediaType.pUnk) {
950 // pUnk should not be used.
951 mediaType.pUnk->Release();
952 mediaType.pUnk = nullptr;
953 }
954 }
955
deleteMediaType(AM_MEDIA_TYPE * mediaType)956 void CaptureDShowPrivate::deleteMediaType(AM_MEDIA_TYPE *mediaType)
957 {
958 if (!mediaType)
959 return;
960
961 CaptureDShowPrivate::freeMediaType(*mediaType);
962 CoTaskMemFree(mediaType);
963 }
964
imageControls(IBaseFilter * filter) const965 QVariantList CaptureDShowPrivate::imageControls(IBaseFilter *filter) const
966 {
967 if (!filter)
968 return QVariantList();
969
970 qint32 min = 0;
971 qint32 max = 0;
972 qint32 step = 0;
973 qint32 defaultValue = 0;
974 qint32 value = 0;
975 qint32 flags = 0;
976
977 QVariantList controls;
978 IAMVideoProcAmp *pProcAmp = nullptr;
979
980 if (SUCCEEDED(filter->QueryInterface(IID_IAMVideoProcAmp,
981 reinterpret_cast<void **>(&pProcAmp)))) {
982 for (auto it = vpapToStr->begin(); it != vpapToStr->end(); it++) {
983 if (SUCCEEDED(pProcAmp->GetRange(it.key(),
984 reinterpret_cast<LONG *>(&min),
985 reinterpret_cast<LONG *>(&max),
986 reinterpret_cast<LONG *>(&step),
987 reinterpret_cast<LONG *>(&defaultValue),
988 reinterpret_cast<LONG *>(&flags)))) {
989 bool autoSupport = flags & VideoProcAmp_Flags_Auto;
990 bool manualSupport = flags & VideoProcAmp_Flags_Manual;
991
992 if (SUCCEEDED(pProcAmp->Get(it.key(),
993 reinterpret_cast<LONG *>(&value),
994 reinterpret_cast<LONG *>(&flags)))) {
995 if (autoSupport) {
996 QVariantList control {
997 it.value() + " (Auto)",
998 QString("boolean"),
999 0,
1000 1,
1001 1,
1002 1,
1003 flags & VideoProcAmp_Flags_Auto,
1004 QStringList()
1005 };
1006
1007 controls << QVariant(control);
1008 }
1009
1010 if (manualSupport) {
1011 QString type;
1012
1013 if (min == 0 && max == 1)
1014 type = "boolean";
1015 else
1016 type = "integer";
1017
1018 QVariantList control {
1019 it.value(),
1020 type,
1021 min,
1022 max,
1023 step,
1024 defaultValue,
1025 value,
1026 QStringList()
1027 };
1028
1029 controls << QVariant(control);
1030 }
1031 }
1032 }
1033 }
1034
1035 pProcAmp->Release();
1036 }
1037
1038 return controls;
1039 }
1040
setImageControls(IBaseFilter * filter,const QVariantMap & imageControls) const1041 bool CaptureDShowPrivate::setImageControls(IBaseFilter *filter,
1042 const QVariantMap &imageControls) const
1043 {
1044 if (!filter)
1045 return false;
1046
1047 IAMVideoProcAmp *pProcAmp = nullptr;
1048
1049 if (SUCCEEDED(filter->QueryInterface(IID_IAMVideoProcAmp,
1050 reinterpret_cast<void **>(&pProcAmp)))) {
1051 for (auto it = vpapToStr->begin(); it != vpapToStr->end(); it++) {
1052 auto key = it.value();
1053
1054 if (imageControls.contains(key)) {
1055 LONG value = 0;
1056 LONG flags = 0;
1057 pProcAmp->Get(it.key(),
1058 reinterpret_cast<LONG *>(&value),
1059 reinterpret_cast<LONG *>(&flags));
1060 value = imageControls[key].toInt();
1061 pProcAmp->Set(it.key(), value, flags);
1062 }
1063
1064 if (imageControls.contains(key + " (Auto)")) {
1065 LONG value = 0;
1066 LONG flags = 0;
1067 pProcAmp->Get(it.key(),
1068 reinterpret_cast<LONG *>(&value),
1069 reinterpret_cast<LONG *>(&flags));
1070
1071 if (imageControls[key + " (Auto)"].toBool())
1072 flags |= VideoProcAmp_Flags_Auto;
1073 else
1074 flags &= ~VideoProcAmp_Flags_Auto;
1075
1076 pProcAmp->Set(it.key(), value, flags);
1077 }
1078 }
1079
1080 pProcAmp->Release();
1081 }
1082
1083 return true;
1084 }
1085
cameraControls(IBaseFilter * filter) const1086 QVariantList CaptureDShowPrivate::cameraControls(IBaseFilter *filter) const
1087 {
1088 if (!filter)
1089 return QVariantList();
1090
1091 qint32 min = 0;
1092 qint32 max = 0;
1093 qint32 step = 0;
1094 qint32 defaultValue = 0;
1095 qint32 value = 0;
1096 qint32 flags = 0;
1097
1098 QVariantList controls;
1099 IAMCameraControl *pCameraControl = nullptr;
1100
1101 if (SUCCEEDED(filter->QueryInterface(IID_IAMCameraControl,
1102 reinterpret_cast<void **>(&pCameraControl)))) {
1103 for (auto it = ccToStr->begin(); it != ccToStr->end(); it++) {
1104 if (SUCCEEDED(pCameraControl->GetRange(it.key(),
1105 reinterpret_cast<LONG *>(&min),
1106 reinterpret_cast<LONG *>(&max),
1107 reinterpret_cast<LONG *>(&step),
1108 reinterpret_cast<LONG *>(&defaultValue),
1109 reinterpret_cast<LONG *>(&flags)))) {
1110 bool autoSupport = flags & CameraControl_Flags_Auto;
1111 bool manualSupport = flags & CameraControl_Flags_Manual;
1112
1113 if (SUCCEEDED(pCameraControl->Get(it.key(),
1114 reinterpret_cast<LONG *>(&value),
1115 reinterpret_cast<LONG *>(&flags)))) {
1116 if (autoSupport) {
1117 QVariantList control {
1118 it.value() + " (Auto)",
1119 QString("boolean"),
1120 0,
1121 1,
1122 1,
1123 1,
1124 flags & CameraControl_Flags_Auto,
1125 QStringList()
1126 };
1127
1128 controls << QVariant(control);
1129 }
1130
1131 if (manualSupport) {
1132 QString type;
1133
1134 if (min == 0 && max == 1)
1135 type = "boolean";
1136 else
1137 type = "integer";
1138
1139 QVariantList control {
1140 it.value(),
1141 type,
1142 min,
1143 max,
1144 step,
1145 defaultValue,
1146 value,
1147 QStringList()
1148 };
1149
1150 controls << QVariant(control);
1151 }
1152 }
1153 }
1154 }
1155
1156 pCameraControl->Release();
1157 }
1158
1159 return controls;
1160 }
1161
setCameraControls(IBaseFilter * filter,const QVariantMap & cameraControls) const1162 bool CaptureDShowPrivate::setCameraControls(IBaseFilter *filter,
1163 const QVariantMap &cameraControls) const
1164 {
1165 if (!filter)
1166 return false;
1167
1168 IAMCameraControl *pCameraControl = nullptr;
1169
1170 if (SUCCEEDED(filter->QueryInterface(IID_IAMCameraControl,
1171 reinterpret_cast<void **>(&pCameraControl)))) {
1172 for (auto it = ccToStr->begin(); it != ccToStr->end(); it++) {
1173 auto key = it.value();
1174
1175 if (cameraControls.contains(key)) {
1176 LONG value = 0;
1177 LONG flags = 0;
1178 pCameraControl->Get(it.key(),
1179 reinterpret_cast<LONG *>(&value),
1180 reinterpret_cast<LONG *>(&flags));
1181 value = cameraControls[key].toInt();
1182 pCameraControl->Set(it.key(), value, flags);
1183 }
1184
1185 if (cameraControls.contains(key + " (Auto)")) {
1186 LONG value = 0;
1187 LONG flags = 0;
1188 pCameraControl->Get(it.key(),
1189 reinterpret_cast<LONG *>(&value),
1190 reinterpret_cast<LONG *>(&flags));
1191
1192 if (cameraControls[key + " (Auto)"].toBool())
1193 flags |= CameraControl_Flags_Auto;
1194 else
1195 flags &= ~CameraControl_Flags_Auto;
1196
1197 pCameraControl->Set(it.key(), value, flags);
1198 }
1199 }
1200
1201 pCameraControl->Release();
1202 }
1203
1204 return true;
1205 }
1206
controlStatus(const QVariantList & controls) const1207 QVariantMap CaptureDShowPrivate::controlStatus(const QVariantList &controls) const
1208 {
1209 QVariantMap controlStatus;
1210
1211 for (auto &control: controls) {
1212 auto params = control.toList();
1213 auto controlName = params[0].toString();
1214 controlStatus[controlName] = params[6];
1215 }
1216
1217 return controlStatus;
1218 }
1219
mapDiff(const QVariantMap & map1,const QVariantMap & map2) const1220 QVariantMap CaptureDShowPrivate::mapDiff(const QVariantMap &map1,
1221 const QVariantMap &map2) const
1222 {
1223 QVariantMap map;
1224
1225 for (auto &control: map2.keys())
1226 if (!map1.contains(control)
1227 || map1[control] != map2[control]) {
1228 map[control] = map2[control];
1229 }
1230
1231 return map;
1232 }
1233
frameReceived(qreal time,const QByteArray & buffer)1234 void CaptureDShowPrivate::frameReceived(qreal time, const QByteArray &buffer)
1235 {
1236 Q_UNUSED(time)
1237
1238 this->m_mutex.lock();
1239 this->m_curBuffer = buffer;
1240 this->m_waitCondition.wakeAll();
1241 this->m_mutex.unlock();
1242 }
1243
updateDevices()1244 void CaptureDShowPrivate::updateDevices()
1245 {
1246 decltype(this->m_devices) devices;
1247 decltype(this->m_descriptions) descriptions;
1248 decltype(this->m_devicesCaps) devicesCaps;
1249
1250 MonikersMap monikers;
1251 IEnumMoniker *pEnum = nullptr;
1252 HRESULT hr = this->enumerateCameras(&pEnum);
1253
1254 if (SUCCEEDED(hr)) {
1255 pEnum->Reset();
1256 IMoniker *moniker = nullptr;
1257
1258 for (int i = 0; pEnum->Next(1, &moniker, nullptr) == S_OK; i++) {
1259 IPropertyBag *propertyBag = nullptr;
1260 HRESULT hr = moniker->BindToStorage(nullptr,
1261 nullptr,
1262 IID_IPropertyBag,
1263 reinterpret_cast<void **>(&propertyBag));
1264
1265 if (FAILED(hr)) {
1266 moniker->Release();
1267
1268 continue;
1269 }
1270
1271 auto devicePath = this->devicePath(propertyBag);
1272
1273 if (devicePath.isEmpty())
1274 devicePath = this->monikerDisplayName(moniker);
1275
1276 auto description = this->deviceDescription(propertyBag);
1277 propertyBag->Release();
1278
1279 IBaseFilter *baseFilter = nullptr;
1280 hr = moniker->BindToObject(nullptr,
1281 nullptr,
1282 IID_IBaseFilter,
1283 reinterpret_cast<void **>(&baseFilter));
1284
1285 if (FAILED(hr)) {
1286 moniker->Release();
1287
1288 continue;
1289 }
1290
1291 auto caps = this->caps(baseFilter);
1292 baseFilter->Release();
1293
1294 if (!caps.isEmpty()) {
1295 devices << devicePath;
1296 descriptions[devicePath] = description;
1297 devicesCaps[devicePath] = caps;
1298 }
1299
1300 moniker->Release();
1301 }
1302
1303 pEnum->Release();
1304 }
1305
1306 if (devicesCaps.isEmpty()) {
1307 devices.clear();
1308 descriptions.clear();
1309 }
1310
1311 this->m_descriptions = descriptions;
1312 this->m_devicesCaps = devicesCaps;
1313
1314 if (this->m_devices != devices) {
1315 this->m_devices = devices;
1316 emit self->webcamsChanged(this->m_devices);
1317 }
1318 }
1319
init()1320 bool CaptureDShow::init()
1321 {
1322 this->d->m_localImageControls.clear();
1323 this->d->m_localCameraControls.clear();
1324
1325 // Create the pipeline.
1326 if (FAILED(CoCreateInstance(CLSID_FilterGraph,
1327 nullptr,
1328 CLSCTX_INPROC_SERVER,
1329 IID_IGraphBuilder,
1330 reinterpret_cast<void **>(&this->d->m_graph))))
1331 return false;
1332
1333 // Create the webcam filter.
1334 this->d->m_webcamFilter = this->d->findFilter(this->d->m_device);
1335
1336 if (!this->d->m_webcamFilter) {
1337 this->d->m_graph->Release();
1338 this->d->m_graph = nullptr;
1339
1340 return false;
1341 }
1342
1343 if (FAILED(this->d->m_graph->AddFilter(this->d->m_webcamFilter.data(),
1344 SOURCE_FILTER_NAME))) {
1345 this->d->m_graph->Release();
1346 this->d->m_graph = nullptr;
1347 this->d->m_webcamFilter.clear();
1348
1349 return false;
1350 }
1351
1352 // Create the Sample Grabber filter.
1353 IBaseFilter *grabberFilter = nullptr;
1354
1355 if (FAILED(CoCreateInstance(CLSID_SampleGrabber,
1356 nullptr,
1357 CLSCTX_INPROC_SERVER,
1358 IID_IBaseFilter,
1359 reinterpret_cast<void **>(&grabberFilter)))) {
1360 this->d->m_graph->Release();
1361 this->d->m_graph = nullptr;
1362 this->d->m_webcamFilter.clear();
1363
1364 return false;
1365 }
1366
1367 if (FAILED(this->d->m_graph->AddFilter(grabberFilter, L"Grabber"))) {
1368 this->d->m_graph->Release();
1369 this->d->m_graph = nullptr;
1370 this->d->m_webcamFilter.clear();
1371
1372 return false;
1373 }
1374
1375 ISampleGrabber *grabberPtr = nullptr;
1376
1377 if (FAILED(grabberFilter->QueryInterface(IID_ISampleGrabber,
1378 reinterpret_cast<void **>(&grabberPtr)))) {
1379 this->d->m_graph->Release();
1380 this->d->m_graph = nullptr;
1381 this->d->m_webcamFilter.clear();
1382
1383 return false;
1384 }
1385
1386 if (FAILED(grabberPtr->SetOneShot(FALSE))) {
1387 this->d->m_graph->Release();
1388 this->d->m_graph = nullptr;
1389 this->d->m_webcamFilter.clear();
1390
1391 return false;
1392 }
1393
1394 HRESULT hr = grabberPtr->SetBufferSamples(TRUE);
1395
1396 if (FAILED(hr)) {
1397 this->d->m_graph->Release();
1398 this->d->m_graph = nullptr;
1399 this->d->m_webcamFilter.clear();
1400
1401 return false;
1402 }
1403
1404 if (this->d->m_ioMethod != IoMethodDirectRead) {
1405 int type = this->d->m_ioMethod == IoMethodGrabSample? 0: 1;
1406 hr = grabberPtr->SetCallback(&this->d->m_frameGrabber, type);
1407 }
1408
1409 this->d->m_grabber =
1410 SampleGrabberPtr(grabberPtr, [] (ISampleGrabber *sampleGrabber) {
1411 sampleGrabber->Release();
1412 });
1413
1414 if (!this->d->connectFilters(this->d->m_graph,
1415 this->d->m_webcamFilter.data(),
1416 grabberFilter)) {
1417 this->d->m_graph->Release();
1418 this->d->m_graph = nullptr;
1419 this->d->m_webcamFilter.clear();
1420
1421 return false;
1422 }
1423
1424 // Create null filter.
1425 IBaseFilter *nullFilter = nullptr;
1426
1427 if (FAILED(CoCreateInstance(CLSID_NullRenderer,
1428 nullptr,
1429 CLSCTX_INPROC_SERVER,
1430 IID_IBaseFilter,
1431 reinterpret_cast<void **>(&nullFilter)))) {
1432 this->d->m_graph->Release();
1433 this->d->m_graph = nullptr;
1434 this->d->m_webcamFilter.clear();
1435
1436 return false;
1437 }
1438
1439 if (FAILED(this->d->m_graph->AddFilter(nullFilter, L"NullFilter"))) {
1440 this->d->m_graph->Release();
1441 this->d->m_graph = nullptr;
1442 this->d->m_webcamFilter.clear();
1443
1444 return false;
1445 }
1446
1447 if (!this->d->connectFilters(this->d->m_graph,
1448 grabberFilter,
1449 nullFilter)) {
1450 this->d->m_graph->Release();
1451 this->d->m_graph = nullptr;
1452 this->d->m_webcamFilter.clear();
1453
1454 return false;
1455 }
1456
1457 // Set capture format
1458 auto streams = this->streams();
1459
1460 if (streams.isEmpty()) {
1461 this->d->m_graph->Release();
1462 this->d->m_graph = nullptr;
1463 this->d->m_webcamFilter.clear();
1464
1465 return false;
1466 }
1467
1468 auto mediaTypes = this->d->listMediaTypes(this->d->m_webcamFilter.data());
1469
1470 if (mediaTypes.isEmpty()) {
1471 this->d->m_graph->Release();
1472 this->d->m_graph = nullptr;
1473 this->d->m_webcamFilter.clear();
1474
1475 return false;
1476 }
1477
1478 MediaTypePtr mediaType = streams[0] < mediaTypes.size()?
1479 mediaTypes[streams[0]]:
1480 mediaTypes.first();
1481
1482 if (FAILED(grabberPtr->SetMediaType(mediaType.data()))) {
1483 this->d->m_graph->Release();
1484 this->d->m_graph = nullptr;
1485 this->d->m_webcamFilter.clear();
1486
1487 return false;
1488 }
1489
1490 auto pins = this->d->enumPins(this->d->m_webcamFilter.data(),
1491 PINDIR_OUTPUT);
1492
1493 for (const PinPtr &pin: pins) {
1494 IAMStreamConfig *pStreamConfig = nullptr;
1495 auto hr =
1496 pin->QueryInterface(IID_IAMStreamConfig,
1497 reinterpret_cast<void **>(&pStreamConfig));
1498
1499 if (SUCCEEDED(hr))
1500 pStreamConfig->SetFormat(mediaType.data());
1501
1502 if (pStreamConfig)
1503 pStreamConfig->Release();
1504 }
1505
1506 // Run the pipeline
1507 IMediaControl *control = nullptr;
1508
1509 if (FAILED(this->d->m_graph->QueryInterface(IID_IMediaControl,
1510 reinterpret_cast<void **>(&control)))) {
1511 this->d->m_graph->Release();
1512 this->d->m_graph = nullptr;
1513 this->d->m_webcamFilter.clear();
1514
1515 return false;
1516 }
1517
1518 this->d->m_id = Ak::id();
1519 AkCaps caps = this->d->capsFromMediaType(mediaType);
1520 this->d->m_timeBase = AkFrac(caps.property("fps").toString()).invert();
1521
1522 if (FAILED(control->Run())) {
1523 control->Release();
1524 this->d->m_graph->Release();
1525 this->d->m_graph = nullptr;
1526 this->d->m_webcamFilter.clear();
1527
1528 return false;
1529 }
1530
1531 control->Release();
1532
1533 return true;
1534 }
1535
uninit()1536 void CaptureDShow::uninit()
1537 {
1538 IMediaControl *control = nullptr;
1539
1540 if (SUCCEEDED(this->d->m_graph->QueryInterface(IID_IMediaControl,
1541 reinterpret_cast<void **>(&control)))) {
1542 control->Stop();
1543 control->Release();
1544 }
1545
1546 this->d->m_grabber.clear();
1547 this->d->m_graph->Release();
1548 this->d->m_graph = nullptr;
1549 this->d->m_webcamFilter.clear();
1550 }
1551
setDevice(const QString & device)1552 void CaptureDShow::setDevice(const QString &device)
1553 {
1554 if (this->d->m_device == device)
1555 return;
1556
1557 this->d->m_device = device;
1558
1559 if (device.isEmpty()) {
1560 this->d->m_controlsMutex.lock();
1561 this->d->m_globalImageControls.clear();
1562 this->d->m_globalCameraControls.clear();
1563 this->d->m_controlsMutex.unlock();
1564 } else {
1565 this->d->m_controlsMutex.lock();
1566 auto camera = this->d->findFilterP(device);
1567
1568 if (camera) {
1569 this->d->m_globalImageControls = this->d->imageControls(camera);
1570 this->d->m_globalCameraControls = this->d->cameraControls(camera);
1571 camera->Release();
1572 }
1573
1574 this->d->m_controlsMutex.unlock();
1575 }
1576
1577 this->d->m_controlsMutex.lock();
1578 auto imageStatus = this->d->controlStatus(this->d->m_globalImageControls);
1579 auto cameraStatus = this->d->controlStatus(this->d->m_globalCameraControls);
1580 this->d->m_controlsMutex.unlock();
1581
1582 emit this->deviceChanged(device);
1583 emit this->imageControlsChanged(imageStatus);
1584 emit this->cameraControlsChanged(cameraStatus);
1585 }
1586
setStreams(const QList<int> & streams)1587 void CaptureDShow::setStreams(const QList<int> &streams)
1588 {
1589 if (streams.isEmpty())
1590 return;
1591
1592 int stream = streams[0];
1593
1594 if (stream < 0)
1595 return;
1596
1597 auto supportedCaps = this->caps(this->d->m_device);
1598
1599 if (stream >= supportedCaps.length())
1600 return;
1601
1602 QList<int> inputStreams {stream};
1603
1604 if (this->streams() == inputStreams)
1605 return;
1606
1607 this->d->m_streams = inputStreams;
1608 emit this->streamsChanged(inputStreams);
1609 }
1610
setIoMethod(const QString & ioMethod)1611 void CaptureDShow::setIoMethod(const QString &ioMethod)
1612 {
1613 IoMethod ioMethodEnum = ioMethodToStr->key(ioMethod, IoMethodGrabSample);
1614
1615 if (this->d->m_ioMethod == ioMethodEnum)
1616 return;
1617
1618 this->d->m_ioMethod = ioMethodEnum;
1619 emit this->ioMethodChanged(ioMethod);
1620 }
1621
setNBuffers(int nBuffers)1622 void CaptureDShow::setNBuffers(int nBuffers)
1623 {
1624 Q_UNUSED(nBuffers)
1625 }
1626
resetDevice()1627 void CaptureDShow::resetDevice()
1628 {
1629 this->setDevice("");
1630 }
1631
resetStreams()1632 void CaptureDShow::resetStreams()
1633 {
1634 QVariantList supportedCaps = this->caps(this->d->m_device);
1635 QList<int> streams;
1636
1637 if (!supportedCaps.isEmpty())
1638 streams << 0;
1639
1640 this->setStreams(streams);
1641 }
1642
resetIoMethod()1643 void CaptureDShow::resetIoMethod()
1644 {
1645 this->setIoMethod("any");
1646 }
1647
resetNBuffers()1648 void CaptureDShow::resetNBuffers()
1649 {
1650 }
1651
reset()1652 void CaptureDShow::reset()
1653 {
1654 this->resetStreams();
1655 this->resetImageControls();
1656 this->resetCameraControls();
1657 }
1658
1659 #include "moc_capturedshow.cpp"
1660