|
|
@ -93,6 +93,8 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include <comdef.h> |
|
|
|
#include <comdef.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <shlwapi.h> // QISearch |
|
|
|
|
|
|
|
|
|
|
|
struct IMFMediaType; |
|
|
|
struct IMFMediaType; |
|
|
|
struct IMFActivate; |
|
|
|
struct IMFActivate; |
|
|
|
struct IMFMediaSource; |
|
|
|
struct IMFMediaSource; |
|
|
@ -595,6 +597,77 @@ void MediaType::Clear() |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SourceReaderCB : public IMFSourceReaderCallback |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
SourceReaderCB() : |
|
|
|
|
|
|
|
m_nRefCount(1), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_dwStreamIndex(0) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// IUnknown methods
|
|
|
|
|
|
|
|
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) CV_OVERRIDE |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
#ifdef _MSC_VER |
|
|
|
|
|
|
|
#pragma warning(push) |
|
|
|
|
|
|
|
#pragma warning(disable:4838) |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
static const QITAB qit[] = |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
QITABENT(SourceReaderCB, IMFSourceReaderCallback), |
|
|
|
|
|
|
|
{ 0 }, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
#ifdef _MSC_VER |
|
|
|
|
|
|
|
#pragma warning(pop) |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
return QISearch(this, qit, iid, ppv); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
STDMETHODIMP_(ULONG) AddRef() CV_OVERRIDE |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return InterlockedIncrement(&m_nRefCount); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
STDMETHODIMP_(ULONG) Release() CV_OVERRIDE |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ULONG uCount = InterlockedDecrement(&m_nRefCount); |
|
|
|
|
|
|
|
if (uCount == 0) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
delete this; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return uCount; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) CV_OVERRIDE; |
|
|
|
|
|
|
|
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) CV_OVERRIDE |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return S_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
STDMETHODIMP OnFlush(DWORD) CV_OVERRIDE |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return S_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT Wait(DWORD dwMilliseconds, _ComPtr<IMFSample>& videoSample, BOOL& pbEOS); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
// Destructor is private. Caller should call Release.
|
|
|
|
|
|
|
|
virtual ~SourceReaderCB() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_WARNING(NULL, "terminating async callback"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
long m_nRefCount; // Reference count.
|
|
|
|
|
|
|
|
cv::Mutex m_mutex; |
|
|
|
|
|
|
|
HANDLE m_hEvent; |
|
|
|
|
|
|
|
BOOL m_bEOS; |
|
|
|
|
|
|
|
HRESULT m_hrStatus; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_ComPtr<IMFSourceReader> m_reader; |
|
|
|
|
|
|
|
DWORD m_dwStreamIndex; |
|
|
|
|
|
|
|
_ComPtr<IMFSample> m_lastSample; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******* Capturing video from camera or file via Microsoft Media Foundation **********/ |
|
|
|
/******* Capturing video from camera or file via Microsoft Media Foundation **********/ |
|
|
|
class CvCapture_MSMF : public cv::IVideoCapture |
|
|
|
class CvCapture_MSMF : public cv::IVideoCapture |
|
|
|
{ |
|
|
|
{ |
|
|
@ -643,6 +716,7 @@ protected: |
|
|
|
_ComPtr<IMFSample> videoSample; |
|
|
|
_ComPtr<IMFSample> videoSample; |
|
|
|
LONGLONG sampleTime; |
|
|
|
LONGLONG sampleTime; |
|
|
|
bool isOpen; |
|
|
|
bool isOpen; |
|
|
|
|
|
|
|
_ComPtr<IMFSourceReaderCallback> readCallback; // non-NULL for "live" streams (camera capture)
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
CvCapture_MSMF::CvCapture_MSMF(): |
|
|
|
CvCapture_MSMF::CvCapture_MSMF(): |
|
|
@ -686,6 +760,7 @@ void CvCapture_MSMF::close() |
|
|
|
camid = -1; |
|
|
|
camid = -1; |
|
|
|
filename.clear(); |
|
|
|
filename.clear(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
readCallback.Release(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool CvCapture_MSMF::configureHW(bool enable) |
|
|
|
bool CvCapture_MSMF::configureHW(bool enable) |
|
|
@ -887,6 +962,14 @@ bool CvCapture_MSMF::open(int _index) |
|
|
|
if (D3DMgr) |
|
|
|
if (D3DMgr) |
|
|
|
srAttr->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, D3DMgr.Get()); |
|
|
|
srAttr->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, D3DMgr.Get()); |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
readCallback = ComPtr<IMFSourceReaderCallback>(new SourceReaderCB()); |
|
|
|
|
|
|
|
HRESULT hr = srAttr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, (IMFSourceReaderCallback*)readCallback.Get()); |
|
|
|
|
|
|
|
if (FAILED(hr)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
readCallback.Release(); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(MFCreateSourceReaderFromMediaSource(mSrc.Get(), srAttr.Get(), &videoFileSource))) |
|
|
|
if (SUCCEEDED(MFCreateSourceReaderFromMediaSource(mSrc.Get(), srAttr.Get(), &videoFileSource))) |
|
|
|
{ |
|
|
|
{ |
|
|
|
isOpen = true; |
|
|
|
isOpen = true; |
|
|
@ -958,10 +1041,116 @@ bool CvCapture_MSMF::open(const cv::String& _filename) |
|
|
|
return isOpen; |
|
|
|
return isOpen; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT SourceReaderCB::Wait(DWORD dwMilliseconds, _ComPtr<IMFSample>& videoSample, BOOL& bEOS) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
bEOS = FALSE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DWORD dwResult = WaitForSingleObject(m_hEvent, dwMilliseconds); |
|
|
|
|
|
|
|
if (dwResult == WAIT_TIMEOUT) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return E_PENDING; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (dwResult != WAIT_OBJECT_0) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return HRESULT_FROM_WIN32(GetLastError()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!bEOS) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
cv::AutoLock lock(m_mutex); |
|
|
|
|
|
|
|
bEOS = m_bEOS; |
|
|
|
|
|
|
|
if (!bEOS) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
videoSample = m_lastSample; |
|
|
|
|
|
|
|
CV_Assert(videoSample); |
|
|
|
|
|
|
|
m_lastSample.Release(); |
|
|
|
|
|
|
|
ResetEvent(m_hEvent); // event is auto-reset, but we need this forced reset due time gap between wait() and mutex hold.
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return m_hrStatus; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STDMETHODIMP SourceReaderCB::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_UNUSED(llTimestamp); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT hr = 0; |
|
|
|
|
|
|
|
cv::AutoLock lock(m_mutex); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(hrStatus)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (pSample) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): got frame at " << llTimestamp); |
|
|
|
|
|
|
|
IMFSample* prev = m_lastSample.Get(); |
|
|
|
|
|
|
|
if (prev) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed)"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
m_lastSample = pSample; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_WARNING(NULL, "videoio(MSMF): OnReadSample() is called with error status: " << hrStatus); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Reached the end of the stream.
|
|
|
|
|
|
|
|
m_bEOS = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
m_hrStatus = hrStatus; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (FAILED(hr = m_reader->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL))) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_WARNING(NULL, "videoio(MSMF): async ReadSample() call is failed with error status: " << hr); |
|
|
|
|
|
|
|
m_bEOS = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pSample || m_bEOS) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
SetEvent(m_hEvent); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return S_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool CvCapture_MSMF::grabFrame() |
|
|
|
bool CvCapture_MSMF::grabFrame() |
|
|
|
{ |
|
|
|
{ |
|
|
|
CV_TRACE_FUNCTION(); |
|
|
|
CV_TRACE_FUNCTION(); |
|
|
|
if (isOpen) |
|
|
|
if (readCallback) // async "live" capture mode
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
HRESULT hr = 0; |
|
|
|
|
|
|
|
SourceReaderCB* reader = ((SourceReaderCB*)readCallback.Get()); |
|
|
|
|
|
|
|
if (!reader->m_reader) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Initiate capturing with async callback
|
|
|
|
|
|
|
|
reader->m_reader = videoFileSource; |
|
|
|
|
|
|
|
reader->m_dwStreamIndex = dwStreamIndex; |
|
|
|
|
|
|
|
if (FAILED(hr = videoFileSource->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL))) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_ERROR(NULL, "videoio(MSMF): can't grab frame - initial async ReadSample() call failed: " << hr); |
|
|
|
|
|
|
|
reader->m_reader = NULL; |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
BOOL bEOS = false; |
|
|
|
|
|
|
|
if (FAILED(hr = reader->Wait(10000, videoSample, bEOS))) // 10 sec
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_WARNING(NULL, "videoio(MSMF): can't grab frame. Error: " << hr); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (bEOS) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CV_LOG_WARNING(NULL, "videoio(MSMF): EOS signal. Capture stream is lost"); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (isOpen) |
|
|
|
{ |
|
|
|
{ |
|
|
|
DWORD streamIndex, flags; |
|
|
|
DWORD streamIndex, flags; |
|
|
|
videoSample.Release(); |
|
|
|
videoSample.Release(); |
|
|
|