diff --git a/avstream/avscamera/DMFT/AvsCameraDMFT.cpp b/avstream/avscamera/DMFT/AvsCameraDMFT.cpp index e12cff652..93f4a3422 100644 --- a/avstream/avscamera/DMFT/AvsCameraDMFT.cpp +++ b/avstream/avscamera/DMFT/AvsCameraDMFT.cpp @@ -6,6 +6,13 @@ #ifdef MF_WPP #include "AvsCameraDMFT.tmh" //--REF_ANALYZER_DONT_REMOVE-- #endif + +// TODO: required to avoid bug OS bug 36971659 in extended property handling that introduces a 16 bytes cookie +typedef struct +{ + KSCAMERA_EXTENDEDPROP_HEADER header; +} KSCAMERA_EXTENDEDPROP_HEADER_BUFFERED, * PKSCAMERA_EXTENDEDPROP_HEADER_BUFFERED; + // // This DeviceMFT is a stripped down implementation of the device MFT Sample present in the sample Repo // The original DMFT is present at https://github.com/microsoft/Windows-driver-samples/tree/main/avstream/sampledevicemft @@ -18,8 +25,13 @@ CMultipinMft::CMultipinMft() m_lWorkQueuePriority ( 0 ), m_spAttributes( nullptr ), m_spSourceTransform( nullptr ), - m_SymbolicLink(nullptr) - + m_SymbolicLink(nullptr), + m_hSelectedProfileKSEvent { nullptr }, + m_hSelectedProfileKSEventSentToDriver { nullptr }, + m_isProfileDDISupportedInBaseDriver{}, + m_selectedProfileId { KSCAMERAPROFILE_Legacy, 0, 0 }, + m_profileCallback {this, &CMultipinMft::ProfileAsyncResultCallback }, + m_profileAsyncResult {nullptr} { HRESULT hr = S_OK; ComPtr pAttributes = nullptr; @@ -637,7 +649,10 @@ IFACEMETHODIMP CMultipinMft::ProcessInput( { goto done; } - + if (m_selectedProfileId.Type == KSCAMERAPROFILE_FaceAuth_Mode) + { + // DMFT might switch to different behavior when profile, KSCAMERAPROFILE_FaceAuth_Mode is selected. + } DMFTCHECKHR_GOTO(spInPin->SendSample( pSample ), done ); done: DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); @@ -990,13 +1005,17 @@ IFACEMETHODIMP CMultipinMft::KsProperty( --*/ { HRESULT hr = S_OK; - - DMFTCHECKHR_GOTO(m_spIkscontrol->KsProperty(pProperty, - ulPropertyLength, - pvPropertyData, - ulDataLength, - pulBytesReturned),done); + if (pProperty->Set == KSPROPERTYSETID_ExtendedCameraControl && pProperty->Id == KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE) + { + DMFTCHECKHR_GOTO(ProfilePropertyHandler(pProperty, ulPropertyLength, pvPropertyData, ulDataLength, pulBytesReturned), done); + } + else + { + DMFTCHECKHR_GOTO(m_spIkscontrol->KsProperty(pProperty, ulPropertyLength, pvPropertyData, + ulDataLength, pulBytesReturned), done); + } done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); return hr; } @@ -1039,14 +1058,57 @@ IFACEMETHODIMP CMultipinMft::KsEvent( Implements IKSProperty::KsEvent function. --*/ { - + CAutoLock Lock(m_critSec); HRESULT hr = S_OK; - // Handle the events here if you want, This sample passes the events to the driver - DMFTCHECKHR_GOTO(m_spIkscontrol->KsEvent(pEvent, - ulEventLength, - pEventData, - ulDataLength, - pBytesReturned), done); + // handle the event if it is to set profile + if (pEvent != nullptr + && ulEventLength >= sizeof(KSEVENT) + && pEvent->Set == KSEVENTSETID_ExtendedCameraControl + && pEventData != nullptr + && ulDataLength >= sizeof(KSEVENTDATA) + && (pEvent->Id == KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE)) + { + + m_hSelectedProfileKSEvent.reset(); + if (DuplicateHandle( + GetCurrentProcess(), + ((KSEVENTDATA*)(pEventData))->EventHandle.Event, + GetCurrentProcess(), + &m_hSelectedProfileKSEvent, + 0, + FALSE, + DUPLICATE_SAME_ACCESS) == false) + { + hr = E_INVALIDARG; + DMFTCHECKHR_GOTO(hr, done); + } + if (m_isProfileDDISupportedInBaseDriver.value_or(true)) + { + hr = m_hSelectedProfileKSEventSentToDriver.create(); + DMFTCHECKHR_GOTO(hr, done); + KSEVENTDATA driverEventData = {}; + driverEventData.NotificationType = KSEVENTF_EVENT_HANDLE; + driverEventData.EventHandle.Event = m_hSelectedProfileKSEventSentToDriver.get(); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "Handling profile set KsEvent, created profile KsEvent handle for driver: %p", m_hSelectedProfileKSEventSentToDriver.get()); + // defer to source device + HRESULT hr2 = m_spIkscontrol->KsEvent(pEvent, ulEventLength, (void*)(&driverEventData), ulDataLength, pBytesReturned); + if (FAILED(hr2)) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "Failed to send profile KsEvent handle to driver: %p | hr=0x%08x", m_hSelectedProfileKSEventSentToDriver.get(), hr); + m_hSelectedProfileKSEventSentToDriver.reset(); + m_isProfileDDISupportedInBaseDriver = false; + } + } + } + else { + // Pass the events to the driver + hr = m_spIkscontrol->KsEvent(pEvent, + ulEventLength, + pEventData, + ulDataLength, + pBytesReturned); + DMFTCHECKHR_GOTO(hr, done); + } done: return hr; } @@ -1293,6 +1355,111 @@ IFACEMETHODIMP CMultipinMft::Shutdown( return ShutdownEventGenerator(); } +/*++ +Description: +Implements the KsProperty KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE handler. +--*/ + +HRESULT CMultipinMft::ProfilePropertyHandler( + _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_to_(ulDataLength, *pulBytesReturned) LPVOID pPropertyData, + _In_ ULONG ulDataLength, + _Inout_ PULONG pulBytesReturned) try +{ + UNREFERENCED_PARAMETER(ulPropertyLength); + HRESULT hr = S_OK; + CAutoLock Lock(m_critSec); + if (pProperty->Flags & KSPROPERTY_TYPE_SET) + { + DMFTCHECKNULL_GOTO(pulBytesReturned, done, E_POINTER); + *pulBytesReturned = sizeof(KSCAMERA_EXTENDEDPROP_HEADER_BUFFERED) + sizeof(KSCAMERA_EXTENDEDPROP_PROFILE); + if (ulDataLength < *pulBytesReturned) + { + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + if (pPropertyData) + { + PBYTE pPayload = (PBYTE)pPropertyData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = &((PKSCAMERA_EXTENDEDPROP_HEADER_BUFFERED)pPayload)->header; + KSCAMERA_EXTENDEDPROP_PROFILE* pProfile = (PKSCAMERA_EXTENDEDPROP_PROFILE)(pExtendedHeader + 1); + + m_selectedProfileId.Type = pProfile->ProfileId; + m_selectedProfileId.Index = pProfile->Index; + m_selectedProfileId.Unused = pProfile->Reserved; + + if (m_selectedProfileId.Type == GUID_NULL) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_WARNING, "The caller incorrectly sets GUID_NULL, default back to legacy."); + m_selectedProfileId = { KSCAMERAPROFILE_Legacy, 0, 0 }; + } + // if we should relay this SET call down to base driver + if (m_isProfileDDISupportedInBaseDriver.value_or(true)) + { + hr = m_spIkscontrol->KsProperty(pProperty, ulPropertyLength, pPropertyData, ulDataLength, pulBytesReturned); + // if base camera does not support the provided profile, set the base camera ksevent right away in case it did accept it so that we don't await it later on + if (FAILED(hr)) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_WARNING, "Failed Profile DDI SET, base camera hr=0x%08x", hr); + if (m_hSelectedProfileKSEventSentToDriver != nullptr) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "Completing driver profile KsEvent handle: %p", m_hSelectedProfileKSEventSentToDriver.get()); + m_hSelectedProfileKSEventSentToDriver.SetEvent(); + m_hSelectedProfileKSEventSentToDriver.reset(); + } + } + } + // signal we are done + // Queue an MF work item which will get invoked when the event is fired + DMFTCHECKNULL_GOTO(m_hSelectedProfileKSEvent, done, E_POINTER); + if (m_hSelectedProfileKSEventSentToDriver != nullptr) + { + m_profileAsyncResult.Reset(); + DMFTCHECKHR_GOTO(MFCreateAsyncResult(nullptr, &m_profileCallback, nullptr, &m_profileAsyncResult), done); + //KSCAMERAPROFILE is not cancelable. Not need to track MFWORKITEM_KEY + DMFTCHECKHR_GOTO(MFPutWaitingWorkItem(m_hSelectedProfileKSEventSentToDriver.get(), 0, m_profileAsyncResult.Get(), nullptr), done); + } + else + { + // if the profile set event was not sent to the driver, we can set our event right away + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "Completing profile KsEvent handle: %p", m_hSelectedProfileKSEvent.get()); + m_hSelectedProfileKSEvent.SetEvent(); + m_hSelectedProfileKSEvent.reset(); + } + } + } + else if (pProperty->Flags & KSPROPERTY_TYPE_GET) + { + DMFTCHECKNULL_GOTO(pulBytesReturned, done, E_POINTER); + *pulBytesReturned = sizeof(KSCAMERA_EXTENDEDPROP_HEADER_BUFFERED) + sizeof(KSCAMERA_EXTENDEDPROP_PROFILE); + if (ulDataLength < *pulBytesReturned) + { + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + if (pPropertyData) + { + if (!m_isProfileDDISupportedInBaseDriver.has_value()) + { + hr = m_spIkscontrol->KsProperty(pProperty, ulPropertyLength, pPropertyData, ulDataLength, pulBytesReturned); + m_isProfileDDISupportedInBaseDriver = SUCCEEDED(hr); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "Profile DDI GET support on base driver: %d, hr=0x%08x", m_isProfileDDISupportedInBaseDriver.value(), hr); + *pulBytesReturned = sizeof(KSCAMERA_EXTENDEDPROP_HEADER_BUFFERED) + sizeof(KSCAMERA_EXTENDEDPROP_PROFILE); + } + } + } + // --GETPAYLOAD-- + else if (pProperty->Flags & KSPROPERTY_TYPE_GETPAYLOADSIZE) + { + DMFTCHECKNULL_GOTO(pulBytesReturned, done, E_POINTER); + *pulBytesReturned = sizeof(KSCAMERA_EXTENDEDPROP_HEADER_BUFFERED) + sizeof(PKSCAMERA_EXTENDEDPROP_PROFILE); + } + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} CATCH_RETURN() + + // // Static method to create an instance of the MFT. // @@ -1309,3 +1476,21 @@ HRESULT CMultipinMft::CreateInstance(REFIID iid, void **ppMFT) DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); return hr; } + +HRESULT CMultipinMft::ProfileAsyncResultCallback(_In_ IMFAsyncResult* pResult) +{ + HRESULT hr = S_OK; + if (FAILED(pResult->GetStatus())) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_ERROR, + "Waiting for driver profile KsEvent handle: %p timed out, failing", + m_hSelectedProfileKSEventSentToDriver.get()); + m_hSelectedProfileKSEvent.SetEvent(); + m_hSelectedProfileKSEvent.reset(); + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + m_hSelectedProfileKSEventSentToDriver.reset(); + return hr; +} + + diff --git a/avstream/avscamera/DMFT/AvsCameraDMFT.h b/avstream/avscamera/DMFT/AvsCameraDMFT.h index 27b8607b5..5dce37239 100644 --- a/avstream/avscamera/DMFT/AvsCameraDMFT.h +++ b/avstream/avscamera/DMFT/AvsCameraDMFT.h @@ -6,7 +6,10 @@ #include "common.h" #include "mftpeventgenerator.h" #include "basepin.h" - +#include +#include +#include +#include // // The Below GUID is needed to transfer photoconfirmation sample successfully in the pipeline // It is used to propagate the mediatype of the sample to the pipeline which will consume the sample @@ -16,7 +19,6 @@ DEFINE_GUID(MFSourceReader_SampleAttribute_MediaType_priv, 0x0ea5c1e8, 0x9845, 0x41e0, 0xa2, 0x43, 0x72, 0x32, 0x07, 0xfc, 0x78, 0x1f); - interface IDirect3DDeviceManager9; // @@ -24,6 +26,66 @@ interface IDirect3DDeviceManager9; // class CMFAttributes; class CPinCreationFactory; + +// T: Type of the parent object +template +class MFAsyncCallback : public IMFAsyncCallback { +public: + typedef HRESULT(T::* InvokeFn)(IMFAsyncResult* pAsyncResult); + + MFAsyncCallback(T* pParent, InvokeFn fn) : + m_pParent(pParent), + m_pInvokeFn(fn) + { + } + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override { + if (!ppv) return E_POINTER; + if (iid == __uuidof(IUnknown)) + { + *ppv = static_cast(static_cast(this)); + } + else if (iid == __uuidof(IMFAsyncCallback)) + { + *ppv = static_cast(this); + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + AddRef(); + return S_OK; + } + + STDMETHODIMP_(ULONG) AddRef() override { + return InterlockedIncrement(&m_refCount); + } + + STDMETHODIMP_(ULONG) Release() override { + ULONG count = InterlockedDecrement(&m_refCount); + if (count == 0) delete this; + return count; + } + + // IMFAsyncCallback methods + STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue) override { + if (pdwFlags) *pdwFlags = 0; + if (pdwQueue) *pdwQueue = MFASYNC_CALLBACK_QUEUE_STANDARD; + return S_OK; + } + + STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult) override { + UNREFERENCED_PARAMETER(pAsyncResult); + return (m_pParent->*m_pInvokeFn)(pAsyncResult); + } + +private: + LONG m_refCount = 1; + T* m_pParent; + InvokeFn m_pInvokeFn; +}; + // // CMultipinMft class: // Implements a device proxy MFT. @@ -253,6 +315,8 @@ class CMultipinMft : _In_ UINT32 ); + HRESULT ProfileAsyncResultCallback(_In_ IMFAsyncResult* pResult); + protected: // @@ -275,9 +339,18 @@ class CMultipinMft : _In_opt_ IMFMediaType *pMediaType, _In_ DeviceStreamState newState ); + HRESULT BridgeInputPinOutputPin( _In_ CInPin* pInPin, _In_ COutPin* pOutPin); + + HRESULT ProfilePropertyHandler( + _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_to_(ulDataLength, *pulBytesReturned) LPVOID pPropertyData, + _In_ ULONG ulDataLength, + _Inout_ PULONG pulBytesReturned); + // //Inline functions // @@ -320,8 +393,15 @@ class CMultipinMft : UINT32 m_punValue; ComPtr m_spIkscontrol; ComPtr m_spAttributes; + map m_outputPinMap; // How output pins are connected to input pins i-><0..outpins> PWCHAR m_SymbolicLink; + wil::unique_event_nothrow m_hSelectedProfileKSEvent; + wil::unique_event_nothrow m_hSelectedProfileKSEventSentToDriver; + std::optional m_isProfileDDISupportedInBaseDriver; + SENSORPROFILEID m_selectedProfileId; + MFAsyncCallbackm_profileCallback; + ComPtr m_profileAsyncResult; }; @@ -331,4 +411,3 @@ inline HRESULT MFT_CreateInstance(REFIID riid, void **ppv) return CMultipinMft::CreateInstance(riid, ppv); } - diff --git a/avstream/avscamera/DMFT/AvsCameraDMFT.vcxproj b/avstream/avscamera/DMFT/AvsCameraDMFT.vcxproj index 3f6180f26..4243d22bb 100644 --- a/avstream/avscamera/DMFT/AvsCameraDMFT.vcxproj +++ b/avstream/avscamera/DMFT/AvsCameraDMFT.vcxproj @@ -136,7 +136,7 @@ AvsCameraDMFT - $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\wil\include + $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\wil\include @@ -162,6 +162,7 @@ %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD $(IntDir);%(AdditionalIncludeDirectories);..\..\common;..\common + stdcpp17 %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD @@ -178,6 +179,7 @@ %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD $(IntDir);%(AdditionalIncludeDirectories);..\..\common;..\common + stdcpp17 %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD @@ -194,6 +196,7 @@ %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD $(IntDir);%(AdditionalIncludeDirectories);..\..\common;..\common + stdcpp17 %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD @@ -210,6 +213,7 @@ %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD $(IntDir);%(AdditionalIncludeDirectories);..\..\common;..\common + stdcpp17 %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD