From 90920c2376be8e5ed92ffeb1021c08804223aa76 Mon Sep 17 00:00:00 2001 From: GregoryMorse Date: Sat, 14 Dec 2013 16:53:30 +0800 Subject: [PATCH] Update cap_msmf.cpp Add support for WinRT in the MF capture framework by removing the disallowed calls to enumerate devices and create a sample grabber sink and adding framework for the MediaCapture interface and a custom sink which interfaces with the sample grabber callback interface. The change requires discussion for making it completely functional as redundancy is required given that if the source is a video file, the old code pathways must be used. Otherwise all IMFMediaSession, IMFMediaSource, and IMFActivate code must use a MediaCapture code path and all sink code must use the CMediaSink custom sink. Support for the custom sink is extended to non-WinRT not for compatibility as Windows Vista client is a minimum regardless, but because it offers more flexibility, could be faster and is able to be used as an optionally different code path during sink creation based on a future configuration parameter. My discussion and proposal to finish this change: Devices are so easily enumerated through WinRT Windows.Devices namespace that wrapping the calls in a library is quite a chore for little benefit though to get the various modes and formats could still be a worthwhile project. For now conditional compilation to remove videodevices and any offending non-video file related activity in videodevice. In my opinion, this is a different , far less fundamental and important change which can possibly be done as a future project and also much more easily implemented in C++/CX. ImageGrabber has the IMFSampleGrabberSinkCallback replaced with a base class (SharedSampleGrabber) which also be is base class for ImageGrabberRT. This change is necessary as the custom sink does not require a thread to pump events which is done through MediaCapture already. IMFSampleGrabberSinkCallback is the common element between both models and that piece can be shared. Initializing the new ImageGrabberRT is as simple as passing an already initialized MediaCapture object and any video format/encoding parameters. The concurrency event is necessary to wait for completion and is the way the underlying, IAsyncAction wrappers in the task library work as well. Native WIN32 event objects would be an option if HAVE_CONCURRENCY is not defined. I could even imagine doing it with sleep/thread yield and InterlockedCompareExchange yet I am not enthusiastic about that approach either. Since there is a specific compiler HAVE_ for concurrency, I do not like pulling it in though I think for WinRT it is safe to say we will always have it available though should probably conditionally compile with the Interlocked option as WIN32 events would require HAVE_WIN32. It looks like C++/CX cannot be used for the IMediaExtension sink (which should not be a problem) as using COM objects requires WRL and though deriving from IMediaExtension can be done, there is little purpose without COM. Objects from C++/CX can be swapped to interact with objects from native C++ as Inspectable* can reinterpret_cast to the ref object IInspectable^ and vice-versa. A solution to the COM class with C++/CX would be great so we could have dual support. Also without #define for every WRL object in use, the code will get quite muddy given that the */^ would need to be ifdef'd everywhere. Update cap_msmf.cpp Fixed bugs and completed the change. I believe the new classes need to be moved to a header file as the file has become to large and more classes need to be added for handling all the asynchronous problems (one wrapping IAsyncAction in a task and another for making a task out of IAsyncAction). Unfortunately, blocking on the UI thread is not an option in WinRT so a synchronous architecture is considered "illegal" by Microsoft's standards even if implementable (C++/CX ppltasks library throws errors if you try it). Worse, either by design or a bug in the MF MediaCapture class with Custom Sinks causes a crash if stop/start previewing without reinitializing (spPreferredPreviewMediaType is fatally nulled). After decompiling Windows.Media.dll, I worked around this in my own projects by using an activate-able custom sink ID which strangely assigns 1 to this pointer allowing it to be reinitialized in what can only be described as a hack by Microsoft. This would add additional overhead to the project to implement especially for static libraries as it requires IDL/DLL exporting followed by manifest declaration. Better to document that it is not supported. Furthermore, an additional class for IMFAttributes should be implemented to make clean architecture for passing around attributes as opposed to directly calling non-COM interface calls on the objects and making use of SetProperties which would also be a set up for an object that uses the RuntimeClass activation ID. The remaining changes are not difficult and will be complete soon along with debug tracing messages. Update cap_msmf.cpp Create cap_msmf.h Update cap_msmf.cpp Update cap_msmf.h Update cap_msmf.cpp Update cap_msmf.h Update and rename cap_msmf.h to cap_msmf.hpp Update cap_msmf.cpp Update CMakeLists.txt Update cap_msmf.hpp Update cap_msmf.cpp Update cap_msmf.cpp Update cap_msmf.cpp Update cap_msmf.hpp Update cap_msmf.hpp Update cap_msmf.cpp Successful test - samples are grabbed Update cap_msmf.cpp Update cap_msmf.hpp Update cap_msmf.cpp Update cap_msmf.hpp Update cap_msmf.hpp Update cap_msmf.cpp Update cap_msmf.hpp Update cap_msmf.cpp Update cap_msmf.hpp Update cap_msmf.cpp Update cap_msmf.hpp Create ppltasks_winrt.h Update cap_msmf.hpp Update cap_msmf.hpp Update cap_msmf.cpp Update ppltasks_winrt.h Library updated and cleaned up with comments, marshaling, exceptions and linker settings Update ppltasks_winrt.h Fixed trailing whitespace --- modules/highgui/CMakeLists.txt | 4 + modules/highgui/src/cap_msmf.cpp | 783 +++- modules/highgui/src/cap_msmf.hpp | 3187 +++++++++++++ modules/highgui/src/ppltasks_winrt.h | 6326 ++++++++++++++++++++++++++ 4 files changed, 10230 insertions(+), 70 deletions(-) create mode 100644 modules/highgui/src/cap_msmf.hpp create mode 100644 modules/highgui/src/ppltasks_winrt.h diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 51ab0c3ef4..a347a4dfce 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 df52f04bdf..80b17cafac 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 0000000000..f755fd7808 --- /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 0000000000..8cf91a3edb --- /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