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