diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 51ab0c3ef..a347a4dfc 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -9,6 +9,10 @@ ocv_add_module(highgui opencv_imgproc OPTIONAL opencv_androidcamera) ocv_clear_vars(GRFMT_LIBS) +if(HAVE_WINRT_CX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") +endif() + if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR) ocv_include_directories(${ZLIB_INCLUDE_DIRS}) list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES}) diff --git a/modules/highgui/src/cap_msmf.cpp b/modules/highgui/src/cap_msmf.cpp index df52f04bd..80b17cafa 100644 --- a/modules/highgui/src/cap_msmf.cpp +++ b/modules/highgui/src/cap_msmf.cpp @@ -75,9 +75,31 @@ #include using namespace Microsoft::WRL; +#include + +#ifdef HAVE_WINRT +#ifdef __cplusplus_winrt +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL::Wrappers; +#endif + struct IMFMediaType; +#ifndef HAVE_WINRT struct IMFActivate; struct IMFMediaSource; +#endif struct IMFAttributes; namespace @@ -105,6 +127,8 @@ private: DebugPrintOut(void); }; +#include "cap_msmf.hpp" + // Structure for collecting info about types of video, which are supported by current video device struct MediaType { @@ -171,45 +195,12 @@ private: RawImage(unsigned int size); }; -// Class for grabbing image from video stream -class ImageGrabber : public IMFSampleGrabberSinkCallback +class ImageGrabberCallback : public IMFSampleGrabberSinkCallback { public: - ~ImageGrabber(void); - HRESULT initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat); - HRESULT startGrabbing(void); void pauseGrabbing(); void resumeGrabbing(); - void stopGrabbing(); RawImage *getRawImage(); - // Function of creation of the instance of the class - static HRESULT CreateInstance(ImageGrabber **ppIG, unsigned int deviceID, bool synchronous = false); - - const HANDLE ig_hFrameReady; - const HANDLE ig_hFrameGrabbed; - const HANDLE ig_hFinish; - -private: - bool ig_RIE; - bool ig_Close; - bool ig_Synchronous; - long m_cRef; - unsigned int ig_DeviceID; - IMFMediaSource *ig_pSource; - IMFMediaSession *ig_pSession; - IMFTopology *ig_pTopology; - RawImage *ig_RIFirst; - RawImage *ig_RISecond; - RawImage *ig_RIOut; - ImageGrabber(unsigned int deviceID, bool synchronous); - HRESULT CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo); - HRESULT AddSourceNode(IMFTopology *pTopology, IMFMediaSource *pSource, - IMFPresentationDescriptor *pPD, IMFStreamDescriptor *pSD, IMFTopologyNode **ppNode); - HRESULT AddOutputNode(IMFTopology *pTopology, IMFActivate *pActivate, DWORD dwId, IMFTopologyNode **ppNode); - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID iid, void** ppv); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); // IMFClockStateSink methods STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset); STDMETHODIMP OnClockStop(MFTIME hnsSystemTime); @@ -222,6 +213,85 @@ private: LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, DWORD dwSampleSize); STDMETHODIMP OnShutdown(); + + const HANDLE ig_hFrameReady; + const HANDLE ig_hFrameGrabbed; + const HANDLE ig_hFinish; +protected: + ImageGrabberCallback(bool synchronous); + bool ig_RIE; + bool ig_Close; + bool ig_Synchronous; + long m_cRef; + + RawImage *ig_RIFirst; + RawImage *ig_RISecond; + RawImage *ig_RIOut; +}; + +#ifdef HAVE_WINRT +extern const __declspec(selectany) WCHAR RuntimeClass_CV_ImageGrabberWinRT[] = L"cv.ImageGrabberWinRT"; + +class ImageGrabberWinRT : + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>, + IMFSampleGrabberSinkCallback>, public ImageGrabberCallback +{ + InspectableClass(RuntimeClass_CV_ImageGrabberWinRT, BaseTrust) +public: + ImageGrabberWinRT(bool synchronous); + ~ImageGrabberWinRT(void); + + HRESULT initImageGrabber(MAKE_WRL_REF(_MediaCapture) pSource, + GUID VideoFormat); + HRESULT startGrabbing(MAKE_WRL_REF(_AsyncAction)* action); + HRESULT stopGrabbing(MAKE_WRL_REF(_AsyncAction)* action); + // IMFClockStateSink methods + STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) { return ImageGrabberCallback::OnClockStart(hnsSystemTime, llClockStartOffset); } + STDMETHODIMP OnClockStop(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockStop(hnsSystemTime); } + STDMETHODIMP OnClockPause(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockPause(hnsSystemTime); } + STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockRestart(hnsSystemTime); } + STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate) { return ImageGrabberCallback::OnClockSetRate(hnsSystemTime, flRate); } + // IMFSampleGrabberSinkCallback methods + STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock) { return ImageGrabberCallback::OnSetPresentationClock(pClock); } + STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, + LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, + DWORD dwSampleSize) { return ImageGrabberCallback::OnProcessSample(guidMajorMediaType, dwSampleFlags, llSampleTime, llSampleDuration, pSampleBuffer, dwSampleSize); } + STDMETHODIMP OnShutdown() { return ImageGrabberCallback::OnShutdown(); } + // Function of creation of the instance of the class + static HRESULT CreateInstance(ImageGrabberWinRT **ppIG, bool synchronous = false); +private: + MAKE_WRL_AGILE_REF(_MediaCapture) ig_pMedCapSource; + MediaSink* ig_pMediaSink; +}; +#endif + +// Class for grabbing image from video stream +class ImageGrabber : public ImageGrabberCallback +{ +public: + ~ImageGrabber(void); + HRESULT initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat); + HRESULT startGrabbing(void); + void stopGrabbing(); + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + // Function of creation of the instance of the class + static HRESULT CreateInstance(ImageGrabber **ppIG, unsigned int deviceID, bool synchronous = false); + +private: + unsigned int ig_DeviceID; + + IMFMediaSource *ig_pSource; + IMFMediaSession *ig_pSession; + IMFTopology *ig_pTopology; + ImageGrabber(unsigned int deviceID, bool synchronous); + HRESULT CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo); + HRESULT AddSourceNode(IMFTopology *pTopology, IMFMediaSource *pSource, + IMFPresentationDescriptor *pPD, IMFStreamDescriptor *pSD, IMFTopologyNode **ppNode); + HRESULT AddOutputNode(IMFTopology *pTopology, IMFActivate *pActivate, DWORD dwId, IMFTopologyNode **ppNode); }; /// Class for controlling of thread of the grabbing raw data from video device @@ -298,7 +368,19 @@ public: CamParametrs getParametrs(); void setParametrs(CamParametrs parametrs); void setEmergencyStopEvent(void *userData, void(*func)(int, void *)); +#ifdef HAVE_WINRT + long readInfoOfDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice, unsigned int Num); + void waitForDevice() + { + if (vd_pAction) { + HRESULT hr; + DO_ACTION_SYNCHRONOUSLY(hr, vd_pAction, GET_CURRENT_CONTEXT); + vd_pAction = nullptr; + } + } +#else long readInfoOfDevice(IMFActivate *pActivate, unsigned int Num); +#endif wchar_t *getName(); int getCountFormats(); unsigned int getWidth(); @@ -329,15 +411,29 @@ private: std::map vd_CaptureFormats; std::vector vd_CurrentFormats; IMFMediaSource *vd_pSource; +#ifdef HAVE_WINRT + MAKE_WRL_AGILE_REF(_MediaCapture) vd_pMedCap; + IMedCapFailHandler* vd_pMedCapFail; + ImageGrabberWinRT *vd_pImGr; + MAKE_WRL_REF(_AsyncAction) vd_pAction; + Concurrency::critical_section vd_lock; +#endif emergensyStopEventCallback vd_func; void *vd_userData; HRESULT enumerateCaptureFormats(IMFMediaSource *pSource); long setDeviceFormat(IMFMediaSource *pSource, unsigned long dwFormatIndex); void buildLibraryofTypes(); int findType(unsigned int size, unsigned int frameRate = 0); +#ifdef HAVE_WINRT + HRESULT enumerateCaptureFormats(MAKE_WRL_REF(_MediaCapture) pSource); + long setDeviceFormat(MAKE_WRL_REF(_MediaCapture) pSource, unsigned long dwFormatIndex, MAKE_WRL_REF(_AsyncAction)* pAction); + long resetDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice); + long checkDevice(_DeviceClass devClass, DEFINE_TASK* pTask, MAKE_WRL_REF(_IDeviceInformation)* ppDevice); +#else long resetDevice(IMFActivate *pActivate); - long initDevice(); long checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice); +#endif + long initDevice(); }; /// Class for managing of list of video devices @@ -345,13 +441,27 @@ class videoDevices { public: ~videoDevices(void); +#ifdef HAVE_WINRT + long initDevices(_DeviceClass devClass); + void waitInit() { + if (vds_enumTask) { + HRESULT hr; + DO_ACTION_SYNCHRONOUSLY(hr, vds_enumTask, GET_CURRENT_CONTEXT); + vds_enumTask = nullptr; + } + } +#else long initDevices(IMFAttributes *pAttributes); +#endif static videoDevices& getInstance(); videoDevice *getDevice(unsigned int i); unsigned int getCount(); void clearDevices(); private: UINT32 count; +#ifdef HAVE_WINRT + MAKE_WRL_REF(_AsyncAction) vds_enumTask; +#endif std::vector vds_Devices; videoDevices(void); }; @@ -414,6 +524,9 @@ public: bool setupDevice(int deviceID, unsigned int w, unsigned int h, unsigned int idealFramerate = 30); // Checking of recivig of new frame from video device with deviceID bool isFrameNew(int deviceID); +#ifdef HAVE_WINRT + void waitForDevice(int deviceID); +#endif // Writing of Raw Data pixels from video device with deviceID with correction of RedAndBlue flipping flipRedAndBlue and vertical flipping flipImage bool getPixels(int deviceID, unsigned char * pixels, bool flipRedAndBlue = false, bool flipImage = false); static void processPixels(unsigned char * src, unsigned char * dst, unsigned int width, unsigned int height, unsigned int bpp, bool bRGB, bool bFlip); @@ -884,12 +997,8 @@ FormatReader::~FormatReader(void) #define CHECK_HR(x) if (FAILED(x)) { goto done; } -ImageGrabber::ImageGrabber(unsigned int deviceID, bool synchronous): +ImageGrabberCallback::ImageGrabberCallback(bool synchronous): m_cRef(1), - ig_DeviceID(deviceID), - ig_pSource(NULL), - ig_pSession(NULL), - ig_pTopology(NULL), ig_RIE(true), ig_Close(false), ig_Synchronous(synchronous), @@ -898,6 +1007,14 @@ ImageGrabber::ImageGrabber(unsigned int deviceID, bool synchronous): ig_hFinish(CreateEvent(NULL, TRUE, FALSE, NULL)) {} +ImageGrabber::ImageGrabber(unsigned int deviceID, bool synchronous): + ImageGrabberCallback(synchronous), + ig_DeviceID(deviceID), + ig_pSource(NULL), + ig_pSession(NULL), + ig_pTopology(NULL) +{} + ImageGrabber::~ImageGrabber(void) { if (ig_pSession) @@ -917,9 +1034,158 @@ ImageGrabber::~ImageGrabber(void) SafeRelease(&ig_pTopology); DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Destroing instance of the ImageGrabber class\n", ig_DeviceID); + DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Destroying instance of the ImageGrabber class\n", ig_DeviceID); } +#ifdef HAVE_WINRT + +ImageGrabberWinRT::ImageGrabberWinRT(bool synchronous): + ImageGrabberCallback(synchronous), + ig_pMediaSink(NULL) +{ + ig_pMedCapSource = nullptr; +} + +ImageGrabberWinRT::~ImageGrabberWinRT(void) +{ + //stop must already be performed and complete by object owner + if (ig_pMediaSink != NULL) { + ((IMFMediaSink*)ig_pMediaSink)->Shutdown(); + } + SafeRelease(&ig_pMediaSink); + RELEASE_AGILE_WRL(ig_pMedCapSource) + + CloseHandle(ig_hFinish); + + if (ig_Synchronous) + { + CloseHandle(ig_hFrameReady); + CloseHandle(ig_hFrameGrabbed); + } + + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + + DPO->printOut(L"IMAGEGRABBER VIDEODEVICE: Destroying instance of the ImageGrabberWinRT class\n"); +} + +HRESULT ImageGrabberWinRT::initImageGrabber(MAKE_WRL_REF(_MediaCapture) pSource, + GUID VideoFormat) +{ + HRESULT hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(pSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_MEDIA_DEVICE_CONTROLLER(pDevCont, pMedDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pMedDevCont, GetMediaStreamProperties, pMedEncProps, hr, _VideoPreview) + if (FAILED(hr)) return hr; + GET_WRL_VIDEO_ENCODING_PROPERTIES(pMedEncProps, pVidProps, hr); + if (FAILED(hr)) return hr; + ComPtr pType = NULL; + hr = MediaSink::ConvertPropertiesToMediaType(DEREF_AS_NATIVE_WRL_OBJ(ABI::Windows::Media::MediaProperties::IMediaEncodingProperties, pMedEncProps), &pType); + if (FAILED(hr)) return hr; + MediaType MT = FormatReader::Read(pType.Get()); + unsigned int sizeRawImage = 0; + if(VideoFormat == MFVideoFormat_RGB24) + { + sizeRawImage = MT.MF_MT_FRAME_SIZE * 3; + } + else if(VideoFormat == MFVideoFormat_RGB32) + { + sizeRawImage = MT.MF_MT_FRAME_SIZE * 4; + } + sizeRawImage = MT.MF_MT_SAMPLE_SIZE; + CHECK_HR(hr = RawImage::CreateInstance(&ig_RIFirst, sizeRawImage)); + CHECK_HR(hr = RawImage::CreateInstance(&ig_RISecond, sizeRawImage)); + ig_RIOut = ig_RISecond; + ig_pMedCapSource = pSource; +done: + return hr; +} + +HRESULT ImageGrabberWinRT::stopGrabbing(MAKE_WRL_REF(_AsyncAction)* action) +{ + HRESULT hr = S_OK; + if (ig_pMedCapSource != nullptr) { + GET_WRL_VIDEO_PREVIEW(DEREF_AGILE_WRL_OBJ(ig_pMedCapSource), imedPrevCap, hr); + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncAction) pAction; + WRL_METHOD_BASE(imedPrevCap, StopPreviewAsync, pAction, hr) + if (SUCCEEDED(hr)) { + SAVE_CURRENT_CONTEXT(context); + *action = reinterpret_cast(BEGIN_CREATE_ASYNC(pAction, context, this) + HRESULT hr; + DO_ACTION_SYNCHRONOUSLY(hr, pAction, context); + SafeRelease(&ig_pMediaSink); + SetEvent(ig_hFinish); + END_CREATE_ASYNC(hr)); + } + } + return hr; +} + +HRESULT ImageGrabberWinRT::startGrabbing(MAKE_WRL_REF(_AsyncAction)* action) +{ + HRESULT hr = S_OK; + GET_WRL_VIDEO_PREVIEW(DEREF_AGILE_WRL_OBJ(ig_pMedCapSource), imedPrevCap, hr); + if (FAILED(hr)) return hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Foundation_Collections_PropertySet, MAKE_WRL_OBJ(_PropertySet), pSet, hr) + if (FAILED(hr)) return hr; + GET_WRL_MAP(pSet, spSetting, hr) + if (FAILED(hr)) return hr; + ACTIVATE_STATIC_OBJ(RuntimeClass_Windows_Foundation_PropertyValue, MAKE_WRL_OBJ(_PropertyValueStatics), spPropVal, hr) + if (FAILED(hr)) return hr; + _ObjectObj pVal; + boolean bReplaced; + WRL_METHOD(spPropVal, CreateUInt32, pVal, hr, (unsigned int)_VideoPreview) + if (FAILED(hr)) return hr; + WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_VIDTYPE)), DEREF_WRL_OBJ(pVal)) + if (FAILED(hr)) return hr; + WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_SAMPLEGRABBERCALLBACK)), reinterpret_cast<_Object>(this)) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(ig_pMedCapSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_MEDIA_DEVICE_CONTROLLER(pDevCont, pMedDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pMedDevCont, GetMediaStreamProperties, pMedEncProps, hr, _VideoPreview) + if (FAILED(hr)) return hr; + GET_WRL_VIDEO_ENCODING_PROPERTIES(pMedEncProps, pVidProps, hr); + if (FAILED(hr)) return hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile, MAKE_WRL_OBJ(_MediaEncodingProfile), pEncProps, hr) + if (FAILED(hr)) return hr; + WRL_PROP_PUT(pEncProps, Video, DEREF_WRL_OBJ(pVidProps), hr) + if (FAILED(hr)) return hr; + WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_VIDENCPROPS)), DEREF_WRL_OBJ(pVidProps)) + if (SUCCEEDED(hr)) { + //can start/stop multiple times with same MediaCapture object if using activatable class + WRL_METHOD(imedPrevCap, _StartPreviewToCustomSinkIdAsync, *action, hr, DEREF_WRL_OBJ(pEncProps), DEREF_WRL_OBJ(_StringReference(RuntimeClass_CV_MediaSink)), DEREF_WRL_OBJ(pSet)) + if (FAILED(hr) && hr == REGDB_E_CLASSNOTREG) { + hr = Microsoft::WRL::Make().CopyTo(&ig_pMediaSink); + if (FAILED(hr)) return hr; + hr = ((ABI::Windows::Media::IMediaExtension*)ig_pMediaSink)->SetProperties(DEREF_AS_NATIVE_WRL_OBJ(ABI::Windows::Foundation::Collections::IPropertySet, pSet)); + if (FAILED(hr)) return hr; + WRL_METHOD(imedPrevCap, StartPreviewToCustomSinkAsync, *action, hr, DEREF_WRL_OBJ(pEncProps), reinterpret_cast(ig_pMediaSink)) + } + } + return hr; +} + +HRESULT ImageGrabberWinRT::CreateInstance(ImageGrabberWinRT **ppIG, bool synchronous) +{ + *ppIG = Microsoft::WRL::Make(synchronous).Detach(); + if (ppIG == NULL) + { + return E_OUTOFMEMORY; + } + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"IMAGEGRABBER VIDEODEVICE: Creating instance of ImageGrabberWinRT\n"); + return S_OK; +} +#endif + HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat) { ComPtr pSinkActivate = NULL; @@ -975,6 +1241,7 @@ err: { sizeRawImage = MT.MF_MT_FRAME_SIZE * 4; } + //sizeRawImage = MT.MF_MT_SAMPLE_SIZE; CHECK_HR(hr = RawImage::CreateInstance(&ig_RIFirst, sizeRawImage)); CHECK_HR(hr = RawImage::CreateInstance(&ig_RISecond, sizeRawImage)); ig_RIOut = ig_RISecond; @@ -1003,7 +1270,6 @@ done: SafeRelease(&ig_pSession); SafeRelease(&ig_pTopology); } - return hr; } @@ -1017,11 +1283,11 @@ void ImageGrabber::stopGrabbing() HRESULT ImageGrabber::startGrabbing(void) { + HRESULT hr = S_OK; DebugPrintOut *DPO = &DebugPrintOut::getInstance(); ComPtr pEvent = NULL; PROPVARIANT var; PropVariantInit(&var); - HRESULT hr = S_OK; hr = ig_pSession->SetTopology(0, ig_pTopology); DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Start Grabbing of the images\n", ig_DeviceID); hr = ig_pSession->Start(&GUID_NULL, &var); @@ -1079,11 +1345,11 @@ done: return hr; } -void ImageGrabber::pauseGrabbing() +void ImageGrabberCallback::pauseGrabbing() { } -void ImageGrabber::resumeGrabbing() +void ImageGrabberCallback::resumeGrabbing() { } @@ -1218,45 +1484,45 @@ STDMETHODIMP_(ULONG) ImageGrabber::Release() return cRef; } -STDMETHODIMP ImageGrabber::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) +STDMETHODIMP ImageGrabberCallback::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) { (void)hnsSystemTime; (void)llClockStartOffset; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockStop(MFTIME hnsSystemTime) +STDMETHODIMP ImageGrabberCallback::OnClockStop(MFTIME hnsSystemTime) { (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockPause(MFTIME hnsSystemTime) +STDMETHODIMP ImageGrabberCallback::OnClockPause(MFTIME hnsSystemTime) { (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockRestart(MFTIME hnsSystemTime) +STDMETHODIMP ImageGrabberCallback::OnClockRestart(MFTIME hnsSystemTime) { (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockSetRate(MFTIME hnsSystemTime, float flRate) +STDMETHODIMP ImageGrabberCallback::OnClockSetRate(MFTIME hnsSystemTime, float flRate) { (void)flRate; (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnSetPresentationClock(IMFPresentationClock* pClock) +STDMETHODIMP ImageGrabberCallback::OnSetPresentationClock(IMFPresentationClock* pClock) { (void)pClock; return S_OK; } -STDMETHODIMP ImageGrabber::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, +STDMETHODIMP ImageGrabberCallback::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, DWORD dwSampleSize) { @@ -1298,13 +1564,13 @@ STDMETHODIMP ImageGrabber::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwS return S_OK; } -STDMETHODIMP ImageGrabber::OnShutdown() +STDMETHODIMP ImageGrabberCallback::OnShutdown() { SetEvent(ig_hFinish); return S_OK; } -RawImage *ImageGrabber::getRawImage() +RawImage *ImageGrabberCallback::getRawImage() { return ig_RIOut; } @@ -1330,7 +1596,7 @@ HRESULT ImageGrabberThread::CreateInstance(ImageGrabberThread **ppIGT, IMFMediaS return S_OK; } -ImageGrabberThread::ImageGrabberThread(IMFMediaSource *pSource, unsigned int deviceID, bool synchronious): +ImageGrabberThread::ImageGrabberThread(IMFMediaSource *pSource, unsigned int deviceID, bool synchronious) : igt_func(NULL), igt_Handle(NULL), igt_stop(false) @@ -1352,7 +1618,7 @@ ImageGrabberThread::ImageGrabberThread(IMFMediaSource *pSource, unsigned int dev } else { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i There is a problem with creation of the instance of the ImageGrabber class\n", deviceID); + DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: There is a problem with creation of the instance of the ImageGrabber class\n", deviceID); } } @@ -1450,6 +1716,10 @@ Media_Foundation::~Media_Foundation(void) bool Media_Foundation::buildListOfDevices() { HRESULT hr = S_OK; +#ifdef HAVE_WINRT + videoDevices *vDs = &videoDevices::getInstance(); + hr = vDs->initDevices(_VideoCapture); +#else ComPtr pAttributes = NULL; CoInitialize(NULL); hr = MFCreateAttributes(pAttributes.GetAddressOf(), 1); @@ -1465,7 +1735,8 @@ bool Media_Foundation::buildListOfDevices() videoDevices *vDs = &videoDevices::getInstance(); hr = vDs->initDevices(pAttributes.Get()); } - else +#endif + if (FAILED(hr)) { DebugPrintOut *DPO = &DebugPrintOut::getInstance(); DPO->printOut(L"MEDIA FOUNDATION: The access to the video cameras denied\n"); @@ -1532,8 +1803,14 @@ unsigned char * RawImage::getpPixels() } videoDevice::videoDevice(void): vd_IsSetuped(false), vd_LockOut(OpenLock), vd_pFriendlyName(NULL), - vd_Width(0), vd_Height(0), vd_pSource(NULL), vd_func(NULL), vd_userData(NULL) + vd_Width(0), vd_Height(0), vd_pSource(NULL), vd_pImGrTh(NULL), vd_func(NULL), vd_userData(NULL) { +#ifdef HAVE_WINRT + vd_pMedCap = nullptr; + vd_pMedCapFail = NULL; + vd_pImGr = NULL; + vd_pAction = nullptr; +#endif } void videoDevice::setParametrs(CamParametrs parametrs) @@ -1616,13 +1893,60 @@ CamParametrs videoDevice::getParametrs() return out; } +#ifdef HAVE_WINRT +long videoDevice::resetDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice) +#else long videoDevice::resetDevice(IMFActivate *pActivate) +#endif { HRESULT hr = -1; vd_CurrentFormats.clear(); if(vd_pFriendlyName) CoTaskMemFree(vd_pFriendlyName); vd_pFriendlyName = NULL; +#ifdef HAVE_WINRT + if (pDevice) + { + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCapture, MAKE_WRL_OBJ(_MediaCapture), pIMedCap, hr) + if (FAILED(hr)) return hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings, MAKE_WRL_OBJ(_MediaCaptureInitializationSettings), pCapInitSet, hr) + if (FAILED(hr)) return hr; + _StringObj str; + WRL_PROP_GET(pDevice, Name, *REF_WRL_OBJ(str), hr) + if (FAILED(hr)) return hr; + unsigned int length = 0; + PCWSTR wstr = WindowsGetStringRawBuffer(reinterpret_cast(DEREF_WRL_OBJ(str)), &length); + vd_pFriendlyName = (wchar_t*)CoTaskMemAlloc((length + 1) * sizeof(wchar_t)); + wcscpy(vd_pFriendlyName, wstr); + WRL_PROP_GET(pDevice, Id, *REF_WRL_OBJ(str), hr) + if (FAILED(hr)) return hr; + WRL_PROP_PUT(pCapInitSet, VideoDeviceId, DEREF_WRL_OBJ(str), hr) + if (FAILED(hr)) return hr; + WRL_PROP_PUT(pCapInitSet, StreamingCaptureMode, _Video, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncAction) pAction; + WRL_METHOD(DEREF_WRL_OBJ(pIMedCap), _InitializeWithSettingsAsync, pAction, hr, DEREF_WRL_OBJ(pCapInitSet)) + if (FAILED(hr)) return hr; + MAKE_WRL_AGILE_REF(_MediaCapture) pAgileMedCap; + pAgileMedCap = PREPARE_TRANSFER_WRL_OBJ(pIMedCap); + Concurrency::critical_section::scoped_lock _LockHolder(vd_lock); + MAKE_WRL_REF(_AsyncAction) pOldAction = vd_pAction; + SAVE_CURRENT_CONTEXT(context); + vd_pAction = reinterpret_cast(BEGIN_CREATE_ASYNC(pAction, pOldAction, context, &pAgileMedCap, this) + HRESULT hr; + if (pOldAction) DO_ACTION_SYNCHRONOUSLY(hr, pOldAction, GET_CURRENT_CONTEXT); + DO_ACTION_SYNCHRONOUSLY(hr, pAction, context); + if (SUCCEEDED(hr)) { + //all camera capture calls only in original context + BEGIN_CALL_IN_CONTEXT(hr, context, pAgileMedCap, this) + enumerateCaptureFormats(DEREF_AGILE_WRL_OBJ(pAgileMedCap)); + END_CALL_IN_CONTEXT(S_OK) + } + buildLibraryofTypes(); + RELEASE_AGILE_WRL(pAgileMedCap) + END_CREATE_ASYNC(hr)); + } +#else if(pActivate) { IMFMediaSource *pSource = NULL; @@ -1645,9 +1969,19 @@ long videoDevice::resetDevice(IMFActivate *pActivate) DPO->printOut(L"VIDEODEVICE %i: IMFMediaSource interface cannot be created \n", vd_CurrentNumber); } } +#endif return hr; } +#ifdef HAVE_WINRT +long videoDevice::readInfoOfDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice, unsigned int Num) +{ + HRESULT hr = -1; + vd_CurrentNumber = Num; + hr = resetDevice(pDevice); + return hr; +} +#else long videoDevice::readInfoOfDevice(IMFActivate *pActivate, unsigned int Num) { HRESULT hr = -1; @@ -1655,7 +1989,44 @@ long videoDevice::readInfoOfDevice(IMFActivate *pActivate, unsigned int Num) hr = resetDevice(pActivate); return hr; } +#endif +#ifdef HAVE_WINRT +long videoDevice::checkDevice(_DeviceClass devClass, DEFINE_TASK* pTask, MAKE_WRL_REF(_IDeviceInformation)* ppDevice) +{ + HRESULT hr = S_OK; + ACTIVATE_STATIC_OBJ(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation, MAKE_WRL_OBJ(_DeviceInformationStatics), pDevStat, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncOperation) pAction; + WRL_METHOD(pDevStat, _FindAllAsyncDeviceClass, pAction, hr, devClass) + if (SUCCEEDED(hr)) { + *pTask = CREATE_TASK([pAction, &ppDevice, this]() -> HRESULT { + HRESULT hr; + MAKE_WRL_OBJ(_VectorView) pVector; + DO_OPERATION_SYNCHRONOUSLY_VECTOR(hr, pAction, GET_CURRENT_CONTEXT, pVector, _VectorView, _DeviceInformation, _DeviceInformationCollection); + UINT32 count = 0; + if (SUCCEEDED(hr)) WRL_PROP_GET(pVector, Size, count, hr) + if (SUCCEEDED(hr) && count > 0) { + for (UINT32 i = 0; i < count; i++) { + MAKE_WRL_OBJ(_IDeviceInformation) pDevice; + WRL_METHOD(pVector, GetAt, pDevice, hr, i) + if (SUCCEEDED(hr)) { + _StringObj str; + unsigned int length = 0; + WRL_PROP_GET(pDevice, Name, *REF_WRL_OBJ(str), hr) + PCWSTR wstr = WindowsGetStringRawBuffer(reinterpret_cast(DEREF_WRL_OBJ(str)), &length); + if (wcscmp(wstr, vd_pFriendlyName) == 0) { + *ppDevice = PREPARE_TRANSFER_WRL_OBJ(pDevice); + } + } + } + } + return hr; + }); + } + return hr; +} +#else long videoDevice::checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice) { HRESULT hr = S_OK; @@ -1714,14 +2085,60 @@ long videoDevice::checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice) } return hr; } +#endif long videoDevice::initDevice() { - HRESULT hr = -1; + HRESULT hr = S_OK; + CoInitialize(NULL); +#ifdef HAVE_WINRT + Concurrency::critical_section::scoped_lock _LockHolder(vd_lock); + MAKE_WRL_REF(_AsyncAction) pOldAction = vd_pAction; + SAVE_CURRENT_CONTEXT(context); + vd_pAction = reinterpret_cast(BEGIN_CREATE_ASYNC(pOldAction, context, this) + HRESULT hr; + if (pOldAction) DO_ACTION_SYNCHRONOUSLY(hr, pOldAction, GET_CURRENT_CONTEXT); + DEFINE_TASK pTask; + MAKE_WRL_OBJ(_IDeviceInformation) pDevInfo; + hr = checkDevice(_VideoCapture, &pTask, REF_WRL_OBJ(pDevInfo)); + if (SUCCEEDED(hr)) hr = pTask.get(); + if (SUCCEEDED(hr)) { + MAKE_WRL_REF(_AsyncAction) pAction; + BEGIN_CALL_IN_CONTEXT(hr, context, pDevInfo, &pAction, context, this) + HRESULT hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCapture, MAKE_WRL_OBJ(_MediaCapture), pIMedCap, hr) + if (SUCCEEDED(hr)) { + RELEASE_WRL(vd_pMedCap); + vd_pMedCap = PREPARE_TRANSFER_WRL_OBJ(pIMedCap); + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings, MAKE_WRL_OBJ(_MediaCaptureInitializationSettings), pCapInitSet, hr) + _StringObj str; + if (SUCCEEDED(hr)) { + WRL_PROP_GET(pDevInfo, Id, *REF_WRL_OBJ(str), hr) + if (SUCCEEDED(hr)) { + WRL_PROP_PUT(pCapInitSet, VideoDeviceId, DEREF_WRL_OBJ(str), hr) + } + } + if (SUCCEEDED(hr)) + WRL_PROP_PUT(pCapInitSet, StreamingCaptureMode, _Video, hr) + if (SUCCEEDED(hr)) { + vd_pMedCapFail = create_medcapfailedhandler([this, context](){ + HRESULT hr; + BEGIN_CALL_IN_CONTEXT(hr, context, this) + closeDevice(); + END_CALL_IN_CONTEXT(S_OK) + }); + } + if (SUCCEEDED(hr)) hr = vd_pMedCapFail->AddHandler(reinterpret_cast(DEREF_AGILE_WRL_OBJ(vd_pMedCap))); + if (SUCCEEDED(hr)) WRL_METHOD(vd_pMedCap, _InitializeWithSettingsAsync, pAction, hr, DEREF_WRL_OBJ(pCapInitSet)) + } + END_CALL_IN_CONTEXT(hr) + DO_ACTION_SYNCHRONOUSLY(hr, pAction, context); + } + END_CREATE_ASYNC(hr)); +#else + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); ComPtr pAttributes = NULL; IMFActivate *vd_pActivate = NULL; - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - CoInitialize(NULL); hr = MFCreateAttributes(pAttributes.GetAddressOf(), 1); if (SUCCEEDED(hr)) { @@ -1754,7 +2171,7 @@ long videoDevice::initDevice() { DPO->printOut(L"VIDEODEVICE %i: The attribute of video cameras cannot be getting \n", vd_CurrentNumber); } - +#endif return hr; } @@ -1768,7 +2185,7 @@ MediaType videoDevice::getFormat(unsigned int id) } int videoDevice::getCountFormats() { - return vd_CurrentFormats.size(); + return (int)vd_CurrentFormats.size(); } void videoDevice::setEmergencyStopEvent(void *userData, void(*func)(int, void *)) { @@ -1780,6 +2197,30 @@ void videoDevice::closeDevice() if(vd_IsSetuped) { vd_IsSetuped = false; + +#ifdef HAVE_WINRT + if (DEREF_AGILE_WRL_OBJ(vd_pMedCap)) { + MAKE_WRL_REF(_AsyncAction) action; + Concurrency::critical_section::scoped_lock _LockHolder(vd_lock); + MAKE_WRL_REF(_AsyncAction) pOldAction = vd_pAction; + vd_pImGr->stopGrabbing(&action); + vd_pMedCapFail->RemoveHandler(reinterpret_cast(DEREF_AGILE_WRL_OBJ(vd_pMedCap))); + SafeRelease(&vd_pMedCapFail); + vd_pAction = reinterpret_cast(BEGIN_CREATE_ASYNC(action, pOldAction, this) + HRESULT hr; + if (pOldAction) DO_ACTION_SYNCHRONOUSLY(hr, pOldAction, GET_CURRENT_CONTEXT); + DO_ACTION_SYNCHRONOUSLY(hr, action, GET_CURRENT_CONTEXT); + RELEASE_WRL(vd_pMedCap) + if(vd_LockOut == RawDataLock) { + delete vd_pImGr; + } + vd_pImGr = NULL; + vd_LockOut = OpenLock; + END_CREATE_ASYNC(hr)); + return; + } +#endif + vd_pSource->Stop(); SafeRelease(&vd_pSource); if(vd_LockOut == RawDataLock) @@ -1884,6 +2325,26 @@ void videoDevice::buildLibraryofTypes() } } +#ifdef HAVE_WINRT +long videoDevice::setDeviceFormat(MAKE_WRL_REF(_MediaCapture) pSource, unsigned long dwFormatIndex, MAKE_WRL_REF(_AsyncAction)* pAction) +{ + HRESULT hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(pSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_MEDIA_DEVICE_CONTROLLER(pDevCont, pMedDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_VectorView) pVector; + WRL_METHOD(pMedDevCont, GetAvailableMediaStreamProperties, pVector, hr, _VideoPreview) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pVector, GetAt, pMedEncProps, hr, dwFormatIndex) + if (FAILED(hr)) return hr; + WRL_METHOD(pMedDevCont, SetMediaStreamPropertiesAsync, *pAction, hr, _VideoPreview, DEREF_WRL_OBJ(pMedEncProps)) + return hr; +} +#endif + long videoDevice::setDeviceFormat(IMFMediaSource *pSource, unsigned long dwFormatIndex) { ComPtr pPD = NULL; @@ -1925,6 +2386,9 @@ bool videoDevice::isDeviceSetup() RawImage * videoDevice::getRawImageOut() { if(!vd_IsSetuped) return NULL; +#ifdef HAVE_WINRT + if(vd_pImGr) return vd_pImGr->getRawImage(); +#endif if(vd_pImGrTh) return vd_pImGrTh->getImageGrabber()->getRawImage(); else @@ -1943,6 +2407,27 @@ bool videoDevice::isFrameNew() if(vd_LockOut == OpenLock) { vd_LockOut = RawDataLock; + + //must already be closed +#ifdef HAVE_WINRT + if (DEREF_AGILE_WRL_OBJ(vd_pMedCap)) { + MAKE_WRL_REF(_AsyncAction) action; + if (FAILED(ImageGrabberWinRT::CreateInstance(&vd_pImGr))) return false; + if (FAILED(vd_pImGr->initImageGrabber(DEREF_AGILE_WRL_OBJ(vd_pMedCap), MFVideoFormat_RGB24)) || FAILED(vd_pImGr->startGrabbing(&action))) { + delete vd_pImGr; + return false; + } + Concurrency::critical_section::scoped_lock _LockHolder(vd_lock); + MAKE_WRL_REF(_AsyncAction) pOldAction = vd_pAction; + SAVE_CURRENT_CONTEXT(context); + vd_pAction = reinterpret_cast(BEGIN_CREATE_ASYNC(action, pOldAction, context, this) + HRESULT hr; + if (pOldAction) DO_ACTION_SYNCHRONOUSLY(hr, pOldAction, GET_CURRENT_CONTEXT); + DO_ACTION_SYNCHRONOUSLY(hr, action, context); + END_CREATE_ASYNC(hr)); + return true; + } +#endif HRESULT hr = ImageGrabberThread::CreateInstance(&vd_pImGrTh, vd_pSource, vd_CurrentNumber); if(FAILED(hr)) { @@ -1954,6 +2439,10 @@ bool videoDevice::isFrameNew() vd_pImGrTh->start(); return true; } +#ifdef HAVE_WINRT + if(vd_pImGr) + return vd_pImGr->getRawImage()->isNew(); +#endif if(vd_pImGrTh) return vd_pImGrTh->getImageGrabber()->getRawImage()->isNew(); } @@ -1981,14 +2470,35 @@ bool videoDevice::setupDevice(unsigned int id) hr = initDevice(); if(SUCCEEDED(hr)) { +#ifdef HAVE_WINRT + Concurrency::critical_section::scoped_lock _LockHolder(vd_lock); + MAKE_WRL_REF(_AsyncAction) pOldAction = vd_pAction; + SAVE_CURRENT_CONTEXT(context); + vd_pAction = reinterpret_cast(BEGIN_CREATE_ASYNC(pOldAction, context, id, DPO, this) + HRESULT hr; + if (pOldAction) DO_ACTION_SYNCHRONOUSLY(hr, pOldAction, GET_CURRENT_CONTEXT); +#endif vd_Width = vd_CurrentFormats[id].width; vd_Height = vd_CurrentFormats[id].height; +#ifdef HAVE_WINRT + if (DEREF_AGILE_WRL_OBJ(vd_pMedCap)) { + MAKE_WRL_REF(_AsyncAction) pAction; + BEGIN_CALL_IN_CONTEXT(hr, context, id, &pAction, this) + END_CALL_IN_CONTEXT(setDeviceFormat(DEREF_AGILE_WRL_OBJ(vd_pMedCap), (DWORD) id, &pAction)) + if (SUCCEEDED(hr)) DO_ACTION_SYNCHRONOUSLY(hr, pAction, context); + } else +#endif hr = setDeviceFormat(vd_pSource, (DWORD) id); vd_IsSetuped = (SUCCEEDED(hr)); if(vd_IsSetuped) DPO->printOut(L"\n\nVIDEODEVICE %i: Device is setuped \n", vd_CurrentNumber); vd_PrevParametrs = getParametrs(); +#ifdef HAVE_WINRT + END_CREATE_ASYNC(hr)); + return true; +#else return vd_IsSetuped; +#endif } else { @@ -2017,11 +2527,43 @@ wchar_t *videoDevice::getName() videoDevice::~videoDevice(void) { closeDevice(); +#ifdef HAVE_WINRT + RELEASE_WRL(vd_pMedCap) +#endif SafeRelease(&vd_pSource); if(vd_pFriendlyName) CoTaskMemFree(vd_pFriendlyName); } +#ifdef HAVE_WINRT +HRESULT videoDevice::enumerateCaptureFormats(MAKE_WRL_REF(_MediaCapture) pSource) +{ + HRESULT hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(pSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_MEDIA_DEVICE_CONTROLLER(pDevCont, pMedDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_VectorView) pVector; + WRL_METHOD(pMedDevCont, GetAvailableMediaStreamProperties, pVector, hr, _VideoPreview) + if (FAILED(hr)) return hr; + UINT32 count; + WRL_PROP_GET(pVector, Size, count, hr) + if (FAILED(hr)) return hr; + for (UINT32 i = 0; i < count; i++) { + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pVector, GetAt, pMedEncProps, hr, i) + if (FAILED(hr)) return hr; + ComPtr pType = NULL; + hr = MediaSink::ConvertPropertiesToMediaType(DEREF_AS_NATIVE_WRL_OBJ(ABI::Windows::Media::MediaProperties::IMediaEncodingProperties, pMedEncProps), &pType); + if (FAILED(hr)) return hr; + MediaType MT = FormatReader::Read(pType.Get()); + vd_CurrentFormats.push_back(MT); + } + return hr; +} +#endif + HRESULT videoDevice::enumerateCaptureFormats(IMFMediaSource *pSource) { ComPtr pPD = NULL; @@ -2066,7 +2608,11 @@ done: } videoDevices::videoDevices(void): count(0) -{} +{ +#ifdef HAVE_WINRT + vds_enumTask = nullptr; +#endif +} void videoDevices::clearDevices() { @@ -2094,11 +2640,44 @@ videoDevice * videoDevices::getDevice(unsigned int i) return vds_Devices[i]; } +#ifdef HAVE_WINRT +long videoDevices::initDevices(_DeviceClass devClass) +{ + HRESULT hr = S_OK; + ACTIVATE_STATIC_OBJ(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation, MAKE_WRL_OBJ(_DeviceInformationStatics), pDevStat, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncOperation) pAction; + WRL_METHOD(pDevStat, _FindAllAsyncDeviceClass, pAction, hr, devClass) + if (SUCCEEDED(hr)) { + SAVE_CURRENT_CONTEXT(context); + vds_enumTask = reinterpret_cast(BEGIN_CREATE_ASYNC(pAction, context, this) + HRESULT hr; + MAKE_WRL_OBJ(_VectorView) pVector; + DO_OPERATION_SYNCHRONOUSLY_VECTOR(hr, pAction, GET_CURRENT_CONTEXT, pVector, _VectorView, _DeviceInformation, _DeviceInformationCollection); + if (SUCCEEDED(hr)) WRL_PROP_GET(pVector, Size, count, hr) + if (SUCCEEDED(hr) && count > 0) { + for (UINT32 i = 0; i < count; i++) { + videoDevice *vd = new videoDevice; + MAKE_WRL_OBJ(_IDeviceInformation) pDevice; + WRL_METHOD(pVector, GetAt, pDevice, hr, i) + if (SUCCEEDED(hr)) { + BEGIN_CALL_IN_CONTEXT(hr, context, vd, pDevice, i) + vd->readInfoOfDevice(DEREF_WRL_OBJ(pDevice), i); + END_CALL_IN_CONTEXT(S_OK) + vds_Devices.push_back(vd); + } + } + } + END_CREATE_ASYNC(hr)); + } + return hr; +} +#else long videoDevices::initDevices(IMFAttributes *pAttributes) { HRESULT hr = S_OK; - IMFActivate **ppDevices = NULL; clearDevices(); + IMFActivate **ppDevices = NULL; hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); if (SUCCEEDED(hr)) { @@ -2123,10 +2702,11 @@ long videoDevices::initDevices(IMFAttributes *pAttributes) } return hr; } +#endif unsigned int videoDevices::getCount() { - return vds_Devices.size(); + return (unsigned int)vds_Devices.size(); } videoDevices& videoDevices::getInstance() @@ -2192,7 +2772,7 @@ videoInput::videoInput(void): accessToDevices(false) DPO->printOut(L"\n***** VIDEOINPUT LIBRARY - 2013 (Author: Evgeny Pereguda) *****\n\n"); updateListOfDevices(); if(!accessToDevices) - DPO->printOut(L"INITIALIZATION: Ther is not any suitable video device\n"); + DPO->printOut(L"INITIALIZATION: There is not any suitable video device\n"); } void videoInput::updateListOfDevices() @@ -2201,7 +2781,7 @@ void videoInput::updateListOfDevices() Media_Foundation *MF = &Media_Foundation::getInstance(); accessToDevices = MF->buildListOfDevices(); if(!accessToDevices) - DPO->printOut(L"UPDATING: Ther is not any suitable video device\n"); + DPO->printOut(L"UPDATING: There is not any suitable video device\n"); } videoInput::~videoInput(void) @@ -2406,6 +2986,37 @@ bool videoInput::isFrameNew(int deviceID) return false; } +#ifdef HAVE_WINRT +void videoInput::waitForDevice(int deviceID) +{ + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + if (deviceID < 0) + { + DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + return; + } + if(accessToDevices) + { + if(!isDeviceSetup(deviceID)) + { + if(isDeviceMediaSource(deviceID)) + return; + } + videoDevices *VDS = &videoDevices::getInstance(); + videoDevice * VD = VDS->getDevice(deviceID); + if(VD) + { + VD->waitForDevice(); + } + } + else + { + DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + } + return; +} +#endif + unsigned int videoInput::getCountFormats(int deviceID) { DebugPrintOut *DPO = &DebugPrintOut::getInstance(); @@ -2573,6 +3184,9 @@ unsigned int videoInput::listDevices(bool silent) if(accessToDevices) { videoDevices *VDS = &videoDevices::getInstance(); +#ifdef HAVE_WINRT + VDS->waitInit(); +#endif out = VDS->getCount(); DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if(!silent)DPO->printOut(L"\nVIDEOINPUT SPY MODE!\n\n"); @@ -2762,6 +3376,10 @@ protected: int index, width, height, fourcc; IplImage* frame; videoInput VI; +#ifdef HAVE_WINRT + DEFINE_TASK openTask; + Concurrency::critical_section lock; +#endif }; struct SuppressVideoInputMessages @@ -2802,6 +3420,10 @@ void CvCaptureCAM_MSMF::close() // Initialize camera input bool CvCaptureCAM_MSMF::open( int _index ) { +#ifdef HAVE_WINRT + SAVE_CURRENT_CONTEXT(context); + auto func = [_index, context, this]() -> bool { +#endif int try_index = _index; int devices = 0; close(); @@ -2809,10 +3431,31 @@ bool CvCaptureCAM_MSMF::open( int _index ) if (devices == 0) return false; try_index = try_index < 0 ? 0 : (try_index > devices-1 ? devices-1 : try_index); +#ifdef HAVE_WINRT + HRESULT hr; + BEGIN_CALL_IN_CONTEXT(hr, context, this, try_index) +#endif VI.setupDevice(try_index); +#ifdef HAVE_WINRT + END_CALL_IN_CONTEXT(S_OK) + VI.waitForDevice(try_index); + BEGIN_CALL_IN_CONTEXT(hr, context, this, try_index) + HRESULT hr = S_OK; +#endif if( !VI.isFrameNew(try_index) ) +#ifdef HAVE_WINRT + hr = E_FAIL; +#else return false; +#endif index = try_index; +#ifdef HAVE_WINRT + END_CALL_IN_CONTEXT(hr); + return true; + }; + Concurrency::critical_section::scoped_lock _LockHolder(lock); + CREATE_OR_CONTINUE_TASK(openTask, bool, func) +#endif return true; } @@ -2928,7 +3571,7 @@ bool CvCaptureFile_MSMF::open(const char* filename) return false; wchar_t* unicodeFileName = new wchar_t[strlen(filename)+1]; - MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, strlen(filename)+1); + MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, (int)strlen(filename)+1); HRESULT hr = S_OK; @@ -3351,7 +3994,7 @@ HRESULT CvVideoWriter_MSMF::InitializeSinkWriter(const char* filename) spAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true); wchar_t* unicodeFileName = new wchar_t[strlen(filename)+1]; - MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, strlen(filename)+1); + MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, (int)strlen(filename)+1); HRESULT hr = MFCreateSinkWriterFromURL(unicodeFileName, NULL, spAttr.Get(), &sinkWriter); diff --git a/modules/highgui/src/cap_msmf.hpp b/modules/highgui/src/cap_msmf.hpp new file mode 100644 index 000000000..f755fd780 --- /dev/null +++ b/modules/highgui/src/cap_msmf.hpp @@ -0,0 +1,3187 @@ +#ifdef HAVE_WINRT +#define ICustomStreamSink StreamSink +#include "ppltasks_winrt.h" +#else +EXTERN_C const IID IID_ICustomStreamSink; + +class DECLSPEC_UUID("4F8A1939-2FD3-46DB-AE70-DB7E0DD79B73") DECLSPEC_NOVTABLE ICustomStreamSink : public IUnknown +{ +public: + virtual HRESULT Initialize() = 0; + virtual HRESULT Shutdown() = 0; + virtual HRESULT Start(MFTIME start) = 0; + virtual HRESULT Pause() = 0; + virtual HRESULT Restart() = 0; + virtual HRESULT Stop() = 0; +}; +#endif + +#define MF_PROP_SAMPLEGRABBERCALLBACK L"samplegrabbercallback" +#define MF_PROP_VIDTYPE L"vidtype" +#define MF_PROP_VIDENCPROPS L"videncprops" + +#include + +// MF_MEDIASINK_SAMPLEGRABBERCALLBACK: {26957AA7-AFF4-464c-BB8B-07BA65CE11DF} +// Type: IUnknown* +DEFINE_GUID(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, + 0x26957aa7, 0xaff4, 0x464c, 0xbb, 0x8b, 0x7, 0xba, 0x65, 0xce, 0x11, 0xdf); + +// {4BD133CC-EB9B-496E-8865-0813BFBC6FAA} +DEFINE_GUID(MF_STREAMSINK_ID, 0x4bd133cc, 0xeb9b, 0x496e, 0x88, 0x65, 0x8, 0x13, 0xbf, 0xbc, 0x6f, 0xaa); + +// {C9E22A8C-6A50-4D78-9183-0834A02A3780} +DEFINE_GUID(MF_STREAMSINK_MEDIASINKINTERFACE, + 0xc9e22a8c, 0x6a50, 0x4d78, 0x91, 0x83, 0x8, 0x34, 0xa0, 0x2a, 0x37, 0x80); + +// {DABD13AB-26B7-47C2-97C1-4B04C187B838} +DEFINE_GUID(MF_MEDIASINK_PREFERREDTYPE, + 0xdabd13ab, 0x26b7, 0x47c2, 0x97, 0xc1, 0x4b, 0x4, 0xc1, 0x87, 0xb8, 0x38); + +#include +#ifdef _UNICODE +#define MAKE_MAP(e) std::map +#define MAKE_ENUM(e) std::pair +#define MAKE_ENUM_PAIR(e, str) std::pair(str, L#str) +#else +#define MAKE_MAP(e) std::map +#define MAKE_ENUM(e) std::pair +#define MAKE_ENUM_PAIR(e, str) std::pair(str, #str) +#endif + +MAKE_ENUM(MediaEventType) MediaEventTypePairs[] = { + MAKE_ENUM_PAIR(MediaEventType, MEUnknown), + MAKE_ENUM_PAIR(MediaEventType, MEError), + MAKE_ENUM_PAIR(MediaEventType, MEExtendedType), + MAKE_ENUM_PAIR(MediaEventType, MENonFatalError), + MAKE_ENUM_PAIR(MediaEventType, MEGenericV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MESessionUnknown), + MAKE_ENUM_PAIR(MediaEventType, MESessionTopologySet), + MAKE_ENUM_PAIR(MediaEventType, MESessionTopologiesCleared), + MAKE_ENUM_PAIR(MediaEventType, MESessionStarted), + MAKE_ENUM_PAIR(MediaEventType, MESessionPaused), + MAKE_ENUM_PAIR(MediaEventType, MESessionStopped), + MAKE_ENUM_PAIR(MediaEventType, MESessionClosed), + MAKE_ENUM_PAIR(MediaEventType, MESessionEnded), + MAKE_ENUM_PAIR(MediaEventType, MESessionRateChanged), + MAKE_ENUM_PAIR(MediaEventType, MESessionScrubSampleComplete), + MAKE_ENUM_PAIR(MediaEventType, MESessionCapabilitiesChanged), + MAKE_ENUM_PAIR(MediaEventType, MESessionTopologyStatus), + MAKE_ENUM_PAIR(MediaEventType, MESessionNotifyPresentationTime), + MAKE_ENUM_PAIR(MediaEventType, MENewPresentation), + MAKE_ENUM_PAIR(MediaEventType, MELicenseAcquisitionStart), + MAKE_ENUM_PAIR(MediaEventType, MELicenseAcquisitionCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEIndividualizationStart), + MAKE_ENUM_PAIR(MediaEventType, MEIndividualizationCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEEnablerProgress), + MAKE_ENUM_PAIR(MediaEventType, MEEnablerCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEPolicyError), + MAKE_ENUM_PAIR(MediaEventType, MEPolicyReport), + MAKE_ENUM_PAIR(MediaEventType, MEBufferingStarted), + MAKE_ENUM_PAIR(MediaEventType, MEBufferingStopped), + MAKE_ENUM_PAIR(MediaEventType, MEConnectStart), + MAKE_ENUM_PAIR(MediaEventType, MEConnectEnd), + MAKE_ENUM_PAIR(MediaEventType, MEReconnectStart), + MAKE_ENUM_PAIR(MediaEventType, MEReconnectEnd), + MAKE_ENUM_PAIR(MediaEventType, MERendererEvent), + MAKE_ENUM_PAIR(MediaEventType, MESessionStreamSinkFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MESessionV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MESourceUnknown), + MAKE_ENUM_PAIR(MediaEventType, MESourceStarted), + MAKE_ENUM_PAIR(MediaEventType, MEStreamStarted), + MAKE_ENUM_PAIR(MediaEventType, MESourceSeeked), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSeeked), + MAKE_ENUM_PAIR(MediaEventType, MENewStream), + MAKE_ENUM_PAIR(MediaEventType, MEUpdatedStream), + MAKE_ENUM_PAIR(MediaEventType, MESourceStopped), + MAKE_ENUM_PAIR(MediaEventType, MEStreamStopped), + MAKE_ENUM_PAIR(MediaEventType, MESourcePaused), + MAKE_ENUM_PAIR(MediaEventType, MEStreamPaused), + MAKE_ENUM_PAIR(MediaEventType, MEEndOfPresentation), + MAKE_ENUM_PAIR(MediaEventType, MEEndOfStream), + MAKE_ENUM_PAIR(MediaEventType, MEMediaSample), + MAKE_ENUM_PAIR(MediaEventType, MEStreamTick), + MAKE_ENUM_PAIR(MediaEventType, MEStreamThinMode), + MAKE_ENUM_PAIR(MediaEventType, MEStreamFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MESourceRateChanged), + MAKE_ENUM_PAIR(MediaEventType, MEEndOfPresentationSegment), + MAKE_ENUM_PAIR(MediaEventType, MESourceCharacteristicsChanged), + MAKE_ENUM_PAIR(MediaEventType, MESourceRateChangeRequested), + MAKE_ENUM_PAIR(MediaEventType, MESourceMetadataChanged), + MAKE_ENUM_PAIR(MediaEventType, MESequencerSourceTopologyUpdated), + MAKE_ENUM_PAIR(MediaEventType, MESourceV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MESinkUnknown), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkStarted), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkStopped), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkPaused), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkRateChanged), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkRequestSample), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkMarker), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkPrerolled), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkScrubSampleComplete), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkDeviceChanged), + MAKE_ENUM_PAIR(MediaEventType, MEQualityNotify), + MAKE_ENUM_PAIR(MediaEventType, MESinkInvalidated), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionNameChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionVolumeChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionDeviceRemoved), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionServerShutdown), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionGroupingParamChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionIconChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionDisconnected), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionExclusiveModeOverride), + MAKE_ENUM_PAIR(MediaEventType, MESinkV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionVolumeChanged), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionDeviceRemoved), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionDisconnected), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionExclusiveModeOverride), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionServerShutdown), + MAKE_ENUM_PAIR(MediaEventType, MESinkV2Anchor), + MAKE_ENUM_PAIR(MediaEventType, METrustUnknown), + MAKE_ENUM_PAIR(MediaEventType, MEPolicyChanged), + MAKE_ENUM_PAIR(MediaEventType, MEContentProtectionMessage), + MAKE_ENUM_PAIR(MediaEventType, MEPolicySet), + MAKE_ENUM_PAIR(MediaEventType, METrustV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseBackupCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseBackupProgress), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseRestoreCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseRestoreProgress), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseAcquisitionCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMIndividualizationCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMIndividualizationProgress), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMProximityCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseStoreCleaned), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMRevocationDownloadCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, METransformUnknown), + MAKE_ENUM_PAIR(MediaEventType, METransformNeedInput), + MAKE_ENUM_PAIR(MediaEventType, METransformHaveOutput), + MAKE_ENUM_PAIR(MediaEventType, METransformDrainComplete), + MAKE_ENUM_PAIR(MediaEventType, METransformMarker), + MAKE_ENUM_PAIR(MediaEventType, MEByteStreamCharacteristicsChanged), + MAKE_ENUM_PAIR(MediaEventType, MEVideoCaptureDeviceRemoved), + MAKE_ENUM_PAIR(MediaEventType, MEVideoCaptureDevicePreempted), + MAKE_ENUM_PAIR(MediaEventType, MEReservedMax) +}; +MAKE_MAP(MediaEventType) MediaEventTypeMap(MediaEventTypePairs, MediaEventTypePairs + sizeof(MediaEventTypePairs) / sizeof(MediaEventTypePairs[0])); + +MAKE_ENUM(MFSTREAMSINK_MARKER_TYPE) StreamSinkMarkerTypePairs[] = { + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_DEFAULT), + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_ENDOFSEGMENT), + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_TICK), + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_EVENT) +}; +MAKE_MAP(MFSTREAMSINK_MARKER_TYPE) StreamSinkMarkerTypeMap(StreamSinkMarkerTypePairs, StreamSinkMarkerTypePairs + sizeof(StreamSinkMarkerTypePairs) / sizeof(StreamSinkMarkerTypePairs[0])); + +#ifdef HAVE_WINRT + +#ifdef __cplusplus_winrt +#define _Object Platform::Object^ +#define _ObjectObj Platform::Object^ +#define _String Platform::String^ +#define _StringObj Platform::String^ +#define _StringReference ref new Platform::String +#define _DeviceInformationCollection Windows::Devices::Enumeration::DeviceInformationCollection +#define _MediaCapture Windows::Media::Capture::MediaCapture +#define _MediaCaptureInitializationSettings Windows::Media::Capture::MediaCaptureInitializationSettings +#define _VideoDeviceController Windows::Media::Devices::VideoDeviceController +#define _MediaEncodingProperties Windows::Media::MediaProperties::IMediaEncodingProperties +#define _VideoPreview Windows::Media::Capture::MediaStreamType::VideoPreview +#define _AsyncAction Windows::Foundation::IAsyncAction +#define _AsyncOperation Windows::Foundation::IAsyncOperation +#define _DeviceClass Windows::Devices::Enumeration::DeviceClass +#define _VideoCapture Windows::Devices::Enumeration::DeviceClass::VideoCapture +#define _IDeviceInformation Windows::Devices::Enumeration::DeviceInformation +#define _DeviceInformation Windows::Devices::Enumeration::DeviceInformation +#define _DeviceInformationStatics Windows::Devices::Enumeration::DeviceInformation +#define _MediaEncodingProfile Windows::Media::MediaProperties::MediaEncodingProfile +#define _Video Windows::Media::Capture::StreamingCaptureMode::Video +#define _PropertySet Windows::Foundation::Collections::PropertySet +#define _PropertyValueStatics Windows::Foundation::PropertyValue +#define _VectorView Windows::Foundation::Collections::IVectorView +#define _StartPreviewToCustomSinkIdAsync StartPreviewToCustomSinkAsync +#define _InitializeWithSettingsAsync InitializeAsync +#define _FindAllAsyncDeviceClass FindAllAsync +#define _MediaExtension Windows::Media::IMediaExtension +#define _ContextCallback Concurrency::details::_ContextCallback +#define BEGIN_CALL_IN_CONTEXT(hr, var, ...) hr = S_OK;\ + var._CallInContext([__VA_ARGS__]() { +#define END_CALL_IN_CONTEXT(hr) if (FAILED(hr)) throw Platform::Exception::CreateException(hr);\ + }); +#define DO_ACTION_SYNCHRONOUSLY(hr, action, ctxt) hr = S_OK;\ + CCompletionHandler::PerformActionSynchronously(reinterpret_cast(action), ctxt) +#define DO_OPERATION_SYNCHRONOUSLY_VECTOR(hr, action, ctxt, pResult, vectortype, elementtype, _type) hr = S_OK;\ + pResult = CCompletionHandler::PerformSynchronously<_type^>(reinterpret_cast^>(action), ctxt) +#define BEGIN_CREATE_ASYNC(...) reinterpret_cast(Concurrency::create_async([__VA_ARGS__]() { +#define END_CREATE_ASYNC(hr) if (FAILED(hr)) throw Platform::Exception::CreateException(hr);\ + })) +#define DEFINE_TASK Concurrency::task +#define CREATE_TASK Concurrency::create_task +#define CREATE_OR_CONTINUE_TASK(_task, rettype, func) _task = (_task == Concurrency::task()) ? Concurrency::create_task(func) : _task.then([func](rettype) -> rettype { return func(); }); +#define MAKE_WRL_OBJ(x) x^ +#define MAKE_WRL_REF(x) x^ +#define MAKE_WRL_AGILE_REF(x) Platform::Agile +#define RELEASE_AGILE_WRL(x) x = nullptr; +#define RELEASE_WRL(x) x = nullptr; +#define GET_WRL_VIDEO_PREVIEW(medCap, prevMedCap, hr) Windows::Media::Capture::MediaCapture^ prevMedCap = medCap;\ + hr = S_OK; +#define GET_WRL_MEDIA_DEVICE_CONTROLLER(devCont, medDevCont, hr) Windows::Media::Devices::VideoDeviceController^ medDevCont = devCont;\ + hr = S_OK; +#define GET_WRL_VIDEO_ENCODING_PROPERTIES(encProp, vidEncProp, hr) Windows::Media::MediaProperties::VideoEncodingProperties^ vidEncProp = safe_cast(encProp);\ + hr = S_OK; +#define GET_WRL_MAP(pSet, map, hr) Windows::Foundation::Collections::PropertySet^ map = pSet;\ + hr = S_OK; +#define WRL_PROP_GET(obj, prop, arg, hr) arg = obj->##prop;\ + hr = S_OK; +#define WRL_PROP_PUT(obj, prop, arg, hr) obj->##prop = arg;\ + hr = S_OK; +#define WRL_METHOD_BASE(obj, method, ret, hr) ret = obj->##method();\ + hr = S_OK; +#define WRL_METHOD(obj, method, ret, hr, ...) ret = obj->##method(__VA_ARGS__);\ + hr = S_OK; +#define REF_WRL_OBJ(obj) &obj +#define DEREF_WRL_OBJ(obj) obj +#define DEREF_AGILE_WRL_OBJ(obj) obj.Get() +#define DEREF_AS_NATIVE_WRL_OBJ(type, obj) reinterpret_cast(obj) +#define PREPARE_TRANSFER_WRL_OBJ(obj) obj +#define ACTIVATE_OBJ(rtclass, objtype, obj, hr) objtype obj;\ + hr = S_OK; +#define ACTIVATE_STATIC_OBJ(rtclass, objtype, obj, hr) objtype obj;\ + hr = S_OK; +#else +#define _Object IInspectable* +#define _ObjectObj Microsoft::WRL::ComPtr +#define _String HSTRING +#define _StringObj HString +#define _StringReference HStringReference +#define _DeviceInformationCollection ABI::Windows::Devices::Enumeration::DeviceInformationCollection +#define _MediaCapture ABI::Windows::Media::Capture::IMediaCapture +#define _MediaCaptureInitializationSettings ABI::Windows::Media::Capture::IMediaCaptureInitializationSettings +#define _VideoDeviceController ABI::Windows::Media::Devices::IVideoDeviceController +#define _MediaEncodingProperties ABI::Windows::Media::MediaProperties::IMediaEncodingProperties +#define _VideoPreview ABI::Windows::Media::Capture::MediaStreamType::MediaStreamType_VideoPreview +#define _AsyncAction ABI::Windows::Foundation::IAsyncAction +#define _AsyncOperation ABI::Windows::Foundation::IAsyncOperation +#define _DeviceClass ABI::Windows::Devices::Enumeration::DeviceClass +#define _VideoCapture ABI::Windows::Devices::Enumeration::DeviceClass::DeviceClass_VideoCapture +#define _IDeviceInformation ABI::Windows::Devices::Enumeration::IDeviceInformation +#define _DeviceInformation ABI::Windows::Devices::Enumeration::DeviceInformation +#define _DeviceInformationStatics ABI::Windows::Devices::Enumeration::IDeviceInformationStatics +#define _MediaEncodingProfile ABI::Windows::Media::MediaProperties::IMediaEncodingProfile +#define _Video ABI::Windows::Media::Capture::StreamingCaptureMode::StreamingCaptureMode_Video +#define _PropertySet ABI::Windows::Foundation::Collections::IPropertySet +#define _PropertyValueStatics ABI::Windows::Foundation::IPropertyValueStatics +#define _VectorView ABI::Windows::Foundation::Collections::IVectorView +#define _StartPreviewToCustomSinkIdAsync StartPreviewToCustomSinkIdAsync +#define _InitializeWithSettingsAsync InitializeWithSettingsAsync +#define _FindAllAsyncDeviceClass FindAllAsyncDeviceClass +#define _MediaExtension ABI::Windows::Media::IMediaExtension +#define _ContextCallback Concurrency_winrt::details::_ContextCallback +#define BEGIN_CALL_IN_CONTEXT(hr, var, ...) hr = var._CallInContext([__VA_ARGS__]() -> HRESULT { +#define END_CALL_IN_CONTEXT(hr) return hr;\ + }); +#define DO_ACTION_SYNCHRONOUSLY(hr, action, ctxt) hr = CCompletionHandler::PerformActionSynchronously(action, ctxt) +#define DO_OPERATION_SYNCHRONOUSLY_VECTOR(hr, action, ctxt, pResult, vectortype, elementtype, _type) hr = CCompletionHandler, ABI::Windows::Foundation::IAsyncOperation<_type*>>::PerformSynchronously*>(action, ctxt, pResult.GetAddressOf()) +#define BEGIN_CREATE_ASYNC(...) Concurrency_winrt::create_async([__VA_ARGS__]() -> HRESULT { +#define END_CREATE_ASYNC(hr) return hr;\ + }) +#define DEFINE_TASK Concurrency_winrt::task +#define CREATE_TASK Concurrency_winrt::create_task +#define CREATE_OR_CONTINUE_TASK(_task, rettype, func) _task = (_task == Concurrency_winrt::task()) ? Concurrency_winrt::create_task(func) : _task.then([func](rettype) -> rettype { return func(); }); +#define MAKE_WRL_OBJ(x) Microsoft::WRL::ComPtr +#define MAKE_WRL_REF(x) x* +#define MAKE_WRL_AGILE_REF(x) x* +#define RELEASE_AGILE_WRL(x) if (x) { (x)->Release(); x = nullptr; } +#define RELEASE_WRL(x) if (x) { (x)->Release(); x = nullptr; } +#define GET_WRL_VIDEO_PREVIEW(medCap, prevMedCap, hr) Microsoft::WRL::ComPtr prevMedCap;\ + hr = medCap->QueryInterface(__uuidof(ABI::Windows::Media::Capture::IMediaCaptureVideoPreview), &prevMedCap); +#define GET_WRL_MEDIA_DEVICE_CONTROLLER(devCont, medDevCont, hr) Microsoft::WRL::ComPtr medDevCont;\ + hr = devCont.As(&medDevCont); +#define GET_WRL_VIDEO_ENCODING_PROPERTIES(encProp, vidEncProp, hr) Microsoft::WRL::ComPtr vidEncProp;\ + hr = encProp.As(&vidEncProp); +#define GET_WRL_MAP(pSet, map, hr) Microsoft::WRL::ComPtr> map;\ + hr = pSet.As(&map); +#define WRL_PROP_GET(obj, prop, arg, hr) hr = obj->get_##prop(&arg); +#define WRL_PROP_PUT(obj, prop, arg, hr) hr = obj->put_##prop(arg); +#define WRL_METHOD_BASE(obj, method, ret, hr) hr = obj->##method(&ret); +#define WRL_METHOD(obj, method, ret, hr, ...) hr = obj->##method(__VA_ARGS__, &ret); +#define REF_WRL_OBJ(obj) obj.GetAddressOf() +#define DEREF_WRL_OBJ(obj) obj.Get() +#define DEREF_AGILE_WRL_OBJ(obj) obj +#define DEREF_AS_NATIVE_WRL_OBJ(type, obj) obj.Get() +#define PREPARE_TRANSFER_WRL_OBJ(obj) obj.Detach() +#define ACTIVATE_OBJ(rtclass, objtype, obj, hr) objtype obj;\ +{\ + Microsoft::WRL::ComPtr objFactory;\ + hr = Windows::Foundation::GetActivationFactory(HStringReference(rtclass).Get(), objFactory.ReleaseAndGetAddressOf());\ + if (SUCCEEDED(hr)) {\ + Microsoft::WRL::ComPtr pInsp;\ + hr = objFactory->ActivateInstance(pInsp.GetAddressOf());\ + if (SUCCEEDED(hr)) hr = pInsp.As(&obj);\ + }\ +} +#define ACTIVATE_STATIC_OBJ(rtclass, objtype, obj, hr) objtype obj;\ +{\ + Microsoft::WRL::ComPtr objFactory;\ + hr = Windows::Foundation::GetActivationFactory(HStringReference(rtclass).Get(), objFactory.ReleaseAndGetAddressOf());\ + if (SUCCEEDED(hr)) {\ + if (SUCCEEDED(hr)) hr = objFactory.As(&obj);\ + }\ +} +#endif + +#define GET_CURRENT_CONTEXT _ContextCallback::_CaptureCurrent() +#define SAVE_CURRENT_CONTEXT(var) _ContextCallback var = GET_CURRENT_CONTEXT + +#ifdef __cplusplus_winrt +ref class CCompletionHandler sealed +#else +template +class CCompletionHandler + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::ClassicCom>, + TCompletionHandler, IAgileObject, FtmBase> +#endif +{ +#ifndef __cplusplus_winrt +public: + CCompletionHandler() {} + + STDMETHODIMP Invoke(TAction* /*asyncInfo*/, AsyncStatus /*asyncStatus*/) + { + m_Event.set(); + return S_OK; + } + void wait() { m_Event.wait(); } +#endif +#ifdef __cplusplus_winrt +internal: + template + static TResult PerformSynchronously(Windows::Foundation::IAsyncOperation^ asyncOp, _ContextCallback context) + { + TResult pResult; + context._CallInContext([asyncOp, &pResult]() { Concurrency::task asyncTask = Concurrency::task(asyncOp); pResult = asyncTask.get(); }); + return pResult; +#else + template + static HRESULT PerformSynchronously(TAction* asyncOp, _ContextCallback context, TResult* pResult) + { + HRESULT hr; + ComPtr> completeHandler = Microsoft::WRL::Make>(); + hr = context._CallInContext([&asyncOp, &completeHandler]() -> HRESULT { + HRESULT hr = asyncOp->put_Completed(completeHandler.Get()); + if (FAILED(hr)) asyncOp->Release(); + return hr; + }); + if (SUCCEEDED(hr)) + completeHandler->wait(); + else + return hr; + hr = context._CallInContext([&asyncOp, &pResult]() -> HRESULT { + HRESULT hr = asyncOp->GetResults(pResult); + asyncOp->Release(); + return hr; + }); + return hr; +#endif + } + +#ifdef __cplusplus_winrt + static void PerformActionSynchronously(Windows::Foundation::IAsyncAction^ asyncOp, _ContextCallback context) + { + context._CallInContext([asyncOp](){ Concurrency::task(asyncOp).get(); }); +#else + static HRESULT PerformActionSynchronously(TAction* asyncOp, _ContextCallback context) + { + HRESULT hr; + ComPtr> completeHandler = Microsoft::WRL::Make>(); + hr = context._CallInContext([&asyncOp, &completeHandler]() -> HRESULT { + HRESULT hr = asyncOp->put_Completed(completeHandler.Get()); + if (FAILED(hr)) asyncOp->Release(); + return hr; + }); + if (SUCCEEDED(hr)) + completeHandler->wait(); + else + return hr; + hr = context._CallInContext([&asyncOp]() -> HRESULT { + HRESULT hr = asyncOp->GetResults(); + asyncOp->Release(); + return hr; + }); + return hr; +#endif + } +#ifndef __cplusplus_winrt +private: + Concurrency::event m_Event; +#endif +}; + +#ifndef __cplusplus_winrt + +// Helpers for create_async validation +// +// A parameter lambda taking no arguments is valid +template +static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> typename decltype(_Param(), std::true_type()); + +// A parameter lambda taking an cancellation_token argument is valid +template +static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> typename decltype(_Param(cancellation_token::none()), std::true_type()); + +// A parameter lambda taking a progress report argument is valid +template +static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); + +// A parameter lambda taking a progress report and a cancellation_token argument is valid +template +static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type()); + +// All else is invalid +template +static std::false_type _IsValidCreateAsync(_Ty _Param, ...); + +//for task specific architecture +//could add a CancelPending which is set when Cancel is called, return as Cancel when get_Status is called and set when a task_canceled exception is thrown + +extern const __declspec(selectany) WCHAR RuntimeClass_CV_CAsyncAction[] = L"cv.CAsyncAction"; + +template +class CAsyncAction + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRt>, + Microsoft::WRL::Implements, Microsoft::WRL::AsyncBase> +{ + InspectableClass(RuntimeClass_CV_CAsyncAction, BaseTrust) +public: + STDMETHOD(RuntimeClassInitialize)() { return S_OK; } + virtual ~CAsyncAction() {} + CAsyncAction(const _Function &_Func) : _M_func(_Func) { + Start(); + } + void _SetTaskCreationAddressHint(void* _SourceAddressHint) + { + if (!(std::is_same>::_AsyncKind, Concurrency_winrt::details::_TypeSelectorAsyncTask>::value)) + { + // Overwrite the creation address with the return address of create_async unless the + // lambda returned a task. If the create async lambda returns a task, that task is reused and + // we want to preserve its creation address hint. + _M_task._SetTaskCreationAddressHint(_SourceAddressHint); + } + } + HRESULT STDMETHODCALLTYPE put_Completed( + /* [in] */ __RPC__in_opt ABI::Windows::Foundation::IAsyncActionCompletedHandler *handler) + { + HRESULT hr; + if (SUCCEEDED(hr = PutOnComplete(handler)) && cCallbackMade_ == 0) { + //okay to use default implementation even for the callback as already running in context + //otherwise check for the alternate case and save the context + _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); + } + return hr; + } + HRESULT STDMETHODCALLTYPE get_Completed( + /* [out][retval] */ __RPC__deref_out_opt ABI::Windows::Foundation::IAsyncActionCompletedHandler **handler) { + if (!handler) return E_POINTER; + return GetOnComplete(handler); + } + HRESULT STDMETHODCALLTYPE GetResults(void) { + HRESULT hr = CheckValidStateForResultsCall(); + if (SUCCEEDED(hr)) { + _M_task.get(); + } + return hr; + } + HRESULT OnStart() { + _M_task = Concurrency_winrt::task(_M_func, _M_cts.get_token()); + AddRef(); + _M_task.then([this](Concurrency_winrt::task _Antecedent) { + try { + HRESULT hr = _Antecedent.get(); + if (FAILED(hr)) TryTransitionToError(hr); + } + catch (Concurrency::task_canceled&){ + } + catch (...) { + TryTransitionToError(E_FAIL); + } + _FireCompletion(); + Release(); + }); + return S_OK; + } + void OnClose() {} + void OnCancel() { _M_cts.cancel(); } + +protected: + //modified for _CallInContext to support UI STA thread + //can wrap the base clase implementation or duplicate it but must use get_Completed to fetch the private member variable + virtual void _FireCompletion() + { + AddRef(); + _M_completeDelegateContext._CallInContext([this]() -> HRESULT { + FireCompletion(); + Release(); + return S_OK; + }); + } +private: + + _Function _M_func; + Concurrency_winrt::task _M_task; + Concurrency::cancellation_token_source _M_cts; + _ContextCallback _M_completeDelegateContext; +}; + +template +__declspec(noinline) +CAsyncAction<_Function>* create_async(const _Function& _Func) +{ + static_assert(std::is_same::value, + "argument to create_async must be a callable object taking zero, one or two arguments"); + CAsyncAction<_Function>* action = Microsoft::WRL::Make>(_Func).Detach(); + action->_SetTaskCreationAddressHint(_ReturnAddress()); + return action; +} +#endif + +EXTERN_C const IID IID_IMedCapFailHandler; + +class DECLSPEC_UUID("CE22BEDB-0B3C-4BE0-BE8F-E53AB457EA2C") DECLSPEC_NOVTABLE IMedCapFailHandler : public IUnknown +{ +public: + virtual HRESULT AddHandler(ABI::Windows::Media::Capture::IMediaCapture* pMedCap) = 0; + virtual HRESULT RemoveHandler(ABI::Windows::Media::Capture::IMediaCapture* pMedCap) = 0; +}; + +template +class MediaCaptureFailedHandler : + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::ClassicCom>, + IMedCapFailHandler, ABI::Windows::Media::Capture::IMediaCaptureFailedEventHandler, IAgileObject, FtmBase> +{ +public: + MediaCaptureFailedHandler(const _Function &_Func) : _M_func(_Func) { m_cookie.value = 0; } + HRESULT AddHandler(ABI::Windows::Media::Capture::IMediaCapture* pMedCap) + { + return pMedCap->add_Failed(this, &m_cookie); + } + HRESULT RemoveHandler(ABI::Windows::Media::Capture::IMediaCapture* pMedCap) + { + return pMedCap->remove_Failed(m_cookie); + } + HRESULT STDMETHODCALLTYPE Invoke( + ABI::Windows::Media::Capture::IMediaCapture *sender, + ABI::Windows::Media::Capture::IMediaCaptureFailedEventArgs *errorEventArgs) + { + (void)sender; + (void)errorEventArgs; + AddRef(); + _M_func(); + Release(); + return S_OK; + } + +private: + _Function _M_func; + EventRegistrationToken m_cookie; +}; + +template +__declspec(noinline) +MediaCaptureFailedHandler<_Function>* create_medcapfailedhandler(const _Function& _Func) +{ + return Microsoft::WRL::Make>(_Func).Detach(); +} + +#endif + +template +class CBaseAttributes : public TBase +{ +protected: + // This version of the constructor does not initialize the + // attribute store. The derived class must call Initialize() in + // its own constructor. + CBaseAttributes() + { + } + + // This version of the constructor initializes the attribute + // store, but the derived class must pass an HRESULT parameter + // to the constructor. + + CBaseAttributes(HRESULT& hr, UINT32 cInitialSize = 0) + { + hr = Initialize(cInitialSize); + } + + // The next version of the constructor uses a caller-provided + // implementation of IMFAttributes. + + // (Sometimes you want to delegate IMFAttributes calls to some + // other object that implements IMFAttributes, rather than using + // MFCreateAttributes.) + + CBaseAttributes(HRESULT& hr, IUnknown *pUnk) + { + hr = Initialize(pUnk); + } + + virtual ~CBaseAttributes() + { + } + + // Initializes the object by creating the standard Media Foundation attribute store. + HRESULT Initialize(UINT32 cInitialSize = 0) + { + if (_spAttributes.Get() == nullptr) + { + return MFCreateAttributes(&_spAttributes, cInitialSize); + } + else + { + return S_OK; + } + } + + // Initializes this object from a caller-provided attribute store. + // pUnk: Pointer to an object that exposes IMFAttributes. + HRESULT Initialize(IUnknown *pUnk) + { + if (_spAttributes) + { + _spAttributes.Reset(); + _spAttributes = nullptr; + } + + + return pUnk->QueryInterface(IID_PPV_ARGS(&_spAttributes)); + } + +public: + + // IMFAttributes methods + + STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT* pValue) + { + assert(_spAttributes); + return _spAttributes->GetItem(guidKey, pValue); + } + + STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) + { + assert(_spAttributes); + return _spAttributes->GetItemType(guidKey, pType); + } + + STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult) + { + assert(_spAttributes); + return _spAttributes->CompareItem(guidKey, Value, pbResult); + } + + STDMETHODIMP Compare( + IMFAttributes* pTheirs, + MF_ATTRIBUTES_MATCH_TYPE MatchType, + BOOL* pbResult + ) + { + assert(_spAttributes); + return _spAttributes->Compare(pTheirs, MatchType, pbResult); + } + + STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32* punValue) + { + assert(_spAttributes); + return _spAttributes->GetUINT32(guidKey, punValue); + } + + STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64* punValue) + { + assert(_spAttributes); + return _spAttributes->GetUINT64(guidKey, punValue); + } + + STDMETHODIMP GetDouble(REFGUID guidKey, double* pfValue) + { + assert(_spAttributes); + return _spAttributes->GetDouble(guidKey, pfValue); + } + + STDMETHODIMP GetGUID(REFGUID guidKey, GUID* pguidValue) + { + assert(_spAttributes); + return _spAttributes->GetGUID(guidKey, pguidValue); + } + + STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32* pcchLength) + { + assert(_spAttributes); + return _spAttributes->GetStringLength(guidKey, pcchLength); + } + + STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength) + { + assert(_spAttributes); + return _spAttributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength); + } + + STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) + { + assert(_spAttributes); + return _spAttributes->GetAllocatedString(guidKey, ppwszValue, pcchLength); + } + + STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize) + { + assert(_spAttributes); + return _spAttributes->GetBlobSize(guidKey, pcbBlobSize); + } + + STDMETHODIMP GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize) + { + assert(_spAttributes); + return _spAttributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize); + } + + STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize) + { + assert(_spAttributes); + return _spAttributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize); + } + + STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID* ppv) + { + assert(_spAttributes); + return _spAttributes->GetUnknown(guidKey, riid, ppv); + } + + STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value) + { + assert(_spAttributes); + return _spAttributes->SetItem(guidKey, Value); + } + + STDMETHODIMP DeleteItem(REFGUID guidKey) + { + assert(_spAttributes); + return _spAttributes->DeleteItem(guidKey); + } + + STDMETHODIMP DeleteAllItems() + { + assert(_spAttributes); + return _spAttributes->DeleteAllItems(); + } + + STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue) + { + assert(_spAttributes); + return _spAttributes->SetUINT32(guidKey, unValue); + } + + STDMETHODIMP SetUINT64(REFGUID guidKey,UINT64 unValue) + { + assert(_spAttributes); + return _spAttributes->SetUINT64(guidKey, unValue); + } + + STDMETHODIMP SetDouble(REFGUID guidKey, double fValue) + { + assert(_spAttributes); + return _spAttributes->SetDouble(guidKey, fValue); + } + + STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue) + { + assert(_spAttributes); + return _spAttributes->SetGUID(guidKey, guidValue); + } + + STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue) + { + assert(_spAttributes); + return _spAttributes->SetString(guidKey, wszValue); + } + + STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize) + { + assert(_spAttributes); + return _spAttributes->SetBlob(guidKey, pBuf, cbBufSize); + } + + STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown* pUnknown) + { + assert(_spAttributes); + return _spAttributes->SetUnknown(guidKey, pUnknown); + } + + STDMETHODIMP LockStore() + { + assert(_spAttributes); + return _spAttributes->LockStore(); + } + + STDMETHODIMP UnlockStore() + { + assert(_spAttributes); + return _spAttributes->UnlockStore(); + } + + STDMETHODIMP GetCount(UINT32* pcItems) + { + assert(_spAttributes); + return _spAttributes->GetCount(pcItems); + } + + STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) + { + assert(_spAttributes); + return _spAttributes->GetItemByIndex(unIndex, pguidKey, pValue); + } + + STDMETHODIMP CopyAllItems(IMFAttributes* pDest) + { + assert(_spAttributes); + return _spAttributes->CopyAllItems(pDest); + } + + // Helper functions + + HRESULT SerializeToStream(DWORD dwOptions, IStream* pStm) + // dwOptions: Flags from MF_ATTRIBUTE_SERIALIZE_OPTIONS + { + assert(_spAttributes); + return MFSerializeAttributesToStream(_spAttributes.Get(), dwOptions, pStm); + } + + HRESULT DeserializeFromStream(DWORD dwOptions, IStream* pStm) + { + assert(_spAttributes); + return MFDeserializeAttributesFromStream(_spAttributes.Get(), dwOptions, pStm); + } + + // SerializeToBlob: Stores the attributes in a byte array. + // + // ppBuf: Receives a pointer to the byte array. + // pcbSize: Receives the size of the byte array. + // + // The caller must free the array using CoTaskMemFree. + HRESULT SerializeToBlob(UINT8 **ppBuffer, UINT *pcbSize) + { + assert(_spAttributes); + + if (ppBuffer == NULL) + { + return E_POINTER; + } + if (pcbSize == NULL) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + UINT32 cbSize = 0; + BYTE *pBuffer = NULL; + + CHECK_HR(hr = MFGetAttributesAsBlobSize(_spAttributes.Get(), &cbSize)); + + pBuffer = (BYTE*)CoTaskMemAlloc(cbSize); + if (pBuffer == NULL) + { + CHECK_HR(hr = E_OUTOFMEMORY); + } + + CHECK_HR(hr = MFGetAttributesAsBlob(_spAttributes.Get(), pBuffer, cbSize)); + + *ppBuffer = pBuffer; + *pcbSize = cbSize; + +done: + if (FAILED(hr)) + { + *ppBuffer = NULL; + *pcbSize = 0; + CoTaskMemFree(pBuffer); + } + return hr; + } + + HRESULT DeserializeFromBlob(const UINT8* pBuffer, UINT cbSize) + { + assert(_spAttributes); + return MFInitAttributesFromBlob(_spAttributes.Get(), pBuffer, cbSize); + } + + HRESULT GetRatio(REFGUID guidKey, UINT32* pnNumerator, UINT32* punDenominator) + { + assert(_spAttributes); + return MFGetAttributeRatio(_spAttributes.Get(), guidKey, pnNumerator, punDenominator); + } + + HRESULT SetRatio(REFGUID guidKey, UINT32 unNumerator, UINT32 unDenominator) + { + assert(_spAttributes); + return MFSetAttributeRatio(_spAttributes.Get(), guidKey, unNumerator, unDenominator); + } + + // Gets an attribute whose value represents the size of something (eg a video frame). + HRESULT GetSize(REFGUID guidKey, UINT32* punWidth, UINT32* punHeight) + { + assert(_spAttributes); + return MFGetAttributeSize(_spAttributes.Get(), guidKey, punWidth, punHeight); + } + + // Sets an attribute whose value represents the size of something (eg a video frame). + HRESULT SetSize(REFGUID guidKey, UINT32 unWidth, UINT32 unHeight) + { + assert(_spAttributes); + return MFSetAttributeSize (_spAttributes.Get(), guidKey, unWidth, unHeight); + } + +protected: + ComPtr _spAttributes; +}; + +class StreamSink : +#ifdef HAVE_WINRT + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::ClassicCom>, + IMFStreamSink, + IMFMediaEventGenerator, + IMFMediaTypeHandler, + CBaseAttributes<> > +#else + public IMFStreamSink, + public IMFMediaTypeHandler, + public CBaseAttributes<>, + public ICustomStreamSink +#endif +{ +public: + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppv) + { + if (ppv == nullptr) { + return E_POINTER; + } + (*ppv) = nullptr; + HRESULT hr = S_OK; + if (riid == IID_IMarshal) { + return MarshalQI(riid, ppv); + } else { +#ifdef HAVE_WINRT + hr = RuntimeClassT::QueryInterface(riid, ppv); +#else + if (riid == IID_IUnknown || riid == IID_IMFStreamSink) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_IMFMediaEventGenerator) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_IMFMediaTypeHandler) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_IMFAttributes) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_ICustomStreamSink) { + *ppv = static_cast(this); + AddRef(); + } else + hr = E_NOINTERFACE; +#endif + } + + return hr; + } + +#ifdef HAVE_WINRT + STDMETHOD(RuntimeClassInitialize)() { return S_OK; } +#else + ULONG AddRef() + { + return InterlockedIncrement(&m_cRef); + } + ULONG Release() + { + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + delete this; + } + return cRef; + } +#endif + HRESULT MarshalQI(REFIID riid, LPVOID* ppv) + { + HRESULT hr = S_OK; + if (m_spFTM == nullptr) { + EnterCriticalSection(&m_critSec); + if (m_spFTM == nullptr) { + hr = CoCreateFreeThreadedMarshaler(static_cast(this), &m_spFTM); + } + LeaveCriticalSection(&m_critSec); + } + + if (SUCCEEDED(hr)) { + if (m_spFTM == nullptr) { + hr = E_UNEXPECTED; + } + else { + hr = m_spFTM.Get()->QueryInterface(riid, ppv); + } + } + return hr; + } + enum State + { + State_TypeNotSet = 0, // No media type is set + State_Ready, // Media type is set, Start has never been called. + State_Started, + State_Stopped, + State_Paused, + State_Count // Number of states + }; + StreamSink() : m_IsShutdown(false), + m_StartTime(0), m_fGetStartTimeFromSample(false), m_fWaitingForFirstSample(false), + m_state(State_TypeNotSet), m_pParent(nullptr), + m_imageWidthInPixels(0), m_imageHeightInPixels(0) { +#ifdef HAVE_WINRT + m_token.value = 0; +#else + m_bConnected = false; +#endif + InitializeCriticalSectionEx(&m_critSec, 3000, 0); + ZeroMemory(&m_guiCurrentSubtype, sizeof(m_guiCurrentSubtype)); + CBaseAttributes::Initialize(0U); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::StreamSink\n"); + } + virtual ~StreamSink() { + DeleteCriticalSection(&m_critSec); + assert(m_IsShutdown); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::~StreamSink\n"); + } + + HRESULT Initialize() + { + HRESULT hr; + // Create the event queue helper. + hr = MFCreateEventQueue(&m_spEventQueue); + if (SUCCEEDED(hr)) + { + ComPtr pMedSink; + hr = CBaseAttributes<>::GetUnknown(MF_STREAMSINK_MEDIASINKINTERFACE, __uuidof(IMFMediaSink), (LPVOID*)pMedSink.GetAddressOf()); + assert(pMedSink.Get() != NULL); + if (SUCCEEDED(hr)) { + hr = pMedSink.Get()->QueryInterface(IID_PPV_ARGS(&m_pParent)); + } + } + return hr; + } + + HRESULT CheckShutdown() const + { + if (m_IsShutdown) + { + return MF_E_SHUTDOWN; + } + else + { + return S_OK; + } + } + // Called when the presentation clock starts. + HRESULT Start(MFTIME start) + { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + + if (m_state != State_TypeNotSet) { + if (start != PRESENTATION_CURRENT_POSITION) + { + m_StartTime = start; // Cache the start time. + m_fGetStartTimeFromSample = false; + } + else + { + m_fGetStartTimeFromSample = true; + } + m_state = State_Started; + GUID guiMajorType; + m_fWaitingForFirstSample = SUCCEEDED(m_spCurrentType->GetMajorType(&guiMajorType)) && (guiMajorType == MFMediaType_Video); + hr = QueueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL); + if (SUCCEEDED(hr)) { + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, hr, NULL); + } + } + else hr = MF_E_NOT_INITIALIZED; + LeaveCriticalSection(&m_critSec); + return hr; + } + + // Called when the presentation clock pauses. + HRESULT Pause() + { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + + if (m_state != State_Stopped && m_state != State_TypeNotSet) { + m_state = State_Paused; + hr = QueueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL); + } else if (hr == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + else + hr = MF_E_INVALIDREQUEST; + LeaveCriticalSection(&m_critSec); + return hr; + } + // Called when the presentation clock restarts. + HRESULT Restart() + { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + + if (m_state == State_Paused) { + m_state = State_Started; + hr = QueueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL); + if (SUCCEEDED(hr)) { + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, hr, NULL); + } + } else if (hr == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + else + hr = MF_E_INVALIDREQUEST; + LeaveCriticalSection(&m_critSec); + return hr; + } + // Called when the presentation clock stops. + HRESULT Stop() + { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + if (m_state != State_TypeNotSet) { + m_state = State_Stopped; + hr = QueueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL); + } + else hr = MF_E_NOT_INITIALIZED; + LeaveCriticalSection(&m_critSec); + return hr; + } + + // Shuts down the stream sink. + HRESULT Shutdown() + { + ComPtr pSampleCallback; + HRESULT hr = S_OK; + assert(!m_IsShutdown); + hr = m_pParent->GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) { + hr = pSampleCallback->OnShutdown(); + } + + if (m_spEventQueue) { + hr = m_spEventQueue->Shutdown(); + } + if (m_pParent) + m_pParent->Release(); + m_spCurrentType.Reset(); + m_IsShutdown = TRUE; + + return hr; + } + + //IMFStreamSink + HRESULT STDMETHODCALLTYPE GetMediaSink( + /* [out] */ __RPC__deref_out_opt IMFMediaSink **ppMediaSink) { + if (ppMediaSink == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + ComPtr pMedSink; + hr = CBaseAttributes<>::GetUnknown(MF_STREAMSINK_MEDIASINKINTERFACE, __uuidof(IMFMediaSink), (LPVOID*)pMedSink.GetAddressOf()); + if (SUCCEEDED(hr)) { + *ppMediaSink = pMedSink.Detach(); + } + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::GetMediaSink: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetIdentifier( + /* [out] */ __RPC__out DWORD *pdwIdentifier) { + if (pdwIdentifier == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = GetUINT32(MF_STREAMSINK_ID, (UINT32*)pdwIdentifier); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::GetIdentifier: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetMediaTypeHandler( + /* [out] */ __RPC__deref_out_opt IMFMediaTypeHandler **ppHandler) { + if (ppHandler == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + // This stream object acts as its own type handler, so we QI ourselves. + if (SUCCEEDED(hr)) + { + hr = QueryInterface(IID_IMFMediaTypeHandler, (void**)ppHandler); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::GetMediaTypeHandler: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE ProcessSample(IMFSample *pSample) { + ComPtr pInput; + ComPtr pSampleCallback; + BYTE *pSrc = NULL; // Source buffer. + // Stride if the buffer does not support IMF2DBuffer + LONGLONG hnsTime = 0; + LONGLONG hnsDuration = 0; + DWORD cbMaxLength; + DWORD cbCurrentLength = 0; + GUID guidMajorType; + if (pSample == NULL) + { + return E_INVALIDARG; + } + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + if (m_state != State_Started && m_state != State_Paused) { + if (m_state == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + else + hr = MF_E_INVALIDREQUEST; + } + if (SUCCEEDED(hr)) + hr = CheckShutdown(); + if (SUCCEEDED(hr)) { + hr = pSample->ConvertToContiguousBuffer(&pInput); + if (SUCCEEDED(hr)) { + hr = pSample->GetSampleTime(&hnsTime); + } + if (SUCCEEDED(hr)) { + hr = pSample->GetSampleDuration(&hnsDuration); + } + if (SUCCEEDED(hr)) { + hr = GetMajorType(&guidMajorType); + } + if (SUCCEEDED(hr)) { + hr = m_pParent->GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + } + if (SUCCEEDED(hr)) { + hr = pInput->Lock(&pSrc, &cbMaxLength, &cbCurrentLength); + } + if (SUCCEEDED(hr)) { + hr = pSampleCallback->OnProcessSample(guidMajorType, 0, hnsTime, hnsDuration, pSrc, cbCurrentLength); + pInput->Unlock(); + } + if (SUCCEEDED(hr)) { + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); + } + } + LeaveCriticalSection(&m_critSec); + return hr; + } + + HRESULT STDMETHODCALLTYPE PlaceMarker( + /* [in] */ MFSTREAMSINK_MARKER_TYPE eMarkerType, + /* [in] */ __RPC__in const PROPVARIANT * /*pvarMarkerValue*/, + /* [in] */ __RPC__in const PROPVARIANT * /*pvarContextValue*/) { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + if (m_state == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + + if (SUCCEEDED(hr)) + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::PlaceMarker: HRESULT=%i %s\n", hr, StreamSinkMarkerTypeMap.at(eMarkerType).c_str()); + return hr; + } + + HRESULT STDMETHODCALLTYPE Flush(void) { + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::Flush: HRESULT=%i\n", hr); + return hr; + } + + //IMFMediaEventGenerator + HRESULT STDMETHODCALLTYPE GetEvent( + DWORD dwFlags, IMFMediaEvent **ppEvent) { + // NOTE: + // GetEvent can block indefinitely, so we don't hold the lock. + // This requires some juggling with the event queue pointer. + + HRESULT hr = S_OK; + + ComPtr pQueue; + + { + EnterCriticalSection(&m_critSec); + + // Check shutdown + hr = CheckShutdown(); + + // Get the pointer to the event queue. + if (SUCCEEDED(hr)) + { + pQueue = m_spEventQueue.Get(); + } + LeaveCriticalSection(&m_critSec); + } + + // Now get the event. + if (SUCCEEDED(hr)) + { + hr = pQueue->GetEvent(dwFlags, ppEvent); + } + MediaEventType meType = MEUnknown; + if (SUCCEEDED(hr) && SUCCEEDED((*ppEvent)->GetType(&meType)) && meType == MEStreamSinkStopped) { + } + HRESULT hrStatus = S_OK; + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + if (SUCCEEDED(hr)) + hr = (*ppEvent)->GetStatus(&hrStatus); + if (SUCCEEDED(hr)) + DPO->printOut(L"StreamSink::GetEvent: HRESULT=%i %s\n", hrStatus, MediaEventTypeMap.at(meType).c_str()); + else + DPO->printOut(L"StreamSink::GetEvent: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE BeginGetEvent( + IMFAsyncCallback *pCallback, IUnknown *punkState) { + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_spEventQueue->BeginGetEvent(pCallback, punkState); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::BeginGetEvent: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE EndGetEvent( + IMFAsyncResult *pResult, IMFMediaEvent **ppEvent) { + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_spEventQueue->EndGetEvent(pResult, ppEvent); + } + + MediaEventType meType = MEUnknown; + if (SUCCEEDED(hr) && SUCCEEDED((*ppEvent)->GetType(&meType)) && meType == MEStreamSinkStopped) { + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + HRESULT hrStatus = S_OK; + if (SUCCEEDED(hr)) + hr = (*ppEvent)->GetStatus(&hrStatus); + if (SUCCEEDED(hr)) + DPO->printOut(L"StreamSink::EndGetEvent: HRESULT=%i %s\n", hrStatus, MediaEventTypeMap.at(meType).c_str()); + else + DPO->printOut(L"StreamSink::EndGetEvent: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE QueueEvent( + MediaEventType met, REFGUID guidExtendedType, + HRESULT hrStatus, const PROPVARIANT *pvValue) { + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_spEventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::QueueEvent: HRESULT=%i %s\n", hrStatus, MediaEventTypeMap.at(met).c_str()); + DPO->printOut(L"StreamSink::QueueEvent: HRESULT=%i\n", hr); + return hr; + } + + /// IMFMediaTypeHandler methods + + // Check if a media type is supported. + STDMETHODIMP IsMediaTypeSupported( + /* [in] */ IMFMediaType *pMediaType, + /* [out] */ IMFMediaType **ppMediaType) + { + if (pMediaType == nullptr) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + GUID majorType = GUID_NULL; + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = pMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType); + } + + // First make sure it's video or audio type. + if (SUCCEEDED(hr)) + { + if (majorType != MFMediaType_Video && majorType != MFMediaType_Audio) + { + hr = MF_E_INVALIDTYPE; + } + } + + if (SUCCEEDED(hr) && m_spCurrentType != nullptr) + { + GUID guiNewSubtype; + if (FAILED(pMediaType->GetGUID(MF_MT_SUBTYPE, &guiNewSubtype)) || + guiNewSubtype != m_guiCurrentSubtype) + { + hr = MF_E_INVALIDTYPE; + } + } + if (ppMediaType) + { + *ppMediaType = nullptr; + } + + if (ppMediaType && SUCCEEDED(hr)) { + ComPtr pType; + hr = MFCreateMediaType(ppMediaType); + if (SUCCEEDED(hr)) { + hr = m_pParent->GetUnknown(MF_MEDIASINK_PREFERREDTYPE, __uuidof(IMFMediaType), (LPVOID*)&pType); + } + if (SUCCEEDED(hr)) { + hr = pType->LockStore(); + } + bool bLocked = false; + if (SUCCEEDED(hr)) { + bLocked = true; + UINT32 uiCount; + UINT32 uiTotal; + hr = pType->GetCount(&uiTotal); + for (uiCount = 0; SUCCEEDED(hr) && uiCount < uiTotal; uiCount++) { + GUID guid; + PROPVARIANT propval; + hr = pType->GetItemByIndex(uiCount, &guid, &propval); + if (SUCCEEDED(hr) && (guid == MF_MT_FRAME_SIZE || guid == MF_MT_MAJOR_TYPE || guid == MF_MT_PIXEL_ASPECT_RATIO || + guid == MF_MT_ALL_SAMPLES_INDEPENDENT || guid == MF_MT_INTERLACE_MODE || guid == MF_MT_SUBTYPE)) { + hr = (*ppMediaType)->SetItem(guid, propval); + PropVariantClear(&propval); + } + } + } + if (bLocked) { + hr = pType->UnlockStore(); + } + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::IsMediaTypeSupported: HRESULT=%i\n", hr); + return hr; + } + + + // Return the number of preferred media types. + STDMETHODIMP GetMediaTypeCount(DWORD *pdwTypeCount) + { + if (pdwTypeCount == nullptr) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + // We've got only one media type + *pdwTypeCount = 1; + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::GetMediaTypeCount: HRESULT=%i\n", hr); + return hr; + } + + + // Return a preferred media type by index. + STDMETHODIMP GetMediaTypeByIndex( + /* [in] */ DWORD dwIndex, + /* [out] */ IMFMediaType **ppType) + { + if (ppType == NULL) { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (dwIndex > 0) + { + hr = MF_E_NO_MORE_TYPES; + } else { + //return preferred type based on media capture library 6 elements preferred preview type + //hr = m_spCurrentType.CopyTo(ppType); + if (SUCCEEDED(hr)) { + ComPtr pType; + hr = MFCreateMediaType(ppType); + if (SUCCEEDED(hr)) { + hr = m_pParent->GetUnknown(MF_MEDIASINK_PREFERREDTYPE, __uuidof(IMFMediaType), (LPVOID*)&pType); + } + if (SUCCEEDED(hr)) { + hr = pType->LockStore(); + } + bool bLocked = false; + if (SUCCEEDED(hr)) { + bLocked = true; + UINT32 uiCount; + UINT32 uiTotal; + hr = pType->GetCount(&uiTotal); + for (uiCount = 0; SUCCEEDED(hr) && uiCount < uiTotal; uiCount++) { + GUID guid; + PROPVARIANT propval; + hr = pType->GetItemByIndex(uiCount, &guid, &propval); + if (SUCCEEDED(hr) && (guid == MF_MT_FRAME_SIZE || guid == MF_MT_MAJOR_TYPE || guid == MF_MT_PIXEL_ASPECT_RATIO || + guid == MF_MT_ALL_SAMPLES_INDEPENDENT || guid == MF_MT_INTERLACE_MODE || guid == MF_MT_SUBTYPE)) { + hr = (*ppType)->SetItem(guid, propval); + PropVariantClear(&propval); + } + } + } + if (bLocked) { + hr = pType->UnlockStore(); + } + } + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::GetMediaTypeByIndex: HRESULT=%i\n", hr); + return hr; + } + + + // Set the current media type. + STDMETHODIMP SetCurrentMediaType(IMFMediaType *pMediaType) + { + if (pMediaType == NULL) { + return E_INVALIDARG; + } + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + if (m_state != State_TypeNotSet && m_state != State_Ready) + hr = MF_E_INVALIDREQUEST; + if (SUCCEEDED(hr)) + hr = CheckShutdown(); + + // We don't allow format changes after streaming starts. + + // We set media type already + if (m_state >= State_Ready) + { + if (SUCCEEDED(hr)) + { + hr = IsMediaTypeSupported(pMediaType, NULL); + } + } + + if (SUCCEEDED(hr)) + { + GUID guiMajorType; + pMediaType->GetMajorType(&guiMajorType); + + hr = MFCreateMediaType(m_spCurrentType.ReleaseAndGetAddressOf()); + if (SUCCEEDED(hr)) + { + hr = pMediaType->CopyAllItems(m_spCurrentType.Get()); + } + if (SUCCEEDED(hr)) + { + hr = m_spCurrentType->GetGUID(MF_MT_SUBTYPE, &m_guiCurrentSubtype); + } + if (SUCCEEDED(hr)) { + hr = MFGetAttributeSize(m_spCurrentType.Get(), MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels); + } + if (SUCCEEDED(hr)) + { + m_state = State_Ready; + } + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::SetCurrentMediaType: HRESULT=%i\n", hr); + return hr; + } + + // Return the current media type, if any. + STDMETHODIMP GetCurrentMediaType(IMFMediaType **ppMediaType) + { + if (ppMediaType == NULL) { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) { + if (m_spCurrentType == nullptr) { + hr = MF_E_NOT_INITIALIZED; + } + } + + if (SUCCEEDED(hr)) { + hr = m_spCurrentType.CopyTo(ppMediaType); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::GetCurrentMediaType: HRESULT=%i\n", hr); + return hr; + } + + + // Return the major type GUID. + STDMETHODIMP GetMajorType(GUID *pguidMajorType) + { + HRESULT hr; + if (pguidMajorType == nullptr) { + return E_INVALIDARG; + } + + ComPtr pType; + hr = m_pParent->GetUnknown(MF_MEDIASINK_PREFERREDTYPE, __uuidof(IMFMediaType), (LPVOID*)&pType); + if (SUCCEEDED(hr)) { + hr = pType->GetMajorType(pguidMajorType); + } + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"StreamSink::GetMajorType: HRESULT=%i\n", hr); + return hr; + } +private: +#ifdef HAVE_WINRT + EventRegistrationToken m_token; +#else + bool m_bConnected; +#endif + + bool m_IsShutdown; // Flag to indicate if Shutdown() method was called. + CRITICAL_SECTION m_critSec; +#ifndef HAVE_WINRT + long m_cRef; +#endif + IMFAttributes* m_pParent; + ComPtr m_spCurrentType; + ComPtr m_spEventQueue; // Event queue + + ComPtr m_spFTM; + State m_state; + bool m_fGetStartTimeFromSample; + bool m_fWaitingForFirstSample; + MFTIME m_StartTime; // Presentation time when the clock started. + GUID m_guiCurrentSubtype; + UINT32 m_imageWidthInPixels; + UINT32 m_imageHeightInPixels; +}; + +// Notes: +// +// The List class template implements a simple double-linked list. +// It uses STL's copy semantics. + +// There are two versions of the Clear() method: +// Clear(void) clears the list w/out cleaning up the object. +// Clear(FN fn) takes a functor object that releases the objects, if they need cleanup. + +// The List class supports enumeration. Example of usage: +// +// List::POSIITON pos = list.GetFrontPosition(); +// while (pos != list.GetEndPosition()) +// { +// T item; +// hr = list.GetItemPos(&item); +// pos = list.Next(pos); +// } + +// The ComPtrList class template derives from List<> and implements a list of COM pointers. + +template +struct NoOp +{ + void operator()(T& /*t*/) + { + } +}; + +template +class List +{ +protected: + + // Nodes in the linked list + struct Node + { + Node *prev; + Node *next; + T item; + + Node() : prev(nullptr), next(nullptr) + { + } + + Node(T item) : prev(nullptr), next(nullptr) + { + this->item = item; + } + + T Item() const { return item; } + }; + +public: + + // Object for enumerating the list. + class POSITION + { + friend class List; + + public: + POSITION() : pNode(nullptr) + { + } + + bool operator==(const POSITION &p) const + { + return pNode == p.pNode; + } + + bool operator!=(const POSITION &p) const + { + return pNode != p.pNode; + } + + private: + const Node *pNode; + + POSITION(Node *p) : pNode(p) + { + } + }; + +protected: + Node m_anchor; // Anchor node for the linked list. + DWORD m_count; // Number of items in the list. + + Node* Front() const + { + return m_anchor.next; + } + + Node* Back() const + { + return m_anchor.prev; + } + + virtual HRESULT InsertAfter(T item, Node *pBefore) + { + if (pBefore == nullptr) + { + return E_POINTER; + } + + Node *pNode = new Node(item); + if (pNode == nullptr) + { + return E_OUTOFMEMORY; + } + + Node *pAfter = pBefore->next; + + pBefore->next = pNode; + pAfter->prev = pNode; + + pNode->prev = pBefore; + pNode->next = pAfter; + + m_count++; + + return S_OK; + } + + virtual HRESULT GetItem(const Node *pNode, T* ppItem) + { + if (pNode == nullptr || ppItem == nullptr) + { + return E_POINTER; + } + + *ppItem = pNode->item; + return S_OK; + } + + // RemoveItem: + // Removes a node and optionally returns the item. + // ppItem can be nullptr. + virtual HRESULT RemoveItem(Node *pNode, T *ppItem) + { + if (pNode == nullptr) + { + return E_POINTER; + } + + assert(pNode != &m_anchor); // We should never try to remove the anchor node. + if (pNode == &m_anchor) + { + return E_INVALIDARG; + } + + + T item; + + // The next node's previous is this node's previous. + pNode->next->prev = pNode->prev; + + // The previous node's next is this node's next. + pNode->prev->next = pNode->next; + + item = pNode->item; + delete pNode; + + m_count--; + + if (ppItem) + { + *ppItem = item; + } + + return S_OK; + } + +public: + + List() + { + m_anchor.next = &m_anchor; + m_anchor.prev = &m_anchor; + + m_count = 0; + } + + virtual ~List() + { + Clear(); + } + + // Insertion functions + HRESULT InsertBack(T item) + { + return InsertAfter(item, m_anchor.prev); + } + + + HRESULT InsertFront(T item) + { + return InsertAfter(item, &m_anchor); + } + + HRESULT InsertPos(POSITION pos, T item) + { + if (pos.pNode == nullptr) + { + return InsertBack(item); + } + + return InsertAfter(item, pos.pNode->prev); + } + + // RemoveBack: Removes the tail of the list and returns the value. + // ppItem can be nullptr if you don't want the item back. (But the method does not release the item.) + HRESULT RemoveBack(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return RemoveItem(Back(), ppItem); + } + } + + // RemoveFront: Removes the head of the list and returns the value. + // ppItem can be nullptr if you don't want the item back. (But the method does not release the item.) + HRESULT RemoveFront(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return RemoveItem(Front(), ppItem); + } + } + + // GetBack: Gets the tail item. + HRESULT GetBack(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return GetItem(Back(), ppItem); + } + } + + // GetFront: Gets the front item. + HRESULT GetFront(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return GetItem(Front(), ppItem); + } + } + + + // GetCount: Returns the number of items in the list. + DWORD GetCount() const { return m_count; } + + bool IsEmpty() const + { + return (GetCount() == 0); + } + + // Clear: Takes a functor object whose operator() + // frees the object on the list. + template + void Clear(FN& clear_fn) + { + Node *n = m_anchor.next; + + // Delete the nodes + while (n != &m_anchor) + { + clear_fn(n->item); + + Node *tmp = n->next; + delete n; + n = tmp; + } + + // Reset the anchor to point at itself + m_anchor.next = &m_anchor; + m_anchor.prev = &m_anchor; + + m_count = 0; + } + + // Clear: Clears the list. (Does not delete or release the list items.) + virtual void Clear() + { + NoOp clearOp; + Clear<>(clearOp); + } + + + // Enumerator functions + + POSITION FrontPosition() + { + if (IsEmpty()) + { + return POSITION(nullptr); + } + else + { + return POSITION(Front()); + } + } + + POSITION EndPosition() const + { + return POSITION(); + } + + HRESULT GetItemPos(POSITION pos, T *ppItem) + { + if (pos.pNode) + { + return GetItem(pos.pNode, ppItem); + } + else + { + return E_FAIL; + } + } + + POSITION Next(const POSITION pos) + { + if (pos.pNode && (pos.pNode->next != &m_anchor)) + { + return POSITION(pos.pNode->next); + } + else + { + return POSITION(nullptr); + } + } + + // Remove an item at a position. + // The item is returns in ppItem, unless ppItem is nullptr. + // NOTE: This method invalidates the POSITION object. + HRESULT Remove(POSITION& pos, T *ppItem) + { + if (pos.pNode) + { + // Remove const-ness temporarily... + Node *pNode = const_cast(pos.pNode); + + pos = POSITION(); + + return RemoveItem(pNode, ppItem); + } + else + { + return E_INVALIDARG; + } + } + +}; + + + +// Typical functors for Clear method. + +// ComAutoRelease: Releases COM pointers. +// MemDelete: Deletes pointers to new'd memory. + +class ComAutoRelease +{ +public: + void operator()(IUnknown *p) + { + if (p) + { + p->Release(); + } + } +}; + +class MemDelete +{ +public: + void operator()(void *p) + { + if (p) + { + delete p; + } + } +}; + + +// ComPtrList class +// Derived class that makes it safer to store COM pointers in the List<> class. +// It automatically AddRef's the pointers that are inserted onto the list +// (unless the insertion method fails). +// +// T must be a COM interface type. +// example: ComPtrList +// +// NULLABLE: If true, client can insert nullptr pointers. This means GetItem can +// succeed but return a nullptr pointer. By default, the list does not allow nullptr +// pointers. + +template +class ComPtrList : public List +{ +public: + + typedef T* Ptr; + + void Clear() + { + ComAutoRelease car; + List::Clear(car); + } + + ~ComPtrList() + { + Clear(); + } + +protected: + HRESULT InsertAfter(Ptr item, Node *pBefore) + { + // Do not allow nullptr item pointers unless NULLABLE is true. + if (item == nullptr && !NULLABLE) + { + return E_POINTER; + } + + if (item) + { + item->AddRef(); + } + + HRESULT hr = List::InsertAfter(item, pBefore); + if (FAILED(hr) && item != nullptr) + { + item->Release(); + } + return hr; + } + + HRESULT GetItem(const Node *pNode, Ptr* ppItem) + { + Ptr pItem = nullptr; + + // The base class gives us the pointer without AddRef'ing it. + // If we return the pointer to the caller, we must AddRef(). + HRESULT hr = List::GetItem(pNode, &pItem); + if (SUCCEEDED(hr)) + { + assert(pItem || NULLABLE); + if (pItem) + { + *ppItem = pItem; + (*ppItem)->AddRef(); + } + } + return hr; + } + + HRESULT RemoveItem(Node *pNode, Ptr *ppItem) + { + // ppItem can be nullptr, but we need to get the + // item so that we can release it. + + // If ppItem is not nullptr, we will AddRef it on the way out. + + Ptr pItem = nullptr; + + HRESULT hr = List::RemoveItem(pNode, &pItem); + + if (SUCCEEDED(hr)) + { + assert(pItem || NULLABLE); + if (ppItem && pItem) + { + *ppItem = pItem; + (*ppItem)->AddRef(); + } + + if (pItem) + { + pItem->Release(); + pItem = nullptr; + } + } + + return hr; + } +}; + +/* Be sure to declare webcam device capability in manifest + For better media capture support, add the following snippet with correct module name to the project manifest + (highgui needs DLL activation class factoryentry points): + + + + modulename + + + + */ + +extern const __declspec(selectany) WCHAR RuntimeClass_CV_MediaSink[] = L"cv.MediaSink"; + +class MediaSink : +#ifdef HAVE_WINRT + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRtClassicComMix >, + Microsoft::WRL::Implements, + IMFMediaSink, + IMFClockStateSink, + FtmBase, + CBaseAttributes<>> +#else + public IMFMediaSink, public IMFClockStateSink, public CBaseAttributes<> +#endif +{ +#ifdef HAVE_WINRT + InspectableClass(RuntimeClass_CV_MediaSink, BaseTrust) +public: +#else +public: + ULONG AddRef() + { + return InterlockedIncrement(&m_cRef); + } + ULONG Release() + { + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + delete this; + } + return cRef; + } + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppv) + { + if (ppv == nullptr) { + return E_POINTER; + } + (*ppv) = nullptr; + HRESULT hr = S_OK; + if (riid == IID_IUnknown || + riid == IID_IMFMediaSink) { + (*ppv) = static_cast(this); + AddRef(); + } else if (riid == IID_IMFClockStateSink) { + (*ppv) = static_cast(this); + AddRef(); + } else if (riid == IID_IMFAttributes) { + (*ppv) = static_cast(this); + AddRef(); + } else { + hr = E_NOINTERFACE; + } + + return hr; + } +#endif + MediaSink() : m_IsShutdown(false), m_llStartTime(0) { + CBaseAttributes<>::Initialize(0U); + InitializeCriticalSectionEx(&m_critSec, 3000, 0); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::MediaSink\n"); + } + + virtual ~MediaSink() { + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::~MediaSink\n"); + DeleteCriticalSection(&m_critSec); + assert(m_IsShutdown); + } + HRESULT CheckShutdown() const + { + if (m_IsShutdown) + { + return MF_E_SHUTDOWN; + } + else + { + return S_OK; + } + } +#ifdef HAVE_WINRT + STDMETHODIMP SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration) + { + HRESULT hr = S_OK; + if (pConfiguration) { + Microsoft::WRL::ComPtr spInsp; + Microsoft::WRL::ComPtr> spSetting; + Microsoft::WRL::ComPtr spPropVal; + ComPtr pMedEncProps; + UINT32 uiType = ABI::Windows::Media::Capture::MediaStreamType_VideoPreview; + + hr = pConfiguration->QueryInterface(IID_PPV_ARGS(&spSetting)); + if (FAILED(hr)) { + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) { + hr = spSetting->Lookup(HStringReference(MF_PROP_SAMPLEGRABBERCALLBACK).Get(), spInsp.ReleaseAndGetAddressOf()); + if (FAILED(hr)) { + hr = E_INVALIDARG; + } + if (SUCCEEDED(hr)) { + hr = SetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, spInsp.Get()); + } + } + if (SUCCEEDED(hr)) { + hr = spSetting->Lookup(HStringReference(MF_PROP_VIDTYPE).Get(), spInsp.ReleaseAndGetAddressOf()); + if (FAILED(hr)) { + hr = E_INVALIDARG; + } + if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr = spInsp.As(&spPropVal))) { + hr = spPropVal->GetUInt32(&uiType); + } + } + } + if (SUCCEEDED(hr)) { + hr = spSetting->Lookup(HStringReference(MF_PROP_VIDENCPROPS).Get(), spInsp.ReleaseAndGetAddressOf()); + if (FAILED(hr)) { + hr = E_INVALIDARG; + } + if (SUCCEEDED(hr)) { + hr = spInsp.As(&pMedEncProps); + } + } + if (SUCCEEDED(hr)) { + hr = SetMediaStreamProperties((ABI::Windows::Media::Capture::MediaStreamType)uiType, pMedEncProps.Get()); + } + } + + return hr; + } + static DWORD GetStreamId(ABI::Windows::Media::Capture::MediaStreamType mediaStreamType) + { + return 3 - mediaStreamType; + } + static HRESULT AddAttribute(_In_ GUID guidKey, _In_ ABI::Windows::Foundation::IPropertyValue *pValue, _In_ IMFAttributes* pAttr) + { + HRESULT hr = S_OK; + PROPVARIANT var; + ABI::Windows::Foundation::PropertyType type; + hr = pValue->get_Type(&type); + ZeroMemory(&var, sizeof(var)); + + if (SUCCEEDED(hr)) + { + switch (type) + { + case ABI::Windows::Foundation::PropertyType_UInt8Array: + { + UINT32 cbBlob; + BYTE *pbBlog = nullptr; + hr = pValue->GetUInt8Array(&cbBlob, &pbBlog); + if (SUCCEEDED(hr)) + { + if (pbBlog == nullptr) + { + hr = E_INVALIDARG; + } + else + { + hr = pAttr->SetBlob(guidKey, pbBlog, cbBlob); + } + } + CoTaskMemFree(pbBlog); + } + break; + + case ABI::Windows::Foundation::PropertyType_Double: + { + DOUBLE value; + hr = pValue->GetDouble(&value); + if (SUCCEEDED(hr)) + { + hr = pAttr->SetDouble(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_Guid: + { + GUID value; + hr = pValue->GetGuid(&value); + if (SUCCEEDED(hr)) + { + hr = pAttr->SetGUID(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_String: + { + HSTRING value; + hr = pValue->GetString(&value); + if (SUCCEEDED(hr)) + { + UINT32 len = 0; + LPCWSTR szValue = WindowsGetStringRawBuffer(value, &len); + hr = pAttr->SetString(guidKey, szValue); + WindowsDeleteString(value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_UInt32: + { + UINT32 value; + hr = pValue->GetUInt32(&value); + if (SUCCEEDED(hr)) + { + pAttr->SetUINT32(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_UInt64: + { + UINT64 value; + hr = pValue->GetUInt64(&value); + if (SUCCEEDED(hr)) + { + hr = pAttr->SetUINT64(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_Inspectable: + { + ComPtr value; + hr = TYPE_E_TYPEMISMATCH; + if (SUCCEEDED(hr)) + { + pAttr->SetUnknown(guidKey, value.Get()); + } + } + break; + + // ignore unknown values + } + } + + return hr; + } + static HRESULT ConvertPropertiesToMediaType(_In_ ABI::Windows::Media::MediaProperties::IMediaEncodingProperties *pMEP, _Outptr_ IMFMediaType **ppMT) + { + HRESULT hr = S_OK; + ComPtr spMT; + ComPtr> spMap; + ComPtr*>> spIterable; + ComPtr*>> spIterator; + + if (pMEP == nullptr || ppMT == nullptr) + { + return E_INVALIDARG; + } + *ppMT = nullptr; + + hr = pMEP->get_Properties(&spMap); + + if (SUCCEEDED(hr)) + { + hr = spMap.As(&spIterable); + } + if (SUCCEEDED(hr)) + { + hr = spIterable->First(&spIterator); + } + if (SUCCEEDED(hr)) + { + MFCreateMediaType(spMT.ReleaseAndGetAddressOf()); + } + + boolean hasCurrent = false; + if (SUCCEEDED(hr)) + { + hr = spIterator->get_HasCurrent(&hasCurrent); + } + + while (hasCurrent) + { + ComPtr > spKeyValuePair; + ComPtr spValue; + ComPtr spPropValue; + GUID guidKey; + + hr = spIterator->get_Current(&spKeyValuePair); + if (FAILED(hr)) + { + break; + } + hr = spKeyValuePair->get_Key(&guidKey); + if (FAILED(hr)) + { + break; + } + hr = spKeyValuePair->get_Value(&spValue); + if (FAILED(hr)) + { + break; + } + hr = spValue.As(&spPropValue); + if (FAILED(hr)) + { + break; + } + hr = AddAttribute(guidKey, spPropValue.Get(), spMT.Get()); + if (FAILED(hr)) + { + break; + } + + hr = spIterator->MoveNext(&hasCurrent); + if (FAILED(hr)) + { + break; + } + } + + + if (SUCCEEDED(hr)) + { + ComPtr spValue; + ComPtr spPropValue; + GUID guiMajorType; + + hr = spMap->Lookup(MF_MT_MAJOR_TYPE, spValue.GetAddressOf()); + + if (SUCCEEDED(hr)) + { + hr = spValue.As(&spPropValue); + } + if (SUCCEEDED(hr)) + { + hr = spPropValue->GetGuid(&guiMajorType); + } + if (SUCCEEDED(hr)) + { + if (guiMajorType != MFMediaType_Video && guiMajorType != MFMediaType_Audio) + { + hr = E_UNEXPECTED; + } + } + } + + if (SUCCEEDED(hr)) + { + *ppMT = spMT.Detach(); + } + + return hr; + } + HRESULT SetMediaStreamProperties( + ABI::Windows::Media::Capture::MediaStreamType MediaStreamType, + _In_opt_ ABI::Windows::Media::MediaProperties::IMediaEncodingProperties *mediaEncodingProperties) + { + HRESULT hr = S_OK; + ComPtr spMediaType; + + if (MediaStreamType != ABI::Windows::Media::Capture::MediaStreamType_VideoPreview && + MediaStreamType != ABI::Windows::Media::Capture::MediaStreamType_VideoRecord && + MediaStreamType != ABI::Windows::Media::Capture::MediaStreamType_Audio) + { + return E_INVALIDARG; + } + + RemoveStreamSink(GetStreamId(MediaStreamType)); + + if (mediaEncodingProperties != nullptr) + { + ComPtr spStreamSink; + hr = ConvertPropertiesToMediaType(mediaEncodingProperties, &spMediaType); + if (SUCCEEDED(hr)) + { + hr = AddStreamSink(GetStreamId(MediaStreamType), nullptr, spStreamSink.GetAddressOf()); + } + if (SUCCEEDED(hr)) { + hr = SetUnknown(MF_MEDIASINK_PREFERREDTYPE, spMediaType.Detach()); + } + } + + return hr; + } +#endif + //IMFMediaSink + HRESULT STDMETHODCALLTYPE GetCharacteristics( + /* [out] */ __RPC__out DWORD *pdwCharacteristics) { + HRESULT hr; + if (pdwCharacteristics == NULL) return E_INVALIDARG; + EnterCriticalSection(&m_critSec); + if (SUCCEEDED(hr = CheckShutdown())) { + *pdwCharacteristics = MEDIASINK_FIXED_STREAMS; + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::GetCharacteristics: HRESULT=%i\n", hr); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE AddStreamSink( + DWORD dwStreamSinkIdentifier, IMFMediaType * /*pMediaType*/, IMFStreamSink **ppStreamSink) { + ComPtr spMFStream; + ComPtr pStream; + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = GetStreamSinkById(dwStreamSinkIdentifier, &spMFStream); + } + + if (SUCCEEDED(hr)) + { + hr = MF_E_STREAMSINK_EXISTS; + } + else + { + hr = S_OK; + } + + if (SUCCEEDED(hr)) + { +#ifdef HAVE_WINRT + pStream = Microsoft::WRL::Make(); + if (pStream == nullptr) { + hr = E_OUTOFMEMORY; + } + if (SUCCEEDED(hr)) + hr = pStream.As(&spMFStream); +#else + StreamSink* pSink = new StreamSink(); + if (pSink) { + hr = pSink->QueryInterface(IID_IMFStreamSink, (void**)spMFStream.GetAddressOf()); + if (SUCCEEDED(hr)) { + hr = spMFStream.As(&pStream); + } + if (FAILED(hr)) delete pSink; + } +#endif + } + + // Initialize the stream. + ComPtr pAttr; + if (SUCCEEDED(hr)) { + hr = pStream.As(&pAttr); + } + if (SUCCEEDED(hr)) { + hr = pAttr->SetUINT32(MF_STREAMSINK_ID, dwStreamSinkIdentifier); + if (SUCCEEDED(hr)) { + hr = pAttr->SetUnknown(MF_STREAMSINK_MEDIASINKINTERFACE, (IMFMediaSink*)this); + } + } + if (SUCCEEDED(hr)) { + hr = pStream->Initialize(); + } + + if (SUCCEEDED(hr)) + { + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION posEnd = m_streams.EndPosition(); + + // Insert in proper position + for (; pos != posEnd; pos = m_streams.Next(pos)) + { + DWORD dwCurrId; + ComPtr spCurr; + hr = m_streams.GetItemPos(pos, &spCurr); + if (FAILED(hr)) + { + break; + } + hr = spCurr->GetIdentifier(&dwCurrId); + if (FAILED(hr)) + { + break; + } + + if (dwCurrId > dwStreamSinkIdentifier) + { + break; + } + } + + if (SUCCEEDED(hr)) + { + hr = m_streams.InsertPos(pos, spMFStream.Get()); + } + } + + if (SUCCEEDED(hr)) + { + *ppStreamSink = spMFStream.Detach(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::AddStreamSink: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE RemoveStreamSink(DWORD dwStreamSinkIdentifier) { + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION endPos = m_streams.EndPosition(); + ComPtr spStream; + + if (SUCCEEDED(hr)) + { + for (; pos != endPos; pos = m_streams.Next(pos)) + { + hr = m_streams.GetItemPos(pos, &spStream); + DWORD dwId; + + if (FAILED(hr)) + { + break; + } + + hr = spStream->GetIdentifier(&dwId); + if (FAILED(hr) || dwId == dwStreamSinkIdentifier) + { + break; + } + } + + if (pos == endPos) + { + hr = MF_E_INVALIDSTREAMNUMBER; + } + } + + if (SUCCEEDED(hr)) + { + hr = m_streams.Remove(pos, nullptr); + static_cast(spStream.Get())->Shutdown(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::RemoveStreamSink: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetStreamSinkCount(DWORD *pStreamSinkCount) { + if (pStreamSinkCount == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + *pStreamSinkCount = m_streams.GetCount(); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::GetStreamSinkCount: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetStreamSinkByIndex( + DWORD dwIndex, IMFStreamSink **ppStreamSink) { + if (ppStreamSink == NULL) + { + return E_INVALIDARG; + } + + ComPtr spStream; + EnterCriticalSection(&m_critSec); + DWORD cStreams = m_streams.GetCount(); + + if (dwIndex >= cStreams) + { + return MF_E_INVALIDINDEX; + } + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION endPos = m_streams.EndPosition(); + DWORD dwCurrent = 0; + + for (; pos != endPos && dwCurrent < dwIndex; pos = m_streams.Next(pos), ++dwCurrent) + { + // Just move to proper position + } + + if (pos == endPos) + { + hr = MF_E_UNEXPECTED; + } + else + { + hr = m_streams.GetItemPos(pos, &spStream); + } + } + + if (SUCCEEDED(hr)) + { + *ppStreamSink = spStream.Detach(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::GetStreamSinkByIndex: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetStreamSinkById( + DWORD dwStreamSinkIdentifier, IMFStreamSink **ppStreamSink) { + if (ppStreamSink == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + ComPtr spResult; + + if (SUCCEEDED(hr)) + { + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION endPos = m_streams.EndPosition(); + + for (; pos != endPos; pos = m_streams.Next(pos)) + { + ComPtr spStream; + hr = m_streams.GetItemPos(pos, &spStream); + DWORD dwId; + + if (FAILED(hr)) + { + break; + } + + hr = spStream->GetIdentifier(&dwId); + if (FAILED(hr)) + { + break; + } + else if (dwId == dwStreamSinkIdentifier) + { + spResult = spStream; + break; + } + } + + if (pos == endPos) + { + hr = MF_E_INVALIDSTREAMNUMBER; + } + } + + if (SUCCEEDED(hr)) + { + assert(spResult); + *ppStreamSink = spResult.Detach(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::GetStreamSinkById: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE SetPresentationClock( + IMFPresentationClock *pPresentationClock) { + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + // If we already have a clock, remove ourselves from that clock's + // state notifications. + if (SUCCEEDED(hr)) { + if (m_spClock) { + hr = m_spClock->RemoveClockStateSink(this); + } + } + + // Register ourselves to get state notifications from the new clock. + if (SUCCEEDED(hr)) { + if (pPresentationClock) { + hr = pPresentationClock->AddClockStateSink(this); + } + } + + ComPtr pSampleCallback; + if (SUCCEEDED(hr)) { + // Release the pointer to the old clock. + // Store the pointer to the new clock. + m_spClock = pPresentationClock; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + } + LeaveCriticalSection(&m_critSec); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnSetPresentationClock(pPresentationClock); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::SetPresentationClock: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetPresentationClock( + IMFPresentationClock **ppPresentationClock) { + if (ppPresentationClock == NULL) { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) { + if (m_spClock == NULL) { + hr = MF_E_NO_CLOCK; // There is no presentation clock. + } else { + // Return the pointer to the caller. + hr = m_spClock.CopyTo(ppPresentationClock); + } + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::GetPresentationClock: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE Shutdown(void) { + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) { + ForEach(m_streams, ShutdownFunc()); + m_streams.Clear(); + m_spClock.ReleaseAndGetAddressOf(); + + hr = DeleteItem(MF_MEDIASINK_PREFERREDTYPE); + m_IsShutdown = true; + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::Shutdown: HRESULT=%i\n", hr); + return hr; + } + class ShutdownFunc + { + public: + HRESULT operator()(IMFStreamSink *pStream) const + { + static_cast(pStream)->Shutdown(); + return S_OK; + } + }; + + class StartFunc + { + public: + StartFunc(LONGLONG llStartTime) + : _llStartTime(llStartTime) + { + } + + HRESULT operator()(IMFStreamSink *pStream) const + { + return static_cast(pStream)->Start(_llStartTime); + } + + LONGLONG _llStartTime; + }; + + class StopFunc + { + public: + HRESULT operator()(IMFStreamSink *pStream) const + { + return static_cast(pStream)->Stop(); + } + }; + + template + HRESULT ForEach(ComPtrList &col, TFunc fn) + { + ComPtrList::POSITION pos = col.FrontPosition(); + ComPtrList::POSITION endPos = col.EndPosition(); + HRESULT hr = S_OK; + + for (; pos != endPos; pos = col.Next(pos)) + { + ComPtr spStream; + + hr = col.GetItemPos(pos, &spStream); + if (FAILED(hr)) + { + break; + } + + hr = fn(spStream.Get()); + } + + return hr; + } + //IMFClockStateSink + HRESULT STDMETHODCALLTYPE OnClockStart( + MFTIME hnsSystemTime, + LONGLONG llClockStartOffset) { + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + // Start each stream. + m_llStartTime = llClockStartOffset; + hr = ForEach(m_streams, StartFunc(llClockStartOffset)); + } + ComPtr pSampleCallback; + if (SUCCEEDED(hr)) + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + LeaveCriticalSection(&m_critSec); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockStart(hnsSystemTime, llClockStartOffset); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::OnClockStart: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockStop( + MFTIME hnsSystemTime) { + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + // Stop each stream + hr = ForEach(m_streams, StopFunc()); + } + ComPtr pSampleCallback; + if (SUCCEEDED(hr)) + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + LeaveCriticalSection(&m_critSec); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockStop(hnsSystemTime); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::OnClockStop: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockPause( + MFTIME hnsSystemTime) { + HRESULT hr; + ComPtr pSampleCallback; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockPause(hnsSystemTime); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::OnClockPause: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockRestart( + MFTIME hnsSystemTime) { + HRESULT hr; + ComPtr pSampleCallback; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockRestart(hnsSystemTime); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::OnClockRestart: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockSetRate( + MFTIME hnsSystemTime, + float flRate) { + HRESULT hr; + ComPtr pSampleCallback; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockSetRate(hnsSystemTime, flRate); + DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + DPO->printOut(L"MediaSink::OnClockSetRate: HRESULT=%i\n", hr); + return hr; + } +private: +#ifndef HAVE_WINRT + long m_cRef; +#endif + CRITICAL_SECTION m_critSec; + bool m_IsShutdown; + ComPtrList m_streams; + ComPtr m_spClock; + LONGLONG m_llStartTime; +}; + +#ifdef HAVE_WINRT +ActivatableClass(MediaSink); +#endif diff --git a/modules/highgui/src/ppltasks_winrt.h b/modules/highgui/src/ppltasks_winrt.h new file mode 100644 index 000000000..8cf91a3ed --- /dev/null +++ b/modules/highgui/src/ppltasks_winrt.h @@ -0,0 +1,6326 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* Modified for native C++ WRL support by Gregory Morse +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* ppltasks_winrt.h +* +* Parallel Patterns Library - PPL Tasks +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __cplusplus_winrt + +#include +#include +#include +#include +#include + +#ifndef _UITHREADCTXT_SUPPORT + +#ifdef WINAPI_FAMILY + +// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user +#include + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP /*IFSTRIP=IGN*/ + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP /*IFSTRIP=IGN*/ + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#else + #define _UITHREADCTXT_SUPPORT 1 +#endif + +#else + // Not supported without a WINAPI_FAMILY setting. + #define _UITHREADCTXT_SUPPORT 0 +#endif // #ifdef WINAPI_FAMILY + +#endif // #ifndef _UITHREADCTXT_SUPPORT + +#if _UITHREADCTXT_SUPPORT +#include +#endif // _UITHREADCTXT_SUPPORT + +#pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") +#pragma pack(push,_CRT_PACKING) + +#pragma warning(push) +#pragma warning(disable: 28197) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation +#pragma warning(disable: 4702) // Unreachable code - it is caused by user lambda throw exceptions + +// All CRT public header files are required to be protected from the macro new +#pragma push_macro("new") +#undef new + +#define __is_valid_winrt_type(_Type) (std::is_void<_Type>::value || \ + std::is_same<_Type, BYTE>::value || \ + std::is_same<_Type, INT16>::value || \ + std::is_same<_Type, UINT16>::value || \ + std::is_same<_Type, INT32>::value || \ + std::is_same<_Type, UINT32>::value || \ + std::is_same<_Type, INT64>::value || \ + std::is_same<_Type, UINT64>::value || \ + std::is_same<_Type, FLOAT>::value || \ + std::is_same<_Type, DOUBLE>::value || \ + std::is_same<_Type, WCHAR>::value || \ + std::is_same<_Type, boolean>::value || \ + std::is_same<_Type, HSTRING>::value || \ + std::is_same<_Type, IInspectable *>::value || \ + std::is_same<_Type, GUID>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::DateTime>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::TimeSpan>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Point>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Size>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Rect>::value || \ + std::is_same<_Type, BYTE*>::value || \ + std::is_same<_Type, INT16*>::value || \ + std::is_same<_Type, UINT16*>::value || \ + std::is_same<_Type, INT32*>::value || \ + std::is_same<_Type, UINT32*>::value || \ + std::is_same<_Type, INT64*>::value || \ + std::is_same<_Type, UINT64*>::value || \ + std::is_same<_Type, FLOAT*>::value || \ + std::is_same<_Type, DOUBLE*>::value || \ + std::is_same<_Type, WCHAR*>::value || \ + std::is_same<_Type, boolean*>::value || \ + std::is_same<_Type, HSTRING*>::value || \ + std::is_same<_Type, IInspectable **>::value || \ + std::is_same<_Type, GUID*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::DateTime*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::TimeSpan*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Point*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Size*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Rect*>::value) + +/// +/// The Concurrency_winrt namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see . +/// +/**/ +namespace Concurrency_winrt +{ +/// +/// A type that represents the terminal state of a task. Valid values are completed and canceled. +/// +/// +/**/ +typedef Concurrency::task_group_status task_status; + +template class task; +template <> class task; + +/// +/// Returns an indication of whether the task that is currently executing has received a request to cancel its +/// execution. Cancellation is requested on a task if the task was created with a cancellation token, and +/// the token source associated with that token is canceled. +/// +/// +/// true if the currently executing task has received a request for cancellation, false otherwise. +/// +/// +/// If you call this method in the body of a task and it returns true, you must respond with a call to +/// cancel_current_task to acknowledge the cancellation request, +/// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into +/// the canceled state. If you do not respond and continue execution, or return instead of calling +/// cancel_current_task, the task will enter the completed state when it is done. +/// state. +/// A task is not cancellable if it was created without a cancellation token. +/// +/// +/// +/// +/// +/**/ +_CRTIMP2 bool __cdecl is_task_cancellation_requested(); + +/// +/// Cancels the currently executing task. This function can be called from within the body of a task to abort the +/// task's execution and cause it to enter the canceled state. While it may be used in response to +/// the is_task_cancellation_requested function, you may +/// also use it by itself, to initiate cancellation of the task that is currently executing. +/// It is not a supported scenario to call this function if you are not within the body of a task. +/// Doing so will result in undefined behavior such as a crash or a hang in your application. +/// +/// +/// +/**/ +_CRTIMP2 __declspec(noreturn) void __cdecl cancel_current_task(); + +namespace details +{ + typedef UINT32 _Unit_type; + + struct _TypeSelectorNoAsync {}; + struct _TypeSelectorAsyncOperationOrTask {}; + struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncAction {}; + struct _TypeSelectorAsyncActionWithProgress {}; + struct _TypeSelectorAsyncOperationWithProgress {}; + + template + struct _NormalizeVoidToUnitType + { + typedef _Ty _Type; + }; + + template<> + struct _NormalizeVoidToUnitType + { + typedef _Unit_type _Type; + }; + + template + struct _IsUnwrappedAsyncSelector + { + static const bool _Value = true; + }; + + template<> + struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> + { + static const bool _Value = false; + }; + + template + struct _UnwrapTaskType + { + typedef _Ty _Type; + }; + + template + struct _UnwrapTaskType> + { + typedef _Ty _Type; + }; + + template + _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); + + _TypeSelectorNoAsync _AsyncOperationKindSelector(...); + + template + struct _Unhat + { + typedef _Type _Value; + }; + + template + struct _Unhat<_Type*> + { + typedef _Type _Value; + }; + + struct _NonUserType { public: int _Dummy; }; + + template + struct _ValueTypeOrRefType + { + typedef _NonUserType _Value; + }; + + template + struct _ValueTypeOrRefType<_Type, true> + { + typedef _Type _Value; + }; + + template + _T2 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*); + + template + _T1 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T1>*); + + template + struct _GetProgressType + { + typedef decltype(_ProgressTypeSelector(std::declval<_Type>())) _Value; + }; + + template