diff options
| author | Joel Corley <[email protected]> | 2020-01-21 15:47:24 -0800 |
|---|---|---|
| committer | Joel Corley <[email protected]> | 2020-01-21 15:47:24 -0800 |
| commit | 1d2865d33a2815c31a93bf9b352b94b096f72f57 (patch) | |
| tree | 1b5b52208d3d8254709b1a2f128618753d77ec58 | |
| parent | 8fb512ac674df5ba129a69906d450f2a1361136d (diff) | |
Update all the AvStream samples for Vibranium.
51 files changed, 1493 insertions, 751 deletions
diff --git a/avstream/avscamera/Package/package.VcxProj b/avstream/avscamera/Package/package.VcxProj index 5b8aa6ef..92690b1a 100644 --- a/avstream/avscamera/Package/package.VcxProj +++ b/avstream/avscamera/Package/package.VcxProj @@ -42,22 +42,18 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>true</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>false</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>true</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>false</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> diff --git a/avstream/avscamera/README.md b/avstream/avscamera/README.md index da8e8d65..ef956c88 100644 --- a/avstream/avscamera/README.md +++ b/avstream/avscamera/README.md @@ -1,26 +1,22 @@ ---- -page_type: sample -description: "Provides a pin-centric AVStream capture driver for a simulated front and back camera that performs simulated captures." -urlFragment: avscamera -languages: -- cpp -products: -- windows -- windows-wdk ---- +<!--- + name: AvsCamera - AVStream Camera Sample Driver + platform: WDM + language: cpp + category: Camera AVStream + description: Provides a pin-centric AVStream capture driver for a simulated front and back camera that performs simulated captures at 320x240 or 640x480 in RGB24, RGB32, YUY2 and NV12 formats at various frame rates. + samplefwlink: http://go.microsoft.com/fwlink/p/?LinkId=620184 +---> -# AvsCamera - AVStream Camera Sample Driver - -The AvsCamera sample provides a pin-centric AVStream capture driver for a simulated front and back camera. The driver performs simulated captures at 320x240 or 640x480 in RGB24, RGB32, YUY2 and NV12 formats at various frame rates. The purpose of the sample is to demonstrate how to write a fully functional AVStream camera driver. +AvsCamera: AVStream Camera Sample Driver +======================================== +The AvsCamera sample provides a pin-centric AVStream capture driver for a simulated front and back camera. The driver performs simulated captures at 320x240 or 640x480 in RGB24, RGB32, YUY2 and NV12 formats at various frame rates. The purpose of the sample is to demonstrate how to write a fully functional AVStream camera driver. This sample features strong parameter validation and overflow detection. It provides validation and simulation logic for all advanced camera controls in the CCaptureFilter class. A real camera driver would replace the filter automation table and CSensor and CSynthesizer class hierarchies to produce a new camera driver. The sample comes with its own MFT0 called AvsCameraMft0.dll. This MFT0 is used to parse metadata supplied in the AvsCamera driver samples. The metadata communications from the driver is primarily a private channel to its MFT0. The MFT0 is responsible for reformatting that information for the capture pipeline. ## Universal Windows Driver Compliant - This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. The sample works on 32-bit and 64-bit x86, amd64 and arm platforms. Once installed, the simulated camera should show in the Windows Inbox camera app. ## Building the sample - -The AvsCamera sample can be built by opening the AvsCamera.sln solution file. A successful build produces AvsCamera.sys, AvsCameraMft0.dll, AvsCamera.inf and AvsCamera.cat. +The AvsCamera sample can be built by opening the AvsCamera.sln solution file. A successful build produces AvsCamera.sys, AvsCameraMft0.dll, AvsCamera.inf and AvsCamera.cat. diff --git a/avstream/avscamera/mft0/AvsCameraMft0.vcxproj b/avstream/avscamera/mft0/AvsCameraMft0.vcxproj index 518a324f..1c59ed8f 100644 --- a/avstream/avscamera/mft0/AvsCameraMft0.vcxproj +++ b/avstream/avscamera/mft0/AvsCameraMft0.vcxproj @@ -31,7 +31,7 @@ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>False</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType /> <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> <ConfigurationType>DynamicLibrary</ConfigurationType> @@ -39,7 +39,7 @@ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>True</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType /> <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> <ConfigurationType>DynamicLibrary</ConfigurationType> @@ -47,7 +47,7 @@ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>False</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType /> <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> <ConfigurationType>DynamicLibrary</ConfigurationType> @@ -55,7 +55,7 @@ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>True</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType /> <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> <ConfigurationType>DynamicLibrary</ConfigurationType> @@ -201,7 +201,7 @@ <ResourceCompile Include="AvsCameraMft0.rc" /> </ItemGroup> <ItemGroup> - <Inf Exclude="@(Inf)" Include="*.inf" /> + <Inf Exclude="@(Inf)" Include="*.inf;*.inx" /> <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> </ItemGroup> <ItemGroup> @@ -213,4 +213,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/avstream/avscamera/mft0/MFT0Impl.cpp b/avstream/avscamera/mft0/MFT0Impl.cpp index 992be0e9..26d40f99 100644 --- a/avstream/avscamera/mft0/MFT0Impl.cpp +++ b/avstream/avscamera/mft0/MFT0Impl.cpp @@ -1950,14 +1950,14 @@ HRESULT CSocMft0::ParseMetadata_FaceDetection( } PMETADATA_FACEDATA pFaceData = (PMETADATA_FACEDATA)(pFaceHeader + 1); UINT32 cbRectSize = sizeof(FaceRectInfoBlobHeader) + (sizeof(FaceRectInfo) * (pFaceHeader->Count)); - BYTE *pRectBuf = new BYTE[cbRectSize]; + BYTE *pRectBuf = new (std::nothrow) BYTE[cbRectSize]; if (pRectBuf == NULL) { return E_OUTOFMEMORY; } UINT32 cbCharSize = sizeof(FaceCharacterizationBlobHeader) + (sizeof(FaceCharacterization) * (pFaceHeader->Count)); - BYTE *pCharBuf = new BYTE[cbCharSize]; + BYTE *pCharBuf = new (std::nothrow) BYTE[cbCharSize]; if (pCharBuf == NULL) { delete[] pRectBuf; diff --git a/avstream/avscamera/mft0/stdafx.h b/avstream/avscamera/mft0/stdafx.h index 037376ec..6d784805 100644 --- a/avstream/avscamera/mft0/stdafx.h +++ b/avstream/avscamera/mft0/stdafx.h @@ -16,7 +16,12 @@ #endif #include "targetver.h" +#if (NTDDI_VERSION >= NTDDI_WIN10_VB) +#pragma message("MFT0 is deprecated for this target Windows version and beyond- Change project settings to target an older version of Windows") +#endif + #if (NTDDI_VERSION <= NTDDI_WIN7) +// TODO: disable the MFT0 conditional to the target system version #pragma message("MFT0 is in not supported in this target Windows version - Change project settings to target a newer version of Windows") #endif diff --git a/avstream/avscamera/sys/AvsCamera.vcxproj b/avstream/avscamera/sys/AvsCamera.vcxproj index 326b1848..81d0078b 100644 --- a/avstream/avscamera/sys/AvsCamera.vcxproj +++ b/avstream/avscamera/sys/AvsCamera.vcxproj @@ -232,7 +232,7 @@ <ResourceCompile Include="AvsCamera.rc" /> </ItemGroup> <ItemGroup> - <Inf Exclude="@(Inf)" Include="*.inf" /> + <Inf Exclude="@(Inf)" Include="*.inf;*.inx" /> <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> </ItemGroup> <ItemGroup> @@ -244,4 +244,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/avstream/avscamera/sys/AvsCameraFilter.cpp b/avstream/avscamera/sys/AvsCameraFilter.cpp index af25b691..1a41997a 100644 --- a/avstream/avscamera/sys/AvsCameraFilter.cpp +++ b/avstream/avscamera/sys/AvsCameraFilter.cpp @@ -221,7 +221,7 @@ DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItems) CCaptureFilter::SetRoiIspControl, NULL, 0, NULL, NULL, 0 ), - DEFINE_PROP_ITEM(CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM, CExtendedVidProcSetting, Zoom), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM, CExtendedVidProcSetting, Zoom), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR, CExtendedProperty, VideoHDR ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VFR, CExtendedProperty, VFR ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED, CExtendedVidProcSetting, IsoAdvanced ), @@ -230,7 +230,8 @@ DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItems) DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_OIS, CExtendedProperty, OpticalImageStabilization ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO, CExtendedProperty, AdvancedPhoto ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE, CExtendedProfile, CameraProfile ), - DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOTEMPORALDENOISING, CExtendedProperty, VideoTemporalDenoising) + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOTEMPORALDENOISING, CExtendedProperty, VideoTemporalDenoising), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_RELATIVEPANELOPTIMIZATION, CExtendedProperty, RelativePanel) }; // Front-facing cameras often have limited capabilities. One way to express that is to use a restricted automation table such as this one. @@ -244,6 +245,7 @@ DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItemsFFC) DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES, CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE, CExtendedProperty, SceneMode ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE, CExtendedProperty, TorchMode ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_IRTORCHMODE, CExtendedVidProcSetting, IRTorch ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT, CExtendedProperty, OptimizationHint ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE, CExtendedVidProcSetting, WhiteBalance ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE, CExtendedVidProcSetting, Exposure ), @@ -258,11 +260,12 @@ DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItemsFFC) DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED, CExtendedVidProcSetting, IsoAdvanced ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION, CExtendedProperty, VideoStabilization ), DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO, CExtendedProperty, AdvancedPhoto ), - DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE, CExtendedProfile, CameraProfile ) + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE, CExtendedProfile, CameraProfile ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_RELATIVEPANELOPTIMIZATION, CExtendedProperty, RelativePanel) }; //-------------------------------------------------------------- -// Legacy Camera Contorl properties +// Legacy Camera Control properties DEFINE_KSPROPERTY_TABLE(CameraControlPropertyTable) { DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXPOSURE, KSPROPERTY_CAMERACONTROL_S, Exposure, &ExposureValues), diff --git a/avstream/avscamera/sys/Capture.cpp b/avstream/avscamera/sys/Capture.cpp index e5d43ddd..aadaca4e 100644 --- a/avstream/avscamera/sys/Capture.cpp +++ b/avstream/avscamera/sys/Capture.cpp @@ -2100,7 +2100,18 @@ Return Value: { PAGED_CODE( ); + // If the host process did not stop the pin, we need to do that before + // the CCapturePin object gets freed. + if (m_Pin->DeviceState != KSSTATE_STOP) + { + // Skip straight to the STOP state because our simulation doesn't + // really need the PAUSE and ACQUIRE transitions when stopping. + SetState(KSSTATE_STOP, m_Pin->DeviceState); + } + // Some reasonable default behavior. + GetFilter()->setPin(nullptr, m_Pin->Id); + return STATUS_SUCCESS; } diff --git a/avstream/avscamera/sys/Common.h b/avstream/avscamera/sys/Common.h index fdcb1c48..d29a0438 100644 --- a/avstream/avscamera/sys/Common.h +++ b/avstream/avscamera/sys/Common.h @@ -23,12 +23,17 @@ #define _NO_SYS_GUID_OPERATOR_EQ_ +#ifdef NTDDI_VERSION +#undef NTDDI_VERSION +#endif //NTDDI_VERSION +#define NTDDI_VERSION NTDDI_WIN10_VB + #include <stdio.h> #include <stdlib.h> #include <wdm.h> -#include <wdmguid.h> #include <windef.h> +//#include <handleapi.h> #include <unknown.h> #include <ks.h> #include <ksmedia.h> diff --git a/avstream/avscamera/sys/Device.cpp b/avstream/avscamera/sys/Device.cpp index 99bc60ce..9fab69d9 100644 --- a/avstream/avscamera/sys/Device.cpp +++ b/avstream/avscamera/sys/Device.cpp @@ -33,6 +33,10 @@ #pragma code_seg("PAGE") #endif // ALLOC_PRAGMA +#ifndef DEVPKEY_Device_PanelId +DEFINE_DEVPROPKEY(DEVPKEY_Device_PanelId, 0x8dbc9c86, 0x97a9, 0x4bff, 0x9b, 0xc6, 0xbf, 0xe9, 0x5d, 0x3e, 0x6d, 0xad, 2); // DEVPROP_TYPE_STRING +#endif + CCaptureDevice:: CCaptureDevice ( _In_ PKSDEVICE Device @@ -616,9 +620,12 @@ Return Value: pld.Revision = 2; pld.Panel = m_Context[i].AcpiPosition; pld.Rotation = m_Context[i].AcpiRotation; - static - const - GUID FFC_Filter = {STATIC_FrontCamera_Filter}; + pld.CabinetNumber = 0; + WCHAR PanelId[MAX_PATH] = { 0 }; + size_t cchDest = MAX_PATH; + + IFFAILED_EXIT( + RtlStringCchPrintfW(PanelId, cchDest, L"{00000000-0000-0000-ffff-ffffffffffff}\\%04X\\%u", pld.CabinetNumber, PnpAcpiPanelSideMap[pld.Panel])); IFFAILED_EXIT( IoSetDeviceInterfacePropertyData(SymbolicLinkName, @@ -629,6 +636,17 @@ Return Value: sizeof(pld), (PVOID)&pld) ); + + //Also set Panel ID manually, because PnP does not do this for the interface currently + // TODO: This is a bug in PnP that they will fix, so don't check the return code + // It will be read only when fixed. + IoSetDeviceInterfacePropertyData(SymbolicLinkName, + &DEVPKEY_Device_PanelId, + LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, + DEVPROP_TYPE_STRING, + (ULONG)(wcslen(PanelId)+1)*sizeof(WCHAR), + (PVOID)PanelId); } // On a real device this object would be constructed whenever the sensor hardware is ready... diff --git a/avstream/avscamera/sys/Device.h b/avstream/avscamera/sys/Device.h index 95debada..c203f70e 100644 --- a/avstream/avscamera/sys/Device.h +++ b/avstream/avscamera/sys/Device.h @@ -34,6 +34,17 @@ struct CSensorContext AcpiPldRotation AcpiRotation; // How the device is oriented with respect to the panel. }; +static constexpr ULONG PnpAcpiPanelSideMap[] +{ + 1, // AcpiPldPanelTop + 2, // AcpiPldPanelBottom + 3, // AcpiPldPanelLeft + 4, // AcpiPldPanelRight + 5, // AcpiPldPanelFront + 6, // AcpiPldPanelBack + 0, // AcpiPldPanelUnknown +}; + class CCaptureDevice { protected: @@ -393,7 +404,7 @@ public: PDEVICE_OBJECT GetDeviceObject(); - static IO_COMPLETION_ROUTINE CCaptureDevice::IrpSynchronousCompletion; + static IO_COMPLETION_ROUTINE IrpSynchronousCompletion; virtual NTSTATUS diff --git a/avstream/avscamera/sys/PreviewHwSim.cpp b/avstream/avscamera/sys/PreviewHwSim.cpp index 775c6b8d..f55ffa20 100644 --- a/avstream/avscamera/sys/PreviewHwSim.cpp +++ b/avstream/avscamera/sys/PreviewHwSim.cpp @@ -40,6 +40,7 @@ CPreviewHardwareSimulation::CPreviewHardwareSimulation( _In_ LONG PinID ) : CHardwareSimulation( Sensor, PinID ) + , m_Illuminated(FALSE) { PAGED_CODE(); } @@ -299,6 +300,11 @@ Return Value: BytesLeft -= sizeof(CAMERA_METADATA_INTRINSICS); } + // TODO: This metadata should only be exposed on a sensor category preview pin. + // It's possible I should derive a new preview sim to handle this; but for now + // we just populate the IR illumination state on every preview pin. Hopefully + // the app is smart enough to realize that this is meaningless if they have not + // selected an IR mediatype. if (BytesLeft >= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION)) { PKSCAMERA_METADATA_FRAMEILLUMINATION pPreviewIllumination = @@ -308,7 +314,31 @@ Return Value: pPreviewIllumination->Header.MetadataId = (ULONG)MetadataId_FrameIllumination; pPreviewIllumination->Header.Size = sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION); - pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON; + // Toggle the Illumination state for an IR frame. + CExtendedVidProcSetting State; + (void) m_Sensor->GetIRTorch(&State); + + if (State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALWAYS_ON) + { + m_Illuminated = TRUE; + pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON; + } + else if ((State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALTERNATING_FRAME_ILLUMINATION)) + { + m_Illuminated = !m_Illuminated; + if (m_Illuminated) + { + pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON; + } + } + else + { + m_Illuminated = FALSE; + } + + DBG_TRACE("Frame Illumination: Flags=0x%016llX, State=%s", + State.Flags, + (pPreviewIllumination->Flags==KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON ?"ON":"OFF")); pMetadata->UsedSize += sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION); BytesLeft -= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION); diff --git a/avstream/avscamera/sys/PreviewHwSim.h b/avstream/avscamera/sys/PreviewHwSim.h index e5a4f536..1d26f692 100644 --- a/avstream/avscamera/sys/PreviewHwSim.h +++ b/avstream/avscamera/sys/PreviewHwSim.h @@ -43,7 +43,6 @@ public: protected: METADATA_PREVIEWAGGREGATION - CPreviewHardwareSimulation:: GetMetadata(); VOID GetCaptureStats( @@ -57,5 +56,8 @@ protected: _Inout_ PKSSTREAM_HEADER pStreamHeader ); +private: + BOOLEAN m_Illuminated; + }; diff --git a/avstream/avscamera/sys/Sensor.cpp b/avstream/avscamera/sys/Sensor.cpp index 9215f6b6..1f062497 100644 --- a/avstream/avscamera/sys/Sensor.cpp +++ b/avstream/avscamera/sys/Sensor.cpp @@ -188,7 +188,7 @@ Initialize() m_InterruptTime[i] = 0; m_LastMappingsCompleted[i] = 0; } - return STATUS_SUCCESS; + return m_Device ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER; } void @@ -356,7 +356,7 @@ CreateSynthesizer( if( NT_SUCCESS( Status ) ) { - DBG_TRACE( "Setting Mounting Orientation to %d�", DbgRotation2Degrees(m_MountingOrientation) ) + DBG_TRACE( "Setting Mounting Orientation to %d°", DbgRotation2Degrees(m_MountingOrientation) ) m_Synthesizer[ Pin->Id ]->SetRotation( m_MountingOrientation ); } @@ -412,15 +412,17 @@ Return Value: NTSTATUS Status = STATUS_SUCCESS; LONG lPindex = Pin->Id; + + // Hold off all image generation while we muck with the capture pin and synthensizer arrays. + KScopedMutex Lock(m_SensorMutex); + // // If we're the first pin to go into acquire (remember we can have // a filter in another graph going simultaneously), grab the resources for Preview // - if( InterlockedCompareExchangePointer( - (PVOID *) &m_CapturePin[lPindex], - CapturePin, - nullptr) == nullptr) + if (m_CapturePin[lPindex] == nullptr) { + m_CapturePin[lPindex] = CapturePin; m_VideoInfoHeader[lPindex] = VideoInfoHeader; // @@ -449,7 +451,6 @@ Return Value: // // If everything has succeeded thus far, set the capture pin. // - m_CapturePin[lPindex] = CapturePin; *pSim = m_HardwareSimulation[lPindex]; } @@ -460,6 +461,8 @@ Return Value: // acquired. // ReleaseHardwareResources(Pin); + *pSim = nullptr; + m_CapturePin[lPindex] = nullptr; } } else @@ -1088,20 +1091,23 @@ Return Value: void CSensor:: -SetSynthesizerAttribute( - CSynthesizer::Attribute Attrib, - LONGLONG Info, - LONG PinId +SetSynthesizerAttributeList( + _In_ size_t Count, + _In_ SynthesizerAttributeEntry AttributeList[] ) { PAGED_CODE(); - - for( ULONG Pin=0; IsValidIndex(Pin); Pin++ ) + // Syncrhonize access to sensor. + KScopedMutex Lock(m_SensorMutex); + for (size_t item = 0; item < Count; item++) { - if( (Pin==(ULONG)PinId || IsStillIndex(Pin)) && - m_Synthesizer[Pin] ) + for (ULONG Pin = 0; IsValidIndex(Pin); Pin++) { - m_Synthesizer[Pin]->Set( Attrib, Info ); + if ((Pin == (ULONG)AttributeList[item].PinId || IsStillIndex(Pin)) && + m_Synthesizer[Pin]) + { + m_Synthesizer[Pin]->Set(AttributeList[item].Attrib, AttributeList[item].Info); + } } } } @@ -1140,11 +1146,15 @@ Return Value: DBG_ENTER( "( Pin=%d )\n", Pin->Id ) ; LONG lPindex = Pin->Id; + // // Blow away the image synth. // m_HardwareSimulation[lPindex]->Reset(); + // Hold off all image generation while we free the capture pin and synthensizer arrays. + KScopedMutex Lock(m_SensorMutex); + SAFE_DELETE( m_Synthesizer[lPindex] ); m_VideoInfoHeader[lPindex] = NULL; @@ -1152,10 +1162,7 @@ Return Value: // Release our "lock" on hardware resources. This will allow another // pin (perhaps in another graph) to acquire them. // - InterlockedExchangePointer( - (PVOID *) &(m_CapturePin[lPindex]), - nullptr - ); + m_CapturePin[lPindex] = nullptr; DBG_LEAVE( "( Pin=%d )\n", Pin->Id ) ; } @@ -1322,6 +1329,7 @@ DEFINE_NULL_PROPERTY_GET(CSensor, KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_ DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, TriggerTime) DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, TorchMode) +DEFINE_NULL_PROPERTY(CSensor, CExtendedVidProcSetting, IRTorch) DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, ExtendedFlash) DEFINE_NULL_PROPERTY_GET(CSensor, CExtendedProperty, PhotoFrameRate) @@ -1352,6 +1360,7 @@ DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, OptimizationHint) DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, AdvancedPhoto) DEFINE_NULL_PROPERTY(CSensor, CExtendedVidProcSetting, FaceDetection) DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, VideoTemporalDenoising) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, RelativePanel) DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode) diff --git a/avstream/avscamera/sys/Sensor.h b/avstream/avscamera/sys/Sensor.h index 327b860a..c68f1b3a 100644 --- a/avstream/avscamera/sys/Sensor.h +++ b/avstream/avscamera/sys/Sensor.h @@ -517,8 +517,19 @@ public: m_MountingOrientation = Orientation; } + struct SynthesizerAttributeEntry + { + CSynthesizer::Attribute Attrib; + LONGLONG Info; + LONG PinId; + + }; + void - SetSynthesizerAttribute( CSynthesizer::Attribute Attrib, LONGLONG Info, LONG PinId ); + SetSynthesizerAttributeList( + _In_ size_t Count, + _In_ SynthesizerAttributeEntry AttributeList[] + ); protected: LONG @@ -554,6 +565,7 @@ public: DECLARE_PROPERTY( CExtendedProperty, TriggerTime ); DECLARE_PROPERTY( CExtendedProperty, TorchMode ); + DECLARE_PROPERTY( CExtendedVidProcSetting, IRTorch ); DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, Focus ); DECLARE_PROPERTY_ASYNC( CExtendedProperty, Iso ); @@ -576,6 +588,7 @@ public: DECLARE_PROPERTY( CExtendedProperty, AdvancedPhoto ); DECLARE_PROPERTY( CExtendedVidProcSetting, FaceDetection ); DECLARE_PROPERTY( CExtendedProperty, VideoTemporalDenoising); + DECLARE_PROPERTY( CExtendedProperty, RelativePanel); DECLARE_PROPERTY_VARSIZE_ASYNC( CRoiProperty, Roi ); diff --git a/avstream/avscamera/sys/SensorSimulation.cpp b/avstream/avscamera/sys/SensorSimulation.cpp index 6ecd9007..e60372c1 100644 --- a/avstream/avscamera/sys/SensorSimulation.cpp +++ b/avstream/avscamera/sys/SensorSimulation.cpp @@ -331,6 +331,15 @@ Return Value: } m_TorchMode.Flags = KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF; m_TorchMode = 50UL; + m_IRTorch.Flags = KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALWAYS_ON; + m_IRTorch.Capability = + KSCAMERA_EXTENDEDPROP_IRTORCHMODE_OFF | + KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALWAYS_ON | + KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALTERNATING_FRAME_ILLUMINATION; + m_IRTorch = 100UL; + m_IRTorch.Min() = 20; + m_IRTorch.Max() = 100; + m_IRTorch.Step() = 5; m_OptimizationHint.Flags = KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO; @@ -471,6 +480,14 @@ Return Value: m_PowerLineFreq.Value = POWERLINEFREQ_DEFAULT; m_PowerLineFreq.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + // Set up our relative panel. + m_RelativePanel.Capability = + KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_ON | + KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_OFF | + KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_DYNAMIC; + m_RelativePanel.Flags = KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_OFF; + m_RelativePanel = (ULONG)AcpiPldPanelUnknown; + m_IsoResult = STATUS_SUCCESS; m_EvCompResult = STATUS_SUCCESS; m_WhiteBalanceResult= STATUS_SUCCESS; @@ -1791,6 +1808,40 @@ SetOpticalImageStabilization( return STATUS_SUCCESS; } +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_RELATIVEPANEL. +NTSTATUS +CSensorSimulation:: +GetRelativePanel( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pProperty = m_RelativePanel; + pProperty->Flags = m_RelativePanel.Flags; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_ON | + KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_OFF | + KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_DYNAMIC; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_RELATIVEPANEL. +NTSTATUS +CSensorSimulation:: +SetRelativePanel( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + m_RelativePanel = *pProperty; + m_RelativePanel.Flags = pProperty->Flags; + return STATUS_SUCCESS; +} + // Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOTEMPORALDENOISING. NTSTATUS CSensorSimulation:: @@ -3024,6 +3075,37 @@ SetTorchMode( return STATUS_SUCCESS; } +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_IRTORCHMODE +NTSTATUS +CSensorSimulation:: +GetIRTorch( + _Inout_ CExtendedVidProcSetting *pTorch +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pTorch = m_IRTorch; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_IRTORCHMODE +NTSTATUS +CSensorSimulation:: +SetIRTorch( + _In_ CExtendedVidProcSetting *pTorch +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + // Only overwrite the IR Torch Flags and value. + m_IRTorch.Flags = pTorch->Flags; + m_IRTorch = pTorch->GetULONG(); + + return STATUS_SUCCESS; +} + // Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE NTSTATUS CSensorSimulation:: diff --git a/avstream/avscamera/sys/SensorSimulation.h b/avstream/avscamera/sys/SensorSimulation.h index a2dd1fe7..6124762c 100644 --- a/avstream/avscamera/sys/SensorSimulation.h +++ b/avstream/avscamera/sys/SensorSimulation.h @@ -50,6 +50,7 @@ protected: ULONG m_FaceDetectionCurrentMax; ULONGLONG m_SceneMode; CExtendedProperty m_TorchMode; + CExtendedVidProcSetting m_IRTorch; CExtendedProperty m_OptimizationHint; CExtendedMetadata *m_MetadataInfo; PKSCAMERA_PERFRAMESETTING_HEADER m_pPerFrameSettings; @@ -61,7 +62,8 @@ protected: ULONGLONG m_Histogram; ULONGLONG m_OpticalImageStabilization; ULONGLONG m_VideoTemporalDenoising; - CExtendedProperty m_AdvancedPhoto; + CExtendedProperty m_RelativePanel; + CExtendedProperty m_AdvancedPhoto; CWhiteBalanceRoiIspControl m_RoiWhiteBalance; CExposureRoiIspControl m_RoiExposureMode; @@ -207,6 +209,7 @@ public: DECLARE_PROPERTY_GET( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence ); DECLARE_PROPERTY( CExtendedProperty, TriggerTime ); DECLARE_PROPERTY( CExtendedProperty, TorchMode ); + DECLARE_PROPERTY( CExtendedVidProcSetting, IRTorch ); DECLARE_PROPERTY( CExtendedProperty, ExtendedFlash ); @@ -236,6 +239,7 @@ public: DECLARE_PROPERTY_GET( CExtendedFieldOfView, FieldOfView ); DECLARE_PROPERTY_GET( CExtendedCameraAngleOffset, CameraAngleOffset ); DECLARE_PROPERTY( CExtendedProperty, VideoTemporalDenoising); + DECLARE_PROPERTY( CExtendedProperty, RelativePanel); DECLARE_PROPERTY_VARSIZE_ASYNC_NOCANCEL( CRoiProperty, Roi ); @@ -288,4 +292,4 @@ const LONG WHITEBALANCE_DEF = 5000; const LONG EXPOSURE_BILOG_MIN = -10; const LONG EXPOSURE_BILOG_MAX = 9; const LONG EXPOSURE_BILOG_STEP = 1; -const LONG EXPOSURE_BILOG_DEF = -5;
\ No newline at end of file +const LONG EXPOSURE_BILOG_DEF = -5; diff --git a/avstream/avscamera/sys/VideoHwSim.cpp b/avstream/avscamera/sys/VideoHwSim.cpp index 33b6de90..839af530 100644 --- a/avstream/avscamera/sys/VideoHwSim.cpp +++ b/avstream/avscamera/sys/VideoHwSim.cpp @@ -27,6 +27,7 @@ CVideoHardwareSimulation::CVideoHardwareSimulation( _In_ LONG PinID ) : CHardwareSimulation( Sensor, PinID ) + , m_Illuminated(FALSE) {} @@ -50,6 +51,54 @@ EmitMetadata( if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA)) { + PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1); + PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO)(pFrameInfo + 1); + ULONG BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize; + + // TODO: This metadata should only be exposed on a sensor category preview pin. + // It's possible I should derive a new preview sim to handle this; but for now + // we just populate the IR illumination state on every preview pin. Hopefully + // the app is smart enough to realize that this is meaningless if they have not + // selected an IR mediatype. + if (BytesLeft >= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION)) + { + PKSCAMERA_METADATA_FRAMEILLUMINATION pPreviewIllumination = + (PKSCAMERA_METADATA_FRAMEILLUMINATION)(((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize); + RtlZeroMemory(pPreviewIllumination, sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION)); + + pPreviewIllumination->Header.MetadataId = (ULONG)MetadataId_FrameIllumination; + pPreviewIllumination->Header.Size = sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION); + + // Toggle the Illumination state for an IR frame. + CExtendedVidProcSetting State; + (void)m_Sensor->GetIRTorch(&State); + + if (State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALWAYS_ON) + { + m_Illuminated = TRUE; + pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON; + } + else if ((State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALTERNATING_FRAME_ILLUMINATION)) + { + m_Illuminated = !m_Illuminated; + if (m_Illuminated) + { + pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON; + } + } + else + { + m_Illuminated = FALSE; + } + + DBG_TRACE("Frame Illumination: Flags=0x%016llX, State=%s", + State.Flags, + (pPreviewIllumination->Flags == KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON ? "ON" : "OFF")); + + pMetadata->UsedSize += sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION); + BytesLeft -= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION); + } + CExtendedVidProcSetting FaceDetect; m_Sensor->GetFaceDetection(&FaceDetect); diff --git a/avstream/avscamera/sys/VideoHwSim.h b/avstream/avscamera/sys/VideoHwSim.h index 40b0b24b..a576d9a1 100644 --- a/avstream/avscamera/sys/VideoHwSim.h +++ b/avstream/avscamera/sys/VideoHwSim.h @@ -38,5 +38,9 @@ public: EmitMetadata( _Inout_ PKSSTREAM_HEADER pStreamHeader ); + +private: + BOOLEAN m_Illuminated; + }; diff --git a/avstream/avscamera/sys/avscamera.inf b/avstream/avscamera/sys/avscamera.inx Binary files differindex 2a17a764..f6f1e846 100644 --- a/avstream/avscamera/sys/avscamera.inf +++ b/avstream/avscamera/sys/avscamera.inx diff --git a/avstream/avscamera/sys/filter.cpp b/avstream/avscamera/sys/filter.cpp index 2680f503..dfb6ce1f 100644 --- a/avstream/avscamera/sys/filter.cpp +++ b/avstream/avscamera/sys/filter.cpp @@ -64,6 +64,7 @@ Return Value: m_pCaptureDevice(NULL), m_pPerFrameSettings(nullptr), m_pinArray(nullptr), + m_pMinimumRequestedFrames(nullptr), m_PhotoModeNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE ), m_PhotoMaxFrameRateNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE) , m_FocusNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE ), @@ -141,10 +142,12 @@ Return Value: IFFAILED_EXIT( m_Sensor->AddFilter(m_pKSFilter) ); IFNULL_EXIT(m_pinArray = new (PagedPool, 'sniP') CCapturePin *[m_Sensor->GetPinCount()]); + IFNULL_EXIT(m_pMinimumRequestedFrames = new (PagedPool, 'sniP') ULONG[m_Sensor->GetPinCount()]); for( ULONG i=0; i<m_Sensor->GetPinCount(); i++ ) { m_pinArray[i] = nullptr; + m_pMinimumRequestedFrames[i] = IMAGE_CAPTURE_PIN_MINIMUM_FRAMES; } done: @@ -209,6 +212,7 @@ CCaptureFilter::~CCaptureFilter() // may need to have some things reset. m_Sensor->Reset(); + delete [] m_pMinimumRequestedFrames; delete [] m_pinArray; DBG_LEAVE("(%S)", Name); @@ -352,6 +356,11 @@ Return Value: // Take out a weak reference to the pin object. m_pinArray[Id] = pPin; + if (m_pinArray[Id] != nullptr) + { + m_pinArray[Id]->SetDesiredFrames(m_pMinimumRequestedFrames[Id]); + (void)m_pinArray[Id]->UpdateAllocatorFraming(); + } } CCapturePin * @@ -391,7 +400,8 @@ Return Value: NTSTATUS CCaptureFilter:: UpdateAllocatorFraming( - _In_ ULONG PinId + _In_ ULONG PinId, + _In_ ULONG DesiredFrames ) /*++ @@ -412,15 +422,20 @@ Return Value: { PAGED_CODE(); - NTSTATUS Status = STATUS_INVALID_PARAMETER; + NTSTATUS Status = STATUS_SUCCESS; // Acquire the lock and update the Pin's allocator framing. LockFilter Lock(m_pKSFilter); if( m_pinArray[PinId] ) { + m_pinArray[PinId]->SetDesiredFrames(DesiredFrames); Status = m_pinArray[PinId]->UpdateAllocatorFraming(); } + else + { + m_pMinimumRequestedFrames[PinId] = DesiredFrames; + } DBG_LEAVE("()=0x%08X",Status); return Status; @@ -636,6 +651,7 @@ SetPhotoMode( PAGED_CODE(); NTSTATUS Status = STATUS_INVALID_PARAMETER; + ULONG TotalFrames = IMAGE_CAPTURE_PIN_MINIMUM_FRAMES; DBG_ENTER( "()" ); @@ -645,10 +661,6 @@ SetPhotoMode( { CExtendedPhotoMode Caps(*pMode); Status = m_Sensor->GetPhotoMode( &Caps ); - CCapturePin *pPin = getPin(pMode->PinId); - - // Should always be valid. - NT_ASSERT(pPin); if( NT_SUCCESS(Status) ) { @@ -686,7 +698,7 @@ SetPhotoMode( // Assume the normal photo sequence case. // For normal photo sequence, we provide history frames +1 or at least a minimum of 3 frames. - ULONG TotalFrames = pMode->RequestedHistoryFrames() + 1; + TotalFrames = pMode->RequestedHistoryFrames() + 1; // Modify TotalFrames for the VPS case. // For VPS, we need LoopCount * FrameCount; but limit by the minimum(3) and maximum (20) frames. @@ -708,14 +720,12 @@ SetPhotoMode( DBG_TRACE( "Advertising Framing requirement: %d", TotalFrames ); - pPin->SetDesiredFrames(TotalFrames); Status = STATUS_SUCCESS; break; } case KSCAMERA_EXTENDEDPROP_PHOTOMODE_NORMAL: { // Set it back to the minimum number of frames. - pPin->SetDesiredFrames(IMAGE_CAPTURE_PIN_MINIMUM_FRAMES); Status = STATUS_SUCCESS; break; } @@ -724,7 +734,7 @@ SetPhotoMode( // Update allocator framing. if( NT_SUCCESS( Status ) ) { - Status = UpdateAllocatorFraming( pMode->PinId ); + Status = UpdateAllocatorFraming( pMode->PinId, TotalFrames ); } // Update the sensor object... @@ -1118,13 +1128,13 @@ GetTorchMode( NTSTATUS Status = STATUS_INVALID_PARAMETER; - if( pTorchMode->isValid() && - pTorchMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + if (pTorchMode->isValid() && + pTorchMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE) { - Status = m_Sensor->GetTorchMode( pTorchMode ); + Status = m_Sensor->GetTorchMode(pTorchMode); } DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", - pTorchMode->PinId, pTorchMode->Flags, pTorchMode->Capability, Status ); + pTorchMode->PinId, pTorchMode->Flags, pTorchMode->Capability, Status); return Status; } @@ -1139,34 +1149,101 @@ SetTorchMode( NTSTATUS Status = STATUS_INVALID_PARAMETER; - if( !(pTorchMode->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && - pTorchMode->isValid() && - pTorchMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && - pTorchMode->m_Value.Value.ul <= 100 ) + if (!(pTorchMode->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pTorchMode->isValid() && + pTorchMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pTorchMode->m_Value.Value.ul <= 100) { CExtendedProperty Caps(*pTorchMode); Status = m_Sensor->GetTorchMode(&Caps); - if( NT_SUCCESS(Status) ) + if (NT_SUCCESS(Status)) { // Assume an invalid parameter. Status = STATUS_INVALID_PARAMETER; - if( pTorchMode->Flags == (pTorchMode->Flags & Caps.Capability) ) + if (pTorchMode->Flags == (pTorchMode->Flags & Caps.Capability)) { - switch( pTorchMode->Flags ) + switch (pTorchMode->Flags) { case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF: case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON: case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON_ADJUSTABLEPOWER: - Status = m_Sensor->SetTorchMode( pTorchMode ); + Status = m_Sensor->SetTorchMode(pTorchMode); break; } } } } DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", - pTorchMode->PinId, pTorchMode->Flags, pTorchMode->Capability, Status ); + pTorchMode->PinId, pTorchMode->Flags, pTorchMode->Capability, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_IRTORCHMODE +NTSTATUS +CCaptureFilter:: +GetIRTorch( + _Inout_ CExtendedVidProcSetting *pTorch +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if (pTorch->isValid() && + pTorch->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE) + { + Status = m_Sensor->GetIRTorch(pTorch); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pTorch->PinId, pTorch->Flags, pTorch->Capability, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_IRTORCHMODE +NTSTATUS +CCaptureFilter:: +SetIRTorch( + _In_ CExtendedVidProcSetting *pTorch +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if (!(pTorch->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pTorch->isValid() && + pTorch->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE) + { + CExtendedVidProcSetting Caps(*pTorch); + Status = m_Sensor->GetIRTorch(&Caps); + + if (NT_SUCCESS(Status)) + { + Status = Caps.BoundsCheck(pTorch->GetULONG()); + + if (NT_SUCCESS(Status)) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if (pTorch->Flags == (pTorch->Flags & Caps.Capability)) + { + switch (pTorch->Flags) + { + case KSCAMERA_EXTENDEDPROP_IRTORCHMODE_OFF: + case KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALWAYS_ON: + case KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALTERNATING_FRAME_ILLUMINATION: + Status = m_Sensor->SetIRTorch(pTorch); + break; + } + } + } + } + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pTorch->PinId, pTorch->Flags, pTorch->Capability, Status); return Status; } @@ -3494,6 +3571,66 @@ SetOpticalImageStabilization( return Status; } +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_RELATIVEPANEL. +NTSTATUS +CCaptureFilter:: +GetRelativePanel( + _Inout_ CExtendedProperty *pRelativePanel +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if (pRelativePanel->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pRelativePanel->isValid()) + { + //Get the current state + Status = m_Sensor->GetRelativePanel(pRelativePanel); + } + + DBG_TRACE("pRelativePanel = %p, PinId = %u, Flags = %llu, Version = %u", + pRelativePanel, pRelativePanel->PinId, pRelativePanel->Flags, pRelativePanel->Version); + + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_RELATIVEPANEL. +NTSTATUS +CCaptureFilter:: +SetRelativePanel( + _In_ CExtendedProperty *pRelativePanel +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Call must be Filter and flags must be supported and version must be 1 + if (pRelativePanel->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pRelativePanel->isValid() && + (KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_ON == pRelativePanel->Flags || + KSCAMERA_EXTENDEDPROP_RELATIVEPANELOPTIMIZATION_OFF == pRelativePanel->Flags)) + { + CExtendedProperty Caps(*pRelativePanel); + Status = m_Sensor->GetRelativePanel(&Caps); + + if (NT_SUCCESS(Status)) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if (pRelativePanel->Flags == (pRelativePanel->Flags & Caps.Capability)) + { + Status = m_Sensor->SetRelativePanel(pRelativePanel); + } + } + } + DBG_TRACE("pOIS = %p, PinId = %u, Flags = %llu, Version = %u", + pRelativePanel, pRelativePanel->PinId, pRelativePanel->Flags, pRelativePanel->Version); + + return Status; +} + // Get KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. NTSTATUS CCaptureFilter:: diff --git a/avstream/avscamera/sys/filter.h b/avstream/avscamera/sys/filter.h index 292a6bc7..07f1c0d4 100644 --- a/avstream/avscamera/sys/filter.h +++ b/avstream/avscamera/sys/filter.h @@ -58,6 +58,7 @@ private: // The AVStream filter object associated with this CCaptureFilter. // CCapturePin **m_pinArray; + ULONG* m_pMinimumRequestedFrames; protected: // @@ -331,7 +332,9 @@ DECLARE_PROPERTY_SET_HANDLER( type, name ) DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Thumbnail ) DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TriggerTime ) DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TorchMode ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, IRTorch ) DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoTemporalDenoising) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, RelativePanel) DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ) DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ) @@ -385,7 +388,7 @@ DECLARE_PROPERTY_SET_HANDLER( type, name ) // Make sure the PERFRAMESETTINGs passed to us are valid. _Success_(return == 0) NTSTATUS - CCaptureFilter::ParsePFSBuffer( + ParsePFSBuffer( _In_reads_bytes_(BufferLimit) PKSCAMERA_PERFRAMESETTING_HEADER pPFS, _In_ ULONG BufferLimit, @@ -434,7 +437,8 @@ DECLARE_PROPERTY_SET_HANDLER( type, name ) // Update the pin's allocator to a specific frame count. NTSTATUS UpdateAllocatorFraming( - _In_ ULONG PinId + _In_ ULONG PinId, + _In_ ULONG DesiredFrames ); private: diff --git a/avstream/avscamera/sys/hwsim.cpp b/avstream/avscamera/sys/hwsim.cpp index 5322cff3..fdf7b367 100644 --- a/avstream/avscamera/sys/hwsim.cpp +++ b/avstream/avscamera/sys/hwsim.cpp @@ -1207,7 +1207,7 @@ Return Value: { PAGED_CODE(); - KScopedMutex Lock( m_ListLock ); + KScopedMutex Lock(m_ListLock); m_InterruptTime++; @@ -1218,10 +1218,10 @@ Return Value: // if (m_PinState == PinRunning) { - LONGLONG Qpc = (LONGLONG) ConvertQPCtoTimeStamp(nullptr); + LONGLONG Qpc = (LONGLONG)ConvertQPCtoTimeStamp(nullptr); LARGE_INTEGER Now; - KeQuerySystemTimePrecise( &Now ); + KeQuerySystemTimePrecise(&Now); // // Generate a "time stamp" just to overlay it onto the capture image. // It makes it more exciting than bars that do nothing. @@ -1230,12 +1230,16 @@ Return Value: // the most recent streaming pin's index. if (!m_Sensor->IsStillIndex(m_PinID)) { + CSensor::SynthesizerAttributeEntry AttributeList[] = + { + { CSynthesizer::FrameNumber, m_InterruptTime, m_PinID }, + { CSynthesizer::RelativePts, (m_InterruptTime + 1) * m_TimePerFrame, m_PinID }, + { CSynthesizer::QpcTime, Qpc, m_PinID } + }; DBG_TRACE("QPC=0x%016llX", Qpc); // Broadcast the preview pin's info to all pin simulations. - m_Sensor->SetSynthesizerAttribute(CSynthesizer::FrameNumber, m_InterruptTime, m_PinID); - m_Sensor->SetSynthesizerAttribute(CSynthesizer::RelativePts, (m_InterruptTime + 1) * m_TimePerFrame, m_PinID); - m_Sensor->SetSynthesizerAttribute(CSynthesizer::QpcTime, Qpc, m_PinID); + m_Sensor->SetSynthesizerAttributeList(SIZEOF_ARRAY(AttributeList), AttributeList); } m_Synthesizer->DoSynthesize(); @@ -1243,13 +1247,13 @@ Return Value: CHAR Text[64]; CExtendedProperty Control; - m_Sensor->GetVideoStabilization( &Control ); + m_Sensor->GetVideoStabilization(&Control); RtlStringCbPrintfA(Text, sizeof(Text), "DVS: %s", DVS_Text(Control.Flags)); - m_Synthesizer->OverlayText( 0, m_Height-38, 1, Text, TRANSPARENT, TEXT_COLOR ); + m_Synthesizer->OverlayText(0, m_Height - 38, 1, Text, TRANSPARENT, TEXT_COLOR); - m_Sensor->GetOpticalImageStabilization( &Control ); + m_Sensor->GetOpticalImageStabilization(&Control); RtlStringCbPrintfA(Text, sizeof(Text), "OIS: %s", OIS_Text(Control.Flags)); - m_Synthesizer->OverlayText( 0, m_Height-48, 1, Text, TRANSPARENT, TEXT_COLOR ); + m_Synthesizer->OverlayText(0, m_Height - 48, 1, Text, TRANSPARENT, TEXT_COLOR); // // Add the Missed frame count @@ -1257,7 +1261,7 @@ Return Value: size_t len = 0; RtlStringCchLengthA(Text, sizeof(Text), &len); m_Synthesizer->OverlayText( - (m_Width - (((ULONG)len*8))), // right-adjust text. + (m_Width - (((ULONG)len * 8))), // right-adjust text. (m_Height - 48), 1, Text, @@ -1268,12 +1272,12 @@ Return Value: // // Add the estimated FPS LONGLONG Target = NANOSECONDS / m_TimePerFrame; - LONGLONG FPS = ((LONGLONG)m_InterruptTime * NANOSECONDS) / ( (Now.QuadPart - m_StartTime.QuadPart) + (NANOSECONDS/2) ); + LONGLONG FPS = ((LONGLONG)m_InterruptTime * NANOSECONDS) / ((Now.QuadPart - m_StartTime.QuadPart) + (NANOSECONDS / 2)); RtlStringCbPrintfA(Text, sizeof(Text), "%lld/%lld FPS", FPS, Target); len = 0; RtlStringCchLengthA(Text, sizeof(Text), &len); - m_Synthesizer->OverlayText ( - (m_Width - (((ULONG)len*8))), // right-adjust text. + m_Synthesizer->OverlayText( + (m_Width - (((ULONG)len * 8))), // right-adjust text. (m_Height - 38), 1, Text, @@ -1284,7 +1288,7 @@ Return Value: // // Fill scatter gather buffers // - if (!NT_SUCCESS (FillScatterGatherBuffers ())) + if (!NT_SUCCESS(FillScatterGatherBuffers())) { m_NumFramesSkipped++; } @@ -1294,28 +1298,28 @@ Return Value: // Issue an interrupt to our hardware sink. This is a "fake" interrupt. // It will occur at DISPATCH_LEVEL. // - m_Sensor -> Interrupt (m_PinID); + m_Sensor->Interrupt(m_PinID); // // Schedule the timer for the next interrupt time, if the pin is still running. // - if( m_PinState == PinRunning ) + if (m_PinState == PinRunning) { LARGE_INTEGER NextTime; NextTime.QuadPart = m_StartTime.QuadPart + - (m_TimePerFrame * (m_InterruptTime + 1)); + (m_TimePerFrame * (m_InterruptTime + 1)); #ifdef ENABLE_TRACING // To keep us from a tight spin when trying to debug this code... LARGE_INTEGER Now; KeQuerySystemTime(&Now); - if( Now.QuadPart >= NextTime.QuadPart ) + if (Now.QuadPart >= NextTime.QuadPart) { - NextTime.QuadPart = 0LL - m_TimePerFrame ; + NextTime.QuadPart = 0LL - m_TimePerFrame; } #endif - m_IsrTimer.Set( NextTime ); + m_IsrTimer.Set(NextTime); } } diff --git a/avstream/avscamera/sys/imagecapture.cpp b/avstream/avscamera/sys/imagecapture.cpp index 06d334d3..7ba53fa4 100644 --- a/avstream/avscamera/sys/imagecapture.cpp +++ b/avstream/avscamera/sys/imagecapture.cpp @@ -121,9 +121,11 @@ Close( { PAGED_CODE(); + // Reset the image sim's counts and queues. Reset(); - return STATUS_SUCCESS; + // Call the base Close operation. + return CCapturePin::Close(Irp); } // ImageCapturePinDispatch: diff --git a/avstream/avshws/README.md b/avstream/avshws/README.md index 49bb9e04..109bc968 100644 --- a/avstream/avshws/README.md +++ b/avstream/avshws/README.md @@ -1,86 +1,96 @@ ---- -page_type: sample -description: "A simulated hardware sample driver providing a pin-centric capture driver to simulate AV capture hardware." -languages: -- cpp -products: -- windows -- windows-wdk ---- +<!--- + name: AVStream simulated hardware sample driver (Avshws) + platform: WDM + language: cpp + category: Camera + description: A simulated hardware sample driver providing a pin-centric capture driver to simulate AV capture hardware. + samplefwlink: http://go.microsoft.com/fwlink/p/?LinkId=620185 +---> -# AVStream simulated hardware sample driver (Avshws) +AVStream simulated hardware sample driver (Avshws) +================================================== -The AVStream simulated hardware sample driver (Avshws) provides a pin-centric [AVStream](https://docs.microsoft.com/windows-hardware/drivers/stream/avstream-overview) capture driver for a simulated piece of hardware. This streaming media driver performs video captures at 320 x 240 pixels in either RGB24 or YUV422 format using direct memory access (DMA) into capture buffers. The purpose of the sample is to demonstrate how to write a pin-centric AVStream minidriver. The sample also shows how to implement DMA by using the related functionality provided by the AVStream class driver. +The AVStream simulated hardware sample driver (Avshws) provides a pin-centric [AVStream](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554240) capture driver for a simulated piece of hardware. This streaming media driver performs video captures at 320 x 240 pixels in either RGB24 or YUV422 format using direct memory access (DMA) into capture buffers. The purpose of the sample is to demonstrate how to write a pin-centric AVStream minidriver. The sample also shows how to implement DMA by using the related functionality provided by the AVStream class driver. This sample features enhanced parameter validation and overflow detection. -## Provision a target computer +Provision a target computer +--------------------------- After you've installed the sample on your host computer, run Visual Studio, and from the **File** menu, select **Open**, then **Project/Solution...**, navigate to the directory where you've copied the Avshws sample, then to the C++ folder, and select **avshws.vcxproj** (the VC++ Project). -In the **Solution Explorer** pane in Visual Studio, at the top is **Solution 'avshws'**. Right-click this and select **Configuration Manager**. Follow the instructions in [Building a Driver with Visual Studio and the WDK](https://docs.microsoft.com/windows-hardware/drivers/develop/building-a-driver) to set the platform, operating system, and debug configuration you want to use, and to build the sample. This sample project will automatically sign the driver package. +In the **Solution Explorer** pane in Visual Studio, at the top is **Solution 'avshws'**. Right-click this and select **Configuration Manager**. Follow the instructions in [Building a Driver with the WDK](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644) to set the platform, operating system, and debug configuration you want to use, and to build the sample. This sample project will automatically sign the driver package. -Provision your target computer using instructions in, for example, [Provision a computer for driver deployment and testing](https://docs.microsoft.com/windows-hardware/drivers/gettingstarted/provision-a-target-computer-wdk-8-1). Ensure that in the **Network and Sharing Center** control panel your target computer has **Network Discovery** and **File and Printer Sharing** enabled. +Provision your target computer using instructions in, for example, [Preparing a Computer for Provisioning](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265573). Ensure that in the **Network and Sharing Center** control panel your target computer has **Network Discovery** and **File and Printer Sharing** enabled. -## Deploy the driver to the target computer +Deploy the driver to the target computer +---------------------------------------- -Now you can deploy the Avshws driver that you've just built to the target computer, using guidance in [Deploying a Driver to a Test Computer](https://docs.microsoft.com/windows-hardware/drivers/develop/deploying-a-driver-to-a-test-computer). Specifically, find the package file under the **Package** folder in the Avshws solution. Right-click **package** and select **Properties**. Under Configuration Properties, click **Driver install** and then **Deployment**. Here you must click the check box for **Enable deployment**, and then click the button to the right of **\<Configure Computer...\>**. In the next dialog you enter the **Target Computer Name** and can let the host computer automatically provision the target computer and set up debugger options. +Now you can deploy the Avshws driver that you've just built to the target computer, using guidance in [Deploying a Driver to a Test Computer](http://msdn.microsoft.com/en-us/library/windows/hardware/hh454834). Specifically, find the package file under the **Package** folder in the Avshws solution. Right-click **package** and select **Properties**. Under Configuration Properties, click **Driver install** and then **Deployment**. Here you must click the check box for **Enable deployment**, and then click the button to the right of **\<Configure Computer...\>**. In the next dialog you enter the **Target Computer Name** and can let the host computer automatically provision the target computer and set up debugger options. Finally, in Visual Studio, from the **Build** menu select **Deploy Solution** to deploy the sample to the target computer. On the target computer, you can see the deployed package in the **%Systemdrive%\\drivertest\\drivers** folder. -## Install the driver +Install the driver +------------------ On the target computer, open Device Manager, and follow these steps: -1. In the **Action** menu, click **Add Legacy Hardware**, and the **Add Hardware Wizard** appears. Click **Next** and then **Next** again. - -1. In the **Add Hardware** window, select **Show All Devices**. - -1. In the **Manufacturer** list in the left pane, click **Microsoft**. - -1. You should see the **AVStream Simulated Hardware Sample** in the **Model** pane on the right. Click this and then click **Next**. - -1. Click **Next** again to install the driver, and then click **Finish** to exit the wizard. +1. In the **Action** menu, click **Add Legacy Hardware**, and the **Add Hardware Wizard** appears. Click **Next** and then **Next** again. +2. In the **Add Hardware** window, select **Show All Devices**. +3. In the **Manufacturer** list in the left pane, click **Microsoft**. +4. You should see the **AVStream Simulated Hardware Sample** in the **Model** pane on the right. Click this and then click **Next**. +5. Click **Next** again to install the driver, and then click **Finish** to exit the wizard. The sample driver now appears in the Device Manager console tree under **Sound, video and game controllers**. The Avshws INF file will be on the system drive at, for example, **...windows\\System32\\DriverStore\\FileRepository\\**. -## Sample code hierarchy +Sample code hierarchy +--------------------- -[**DriverEntry**](https://docs.microsoft.com/previous-versions//ff558717(v=vs.85)) in Device.cpp is the initial point of entry into the driver. This routine passes control to AVStream by calling the [**KsInitializeDriver**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/nf-ks-ksinitializedriver) function. In this call, the minidriver passes the device descriptor, an AVStream structure that recursively defines the AVStream object hierarchy for a driver. This is common behavior for an AVStream minidriver. +[**DriverEntry**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff558717) in Device.cpp is the initial point of entry into the driver. This routine passes control to AVStream by calling the [**KsInitializeDriver**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562683) function. In this call, the minidriver passes the device descriptor, an AVStream structure that recursively defines the AVStream object hierarchy for a driver. This is common behavior for an AVStream minidriver. -At device start time, a simulated piece of capture hardware is created (the **CHardwareSimulation** class), and a DMA adapter is acquired from the operating system and is registered with AVStream by calling the [**KsDeviceRegisterAdapterObject**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/nf-ks-ksdeviceregisteradapterobject) function. This call is required for a sample that performs DMA access directly into the capture buffers, instead of using DMA access to write to a common buffer. The driver creates the [KS Filter](https://docs.microsoft.com/windows-hardware/drivers/stream/ks-filters) for this device dynamically by calling the [**KsCreateFilterFactory**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/nf-ks-kscreatefilterfactory) function. +At device start time, a simulated piece of capture hardware is created (the **CHardwareSimulation** class), and a DMA adapter is acquired from the operating system and is registered with AVStream by calling the [**KsDeviceRegisterAdapterObject**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff561687) function. This call is required for a sample that performs DMA access directly into the capture buffers, instead of using DMA access to write to a common buffer. The driver creates the [KS Filter](http://msdn.microsoft.com/en-us/library/windows/hardware/ff567644) for this device dynamically by calling the [**KsCreateFilterFactory**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff561650) function. -Filter.cpp is where the sample lays out the [**KSPIN\_DESCRIPTOR\_EX**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/ns-ks-_kspin_descriptor_ex) structure for the single video pin. In addition, a [**KSFILTER\_DISPATCH**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/ns-ks-_ksfilter_dispatch) structure and a [**KSFILTER\_DESCRIPTOR**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/ns-ks-_ksfilter_descriptor) structure are provided in this source file. The filter dispatch provides only a create dispatch, a routine that is included in Filter.cpp. The process dispatch is provided on the pin because this is a pin-centric sample. +Filter.cpp is where the sample lays out the [**KSPIN\_DESCRIPTOR\_EX**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563534) structure for the single video pin. In addition, a [**KSFILTER\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562554) structure and a [**KSFILTER\_DESCRIPTOR**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562553) structure are provided in this source file. The filter dispatch provides only a create dispatch, a routine that is included in Filter.cpp. The process dispatch is provided on the pin because this is a pin-centric sample. -Capture.cpp contains source for the video capture pin on the capture filter. This is where the [**KSPIN\_DISPATCH**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/ns-ks-_kspin_dispatch) structure for the unique pin is provided. This dispatch structure specifies a *Process* callback routine, also defined in this source file. This routine is where stream pointer manipulation and cloning occurs. +Capture.cpp contains source for the video capture pin on the capture filter. This is where the [**KSPIN\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563535) structure for the unique pin is provided. This dispatch structure specifies a *Process* callback routine, also defined in this source file. This routine is where stream pointer manipulation and cloning occurs. The process callback is one of two routines of interest in Capture.cpp that demonstrate how to perform DMA transfers with AVStream functionality. The other is the **CCapturePin::CompleteMappings** method. These two methods show how to use the queue, obtain clone pointers, use scatter/gather lists, and perform other DMA-related tasks. For more information, see the comments in all .cpp files. -## Run the sample +Run the sample +-------------- Follow these steps to see how the sample driver functions: -1. After installation has completed, access the driver through the Graphedt tool. Graphedt.exe is available in the *tools* directory of the WDK. - -1. Before running GraphEdit, use the regsvr32 utility to register the proppage.dll DLL and to enable GraphEdit to display property pages for some of the built-in Microsoft DirectShow filters. Open an elevated command window with Administrator privileges, and navigate to the WDK or SDK *tools* directory that contains proppage.dll. - -1. On the command line, type regsvr32 proppage.dll. If the registration succeeds, you'll get a message, "DllRegisterServer in proppage.dll succeeded." Click OK. - -1. In the Graphedt tool, click the **Graph** menu and click **Insert Filters**. The sample appears under "WDM Streaming Capture Devices" as "avshws Source." - -1. Click **Insert Filter**. The sample appears in the graph as a single filter labeled, "avshws Source." There is one output pin, which is the video capture pin. This pin emits video in YUY2 format. - -1. Attach this filter to either a DirectShow Video Renderer or to the VMR default video renderer. Then click **Play**. +1. After installation has completed, access the driver through the Graphedt tool. Graphedt.exe is available in the *tools* directory of the WDK. +2. Before running GraphEdit, use the regsvr32 utility to register the proppage.dll DLL and to enable GraphEdit to display property pages for some of the built-in Microsoft DirectShow filters. Open an elevated command window with Administrator privileges, and navigate to the WDK or SDK *tools* directory that contains proppage.dll. +3. On the command line, type regsvr32 proppage.dll. If the registration succeeds, you'll get a message, "DllRegisterServer in proppage.dll succeeded." Click OK. +4. In the Graphedt tool, click the **Graph** menu and click **Insert Filters**. The sample appears under "WDM Streaming Capture Devices" as "avshws Source." +5. Click **Insert Filter**. The sample appears in the graph as a single filter labeled, "avshws Source." There is one output pin, which is the video capture pin. This pin emits video in YUY2 format. +6. Attach this filter to either a DirectShow Video Renderer or to the VMR default video renderer. Then click **Play**. The output that is produced by the sample is a 320 x 240 pixel image of standard EIA-189-A color bars. In the middle of the image near the bottom, a clock appears over the image. This clock displays the elapsed time since the graph was introduced into the run state following the last stop. The clock display format is MINUTES:SECONDS.HUNDREDTHS. In the upper-left corner of the image, a counter counts the number of frames that have been dropped since the graph was introduced into the run state after the last stop. -## File manifest +Code tour +--------- + +### File Manifest -| File | Description | -| --- | --- | -| Avshws.h | Main header file for the sample | -| Avshws.inf | Sample installation file | +File | Description +-----|------------ +Avshws.h | Main header file for the sample. +Avshws.inx | Template installation file from which an architecture specific INF file is generated on build. +Capture.cpp | Capture pin implementation for all capture pins on the sample filter. +Capture.h | Capture pin level header for all capture pins on the sample filter. +Device.cpp | Device level implementation of the simulated hardware. +Device.h | Device level header for the simulated hardware. +Filter.cpp | Capture filter implementation (including frame synthesis) for the fake capture filter. +Filter.h | Filter level header for the filter-centric capture filter. +HWSim.cpp | Hardware simulation implementation, including fake "DMA" transfers, scatter gather mapping handling, ISRs, etc. +HWSim.h | Hardware simulation header. +Image.cpp | Image synthesis and overlay code. These objects provide image synthesis (pixel, color-bar, etc) onto RGB24 and UYVY buffers as well as software string overlay into these buffers. +Image.h | Image synthesis and overlay header. +Purecall.h | _purecall stub necessary for virtual function usage in drivers. diff --git a/avstream/avshws/avshws.inf b/avstream/avshws/avshws.inf deleted file mode 100644 index 9bbbc42a..00000000 --- a/avstream/avshws/avshws.inf +++ /dev/null @@ -1,173 +0,0 @@ -;/*++ -; -;Copyright (c) Microsoft Corporation. All rights reserved. -; -; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY -; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR -; PURPOSE. -; -;Module Name: -; -; AVSHWS.INF -; -;Abstract: -; -; -;AVStream pin centric sample mini driver (AVSHWS.sys) installation file. -;Supports x86, amd64, arm and arm64 platforms -; -;--*/ - -[Version] -signature="$WINDOWS NT$" -Class=Camera -ClassGuid={ca3e7ab9-b4c3-4ae6-8251-579ef933890f} -Provider=%ProviderName% -Catalogfile=avshws.cat -DriverVer=08/31/2006,6.0.5600.0 - -[SourceDisksNames] -1000=%cdname%,,, - -[SourceDisksFiles] -avshws.sys=1000 - -[DestinationDirs] -DefaultDestDir=12 -avshws.CopyFiles=12 - -[avshws.CopyFiles] -avshws.sys - -[Manufacturer] -%ManufacturerName%=Standard,NTx86,NTamd64,ntarm,ntarm64 - -[Standard.NTx86] -%avshws.DeviceDesc%=avshws.NTx86,AVSHWS - -[Standard.NTamd64] -%avshws.DeviceDesc%=avshws.NTamd64,AVSHWS - -[Standard.NTarm] -%avshws.DeviceDesc%=avshws.NTarm,AVSHWS - -[Standard.NTarm64] -%avshws.DeviceDesc%=avshws.NTarm64,AVSHWS - -;--------------------------------------------------------------- -; x 86 D D I n s t a l l -;--------------------------------------------------------------- - -[avshws.NTx86] -Include=ks.inf, KSCAPTUR.inf -Needs=KS.Registration,KSCAPTUR.Registration.NT -CopyFiles=avshws.CopyFiles - -;--------------------------------------------------------------- -; A M D 64 D D I n s t a l l -;--------------------------------------------------------------- - -[avshws.NTamd64] -Include=ks.inf,KSCAPTUR.inf -Needs=KS.Registration,KSCAPTUR.Registration.NT -CopyFiles=avshws.CopyFiles - -;--------------------------------------------------------------- -; A R M D D I n s t a l l -;--------------------------------------------------------------- - -[avshws.NTarm] -Include=ks.inf,KSCAPTUR.inf -Needs=KS.Registration,KSCAPTUR.Registration.NT -CopyFiles=avshws.CopyFiles - -;--------------------------------------------------------------- -; A R M 64 D D I n s t a l l -;--------------------------------------------------------------- - -[avshws.NTarm64] -Include=ks.inf,KSCAPTUR.inf -Needs=KS.Registration,KSCAPTUR.Registration.NT -CopyFiles=avshws.CopyFiles - -;--------------------------------------------------------------- -; I n t e r f a c e s -;--------------------------------------------------------------- - -[avshws.NTx86.Interfaces] -AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 - -[avshws.NTamd64.Interfaces] -AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 - -[avshws.NTarm.Interfaces] -AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 - -[avshws.NTarm64.Interfaces] -AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 -AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 - -[CaptureInterface.NT] -AddReg=avshws.Reader.AddReg - -;--------------------------------------------------------------- -; A d d R e g -;--------------------------------------------------------------- - -[avshws.Reader.AddReg] -HKR,,CLSID,,%Proxy.CLSID% -HKR,,FriendlyName,,%avshws.Reader.FriendlyName% - -;--------------------------------------------------------------- -; S e r v i c e s -;--------------------------------------------------------------- - -[avshws.NTx86.Services] -AddService=avshws, 0x00000002, avshws.ServiceInstall - -[avshws.NTamd64.Services] -AddService=avshws, 0x00000002, avshws.ServiceInstall - -[avshws.NTarm.Services] -AddService=avshws, 0x00000002, avshws.ServiceInstall - -[avshws.NTarm64.Services] -AddService=avshws, 0x00000002, avshws.ServiceInstall - -[avshws.ServiceInstall] -DisplayName=%avshws.DeviceDesc% -ServiceType=%SERVICE_KERNEL_DRIVER% -StartType=%SERVICE_DEMAND_START% -ErrorControl=%SERVICE_ERROR_NORMAL% -ServiceBinary=%12%\avshws.sys - -;--------------------------------------------------------------- -; S t r i n g s -;--------------------------------------------------------------- - -[Strings] -; non-localizable -Proxy.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}" -KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}" -KSCATEGORY_VIDEO="{6994AD05-93EF-11D0-A3CC-00A0C9223196}" -KSCATEGORY_VIDEO_CAMERA="{E5323777-F976-4f5b-9B55-B94699C46E44}" - -SERVICE_KERNEL_DRIVER=1 -SERVICE_DEMAND_START=3 -SERVICE_ERROR_NORMAL=1 -REG_DWORD=0x00010001 - -;localizable -ProviderName="TODO-Set-Provider" -ManufacturerName="TODO-Set-Manufacturer" -avshws.DeviceDesc="AVStream Simulated Hardware Sample" -avshws.Reader.FriendlyName="avshws Source" -cdname="AVSHWS Installation Disk"
\ No newline at end of file diff --git a/avstream/avshws/avshws.inx b/avstream/avshws/avshws.inx Binary files differnew file mode 100644 index 00000000..ca763d0a --- /dev/null +++ b/avstream/avshws/avshws.inx diff --git a/avstream/avshws/avshws.sln b/avstream/avshws/avshws.sln index 877a4ca9..ba4ee493 100644 --- a/avstream/avshws/avshws.sln +++ b/avstream/avshws/avshws.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.136 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.181 MinimumVisualStudioVersion = 12.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avshws", "avshws.vcxproj", "{77FA53EC-3637-411C-B708-C8EED64543A4}" EndProject @@ -45,4 +45,3 @@ Global SolutionGuid = {5EBC439F-EC31-448F-B129-434D42485011} EndGlobalSection EndGlobal -
\ No newline at end of file diff --git a/avstream/avshws/avshws.vcxproj b/avstream/avshws/avshws.vcxproj index 115a05c9..7d9e015a 100644 --- a/avstream/avshws/avshws.vcxproj +++ b/avstream/avshws/avshws.vcxproj @@ -319,7 +319,7 @@ <ResourceCompile Include="avshws.rc" /> </ItemGroup> <ItemGroup> - <Inf Exclude="@(Inf)" Include="*.inf" /> + <Inf Exclude="@(Inf)" Include="*.inf;*.inx" /> <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> </ItemGroup> <ItemGroup> @@ -331,4 +331,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/avstream/avshws/makefile b/avstream/avshws/makefile new file mode 100644 index 00000000..e8428c0d --- /dev/null +++ b/avstream/avshws/makefile @@ -0,0 +1,15 @@ +!IF 0 + +Copyright (C) Microsoft Corporation, 1999 - 1999 + +Module Name: + + makefile. + +!ENDIF + +!if "$(DDK_TARGET_OS)"=="Win2K" +!message This sample is not intended to target the Windows 2000 platform. +!else +!INCLUDE $(NTMAKEENV)\makefile.def +!endif
\ No newline at end of file diff --git a/avstream/avssamp/README.md b/avstream/avssamp/README.md index 42f0d393..44a44139 100644 --- a/avstream/avssamp/README.md +++ b/avstream/avssamp/README.md @@ -1,58 +1,57 @@ ---- -page_type: sample -description: "An AVStream filter-centric simulated capture sample driver with functional audio." -languages: -- cpp -products: -- windows -- windows-wdk ---- +<!--- + name: AVStream filter-centric simulated capture sample driver (Avssamp) + platform: WDM + language: cpp + category: Camera + description: An AVStream filter-centric simulated capture sample driver with functional audio. + samplefwlink: http://go.microsoft.com/fwlink/p/?LinkId=620186 +---> -# AVStream filter-centric simulated capture sample driver (Avssamp) +AVStream filter-centric simulated capture sample driver (Avssamp) +================================================================= -The AVStream filter-centric simulated capture sample driver (Avssamp) provides a filter-centric [AVStream](https://docs.microsoft.com/windows-hardware/drivers/stream/avstream-overview) capture driver with functional audio. This streaming media driver performs video captures at 320 x 240 pixel resolution in RGB24 or YUV422 format while playing a user-provided Pulse Code Modulation (PCM) wave audio file in a loop. The sample demonstrates how to write a filter-centric AVStream minidriver. +The AVStream filter-centric simulated capture sample driver (Avssamp) provides a filter-centric [AVStream](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554240) capture driver with functional audio. This streaming media driver performs video captures at 320 x 240 pixel resolution in RGB24 or YUV422 format while playing a user-provided Pulse Code Modulation (PCM) wave audio file in a loop. The sample demonstrates how to write a filter-centric AVStream minidriver. -## Installation instructions -1. Copy AVssamp.inf to a directory, for example, C:\\Avstream\\. - -1. In this directory, create a new subdirectory named objfre\_x86 if the target operating system is x86-based, or objfre\_amd64 for an x64-based target operating system, for example, C:\\AVstream\\objfre\_x86\\. - -1. Copy the processor-appropriate Avssamp.sys file to the objfre\_\* directory. - -1. Start a command prompt with administrator privilege and run the processor-specific WDK tool Devcon.exe to launch the installation. For example: +Installation instructions +------------------------- +1. Build the solution and locate the produced processor-appropriate avssamp.inf and avssamp.sys files + - For your convenience, you may wish to move these files into a separate folder such as C:\AVStream +2. Start a command prompt with administrator privilege and run the processor-specific WDK tool Devcon.exe to launch the installation. For example: `C:\WinDDK\7600.16384.0\tools\devcon\i386\devcon.exe install C:\AVstream\avssamp.inf SW\{20698827-7099-4c4e-861A-4879D639A35F}` -## Programming tour +Programming Tour +---------------- -[**DriverEntry**](https://docs.microsoft.com/previous-versions//ff558717(v=vs.85)) in Avssamp.cpp is the initial point of entry into the driver. This routine passes control to AVStream by calling [**KsInitializeDriver**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/nf-ks-ksinitializedriver). In this call, the minidriver passes the device descriptor, an AVStream structure that recursively defines the AVStream object hierarchy for a driver. This is common behavior for an AVStream minidriver. +[**DriverEntry**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff558717) in Avssamp.cpp is the initial point of entry into the driver. This routine passes control to AVStream by calling [**KsInitializeDriver**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562683). In this call, the minidriver passes the device descriptor, an AVStream structure that recursively defines the AVStream object hierarchy for a driver. This is common behavior for an AVStream minidriver. -Filter.cpp is where the sample lays out the [**KSPIN\_DESCRIPTOR\_EX**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/ns-ks-_kspin_descriptor_ex) structure for the single capture pin. Audio.cpp contains the **KSPIN\_DESCRIPTOR\_EX** structure for the audio capture pin. This pin is dynamically created only if C:\\avssamp.wav exists and is a valid and readable PCM format wave file. +Filter.cpp is where the sample lays out the [**KSPIN\_DESCRIPTOR\_EX**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563534) structure for the single capture pin. Audio.cpp contains the **KSPIN\_DESCRIPTOR\_EX** structure for the audio capture pin. This pin is dynamically created only if C:\\avssamp.wav exists and is a valid and readable PCM format wave file. -The filter dispatch structure [**KSFILTER\_DISPATCH**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/ns-ks-_ksfilter_dispatch) in Filter.cpp provides dispatches to create and process data. The **DispatchProcess** method is defined inline in Filter.h. It calls the **Process** method in Filter.cpp in the context of the **CCaptureFilter** class. Be aware that the process dispatch is provided in **KSFILTER\_DISPATCH** because this sample is filter-centric. +The filter dispatch structure [**KSFILTER\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562554) in Filter.cpp provides dispatches to create and process data. The **DispatchProcess** method is defined inline in Filter.h. It calls the **Process** method in Filter.cpp in the context of the **CCaptureFilter** class. Be aware that the process dispatch is provided in **KSFILTER\_DISPATCH** because this sample is filter-centric. -Audio.cpp lays out a [**KSPIN\_DISPATCH**](https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ks/ns-ks-_kspin_dispatch) pin dispatch structure, which contains the dispatch table for the audio pin. Be aware that the **Process** member of this structure is **NULL** because the sample is filter-centric. Similarly, Video.cpp contains the **KSPIN\_DISPATCH** structure for the video capture pin, again with the **Process** member set to **NULL**. +Audio.cpp lays out a [**KSPIN\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563535) pin dispatch structure, which contains the dispatch table for the audio pin. Be aware that the **Process** member of this structure is **NULL** because the sample is filter-centric. Similarly, Video.cpp contains the **KSPIN\_DISPATCH** structure for the video capture pin, again with the **Process** member set to **NULL**. For more information, see the comments in all .cpp files. -## File manifest +File Manifest +------------- -| File | Description | -| --- | --- | -| Audio.cpp | Audio capture pin implementation | -| Audio.h | Header file for Audio.cpp | -| Avssamp.cpp | Main file for the AVStream filter-centric sample. | -| Avssamp.h | Main header for the AVStream filter-centric sample. | -| Avssamp.inf | Installation information for the AVStream sample driver (avssamp.sys). | -| Capture.cpp | Capture pin implementation for all capture pins on the sample filter. | -| Capture .h | Capture pin level header for all capture pins on the sample filter. | -| Filter.cpp | Capture filter implementation (including frame synthesis) for the fake capture filter. | -| Filter.h | Filter level header for the filter-centric capture filter. | -| Image.cpp | Image synthesis and overlay code. These objects provide image synthesis (pixel, color-bar, etc) onto RGB24 and UYVY buffers as well as software string overlay into these buffers. | -| Image.h | Image synthesis and overlay header | -| Purecall.h | _purecall stub necessary for virtual function usage in drivers. | -| Video.cpp | Video capture pin implementation | -| Video.h | Video capture pin header | -| Wave.cpp | Wave object implementation | -| Wave.h | Wave object header | +File | Description +-----|---------- +Audio.cpp | Audio capture pin implementation. +Audio.h | Header file for Audio.cpp. +Avssamp.cpp | Main file for the AVStream filter-centric sample. +Avssamp.h | Main header for the AVStream filter-centric sample. +Avssamp.inx | Installation information template for the AVStream sample driver (avssamp.sys), from which an architecture specific INF file is generated on build. +Capture.cpp | Capture pin implementation for all capture pins on the sample filter. +Capture .h | Capture pin level header for all capture pins on the sample filter. +Filter.cpp | Capture filter implementation (including frame synthesis) for the fake capture filter. +Filter.h | Filter level header for the filter-centric capture filter. +Image.cpp | Image synthesis and overlay code. These objects provide image synthesis (pixel, color-bar, etc) onto RGB24 and UYVY buffers as well as software string overlay into these buffers. +Image.h | Image synthesis and overlay header. +Purecall.h | _purecall stub necessary for virtual function usage in drivers. +Video.cpp | Video capture pin implementation. +Video.h | Video capture pin header. +Wave.cpp | Wave object implementation +Wave.h | Wave object header diff --git a/avstream/avssamp/audio.cpp b/avstream/avssamp/audio.cpp index 579fa002..cc35f869 100644 --- a/avstream/avssamp/audio.cpp +++ b/avstream/avssamp/audio.cpp @@ -358,7 +358,7 @@ Routine Description: selected in order to change the format for the pin. In this case, OldFormat will not be NULL. - Validate that the format is acceptable and perform the actions necessary + Validate that the format is acceptible and perform the actions necessary to change format if appropriate. Arguments: diff --git a/avstream/avssamp/avssamp.inf b/avstream/avssamp/avssamp.inf deleted file mode 100644 index 00003e78..00000000 --- a/avstream/avssamp/avssamp.inf +++ /dev/null @@ -1,93 +0,0 @@ -; Copyright (c) Microsoft Corporation. All rights reserved. -; -; avssamp.INF -- This file contains installation information for the filter-based -; AVStream sample driver avssamp.sys -; -; Note: -; -; This INF expects the following hierarchy in the installation folder: -; -; \ -; avssamp.inf -; avssamp.sys -; - -[Version] -Signature="$Windows NT$" -Class=Camera -ClassGuid={ca3e7ab9-b4c3-4ae6-8251-579ef933890f} -Provider=%ProviderName% -CatalogFile=avssamp.cat -DriverVer=09/30/2004,1.0.0.0 - -[SourceDisksNames] -1000 = %cdname%,,, - -[SourceDisksFiles] -avssamp.sys = 1000 - -[ControlFlags] -ExcludeFromSelect=* - -[DestinationDirs] -avssamp.CopyFiles=12 - -[Manufacturer] -%ManufacturerName%=Standard,NTamd64,NTx86 - -;--------------------------------------------------------------- -; The preferred method to install as a Root-enumerated device. -; NOTE: DO NOT INCLUDE THIS FOR A HARDWARE DRIVER! -;--------------------------------------------------------------- - -[DeviceInstall32] -AddDevice = ROOT\SW\{20698827-7099-4c4e-861A-4879D639A35F},,avssamp_RootEnumInstall - -[avssamp_RootEnumInstall] -HardwareIds = SW\{20698827-7099-4c4e-861A-4879D639A35F} -;--------------------------------------------------------------- - -[Standard.NTx86] -%avssamp.DeviceDesc%=avssamp,SW\{20698827-7099-4c4e-861A-4879D639A35F} - -[Standard.NTamd64] -%avssamp.DeviceDesc%=avssamp,SW\{20698827-7099-4c4e-861A-4879D639A35F} - -[avssamp.NT] -include=ks.inf,kscaptur.inf -needs=KS.Registration,KSCAPTUR.Registration.NT -CopyFiles=avssamp.CopyFiles - -[avssamp.CopyFiles] -avssamp.sys - -[avssamp.NT.Services] -AddService=avssamp, 0x00000002, avssamp.ServiceInstall - -[avssamp.ServiceInstall] -DisplayName=%avssamp.DeviceDesc% -ServiceType=%SERVICE_KERNEL_DRIVER% -StartType=%SERVICE_DEMAND_START% -ErrorControl=%SERVICE_ERROR_NORMAL% -ServiceBinary=%12%\avssamp.sys - -[Strings] -; non-localizable -Proxy.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}" -avssamp.DeviceId="{20698827-7099-4c4e-861A-4879D639A35F}" -KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}" -KSSTRING_Filter="{9B365890-165F-11D0-A195-0020AFD156E4}" - -SERVICE_KERNEL_DRIVER=1 -SERVICE_DEMAND_START=3 -SERVICE_ERROR_NORMAL=1 -REG_EXPAND_SZ=0x00020000 -REG_DWORD=0x00010001 - -;localizable -ProviderName="TODO-Set-Provider" -ManufacturerName="TODO-Set-Manufacturer" -avssamp.DeviceDesc="AVStream Filter-Centric Sample Driver" -avssamp.Reader.FriendlyName="avssamp Source" - -cdname="Disk 1" diff --git a/avstream/avssamp/avssamp.inx b/avstream/avssamp/avssamp.inx Binary files differnew file mode 100644 index 00000000..bcb18baa --- /dev/null +++ b/avstream/avssamp/avssamp.inx diff --git a/avstream/avssamp/avssamp.vcxproj b/avstream/avssamp/avssamp.vcxproj index 21c292d2..77567a8f 100644 --- a/avstream/avssamp/avssamp.vcxproj +++ b/avstream/avssamp/avssamp.vcxproj @@ -187,7 +187,7 @@ <ResourceCompile Include="avssamp.rc" /> </ItemGroup> <ItemGroup> - <Inf Exclude="@(Inf)" Include="*.inf" /> + <Inf Exclude="@(Inf)" Include="*.inf;*.inx" /> <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> </ItemGroup> <ItemGroup> diff --git a/avstream/avssamp/makefile b/avstream/avssamp/makefile new file mode 100644 index 00000000..ab62d10e --- /dev/null +++ b/avstream/avssamp/makefile @@ -0,0 +1,15 @@ +!IF 0 + +Copyright (C) Microsoft Corporation, 1999 - 1999 + +Module Name: + + makefile. + +!ENDIF + +!if "$(DDK_TARGET_OS)"=="Win2K" +!message This sample is not intended to target the Windows 2000 platform. +!else +!INCLUDE $(NTMAKEENV)\makefile.def +!endif diff --git a/avstream/sampledevicemft/basepin.cpp b/avstream/sampledevicemft/basepin.cpp index d4220397..9c989000 100644 --- a/avstream/sampledevicemft/basepin.cpp +++ b/avstream/sampledevicemft/basepin.cpp @@ -3,6 +3,7 @@ #include "multipinmft.h" #include "multipinmfthelpers.h" #include "basepin.h" +#include "contosodevice.h" #ifdef MF_WPP #include "basepin.tmh" //--REF_ANALYZER_DONT_REMOVE-- @@ -323,7 +324,7 @@ STDMETHODIMP CInPin::WaitForSetInputPinMediaChange() if ( dwWait != WAIT_OBJECT_0 ) { - hr = E_FAIL; + hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } done: @@ -384,23 +385,79 @@ HRESULT CInPin::SetInputStreamState( return hr; } -void CInPin::ReleaseConnectedPins() +STDMETHODIMP_(VOID) CInPin::ShutdownPin() { m_spSourceTransform = nullptr; m_outpin = nullptr; } +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) +// +// ForwardSecureBuffer() +// +// Parameters: +// sample - The sample to send out for secure processing +// +// Returns: +// HRESULT +// +// Notes: +// This helper function calls back to the AVstream driver for post-processing of a +// secure buffer. This is done here as an example for simplicity. A more realistic +// scenario might have you calling into an ISP driver that is not an AVstream driver. +// To do that you would need to acquire a handle to that device and post an IOCTL. +// That device would in turn need to have a secure companion driver (trustlet) +// installed as part of the package to process the buffer. +// +HRESULT CInPin::ForwardSecureBuffer( + _In_ IMFSample *sample +) +{ + DWORD bytesReturned = 0; + CONTOSODEVICE_PROCESSBUFFER_PAYLOAD payload = { 0 }; + KSPROPERTY property = { 0 }; + + wil::com_ptr_nothrow<IMFMediaBuffer> mediaBuffer; + wil::com_ptr_nothrow<IMFSecureBuffer> secureBuffer; + + // Set up our private KSPROPERTY to some sample avstream driver. + property.Set = PROPSETID_CONTOSODEVICE; + property.Id = KSPROPERTY_CONTOSODEVICE_PROCESSBUFFER; + property.Flags = KSPROPERTY_TYPE_SET; + // Set up the payload for that property. Includes the secure buffer ID and buffer length. + RETURN_IF_FAILED(sample->GetBufferByIndex(0, &mediaBuffer)); + // Forward the buffer if it is secure. + if (mediaBuffer.try_query_to(&secureBuffer)) + { + RETURN_IF_FAILED(secureBuffer->GetIdentifier(&payload.identifier)); + RETURN_IF_FAILED(mediaBuffer->GetMaxLength(&payload.size)); + + // Log the buffer's secure ID and size for debugging. + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! identifier=%!GUID!, length=%d", &payload.identifier, payload.size); + + // Send the KSPROPERTY synchronously to the driver. + RETURN_IF_FAILED(KsProperty(&property, sizeof(property), &payload, sizeof(payload), &bytesReturned)); + } + return S_OK; +} +#endif // ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) // //Output Pin Implementation // COutPin::COutPin( _In_ ULONG ulPinId, _In_opt_ CMultipinMft *pparent, - _In_ IKsControl* pIksControl + _In_ IKsControl* pIksControl +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + , _In_ MFSampleAllocatorUsage allocatorUsage +#endif ) - : CBasePin( ulPinId, pparent ), - m_firstSample( false ), - m_queue(nullptr) + : CBasePin(ulPinId, pparent) + , m_firstSample(false) + , m_queue(nullptr) +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + , m_allocatorUsage(allocatorUsage) +#endif { HRESULT hr = S_OK; ComPtr<IMFAttributes> spAttributes; @@ -507,6 +564,14 @@ STDMETHODIMP_(VOID) COutPin::SetFirstSample( m_firstSample = fisrtSample; } +STDMETHODIMP_(VOID) COutPin::SetAllocator( + _In_ IMFVideoSampleAllocator* pAllocator +) +{ + CAutoLock Lock(lock()); + m_spDefaultAllocator = pAllocator; +} + /*++ COutPin::FlushQueues Description: @@ -524,7 +589,7 @@ HRESULT COutPin::FlushQueues() /*++ COutPin::ChangeMediaTypeFromInpin Description: -called from the Device Transfrom When the input media type is changed. This will result in +called from the Device Transform when the input media type is changed. This will result in the xvp being possibly installed in the queue if the media types set on the input and the output dont match --*/ @@ -542,7 +607,11 @@ HRESULT COutPin::ChangeMediaTypeFromInpin( SetState(DeviceStreamState_Disabled); DMFTCHECKHR_GOTO(FlushQueues(),done); DMFTCHECKNULL_GOTO(m_queue,done, E_UNEXPECTED); // The queue should alwaye be set +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + hr = m_queue->RecreateTeeByAllocatorMode(pInMediatype, pOutMediaType, m_spDxgiManager.Get(), m_allocatorUsage, m_spDefaultAllocator.get()); +#else hr = m_queue->RecreateTee(pInMediatype, pOutMediaType, m_spDxgiManager.Get()); +#endif // ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) if ( SUCCEEDED( hr ) ) { (VOID)setMediaType( pOutMediaType ); @@ -583,10 +652,10 @@ STDMETHODIMP COutPin::GetOutputStreamInfo( /*++ COutPin::ProcessOutput Description: - called from the Device Transfrom when the transform manager demands output samples.. + called from the Device Transform when the transform manager demands output samples.. If we have samples we forward it. - If we are a photo pin then we forward only if trigger is sent. We ask the devicetransform if we have recieved the transform or not. - If we have recieved the sample and we are passing out a sample we should reset the trigger set on the Device Transform + If we are a photo pin then we forward only if trigger is sent. We ask the devicetransform if we have received the transform or not. + If we have received the sample and we are passing out a sample we should reset the trigger set on the Device Transform --*/ STDMETHODIMP COutPin::ProcessOutput(_In_ DWORD dwFlags, @@ -663,18 +732,9 @@ STDMETHODIMP CAsyncInPin::SendSample(_In_ IMFSample *pSample) { HRESULT hr = S_OK; CAutoLock Lock(lock()); - if (!m_bFlushing) + if (SUCCEEDED(Active())) { DMFTCHECKHR_GOTO(MFPutWorkItem(m_dwWorkQueueId, static_cast<IMFAsyncCallback*>(m_asyncCallback.Get()), pSample), done); - if (1 == InterlockedIncrement(&m_dwSamplesInFlight)) - { - ResetEvent(m_hHandle); - } - } - else - { - DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! 0x%p Pin Flushing %d",this, streamId()); - // Let the sample fall through } done: return hr; @@ -682,18 +742,20 @@ done: STDMETHODIMP CAsyncInPin::Init() { - m_asyncCallback = new (std::nothrow) CDMFTAsyncCallback<CAsyncInPin, &CAsyncInPin::Invoke>(this); + m_asyncCallback = new (std::nothrow) CDMFTAsyncCallback<CAsyncInPin, &CAsyncInPin::Invoke>(this,m_dwWorkQueueId); if (!m_asyncCallback) throw bad_alloc(); return S_OK; } +// Pass a secure frame buffer to our AVstream driver. HRESULT CAsyncInPin::Invoke( _In_ IMFAsyncResult* pResult ) { HRESULT hr = S_OK; ComPtr<IUnknown> spUnknown; ComPtr<IMFSample> spSample; + DMFTCHECKHR_GOTO(Active(), done); DMFTCHECKNULL_GOTO(pResult, done, E_UNEXPECTED); DMFTCHECKHR_GOTO(pResult->GetState(&spUnknown), done); @@ -701,46 +763,26 @@ HRESULT CAsyncInPin::Invoke( _In_ IMFAsyncResult* pResult ) DMFTCHECKNULL_GOTO(spSample.Get(), done, E_INVALIDARG); +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + // + // Do secure buffer post-processing. + // + RETURN_IF_FAILED(ForwardSecureBuffer(spSample.Get())); +#endif // ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) COutPin *poPin = static_cast<COutPin*>(m_outpin.Get()); DMFTCHECKHR_GOTO(poPin->AddSample(spSample.Get(), this), done); - - if (0 == InterlockedDecrement(&m_dwSamplesInFlight)) - { - // No samples in flight.. Set the Event - SetEvent(m_hHandle); - } - done: return hr; } - - -STDMETHODIMP CAsyncInPin::FlushQueues() +STDMETHODIMP_(VOID) CAsyncInPin::ShutdownPin() { - HRESULT hr = S_OK; - DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Entering "); - // Flush in async mode else it is a NOOP - { - CAutoLock Lock(lock()); - if (m_bFlushing) - { - DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Pin %d Already Flushing ", streamId()); - goto done; - } - m_bFlushing = TRUE; - } - // - // Wait for the IOs to drain - // - WaitForSingleObject(m_hHandle, INFINITE); + CAutoLock Lock(lock()); + if (m_asyncCallback.Get()) { - CAutoLock Lock(lock()); - m_bFlushing = FALSE; + (VOID)m_asyncCallback->Shutdown(); //Break reference with the parent } -done: - DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); - return hr; + CInPin::ShutdownPin(); } // @@ -808,6 +850,7 @@ STDMETHODIMP CTranslateOutPin::AddMediaType( done: return hr; } + STDMETHODIMP_(BOOL) CTranslateOutPin::IsMediaTypeSupported( _In_ IMFMediaType *pMediaType, _When_(ppIMFMediaTypeFull != nullptr, _Outptr_result_maybenull_) @@ -824,9 +867,10 @@ STDMETHODIMP_(BOOL) CTranslateOutPin::IsMediaTypeSupported( if (found != m_TranslatedMediaTypes.end()) { + ComPtr<IMFMediaType> spMediaType = (*found).second; if (ppIMFMediaTypeFull) { - *ppIMFMediaTypeFull = (*found).second; + *ppIMFMediaTypeFull = spMediaType.Detach(); } return true; } @@ -836,3 +880,24 @@ STDMETHODIMP_(BOOL) CTranslateOutPin::IsMediaTypeSupported( return CBasePin::IsMediaTypeSupported(pMediaType,ppIMFMediaTypeFull); } } + +HRESULT CTranslateOutPin::ChangeMediaTypeFromInpin( + _In_ IMFMediaType *pInMediatype, + _In_ IMFMediaType* pOutMediaType, + _In_ DeviceStreamState state) +{ + CAutoLock Lock(lock()); + // + // Set the state to disabled and while going out we will reset the state back to the requested state + // Flush so that we drop any samples we have in store!! + // + SetState(DeviceStreamState_Disabled); + RETURN_IF_FAILED(FlushQueues()); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_queue); // The queue should always be set + RETURN_IF_FAILED(m_queue->RecreateTee(pInMediatype, pOutMediaType, m_spDxgiManager.Get())); + + (VOID)setMediaType(pOutMediaType); + (VOID)SetState(state); + + return S_OK; +} diff --git a/avstream/sampledevicemft/basepin.h b/avstream/sampledevicemft/basepin.h index faafd759..cce17d0b 100644 --- a/avstream/sampledevicemft/basepin.h +++ b/avstream/sampledevicemft/basepin.h @@ -369,7 +369,7 @@ public: __requires_lock_held(m_lock) __inline HRESULT Active() { - return (m_state == DeviceStreamState_Run)?S_OK:E_FAIL; + return (m_state == DeviceStreamState_Run)?S_OK:HRESULT_FROM_WIN32(ERROR_INVALID_STATE); } __inline DWORD streamId() { @@ -511,7 +511,7 @@ public: return m_preferredStreamState; } - void ReleaseConnectedPins(); + STDMETHOD_( VOID, ShutdownPin)(); protected: ComPtr<IMFTransform> m_spSourceTransform; /*Source Transform i.e. DevProxy*/ @@ -520,15 +520,27 @@ protected: DeviceStreamState m_preferredStreamState; ComPtr<IMFMediaType> m_spPrefferedMediaType; HANDLE m_waitInputMediaTypeWaiter; /*Set when the input media type is changed*/ + + // Helper functions +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + HRESULT ForwardSecureBuffer( + _In_ IMFSample *sample + ); +#endif }; class COutPin: public CBasePin{ public: - COutPin( _In_ ULONG id = 0, + COutPin( + _In_ ULONG id = 0, _In_opt_ CMultipinMft *pparent = NULL, - _In_ IKsControl* iksControl=NULL); + _In_ IKsControl* iksControl=NULL +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + , _In_ MFSampleAllocatorUsage allocatorUsage = MFSampleAllocatorUsage_DoesNotAllocate +#endif + ); ~COutPin(); STDMETHODIMP FlushQueues(); STDMETHODIMP AddPin( @@ -541,7 +553,7 @@ public: STDMETHODIMP GetOutputStreamInfo( _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo ); - STDMETHODIMP ChangeMediaTypeFromInpin( + virtual STDMETHODIMP ChangeMediaTypeFromInpin( _In_ IMFMediaType *pInMediatype, _In_ IMFMediaType* pOutMediaType, _In_ DeviceStreamState state ); @@ -560,6 +572,16 @@ public: STDMETHODIMP_(VOID) SetFirstSample( _In_ BOOL ); + + STDMETHODIMP_(VOID) SetAllocator( + _In_ IMFVideoSampleAllocator* pAllocator + ); +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + MFSampleAllocatorUsage GetSampleAllocatorUsage() + { + return m_allocatorUsage; + } +#endif UINT32 GetMediatypeCount() { return (UINT32)m_listOfMediaTypes.size(); @@ -568,13 +590,17 @@ public: protected: CPinQueue * m_queue; /* Queue where the sample will be stored*/ BOOL m_firstSample; +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + MFSampleAllocatorUsage m_allocatorUsage; +#endif + wil::com_ptr_nothrow<IMFVideoSampleAllocator> m_spDefaultAllocator; + }; class CAsyncInPin: public CInPin { public: - STDMETHODIMP FlushQueues(); STDMETHODIMP SendSample( _In_ IMFSample * ); @@ -587,30 +613,19 @@ public: _In_ ULONG ulPinId, _In_ CMultipinMft *pParent) : CInPin(pAttributes, ulPinId, pParent) - , m_dwSamplesInFlight(0) - , m_hHandle(INVALID_HANDLE_VALUE) - , m_bFlushing(FALSE) , m_asyncCallback(nullptr) { - m_hHandle = CreateEvent(NULL, TRUE, TRUE, L"Async_Pin Event"); - if (m_hHandle == nullptr) - throw bad_alloc(); Init(); - } + STDMETHOD_(VOID, ShutdownPin)(); ~CAsyncInPin() { FlushQueues(); - CloseHandle(m_hHandle); - m_hHandle = INVALID_HANDLE_VALUE; } ComPtr<CDMFTAsyncCallback<CAsyncInPin,&CAsyncInPin::Invoke> > m_asyncCallback; // Callback object - HANDLE m_hHandle; // Handles to keep flush state - DWORD m_dwSamplesInFlight; // Samples in flight i.e. waiting for the callback functions to be called - BOOL m_bFlushing; //////////////////////////////////////////////////////////////////////////////////////// // End of Asynchronous callback definitions //////////////////////////////////////////////////////////////////////////////////////// @@ -628,7 +643,12 @@ class CTranslateOutPin : public COutPin { public: CTranslateOutPin(_In_ ULONG id = 0, _In_opt_ CMultipinMft *pparent = NULL, - _In_ IKsControl* iksControl = NULL) : COutPin(id, pparent, iksControl) + _In_ IKsControl* iksControl = NULL) + : COutPin(id, pparent, iksControl +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + , MFSampleAllocatorUsage_UsesCustomAllocator +#endif + ) { SetUINT32(MF_SD_VIDEO_SPHERICAL, TRUE); } @@ -639,8 +659,14 @@ public: _In_ IMFMediaType *pMediaType, _When_(ppIMFMediaTypeFull != nullptr, _Outptr_result_maybenull_) IMFMediaType **ppIMFMediaTypeFull); + STDMETHOD(ChangeMediaTypeFromInpin)( + _In_ IMFMediaType *pInMediatype, + _In_ IMFMediaType* pOutMediaType, + _In_ DeviceStreamState state); + protected: map<IMFMediaType*, IMFMediaType*> m_TranslatedMediaTypes; }; + diff --git a/avstream/sampledevicemft/common.h b/avstream/sampledevicemft/common.h index 72242fa3..bc6bbf28 100644 --- a/avstream/sampledevicemft/common.h +++ b/avstream/sampledevicemft/common.h @@ -291,7 +291,7 @@ HRESULT ExceptionBoundary(Lambda&& lambda) } catch (...) { - return E_FAIL; + return E_UNEXPECTED; } } @@ -581,7 +581,7 @@ public: pcbBuffer); if (dwHeightInPixels* abs(*plStride) > *pcbBuffer) { - hr = E_FAIL; + hr = E_UNEXPECTED; } } else if (m_sp2DBuffer) { @@ -652,6 +652,13 @@ HRESULT IsDXFormatSupported( _Outptr_opt_ ID3D11Device** ppDevice, _In_opt_ PUINT32 pSupportedFormat); +HRESULT ConfigureAllocator( + _In_ IMFMediaType* pOutputMediaType, + _In_ GUID streamCategory, + _In_ IUnknown* pDeviceManagerUnk, + _In_ BOOL &isDxAllocator, + _In_ IMFVideoSampleAllocator* pAllocator); + HRESULT CreateAllocator( _In_ IMFMediaType* pOutputMediaType, _In_ GUID streamCategory, _In_ IUnknown* pDeviceManagerUnk, diff --git a/avstream/sampledevicemft/contosodevice.h b/avstream/sampledevicemft/contosodevice.h new file mode 100644 index 00000000..66759f69 --- /dev/null +++ b/avstream/sampledevicemft/contosodevice.h @@ -0,0 +1,28 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// The Contoso device comes with an SDK +// that defines various Contoso-specific types, +// all of which have CONTOSO in their names. + +#pragma once + +// This payload is used when sending a KSPROPERTY_CONTOSODEVICE_PROCESSBUFFER. +struct CONTOSODEVICE_PROCESSBUFFER_PAYLOAD +{ + GUID identifier; + DWORD size; +}; + +// Define a custom property set for driver communications. +// {C5175EE2-583E-43A2-8C26-765287BCEB9D} +#define STATIC_PROPSETID_CONTOSODEVICE\ + 0xc5175ee2, 0x583e, 0x43a2, 0x8c, 0x26, 0x76, 0x52, 0x87, 0xbc, 0xeb, 0x9d +DEFINE_GUIDSTRUCT("C5175EE2-583E-43A2-8C26-765287BCEB9D", PROPSETID_CONTOSODEVICE); +#define PROPSETID_CONTOSODEVICE DEFINE_GUIDNAMED(PROPSETID_CONTOSODEVICE) +#define KSPROPERTY_CONTOSODEVICE_PROCESSBUFFER 0 // Send a frame buffer ID to the driver. + diff --git a/avstream/sampledevicemft/custompin.cpp b/avstream/sampledevicemft/custompin.cpp index 00edb329..edb5fadb 100644 --- a/avstream/sampledevicemft/custompin.cpp +++ b/avstream/sampledevicemft/custompin.cpp @@ -35,7 +35,7 @@ STDMETHODIMP CCustomPin::SendSample( // Log sample and exit.. The pipeline will just keep on churning more samples till // We go into the stop state // - DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Custom Pin %d recieved Sample %p", streamId(), pSample); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Custom Pin %d received Sample %p", streamId(), pSample); return S_OK; } diff --git a/avstream/sampledevicemft/dllmain.cpp b/avstream/sampledevicemft/dllmain.cpp index 6978a9d5..88bfe6ab 100644 --- a/avstream/sampledevicemft/dllmain.cpp +++ b/avstream/sampledevicemft/dllmain.cpp @@ -196,13 +196,35 @@ STDMETHODIMP_(BOOL) WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, void *) DisableThreadLibraryCalls(hInstance); #ifdef MF_WPP WPP_INIT_TRACING(L"MultiPinMft"); + + // Hook up WIL tracing to our trace provider. + wil::SetResultLoggingCallback( + [](const wil::FailureInfo &failure) + { + wchar_t debugString[2048]; + if (SUCCEEDED(wil::GetFailureLogString(debugString, ARRAYSIZE(debugString), failure))) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_ERROR, L"%S", debugString); + } + else + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_ERROR, + L"File: %s, Line: %u, Error: 0x%08X - %S\n", + failure.pszFile, + failure.uLineNumber, + failure.hr, + failure.pszMessage); + } + } + ); #endif } else if (dwReason == DLL_PROCESS_DETACH) { #ifdef MF_WPP - WPP_CLEANUP(); + wil::SetResultLoggingCallback(nullptr); + WPP_CLEANUP(); #endif } return TRUE; diff --git a/avstream/sampledevicemft/multipinmft.cpp b/avstream/sampledevicemft/multipinmft.cpp index 3467870b..f4fe3d27 100644 --- a/avstream/sampledevicemft/multipinmft.cpp +++ b/avstream/sampledevicemft/multipinmft.cpp @@ -1322,6 +1322,54 @@ done: } #endif +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) +// +// IMFSampleAllocatorControl Inferface function declarations +// + +STDMETHODIMP CMultipinMft::SetDefaultAllocator( + _In_ DWORD dwOutputStreamID, + _In_ IUnknown *pAllocator +) +{ + CAutoLock Lock(m_critSec); + + // SetAllocator will be called on the streamId that returns MFSampleAllocatorMode_Default + wil::com_ptr_nothrow<COutPin> outPin = GetOutPin(dwOutputStreamID); + RETURN_HR_IF_NULL(E_INVALIDARG, outPin); + RETURN_HR_IF_NULL(E_INVALIDARG, pAllocator); + + wil::com_ptr_nothrow<IMFVideoSampleAllocator> defaultAllocator; + RETURN_IF_FAILED(pAllocator->QueryInterface(&defaultAllocator)); + outPin->SetAllocator(defaultAllocator.get()); + + return S_OK; +} + +STDMETHODIMP CMultipinMft::GetAllocatorUsage( + _In_ DWORD dwOutputStreamID, + _Out_ DWORD* pdwInputStreamID, + _Out_ MFSampleAllocatorUsage* peUsage +) +{ + CAutoLock Lock(m_critSec); + + RETURN_HR_IF_NULL(E_INVALIDARG, peUsage); + + wil::com_ptr_nothrow<COutPin> outPin = GetOutPin(dwOutputStreamID); + RETURN_HR_IF_NULL(MF_E_INVALIDSTREAMNUMBER, outPin); + *peUsage = outPin->GetSampleAllocatorUsage(); + + if (*peUsage == MFSampleAllocatorUsage_DoesNotAllocate) + { + RETURN_HR_IF_NULL(E_INVALIDARG, pdwInputStreamID); + RETURN_IF_FAILED(GetConnectedInpin((ULONG)dwOutputStreamID, *(ULONG*)pdwInputStreamID)); + } + + return S_OK; +} +#endif // ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + // // HELPER FUNCTIONS // @@ -1513,7 +1561,7 @@ HRESULT CMultipinMft::BridgeInputPinOutputPin( HRESULT hr = S_OK; ULONG ulIndex = 0; ULONG ulAddedMediaTypeCount = 0; - ComPtr<IMFMediaType> pMediaType = nullptr; + ComPtr<IMFMediaType> spMediaType; DMFTCHECKNULL_GOTO( piPin, done, E_INVALIDARG ); DMFTCHECKNULL_GOTO( poPin, done, E_INVALIDARG ); @@ -1523,19 +1571,17 @@ HRESULT CMultipinMft::BridgeInputPinOutputPin( // sure any pin advertised supports at least one media type. The pipeline doesn't // like pins with no media types // - while ( SUCCEEDED( hr = piPin->GetMediaTypeAt( ulIndex++, &pMediaType ))) + while ( SUCCEEDED( hr = piPin->GetMediaTypeAt( ulIndex++, spMediaType.ReleaseAndGetAddressOf() ))) { GUID subType = GUID_NULL; - DMFTCHECKHR_GOTO( pMediaType->GetGUID(MF_MT_SUBTYPE,&subType), done ); + DMFTCHECKHR_GOTO( spMediaType->GetGUID(MF_MT_SUBTYPE,&subType), done ); { - DMFTCHECKHR_GOTO(hr = poPin->AddMediaType(NULL, pMediaType.Get() ), done ); + DMFTCHECKHR_GOTO(hr = poPin->AddMediaType(NULL, spMediaType.Get() ), done ); if (hr == S_OK) { ulAddedMediaTypeCount++; } } - - pMediaType = nullptr; } if (ulAddedMediaTypeCount == 0) { @@ -1679,7 +1725,7 @@ STDMETHODIMP CMultipinMft::Shutdown( CInPin *pInPin = static_cast<CInPin *>(m_InPins[ulIndex]); // Deref on the connected outpins to break reference loop - pInPin->ReleaseConnectedPins(); + (VOID)pInPin->ShutdownPin(); } #if defined (MF_DEVICEMFT_ALLOW_MFT0_LOAD) && defined (MFT_UNIQUE_METHOD_NAMES) for (ULONG ulIndex = 0, ulSize = (ULONG)m_OutPins.size(); ulIndex < ulSize; ulIndex++) diff --git a/avstream/sampledevicemft/multipinmft.h b/avstream/sampledevicemft/multipinmft.h index 080f4a7a..70367f1d 100644 --- a/avstream/sampledevicemft/multipinmft.h +++ b/avstream/sampledevicemft/multipinmft.h @@ -48,6 +48,9 @@ class CMultipinMft : , public IMFGetService #endif , public CDMFTModuleLifeTimeManager +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + , public IMFSampleAllocatorControl +#endif { friend class CPinCreationFactory; public: @@ -275,7 +278,22 @@ public: } #endif - +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + // + // IMFSampleAllocatorControl Inferface function declarations + // + + STDMETHOD(SetDefaultAllocator)( + _In_ DWORD dwOutputStreamID, + _In_ IUnknown *pAllocator + ); + + STDMETHOD(GetAllocatorUsage)( + _In_ DWORD dwOutputStreamID, + _Out_ DWORD* pdwInputStreamID, + _Out_ MFSampleAllocatorUsage* peUsage + ); +#endif static STDMETHODIMP CreateInstance( REFIID iid, void **ppMFT); diff --git a/avstream/sampledevicemft/multipinmft.vcxproj b/avstream/sampledevicemft/multipinmft.vcxproj index 0f560e2c..1a0716bb 100644 --- a/avstream/sampledevicemft/multipinmft.vcxproj +++ b/avstream/sampledevicemft/multipinmft.vcxproj @@ -262,9 +262,17 @@ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + <None Include="packages.config" /> </ItemGroup> <ItemGroup> <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <Import Project="packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> + </Target> </Project>
\ No newline at end of file diff --git a/avstream/sampledevicemft/multipinmft.vcxproj.Filters b/avstream/sampledevicemft/multipinmft.vcxproj.Filters index e06f675d..36af8e37 100644 --- a/avstream/sampledevicemft/multipinmft.vcxproj.Filters +++ b/avstream/sampledevicemft/multipinmft.vcxproj.Filters @@ -42,5 +42,6 @@ <None Include="Source.def"> <Filter>Source Files</Filter> </None> + <None Include="packages.config" /> </ItemGroup> </Project>
\ No newline at end of file diff --git a/avstream/sampledevicemft/multipinmfthelpers.cpp b/avstream/sampledevicemft/multipinmfthelpers.cpp index 904625ad..34cebe8b 100644 --- a/avstream/sampledevicemft/multipinmfthelpers.cpp +++ b/avstream/sampledevicemft/multipinmfthelpers.cpp @@ -16,8 +16,7 @@ class CMediaTypePrinter; // CPinQueue::CPinQueue( _In_ DWORD dwPinId , _In_ IMFDeviceTransform* pParent) -: m_teer(0), - m_dwInPinId(dwPinId), + :m_dwInPinId(dwPinId), m_pTransform(pParent), m_cRef(1) @@ -30,6 +29,7 @@ CPinQueue::CPinQueue( _In_ DWORD dwPinId , } CPinQueue::~CPinQueue( ) { + Ctee::ReleaseTee(m_spTeer); } /*++ @@ -59,10 +59,10 @@ STDMETHODIMP_(VOID) CPinQueue::InsertInternal( _In_ IMFSample *pSample ) STDMETHODIMP CPinQueue::Insert( _In_ IMFSample *pSample ) // -//m_teer is the wraptee.. this could be a null tee which is a passthrough, an xvp tee which inserts an xvp into the queue etc +//m_spTeer is the wraptee.. this could be a null tee which is a passthrough, an xvp tee which inserts an xvp into the queue etc // { - return m_teer->PassThrough( pSample ); + return m_spTeer->PassThrough( pSample ); } /*++ @@ -97,9 +97,9 @@ VOID CPinQueue::Clear( ) // Stop the tees // Execute Flush // - if (m_teer) + if (m_spTeer) { - m_teer->Stop(); + m_spTeer->Stop(); } while ( !Empty() ) @@ -114,62 +114,99 @@ Description: RecreateTee creates the underlying Tees in the queue. It accepts the input media type which is the media type set on the input pin and the output mediatype, which (duh) is the media type on the output pin - It also takes an IUnknown which is the D3D Manager. if it is valid we might use it if we + It also takes an IUnknown which is the D3D Manager. if it is valid we might use it if we have an xvp in the path i.e. inputMediatype =! outputMediatype --*/ -STDMETHODIMP CPinQueue::RecreateTee( _In_ IMFMediaType *inMediatype, +STDMETHODIMP CPinQueue::RecreateTee(_In_ IMFMediaType *inMediatype, _In_ IMFMediaType *outMediatype, - _In_opt_ IUnknown* punkManager ) + _In_opt_ IUnknown* punkManager) { HRESULT hr = S_OK; DMFT_conversion_type operation = DeviceMftTransformTypeIllegal; - - SAFE_DELETE(m_teer); // Should release the reference - CNullTee *nulltee = new (std::nothrow) CNullTee(this); - DMFTCHECKNULL_GOTO( nulltee, done, E_OUTOFMEMORY); + Ctee::ReleaseTee(m_spTeer); + + ComPtr<CNullTee> spNulltee = new (std::nothrow) CNullTee(this); + DMFTCHECKNULL_GOTO(spNulltee.Get(), done, E_OUTOFMEMORY); DMFTCHECKHR_GOTO(CompareMediaTypesForConverter(inMediatype, outMediatype, &operation), done); if (operation == DeviceMftTransformTypeDecoder) { // Decoder needed - CDecoderTee* pDecTee = new (std::nothrow) CDecoderTee(nulltee, + CDecoderTee* pDecTee = new (std::nothrow) CDecoderTee(spNulltee.Get(), static_cast<CMultipinMft*>(m_pTransform)->GetQueueId(), pinCategory()); DMFTCHECKNULL_GOTO(pDecTee, done, E_OUTOFMEMORY); (void)pDecTee->SetD3DManager(punkManager); - DMFTCHECKHR_GOTO(pDecTee->SetMediaTypes(inMediatype, outMediatype),done); - m_teer = dynamic_cast<Ctee*>(pDecTee); + DMFTCHECKHR_GOTO(pDecTee->SetMediaTypes(inMediatype, outMediatype), done); + m_spTeer = pDecTee; } - else if (operation == DeviceMftTransformTypeXVP) + else if (operation == DeviceMftTransformTypeXVP) { - CXvptee* pXvptee = new (std::nothrow) CXvptee(nulltee, pinCategory()); + CXvptee* pXvptee = new (std::nothrow) CXvptee(spNulltee.Get(), pinCategory()); DMFTCHECKNULL_GOTO(pXvptee, done, E_OUTOFMEMORY); - (void)pXvptee->SetD3DManager( punkManager ); - DMFTCHECKHR_GOTO(pXvptee->SetMediaTypes( inMediatype, outMediatype ),done); - m_teer = dynamic_cast< Ctee* >( pXvptee ); + (void)pXvptee->SetD3DManager(punkManager); + DMFTCHECKHR_GOTO(pXvptee->SetMediaTypes(inMediatype, outMediatype), done); + m_spTeer = pXvptee; } else { - m_teer = nulltee; /*A simple passthrough*/ + m_spTeer = spNulltee.Get(); /*A simple passthrough*/ } done: - DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); if (FAILED(hr)) { - if (m_teer){ - delete(m_teer); - m_teer = NULL; + Ctee::ReleaseTee(m_spTeer); + } + return hr; +} +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) +STDMETHODIMP CPinQueue::RecreateTeeByAllocatorMode( + _In_ IMFMediaType* inMediatype, + _In_ IMFMediaType* outMediatype, + _In_opt_ IUnknown* punkManager, + _In_ MFSampleAllocatorUsage allocatorUsage, + _In_opt_ IMFVideoSampleAllocator* pAllocator) +{ + HRESULT hr = S_OK; + + Ctee::ReleaseTee(m_spTeer);// Should release the reference + + wistd::unique_ptr<CNullTee> nulltee = wil::make_unique_nothrow<CNullTee>(this); + RETURN_IF_NULL_ALLOC(nulltee); + + if (allocatorUsage == MFSampleAllocatorUsage_DoesNotAllocate) + { + m_spTeer.Attach(nulltee.release()); /*A simple passthrough*/ + } + else + { + wistd::unique_ptr<CSampleCopytee> sampleCopytee; + RETURN_IF_NULL_ALLOC(sampleCopytee); + (void)sampleCopytee->SetD3DManager(punkManager); + + if (allocatorUsage == MFSampleAllocatorUsage_UsesProvidedAllocator) + { + RETURN_HR_IF_NULL(E_INVALIDARG, pAllocator); + sampleCopytee = wil::make_unique_nothrow<CSampleCopytee>(nulltee.release(), pinCategory(), pAllocator); + } + else + { + sampleCopytee = wil::make_unique_nothrow<CSampleCopytee>(nulltee.release(), pinCategory(), nullptr); } + RETURN_IF_FAILED(sampleCopytee->SetMediaTypes(inMediatype, outMediatype)); + m_spTeer.Attach(sampleCopytee.release()); } + return hr; } - +#endif // ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) #ifdef MF_DEVICEMFT_ADD_GRAYSCALER_ STDMETHODIMP CPinQueueWithGrayScale::RecreateTee( _In_ IMFMediaType *inMediatype, _In_ IMFMediaType *outMediatype, @@ -183,7 +220,7 @@ STDMETHODIMP CPinQueueWithGrayScale::RecreateTee( _In_ IMFMediaType *inMediatyp DMFTCHECKHR_GOTO(CPinQueue::RecreateTee(inMediatype, outMediatype, punkManager),done); - DMFTCHECKNULL_GOTO(m_teer, done, E_UNEXPECTED); + DMFTCHECKNULL_GOTO(m_spTeer, done, E_UNEXPECTED); // Wrap the media type with Gray scale tee only if the input media type is a YUY2, UYVY, NV12 or RGB32 DMFTCHECKHR_GOTO(inMediatype->GetGUID(MF_MT_SUBTYPE, &gInputSubType), done); if (IsEqualCLSID(gInputSubType, MFVideoFormat_NV12) @@ -192,9 +229,9 @@ STDMETHODIMP CPinQueueWithGrayScale::RecreateTee( _In_ IMFMediaType *inMediatyp ||IsEqualCLSID(gInputSubType, MFVideoFormat_RGB32)) { CGrayTee *pTee = NULL; - pTee = new (std::nothrow) CGrayTee(m_teer); + pTee = new (std::nothrow) CGrayTee(m_spTeer); DMFTCHECKHR_GOTO(pTee->SetMediaTypes(inMediatype, outMediatype), done); - m_teer = dynamic_cast< Ctee* >(pTee); + m_spTeer = dynamic_cast< Ctee* >(pTee); } else { @@ -251,9 +288,9 @@ STDMETHODIMP CWrapTee::PassThrough( _In_ IMFSample* pInSample ) DMFTCHECKNULL_GOTO(pInSample, done, S_OK); // pass through for no sample DMFTCHECKHR_GOTO(Do(pInSample, &pOutSample,newSample),done); - if (m_objectWrapped) + if (m_spObjectWrapped) { - if (SUCCEEDED(hr = m_objectWrapped->PassThrough( pOutSample ))) + if (SUCCEEDED(hr = m_spObjectWrapped->PassThrough( pOutSample ))) { //@@@@README There is a very bad bug in the pipeline that the device transform manager // is not releasing the reference on the sample when it is passed to the device MFT so any @@ -289,7 +326,7 @@ HRESULT CVideoProcTee::SetMediaTypes(_In_ IMFMediaType* pInMediaType, _In_ IMFMe ComPtr<IMFTransform> spTransform; DMFTCHECKHR_GOTO(CWrapTee::SetMediaTypes(pInMediaType, pOutMediaType),done); DMFTCHECKHR_GOTO(Configure(pInMediaType, pOutMediaType, spTransform.GetAddressOf()), done); - m_videoProcessor = spTransform.Detach(); + m_spVideoProcessor = spTransform.Detach(); // // Start streaming // @@ -298,6 +335,8 @@ done: return hr; } + + HRESULT CVideoProcTee::CreateAllocator() { HRESULT hr = S_OK; @@ -307,18 +346,37 @@ HRESULT CVideoProcTee::CreateAllocator() return S_OK; } - if (m_spAllocator.Get()) + if (m_spPrivateAllocator) { // Release what we have currently - DMFTCHECKHR_GOTO(m_spAllocator->UninitializeSampleAllocator(), done); - m_spAllocator = nullptr; + RETURN_IF_FAILED(m_spPrivateAllocator->UninitializeSampleAllocator()); + m_spPrivateAllocator = nullptr; + } +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + if (m_spDefaultAllocator) + { + // Configure default allocator + m_spDefaultAllocator->UninitializeSampleAllocator(); + RETURN_IF_FAILED(::ConfigureAllocator( + m_pOutputMediaType.Get(), + m_streamCategory, + m_spDeviceManagerUnk.Get(), + m_fSetD3DManager, + m_spDefaultAllocator.Get())); + } + else +#endif // ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + { + // Create and configure private allocator + RETURN_IF_FAILED(MFCreateVideoSampleAllocatorEx(IID_PPV_ARGS(m_spPrivateAllocator.ReleaseAndGetAddressOf()))); + RETURN_IF_FAILED(::ConfigureAllocator( + m_pOutputMediaType.Get(), + m_streamCategory, + m_spDeviceManagerUnk.Get(), + m_fSetD3DManager, + m_spPrivateAllocator.Get())); } - DMFTCHECKHR_GOTO(::CreateAllocator(m_pOutputMediaType.Get(), - m_streamCategory, m_spDeviceManagerUnk.Get(), - m_fSetD3DManager, - m_spAllocator.GetAddressOf()), done); -done: return hr; } @@ -342,6 +400,7 @@ CXvptee::~CXvptee() HRESULT CXvptee::StartStreaming() { HRESULT hr = S_OK; + CAutoLock Lock(m_Lock); DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0), done); // ulParam set to zero DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0), done); // ulParam set to zero done: @@ -351,6 +410,8 @@ done: HRESULT CXvptee::StopStreaming() { HRESULT hr = S_OK; + CAutoLock Lock(m_Lock); + SetAsyncStatus(MF_E_SHUTDOWN); DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0), done); // Flush the stream DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0), done); // Notify end of stream DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0), done); // Notify end of streaming @@ -374,17 +435,17 @@ STDMETHODIMP CXvptee::Do(_In_ IMFSample *pSample, _Outptr_ IMFSample** ppOutSamp MFT_OUTPUT_DATA_BUFFER outputSample; ComPtr <IMFSample> spXVPOutputSample; DWORD dwStatus = 0; + CAutoLock Lock(m_Lock); DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! XVP ProcessOutput Processing Sample =%p", pSample); DMFTCHECKNULL_GOTO(ppOutSample, done, E_INVALIDARG); - *ppOutSample = nullptr; - + DMFTCHECKHR_GOTO(GetAsyncStatus(), done); DMFTCHECKHR_GOTO(Transform()->MFTProcessInput(0, pSample, 0),done); outputSample.dwStreamID = 0; outputSample.pSample = NULL; - DMFTCHECKHR_GOTO(m_spAllocator->AllocateSample(&outputSample.pSample), done); + DMFTCHECKHR_GOTO(m_spPrivateAllocator->AllocateSample(&outputSample.pSample), done); hr = Transform()->MFTProcessOutput(0, 1, &outputSample, &dwStatus); @@ -492,16 +553,18 @@ done: CDecoderTee::~CDecoderTee() { (VOID)StopStreaming(); - if (m_spAllocator) + MFUnlockWorkQueue(m_dwCameraStreamWorkQueueId); + if (m_spPrivateAllocator) { - m_spAllocator->UninitializeSampleAllocator(); - m_spAllocator = nullptr; + m_spPrivateAllocator->UninitializeSampleAllocator(); + m_spPrivateAllocator = nullptr; } } HRESULT CDecoderTee::StartStreaming() { HRESULT hr = S_OK; + CAutoLock Lock(&m_Lock); DMFTCHECKNULL_GOTO(Transform(), done, MF_E_UNEXPECTED); DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0), done); DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0), done); @@ -512,13 +575,11 @@ HRESULT CDecoderTee::StartStreaming() HRESULT CDecoderTee::StopStreaming() { HRESULT hr = S_OK; - ComPtr<IMFTransform> spTransform = Transform(); + ComPtr<IMFTransform> spTransform; { - CAutoLock Lock(&m_critSec); - if (m_hSyncHandle) - { - ResetEvent(m_hSyncHandle); - } + CAutoLock Lock(&m_Lock); + spTransform = Transform(); + SetAsyncStatus(MF_E_SHUTDOWN); } if (spTransform.Get()) { @@ -531,15 +592,7 @@ HRESULT CDecoderTee::StopStreaming() { (void)spShutdown->Shutdown(); } - m_critSec.Lock(); - SetAsyncStatus(MF_E_SHUTDOWN); - MFUnlockWorkQueue(m_dwCameraStreamWorkQueueId); - if (m_ulProcessOutputsInFlight > 0) - { - m_critSec.Unlock(); - WaitForSingleObject(m_hSyncHandle, INFINITE); - - } + spTransform = nullptr; } done: @@ -553,17 +606,12 @@ STDMETHODIMP CDecoderTee::Configure(_In_opt_ IMFMediaType *inType, HRESULT hr = S_OK; ComPtr<IMFTransform> spTransform; GUID gInSubType = GUID_NULL, gOutSubType = GUID_NULL; + DMFTCHECKNULL_GOTO(ppTransform, done, E_INVALIDARG); DMFTCHECKNULL_GOTO(inType, done, E_INVALIDARG); DMFTCHECKNULL_GOTO(outType, done, E_INVALIDARG); *ppTransform = nullptr; - SAFE_CLOSEHANDLE(m_hSyncHandle); - m_hSyncHandle = CreateEvent(NULL, - FALSE, - TRUE, - TEXT("SyncDecoderWaiter") - ); if (!(SUCCEEDED(inType->GetGUID(MF_MT_SUBTYPE, &gInSubType)) && SUCCEEDED(outType->GetGUID(MF_MT_SUBTYPE, &gOutSubType)))) { @@ -592,11 +640,6 @@ STDMETHODIMP CDecoderTee::Configure(_In_opt_ IMFMediaType *inType, } DMFTCHECKNULL_GOTO(spTransform.Get(), done, E_UNEXPECTED); - if (m_D3daware && SUCCEEDED(IsDXFormatSupported(reinterpret_cast<IMFDXGIDeviceManager*>(m_spDeviceManagerUnk.Get()), gOutSubType, nullptr, nullptr))) - { - DMFTCHECKHR_GOTO(spTransform->MFTProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)m_spDeviceManagerUnk.Get()), done); - } - DMFTCHECKHR_GOTO(spTransform->MFTGetOutputStreamInfo(m_dwMFTOutputId, &outputStreamInfo), done); m_bProducesSamples = (outputStreamInfo.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)); @@ -608,7 +651,7 @@ STDMETHODIMP CDecoderTee::Configure(_In_opt_ IMFMediaType *inType, } // Create a callback for processoutputs to be called on - m_asyncCallback = new (std::nothrow) CDMFTAsyncCallback<CDecoderTee, &CDecoderTee::Invoke>(this); + m_asyncCallback = new (std::nothrow) CDMFTAsyncCallback<CDecoderTee, &CDecoderTee::Invoke>(this, m_dwQueueId); DMFTCHECKNULL_GOTO(m_asyncCallback.Get(), done, E_OUTOFMEMORY); if (m_fAsyncMFT) @@ -636,9 +679,10 @@ STDMETHODIMP CDecoderTee::Do(_In_ IMFSample* pSample, _Outptr_ IMFSample **ppout { HRESULT hr = S_OK; ComPtr<IMFSample> spOutputSample; + CAutoLock lock(m_Lock); ComPtr<IMFTransform> spTransform = Transform(); + newSample = false; - CAutoLock lock(m_critSec); DMFTCHECKNULL_GOTO(ppoutSample, done, E_INVALIDARG); *ppoutSample = nullptr; DMFTCHECKHR_GOTO(GetAsyncStatus(), done); @@ -663,7 +707,6 @@ STDMETHODIMP CDecoderTee::Do(_In_ IMFSample* pSample, _Outptr_ IMFSample **ppout spInputSample.Detach(); hr = S_OK; // Queue a work item for process output - m_ulProcessOutputsInFlight++; DMFTCHECKHR_GOTO(MFPutWorkItem(m_dwCameraStreamWorkQueueId, m_asyncCallback.Get(), nullptr), done); break; } @@ -720,7 +763,7 @@ HRESULT CDecoderTee::Invoke(_In_ IMFAsyncResult* pResult) ComPtr<IMFTransform> spDecoderTemp; ComPtr<IMFSample> spOutputSample; BOOL bSendSample = FALSE; - CAutoLock lock(m_critSec); + CAutoLock lock(m_Lock); spDecoderTemp = Transform(); @@ -748,12 +791,12 @@ HRESULT CDecoderTee::Invoke(_In_ IMFAsyncResult* pResult) case METransformHaveOutput: { ComPtr<IMFSample> spDecodedSample; - m_critSec.Unlock(); + m_Lock.Unlock(); // // The processoutput call blocks for H264 sometimes. This call back is serialized // hr = ProcessOutputSync(spDecodedSample.GetAddressOf()); - m_critSec.Lock(); + m_Lock.Lock(); // Did we error out when we released the lock? DMFTCHECKHR_GOTO(GetAsyncStatus(), done); if (SUCCEEDED(hr)) @@ -780,13 +823,12 @@ HRESULT CDecoderTee::Invoke(_In_ IMFAsyncResult* pResult) { // Synchronous mode. We only need it for processoutputs ComPtr<IMFSample> spDecodedSample; - m_ulProcessOutputsInFlight--; DMFTCHECKHR_GOTO(GetAsyncStatus(), done); DMFTCHECKNULL_GOTO(pResult, done, E_UNEXPECTED); DMFTCHECKHR_GOTO(pResult->GetStatus(), done); - m_critSec.Unlock(); + m_Lock.Unlock(); hr = ProcessOutputSync(spDecodedSample.GetAddressOf()); - m_critSec.Lock(); + m_Lock.Lock(); if (SUCCEEDED(hr)) { // send the sample on its way @@ -805,9 +847,9 @@ done: if (bSendSample && spOutputSample.Get()) { - if (m_objectWrapped) + if (m_spObjectWrapped) { - hr = m_objectWrapped->PassThrough(spOutputSample.Get()); + hr = m_spObjectWrapped->PassThrough(spOutputSample.Get()); spOutputSample = nullptr; } } @@ -823,11 +865,6 @@ done: { SetAsyncStatus(hr); } - if (m_StoppedAndWaiting) - { - // Stop is waiting on the threads to run - SetEvent(m_hSyncHandle); - } return hr; } @@ -836,11 +873,11 @@ done: // is holding on to the buffers, could be the xvp then this function will error // out the pipeline. you should see the ALLOCATOR_E_EMPTY error pop up // -_Ret_maybenull_ HRESULT CDecoderTee::GetSample( IMFSample** ppSample ) +HRESULT CDecoderTee::GetSample( IMFSample** ppSample ) { - if (!m_bProducesSamples && m_spAllocator.Get()) + if (!m_bProducesSamples && m_spPrivateAllocator.Get()) { - return m_spAllocator->AllocateSample(ppSample); + return m_spPrivateAllocator->AllocateSample(ppSample); } return S_OK; } @@ -890,7 +927,7 @@ HRESULT CDecoderTee::ProcessFormatChange() // Also note, The platform doesn't support dynamic media type changes from the stream coming from the // source. // - ComPtr<IMFTransform> spXvp; + ComPtr<CXvptee> spXvpTee; DMFTCHECKHR_GOTO(m_pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &guidPreviousSubType), done); for (DWORD i = 0; ; i++) @@ -918,11 +955,11 @@ HRESULT CDecoderTee::ProcessFormatChange() // // Create the XVP and insert it into the chain manually. set the output to the mediatype requested by the platform // - CXvptee* pXvpTee = new (std::nothrow) CXvptee(m_objectWrapped ,m_streamCategory); - DMFTCHECKNULL_GOTO(pXvpTee, done, E_OUTOFMEMORY); - (VOID)pXvpTee->SetD3DManager(m_spDeviceManagerUnk.Get()); - DMFTCHECKHR_GOTO(pXvpTee->SetMediaTypes(spDecoderOutputMediaType.Get(), m_pOutputMediaType.Get()), done); - m_objectWrapped = pXvpTee; + spXvpTee = new (std::nothrow) CXvptee(m_spObjectWrapped.Get() ,m_streamCategory); + DMFTCHECKNULL_GOTO(spXvpTee.Get(), done, E_OUTOFMEMORY); + (VOID)spXvpTee->SetD3DManager(m_spDeviceManagerUnk.Get()); + DMFTCHECKHR_GOTO(spXvpTee->SetMediaTypes(spDecoderOutputMediaType.Get(), m_pOutputMediaType.Get()), done); + m_spObjectWrapped = spXvpTee; spDecoderOutputMediaType = nullptr; @@ -946,6 +983,7 @@ HRESULT CDecoderTee::ConfigDecoder(_In_ IMFTransform* pTransform, _In_ GUID guid GUID guidMajorType; GUID guidSubtype; DWORD dwMediaTypeIndex = 0; + ComPtr<IMFDXGIDeviceManager> spDxgiManager; UNREFERENCED_PARAMETER(guidSubType); DMFTCHECKNULL_GOTO(pTransform, done, E_INVALIDARG); @@ -973,12 +1011,22 @@ HRESULT CDecoderTee::ConfigDecoder(_In_ IMFTransform* pTransform, _In_ GUID guid (void)ConfigRealTimeMFT(pTransform); (void)pTransform->MFTGetStreamIDs(1, &m_dwMFTInputId, 1, &m_dwMFTOutputId); - // Try to set input mediatype on MJPG decoder. + DMFTCHECKHR_GOTO(m_pOutputMediaType->GetMajorType(&guidMajorType), done); + DMFTCHECKHR_GOTO(m_pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &guidSubtype), done); + + if (m_spDeviceManagerUnk.Get()) + { + DMFTCHECKHR_GOTO(m_spDeviceManagerUnk.As(&spDxgiManager), done); + if (m_D3daware && SUCCEEDED(IsDXFormatSupported(spDxgiManager.Get(), guidSubtype, nullptr, nullptr))) + { + // Set the DXGI Manager here, before the input type is set + DMFTCHECKHR_GOTO(pTransform->MFTProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)m_spDeviceManagerUnk.Get()), done); + } + } DMFTCHECKHR_GOTO(pTransform->MFTSetInputType(m_dwMFTInputId, m_pInputMediaType.Get(), 0), done); // Find a matching output mediatype that has the same major/Subtype as pOutputType - DMFTCHECKHR_GOTO(m_pOutputMediaType->GetMajorType(&guidMajorType), done); - DMFTCHECKHR_GOTO(m_pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &guidSubtype), done); + while (SUCCEEDED(pTransform->MFTGetOutputAvailableType(m_dwMFTOutputId, dwMediaTypeIndex, &spMediaType))) { GUID guidMajorType2; @@ -1061,6 +1109,18 @@ HRESULT CDecoderTee::ProcessOutputSync(_COM_Outptr_opt_ IMFSample** ppSample) return hr; } +VOID CDecoderTee::ShutdownTee() +{ + CAutoLock lock(m_Lock); + SetAsyncStatus(MF_E_SHUTDOWN); + if (m_asyncCallback.Get()) + { + // Break the circular reference between the + // internal async callback and the decoder + m_asyncCallback->Shutdown(); + } +} + #ifdef MF_DEVICEMFT_ADD_GRAYSCALER_ CGrayTee::CGrayTee(_In_ Ctee *tee) : CWrapTee(tee),m_transformfn(nullptr) { @@ -1174,6 +1234,122 @@ done: } #endif +// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@// +// @@@@ README: Sample copy related functions below +// +// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@// + +CSampleCopytee::CSampleCopytee(_In_ Ctee *tee, GUID category + , IMFVideoSampleAllocator* sampleAllocator +) : + CVideoProcTee(tee, category + , sampleAllocator + ) +{ + +} + +CSampleCopytee::~CSampleCopytee() +{ + (VOID)StopStreaming(); + m_spDeviceManagerUnk = nullptr; +} + +HRESULT CSampleCopytee::StartStreaming() +{ + CAutoLock lock(m_Lock); + RETURN_IF_FAILED(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0)); + RETURN_IF_FAILED(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0)); + + return S_OK; +} + +HRESULT CSampleCopytee::StopStreaming() +{ + CAutoLock lock(m_Lock); + SetAsyncStatus(MF_E_SHUTDOWN); + RETURN_IF_FAILED(Transform()->MFTProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0)); // Flush the stream + RETURN_IF_FAILED(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0)); // Notify end of stream + RETURN_IF_FAILED(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0)); // Notify end of streaming + + return S_OK; +} + +STDMETHODIMP CSampleCopytee::Do(_In_ IMFSample *pSample, _Outptr_ IMFSample** ppOutSample, _Inout_ bool &newSample) +{ + + HRESULT hr = S_OK; + MFT_OUTPUT_DATA_BUFFER outputSample; + ComPtr <IMFSample> spXVPOutputSample; + DWORD dwStatus = 0; + CAutoLock lock(m_Lock); + + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Processing Sample =%p", pSample); + RETURN_HR_IF_NULL(E_INVALIDARG, ppOutSample); + + *ppOutSample = nullptr; + RETURN_IF_FAILED(GetAsyncStatus()); + RETURN_IF_FAILED(Transform()->MFTProcessInput(0, pSample, 0)); + + outputSample.dwStreamID = 0; + outputSample.pSample = NULL; +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + if (m_spDefaultAllocator) + { + RETURN_IF_FAILED(m_spDefaultAllocator->AllocateSample(&outputSample.pSample)); + } + else +#endif // ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + { + RETURN_IF_FAILED(m_spPrivateAllocator->AllocateSample(&outputSample.pSample)); + } + + + hr = Transform()->MFTProcessOutput(0, 1, &outputSample, &dwStatus); + + if (SUCCEEDED(hr) && pSample) + { + spXVPOutputSample.Attach(outputSample.pSample); + } + else + { + SAFE_RELEASE(outputSample.pSample); + } + + if (spXVPOutputSample.Get()) + { + newSample = true; + *ppOutSample = spXVPOutputSample.Detach(); + } + + return hr; +} + +STDMETHODIMP CSampleCopytee::Configure( + _In_opt_ IMFMediaType* inMediaType, + _In_opt_ IMFMediaType* outMediaType, + _Outptr_ IMFTransform **ppTransform +) +{ + RETURN_HR_IF_NULL(E_INVALIDARG, ppTransform); + + CMediaTypePrinter inType(inMediaType); + CMediaTypePrinter outType(outMediaType); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Input MediaType %s", inType.ToString()); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Output MediaType %s", outType.ToString()); + + RETURN_IF_FAILED(MFCreateSampleCopierMFT(ppTransform)); + RETURN_IF_FAILED(CreateAllocator()); + + if (m_fSetD3DManager) + { + RETURN_IF_FAILED((*ppTransform)->MFTProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<UINT_PTR>(m_spDeviceManagerUnk.Get()))); + } + + return S_OK; +} + + /*++ Descrtiption: Handles events sent by the pipeline @@ -1256,7 +1432,7 @@ HRESULT CDMFTEventHandler::KSEvent( for (; it != m_RegularEventList.end(); it++) { PDMFTEventEntry pEntry = *it; - DMFTCHECKNULL_GOTO(pEntry, done, E_FAIL); + DMFTCHECKNULL_GOTO(pEntry, done, E_UNEXPECTED); if (pEntry->m_pEventData == pEventData) { break; @@ -1547,7 +1723,11 @@ HRESULT CPinCreationFactory::CreatePin(_In_ ULONG ulInputStreamId, /* The Input #if defined MF_DEVICEMFT_DECODING_MEDIATYPE_NEEDED spOutPin = new (std::nothrow) CTranslateOutPin(ulOutStreamId, m_spDeviceTransform.Get(), spKscontrol.Get()); // Create the output pin #else - spOutPin = new (std::nothrow) COutPin(ulOutStreamId, m_spDeviceTransform.Get(), spKscontrol.Get()); // Create the output pin + spOutPin = new (std::nothrow) COutPin(ulOutStreamId, m_spDeviceTransform.Get(), spKscontrol.Get() +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + , MFSampleAllocatorUsage_DoesNotAllocate +#endif + ); // Create the output pin #endif DMFTCHECKNULL_GOTO(spOutPin.Get(), done, E_OUTOFMEMORY); @@ -1621,14 +1801,10 @@ done: return hr; } -HRESULT CheckImagePin( _In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsImagePin ) +BOOL CheckImagePin( _In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsImagePin ) { - if ((SUCCEEDED(CheckPinType(pAttributes, PINNAME_IMAGE, pbIsImagePin)) && pbIsImagePin) || - (SUCCEEDED(CheckPinType(pAttributes, PINNAME_VIDEO_STILL, pbIsImagePin)) && pbIsImagePin)) - { - return S_OK; - } - return E_FAIL; + return ((SUCCEEDED(CheckPinType(pAttributes, PINNAME_IMAGE, pbIsImagePin)) && pbIsImagePin) || + (SUCCEEDED(CheckPinType(pAttributes, PINNAME_VIDEO_STILL, pbIsImagePin)) && pbIsImagePin)); } HRESULT CheckPreviewPin( _In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsPreviewPin) diff --git a/avstream/sampledevicemft/multipinmfthelpers.h b/avstream/sampledevicemft/multipinmfthelpers.h index bd3f0876..681ca071 100644 --- a/avstream/sampledevicemft/multipinmfthelpers.h +++ b/avstream/sampledevicemft/multipinmfthelpers.h @@ -32,9 +32,9 @@ template< typename T , HRESULT ( __stdcall T::*Func) ( IMFAsyncResult* ) > class CDMFTAsyncCallback : public IMFAsyncCallback { public: - CDMFTAsyncCallback( T* parent ) : - m_cRef(1), - m_Parent(parent) + CDMFTAsyncCallback( T* parent , DWORD dwWorkQueueId = MFASYNC_CALLBACK_QUEUE_STANDARD) : + m_Parent(parent), + m_dwQueueId(dwWorkQueueId) { } virtual ~CDMFTAsyncCallback() { } @@ -77,25 +77,40 @@ public: STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue) { - // Implementation of this method is optional. - UNREFERENCED_PARAMETER(pdwFlags); - UNREFERENCED_PARAMETER(pdwQueue); + *pdwFlags = 0; + *pdwQueue = m_dwQueueId; return E_NOTIMPL; } STDMETHODIMP Invoke( IMFAsyncResult* pAsyncResult ) { - return (m_Parent->*Func)(pAsyncResult); + ComPtr<T> spParent; + { + // Take a reference on the parent so that + // shutdown may not yank it from us + CAutoLock Lock(&m_Lock); + spParent = m_Parent; + } + if (spParent.Get()) + { + return ((spParent.Get())->*Func)(pAsyncResult); + } + return MF_E_SHUTDOWN; + } + VOID Shutdown() + { + CAutoLock Lock(&m_Lock); + m_Parent = nullptr; //Break the reference } - // TODO: Implement this method. - T GetParent() { return m_Parent; } protected: - T* m_Parent; // Weak reference to the parent - long m_cRef; + CCritSec m_Lock; + ComPtr<T> m_Parent; + long m_cRef = 0; + DWORD m_dwQueueId = MFASYNC_CALLBACK_QUEUE_STANDARD; }; @@ -107,7 +122,18 @@ public: STDMETHODIMP_(VOID) InsertInternal ( _In_ IMFSample *pSample = nullptr ); STDMETHODIMP Insert ( _In_ IMFSample *pSample ); STDMETHODIMP Remove (_Outptr_result_maybenull_ IMFSample **pSample); - virtual STDMETHODIMP RecreateTee ( _In_ IMFMediaType *inMediatype, _In_ IMFMediaType *outMediatype, _In_opt_ IUnknown* punkManager ); + virtual STDMETHODIMP RecreateTee ( + _In_ IMFMediaType *inMediatype, + _In_ IMFMediaType *outMediatype, + _In_opt_ IUnknown* punkManager); +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + STDMETHODIMP RecreateTeeByAllocatorMode( + _In_ IMFMediaType* inMediatype, + _In_ IMFMediaType* outMediatype, + _In_opt_ IUnknown* punkManager, + _In_ MFSampleAllocatorUsage allocatorUsage, + _In_opt_ IMFVideoSampleAllocator* pAllcoator); +#endif STDMETHODIMP_(VOID) Clear(); // @@ -179,7 +205,7 @@ private: GUID m_streamCategory; ULONG m_cRef; protected: - Ctee* m_teer; /*Tee that acts as a passthrough or an XVP */ + ComPtr<Ctee> m_spTeer; /*Tee that acts as a passthrough or an XVP */ }; @@ -197,8 +223,20 @@ public: // The below classes are used to add the // XVP and the Decoder components. // -class Ctee{ +class Ctee: public IUnknown{ public: + // This is a helper class to release the interface + // It will first call shutdowntee to break any circular + // references any components might have with their composed + // objects + static VOID ReleaseTee( _In_ ComPtr<Ctee> &tee) + { + if (tee) + { + tee->ShutdownTee(); + tee = nullptr; + } + } STDMETHOD(Start)() { return S_OK; @@ -208,35 +246,85 @@ public: return S_OK; } virtual STDMETHODIMP PassThrough( _In_ IMFSample * ) = 0; + + STDMETHOD_(VOID, ShutdownTee)() + { + return; // NOOP + } + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) + { + HRESULT hr = S_OK; + if (ppv != nullptr) + { + *ppv = nullptr; + if (riid == __uuidof(IUnknown)) + { + AddRef(); + *ppv = static_cast<IUnknown*>(this); + } + else + { + hr = E_NOINTERFACE; + } + } + else + { + hr = E_POINTER; + } + return hr; + } + Ctee() + { + } + virtual ~Ctee() + {} + STDMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&m_cRef); + } + STDMETHODIMP_(ULONG) Release() + { + long cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + delete this; + } + return cRef; + } + +protected: + ULONG m_cRef = 0; }; class CNullTee:public Ctee{ public: - CNullTee(_In_ CPinQueue* q) :m_Queue(q) {} + CNullTee(_In_ CPinQueue* q) + : m_Queue(q) + { + } STDMETHODIMP PassThrough( _In_ IMFSample* ); + protected: // Store the queue here for simplicity ComPtr<CPinQueue> m_Queue; }; -class CWrapTee : public Ctee{ +class CWrapTee : public Ctee +{ public: CWrapTee( _In_ Ctee *tee=nullptr ) -: m_objectWrapped(tee) + : m_spObjectWrapped(tee) , m_pInputMediaType(nullptr) , m_pOutputMediaType(nullptr) { } - virtual ~CWrapTee()=0 + virtual ~CWrapTee() { - if (m_objectWrapped) - { - delete(m_objectWrapped); - } } + STDMETHODIMP PassThrough ( _In_ IMFSample* ); virtual STDMETHODIMP Do ( _In_ IMFSample* pSample, _Out_ IMFSample ** , _Inout_ bool &newSample) = 0; STDMETHODIMP SetMediaTypes(_In_ IMFMediaType* pInMediaType, _In_ IMFMediaType* pOutMediaType); @@ -262,7 +350,7 @@ protected: ComPtr< IMFMediaType > m_pInputMediaType; ComPtr< IMFMediaType > m_pOutputMediaType; - Ctee *m_objectWrapped; + ComPtr<Ctee> m_spObjectWrapped; }; // @@ -272,20 +360,26 @@ class CVideoProcTee: public CWrapTee { public: - CVideoProcTee( _In_ Ctee* p, _In_ GUID category = PINNAME_PREVIEW ) :CWrapTee(p) + CVideoProcTee( _In_ Ctee* p, _In_ GUID category = PINNAME_PREVIEW + , _In_ IMFVideoSampleAllocator* sampleAllocator=nullptr + ) + :CWrapTee(p) , m_bProducesSamples(FALSE) + , m_asyncHresult(S_OK) , m_streamCategory(category) , m_fSetD3DManager(FALSE) + , m_spDefaultAllocator(sampleAllocator) {} __inline IMFTransform* Transform() { - return m_videoProcessor.Get(); + return m_spVideoProcessor.Get(); } VOID SetD3DManager( _In_opt_ IUnknown* pUnk ) { - m_spDeviceManagerUnk = pUnk; + m_spDeviceManagerUnk = pUnk; } + STDMETHODIMP SetMediaTypes(_In_ IMFMediaType* pInMediaType, _In_ IMFMediaType* pOutMediaType); virtual STDMETHODIMP Configure(_In_ IMFMediaType *, _In_ IMFMediaType *, _Inout_ IMFTransform**) = 0; STDMETHOD(CreateAllocator)(); @@ -294,19 +388,33 @@ public: HRESULT hr = S_OK; if (SUCCEEDED(hr = StopStreaming())) { - if (m_objectWrapped) + if (m_spObjectWrapped) { - hr = m_objectWrapped->Stop(); + hr = m_spObjectWrapped->Stop(); } } return hr; } + virtual ~CVideoProcTee() + {} protected: + CCritSec m_Lock; + __inline VOID SetAsyncStatus(_In_ HRESULT hrStatus) + { + InterlockedCompareExchange(&m_asyncHresult, hrStatus, S_OK); + } + HRESULT GetAsyncStatus() + { + return InterlockedCompareExchange(&m_asyncHresult, S_OK, S_OK); + } STDMETHOD(StartStreaming)() = 0; STDMETHOD(StopStreaming)() = 0; - ComPtr< IMFTransform > m_videoProcessor; + HRESULT m_asyncHresult; + ComPtr< IMFTransform > m_spVideoProcessor; ComPtr<IUnknown> m_spDeviceManagerUnk; - ComPtr<IMFVideoSampleAllocatorEx> m_spAllocator; + ComPtr<IMFVideoSampleAllocatorEx> m_spPrivateAllocator; + ComPtr<IMFVideoSampleAllocator> m_spDefaultAllocator; + BOOL m_bProducesSamples; GUID m_streamCategory; BOOL m_fSetD3DManager; @@ -315,11 +423,12 @@ protected: class CXvptee :public CVideoProcTee{ public: CXvptee( _In_ Ctee *, _In_ GUID category = PINNAME_PREVIEW ); - ~CXvptee(); + virtual ~CXvptee(); STDMETHOD(StartStreaming)(); STDMETHOD(StopStreaming)(); STDMETHODIMP Do ( _In_ IMFSample* pSample, _Outptr_ IMFSample **, _Inout_ bool &newSample); STDMETHODIMP Configure ( _In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform** ); + }; class CDecoderTee : public CVideoProcTee { @@ -338,23 +447,11 @@ public: , m_dwMFTInputId(0) , m_dwMFTOutputId(0) , m_dwQueueId(dwQueueId) - ,m_dwCameraStreamWorkQueueId(0) - , m_ulProcessOutputsInFlight(0) - , m_hSyncHandle(INVALID_HANDLE_VALUE) - , m_StoppedAndWaiting(FALSE) + , m_dwCameraStreamWorkQueueId(0) { m_streamCategory = category; } - ~CDecoderTee(); - - __inline VOID SetAsyncStatus( _In_ HRESULT hrStatus ) - { - InterlockedCompareExchange(&m_asyncHresult, hrStatus, S_OK); - } - HRESULT GetAsyncStatus() - { - return InterlockedCompareExchange(&m_asyncHresult, S_OK, S_OK); - } + virtual ~CDecoderTee(); STDMETHODIMP Do(_In_ IMFSample* pSample, _Outptr_ IMFSample **, _Inout_ bool &newSample); STDMETHODIMP Configure(_In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform**); @@ -362,16 +459,17 @@ public: protected: STDMETHODIMP StartStreaming(); STDMETHODIMP StopStreaming(); - _Ret_maybenull_ HRESULT CDecoderTee::GetSample( _Outptr_ IMFSample**); + HRESULT GetSample( _Outptr_result_maybenull_ IMFSample**); HRESULT ConfigDecoder( _In_ IMFTransform* ,_In_ GUID guidSubType = GUID_NULL); HRESULT ConfigRealTimeMFT(_In_ IMFTransform* ); HRESULT ProcessOutputSync( _COM_Outptr_opt_ IMFSample** ); HRESULT ProcessFormatChange(); + STDMETHOD_(VOID, ShutdownTee)(); BOOL m_fAsyncMFT; BOOL m_D3daware; BOOL m_hwMFT; - ComPtr<CDMFTAsyncCallback<CDecoderTee,&CDecoderTee::Invoke> > m_asyncCallback; + ComPtr<CDMFTAsyncCallback<CDecoderTee, &CDecoderTee::Invoke> > m_asyncCallback; HRESULT m_asyncHresult; DWORD m_lNeedInputRequest; GUID m_streamCategory; // Needed for bind flags @@ -381,14 +479,22 @@ protected: ComPtr<IMFSample> m_spUnprocessedSample; DWORD m_dwQueueId; DWORD m_dwCameraStreamWorkQueueId; - CCritSec m_critSec; std::deque<ComPtr<IMFSample> > m_InputSampleList; - ULONG m_ulProcessOutputsInFlight; - HANDLE m_hSyncHandle; - BOOL m_StoppedAndWaiting; }; +class CSampleCopytee :public CVideoProcTee { +public: + CSampleCopytee(_In_ Ctee*, _In_ GUID category = PINNAME_PREVIEW + , _In_ IMFVideoSampleAllocator* sampleAllocator = nullptr + ); + ~CSampleCopytee(); + STDMETHOD(StartStreaming)(); + STDMETHOD(StopStreaming)(); + STDMETHODIMP Do(_In_ IMFSample* pSample, _Outptr_ IMFSample **, _Inout_ bool &newSample); + STDMETHODIMP Configure(_In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform**); +}; + #ifdef MF_DEVICEMFT_ADD_GRAYSCALER_ class CGrayTee : public CWrapTee { public: @@ -499,6 +605,7 @@ public: DMFT_PIN_INPUT, DMFT_PIN_OUTPUT, DMFT_PIN_CUSTOM, + DMFT_PIN_ALLOCATOR_PIN, DMFT_MAX }type_pin; HRESULT CreatePin( _In_ ULONG ulInputStreamId, _In_ ULONG ulOutStreamId, _In_ type_pin type,_Outptr_ CBasePin** ppPin, _In_ BOOL& isCustom); @@ -506,7 +613,7 @@ public: } }; HRESULT CheckPinType(_In_ IMFAttributes* pAttributes, _In_ GUID pinType, _Out_ PBOOL pbIsImagePin); -HRESULT CheckImagePin(_In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsImagePin); +BOOL CheckImagePin(_In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsImagePin); HRESULT CheckPreviewPin(_In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsPreviewPin); diff --git a/avstream/sampledevicemft/multipinmftutils.cpp b/avstream/sampledevicemft/multipinmftutils.cpp index d056b364..e4a62d52 100644 --- a/avstream/sampledevicemft/multipinmftutils.cpp +++ b/avstream/sampledevicemft/multipinmftutils.cpp @@ -1301,6 +1301,83 @@ done: const UINT32 ALLOCATOR_MIN_SAMPLES = 10; const UINT32 ALLOCATOR_MAX_SAMPLES = 50; + +HRESULT ConfigureAllocator( + _In_ IMFMediaType* pOutputMediaType, + _In_ GUID streamCategory, + _In_ IUnknown* pDeviceManagerUnk, + _In_ BOOL &bDxAllocator, + _In_ IMFVideoSampleAllocator* pAllocator) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IMFVideoSampleAllocatorEx> spPrivateAllocator; +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + wil::com_ptr_nothrow<IMFVideoCaptureSampleAllocator> spDefaultAllocator; +#endif + wil::com_ptr_nothrow<IMFAttributes> spAllocatorAttributes; + GUID guidMajorType = GUID_NULL; + GUID guidSubtype = GUID_NULL; + BOOL fDXAllocator = FALSE; + + RETURN_HR_IF_NULL(E_INVALIDARG, pAllocator ); + RETURN_HR_IF_NULL(E_INVALIDARG, pOutputMediaType ); + + RETURN_IF_FAILED(pOutputMediaType->GetMajorType(&guidMajorType)); + + if (!IsEqualGUID(guidMajorType, MFMediaType_Video)) + { + RETURN_HR(MF_E_INVALIDMEDIATYPE); + } + + RETURN_IF_FAILED(pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &guidSubtype)); + // + // Set Attributes on the allocator we need. First get the Bind Flags. + // + RETURN_IF_FAILED(MFCreateAttributes(&spAllocatorAttributes, 8)); + RETURN_IF_FAILED(spAllocatorAttributes->SetUINT32(MF_SA_BUFFERS_PER_SAMPLE, 1)); + + if (pDeviceManagerUnk != nullptr) + { + if (SUCCEEDED(UpdateAllocatorAttributes(spAllocatorAttributes.get(), streamCategory, guidSubtype, (IMFDXGIDeviceManager*)pDeviceManagerUnk))) + { + fDXAllocator = TRUE; + } + } + + if (fDXAllocator) + { + RETURN_IF_FAILED(pAllocator->SetDirectXManager(pDeviceManagerUnk)); + } + + if (SUCCEEDED(pAllocator->QueryInterface(IID_PPV_ARGS(&spPrivateAllocator)))) + { + RETURN_IF_FAILED(spPrivateAllocator->InitializeSampleAllocatorEx( + ALLOCATOR_MIN_SAMPLES, + ALLOCATOR_MAX_SAMPLES, + spAllocatorAttributes.get(), + pOutputMediaType)); + } +#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB)) + else if (SUCCEEDED(pAllocator->QueryInterface(IID_PPV_ARGS(&spDefaultAllocator)))) + { + RETURN_IF_FAILED(spDefaultAllocator->InitializeCaptureSampleAllocator( + 0, /*use sample size by MediaType*/ + 0, /*metadata size*/ + 0, /*default alignment*/ + ALLOCATOR_MIN_SAMPLES, + spAllocatorAttributes.get(), + pOutputMediaType)); + } +#endif + else + { + hr = E_INVALIDARG; + } + + bDxAllocator = fDXAllocator; + + return hr; +} // //@@@@ README: Creating an Allocator.. Please don't allocate samples individually using MFCreateSample as that can lead to fragmentation and is // extremely inefficent. Instead create an Allocator which will create a fixed number of samples which are recycled when the pipeline returns back @@ -1337,7 +1414,7 @@ HRESULT CreateAllocator( _In_ IMFMediaType* pOutputMediaType, DMFTCHECKHR_GOTO(MFCreateAttributes(&spAllocatorAttributes, 8), done); DMFTCHECKHR_GOTO(spAllocatorAttributes->SetUINT32(MF_SA_BUFFERS_PER_SAMPLE, 1), done); - if (pDeviceManagerUnk != nullptr) + if (pDeviceManagerUnk != nullptr) { if (SUCCEEDED(UpdateAllocatorAttributes(spAllocatorAttributes.Get(), streamCategory,guidSubtype, (IMFDXGIDeviceManager*)pDeviceManagerUnk))) { @@ -1349,6 +1426,7 @@ HRESULT CreateAllocator( _In_ IMFMediaType* pOutputMediaType, { DMFTCHECKHR_GOTO(spVideoSampleAllocator->SetDirectXManager(pDeviceManagerUnk), done); } + DMFTCHECKHR_GOTO(spVideoSampleAllocator->InitializeSampleAllocatorEx(ALLOCATOR_MIN_SAMPLES, ALLOCATOR_MAX_SAMPLES, spAllocatorAttributes.Get(), pOutputMediaType), done); *ppAllocator = spVideoSampleAllocator.Detach(); @@ -1487,16 +1565,16 @@ done: HRESULT MergeSampleAttributes( _In_ IMFSample* pInSample, _Inout_ IMFSample* pOutSample) { HRESULT hr = S_OK; - DMFTCHECKNULL_GOTO(pInSample, done, E_INVALIDARG); - DMFTCHECKNULL_GOTO(pOutSample, done, E_INVALIDARG); UINT32 cAttributes = 0; GUID guidAttribute; PROPVARIANT varAttribute; PROPVARIANT varAttributeExists; - PropVariantInit(&varAttribute); PropVariantInit(&varAttributeExists); + DMFTCHECKNULL_GOTO(pInSample, done, E_INVALIDARG); + DMFTCHECKNULL_GOTO(pOutSample, done, E_INVALIDARG); + DMFTCHECKHR_GOTO(pInSample->GetCount(&cAttributes), done); for (UINT32 i = 0; i < cAttributes; i++) { diff --git a/avstream/sampledevicemft/stdafx.h b/avstream/sampledevicemft/stdafx.h index e2f5a6cd..657208cd 100644 --- a/avstream/sampledevicemft/stdafx.h +++ b/avstream/sampledevicemft/stdafx.h @@ -41,6 +41,11 @@ using namespace std; using namespace ABI::Windows::Foundation; using namespace Microsoft::WRL; +#include <wil\result.h> +#include <wil\resource.h> +#include <wil\com.h> +#include <wil\result_macros.h> + // @@@@ README Please check / uncheck various hash defines to enable/ disable features // MF_DEVICEMFT_ASYNCPIN_NEEDED will show how to use Asynchronous queues // MF_DEVICEMFT_DECODING_MEDIATYPE_NEEDED will show how to decode a compressed mediatype.. it supports H264 and MJPG @@ -49,7 +54,7 @@ using namespace Microsoft::WRL; // //#define MF_DEVICEMFT_ADD_GRAYSCALER_ 1 #define MF_DEVICEMFT_ASYNCPIN_NEEDED 1 -#define MF_DEVICEMFT_DECODING_MEDIATYPE_NEEDED 1 +//#define MF_DEVICEMFT_DECODING_MEDIATYPE_NEEDED 0 #define MF_DEVICEMFT_SET_SPHERICAL_ATTRIBUTES 1 //#define MF_DEVICEMFT_ENUM_HW_DECODERS 1 diff --git a/avstream/samplemft0/README.md b/avstream/samplemft0/README.md index 4ed8adbf..42f23db4 100644 --- a/avstream/samplemft0/README.md +++ b/avstream/samplemft0/README.md @@ -1,39 +1,37 @@ ---- -page_type: sample -description: "A driver MFT for use with a camera's Windows device app." -languages: -- cpp -products: -- windows -- windows-wdk ---- +<!--- + name: Driver MFT Sample + platform: WDM + language: cpp + category: Camera + description: A driver MFT for use with a camera's Windows Store device app. + samplefwlink: http://go.microsoft.com/fwlink/p/?LinkId=617126 +---> -# Driver MFT Sample +Driver MFT Sample +================= -Provides a *driver MFT* for use with a camera's UWP device app.A *driver MFT* is a Media Foundation Transform that's used with a specific camera when capturing video. The driver MFT is also known as MFT0 because it is the first MFT applied to the video stream captured from the camera. This MFT can provide a video effect or other processing when capturing photos or video from the camera. It can be distributed along with the driver package for a camera. +Provides a *driver MFT* for use with a camera's Windows Store device app.A *driver MFT* is a Media Foundation Transform that's used with a specific camera when capturing video. The driver MFT is also known as MFT0 because it is the first MFT applied to the video stream captured from the camera. This MFT can provide a video effect or other processing when capturing photos or video from the camera. It can be distributed along with the driver package for a camera. -In this sample, the driver MFT, when enabled, replaces a portion of the captured video with a green box. To test this sample, download the [UWP device app for camera sample](https://go.microsoft.com/fwlink/p/?linkid=249442) and the [Camera Capture UI sample](https://go.microsoft.com/fwlink/p/?linkid=249441). - -The **UWP device app for camera sample** provides a *UWP device app* that controls the effect implemented by the driver MFT. - -The **Camera Capture UI sample** provides a way to invoke the **UWP device app**. +In this sample, the driver MFT, when enabled, replaces a portion of the captured video with a green box. To test this sample, download the [Windows Store device app for camera sample](http://go.microsoft.com/fwlink/p/?linkid=249442) and the [Camera Capture UI sample](http://go.microsoft.com/fwlink/p/?linkid=249441). The [Windows Store device app for camera sample](http://go.microsoft.com/fwlink/p/?linkid=249442) provides a *Windows Store device app* that controls the effect implemented by the driver MFT. The [Camera Capture UI sample](http://go.microsoft.com/fwlink/p/?linkid=249441) provides a way to invoke the *Windows Store device app*. This sample is designed to be used with a specific camera. To run the sample, you need the your camera's device ID and device metadata package. -## Related topics -### Concepts +Related topics +-------------- + +**Concepts** -[UWP device apps for cameras](https://docs.microsoft.com/windows-hardware/drivers/devapps/uwp-device-apps-for-webcams) +[Windows Store device apps for cameras](http://go.microsoft.com/fwlink/p/?LinkId=306683) -[Media Foundation Transforms](https://docs.microsoft.com/windows/win32/medfound/media-foundation-transforms) +[Media Foundation Transforms](http://msdn.microsoft.com/en-us/library/windows/hardware/ms703138) -[Streaming media device driver design guide](https://docs.microsoft.com/windows-hardware/drivers/stream) +[Roadmap for Developing Streaming Media Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff568130) -[Universal camera driver design guide for Windows 10](https://docs.microsoft.com/windows-hardware/drivers/stream/windows-10-technical-preview-camera-drivers-design-guide) +[Universal camera driver design guide for Windows 10](https://msdn.microsoft.com/en-us/Library/Windows/Hardware/dn937080) -### Samples +**Samples** -[Device app for camera sample](https://go.microsoft.com/fwlink/p/?linkid=249442) +[Windows Store device app for camera sample](http://go.microsoft.com/fwlink/p/?linkid=249442) -[Camera Capture UI sample](https://go.microsoft.com/fwlink/p/?linkid=249441%20) +[Camera Capture UI sample](http://go.microsoft.com/fwlink/p/?linkid=249441%20) |
