Merge remote-tracking branch 'upstream/3.4' into merge-3.4
[platform/upstream/opencv.git] / modules / videoio / src / cap_msmf.cpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html
4
5 #include "precomp.hpp"
6 /*
7    Media Foundation-based Video Capturing module is based on
8    videoInput library by Evgeny Pereguda:
9    http://www.codeproject.com/Articles/559437/Capturing-of-video-from-web-camera-on-Windows-7-an
10    Originally licensed under The Code Project Open License (CPOL) 1.02:
11    http://www.codeproject.com/info/cpol10.aspx
12 */
13 //require Windows 8 for some of the formats defined otherwise could baseline on lower version
14 #if WINVER < _WIN32_WINNT_WIN8
15 #undef WINVER
16 #define WINVER _WIN32_WINNT_WIN8
17 #endif
18
19 #include <windows.h>
20 #include <guiddef.h>
21 #include <mfidl.h>
22 #include <mfapi.h>
23 #include <mfplay.h>
24 #include <mfobjects.h>
25 #include <tchar.h>
26 #include <strsafe.h>
27 #include <mfreadwrite.h>
28 #ifdef HAVE_MSMF_DXVA
29 #include <d3d11.h>
30 #include <d3d11_4.h>
31 #endif
32 #include <new>
33 #include <map>
34 #include <vector>
35 #include <string>
36 #include <algorithm>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <string.h>
40
41 #ifdef _MSC_VER
42 #pragma warning(disable:4503)
43 #pragma comment(lib, "mfplat")
44 #pragma comment(lib, "mf")
45 #pragma comment(lib, "mfuuid")
46 #pragma comment(lib, "Strmiids")
47 #pragma comment(lib, "Mfreadwrite")
48 #ifdef HAVE_MSMF_DXVA
49 #pragma comment(lib, "d3d11")
50 // MFCreateDXGIDeviceManager() is available since Win8 only.
51 // To avoid OpenCV loading failure on Win7 use dynamic detection of this symbol.
52 // Details: https://github.com/opencv/opencv/issues/11858
53 typedef HRESULT (WINAPI *FN_MFCreateDXGIDeviceManager)(UINT *resetToken, IMFDXGIDeviceManager **ppDeviceManager);
54 static bool pMFCreateDXGIDeviceManager_initialized = false;
55 static FN_MFCreateDXGIDeviceManager pMFCreateDXGIDeviceManager = NULL;
56 static void init_MFCreateDXGIDeviceManager()
57 {
58     HMODULE h = LoadLibraryExA("mfplat.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
59     if (h)
60     {
61         pMFCreateDXGIDeviceManager = (FN_MFCreateDXGIDeviceManager)GetProcAddress(h, "MFCreateDXGIDeviceManager");
62     }
63     pMFCreateDXGIDeviceManager_initialized = true;
64 }
65 #endif
66 #pragma comment(lib, "Shlwapi.lib")
67 #endif
68
69 #include <mferror.h>
70
71 #include <comdef.h>
72
73 #include <shlwapi.h>  // QISearch
74
75 struct IMFMediaType;
76 struct IMFActivate;
77 struct IMFMediaSource;
78 struct IMFAttributes;
79
80 #define CV_CAP_MODE_BGR CV_FOURCC_MACRO('B','G','R','3')
81 #define CV_CAP_MODE_RGB CV_FOURCC_MACRO('R','G','B','3')
82 #define CV_CAP_MODE_GRAY CV_FOURCC_MACRO('G','R','E','Y')
83 #define CV_CAP_MODE_YUYV CV_FOURCC_MACRO('Y', 'U', 'Y', 'V')
84
85 namespace
86 {
87
88 template <class T>
89 class ComPtr
90 {
91 public:
92     ComPtr()
93     {
94     }
95     ComPtr(T* lp)
96     {
97         p = lp;
98     }
99     ComPtr(_In_ const ComPtr<T>& lp)
100     {
101         p = lp.p;
102     }
103     virtual ~ComPtr()
104     {
105     }
106
107     T** operator&()
108     {
109         CV_Assert(p == NULL);
110         return p.operator&();
111     }
112     T* operator->() const
113     {
114         CV_Assert(p != NULL);
115         return p.operator->();
116     }
117     operator bool()
118     {
119         return p.operator!=(NULL);
120     }
121
122     T* Get() const
123     {
124         return p;
125     }
126
127     void Release()
128     {
129         if (p)
130             p.Release();
131     }
132
133     // query for U interface
134     template<typename U>
135     HRESULT As(_Out_ ComPtr<U>& lp) const
136     {
137         lp.Release();
138         return p->QueryInterface(__uuidof(U), reinterpret_cast<void**>((T**)&lp));
139     }
140 private:
141     _COM_SMARTPTR_TYPEDEF(T, __uuidof(T));
142     TPtr p;
143 };
144
145 #define _ComPtr ComPtr
146
147 template <typename T> inline T absDiff(T a, T b) { return a >= b ? a - b : b - a; }
148
149 //==================================================================================================
150
151 // Structure for collecting info about types of video which are supported by current video device
152 struct MediaType
153 {
154     UINT32 width;
155     UINT32 height;
156     INT32 stride; // stride is negative if image is bottom-up
157     UINT32 isFixedSize;
158     UINT32 frameRateNum;
159     UINT32 frameRateDenom;
160     UINT32 aspectRatioNum;
161     UINT32 aspectRatioDenom;
162     UINT32 sampleSize;
163     UINT32 interlaceMode;
164     GUID majorType; // video or audio
165     GUID subType; // fourCC
166     MediaType(IMFMediaType *pType = 0) :
167         width(0), height(0),
168         stride(0),
169         isFixedSize(true),
170         frameRateNum(1), frameRateDenom(1),
171         aspectRatioNum(1), aspectRatioDenom(1),
172         sampleSize(0),
173         interlaceMode(0),
174         majorType(MFMediaType_Video),
175         subType({ 0 })
176     {
177         if (pType)
178         {
179             MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
180             pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&stride); // value is stored as UINT32 but should be casted to INT3)
181             pType->GetUINT32(MF_MT_FIXED_SIZE_SAMPLES, &isFixedSize);
182             MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, &frameRateNum, &frameRateDenom);
183             MFGetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, &aspectRatioNum, &aspectRatioDenom);
184             pType->GetUINT32(MF_MT_SAMPLE_SIZE, &sampleSize);
185             pType->GetUINT32(MF_MT_INTERLACE_MODE, &interlaceMode);
186             pType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
187             pType->GetGUID(MF_MT_SUBTYPE, &subType);
188         }
189     }
190     static MediaType createDefault()
191     {
192         MediaType res;
193         res.width = 640;
194         res.height = 480;
195         res.setFramerate(30.0);
196         return res;
197     }
198     inline bool isEmpty() const
199     {
200         return width == 0 && height == 0;
201     }
202     _ComPtr<IMFMediaType> createMediaType() const
203     {
204         _ComPtr<IMFMediaType> res;
205         MFCreateMediaType(&res);
206         if (width != 0 || height != 0)
207             MFSetAttributeSize(res.Get(), MF_MT_FRAME_SIZE, width, height);
208         if (stride != 0)
209             res->SetUINT32(MF_MT_DEFAULT_STRIDE, stride);
210         res->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, isFixedSize);
211         if (frameRateNum != 0 || frameRateDenom != 0)
212             MFSetAttributeRatio(res.Get(), MF_MT_FRAME_RATE, frameRateNum, frameRateDenom);
213         if (aspectRatioNum != 0 || aspectRatioDenom != 0)
214             MFSetAttributeRatio(res.Get(), MF_MT_PIXEL_ASPECT_RATIO, aspectRatioNum, aspectRatioDenom);
215         if (sampleSize > 0)
216             res->SetUINT32(MF_MT_SAMPLE_SIZE, sampleSize);
217         res->SetUINT32(MF_MT_INTERLACE_MODE, interlaceMode);
218         if (majorType != GUID())
219             res->SetGUID(MF_MT_MAJOR_TYPE, majorType);
220         if (subType != GUID())
221             res->SetGUID(MF_MT_SUBTYPE, subType);
222         return res;
223     }
224     void setFramerate(double fps)
225     {
226         frameRateNum = (UINT32)cvRound(fps * 1000.0);
227         frameRateDenom = 1000;
228     }
229     double getFramerate() const
230     {
231         return frameRateDenom != 0 ? ((double)frameRateNum) / ((double)frameRateDenom) : 0;
232     }
233     LONGLONG getFrameStep() const
234     {
235         const double fps = getFramerate();
236         return (LONGLONG)(fps > 0 ? 1e7 / fps : 0);
237     }
238     inline unsigned long resolutionDiff(const MediaType& other) const
239     {
240         const unsigned long wdiff = absDiff(width, other.width);
241         const unsigned long hdiff = absDiff(height, other.height);
242         return wdiff + hdiff;
243     }
244     // check if 'this' is better than 'other' comparing to reference
245     bool isBetterThan(const MediaType& other, const MediaType& ref) const
246     {
247         const unsigned long thisDiff = resolutionDiff(ref);
248         const unsigned long otherDiff = other.resolutionDiff(ref);
249         if (thisDiff < otherDiff)
250             return true;
251         if (thisDiff == otherDiff)
252         {
253             if (width > other.width)
254                 return true;
255             if (width == other.width && height > other.height)
256                 return true;
257             if (width == other.width && height == other.height)
258             {
259                 const double thisRateDiff = absDiff(getFramerate(), ref.getFramerate());
260                 const double otherRateDiff = absDiff(other.getFramerate(), ref.getFramerate());
261                 if (thisRateDiff < otherRateDiff)
262                     return true;
263             }
264         }
265         return false;
266     }
267 };
268
269 void printFormat(std::ostream& out, const GUID& fmt)
270 {
271 #define PRNT(FMT) else if (fmt == FMT) out << #FMT;
272     if (fmt == MFVideoFormat_Base) out << "Base";
273     PRNT(MFVideoFormat_RGB32)
274     PRNT(MFVideoFormat_ARGB32)
275     PRNT(MFVideoFormat_RGB24)
276     PRNT(MFVideoFormat_RGB555)
277     PRNT(MFVideoFormat_RGB565)
278     PRNT(MFVideoFormat_RGB8)
279     else
280     {
281         char fourcc[5] = { 0 };
282         memcpy(fourcc, &fmt.Data1, 4);
283         out << fourcc;
284     }
285 #undef PRNT
286 }
287
288 std::ostream& operator<<(std::ostream& out, const MediaType& mt)
289 {
290     out << "(" << mt.width << "x" << mt.height << " @ " << mt.getFramerate() << ") ";
291     printFormat(out, mt.subType);
292     return out;
293 }
294
295 //==================================================================================================
296
297 // Class for creating of Media Foundation context
298 class Media_Foundation
299 {
300 public:
301     ~Media_Foundation(void) { /*CV_Assert(SUCCEEDED(MFShutdown()));*/ CoUninitialize(); }
302     static Media_Foundation& getInstance()
303     {
304         static Media_Foundation instance;
305         return instance;
306     }
307 private:
308     Media_Foundation(void) { CoInitialize(0); CV_Assert(SUCCEEDED(MFStartup(MF_VERSION))); }
309 };
310
311 //==================================================================================================
312
313 class SourceReaderCB : public IMFSourceReaderCallback
314 {
315 public:
316     SourceReaderCB() :
317         m_nRefCount(0), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_reader(NULL), m_dwStreamIndex(0)
318     {
319     }
320
321     // IUnknown methods
322     STDMETHODIMP QueryInterface(REFIID iid, void** ppv) CV_OVERRIDE
323     {
324 #ifdef _MSC_VER
325 #pragma warning(push)
326 #pragma warning(disable:4838)
327 #endif
328         static const QITAB qit[] =
329         {
330             QITABENT(SourceReaderCB, IMFSourceReaderCallback),
331             { 0 },
332         };
333 #ifdef _MSC_VER
334 #pragma warning(pop)
335 #endif
336         return QISearch(this, qit, iid, ppv);
337     }
338     STDMETHODIMP_(ULONG) AddRef() CV_OVERRIDE
339     {
340         return InterlockedIncrement(&m_nRefCount);
341     }
342     STDMETHODIMP_(ULONG) Release() CV_OVERRIDE
343     {
344         ULONG uCount = InterlockedDecrement(&m_nRefCount);
345         if (uCount == 0)
346         {
347             delete this;
348         }
349         return uCount;
350     }
351
352     STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) CV_OVERRIDE
353     {
354         HRESULT hr = 0;
355         cv::AutoLock lock(m_mutex);
356
357         if (SUCCEEDED(hrStatus))
358         {
359             if (pSample)
360             {
361                 CV_LOG_DEBUG(NULL, "videoio(MSMF): got frame at " << llTimestamp);
362                 if (m_lastSample.Get())
363                 {
364                     CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed)");
365                 }
366                 m_lastSampleTimestamp = llTimestamp;
367                 m_lastSample = pSample;
368             }
369         }
370         else
371         {
372             CV_LOG_WARNING(NULL, "videoio(MSMF): OnReadSample() is called with error status: " << hrStatus);
373         }
374
375         if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
376         {
377             // Reached the end of the stream.
378             m_bEOS = true;
379         }
380         m_hrStatus = hrStatus;
381
382         if (FAILED(hr = m_reader->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
383         {
384             CV_LOG_WARNING(NULL, "videoio(MSMF): async ReadSample() call is failed with error status: " << hr);
385             m_bEOS = true;
386         }
387
388         if (pSample || m_bEOS)
389         {
390             SetEvent(m_hEvent);
391         }
392         return S_OK;
393     }
394
395     STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) CV_OVERRIDE
396     {
397         return S_OK;
398     }
399     STDMETHODIMP OnFlush(DWORD) CV_OVERRIDE
400     {
401         return S_OK;
402     }
403
404     HRESULT Wait(DWORD dwMilliseconds, _ComPtr<IMFSample>& videoSample, BOOL& pbEOS)
405     {
406         pbEOS = FALSE;
407
408         DWORD dwResult = WaitForSingleObject(m_hEvent, dwMilliseconds);
409         if (dwResult == WAIT_TIMEOUT)
410         {
411             return E_PENDING;
412         }
413         else if (dwResult != WAIT_OBJECT_0)
414         {
415             return HRESULT_FROM_WIN32(GetLastError());
416         }
417
418         pbEOS = m_bEOS;
419         if (!pbEOS)
420         {
421             cv::AutoLock lock(m_mutex);
422             videoSample = m_lastSample;
423             CV_Assert(videoSample);
424             m_lastSample.Release();
425             ResetEvent(m_hEvent);  // event is auto-reset, but we need this forced reset due time gap between wait() and mutex hold.
426         }
427
428         return m_hrStatus;
429     }
430 private:
431     // Destructor is private. Caller should call Release.
432     virtual ~SourceReaderCB()
433     {
434         CV_LOG_WARNING(NULL, "terminating async callback");
435     }
436
437 public:
438     long                m_nRefCount;        // Reference count.
439     cv::Mutex           m_mutex;
440     HANDLE              m_hEvent;
441     BOOL                m_bEOS;
442     HRESULT             m_hrStatus;
443
444     IMFSourceReader *m_reader;
445     DWORD m_dwStreamIndex;
446     LONGLONG m_lastSampleTimestamp;
447     _ComPtr<IMFSample>  m_lastSample;
448 };
449
450 //==================================================================================================
451
452 // Enumerate and store supported formats and finds format which is most similar to the one requested
453 class FormatStorage
454 {
455 public:
456     struct MediaID
457     {
458         DWORD stream;
459         DWORD media;
460         MediaID() : stream(0), media(0) {}
461         void nextStream()
462         {
463             stream++;
464             media = 0;
465         }
466         void nextMedia()
467         {
468             media++;
469         }
470         bool operator<(const MediaID& other) const
471         {
472             return (stream < other.stream) || (stream == other.stream && media < other.media);
473         }
474     };
475     void read(IMFSourceReader* source)
476     {
477         HRESULT hr = S_OK;
478         MediaID cur;
479         while (SUCCEEDED(hr))
480         {
481             _ComPtr<IMFMediaType> raw_type;
482             hr = source->GetNativeMediaType(cur.stream, cur.media, &raw_type);
483             if (hr == MF_E_NO_MORE_TYPES)
484             {
485                 hr = S_OK;
486                 cur.nextStream();
487             }
488             else if (SUCCEEDED(hr))
489             {
490                 formats[cur] = MediaType(raw_type.Get());
491                 cur.nextMedia();
492             }
493         }
494     }
495     std::pair<MediaID, MediaType> findBestVideoFormat(const MediaType& newType)
496     {
497         std::pair<MediaID, MediaType> best;
498         std::map<MediaID, MediaType>::const_iterator i = formats.begin();
499         for (; i != formats.end(); ++i)
500         {
501             if (i->second.majorType != MFMediaType_Video)
502                 continue;
503             if (newType.isEmpty()) // file input - choose first returned media type
504             {
505                 best = *i;
506                 break;
507             }
508             if (best.second.isEmpty() || i->second.isBetterThan(best.second, newType))
509             {
510                 best = *i;
511             }
512         }
513         return best;
514     }
515 private:
516     std::map<MediaID, MediaType> formats;
517 };
518
519 //==================================================================================================
520
521 // Enumerates devices and activates one of them
522 class DeviceList
523 {
524 public:
525     DeviceList() : devices(NULL), count(0) {}
526     ~DeviceList()
527     {
528         if (devices)
529         {
530             for (UINT32 i = 0; i < count; ++i)
531                 if (devices[i])
532                     devices[i]->Release();
533             CoTaskMemFree(devices);
534         }
535     }
536     UINT32 read(IID sourceType = MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)
537     {
538         _ComPtr<IMFAttributes> attr;
539         if (FAILED(MFCreateAttributes(&attr, 1)) ||
540             FAILED(attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, sourceType)))
541         {
542             CV_Error(CV_StsError, "Failed to create attributes");
543         }
544         if (FAILED(MFEnumDeviceSources(attr.Get(), &devices, &count)))
545         {
546             CV_LOG_DEBUG(NULL, "Failed to enumerate MSMF devices");
547             return 0;
548         }
549         return count;
550     }
551     _ComPtr<IMFMediaSource> activateSource(UINT32 index)
552     {
553         _ComPtr<IMFMediaSource> result;
554         if (count == 0 || index >= count || FAILED(devices[index]->ActivateObject(__uuidof(IMFMediaSource), (void**)&result)))
555         {
556             CV_LOG_DEBUG(NULL, "Failed to activate media source (device " << index << ")");
557         }
558         return result;
559     }
560 private:
561     IMFActivate** devices;
562     UINT32 count;
563 };
564
565 } // namespace::
566
567 //==================================================================================================
568
569 /******* Capturing video from camera or file via Microsoft Media Foundation **********/
570 class CvCapture_MSMF : public cv::IVideoCapture
571 {
572 public:
573     typedef enum {
574         MODE_SW = 0,
575         MODE_HW = 1
576     } MSMFCapture_Mode;
577     CvCapture_MSMF();
578     virtual ~CvCapture_MSMF();
579     virtual bool open(int);
580     virtual bool open(const cv::String&);
581     virtual void close();
582     virtual double getProperty(int) const CV_OVERRIDE;
583     virtual bool setProperty(int, double) CV_OVERRIDE;
584     virtual bool grabFrame() CV_OVERRIDE;
585     virtual bool retrieveFrame(int, cv::OutputArray) CV_OVERRIDE;
586     virtual bool isOpened() const CV_OVERRIDE { return isOpen; }
587     virtual int getCaptureDomain() CV_OVERRIDE { return CV_CAP_MSMF; }
588 protected:
589     bool configureOutput(MediaType newType, cv::uint32_t outFormat);
590     bool setTime(double time, bool rough);
591     bool configureHW(bool enable);
592
593     template <typename CtrlT>
594     bool readComplexPropery(long prop, long& val) const;
595     template <typename CtrlT>
596     bool writeComplexProperty(long prop, double val, long flags);
597     _ComPtr<IMFAttributes> getDefaultSourceConfig(UINT32 num = 10);
598     bool initStream(DWORD streamID, const MediaType& mt);
599
600     Media_Foundation& MF;
601     cv::String filename;
602     int camid;
603     MSMFCapture_Mode captureMode;
604 #ifdef HAVE_MSMF_DXVA
605     _ComPtr<ID3D11Device> D3DDev;
606     _ComPtr<IMFDXGIDeviceManager> D3DMgr;
607 #endif
608     _ComPtr<IMFSourceReader> videoFileSource;
609     DWORD dwStreamIndex;
610     MediaType nativeFormat;
611     MediaType captureFormat;
612     int outputFormat;
613     bool convertFormat;
614     MFTIME duration;
615     LONGLONG frameStep;
616     _ComPtr<IMFSample> videoSample;
617     LONGLONG sampleTime;
618     bool isOpen;
619     _ComPtr<IMFSourceReaderCallback> readCallback;  // non-NULL for "live" streams (camera capture)
620 };
621
622 CvCapture_MSMF::CvCapture_MSMF():
623     MF(Media_Foundation::getInstance()),
624     filename(""),
625     camid(-1),
626     captureMode(MODE_SW),
627 #ifdef HAVE_MSMF_DXVA
628     D3DDev(NULL),
629     D3DMgr(NULL),
630 #endif
631     videoFileSource(NULL),
632     videoSample(NULL),
633     outputFormat(CV_CAP_MODE_BGR),
634     convertFormat(true),
635     sampleTime(0),
636     isOpen(false)
637 {
638     configureHW(true);
639 }
640
641 CvCapture_MSMF::~CvCapture_MSMF()
642 {
643     close();
644     configureHW(false);
645 }
646
647 void CvCapture_MSMF::close()
648 {
649     if (isOpen)
650     {
651         isOpen = false;
652         videoSample.Release();
653         videoFileSource.Release();
654         camid = -1;
655         filename.clear();
656     }
657     readCallback.Release();
658 }
659
660 bool CvCapture_MSMF::initStream(DWORD streamID, const MediaType& mt)
661 {
662     CV_LOG_DEBUG(NULL, "Init stream " << streamID << " with MediaType " << mt);
663     _ComPtr<IMFMediaType> mediaTypeOut = mt.createMediaType();
664     if (FAILED(videoFileSource->SetStreamSelection((DWORD)MF_SOURCE_READER_ALL_STREAMS, false)))
665     {
666         CV_LOG_WARNING(NULL, "Failed to reset streams");
667         return false;
668     }
669     if (FAILED(videoFileSource->SetStreamSelection(streamID, true)))
670     {
671         CV_LOG_WARNING(NULL, "Failed to select stream " << streamID);
672         return false;
673     }
674     HRESULT hr = videoFileSource->SetCurrentMediaType(streamID, NULL, mediaTypeOut.Get());
675     if (hr == MF_E_TOPO_CODEC_NOT_FOUND)
676     {
677         CV_LOG_WARNING(NULL, "Failed to set mediaType (stream " << streamID << ", " << mt << "(codec not found)");
678         return false;
679     }
680     else if (hr == MF_E_INVALIDMEDIATYPE)
681     {
682         CV_LOG_WARNING(NULL, "Failed to set mediaType (stream " << streamID << ", " << mt << "(unsupported media type)");
683         return false;
684     }
685     else if (FAILED(hr))
686     {
687         CV_LOG_WARNING(NULL, "Failed to set mediaType (stream " << streamID << ", " << mt << "(HRESULT " << hr << ")");
688         return false;
689     }
690     captureFormat = mt;
691     return true;
692 }
693
694 _ComPtr<IMFAttributes> CvCapture_MSMF::getDefaultSourceConfig(UINT32 num)
695 {
696     CV_Assert(num > 0);
697     _ComPtr<IMFAttributes> res;
698     if (FAILED(MFCreateAttributes(&res, num)) ||
699         FAILED(res->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)) ||
700         FAILED(res->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, false)) ||
701         FAILED(res->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, false)) ||
702         FAILED(res->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, true))
703         )
704     {
705         CV_Error(CV_StsError, "Failed to create attributes");
706     }
707 #ifdef HAVE_MSMF_DXVA
708     if (D3DMgr)
709     {
710         if (FAILED(res->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, D3DMgr.Get())))
711         {
712             CV_Error(CV_StsError, "Failed to create attributes");
713         }
714     }
715 #endif
716     return res;
717 }
718
719 bool CvCapture_MSMF::configureHW(bool enable)
720 {
721 #ifdef HAVE_MSMF_DXVA
722     if ((enable && D3DMgr && D3DDev) || (!enable && !D3DMgr && !D3DDev))
723         return true;
724     if (!pMFCreateDXGIDeviceManager_initialized)
725         init_MFCreateDXGIDeviceManager();
726     if (enable && !pMFCreateDXGIDeviceManager)
727         return false;
728
729     bool reopen = isOpen;
730     int prevcam = camid;
731     cv::String prevfile = filename;
732     close();
733     if (enable)
734     {
735         D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
736             D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0,
737             D3D_FEATURE_LEVEL_9_3,  D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 };
738         if (SUCCEEDED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
739             levels, sizeof(levels) / sizeof(*levels), D3D11_SDK_VERSION, &D3DDev, NULL, NULL)))
740         {
741             // NOTE: Getting ready for multi-threaded operation
742             _ComPtr<ID3D11Multithread> D3DDevMT;
743             UINT mgrRToken;
744             if (SUCCEEDED(D3DDev->QueryInterface(IID_PPV_ARGS(&D3DDevMT))))
745             {
746                 D3DDevMT->SetMultithreadProtected(TRUE);
747                 D3DDevMT.Release();
748                 if (SUCCEEDED(pMFCreateDXGIDeviceManager(&mgrRToken, &D3DMgr)))
749                 {
750                     if (SUCCEEDED(D3DMgr->ResetDevice(D3DDev.Get(), mgrRToken)))
751                     {
752                         captureMode = MODE_HW;
753                         return reopen ? (prevcam >= 0 ? open(prevcam) : open(prevfile.c_str())) : true;
754                     }
755                     D3DMgr.Release();
756                 }
757             }
758             D3DDev.Release();
759         }
760         return false;
761     }
762     else
763     {
764         if (D3DMgr)
765             D3DMgr.Release();
766         if (D3DDev)
767             D3DDev.Release();
768         captureMode = MODE_SW;
769         return reopen ? (prevcam >= 0 ? open(prevcam) : open(prevfile.c_str())) : true;
770     }
771 #else
772     return !enable;
773 #endif
774 }
775
776 bool CvCapture_MSMF::configureOutput(MediaType newType, cv::uint32_t outFormat)
777 {
778     FormatStorage formats;
779     formats.read(videoFileSource.Get());
780     std::pair<FormatStorage::MediaID, MediaType> bestMatch = formats.findBestVideoFormat(newType);
781     if (bestMatch.second.isEmpty())
782     {
783         CV_LOG_DEBUG(NULL, "Can not find video stream with requested parameters");
784         return false;
785     }
786     dwStreamIndex = bestMatch.first.stream;
787     nativeFormat = bestMatch.second;
788     MediaType newFormat = nativeFormat;
789     if (convertFormat)
790     {
791         switch (outFormat)
792         {
793         case CV_CAP_MODE_BGR:
794         case CV_CAP_MODE_RGB:
795             newFormat.subType = captureMode == MODE_HW ? MFVideoFormat_RGB32 : MFVideoFormat_RGB24;
796             newFormat.stride = (captureMode == MODE_HW ? 4 : 3) * newFormat.width;
797             newFormat.sampleSize = newFormat.stride * newFormat.height;
798             break;
799         case CV_CAP_MODE_GRAY:
800             newFormat.subType = MFVideoFormat_YUY2;
801             newFormat.stride = newFormat.width;
802             newFormat.sampleSize = newFormat.stride * newFormat.height * 3 / 2;
803             break;
804         case CV_CAP_MODE_YUYV:
805             newFormat.subType = MFVideoFormat_YUY2;
806             newFormat.stride = 2 * newFormat.width;
807             newFormat.sampleSize = newFormat.stride * newFormat.height;
808             break;
809         default:
810             return false;
811         }
812         newFormat.interlaceMode = MFVideoInterlace_Progressive;
813         newFormat.isFixedSize = true;
814         if (nativeFormat.subType == MFVideoFormat_MP43) //Unable to estimate FPS for MP43
815             newFormat.frameRateNum = 0;
816     }
817     // we select native format first and then our requested format (related issue #12822)
818     if (!newType.isEmpty()) // camera input
819         initStream(dwStreamIndex, nativeFormat);
820     return initStream(dwStreamIndex, newFormat);
821 }
822
823 bool CvCapture_MSMF::open(int index)
824 {
825     close();
826     if (index < 0)
827         return false;
828     DeviceList devices;
829     UINT32 count = devices.read();
830     if (count == 0 || static_cast<UINT32>(index) > count)
831     {
832         CV_LOG_DEBUG(NULL, "Device " << index << " not found (total " << count << " devices)");
833         return false;
834     }
835     _ComPtr<IMFAttributes> attr = getDefaultSourceConfig();
836     _ComPtr<IMFSourceReaderCallback> cb = new SourceReaderCB();
837     attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, cb.Get());
838     _ComPtr<IMFMediaSource> src = devices.activateSource(index);
839     if (!src.Get() || FAILED(MFCreateSourceReaderFromMediaSource(src.Get(), attr.Get(), &videoFileSource)))
840     {
841         CV_LOG_DEBUG(NULL, "Failed to create source reader");
842         return false;
843     }
844
845     isOpen = true;
846     camid = index;
847     readCallback = cb;
848     duration = 0;
849     if (configureOutput(MediaType::createDefault(), outputFormat))
850     {
851         frameStep = captureFormat.getFrameStep();
852     }
853     return isOpen;
854 }
855
856 bool CvCapture_MSMF::open(const cv::String& _filename)
857 {
858     close();
859     if (_filename.empty())
860         return false;
861
862     // Set source reader parameters
863     _ComPtr<IMFAttributes> attr = getDefaultSourceConfig();
864     cv::AutoBuffer<wchar_t> unicodeFileName(_filename.length() + 1);
865     MultiByteToWideChar(CP_ACP, 0, _filename.c_str(), -1, unicodeFileName.data(), (int)_filename.length() + 1);
866     if (SUCCEEDED(MFCreateSourceReaderFromURL(unicodeFileName.data(), attr.Get(), &videoFileSource)))
867     {
868         isOpen = true;
869         sampleTime = 0;
870         if (configureOutput(MediaType(), outputFormat))
871         {
872             frameStep = captureFormat.getFrameStep();
873             filename = _filename;
874             PROPVARIANT var;
875             HRESULT hr;
876             if (SUCCEEDED(hr = videoFileSource->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var)) &&
877                 var.vt == VT_UI8)
878             {
879                 duration = var.uhVal.QuadPart;
880                 PropVariantClear(&var);
881             }
882             else
883                 duration = 0;
884         }
885     }
886
887     return isOpen;
888 }
889
890 bool CvCapture_MSMF::grabFrame()
891 {
892     CV_TRACE_FUNCTION();
893     if (readCallback)  // async "live" capture mode
894     {
895         HRESULT hr = 0;
896         SourceReaderCB* reader = ((SourceReaderCB*)readCallback.Get());
897         if (!reader->m_reader)
898         {
899             // Initiate capturing with async callback
900             reader->m_reader = videoFileSource.Get();
901             reader->m_dwStreamIndex = dwStreamIndex;
902             if (FAILED(hr = videoFileSource->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
903             {
904                 CV_LOG_ERROR(NULL, "videoio(MSMF): can't grab frame - initial async ReadSample() call failed: " << hr);
905                 reader->m_reader = NULL;
906                 return false;
907             }
908         }
909         BOOL bEOS = false;
910         if (FAILED(hr = reader->Wait(10000, videoSample, bEOS)))  // 10 sec
911         {
912             CV_LOG_WARNING(NULL, "videoio(MSMF): can't grab frame. Error: " << hr);
913             return false;
914         }
915         if (bEOS)
916         {
917             CV_LOG_WARNING(NULL, "videoio(MSMF): EOS signal. Capture stream is lost");
918             return false;
919         }
920         sampleTime = reader->m_lastSampleTimestamp;
921         return true;
922     }
923     else if (isOpen)
924     {
925         DWORD streamIndex, flags;
926         videoSample.Release();
927         HRESULT hr;
928         for(;;)
929         {
930             CV_TRACE_REGION("ReadSample");
931             if (!SUCCEEDED(hr = videoFileSource->ReadSample(
932                 dwStreamIndex, // Stream index.
933                 0,             // Flags.
934                 &streamIndex,  // Receives the actual stream index.
935                 &flags,        // Receives status flags.
936                 &sampleTime,   // Receives the time stamp.
937                 &videoSample   // Receives the sample or NULL.
938             )))
939                 break;
940             if (streamIndex != dwStreamIndex)
941                 break;
942             if (flags & (MF_SOURCE_READERF_ERROR | MF_SOURCE_READERF_ALLEFFECTSREMOVED | MF_SOURCE_READERF_ENDOFSTREAM))
943                 break;
944             if (videoSample)
945                 break;
946             if (flags & MF_SOURCE_READERF_STREAMTICK)
947             {
948                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream tick detected. Retrying to grab the frame");
949             }
950         }
951
952         if (SUCCEEDED(hr))
953         {
954             if (streamIndex != dwStreamIndex)
955             {
956                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Wrong stream read. Abort capturing");
957                 close();
958             }
959             else if (flags & MF_SOURCE_READERF_ERROR)
960             {
961                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream reading error. Abort capturing");
962                 close();
963             }
964             else if (flags & MF_SOURCE_READERF_ALLEFFECTSREMOVED)
965             {
966                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream decoding error. Abort capturing");
967                 close();
968             }
969             else if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
970             {
971                 sampleTime += frameStep;
972                 CV_LOG_DEBUG(NULL, "videoio(MSMF): End of stream detected");
973             }
974             else
975             {
976                 sampleTime += frameStep;
977                 if (flags & MF_SOURCE_READERF_NEWSTREAM)
978                 {
979                     CV_LOG_DEBUG(NULL, "videoio(MSMF): New stream detected");
980                 }
981                 if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
982                 {
983                     CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream native media type changed");
984                 }
985                 if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
986                 {
987                     CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream current media type changed");
988                 }
989                 return true;
990             }
991         }
992     }
993     return false;
994 }
995
996 bool CvCapture_MSMF::retrieveFrame(int, cv::OutputArray frame)
997 {
998     CV_TRACE_FUNCTION();
999     do
1000     {
1001         if (!videoSample)
1002             break;
1003
1004         _ComPtr<IMFMediaBuffer> buf = NULL;
1005
1006         CV_TRACE_REGION("get_contiguous_buffer");
1007         if (!SUCCEEDED(videoSample->ConvertToContiguousBuffer(&buf)))
1008         {
1009             CV_TRACE_REGION("get_buffer");
1010             DWORD bcnt = 0;
1011             if (!SUCCEEDED(videoSample->GetBufferCount(&bcnt)))
1012                 break;
1013             if (bcnt == 0)
1014                 break;
1015             if (!SUCCEEDED(videoSample->GetBufferByIndex(0, &buf)))
1016                 break;
1017         }
1018
1019         bool lock2d = false;
1020         BYTE* ptr = NULL;
1021         LONG pitch = 0;
1022         DWORD maxsize = 0, cursize = 0;
1023
1024         // "For 2-D buffers, the Lock2D method is more efficient than the Lock method"
1025         // see IMFMediaBuffer::Lock method documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/bb970366(v=vs.85).aspx
1026         _ComPtr<IMF2DBuffer> buffer2d;
1027         if (convertFormat)
1028         {
1029             if (SUCCEEDED(buf.As<IMF2DBuffer>(buffer2d)))
1030             {
1031                 CV_TRACE_REGION_NEXT("lock2d");
1032                 if (SUCCEEDED(buffer2d->Lock2D(&ptr, &pitch)))
1033                 {
1034                     lock2d = true;
1035                 }
1036             }
1037         }
1038         if (ptr == NULL)
1039         {
1040             CV_Assert(lock2d == false);
1041             CV_TRACE_REGION_NEXT("lock");
1042             if (!SUCCEEDED(buf->Lock(&ptr, &maxsize, &cursize)))
1043             {
1044                 break;
1045             }
1046         }
1047         if (!ptr)
1048             break;
1049         if (convertFormat)
1050         {
1051             if (lock2d || (unsigned int)cursize == captureFormat.sampleSize)
1052             {
1053                 switch (outputFormat)
1054                 {
1055                 case CV_CAP_MODE_YUYV:
1056                     cv::Mat(captureFormat.height, captureFormat.width, CV_8UC2, ptr, pitch).copyTo(frame);
1057                     break;
1058                 case CV_CAP_MODE_BGR:
1059                     if (captureMode == MODE_HW)
1060                         cv::cvtColor(cv::Mat(captureFormat.height, captureFormat.width, CV_8UC4, ptr, pitch), frame, cv::COLOR_BGRA2BGR);
1061                     else
1062                         cv::Mat(captureFormat.height, captureFormat.width, CV_8UC3, ptr, pitch).copyTo(frame);
1063                     break;
1064                 case CV_CAP_MODE_RGB:
1065                     if (captureMode == MODE_HW)
1066                         cv::cvtColor(cv::Mat(captureFormat.height, captureFormat.width, CV_8UC4, ptr, pitch), frame, cv::COLOR_BGRA2BGR);
1067                     else
1068                         cv::cvtColor(cv::Mat(captureFormat.height, captureFormat.width, CV_8UC3, ptr, pitch), frame, cv::COLOR_BGR2RGB);
1069                     break;
1070                 case CV_CAP_MODE_GRAY:
1071                     cv::Mat(captureFormat.height, captureFormat.width, CV_8UC1, ptr, pitch).copyTo(frame);
1072                     break;
1073                 default:
1074                     frame.release();
1075                     break;
1076                 }
1077             }
1078             else
1079                 frame.release();
1080         }
1081         else
1082         {
1083             cv::Mat(1, cursize, CV_8UC1, ptr, pitch).copyTo(frame);
1084         }
1085         CV_TRACE_REGION_NEXT("unlock");
1086         if (lock2d)
1087             buffer2d->Unlock2D();
1088         else
1089             buf->Unlock();
1090         return !frame.empty();
1091     } while (0);
1092
1093     frame.release();
1094     return false;
1095 }
1096
1097 bool CvCapture_MSMF::setTime(double time, bool rough)
1098 {
1099     PROPVARIANT var;
1100     if (SUCCEEDED(videoFileSource->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, &var)) &&
1101         var.vt == VT_UI4 && var.ulVal & MFMEDIASOURCE_CAN_SEEK)
1102     {
1103         videoSample.Release();
1104         bool useGrabbing = time > 0 && !rough && !(var.ulVal & MFMEDIASOURCE_HAS_SLOW_SEEK);
1105         PropVariantClear(&var);
1106         sampleTime = (useGrabbing && time >= frameStep) ? (LONGLONG)floor(time + 0.5) - frameStep : (LONGLONG)floor(time + 0.5);
1107         var.vt = VT_I8;
1108         var.hVal.QuadPart = sampleTime;
1109         bool resOK = SUCCEEDED(videoFileSource->SetCurrentPosition(GUID_NULL, var));
1110         PropVariantClear(&var);
1111         if (resOK && useGrabbing)
1112         {
1113             LONGLONG timeborder = (LONGLONG)floor(time + 0.5) - frameStep / 2;
1114             do { resOK = grabFrame(); videoSample.Release(); } while (resOK && sampleTime < timeborder);
1115         }
1116         return resOK;
1117     }
1118     return false;
1119 }
1120
1121 template <typename CtrlT>
1122 bool CvCapture_MSMF::readComplexPropery(long prop, long & val) const
1123 {
1124     _ComPtr<CtrlT> ctrl;
1125     if (FAILED(videoFileSource->GetServiceForStream((DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL, IID_PPV_ARGS(&ctrl))))
1126     {
1127         CV_LOG_DEBUG(NULL, "Failed to get service for stream");
1128         return false;
1129     }
1130     long paramVal, paramFlag;
1131     if (FAILED(ctrl->Get(prop, &paramVal, &paramFlag)))
1132     {
1133         CV_LOG_DEBUG(NULL, "Failed to get property " << prop);
1134         // we continue
1135     }
1136     // fallback - get default value
1137     long minVal, maxVal, stepVal;
1138     if (FAILED(ctrl->GetRange(prop, &minVal, &maxVal, &stepVal, &paramVal, &paramFlag)))
1139     {
1140         CV_LOG_DEBUG(NULL, "Failed to get default value for property " << prop);
1141         return false;
1142     }
1143     val = paramVal;
1144     return true;
1145 }
1146
1147 double CvCapture_MSMF::getProperty( int property_id ) const
1148 {
1149     long cVal = 0;
1150     if (isOpen)
1151         switch (property_id)
1152         {
1153         case CV_CAP_PROP_MODE:
1154                 return captureMode;
1155         case CV_CAP_PROP_CONVERT_RGB:
1156                 return convertFormat ? 1 : 0;
1157         case CV_CAP_PROP_SAR_NUM:
1158                 return captureFormat.aspectRatioNum;
1159         case CV_CAP_PROP_SAR_DEN:
1160                 return captureFormat.aspectRatioDenom;
1161         case CV_CAP_PROP_FRAME_WIDTH:
1162             return captureFormat.width;
1163         case CV_CAP_PROP_FRAME_HEIGHT:
1164             return captureFormat.height;
1165         case CV_CAP_PROP_FOURCC:
1166             return captureFormat.subType.Data1;
1167         case CV_CAP_PROP_FPS:
1168             return captureFormat.getFramerate();
1169         case CV_CAP_PROP_FRAME_COUNT:
1170             if (duration != 0)
1171                 return floor(((double)duration / 1e7)* captureFormat.getFramerate() + 0.5);
1172             else
1173                 break;
1174         case CV_CAP_PROP_POS_FRAMES:
1175             return floor(((double)sampleTime / 1e7)* captureFormat.getFramerate() + 0.5);
1176         case CV_CAP_PROP_POS_MSEC:
1177             return (double)sampleTime / 1e4;
1178         case CV_CAP_PROP_POS_AVI_RATIO:
1179             if (duration != 0)
1180                 return (double)sampleTime / duration;
1181             else
1182                 break;
1183         case CV_CAP_PROP_BRIGHTNESS:
1184             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Brightness, cVal))
1185                 return cVal;
1186             break;
1187         case CV_CAP_PROP_CONTRAST:
1188             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Contrast, cVal))
1189                 return cVal;
1190             break;
1191         case CV_CAP_PROP_SATURATION:
1192             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Saturation, cVal))
1193                 return cVal;
1194             break;
1195         case CV_CAP_PROP_HUE:
1196             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Hue, cVal))
1197                 return cVal;
1198             break;
1199         case CV_CAP_PROP_GAIN:
1200             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Gain, cVal))
1201                 return cVal;
1202             break;
1203         case CV_CAP_PROP_SHARPNESS:
1204             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Sharpness, cVal))
1205                 return cVal;
1206             break;
1207         case CV_CAP_PROP_GAMMA:
1208             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Gamma, cVal))
1209                 return cVal;
1210             break;
1211         case CV_CAP_PROP_BACKLIGHT:
1212             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_BacklightCompensation, cVal))
1213                 return cVal;
1214             break;
1215         case CV_CAP_PROP_MONOCHROME:
1216             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_ColorEnable, cVal))
1217                 return cVal == 0 ? 1 : 0;
1218             break;
1219         case CV_CAP_PROP_TEMPERATURE:
1220             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_WhiteBalance, cVal))
1221                 return cVal;
1222             break;
1223         case CV_CAP_PROP_PAN:
1224             if (readComplexPropery<IAMCameraControl>(CameraControl_Pan, cVal))
1225                 return cVal;
1226             break;
1227         case CV_CAP_PROP_TILT:
1228             if (readComplexPropery<IAMCameraControl>(CameraControl_Tilt, cVal))
1229                 return cVal;
1230             break;
1231         case CV_CAP_PROP_ROLL:
1232             if (readComplexPropery<IAMCameraControl>(CameraControl_Roll, cVal))
1233                 return cVal;
1234             break;
1235         case CV_CAP_PROP_IRIS:
1236             if (readComplexPropery<IAMCameraControl>(CameraControl_Iris, cVal))
1237                 return cVal;
1238             break;
1239         case CV_CAP_PROP_EXPOSURE:
1240         case CV_CAP_PROP_AUTO_EXPOSURE:
1241             if (readComplexPropery<IAMCameraControl>(CameraControl_Exposure, cVal))
1242             {
1243                 if (property_id == CV_CAP_PROP_EXPOSURE)
1244                     return cVal;
1245                 else
1246                     return cVal == VideoProcAmp_Flags_Auto;
1247             }
1248             break;
1249         case CV_CAP_PROP_ZOOM:
1250             if (readComplexPropery<IAMCameraControl>(CameraControl_Zoom, cVal))
1251                 return cVal;
1252             break;
1253         case CV_CAP_PROP_FOCUS:
1254         case CV_CAP_PROP_AUTOFOCUS:
1255             if (readComplexPropery<IAMCameraControl>(CameraControl_Focus, cVal))
1256             {
1257                 if (property_id == CV_CAP_PROP_FOCUS)
1258                     return cVal;
1259                 else
1260                     return cVal == VideoProcAmp_Flags_Auto;
1261             }
1262             break;
1263         case CV_CAP_PROP_WHITE_BALANCE_BLUE_U:
1264         case CV_CAP_PROP_WHITE_BALANCE_RED_V:
1265         case CV_CAP_PROP_RECTIFICATION:
1266         case CV_CAP_PROP_TRIGGER:
1267         case CV_CAP_PROP_TRIGGER_DELAY:
1268         case CV_CAP_PROP_GUID:
1269         case CV_CAP_PROP_ISO_SPEED:
1270         case CV_CAP_PROP_SETTINGS:
1271         case CV_CAP_PROP_BUFFERSIZE:
1272         default:
1273             break;
1274         }
1275     return -1;
1276 }
1277
1278 template <typename CtrlT>
1279 bool CvCapture_MSMF::writeComplexProperty(long prop, double val, long flags)
1280 {
1281     _ComPtr<CtrlT> ctrl;
1282     if (FAILED(videoFileSource->GetServiceForStream((DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL, IID_PPV_ARGS(&ctrl))))
1283     {
1284         CV_LOG_DEBUG(NULL, "Failed get service for stream");
1285         return false;
1286     }
1287     if (FAILED(ctrl->Set(prop, (long)val, flags)))
1288     {
1289         CV_LOG_DEBUG(NULL, "Failed to set property " << prop);
1290         return false;
1291     }
1292     return true;
1293 }
1294
1295 bool CvCapture_MSMF::setProperty( int property_id, double value )
1296 {
1297     MediaType newFormat = captureFormat;
1298     if (isOpen)
1299         switch (property_id)
1300         {
1301         case CV_CAP_PROP_MODE:
1302             switch ((MSMFCapture_Mode)((int)value))
1303             {
1304             case MODE_SW:
1305                 return configureHW(false);
1306             case MODE_HW:
1307                 return configureHW(true);
1308             default:
1309                 return false;
1310             }
1311         case CV_CAP_PROP_FOURCC:
1312             return configureOutput(newFormat, (int)cvRound(value));
1313         case CV_CAP_PROP_FORMAT:
1314             return configureOutput(newFormat, (int)cvRound(value));
1315         case CV_CAP_PROP_CONVERT_RGB:
1316             convertFormat = (value != 0);
1317             return configureOutput(newFormat, outputFormat);
1318         case CV_CAP_PROP_SAR_NUM:
1319             if (value > 0)
1320             {
1321                 newFormat.aspectRatioNum = (UINT32)cvRound(value);
1322                 return configureOutput(newFormat, outputFormat);
1323             }
1324             break;
1325         case CV_CAP_PROP_SAR_DEN:
1326             if (value > 0)
1327             {
1328                 newFormat.aspectRatioDenom = (UINT32)cvRound(value);
1329                 return configureOutput(newFormat, outputFormat);
1330             }
1331             break;
1332         case CV_CAP_PROP_FRAME_WIDTH:
1333             if (value >= 0)
1334             {
1335                 newFormat.width = (UINT32)cvRound(value);
1336                 return configureOutput(newFormat, outputFormat);
1337             }
1338             break;
1339         case CV_CAP_PROP_FRAME_HEIGHT:
1340             if (value >= 0)
1341             {
1342                 newFormat.height = (UINT32)cvRound(value);
1343                 return configureOutput(newFormat, outputFormat);
1344             }
1345             break;
1346         case CV_CAP_PROP_FPS:
1347             if (value >= 0)
1348             {
1349                 newFormat.setFramerate(value);
1350                 return configureOutput(newFormat, outputFormat);
1351             }
1352             break;
1353         case CV_CAP_PROP_FRAME_COUNT:
1354             break;
1355         case CV_CAP_PROP_POS_AVI_RATIO:
1356             if (duration != 0)
1357                 return setTime(duration * value, true);
1358             break;
1359         case CV_CAP_PROP_POS_FRAMES:
1360             if (std::fabs(captureFormat.getFramerate()) > 0)
1361                 return setTime(value  * 1e7 / captureFormat.getFramerate(), false);
1362             break;
1363         case CV_CAP_PROP_POS_MSEC:
1364                 return setTime(value  * 1e4, false);
1365         case CV_CAP_PROP_BRIGHTNESS:
1366             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Brightness, value, VideoProcAmp_Flags_Manual);
1367         case CV_CAP_PROP_CONTRAST:
1368             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Contrast, value, VideoProcAmp_Flags_Manual);
1369         case CV_CAP_PROP_SATURATION:
1370             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Saturation, value, VideoProcAmp_Flags_Manual);
1371         case CV_CAP_PROP_HUE:
1372             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Hue, value, VideoProcAmp_Flags_Manual);
1373         case CV_CAP_PROP_GAIN:
1374             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Gain, value, VideoProcAmp_Flags_Manual);
1375         case CV_CAP_PROP_SHARPNESS:
1376             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Sharpness, value, VideoProcAmp_Flags_Manual);
1377         case CV_CAP_PROP_GAMMA:
1378             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Gamma, value, VideoProcAmp_Flags_Manual);
1379         case CV_CAP_PROP_BACKLIGHT:
1380             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_BacklightCompensation, value, VideoProcAmp_Flags_Manual);
1381         case CV_CAP_PROP_MONOCHROME:
1382             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_ColorEnable, value, VideoProcAmp_Flags_Manual);
1383         case CV_CAP_PROP_TEMPERATURE:
1384             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_WhiteBalance, value, VideoProcAmp_Flags_Manual);
1385         case CV_CAP_PROP_PAN:
1386             return writeComplexProperty<IAMCameraControl>(CameraControl_Pan, value, CameraControl_Flags_Manual);
1387         case CV_CAP_PROP_TILT:
1388             return writeComplexProperty<IAMCameraControl>(CameraControl_Tilt, value, CameraControl_Flags_Manual);
1389         case CV_CAP_PROP_ROLL:
1390             return writeComplexProperty<IAMCameraControl>(CameraControl_Roll, value, CameraControl_Flags_Manual);
1391         case CV_CAP_PROP_IRIS:
1392             return writeComplexProperty<IAMCameraControl>(CameraControl_Iris, value, CameraControl_Flags_Manual);
1393         case CV_CAP_PROP_EXPOSURE:
1394             return writeComplexProperty<IAMCameraControl>(CameraControl_Exposure, value, CameraControl_Flags_Manual);
1395         case CV_CAP_PROP_AUTO_EXPOSURE:
1396             return writeComplexProperty<IAMCameraControl>(CameraControl_Exposure, value, value != 0 ? VideoProcAmp_Flags_Auto : VideoProcAmp_Flags_Manual);
1397         case CV_CAP_PROP_ZOOM:
1398             return writeComplexProperty<IAMCameraControl>(CameraControl_Zoom, value, CameraControl_Flags_Manual);
1399         case CV_CAP_PROP_FOCUS:
1400             return writeComplexProperty<IAMCameraControl>(CameraControl_Focus, value, CameraControl_Flags_Manual);
1401         case CV_CAP_PROP_AUTOFOCUS:
1402             return writeComplexProperty<IAMCameraControl>(CameraControl_Focus, value, value != 0 ? CameraControl_Flags_Auto : CameraControl_Flags_Manual);
1403         case CV_CAP_PROP_WHITE_BALANCE_BLUE_U:
1404         case CV_CAP_PROP_WHITE_BALANCE_RED_V:
1405         case CV_CAP_PROP_RECTIFICATION:
1406         case CV_CAP_PROP_TRIGGER:
1407         case CV_CAP_PROP_TRIGGER_DELAY:
1408         case CV_CAP_PROP_GUID:
1409         case CV_CAP_PROP_ISO_SPEED:
1410         case CV_CAP_PROP_SETTINGS:
1411         case CV_CAP_PROP_BUFFERSIZE:
1412         default:
1413             break;
1414         }
1415     return false;
1416 }
1417
1418 cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF( int index )
1419 {
1420     cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>();
1421     if (capture)
1422     {
1423         capture->open(index);
1424         if (capture->isOpened())
1425             return capture;
1426     }
1427     return cv::Ptr<cv::IVideoCapture>();
1428 }
1429
1430 cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF (const cv::String& filename)
1431 {
1432     cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>();
1433     if (capture)
1434     {
1435         capture->open(filename);
1436         if (capture->isOpened())
1437             return capture;
1438     }
1439     return cv::Ptr<cv::IVideoCapture>();
1440 }
1441
1442 //
1443 //
1444 // Media Foundation-based Video Writer
1445 //
1446 //
1447
1448 class CvVideoWriter_MSMF : public cv::IVideoWriter
1449 {
1450 public:
1451     CvVideoWriter_MSMF();
1452     virtual ~CvVideoWriter_MSMF();
1453     virtual bool open(const cv::String& filename, int fourcc,
1454                       double fps, cv::Size frameSize, bool isColor);
1455     virtual void close();
1456     virtual void write(cv::InputArray);
1457
1458     virtual double getProperty(int) const { return 0; }
1459     virtual bool setProperty(int, double) { return false; }
1460     virtual bool isOpened() const { return initiated; }
1461
1462     int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_MSMF; }
1463 private:
1464     Media_Foundation& MF;
1465     UINT32 videoWidth;
1466     UINT32 videoHeight;
1467     double fps;
1468     UINT32 bitRate;
1469     UINT32 frameSize;
1470     GUID   encodingFormat;
1471     GUID   inputFormat;
1472
1473     DWORD  streamIndex;
1474     _ComPtr<IMFSinkWriter> sinkWriter;
1475
1476     bool   initiated;
1477
1478     LONGLONG rtStart;
1479     UINT64 rtDuration;
1480
1481     static const GUID FourCC2GUID(int fourcc);
1482 };
1483
1484 CvVideoWriter_MSMF::CvVideoWriter_MSMF():
1485     MF(Media_Foundation::getInstance()),
1486     videoWidth(0),
1487     videoHeight(0),
1488     fps(0),
1489     bitRate(0),
1490     frameSize(0),
1491     encodingFormat(),
1492     inputFormat(),
1493     streamIndex(0),
1494     initiated(false),
1495     rtStart(0),
1496     rtDuration(0)
1497 {
1498 }
1499
1500 CvVideoWriter_MSMF::~CvVideoWriter_MSMF()
1501 {
1502     close();
1503 }
1504
1505 const GUID CvVideoWriter_MSMF::FourCC2GUID(int fourcc)
1506 {
1507     switch(fourcc)
1508     {
1509         case CV_FOURCC_MACRO('d', 'v', '2', '5'):
1510             return MFVideoFormat_DV25; break;
1511         case CV_FOURCC_MACRO('d', 'v', '5', '0'):
1512             return MFVideoFormat_DV50; break;
1513         case CV_FOURCC_MACRO('d', 'v', 'c', ' '):
1514             return MFVideoFormat_DVC; break;
1515         case CV_FOURCC_MACRO('d', 'v', 'h', '1'):
1516             return MFVideoFormat_DVH1; break;
1517         case CV_FOURCC_MACRO('d', 'v', 'h', 'd'):
1518             return MFVideoFormat_DVHD; break;
1519         case CV_FOURCC_MACRO('d', 'v', 's', 'd'):
1520             return MFVideoFormat_DVSD; break;
1521         case CV_FOURCC_MACRO('d', 'v', 's', 'l'):
1522                 return MFVideoFormat_DVSL; break;
1523 #if (WINVER >= 0x0602)
1524         case CV_FOURCC_MACRO('H', '2', '6', '3'):   // Available only for Win 8 target.
1525                 return MFVideoFormat_H263; break;
1526 #endif
1527         case CV_FOURCC_MACRO('H', '2', '6', '4'):
1528                 return MFVideoFormat_H264; break;
1529         case CV_FOURCC_MACRO('M', '4', 'S', '2'):
1530                 return MFVideoFormat_M4S2; break;
1531         case CV_FOURCC_MACRO('M', 'J', 'P', 'G'):
1532                 return MFVideoFormat_MJPG; break;
1533         case CV_FOURCC_MACRO('M', 'P', '4', '3'):
1534                 return MFVideoFormat_MP43; break;
1535         case CV_FOURCC_MACRO('M', 'P', '4', 'S'):
1536                 return MFVideoFormat_MP4S; break;
1537         case CV_FOURCC_MACRO('M', 'P', '4', 'V'):
1538                 return MFVideoFormat_MP4V; break;
1539         case CV_FOURCC_MACRO('M', 'P', 'G', '1'):
1540                 return MFVideoFormat_MPG1; break;
1541         case CV_FOURCC_MACRO('M', 'S', 'S', '1'):
1542                 return MFVideoFormat_MSS1; break;
1543         case CV_FOURCC_MACRO('M', 'S', 'S', '2'):
1544                 return MFVideoFormat_MSS2; break;
1545         case CV_FOURCC_MACRO('W', 'M', 'V', '1'):
1546                 return MFVideoFormat_WMV1; break;
1547         case CV_FOURCC_MACRO('W', 'M', 'V', '2'):
1548                 return MFVideoFormat_WMV2; break;
1549         case CV_FOURCC_MACRO('W', 'M', 'V', '3'):
1550                 return MFVideoFormat_WMV3; break;
1551         case CV_FOURCC_MACRO('W', 'V', 'C', '1'):
1552                 return MFVideoFormat_WVC1; break;
1553         default:
1554             return MFVideoFormat_H264;
1555     }
1556 }
1557
1558 bool CvVideoWriter_MSMF::open( const cv::String& filename, int fourcc,
1559                                double _fps, cv::Size _frameSize, bool /*isColor*/ )
1560 {
1561     if (initiated)
1562         close();
1563     videoWidth = _frameSize.width;
1564     videoHeight = _frameSize.height;
1565     fps = _fps;
1566     bitRate = (UINT32)fps*videoWidth*videoHeight; // 1-bit per pixel
1567     encodingFormat = FourCC2GUID(fourcc);
1568     inputFormat = MFVideoFormat_RGB32;
1569
1570     _ComPtr<IMFMediaType>  mediaTypeOut;
1571     _ComPtr<IMFMediaType>  mediaTypeIn;
1572     _ComPtr<IMFAttributes> spAttr;
1573     if (// Set the output media type.
1574         SUCCEEDED(MFCreateMediaType(&mediaTypeOut)) &&
1575         SUCCEEDED(mediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)) &&
1576         SUCCEEDED(mediaTypeOut->SetGUID(MF_MT_SUBTYPE, encodingFormat)) &&
1577         SUCCEEDED(mediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, bitRate)) &&
1578         SUCCEEDED(mediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)) &&
1579         SUCCEEDED(MFSetAttributeSize(mediaTypeOut.Get(), MF_MT_FRAME_SIZE, videoWidth, videoHeight)) &&
1580         SUCCEEDED(MFSetAttributeRatio(mediaTypeOut.Get(), MF_MT_FRAME_RATE, (UINT32)(fps * 1000), 1000)) &&
1581         SUCCEEDED(MFSetAttributeRatio(mediaTypeOut.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1)) &&
1582         // Set the input media type.
1583         SUCCEEDED(MFCreateMediaType(&mediaTypeIn)) &&
1584         SUCCEEDED(mediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)) &&
1585         SUCCEEDED(mediaTypeIn->SetGUID(MF_MT_SUBTYPE, inputFormat)) &&
1586         SUCCEEDED(mediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)) &&
1587         SUCCEEDED(mediaTypeIn->SetUINT32(MF_MT_DEFAULT_STRIDE, 4 * videoWidth)) && //Assume BGR32 input
1588         SUCCEEDED(MFSetAttributeSize(mediaTypeIn.Get(), MF_MT_FRAME_SIZE, videoWidth, videoHeight)) &&
1589         SUCCEEDED(MFSetAttributeRatio(mediaTypeIn.Get(), MF_MT_FRAME_RATE, (UINT32)(fps * 1000), 1000)) &&
1590         SUCCEEDED(MFSetAttributeRatio(mediaTypeIn.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1)) &&
1591         // Set sink writer parameters
1592         SUCCEEDED(MFCreateAttributes(&spAttr, 10)) &&
1593         SUCCEEDED(spAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)) &&
1594         SUCCEEDED(spAttr->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, true))
1595         )
1596     {
1597         // Create the sink writer
1598         cv::AutoBuffer<wchar_t> unicodeFileName(filename.length() + 1);
1599         MultiByteToWideChar(CP_ACP, 0, filename.c_str(), -1, unicodeFileName.data(), (int)filename.length() + 1);
1600         HRESULT hr = MFCreateSinkWriterFromURL(unicodeFileName.data(), NULL, spAttr.Get(), &sinkWriter);
1601         if (SUCCEEDED(hr))
1602         {
1603             // Configure the sink writer and tell it start to start accepting data
1604             if (SUCCEEDED(sinkWriter->AddStream(mediaTypeOut.Get(), &streamIndex)) &&
1605                 SUCCEEDED(sinkWriter->SetInputMediaType(streamIndex, mediaTypeIn.Get(), NULL)) &&
1606                 SUCCEEDED(sinkWriter->BeginWriting()))
1607             {
1608                 initiated = true;
1609                 rtStart = 0;
1610                 MFFrameRateToAverageTimePerFrame((UINT32)(fps * 1000), 1000, &rtDuration);
1611                 return true;
1612             }
1613         }
1614     }
1615
1616     return false;
1617 }
1618
1619 void CvVideoWriter_MSMF::close()
1620 {
1621     if (initiated)
1622     {
1623         initiated = false;
1624         sinkWriter->Finalize();
1625         sinkWriter.Release();
1626     }
1627 }
1628
1629 void CvVideoWriter_MSMF::write(cv::InputArray img)
1630 {
1631     if (img.empty() ||
1632         (img.channels() != 1 && img.channels() != 3 && img.channels() != 4) ||
1633         (UINT32)img.cols() != videoWidth || (UINT32)img.rows() != videoHeight)
1634         return;
1635
1636     const LONG cbWidth = 4 * videoWidth;
1637     const DWORD cbBuffer = cbWidth * videoHeight;
1638     _ComPtr<IMFSample> sample;
1639     _ComPtr<IMFMediaBuffer> buffer;
1640     BYTE *pData = NULL;
1641     // Prepare a media sample.
1642     if (SUCCEEDED(MFCreateSample(&sample)) &&
1643         // Set sample time stamp and duration.
1644         SUCCEEDED(sample->SetSampleTime(rtStart)) &&
1645         SUCCEEDED(sample->SetSampleDuration(rtDuration)) &&
1646         // Create a memory buffer.
1647         SUCCEEDED(MFCreateMemoryBuffer(cbBuffer, &buffer)) &&
1648         // Set the data length of the buffer.
1649         SUCCEEDED(buffer->SetCurrentLength(cbBuffer)) &&
1650         // Add the buffer to the sample.
1651         SUCCEEDED(sample->AddBuffer(buffer.Get())) &&
1652         // Lock the buffer.
1653         SUCCEEDED(buffer->Lock(&pData, NULL, NULL)))
1654     {
1655         // Copy the video frame to the buffer.
1656         cv::cvtColor(img.getMat(), cv::Mat(videoHeight, videoWidth, CV_8UC4, pData, cbWidth), img.channels() > 1 ? cv::COLOR_BGR2BGRA : cv::COLOR_GRAY2BGRA);
1657         buffer->Unlock();
1658         // Send media sample to the Sink Writer.
1659         if (SUCCEEDED(sinkWriter->WriteSample(streamIndex, sample.Get())))
1660         {
1661             rtStart += rtDuration;
1662         }
1663     }
1664 }
1665
1666 cv::Ptr<cv::IVideoWriter> cv::cvCreateVideoWriter_MSMF( const std::string& filename, int fourcc,
1667                                                         double fps, const cv::Size& frameSize,
1668                                                         const VideoWriterParameters& params)
1669 {
1670     cv::Ptr<CvVideoWriter_MSMF> writer = cv::makePtr<CvVideoWriter_MSMF>();
1671     if (writer)
1672     {
1673         const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true);
1674         writer->open(filename, fourcc, fps, frameSize, isColor);
1675         if (writer->isOpened())
1676             return writer;
1677     }
1678     return cv::Ptr<cv::IVideoWriter>();
1679 }
1680
1681 #if defined(BUILD_PLUGIN)
1682
1683 #include "plugin_api.hpp"
1684
1685 namespace cv {
1686
1687 typedef CvCapture_MSMF CaptureT;
1688 typedef CvVideoWriter_MSMF WriterT;
1689
1690 static
1691 CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_OUT CvPluginCapture* handle)
1692 {
1693     if (!handle)
1694         return CV_ERROR_FAIL;
1695     *handle = NULL;
1696     if (!filename)
1697         return CV_ERROR_FAIL;
1698     CaptureT* cap = 0;
1699     try
1700     {
1701         cap = new CaptureT();
1702         bool res;
1703         if (filename)
1704             res = cap->open(std::string(filename));
1705         else
1706             res = cap->open(camera_index);
1707         if (res)
1708         {
1709             *handle = (CvPluginCapture)cap;
1710             return CV_ERROR_OK;
1711         }
1712     }
1713     catch (...)
1714     {
1715     }
1716     if (cap)
1717         delete cap;
1718     return CV_ERROR_FAIL;
1719 }
1720
1721 static
1722 CvResult CV_API_CALL cv_capture_release(CvPluginCapture handle)
1723 {
1724     if (!handle)
1725         return CV_ERROR_FAIL;
1726     CaptureT* instance = (CaptureT*)handle;
1727     delete instance;
1728     return CV_ERROR_OK;
1729 }
1730
1731
1732 static
1733 CvResult CV_API_CALL cv_capture_get_prop(CvPluginCapture handle, int prop, CV_OUT double* val)
1734 {
1735     if (!handle)
1736         return CV_ERROR_FAIL;
1737     if (!val)
1738         return CV_ERROR_FAIL;
1739     try
1740     {
1741         CaptureT* instance = (CaptureT*)handle;
1742         *val = instance->getProperty(prop);
1743         return CV_ERROR_OK;
1744     }
1745     catch (...)
1746     {
1747         return CV_ERROR_FAIL;
1748     }
1749 }
1750
1751 static
1752 CvResult CV_API_CALL cv_capture_set_prop(CvPluginCapture handle, int prop, double val)
1753 {
1754     if (!handle)
1755         return CV_ERROR_FAIL;
1756     try
1757     {
1758         CaptureT* instance = (CaptureT*)handle;
1759         return instance->setProperty(prop, val) ? CV_ERROR_OK : CV_ERROR_FAIL;
1760     }
1761     catch (...)
1762     {
1763         return CV_ERROR_FAIL;
1764     }
1765 }
1766
1767 static
1768 CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle)
1769 {
1770     if (!handle)
1771         return CV_ERROR_FAIL;
1772     try
1773     {
1774         CaptureT* instance = (CaptureT*)handle;
1775         return instance->grabFrame() ? CV_ERROR_OK : CV_ERROR_FAIL;
1776     }
1777     catch (...)
1778     {
1779         return CV_ERROR_FAIL;
1780     }
1781 }
1782
1783 static
1784 CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata)
1785 {
1786     if (!handle)
1787         return CV_ERROR_FAIL;
1788     try
1789     {
1790         CaptureT* instance = (CaptureT*)handle;
1791         Mat img;
1792         if (instance->retrieveFrame(stream_idx, img))
1793             return callback(stream_idx, img.data, (int)img.step, img.cols, img.rows, img.channels(), userdata);
1794         return CV_ERROR_FAIL;
1795     }
1796     catch (...)
1797     {
1798         return CV_ERROR_FAIL;
1799     }
1800 }
1801
1802 static
1803 CvResult CV_API_CALL cv_writer_open(const char* filename, int fourcc, double fps, int width, int height, int isColor, CV_OUT CvPluginWriter* handle)
1804 {
1805     WriterT* wrt = 0;
1806     try
1807     {
1808         wrt = new WriterT();
1809         Size sz(width, height);
1810         if (wrt && wrt->open(filename, fourcc, fps, sz, isColor != 0))
1811         {
1812             *handle = (CvPluginWriter)wrt;
1813             return CV_ERROR_OK;
1814         }
1815     }
1816     catch (...)
1817     {
1818     }
1819     if (wrt)
1820         delete wrt;
1821     return CV_ERROR_FAIL;
1822 }
1823
1824 static
1825 CvResult CV_API_CALL cv_writer_release(CvPluginWriter handle)
1826 {
1827     if (!handle)
1828         return CV_ERROR_FAIL;
1829     WriterT* instance = (WriterT*)handle;
1830     delete instance;
1831     return CV_ERROR_OK;
1832 }
1833
1834 static
1835 CvResult CV_API_CALL cv_writer_get_prop(CvPluginWriter /*handle*/, int /*prop*/, CV_OUT double* /*val*/)
1836 {
1837     return CV_ERROR_FAIL;
1838 }
1839
1840 static
1841 CvResult CV_API_CALL cv_writer_set_prop(CvPluginWriter /*handle*/, int /*prop*/, double /*val*/)
1842 {
1843     return CV_ERROR_FAIL;
1844 }
1845
1846 static
1847 CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char* data, int step, int width, int height, int cn)
1848 {
1849     if (!handle)
1850         return CV_ERROR_FAIL;
1851     try
1852     {
1853         CV_Assert(step >= 0);
1854         WriterT* instance = (WriterT*)handle;
1855         Size sz(width, height);
1856         Mat img(sz, CV_MAKETYPE(CV_8U, cn), (void*)data, (size_t)step);
1857         instance->write(img);
1858         return CV_ERROR_OK;
1859     }
1860     catch (...)
1861     {
1862         return CV_ERROR_FAIL;
1863     }
1864 }
1865
1866 static const OpenCV_VideoIO_Plugin_API_preview plugin_api_v0 =
1867 {
1868     {
1869         sizeof(OpenCV_VideoIO_Plugin_API_preview), ABI_VERSION, API_VERSION,
1870         CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
1871         "Microsoft Media Foundation OpenCV Video I/O plugin"
1872     },
1873     /*  1*/CAP_MSMF,
1874     /*  2*/cv_capture_open,
1875     /*  3*/cv_capture_release,
1876     /*  4*/cv_capture_get_prop,
1877     /*  5*/cv_capture_set_prop,
1878     /*  6*/cv_capture_grab,
1879     /*  7*/cv_capture_retrieve,
1880     /*  8*/cv_writer_open,
1881     /*  9*/cv_writer_release,
1882     /* 10*/cv_writer_get_prop,
1883     /* 11*/cv_writer_set_prop,
1884     /* 12*/cv_writer_write
1885 };
1886
1887 } // namespace
1888
1889 const OpenCV_VideoIO_Plugin_API_preview* opencv_videoio_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
1890 {
1891     if (requested_abi_version != 0)
1892         return NULL;
1893     if (requested_api_version != 0)
1894         return NULL;
1895     return &cv::plugin_api_v0;
1896 }
1897
1898 #endif // BUILD_PLUGIN