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