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         CV_UNUSED(llTimestamp);
355
356         HRESULT hr = 0;
357         cv::AutoLock lock(m_mutex);
358
359         if (SUCCEEDED(hrStatus))
360         {
361             if (pSample)
362             {
363                 CV_LOG_DEBUG(NULL, "videoio(MSMF): got frame at " << llTimestamp);
364                 if (m_lastSample.Get())
365                 {
366                     CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed)");
367                 }
368                 m_lastSample = pSample;
369             }
370         }
371         else
372         {
373             CV_LOG_WARNING(NULL, "videoio(MSMF): OnReadSample() is called with error status: " << hrStatus);
374         }
375
376         if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
377         {
378             // Reached the end of the stream.
379             m_bEOS = true;
380         }
381         m_hrStatus = hrStatus;
382
383         if (FAILED(hr = m_reader->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
384         {
385             CV_LOG_WARNING(NULL, "videoio(MSMF): async ReadSample() call is failed with error status: " << hr);
386             m_bEOS = true;
387         }
388
389         if (pSample || m_bEOS)
390         {
391             SetEvent(m_hEvent);
392         }
393         return S_OK;
394     }
395
396     STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) CV_OVERRIDE
397     {
398         return S_OK;
399     }
400     STDMETHODIMP OnFlush(DWORD) CV_OVERRIDE
401     {
402         return S_OK;
403     }
404
405     HRESULT Wait(DWORD dwMilliseconds, _ComPtr<IMFSample>& videoSample, BOOL& pbEOS)
406     {
407         pbEOS = FALSE;
408
409         DWORD dwResult = WaitForSingleObject(m_hEvent, dwMilliseconds);
410         if (dwResult == WAIT_TIMEOUT)
411         {
412             return E_PENDING;
413         }
414         else if (dwResult != WAIT_OBJECT_0)
415         {
416             return HRESULT_FROM_WIN32(GetLastError());
417         }
418
419         pbEOS = m_bEOS;
420         if (!pbEOS)
421         {
422             cv::AutoLock lock(m_mutex);
423             videoSample = m_lastSample;
424             CV_Assert(videoSample);
425             m_lastSample.Release();
426             ResetEvent(m_hEvent);  // event is auto-reset, but we need this forced reset due time gap between wait() and mutex hold.
427         }
428
429         return m_hrStatus;
430     }
431 private:
432     // Destructor is private. Caller should call Release.
433     virtual ~SourceReaderCB()
434     {
435         CV_LOG_WARNING(NULL, "terminating async callback");
436     }
437
438 public:
439     long                m_nRefCount;        // Reference count.
440     cv::Mutex           m_mutex;
441     HANDLE              m_hEvent;
442     BOOL                m_bEOS;
443     HRESULT             m_hrStatus;
444
445     IMFSourceReader *m_reader;
446     DWORD m_dwStreamIndex;
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         return true;
921     }
922     else if (isOpen)
923     {
924         DWORD streamIndex, flags;
925         videoSample.Release();
926         HRESULT hr;
927         for(;;)
928         {
929             CV_TRACE_REGION("ReadSample");
930             if (!SUCCEEDED(hr = videoFileSource->ReadSample(
931                 dwStreamIndex, // Stream index.
932                 0,             // Flags.
933                 &streamIndex,  // Receives the actual stream index.
934                 &flags,        // Receives status flags.
935                 &sampleTime,   // Receives the time stamp.
936                 &videoSample   // Receives the sample or NULL.
937             )))
938                 break;
939             if (streamIndex != dwStreamIndex)
940                 break;
941             if (flags & (MF_SOURCE_READERF_ERROR | MF_SOURCE_READERF_ALLEFFECTSREMOVED | MF_SOURCE_READERF_ENDOFSTREAM))
942                 break;
943             if (videoSample)
944                 break;
945             if (flags & MF_SOURCE_READERF_STREAMTICK)
946             {
947                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream tick detected. Retrying to grab the frame");
948             }
949         }
950
951         if (SUCCEEDED(hr))
952         {
953             if (streamIndex != dwStreamIndex)
954             {
955                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Wrong stream read. Abort capturing");
956                 close();
957             }
958             else if (flags & MF_SOURCE_READERF_ERROR)
959             {
960                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream reading error. Abort capturing");
961                 close();
962             }
963             else if (flags & MF_SOURCE_READERF_ALLEFFECTSREMOVED)
964             {
965                 CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream decoding error. Abort capturing");
966                 close();
967             }
968             else if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
969             {
970                 sampleTime += frameStep;
971                 CV_LOG_DEBUG(NULL, "videoio(MSMF): End of stream detected");
972             }
973             else
974             {
975                 sampleTime += frameStep;
976                 if (flags & MF_SOURCE_READERF_NEWSTREAM)
977                 {
978                     CV_LOG_DEBUG(NULL, "videoio(MSMF): New stream detected");
979                 }
980                 if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
981                 {
982                     CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream native media type changed");
983                 }
984                 if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
985                 {
986                     CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream current media type changed");
987                 }
988                 return true;
989             }
990         }
991     }
992     return false;
993 }
994
995 bool CvCapture_MSMF::retrieveFrame(int, cv::OutputArray frame)
996 {
997     CV_TRACE_FUNCTION();
998     do
999     {
1000         if (!videoSample)
1001             break;
1002
1003         _ComPtr<IMFMediaBuffer> buf = NULL;
1004
1005         CV_TRACE_REGION("get_contiguous_buffer");
1006         if (!SUCCEEDED(videoSample->ConvertToContiguousBuffer(&buf)))
1007         {
1008             CV_TRACE_REGION("get_buffer");
1009             DWORD bcnt = 0;
1010             if (!SUCCEEDED(videoSample->GetBufferCount(&bcnt)))
1011                 break;
1012             if (bcnt == 0)
1013                 break;
1014             if (!SUCCEEDED(videoSample->GetBufferByIndex(0, &buf)))
1015                 break;
1016         }
1017
1018         bool lock2d = false;
1019         BYTE* ptr = NULL;
1020         LONG pitch = 0;
1021         DWORD maxsize = 0, cursize = 0;
1022
1023         // "For 2-D buffers, the Lock2D method is more efficient than the Lock method"
1024         // see IMFMediaBuffer::Lock method documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/bb970366(v=vs.85).aspx
1025         _ComPtr<IMF2DBuffer> buffer2d;
1026         if (convertFormat)
1027         {
1028             if (SUCCEEDED(buf.As<IMF2DBuffer>(buffer2d)))
1029             {
1030                 CV_TRACE_REGION_NEXT("lock2d");
1031                 if (SUCCEEDED(buffer2d->Lock2D(&ptr, &pitch)))
1032                 {
1033                     lock2d = true;
1034                 }
1035             }
1036         }
1037         if (ptr == NULL)
1038         {
1039             CV_Assert(lock2d == false);
1040             CV_TRACE_REGION_NEXT("lock");
1041             if (!SUCCEEDED(buf->Lock(&ptr, &maxsize, &cursize)))
1042             {
1043                 break;
1044             }
1045         }
1046         if (!ptr)
1047             break;
1048         if (convertFormat)
1049         {
1050             if (lock2d || (unsigned int)cursize == captureFormat.sampleSize)
1051             {
1052                 switch (outputFormat)
1053                 {
1054                 case CV_CAP_MODE_YUYV:
1055                     cv::Mat(captureFormat.height, captureFormat.width, CV_8UC2, ptr, pitch).copyTo(frame);
1056                     break;
1057                 case CV_CAP_MODE_BGR:
1058                     if (captureMode == MODE_HW)
1059                         cv::cvtColor(cv::Mat(captureFormat.height, captureFormat.width, CV_8UC4, ptr, pitch), frame, cv::COLOR_BGRA2BGR);
1060                     else
1061                         cv::Mat(captureFormat.height, captureFormat.width, CV_8UC3, ptr, pitch).copyTo(frame);
1062                     break;
1063                 case CV_CAP_MODE_RGB:
1064                     if (captureMode == MODE_HW)
1065                         cv::cvtColor(cv::Mat(captureFormat.height, captureFormat.width, CV_8UC4, ptr, pitch), frame, cv::COLOR_BGRA2BGR);
1066                     else
1067                         cv::cvtColor(cv::Mat(captureFormat.height, captureFormat.width, CV_8UC3, ptr, pitch), frame, cv::COLOR_BGR2RGB);
1068                     break;
1069                 case CV_CAP_MODE_GRAY:
1070                     cv::Mat(captureFormat.height, captureFormat.width, CV_8UC1, ptr, pitch).copyTo(frame);
1071                     break;
1072                 default:
1073                     frame.release();
1074                     break;
1075                 }
1076             }
1077             else
1078                 frame.release();
1079         }
1080         else
1081         {
1082             cv::Mat(1, cursize, CV_8UC1, ptr, pitch).copyTo(frame);
1083         }
1084         CV_TRACE_REGION_NEXT("unlock");
1085         if (lock2d)
1086             buffer2d->Unlock2D();
1087         else
1088             buf->Unlock();
1089         return !frame.empty();
1090     } while (0);
1091
1092     frame.release();
1093     return false;
1094 }
1095
1096 bool CvCapture_MSMF::setTime(double time, bool rough)
1097 {
1098     PROPVARIANT var;
1099     if (SUCCEEDED(videoFileSource->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, &var)) &&
1100         var.vt == VT_UI4 && var.ulVal & MFMEDIASOURCE_CAN_SEEK)
1101     {
1102         videoSample.Release();
1103         bool useGrabbing = time > 0 && !rough && !(var.ulVal & MFMEDIASOURCE_HAS_SLOW_SEEK);
1104         PropVariantClear(&var);
1105         sampleTime = (useGrabbing && time >= frameStep) ? (LONGLONG)floor(time + 0.5) - frameStep : (LONGLONG)floor(time + 0.5);
1106         var.vt = VT_I8;
1107         var.hVal.QuadPart = sampleTime;
1108         bool resOK = SUCCEEDED(videoFileSource->SetCurrentPosition(GUID_NULL, var));
1109         PropVariantClear(&var);
1110         if (resOK && useGrabbing)
1111         {
1112             LONGLONG timeborder = (LONGLONG)floor(time + 0.5) - frameStep / 2;
1113             do { resOK = grabFrame(); videoSample.Release(); } while (resOK && sampleTime < timeborder);
1114         }
1115         return resOK;
1116     }
1117     return false;
1118 }
1119
1120 template <typename CtrlT>
1121 bool CvCapture_MSMF::readComplexPropery(long prop, long & val) const
1122 {
1123     _ComPtr<CtrlT> ctrl;
1124     if (FAILED(videoFileSource->GetServiceForStream((DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL, IID_PPV_ARGS(&ctrl))))
1125     {
1126         CV_LOG_DEBUG(NULL, "Failed to get service for stream");
1127         return false;
1128     }
1129     long paramVal, paramFlag;
1130     if (FAILED(ctrl->Get(prop, &paramVal, &paramFlag)))
1131     {
1132         CV_LOG_DEBUG(NULL, "Failed to get property " << prop);
1133         // we continue
1134     }
1135     // fallback - get default value
1136     long minVal, maxVal, stepVal;
1137     if (FAILED(ctrl->GetRange(prop, &minVal, &maxVal, &stepVal, &paramVal, &paramFlag)))
1138     {
1139         CV_LOG_DEBUG(NULL, "Failed to get default value for property " << prop);
1140         return false;
1141     }
1142     val = paramVal;
1143     return true;
1144 }
1145
1146 double CvCapture_MSMF::getProperty( int property_id ) const
1147 {
1148     long cVal = 0;
1149     if (isOpen)
1150         switch (property_id)
1151         {
1152         case CV_CAP_PROP_MODE:
1153                 return captureMode;
1154         case CV_CAP_PROP_CONVERT_RGB:
1155                 return convertFormat ? 1 : 0;
1156         case CV_CAP_PROP_SAR_NUM:
1157                 return captureFormat.aspectRatioNum;
1158         case CV_CAP_PROP_SAR_DEN:
1159                 return captureFormat.aspectRatioDenom;
1160         case CV_CAP_PROP_FRAME_WIDTH:
1161             return captureFormat.width;
1162         case CV_CAP_PROP_FRAME_HEIGHT:
1163             return captureFormat.height;
1164         case CV_CAP_PROP_FOURCC:
1165             return captureFormat.subType.Data1;
1166         case CV_CAP_PROP_FPS:
1167             return captureFormat.getFramerate();
1168         case CV_CAP_PROP_FRAME_COUNT:
1169             if (duration != 0)
1170                 return floor(((double)duration / 1e7)* captureFormat.getFramerate() + 0.5);
1171             else
1172                 break;
1173         case CV_CAP_PROP_POS_FRAMES:
1174             return floor(((double)sampleTime / 1e7)* captureFormat.getFramerate() + 0.5);
1175         case CV_CAP_PROP_POS_MSEC:
1176             return (double)sampleTime / 1e4;
1177         case CV_CAP_PROP_POS_AVI_RATIO:
1178             if (duration != 0)
1179                 return (double)sampleTime / duration;
1180             else
1181                 break;
1182         case CV_CAP_PROP_BRIGHTNESS:
1183             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Brightness, cVal))
1184                 return cVal;
1185             break;
1186         case CV_CAP_PROP_CONTRAST:
1187             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Contrast, cVal))
1188                 return cVal;
1189             break;
1190         case CV_CAP_PROP_SATURATION:
1191             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Saturation, cVal))
1192                 return cVal;
1193             break;
1194         case CV_CAP_PROP_HUE:
1195             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Hue, cVal))
1196                 return cVal;
1197             break;
1198         case CV_CAP_PROP_GAIN:
1199             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Gain, cVal))
1200                 return cVal;
1201             break;
1202         case CV_CAP_PROP_SHARPNESS:
1203             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Sharpness, cVal))
1204                 return cVal;
1205             break;
1206         case CV_CAP_PROP_GAMMA:
1207             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_Gamma, cVal))
1208                 return cVal;
1209             break;
1210         case CV_CAP_PROP_BACKLIGHT:
1211             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_BacklightCompensation, cVal))
1212                 return cVal;
1213             break;
1214         case CV_CAP_PROP_MONOCHROME:
1215             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_ColorEnable, cVal))
1216                 return cVal == 0 ? 1 : 0;
1217             break;
1218         case CV_CAP_PROP_TEMPERATURE:
1219             if (readComplexPropery<IAMVideoProcAmp>(VideoProcAmp_WhiteBalance, cVal))
1220                 return cVal;
1221             break;
1222         case CV_CAP_PROP_PAN:
1223             if (readComplexPropery<IAMCameraControl>(CameraControl_Pan, cVal))
1224                 return cVal;
1225             break;
1226         case CV_CAP_PROP_TILT:
1227             if (readComplexPropery<IAMCameraControl>(CameraControl_Tilt, cVal))
1228                 return cVal;
1229             break;
1230         case CV_CAP_PROP_ROLL:
1231             if (readComplexPropery<IAMCameraControl>(CameraControl_Roll, cVal))
1232                 return cVal;
1233             break;
1234         case CV_CAP_PROP_IRIS:
1235             if (readComplexPropery<IAMCameraControl>(CameraControl_Iris, cVal))
1236                 return cVal;
1237             break;
1238         case CV_CAP_PROP_EXPOSURE:
1239         case CV_CAP_PROP_AUTO_EXPOSURE:
1240             if (readComplexPropery<IAMCameraControl>(CameraControl_Exposure, cVal))
1241             {
1242                 if (property_id == CV_CAP_PROP_EXPOSURE)
1243                     return cVal;
1244                 else
1245                     return cVal == VideoProcAmp_Flags_Auto;
1246             }
1247             break;
1248         case CV_CAP_PROP_ZOOM:
1249             if (readComplexPropery<IAMCameraControl>(CameraControl_Zoom, cVal))
1250                 return cVal;
1251             break;
1252         case CV_CAP_PROP_FOCUS:
1253         case CV_CAP_PROP_AUTOFOCUS:
1254             if (readComplexPropery<IAMCameraControl>(CameraControl_Focus, cVal))
1255             {
1256                 if (property_id == CV_CAP_PROP_FOCUS)
1257                     return cVal;
1258                 else
1259                     return cVal == VideoProcAmp_Flags_Auto;
1260             }
1261             break;
1262         case CV_CAP_PROP_WHITE_BALANCE_BLUE_U:
1263         case CV_CAP_PROP_WHITE_BALANCE_RED_V:
1264         case CV_CAP_PROP_RECTIFICATION:
1265         case CV_CAP_PROP_TRIGGER:
1266         case CV_CAP_PROP_TRIGGER_DELAY:
1267         case CV_CAP_PROP_GUID:
1268         case CV_CAP_PROP_ISO_SPEED:
1269         case CV_CAP_PROP_SETTINGS:
1270         case CV_CAP_PROP_BUFFERSIZE:
1271         default:
1272             break;
1273         }
1274     return -1;
1275 }
1276
1277 template <typename CtrlT>
1278 bool CvCapture_MSMF::writeComplexProperty(long prop, double val, long flags)
1279 {
1280     _ComPtr<CtrlT> ctrl;
1281     if (FAILED(videoFileSource->GetServiceForStream((DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL, IID_PPV_ARGS(&ctrl))))
1282     {
1283         CV_LOG_DEBUG(NULL, "Failed get service for stream");
1284         return false;
1285     }
1286     if (FAILED(ctrl->Set(prop, (long)val, flags)))
1287     {
1288         CV_LOG_DEBUG(NULL, "Failed to set property " << prop);
1289         return false;
1290     }
1291     return true;
1292 }
1293
1294 bool CvCapture_MSMF::setProperty( int property_id, double value )
1295 {
1296     MediaType newFormat = captureFormat;
1297     if (isOpen)
1298         switch (property_id)
1299         {
1300         case CV_CAP_PROP_MODE:
1301             switch ((MSMFCapture_Mode)((int)value))
1302             {
1303             case MODE_SW:
1304                 return configureHW(false);
1305             case MODE_HW:
1306                 return configureHW(true);
1307             default:
1308                 return false;
1309             }
1310         case CV_CAP_PROP_FOURCC:
1311             return configureOutput(newFormat, (int)cvRound(value));
1312         case CV_CAP_PROP_FORMAT:
1313             return configureOutput(newFormat, (int)cvRound(value));
1314         case CV_CAP_PROP_CONVERT_RGB:
1315             convertFormat = (value != 0);
1316             return configureOutput(newFormat, outputFormat);
1317         case CV_CAP_PROP_SAR_NUM:
1318             if (value > 0)
1319             {
1320                 newFormat.aspectRatioNum = (UINT32)cvRound(value);
1321                 return configureOutput(newFormat, outputFormat);
1322             }
1323             break;
1324         case CV_CAP_PROP_SAR_DEN:
1325             if (value > 0)
1326             {
1327                 newFormat.aspectRatioDenom = (UINT32)cvRound(value);
1328                 return configureOutput(newFormat, outputFormat);
1329             }
1330             break;
1331         case CV_CAP_PROP_FRAME_WIDTH:
1332             if (value >= 0)
1333             {
1334                 newFormat.width = (UINT32)cvRound(value);
1335                 return configureOutput(newFormat, outputFormat);
1336             }
1337             break;
1338         case CV_CAP_PROP_FRAME_HEIGHT:
1339             if (value >= 0)
1340             {
1341                 newFormat.height = (UINT32)cvRound(value);
1342                 return configureOutput(newFormat, outputFormat);
1343             }
1344             break;
1345         case CV_CAP_PROP_FPS:
1346             if (value >= 0)
1347             {
1348                 newFormat.setFramerate(value);
1349                 return configureOutput(newFormat, outputFormat);
1350             }
1351             break;
1352         case CV_CAP_PROP_FRAME_COUNT:
1353             break;
1354         case CV_CAP_PROP_POS_AVI_RATIO:
1355             if (duration != 0)
1356                 return setTime(duration * value, true);
1357             break;
1358         case CV_CAP_PROP_POS_FRAMES:
1359             if (std::fabs(captureFormat.getFramerate()) > 0)
1360                 return setTime(value  * 1e7 / captureFormat.getFramerate(), false);
1361             break;
1362         case CV_CAP_PROP_POS_MSEC:
1363                 return setTime(value  * 1e4, false);
1364         case CV_CAP_PROP_BRIGHTNESS:
1365             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Brightness, value, VideoProcAmp_Flags_Manual);
1366         case CV_CAP_PROP_CONTRAST:
1367             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Contrast, value, VideoProcAmp_Flags_Manual);
1368         case CV_CAP_PROP_SATURATION:
1369             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Saturation, value, VideoProcAmp_Flags_Manual);
1370         case CV_CAP_PROP_HUE:
1371             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Hue, value, VideoProcAmp_Flags_Manual);
1372         case CV_CAP_PROP_GAIN:
1373             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Gain, value, VideoProcAmp_Flags_Manual);
1374         case CV_CAP_PROP_SHARPNESS:
1375             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Sharpness, value, VideoProcAmp_Flags_Manual);
1376         case CV_CAP_PROP_GAMMA:
1377             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_Gamma, value, VideoProcAmp_Flags_Manual);
1378         case CV_CAP_PROP_BACKLIGHT:
1379             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_BacklightCompensation, value, VideoProcAmp_Flags_Manual);
1380         case CV_CAP_PROP_MONOCHROME:
1381             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_ColorEnable, value, VideoProcAmp_Flags_Manual);
1382         case CV_CAP_PROP_TEMPERATURE:
1383             return writeComplexProperty<IAMVideoProcAmp>(VideoProcAmp_WhiteBalance, value, VideoProcAmp_Flags_Manual);
1384         case CV_CAP_PROP_PAN:
1385             return writeComplexProperty<IAMCameraControl>(CameraControl_Pan, value, CameraControl_Flags_Manual);
1386         case CV_CAP_PROP_TILT:
1387             return writeComplexProperty<IAMCameraControl>(CameraControl_Tilt, value, CameraControl_Flags_Manual);
1388         case CV_CAP_PROP_ROLL:
1389             return writeComplexProperty<IAMCameraControl>(CameraControl_Roll, value, CameraControl_Flags_Manual);
1390         case CV_CAP_PROP_IRIS:
1391             return writeComplexProperty<IAMCameraControl>(CameraControl_Iris, value, CameraControl_Flags_Manual);
1392         case CV_CAP_PROP_EXPOSURE:
1393             return writeComplexProperty<IAMCameraControl>(CameraControl_Exposure, value, CameraControl_Flags_Manual);
1394         case CV_CAP_PROP_AUTO_EXPOSURE:
1395             return writeComplexProperty<IAMCameraControl>(CameraControl_Exposure, value, value != 0 ? VideoProcAmp_Flags_Auto : VideoProcAmp_Flags_Manual);
1396         case CV_CAP_PROP_ZOOM:
1397             return writeComplexProperty<IAMCameraControl>(CameraControl_Zoom, value, CameraControl_Flags_Manual);
1398         case CV_CAP_PROP_FOCUS:
1399             return writeComplexProperty<IAMCameraControl>(CameraControl_Focus, value, CameraControl_Flags_Manual);
1400         case CV_CAP_PROP_AUTOFOCUS:
1401             return writeComplexProperty<IAMCameraControl>(CameraControl_Focus, value, value != 0 ? CameraControl_Flags_Auto : CameraControl_Flags_Manual);
1402         case CV_CAP_PROP_WHITE_BALANCE_BLUE_U:
1403         case CV_CAP_PROP_WHITE_BALANCE_RED_V:
1404         case CV_CAP_PROP_RECTIFICATION:
1405         case CV_CAP_PROP_TRIGGER:
1406         case CV_CAP_PROP_TRIGGER_DELAY:
1407         case CV_CAP_PROP_GUID:
1408         case CV_CAP_PROP_ISO_SPEED:
1409         case CV_CAP_PROP_SETTINGS:
1410         case CV_CAP_PROP_BUFFERSIZE:
1411         default:
1412             break;
1413         }
1414     return false;
1415 }
1416
1417 cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF( int index )
1418 {
1419     cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>();
1420     if (capture)
1421     {
1422         capture->open(index);
1423         if (capture->isOpened())
1424             return capture;
1425     }
1426     return cv::Ptr<cv::IVideoCapture>();
1427 }
1428
1429 cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF (const cv::String& filename)
1430 {
1431     cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>();
1432     if (capture)
1433     {
1434         capture->open(filename);
1435         if (capture->isOpened())
1436             return capture;
1437     }
1438     return cv::Ptr<cv::IVideoCapture>();
1439 }
1440
1441 //
1442 //
1443 // Media Foundation-based Video Writer
1444 //
1445 //
1446
1447 class CvVideoWriter_MSMF : public cv::IVideoWriter
1448 {
1449 public:
1450     CvVideoWriter_MSMF();
1451     virtual ~CvVideoWriter_MSMF();
1452     virtual bool open(const cv::String& filename, int fourcc,
1453                       double fps, cv::Size frameSize, bool isColor);
1454     virtual void close();
1455     virtual void write(cv::InputArray);
1456
1457     virtual double getProperty(int) const { return 0; }
1458     virtual bool setProperty(int, double) { return false; }
1459     virtual bool isOpened() const { return initiated; }
1460
1461     int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_MSMF; }
1462 private:
1463     Media_Foundation& MF;
1464     UINT32 videoWidth;
1465     UINT32 videoHeight;
1466     double fps;
1467     UINT32 bitRate;
1468     UINT32 frameSize;
1469     GUID   encodingFormat;
1470     GUID   inputFormat;
1471
1472     DWORD  streamIndex;
1473     _ComPtr<IMFSinkWriter> sinkWriter;
1474
1475     bool   initiated;
1476
1477     LONGLONG rtStart;
1478     UINT64 rtDuration;
1479
1480     static const GUID FourCC2GUID(int fourcc);
1481 };
1482
1483 CvVideoWriter_MSMF::CvVideoWriter_MSMF():
1484     MF(Media_Foundation::getInstance()),
1485     videoWidth(0),
1486     videoHeight(0),
1487     fps(0),
1488     bitRate(0),
1489     frameSize(0),
1490     encodingFormat(),
1491     inputFormat(),
1492     streamIndex(0),
1493     initiated(false),
1494     rtStart(0),
1495     rtDuration(0)
1496 {
1497 }
1498
1499 CvVideoWriter_MSMF::~CvVideoWriter_MSMF()
1500 {
1501     close();
1502 }
1503
1504 const GUID CvVideoWriter_MSMF::FourCC2GUID(int fourcc)
1505 {
1506     switch(fourcc)
1507     {
1508         case CV_FOURCC_MACRO('d', 'v', '2', '5'):
1509             return MFVideoFormat_DV25; break;
1510         case CV_FOURCC_MACRO('d', 'v', '5', '0'):
1511             return MFVideoFormat_DV50; break;
1512         case CV_FOURCC_MACRO('d', 'v', 'c', ' '):
1513             return MFVideoFormat_DVC; break;
1514         case CV_FOURCC_MACRO('d', 'v', 'h', '1'):
1515             return MFVideoFormat_DVH1; break;
1516         case CV_FOURCC_MACRO('d', 'v', 'h', 'd'):
1517             return MFVideoFormat_DVHD; break;
1518         case CV_FOURCC_MACRO('d', 'v', 's', 'd'):
1519             return MFVideoFormat_DVSD; break;
1520         case CV_FOURCC_MACRO('d', 'v', 's', 'l'):
1521                 return MFVideoFormat_DVSL; break;
1522 #if (WINVER >= 0x0602)
1523         case CV_FOURCC_MACRO('H', '2', '6', '3'):   // Available only for Win 8 target.
1524                 return MFVideoFormat_H263; break;
1525 #endif
1526         case CV_FOURCC_MACRO('H', '2', '6', '4'):
1527                 return MFVideoFormat_H264; break;
1528         case CV_FOURCC_MACRO('M', '4', 'S', '2'):
1529                 return MFVideoFormat_M4S2; break;
1530         case CV_FOURCC_MACRO('M', 'J', 'P', 'G'):
1531                 return MFVideoFormat_MJPG; break;
1532         case CV_FOURCC_MACRO('M', 'P', '4', '3'):
1533                 return MFVideoFormat_MP43; break;
1534         case CV_FOURCC_MACRO('M', 'P', '4', 'S'):
1535                 return MFVideoFormat_MP4S; break;
1536         case CV_FOURCC_MACRO('M', 'P', '4', 'V'):
1537                 return MFVideoFormat_MP4V; break;
1538         case CV_FOURCC_MACRO('M', 'P', 'G', '1'):
1539                 return MFVideoFormat_MPG1; break;
1540         case CV_FOURCC_MACRO('M', 'S', 'S', '1'):
1541                 return MFVideoFormat_MSS1; break;
1542         case CV_FOURCC_MACRO('M', 'S', 'S', '2'):
1543                 return MFVideoFormat_MSS2; break;
1544         case CV_FOURCC_MACRO('W', 'M', 'V', '1'):
1545                 return MFVideoFormat_WMV1; break;
1546         case CV_FOURCC_MACRO('W', 'M', 'V', '2'):
1547                 return MFVideoFormat_WMV2; break;
1548         case CV_FOURCC_MACRO('W', 'M', 'V', '3'):
1549                 return MFVideoFormat_WMV3; break;
1550         case CV_FOURCC_MACRO('W', 'V', 'C', '1'):
1551                 return MFVideoFormat_WVC1; break;
1552         default:
1553             return MFVideoFormat_H264;
1554     }
1555 }
1556
1557 bool CvVideoWriter_MSMF::open( const cv::String& filename, int fourcc,
1558                                double _fps, cv::Size _frameSize, bool /*isColor*/ )
1559 {
1560     if (initiated)
1561         close();
1562     videoWidth = _frameSize.width;
1563     videoHeight = _frameSize.height;
1564     fps = _fps;
1565     bitRate = (UINT32)fps*videoWidth*videoHeight; // 1-bit per pixel
1566     encodingFormat = FourCC2GUID(fourcc);
1567     inputFormat = MFVideoFormat_RGB32;
1568
1569     _ComPtr<IMFMediaType>  mediaTypeOut;
1570     _ComPtr<IMFMediaType>  mediaTypeIn;
1571     _ComPtr<IMFAttributes> spAttr;
1572     if (// Set the output media type.
1573         SUCCEEDED(MFCreateMediaType(&mediaTypeOut)) &&
1574         SUCCEEDED(mediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)) &&
1575         SUCCEEDED(mediaTypeOut->SetGUID(MF_MT_SUBTYPE, encodingFormat)) &&
1576         SUCCEEDED(mediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, bitRate)) &&
1577         SUCCEEDED(mediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)) &&
1578         SUCCEEDED(MFSetAttributeSize(mediaTypeOut.Get(), MF_MT_FRAME_SIZE, videoWidth, videoHeight)) &&
1579         SUCCEEDED(MFSetAttributeRatio(mediaTypeOut.Get(), MF_MT_FRAME_RATE, (UINT32)(fps * 1000), 1000)) &&
1580         SUCCEEDED(MFSetAttributeRatio(mediaTypeOut.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1)) &&
1581         // Set the input media type.
1582         SUCCEEDED(MFCreateMediaType(&mediaTypeIn)) &&
1583         SUCCEEDED(mediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)) &&
1584         SUCCEEDED(mediaTypeIn->SetGUID(MF_MT_SUBTYPE, inputFormat)) &&
1585         SUCCEEDED(mediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)) &&
1586         SUCCEEDED(mediaTypeIn->SetUINT32(MF_MT_DEFAULT_STRIDE, 4 * videoWidth)) && //Assume BGR32 input
1587         SUCCEEDED(MFSetAttributeSize(mediaTypeIn.Get(), MF_MT_FRAME_SIZE, videoWidth, videoHeight)) &&
1588         SUCCEEDED(MFSetAttributeRatio(mediaTypeIn.Get(), MF_MT_FRAME_RATE, (UINT32)(fps * 1000), 1000)) &&
1589         SUCCEEDED(MFSetAttributeRatio(mediaTypeIn.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1)) &&
1590         // Set sink writer parameters
1591         SUCCEEDED(MFCreateAttributes(&spAttr, 10)) &&
1592         SUCCEEDED(spAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)) &&
1593         SUCCEEDED(spAttr->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, true))
1594         )
1595     {
1596         // Create the sink writer
1597         cv::AutoBuffer<wchar_t> unicodeFileName(filename.length() + 1);
1598         MultiByteToWideChar(CP_ACP, 0, filename.c_str(), -1, unicodeFileName.data(), (int)filename.length() + 1);
1599         HRESULT hr = MFCreateSinkWriterFromURL(unicodeFileName.data(), NULL, spAttr.Get(), &sinkWriter);
1600         if (SUCCEEDED(hr))
1601         {
1602             // Configure the sink writer and tell it start to start accepting data
1603             if (SUCCEEDED(sinkWriter->AddStream(mediaTypeOut.Get(), &streamIndex)) &&
1604                 SUCCEEDED(sinkWriter->SetInputMediaType(streamIndex, mediaTypeIn.Get(), NULL)) &&
1605                 SUCCEEDED(sinkWriter->BeginWriting()))
1606             {
1607                 initiated = true;
1608                 rtStart = 0;
1609                 MFFrameRateToAverageTimePerFrame((UINT32)(fps * 1000), 1000, &rtDuration);
1610                 return true;
1611             }
1612         }
1613     }
1614
1615     return false;
1616 }
1617
1618 void CvVideoWriter_MSMF::close()
1619 {
1620     if (initiated)
1621     {
1622         initiated = false;
1623         sinkWriter->Finalize();
1624         sinkWriter.Release();
1625     }
1626 }
1627
1628 void CvVideoWriter_MSMF::write(cv::InputArray img)
1629 {
1630     if (img.empty() ||
1631         (img.channels() != 1 && img.channels() != 3 && img.channels() != 4) ||
1632         (UINT32)img.cols() != videoWidth || (UINT32)img.rows() != videoHeight)
1633         return;
1634
1635     const LONG cbWidth = 4 * videoWidth;
1636     const DWORD cbBuffer = cbWidth * videoHeight;
1637     _ComPtr<IMFSample> sample;
1638     _ComPtr<IMFMediaBuffer> buffer;
1639     BYTE *pData = NULL;
1640     // Prepare a media sample.
1641     if (SUCCEEDED(MFCreateSample(&sample)) &&
1642         // Set sample time stamp and duration.
1643         SUCCEEDED(sample->SetSampleTime(rtStart)) &&
1644         SUCCEEDED(sample->SetSampleDuration(rtDuration)) &&
1645         // Create a memory buffer.
1646         SUCCEEDED(MFCreateMemoryBuffer(cbBuffer, &buffer)) &&
1647         // Set the data length of the buffer.
1648         SUCCEEDED(buffer->SetCurrentLength(cbBuffer)) &&
1649         // Add the buffer to the sample.
1650         SUCCEEDED(sample->AddBuffer(buffer.Get())) &&
1651         // Lock the buffer.
1652         SUCCEEDED(buffer->Lock(&pData, NULL, NULL)))
1653     {
1654         // Copy the video frame to the buffer.
1655         cv::cvtColor(img.getMat(), cv::Mat(videoHeight, videoWidth, CV_8UC4, pData, cbWidth), img.channels() > 1 ? cv::COLOR_BGR2BGRA : cv::COLOR_GRAY2BGRA);
1656         buffer->Unlock();
1657         // Send media sample to the Sink Writer.
1658         if (SUCCEEDED(sinkWriter->WriteSample(streamIndex, sample.Get())))
1659         {
1660             rtStart += rtDuration;
1661         }
1662     }
1663 }
1664
1665 cv::Ptr<cv::IVideoWriter> cv::cvCreateVideoWriter_MSMF( const std::string& filename, int fourcc,
1666                                                         double fps, const cv::Size& frameSize,
1667                                                         const VideoWriterParameters& params)
1668 {
1669     cv::Ptr<CvVideoWriter_MSMF> writer = cv::makePtr<CvVideoWriter_MSMF>();
1670     if (writer)
1671     {
1672         const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true);
1673         writer->open(filename, fourcc, fps, frameSize, isColor);
1674         if (writer->isOpened())
1675             return writer;
1676     }
1677     return cv::Ptr<cv::IVideoWriter>();
1678 }
1679
1680 #if defined(BUILD_PLUGIN)
1681
1682 #include "plugin_api.hpp"
1683
1684 namespace cv {
1685
1686 typedef CvCapture_MSMF CaptureT;
1687 typedef CvVideoWriter_MSMF WriterT;
1688
1689 static
1690 CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_OUT CvPluginCapture* handle)
1691 {
1692     if (!handle)
1693         return CV_ERROR_FAIL;
1694     *handle = NULL;
1695     if (!filename)
1696         return CV_ERROR_FAIL;
1697     CaptureT* cap = 0;
1698     try
1699     {
1700         cap = new CaptureT();
1701         bool res;
1702         if (filename)
1703             res = cap->open(std::string(filename));
1704         else
1705             res = cap->open(camera_index);
1706         if (res)
1707         {
1708             *handle = (CvPluginCapture)cap;
1709             return CV_ERROR_OK;
1710         }
1711     }
1712     catch (...)
1713     {
1714     }
1715     if (cap)
1716         delete cap;
1717     return CV_ERROR_FAIL;
1718 }
1719
1720 static
1721 CvResult CV_API_CALL cv_capture_release(CvPluginCapture handle)
1722 {
1723     if (!handle)
1724         return CV_ERROR_FAIL;
1725     CaptureT* instance = (CaptureT*)handle;
1726     delete instance;
1727     return CV_ERROR_OK;
1728 }
1729
1730
1731 static
1732 CvResult CV_API_CALL cv_capture_get_prop(CvPluginCapture handle, int prop, CV_OUT double* val)
1733 {
1734     if (!handle)
1735         return CV_ERROR_FAIL;
1736     if (!val)
1737         return CV_ERROR_FAIL;
1738     try
1739     {
1740         CaptureT* instance = (CaptureT*)handle;
1741         *val = instance->getProperty(prop);
1742         return CV_ERROR_OK;
1743     }
1744     catch (...)
1745     {
1746         return CV_ERROR_FAIL;
1747     }
1748 }
1749
1750 static
1751 CvResult CV_API_CALL cv_capture_set_prop(CvPluginCapture handle, int prop, double val)
1752 {
1753     if (!handle)
1754         return CV_ERROR_FAIL;
1755     try
1756     {
1757         CaptureT* instance = (CaptureT*)handle;
1758         return instance->setProperty(prop, val) ? CV_ERROR_OK : CV_ERROR_FAIL;
1759     }
1760     catch (...)
1761     {
1762         return CV_ERROR_FAIL;
1763     }
1764 }
1765
1766 static
1767 CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle)
1768 {
1769     if (!handle)
1770         return CV_ERROR_FAIL;
1771     try
1772     {
1773         CaptureT* instance = (CaptureT*)handle;
1774         return instance->grabFrame() ? CV_ERROR_OK : CV_ERROR_FAIL;
1775     }
1776     catch (...)
1777     {
1778         return CV_ERROR_FAIL;
1779     }
1780 }
1781
1782 static
1783 CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata)
1784 {
1785     if (!handle)
1786         return CV_ERROR_FAIL;
1787     try
1788     {
1789         CaptureT* instance = (CaptureT*)handle;
1790         Mat img;
1791         if (instance->retrieveFrame(stream_idx, img))
1792             return callback(stream_idx, img.data, (int)img.step, img.cols, img.rows, img.channels(), userdata);
1793         return CV_ERROR_FAIL;
1794     }
1795     catch (...)
1796     {
1797         return CV_ERROR_FAIL;
1798     }
1799 }
1800
1801 static
1802 CvResult CV_API_CALL cv_writer_open(const char* filename, int fourcc, double fps, int width, int height, int isColor, CV_OUT CvPluginWriter* handle)
1803 {
1804     WriterT* wrt = 0;
1805     try
1806     {
1807         wrt = new WriterT();
1808         Size sz(width, height);
1809         if (wrt && wrt->open(filename, fourcc, fps, sz, isColor != 0))
1810         {
1811             *handle = (CvPluginWriter)wrt;
1812             return CV_ERROR_OK;
1813         }
1814     }
1815     catch (...)
1816     {
1817     }
1818     if (wrt)
1819         delete wrt;
1820     return CV_ERROR_FAIL;
1821 }
1822
1823 static
1824 CvResult CV_API_CALL cv_writer_release(CvPluginWriter handle)
1825 {
1826     if (!handle)
1827         return CV_ERROR_FAIL;
1828     WriterT* instance = (WriterT*)handle;
1829     delete instance;
1830     return CV_ERROR_OK;
1831 }
1832
1833 static
1834 CvResult CV_API_CALL cv_writer_get_prop(CvPluginWriter /*handle*/, int /*prop*/, CV_OUT double* /*val*/)
1835 {
1836     return CV_ERROR_FAIL;
1837 }
1838
1839 static
1840 CvResult CV_API_CALL cv_writer_set_prop(CvPluginWriter /*handle*/, int /*prop*/, double /*val*/)
1841 {
1842     return CV_ERROR_FAIL;
1843 }
1844
1845 static
1846 CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char* data, int step, int width, int height, int cn)
1847 {
1848     if (!handle)
1849         return CV_ERROR_FAIL;
1850     try
1851     {
1852         CV_Assert(step >= 0);
1853         WriterT* instance = (WriterT*)handle;
1854         Size sz(width, height);
1855         Mat img(sz, CV_MAKETYPE(CV_8U, cn), (void*)data, (size_t)step);
1856         instance->write(img);
1857         return CV_ERROR_OK;
1858     }
1859     catch (...)
1860     {
1861         return CV_ERROR_FAIL;
1862     }
1863 }
1864
1865 static const OpenCV_VideoIO_Plugin_API_preview plugin_api_v0 =
1866 {
1867     {
1868         sizeof(OpenCV_VideoIO_Plugin_API_preview), ABI_VERSION, API_VERSION,
1869         CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
1870         "Microsoft Media Foundation OpenCV Video I/O plugin"
1871     },
1872     /*  1*/CAP_MSMF,
1873     /*  2*/cv_capture_open,
1874     /*  3*/cv_capture_release,
1875     /*  4*/cv_capture_get_prop,
1876     /*  5*/cv_capture_set_prop,
1877     /*  6*/cv_capture_grab,
1878     /*  7*/cv_capture_retrieve,
1879     /*  8*/cv_writer_open,
1880     /*  9*/cv_writer_release,
1881     /* 10*/cv_writer_get_prop,
1882     /* 11*/cv_writer_set_prop,
1883     /* 12*/cv_writer_write
1884 };
1885
1886 } // namespace
1887
1888 const OpenCV_VideoIO_Plugin_API_preview* opencv_videoio_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
1889 {
1890     if (requested_abi_version != 0)
1891         return NULL;
1892     if (requested_api_version != 0)
1893         return NULL;
1894     return &cv::plugin_api_v0;
1895 }
1896
1897 #endif // BUILD_PLUGIN