diff options
| author | karlf <[email protected]> | 2016-08-11 13:28:13 -0700 |
|---|---|---|
| committer | karlf <[email protected]> | 2016-08-11 13:28:13 -0700 |
| commit | 96eb96dfb613e4c745db6bd1f53a92fe7e2290fc (patch) | |
| tree | ad5f3ede5cbcd6b598677ce41bcf8318471bdd92 /AVStream/avscamera/sys | |
| parent | 687b274aa38fd05c8c26e3068932121876d7f745 (diff) | |
Updated for "Windows 10 Anniversary Update" (Version 1607)
Diffstat (limited to 'AVStream/avscamera/sys')
71 files changed, 27944 insertions, 0 deletions
diff --git a/AVStream/avscamera/sys/AvsCamera.cpp b/AVStream/avscamera/sys/AvsCamera.cpp new file mode 100644 index 00000000..3742e69d --- /dev/null +++ b/AVStream/avscamera/sys/AvsCamera.cpp @@ -0,0 +1,108 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + AvsCamera.cpp + + Abstract: + + Sample Camera driver initialization. + + History: + + created 5/15/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +// +// CaptureDeviceDispatch: +// +// This is the dispatch table for the capture device. Plug and play +// notifications as well as power management notifications are dispatched +// through this table. +// +DEFINE_CAMERA_KSDEVICE_DISPATCH( AvsCameraDispatch, CAvsCameraDevice ); + +// +// CaptureDeviceDescriptor: +// +// This is the device descriptor for the capture device. It points to the +// dispatch table and contains a list of filter descriptors that describe +// filter-types that this device supports. Note that the filter-descriptors +// can be created dynamically and the factories created via +// KsCreateFilterFactory as well. +// +const +KSDEVICE_DESCRIPTOR +AvsCameraDeviceDescriptor = +{ + &AvsCameraDispatch, + 0, + NULL +}; + +/************************************************************************** + + INITIALIZATION CODE + +**************************************************************************/ + + +extern "C" DRIVER_INITIALIZE DriverEntry; + +extern "C" +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath +) + +/*++ + +Routine Description: + + Driver entry point. Pass off control to the AVStream initialization + function (KsInitializeDriver) and return the status code from it. + +Arguments: + + DriverObject - + The WDM driver object for our driver + + RegistryPath - + The registry path for our registry info + +Return Value: + + As from KsInitializeDriver + +--*/ + +{ + // + // Simply pass the device descriptor and parameters off to AVStream + // to initialize us. This will cause filter factories to be set up + // at add & start. Everything is done based on the descriptors passed + // here. + // + return + KsInitializeDriver ( + DriverObject, + RegistryPath, + &AvsCameraDeviceDescriptor + ); + +} + diff --git a/AVStream/avscamera/sys/AvsCamera.rc b/AVStream/avscamera/sys/AvsCamera.rc new file mode 100644 index 00000000..25131b0a --- /dev/null +++ b/AVStream/avscamera/sys/AvsCamera.rc @@ -0,0 +1,21 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 2015 +// +// File: AvsCam.rc +// +//-------------------------------------------------------------------------- + +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "A/V Stream Camera Simulator" +#define VER_INTERNALNAME_STR "AvsCam.sys" +#define VER_ORIGINALFILENAME_STR "AvsCam.sys" + +#include "common.ver" diff --git a/AVStream/avscamera/sys/AvsCamera.vcxproj b/AVStream/avscamera/sys/AvsCamera.vcxproj new file mode 100644 index 00000000..d46c7277 --- /dev/null +++ b/AVStream/avscamera/sys/AvsCamera.vcxproj @@ -0,0 +1,247 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{24D35D6E-BDB0-4949-8A34-7DD25F0E8F68}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <SupportsPackaging>false</SupportsPackaging> + <RequiresPackageProject>true</RequiresPackageProject> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{7F43971C-9E5A-46A0-B925-AFE3282E014C}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>AvsCamera</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>AvsCamera</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>AvsCamera</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>AvsCamera</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <NTDDI_VERSION>NTDDI_WINTHRESHOLD</NTDDI_VERSION> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <NTDDI_VERSION>NTDDI_WINTHRESHOLD</NTDDI_VERSION> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <NTDDI_VERSION>NTDDI_WINTHRESHOLD</NTDDI_VERSION> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <NTDDI_VERSION>NTDDI_WINTHRESHOLD</NTDDI_VERSION> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="AvsCamera.cpp" /> + <ClCompile Include="AvsCameraDevice.cpp" /> + <ClCompile Include="AvsCameraFilter.cpp" /> + <ClCompile Include="capture.cpp" /> + <ClCompile Include="device.cpp" /> + <ClCompile Include="filter.cpp" /> + <ClCompile Include="formats.cpp" /> + <ClCompile Include="hwsim.cpp" /> + <ClCompile Include="imagecapture.cpp" /> + <ClCompile Include="ImagehwSim.cpp" /> + <ClCompile Include="Mutex.cpp" /> + <ClCompile Include="NV12Synthesizer.cpp" /> + <ClCompile Include="PreviewHwSim.cpp" /> + <ClCompile Include="RGB24Synthesizer.cpp" /> + <ClCompile Include="Roi.cpp" /> + <ClCompile Include="Sensor.cpp" /> + <ClCompile Include="SensorSimulation.cpp" /> + <ClCompile Include="Synthesizer.cpp" /> + <ClCompile Include="Timer.cpp" /> + <ClCompile Include="util.cpp" /> + <ClCompile Include="videocapture.cpp" /> + <ClCompile Include="VideoHwSim.cpp" /> + <ClCompile Include="Waitable.cpp" /> + <ClCompile Include="WorkItem.cpp" /> + <ClCompile Include="XRGBSynthesizer.cpp" /> + <ClCompile Include="YUVSynthesizer.cpp" /> + <ClCompile Include="YUY2Synthesizer.cpp" /> + <ResourceCompile Include="AvsCamera.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <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" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/AVStream/avscamera/sys/AvsCamera.vcxproj.Filters b/AVStream/avscamera/sys/AvsCamera.vcxproj.Filters new file mode 100644 index 00000000..8d9ec469 --- /dev/null +++ b/AVStream/avscamera/sys/AvsCamera.vcxproj.Filters @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{1E6E0D75-6064-4AA6-9EED-9795A14A9565}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{673D2600-D125-46B1-886A-679ED918CAE3}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{4E7C2C4D-E0C2-4719-8255-3A104085149B}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{39B99289-64B8-4EB7-808C-3AFE1DEF2048}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="AvsCamera.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="AvsCameraDevice.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="AvsCameraFilter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="capture.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="device.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="filter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="hwsim.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="imagecapture.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ImagehwSim.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Mutex.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="NV12Synthesizer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PreviewHwSim.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RGB24Synthesizer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Roi.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Sensor.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SensorSimulation.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Synthesizer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Timer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="util.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="videocapture.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="VideoHwSim.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Waitable.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WorkItem.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="XRGBSynthesizer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="YUVSynthesizer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="YUY2Synthesizer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="AvsCamera.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/AVStream/avscamera/sys/AvsCameraDevice.cpp b/AVStream/avscamera/sys/AvsCameraDevice.cpp new file mode 100644 index 00000000..8916e2ef --- /dev/null +++ b/AVStream/avscamera/sys/AvsCameraDevice.cpp @@ -0,0 +1,99 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraDevice.cpp + + Abstract: + + Device class implementation. Derived from CCaptureDevice. + This class overloads our base device class implementation. + + History: + + created 02/24/2015 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// +// DispatchCreate(): +// +// This is the Add Device dispatch for the capture device. It creates +// the CCaptureDevice and associates it with the device via the bag. +// +NTSTATUS +CAvsCameraDevice:: +DispatchCreate ( + _In_ PKSDEVICE Device +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + CAvsCameraDevice *CapDevice = new (NonPagedPoolNx, 'eviD') CAvsCameraDevice (Device); + if( !CapDevice) + { + // + // Return failure if we couldn't create the device. + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + NTSTATUS Status = CapDevice->Prepare(); + if (!NT_SUCCESS (Status)) + { + // Init failure. + delete CapDevice; + } + + DBG_LEAVE("()"); + return Status; +} + +NTSTATUS +CAvsCameraDevice:: +Initialize() +{ + PAGED_CODE(); + + static + CSensorContext Context[] = + { + { L"RFC", &AvsCameraFilterDescriptor, AcpiPldPanelBack, AcpiPldRotation0 }, + { L"FFC", &AvsCameraFilterDescriptorFFC, AcpiPldPanelFront, AcpiPldRotation90 } + }; + + m_FilterDescriptorCount = SIZEOF_ARRAY(Context); + m_Context = Context; + + return CCaptureDevice::Initialize(); +} + +CSensor * +CAvsCameraDevice:: +CreateSensor( + _In_ const KSFILTER_DESCRIPTOR *Descriptors +) +{ + PAGED_CODE(); + + return new (NonPagedPoolNx, 'sneS') CSensorSimulation( this, Descriptors ); +} + diff --git a/AVStream/avscamera/sys/AvsCameraDevice.h b/AVStream/avscamera/sys/AvsCameraDevice.h new file mode 100644 index 00000000..ec98e892 --- /dev/null +++ b/AVStream/avscamera/sys/AvsCameraDevice.h @@ -0,0 +1,73 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraDevice.h + + Abstract: + + Device class implementation. Derived from CCaptureDevice. + This class overloads our base device class implementation. + + History: + + created 02/24/2015 + +**************************************************************************/ + +#pragma once + +class CAvsCameraDevice : public CCaptureDevice +{ +public: + // + // DispatchCreate(): + // + // This is the Add Device dispatch for the capture device. It creates + // the CCaptureDevice and associates it with the device via the bag. + // + static + NTSTATUS + DispatchCreate ( + _In_ PKSDEVICE Device + ); + +public: + // + // CCaptureDevice(): + // + // The capture device class constructor. Since everything should have + // been zero'ed by the new operator, don't bother setting anything to + // zero or NULL. Only initialize non-NULL, non-0 fields. + // + CAvsCameraDevice( + _In_ PKSDEVICE Device + ) + : CCaptureDevice( Device ) + {} + + virtual + NTSTATUS + Initialize(); + +protected: + virtual + CSensor * + CreateSensor( + _In_ const KSFILTER_DESCRIPTOR *Descriptors + ); + +}; + +// {B27E3887-AD10-4A4E-BFB8-D6765ADD0E38} +#define STATIC_FrontCamera_Filter \ + 0xb27e3887, 0xad10, 0x4a4e, 0xbf, 0xb8, 0xd6, 0x76, 0x5a, 0xdd, 0xe, 0x38 + +// {4EE16166-F358-4F10-8889-93107806B7A7} +#define STATIC_RearCamera_Filter \ + 0x4ee16166, 0xf358, 0x4f10, 0x88, 0x89, 0x93, 0x10, 0x78, 0x6, 0xb7, 0xa7 + diff --git a/AVStream/avscamera/sys/AvsCameraFilter.cpp b/AVStream/avscamera/sys/AvsCameraFilter.cpp new file mode 100644 index 00000000..bff6466f --- /dev/null +++ b/AVStream/avscamera/sys/AvsCameraFilter.cpp @@ -0,0 +1,588 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraFilter.cpp + + Abstract: + + Filter class implementation. Derived from CCaptureFilter. + This class overrides the default implementation. + + History: + + created 02/24/2015 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CAvsCameraFilter:: +CAvsCameraFilter( + _In_ PKSFILTER Filter +) + : CCaptureFilter( Filter ) +{ + PAGED_CODE(); +} + +// +// ~CCaptureFilter(): +// +// The capture filter destructor. +// +CAvsCameraFilter:: +~CAvsCameraFilter() +{ + PAGED_CODE(); +} + + +NTSTATUS +CAvsCameraFilter:: +DispatchCreate ( + _In_ PKSFILTER Filter, + _In_ PIRP Irp +) + +/*++ + +Routine Description: + + This is the creation dispatch for the capture filter. It creates + the CCaptureFilter object. + +Arguments: + + Filter - + The AVStream filter being created + + Irp - + The creation Irp + +Return Value: + + Success / failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("(Filter=0x%p)", Filter); + + NTSTATUS Status = STATUS_SUCCESS; + + CAvsCameraFilter *CapFilter = new (NonPagedPoolNx, 'rtlf') CAvsCameraFilter(Filter); + + if (!CapFilter) + { + // + // Return failure if we couldn't create the filter. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + else + { + // Allocate memory or whatever. + Status = CapFilter->Initialize(); + + if (!NT_SUCCESS (Status)) + { + delete CapFilter; + } + else + { + Filter -> Context = reinterpret_cast <PVOID> (CapFilter); + } + } + + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +// Get KSPROPERTY_CUSTOMCONTROL_DUMMY. +NTSTATUS +CAvsCameraFilter:: +GetCustomDummy( + _Inout_ ULONG *pDummy +) +{ + PAGED_CODE(); + + *pDummy = m_CustomValue; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CUSTOMCONTROL_DUMMY. +NTSTATUS +CAvsCameraFilter:: +SetCustomDummy( + _In_ ULONG *pDummy +) +{ + PAGED_CODE(); + + m_CustomValue = *pDummy; + return STATUS_SUCCESS; +} + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +GUID g_PINNAME_VIDEO_CAPTURE = {STATIC_PINNAME_VIDEO_CAPTURE}; +GUID g_PINNAME_VIDEO_PREVIEW = {STATIC_PINNAME_VIDEO_PREVIEW}; +GUID g_PINNAME_IMAGE_CAPTURE = {STATIC_PINNAME_IMAGE}; + +// +// CaptureFilterCategories: +// +// The list of category GUIDs for the capture filter. +// +const +GUID +FilterCategories [CAPTURE_FILTER_CATEGORIES_COUNT] = +{ + STATICGUIDOF (KSCATEGORY_VIDEO_CAMERA), + STATICGUIDOF (KSCATEGORY_VIDEO), + STATICGUIDOF (KSCATEGORY_CAPTURE) +}; + +DEFINE_KSPROPERTY_TABLE(FocusPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ) +}; + +DEFINE_KSPROPERTY_TABLE(VideoStabPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, 0, KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ) +}; + +DEFINE_KSPROPERTY_TABLE(FlashPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID, KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ) +}; + +DEFINE_KSPROPERTY_TABLE(PinDependencePropertyItems) +{ + // This control only requires a getter. + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_PROPERTY_ID, KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, + PinDependence ) +}; + +DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE, CExtendedPhotoMode, PhotoMode ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE, CExtendedProperty, PhotoFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE, CExtendedProperty, PhotoMaxFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME, CExtendedProperty, TriggerTime ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART, CExtendedProperty, WarmStart ), + DEFINE_PROP_ITEM( 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_FLASHMODE, CExtendedProperty, ExtendedFlash ), + 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 ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE, CExtendedVidProcSetting, Focus ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO, CExtendedProperty, Iso ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW, CExtendedFieldOfView, FieldOfView ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION, CExtendedEvCompensation, EvCompensation ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_CAMERAANGLEOFFSET, CExtendedCameraAngleOffset, CameraAngleOffset ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION, CExtendedProperty, PhotoConfirmation ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA, CExtendedMetadata, Metadata ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSSTATE, CExtendedProperty, FocusState ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY, CExtendedProperty, FocusPriority ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION, CExtendedVidProcSetting, FaceDetection ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_CONFIGCAPS, CRoiConfig, RoiConfigCaps ), + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL, + CCaptureFilter::GetRoiIspControl, + sizeof(KSPROPERTY), + 0, + CCaptureFilter::SetRoiIspControl, + NULL, 0, NULL, NULL, 0 + ), + 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 ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION, CExtendedProperty, VideoStabilization), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM, CExtendedProperty, Histogram ), + 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 ) +}; + +// Front-facing cameras often have limited capabilities. One way to express that is to use a restricted automation table such as this one. +DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItemsFFC) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE, CExtendedPhotoMode, PhotoMode ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE, CExtendedProperty, PhotoFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE, CExtendedProperty, PhotoMaxFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME, CExtendedProperty, TriggerTime ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART, CExtendedProperty, WarmStart ), + 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_OPTIMIZATIONHINT, CExtendedProperty, OptimizationHint ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE, CExtendedVidProcSetting, WhiteBalance ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE, CExtendedVidProcSetting, Exposure ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO, CExtendedProperty, Iso ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW, CExtendedFieldOfView, FieldOfView ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION, CExtendedEvCompensation, EvCompensation ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_CAMERAANGLEOFFSET, CExtendedCameraAngleOffset, CameraAngleOffset ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION, CExtendedProperty, PhotoConfirmation ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA, CExtendedMetadata, Metadata ), + DEFINE_PROP_ITEM(CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM, CExtendedVidProcSetting, Zoom), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION, CExtendedVidProcSetting, FaceDetection ), + 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 ) +}; + +//-------------------------------------------------------------- +// Legacy Camera Contorl properties +DEFINE_KSPROPERTY_TABLE(CameraControlPropertyTable) +{ + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXPOSURE, KSPROPERTY_CAMERACONTROL_S, Exposure, &ExposureValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_FOCUS, KSPROPERTY_CAMERACONTROL_S, Focus, &FocusValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_ZOOM, KSPROPERTY_CAMERACONTROL_S, Zoom, &ZoomValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE, KSPROPERTY_CAMERACONTROL_S, ZoomRelative, &ZoomRelativeValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_PAN, KSPROPERTY_CAMERACONTROL_S, Pan, &PanValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_ROLL, KSPROPERTY_CAMERACONTROL_S, Roll, &RollValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_TILT, KSPROPERTY_CAMERACONTROL_S, Tilt, &TiltValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH, KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength, NULL) +}; + +DEFINE_KSPROPERTY_TABLE(PFSPropertyTable) +{ + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CAPABILITY, + CCaptureFilter::GetPFSCaps, // GetSupported or Handler + sizeof(KSPROPERTY), // MinProperty + 0, // MinData + NULL, // SetSupported or Handler + NULL, // Values + 0, // RelationsCount + NULL, // Relations + NULL, // SupportHandler + 0 // SerializedSize + ), + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_SET, + CCaptureFilter::GetPerFrameSettings, // GetSupported or Handler + sizeof(KSPROPERTY), // MinProperty + 0, // MinData + CCaptureFilter::SetPerFrameSettings, // SetSupported or Handler + NULL, // Values + 0, // RelationsCount + NULL, // Relations + NULL, // SupportHandler + 0 // SerializedSize + ), + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CLEAR, + NULL, // GetSupported or Handler + sizeof(KSPROPERTY), // MinProperty + 0, // MinData + CCaptureFilter::ClearPerFrameSettings, // SetSupported or Handler + NULL, // Values + 0, // RelationsCount + NULL, // Relations + NULL, // SupportHandler + 0 // SerializedSize + ) +}; + +DEFINE_KSPROPERTY_TABLE(FilterVidcapPropertyTable) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_VIDEOCONTROL_MODE, KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_VIDEOCONTROL_CAPS, KSPROPERTY_VIDEOCONTROL_CAPS_S, VideoControlCaps ) +}; + +DEFINE_KSPROPERTY_TABLE( VideoProcampPropertyTable ) +{ + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION, KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation, + &BacklightCompensationValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS, KSPROPERTY_VIDEOPROCAMP_S, Brightness, &BrightnessValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_CONTRAST, KSPROPERTY_VIDEOPROCAMP_S, Contrast, &ContrastValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_HUE, KSPROPERTY_VIDEOPROCAMP_S, Hue, &HueValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE, KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance, &WhiteBalanceValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq, &PLFValues) +}; + +DEFINE_KSPROPERTY_TABLE( CustomPropertyTable ) +{ + DEFINE_PROP_ITEM(CAvsCameraFilter, KSPROPERTY_CUSTOMCONTROL_DUMMY, ULONG, CustomDummy) +}; + +DEFINE_KSEVENT_TABLE(VidCapRoiEventTable) +{ + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID) +}; + +DEFINE_KSEVENT_TABLE(ExtendedPropertyEventTable) +{ + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_ISO), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE) +}; + +DEFINE_KSPROPERTY_SET_TABLE(PropertySets) +{ + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_REGION_OF_INTEREST, FocusPropertyItems ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_FLASH, FlashPropertyItems ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION, VideoStabPropertyItems ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOCONTROL, FilterVidcapPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_IMAGE_PIN_CAPABILITY, PinDependencePropertyItems ), + DEFINE_STD_PROPERTY_SET( KSPROPERTYSETID_ExtendedCameraControl, ExtendedPropertyItems ), + DEFINE_STD_PROPERTY_SET( KSPROPERTYSETID_PerFrameSettingControl, PFSPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL, CameraControlPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOPROCAMP, VideoProcampPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CUSTOMCONTROL, CustomPropertyTable ) +}; + +DEFINE_KSEVENT_SET_TABLE(EventSets) +{ + DEFINE_KSEVENT_SET + ( + &KSEVENTSETID_ExtendedCameraControl, + SIZEOF_ARRAY(ExtendedPropertyEventTable), + ExtendedPropertyEventTable + ), + DEFINE_KSEVENT_SET + ( + &EVENTSETID_VIDCAP_CAMERACONTROL_REGION_OF_INTEREST, + SIZEOF_ARRAY(VidCapRoiEventTable), + VidCapRoiEventTable + ) +}; + +DEFINE_KSAUTOMATION_TABLE(AvsCameraFilterAutomationTable) +{ + DEFINE_KSAUTOMATION_PROPERTIES(PropertySets), + DEFINE_KSAUTOMATION_METHODS_NULL, + DEFINE_KSAUTOMATION_EVENTS(EventSets) +}; + +//FFC +DEFINE_KSPROPERTY_SET_TABLE(PropertySetsFFC) +{ + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOCONTROL, FilterVidcapPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_IMAGE_PIN_CAPABILITY, PinDependencePropertyItems ), + DEFINE_STD_PROPERTY_SET( KSPROPERTYSETID_ExtendedCameraControl, ExtendedPropertyItemsFFC ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOPROCAMP, VideoProcampPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CUSTOMCONTROL, CustomPropertyTable ) +}; + +DEFINE_KSEVENT_SET_TABLE(EventSetsFFC) +{ + DEFINE_KSEVENT_SET + ( + &KSEVENTSETID_ExtendedCameraControl, + SIZEOF_ARRAY(ExtendedPropertyEventTable), + ExtendedPropertyEventTable + ) +}; + +DEFINE_KSAUTOMATION_TABLE(AvsCameraFilterAutomationTableFFC) +{ + DEFINE_KSAUTOMATION_PROPERTIES(PropertySetsFFC), + DEFINE_KSAUTOMATION_METHODS_NULL, + DEFINE_KSAUTOMATION_EVENTS(EventSetsFFC) +}; + +// +// CaptureFilterPinDescriptors: +// +// The list of pin descriptors on the capture filter. +// +const +KSPIN_DESCRIPTOR_EX +PinDescriptors[] = +{ + // + // Video Capture Pin + // + { + &VideoCapturePinDispatch, + NULL, + { + 0, // Interfaces (NULL, 0 == default) + NULL, + 0, // Mediums (NULL, 0 == default) + NULL, + SIZEOF_ARRAY(VideoCapturePinDataRanges),// Range Count + VideoCapturePinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &PIN_CATEGORY_CAPTURE, // Category + &g_PINNAME_VIDEO_CAPTURE, // Name + 0 // Reserved + }, + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 0, // Instances Necessary + &VideoCapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast <PFNKSINTERSECTHANDLEREX> + (CVideoCapturePin::IntersectHandler) + }, + // + // Video Preview Pin + // + { + &VideoCapturePinDispatch, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(VideoPreviewPinDataRanges),// Range Count + VideoPreviewPinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &PIN_CATEGORY_PREVIEW, // Category + &g_PINNAME_VIDEO_PREVIEW, // Name + 0 // Reserved + }, + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 0, // Instances Necessary + &VideoCapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast <PFNKSINTERSECTHANDLEREX> + (CVideoCapturePin::IntersectHandler) + }, + // + // Image Capture Pin + // + { + &ImageCapturePinDispatch, + NULL, + { + 0, // Interfaces (NULL, 0 == default) + NULL, + 0, // Mediums (NULL, 0 == default) + NULL, + SIZEOF_ARRAY(ImageCapturePinDataRanges),// Range Count + ImageCapturePinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &PINNAME_IMAGE, // Category + &g_PINNAME_IMAGE_CAPTURE, // Name + 0 // Reserved + }, +////////////////////////////////////////////////////////////////////////////////////////////////////// + KSPIN_FLAG_INITIATE_PROCESSING_ON_EVERY_ARRIVAL | +////////////////////////////////////////////////////////////////////////////////////////////////////// + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 0, // Instances Necessary + &ImageCapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast <PFNKSINTERSECTHANDLEREX> + (CImageCapturePin::IntersectHandler) + } +}; + +// +// CaptureFilterDispatch: +// +// This is the dispatch table for the capture filter. It provides notification +// of creation, closure, processing (for filter-centrics, not for the capture +// filter), and resets (for filter-centrics, not for the capture filter). +// +const +KSFILTER_DISPATCH +AvsCameraFilterDispatch = +{ + CAvsCameraFilter::DispatchCreate, // Filter Create + CCaptureFilter::DispatchClose, // Filter Close + NULL, // Filter Process + NULL // Filter Reset +}; + +// {B27E3887-AD10-4A4E-BFB8-D6765ADD0E38} +const +GUID +AvsCam_FrontCamera_Filter = {STATIC_FrontCamera_Filter}; + +// {4EE16166-F358-4F10-8889-93107806B7A7} +const +GUID +AvsCam_RearCamera_Filter = {STATIC_RearCamera_Filter}; + +// +// AvsCameraFilterDescriptor: +// +// The descriptor for the capture filter. We don't specify any topology +// since there's only one pin on the filter. Realistically, there would +// be some topological relationships here because there would be input +// pins from crossbars and the like. +// +const +KSFILTER_DESCRIPTOR +AvsCameraFilterDescriptor = +{ + &AvsCameraFilterDispatch, // Dispatch Table + &AvsCameraFilterAutomationTable, // Automation Table + KSFILTER_DESCRIPTOR_VERSION, // Version + KSFILTER_FLAG_PRIORITIZE_REFERENCEGUID, // Flags + &AvsCam_RearCamera_Filter, // Reference GUID + DEFINE_KSFILTER_PIN_DESCRIPTORS(PinDescriptors), + DEFINE_KSFILTER_CATEGORIES(FilterCategories), + 0, + sizeof (KSNODE_DESCRIPTOR), + NULL, + 0, + NULL, + NULL // Component ID +}; + +const +KSFILTER_DESCRIPTOR +AvsCameraFilterDescriptorFFC = +{ + &AvsCameraFilterDispatch, // Dispatch Table + &AvsCameraFilterAutomationTableFFC, // Automation Table + KSFILTER_DESCRIPTOR_VERSION, // Version + KSFILTER_FLAG_PRIORITIZE_REFERENCEGUID, // Flags + &AvsCam_FrontCamera_Filter, // Reference GUID + DEFINE_KSFILTER_PIN_DESCRIPTORS(PinDescriptors), + DEFINE_KSFILTER_CATEGORIES(FilterCategories), + 0, + sizeof (KSNODE_DESCRIPTOR), + NULL, + 0, + NULL, + NULL // Component ID +}; + diff --git a/AVStream/avscamera/sys/AvsCameraFilter.h b/AVStream/avscamera/sys/AvsCameraFilter.h new file mode 100644 index 00000000..ce81cede --- /dev/null +++ b/AVStream/avscamera/sys/AvsCameraFilter.h @@ -0,0 +1,57 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraFilter.h + + Abstract: + + Filter class implementation. Derived from CCapturefilter. + This class overloads basic filter behavior for our device. + + History: + + created 02/24/2015 + +**************************************************************************/ + +class CAvsCameraFilter : public CCaptureFilter +{ +public: + // + // Ctor + // + CAvsCameraFilter( + _In_ PKSFILTER Filter + ); + + // + // Dtor + // + virtual + ~CAvsCameraFilter (); + + // + // DispatchCreate(): + // + // This is the filter creation dispatch for the capture filter. It + // creates the CCaptureFilter object, associates it with the AVStream + // object, and bags it for easy cleanup later. + // + static + NTSTATUS + DispatchCreate ( + _In_ PKSFILTER Filter, + _In_ PIRP Irp + ); + + // Example of adding a new, custom property. + DECLARE_PROPERTY_HANDLERS( ULONG, CustomDummy ) + +protected: + DWORD m_CustomValue; +}; diff --git a/AVStream/avscamera/sys/CameraProfile.h b/AVStream/avscamera/sys/CameraProfile.h new file mode 100644 index 00000000..1a2aca8a --- /dev/null +++ b/AVStream/avscamera/sys/CameraProfile.h @@ -0,0 +1,54 @@ +/************************************************************************** + + AvsCam - An AVStream Simulated Camera Device. + + Copyright (c) 2014, Microsoft Corporation. + + File: + + CameraProfile.h + + Abstract: + + This file contains the helper class CExtendedProfile. + + History: + + created 3/19/2015 + +**************************************************************************/ + +// +// Helper class for CameraProfile +// +class CExtendedProfile : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_PROFILE m_Profile; + +public: + CExtendedProfile( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedProfile( flags, result ) + { + Size = sizeof(*this); + m_Profile.ProfileId = KSCAMERAPROFILE_Legacy; + m_Profile.Index = 0; + m_Profile.Reserved = 0; + } + + CExtendedProfile( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + m_Profile.ProfileId = KSCAMERAPROFILE_Legacy; + m_Profile.Index = 0; + m_Profile.Reserved = 0; + } + + bool isValid() + { + return CExtendedHeader::isValid() && (Size >= sizeof(*this)); + } +}; + + diff --git a/AVStream/avscamera/sys/Capture.cpp b/AVStream/avscamera/sys/Capture.cpp new file mode 100644 index 00000000..764052b3 --- /dev/null +++ b/AVStream/avscamera/sys/Capture.cpp @@ -0,0 +1,2086 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + capture.cpp + + Abstract: + + This is the implementation of the CCapturePin class. + + CCapturePin wraps a PKSPIN object and does: + 1. Object construction and cleanup, + 2. State management, + 3. Accepts new frame buffers for filling, + 4. Completes frame buffers delivered by the simulation, + 5. Negotiates for and sets new formats / data ranges, and + 6. Manages allocator framing. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "Common.h" +#include <ksmedia.h> +#include "ntintsafe.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CCapturePin:: +CCapturePin ( + _In_ PKSPIN Pin +) + : m_Pin (Pin) + , m_PinState(PinStopped) + , m_Clock(nullptr) + , m_PendIo(FALSE) + , m_AcquiredResources(FALSE) + , m_VideoInfoHeader(nullptr) + , m_pBitmapInfoHeader(nullptr) + , m_PreviousStreamPointer(nullptr) + , m_PresentationTime(0) + , m_FrameNumber(0) + , m_DroppedFrames(0) + , m_DesiredFrames(2) +/*++ + +Routine Description: + + Construct a new capture pin. + +Arguments: + + Pin - + The AVStream pin object corresponding to the capture pin + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(Pin); + + PKSDEVICE Device = KsPinGetDevice (Pin); + + NT_ASSERT(Device); + + // + // Set up our device pointer. This gives us access to "hardware I/O" + // during the capture routines. + // + m_Device = CCaptureDevice::Recast(Device); + + NT_ASSERT( m_Device ); + + m_Sensor = m_Device->GetSensor( KsPinGetParentFilter(Pin ) ); + + NT_ASSERT( m_Sensor ); +} + +CCapturePin:: +~CCapturePin() +{ + PAGED_CODE(); + + SAFE_FREE( m_pBitmapInfoHeader ); + SAFE_FREE( m_VideoInfoHeader ); +} + +/*************************************************/ + +NTSTATUS +CCapturePin:: +ReleaseAllFrames() + +/*++ + +Routine Description: + + Clean up any references we're holding on frames after we abruptly + stop the hardware. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + PKSSTREAM_POINTER Clone = KsPinGetFirstCloneStreamPointer (m_Pin); + PKSSTREAM_POINTER NextClone = NULL; + + // + // Walk through the clones, deleting them, and setting DataUsed to + // zero since we didn't use any data! + // + while (Clone) + { + + NextClone = KsStreamPointerGetNextClone (Clone); + + Clone->StreamHeader->DataUsed = 0; + KsStreamPointerDelete (Clone); + + Clone = NextClone; + + } + + return STATUS_SUCCESS; + +} + +NTSTATUS +CCapturePin:: +Initialize() +/*++ + +Routine Description: + + Post construction initialization: + 1. Add us to the Pin's bag, + 2. Capture information about the image/video format, and + 3. Update the allocator's framing to match. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER("(Pin=%d)", m_Pin->Id); + + NTSTATUS Status = STATUS_SUCCESS; + + if( !m_Sensor ) + { + // Fail if we couldn't find the sensor. + Status = STATUS_INVALID_PARAMETER; + } + else + { + // + // Add the item to the object bag if we we were successful. + // Whenever the pin closes, the bag is cleaned up and we will be + // freed. + // + Status = KsAddItemToObjectBag ( + m_Pin->Bag, + this, + reinterpret_cast<PFNKSFREE> (Cleanup) + ); + + if( NT_SUCCESS(Status) ) + { + m_Pin->Context = this; + } + } + + // + // If we succeeded so far, stash the video info header away and change + // our allocator framing to reflect the fact that only now do we know + // the framing requirements based on the connection format. + // + if (NT_SUCCESS (Status)) + { + Status = CaptureBitmapInfoHeader(); + } + + if (NT_SUCCESS(Status)) + { + // + // Edit the framing descriptors to match our pin's requirements given the current format. + // + Status = UpdateAllocatorFraming(); + } + + DBG_LEAVE("(Pin=%d)=0x%08X", m_Pin->Id, Status); + return Status; +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +CaptureBitmapInfoHeader() +/*++ + +Routine Description: + + Capture information about the image/video format for the pin. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + const GUID ImageInfoSpecifier ={ STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) }; + const GUID VideoInfoSpecifier ={ STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) }; + + // Free any previous copy of these header. + SAFE_FREE(m_pBitmapInfoHeader); + SAFE_FREE(m_VideoInfoHeader); + + m_pBitmapInfoHeader = reinterpret_cast <PKS_BITMAPINFOHEADER> ( + ExAllocatePoolWithTag ( + NonPagedPoolNx, + sizeof(KS_BITMAPINFOHEADER), + AVSHWS_POOLTAG + ) + ); + if( !m_pBitmapInfoHeader ) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + if( IsEqualGUID( m_Pin->ConnectionFormat->Specifier, ImageInfoSpecifier ) && + m_Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_IMAGEINFO) ) + { + + PKS_BITMAPINFOHEADER ConnectionHeader = + &((reinterpret_cast <PKS_DATAFORMAT_IMAGEINFO> + (m_Pin->ConnectionFormat))->ImageInfoHeader); + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + *m_pBitmapInfoHeader = *ConnectionHeader; + + m_VideoInfoHeader = reinterpret_cast <PKS_VIDEOINFOHEADER> ( + ExAllocatePoolWithTag ( + NonPagedPoolNx, + sizeof(KS_VIDEOINFOHEADER), + AVSHWS_POOLTAG + ) + ); + if( !m_VideoInfoHeader ) + { + SAFE_FREE( m_pBitmapInfoHeader ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + m_VideoInfoHeader->bmiHeader = *ConnectionHeader; + + // If we don't have a known bit-depth (compressed formats), assume the worse-case. + if( m_pBitmapInfoHeader->biBitCount==0 ) + { + m_pBitmapInfoHeader->biBitCount=32; + } + + // Estimate the image size. The derived pin can chose to override this value. + m_VideoInfoHeader->bmiHeader.biSizeImage = + (m_pBitmapInfoHeader->biWidth*m_pBitmapInfoHeader->biHeight*m_pBitmapInfoHeader->biBitCount)/8; + + // Estimate a frame rate. The derived pin can chose to override this value. + m_VideoInfoHeader->AvgTimePerFrame = ONESECOND/30; + } + else + if( IsEqualGUID( m_Pin->ConnectionFormat->Specifier, VideoInfoSpecifier ) && + m_Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_VIDEOINFOHEADER) ) + { + PKS_VIDEOINFOHEADER ConnectionHeader = + &((reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER> + (m_Pin->ConnectionFormat))-> + VideoInfoHeader); + + m_VideoInfoHeader = reinterpret_cast <PKS_VIDEOINFOHEADER> ( + ExAllocatePoolWithTag ( + NonPagedPoolNx, + KS_SIZE_VIDEOHEADER (ConnectionHeader), + AVSHWS_POOLTAG + ) + ); + if( !m_VideoInfoHeader ) + { + SAFE_FREE( m_pBitmapInfoHeader ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + RtlCopyMemory ( + m_VideoInfoHeader, + ConnectionHeader, + KS_SIZE_VIDEOHEADER (ConnectionHeader) + ); + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + *m_pBitmapInfoHeader = m_VideoInfoHeader->bmiHeader; + } + + DBG_TRACE( "+++ AvgTimePerFrame = %lld +++", m_VideoInfoHeader->AvgTimePerFrame ); + + return STATUS_SUCCESS; +} + +/*************************************************/ + +NTSTATUS +CCapturePin:: +SetState ( + _In_ KSSTATE ToState, + _In_ KSSTATE FromState +) + +/*++ + +Routine Description: + + This is called when the caputre pin transitions state. The routine + attempts to acquire / release any hardware resources and start up + or shut down capture based on the states we are transitioning to + and away from. + +Arguments: + + ToState - + The state we're transitioning to + + FromState - + The state we're transitioning away from + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + DBG_ENTER( "( Pin=%d, ToState=%s, FromState=%s )\n", + m_Pin->Id, KSStateToStateName(ToState), KSStateToStateName(FromState) ) ; + + switch (ToState) + { + + case KSSTATE_STOP: + + // + // First, stop the hardware if we actually did anything to it. + // + if (m_PinState != PinStopped) + { + Status = m_Sensor->Stop(m_Pin); + NT_ASSERT (NT_SUCCESS (Status)); + + m_PinState = PinStopped; + } + + // + // We've stopped the "fake hardware". It has cleared out + // it's scatter / gather tables and will no longer be + // completing clones. We had locks on some frames that were, + // however, in hardware. This will clean them up. An + // alternative location would be in the reset dispatch. + // Note, however, that the reset dispatch can occur in any + // state and this should be understood. + // + // Some hardware may fill all S/G mappings before stopping... + // in this case, you may not have to do this. The + // "fake hardware" here simply stops filling mappings and + // cleans its scatter / gather tables out on the Stop call. + // + Status = ReleaseAllFrames (); + + // + // Release any hardware resources related to this pin. + // + if (m_AcquiredResources) + { + // + // If we got an interface to the clock, we must release it. + // + if (m_Clock) + { + m_Clock->Release (); + m_Clock = NULL; + } + + m_Sensor->ReleaseHardwareResources (m_Pin); + + m_AcquiredResources = FALSE; + } + + break; + + case KSSTATE_ACQUIRE: + // + // Acquire any hardware resources related to this pin. We should + // only acquire them here -- **NOT** at filter create time. + // This means we do not fail creation of a filter because of + // limited hardware resources. + // + // TODO: Move this to a derived CCapturePin class - one + // specifically for testing, not as a sample. + if(GetAcquireFailureKey()) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + if (FromState == KSSTATE_STOP) + { + Status = + m_Sensor-> + AcquireHardwareResources ( + m_Pin, + this, + m_VideoInfoHeader, + &m_HardwareSimulation + ); + + if (NT_SUCCESS (Status)) + { + m_AcquiredResources = TRUE; + + // + // Attempt to get an interface to the master clock. + // This will fail if one has not been assigned. Since + // one must be assigned while the pin is still in + // KSSTATE_STOP, this is a guranteed method of getting + // the clock should one be assigned. + // + if (!NT_SUCCESS ( + KsPinGetReferenceClockInterface ( + m_Pin, + &m_Clock + ) + )) + { + // + // If we could not get an interface to the clock, + // don't use one. + // + m_Clock = NULL; + } + } + else + { + m_AcquiredResources = FALSE; + } + + } + + // + // Standard transport pins will always receive transitions in + // +/- 1 manner. This means we'll always see a PAUSE->ACQUIRE + // transition before stopping the pin. + // + + m_FrameNumber = 0; + m_DroppedFrames = 0; + break; + + case KSSTATE_PAUSE: + // + // Stop the hardware simulation if we're coming down from run. + // + if (FromState == KSSTATE_RUN) + { + m_PresentationTime = 0; + Status = m_Sensor->Pause (m_Pin, TRUE); + + if (NT_SUCCESS (Status)) + { + m_PinState = PinPaused; + } + } + m_FrameNumber = 0; + break; + + case KSSTATE_RUN: + // + // Start the hardware simulation or unpause it depending on + // whether we're initially running or we've paused and restarted. + // + if (FromState == KSSTATE_PAUSE && m_PinState == PinPaused) + { + Status = m_Sensor->Pause (m_Pin, FALSE); + } + else + { + Status = m_Sensor->Start (m_Pin); + } + + if (NT_SUCCESS (Status)) + { + m_PinState = PinRunning; + } + break; + + } + + DBG_LEAVE( "( Pin=%d, ToState=%s, FromState=%s ) = 0x%08X\n", + m_Pin->Id, KSStateToStateName(ToState), KSStateToStateName(FromState), Status ) ; + + return Status; + +} + +NTSTATUS +CCapturePin:: +Process() + +/*++ + +Routine Description: + + The process dispatch for the pin bridges to this location. + We handle setting up scatter gather mappings, etc... + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE( ); + + DBG_ENTER( "( Pin=%d )", m_Pin->Id ); + + NTSTATUS Status = STATUS_SUCCESS; + PKSSTREAM_POINTER Leading; + + Leading = KsPinGetLeadingEdgeStreamPointer( m_Pin, KSSTREAM_POINTER_STATE_LOCKED ); + + while( NT_SUCCESS( Status ) && Leading ) + { + + PKSSTREAM_POINTER ClonePointer; + PSTREAM_POINTER_CONTEXT SPContext = NULL; + + // + // If no data is present in the Leading edge stream pointer, just + // move on to the next frame + // + if( NULL == Leading->StreamHeader->Data ) + { + Status = KsStreamPointerAdvance( Leading ); + continue; + } + // + // For optimization sake in this particular sample, I will only keep + // one clone stream pointer per frame. This complicates the logic + // here but simplifies the completions. + // + // I'm also choosing to do this since I need to keep track of the + // virtual addresses corresponding to each mapping since I'm faking + // DMA. It simplifies that too. + // + if( !m_PreviousStreamPointer ) + { + // + // First thing we need to do is clone the leading edge. This allows + // us to keep reference on the frames while they're in DMA. + // + Status = KsStreamPointerClone( + Leading, + NULL, + sizeof (STREAM_POINTER_CONTEXT), + &ClonePointer + ); + + // + // I use this for easy chunking of the buffer. We're not really + // dealing with physical addresses. This keeps track of what + // virtual address in the buffer the current scatter / gather + // mapping corresponds to for the fake hardware. + // + if( NT_SUCCESS( Status ) ) + { + // + // Set the stream header data used to 0. We update this + // in the DMA completions. For queues with DMA, we must + // update this field ourselves. + // + ClonePointer->StreamHeader->DataUsed = 0; + + SPContext = reinterpret_cast <PSTREAM_POINTER_CONTEXT> + (ClonePointer->Context); + + SPContext->BufferVirtual = + reinterpret_cast <PUCHAR> ( + ClonePointer->StreamHeader->Data + ); + } + } + else + { + ClonePointer = m_PreviousStreamPointer; + SPContext = reinterpret_cast <PSTREAM_POINTER_CONTEXT> + (ClonePointer->Context); + } + + // + // If the clone failed, likely we're out of resources. Break out + // of the loop for now. We may end up starving DMA. + // + if( !NT_SUCCESS( Status ) ) + { + KsStreamPointerUnlock( Leading, FALSE ); + break; + } + + // + // Program the fake hardware. I would use Clone->OffsetOut.*, but + // because of the optimization of one stream pointer per frame, it + // doesn't make complete sense. + // + ULONG MappingsUsed = + m_Sensor->ProgramScatterGatherMappings( + m_Pin, + &ClonePointer, + &(SPContext->BufferVirtual), + Leading->OffsetOut.Mappings, + Leading->OffsetOut.Remaining + ); + + // + // In order to keep one clone per frame and simplify the fake DMA + // logic, make a check to see if we completely used the mappings in + // the leading edge. Set a flag. + // + if( MappingsUsed == Leading->OffsetOut.Remaining ) + { + m_PreviousStreamPointer = NULL; + } + else + { + m_PreviousStreamPointer = ClonePointer; + } + + if( MappingsUsed ) + { + // + // If any mappings were added to scatter / gather queues, + // advance the leading edge by that number of mappings. If + // we run off the end of the queue, Status will be + // STATUS_DEVICE_NOT_READY. Otherwise, the leading edge will + // point to a new frame. The previous one will not have been + // dismissed (unless "DMA" completed) since there's a clone + // pointer referencing the frames. + // + Status = + KsStreamPointerAdvanceOffsets( + Leading, + 0, + MappingsUsed, + FALSE + ); + } + else + { + // + // The hardware was incapable of adding more entries. The S/G + // table is full. + // + Status = STATUS_PENDING; + break; + } + } + + // + // If the leading edge failed to lock (this is always possible, remember + // that locking CAN occassionally fail), don't blow up passing NULL + // into KsStreamPointerUnlock. Also, set m_PendIo to kick us later... + // + if( !Leading ) + { + m_PendIo = TRUE; + + // + // If the lock failed, there's no point in getting called back + // immediately. The lock could fail due to insufficient memory, + // etc... In this case, we don't want to get called back immediately. + // Return pending. The m_PendIo flag will cause us to get kicked + // later. + // + Status = STATUS_PENDING; + } + + // + // If we didn't run the leading edge off the end of the queue, unlock it. + // + if( NT_SUCCESS( Status ) && Leading ) + { + KsStreamPointerUnlock( Leading, FALSE ); + } + else + { + // + // DEVICE_NOT_READY indicates that the advancement ran off the end + // of the queue. We couldn't lock the leading edge. + // + if( Status == STATUS_DEVICE_NOT_READY ) + { + Status = STATUS_SUCCESS; + } + } + + // + // If we failed with something that requires pending, set the pending I/O + // flag so we know we need to start it again. + // + if( !NT_SUCCESS( Status ) || Status == STATUS_PENDING ) + { + m_PendIo = TRUE; + } + + DBG_LEAVE( "( Pin=%d )=0x%08X", m_Pin->Id, Status ); + + return Status; +} + +// +// Emit metadata here for video or preview pin. +// +// This function gives us one last chance to tack metadata onto the sample. +// +void +CCapturePin:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); +} + + +NTSTATUS +CCapturePin:: +CompleteMapping( + _In_ PKSSTREAM_POINTER Clone + ) + +/*++ + +Routine Description: + + Called to notify the pin that a given number of scatter / gather + mappings have completed. Let the buffers go if possible. + +Arguments: + + Clone - + The stream pointer for the frame to complete. + If Clone is null, use the head of the queue. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("(Clone=%p)", Clone); + + NTSTATUS Status = STATUS_SUCCESS; + + if( !Clone ) + { + Clone = KsPinGetFirstCloneStreamPointer(m_Pin); + } + + // + // If we have completed all remaining mappings in this clone, it + // is an indication that the clone is ready to be deleted and the + // buffer released. Set anything required in the stream header which + // has not yet been set. If we have a clock, we can timestamp the + // sample. + // + if( Clone ) + { + if( Clone->StreamHeader->DataUsed >= Clone->OffsetOut.Remaining ) + { + Clone->StreamHeader->Duration = + m_VideoInfoHeader->AvgTimePerFrame; + + Clone->StreamHeader->OptionsFlags |= + KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; + + // + // Increment the frame number. This is the total count of frames which + // have attempted capture. + // + m_FrameNumber++; + DBG_TRACE( "m_FrameNumber=%lld", m_FrameNumber ); + + // + // Double check the Stream Header size. AVStream makes no guarantee + // that because StreamHeaderSize is set to a specific size that you + // will get that size. If the proper data type handlers are not + // installed, the stream header will be of default size. + // + if ( Clone->StreamHeader->Size >= sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO)) + { + PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> ( + Clone->StreamHeader + 1 + ); + + FrameInfo->ExtendedHeaderSize = sizeof (KS_FRAME_INFO); + FrameInfo->dwFrameFlags = KS_VIDEO_FLAG_FRAME; + FrameInfo->PictureNumber = (LONGLONG)m_FrameNumber; + + // I don't really have a way to tell if the device has dropped a frame + // or was not able to send a frame on time. + FrameInfo->DropCount = (LONGLONG)m_DroppedFrames; + } + + KsStreamPointerDelete (Clone); + } + else + { + // + // If only part of the mappings in this clone have been completed, + // update the pointers. Since we're guaranteed this won't advance + // to a new frame by the check above, it won't fail. + // + Status = + KsStreamPointerAdvanceOffsets( + Clone, + 0, + Clone->StreamHeader->DataUsed, + FALSE + ); + } + } + else + { + // We had nothing to process. + Status = STATUS_UNSUCCESSFUL; + } + + // + // If we've used all the mappings in hardware and pended, we can kick + // processing to happen again if we've completed mappings. + // + if (m_PendIo) + { + KsPinAttemptProcessing (m_Pin, TRUE); + m_PendIo = FALSE; + } + + DBG_LEAVE("(Clone=%p)=0x%08X", Clone, Status); + return Status; +} + + +bool +CCapturePin:: +GetAcquireFailureKey() +{ + NTSTATUS ntstatus; + bool acquireFailure = false; + UNICODE_STRING RegistryKeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE handleRegKey = NULL; + PKEY_VALUE_FULL_INFORMATION pKeyInfo = NULL; + ULONG ulKeyInfoSizeNeeded = 0; + UNICODE_STRING ValueName = {0}; + + + PAGED_CODE(); + + // Get the Registry key + RtlInitUnicodeString(&RegistryKeyName, L"\\Registry\\Machine\\Software\\Microsoft\\wtt\\MachineConfig"); + InitializeObjectAttributes(&ObjectAttributes, + &RegistryKeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, // handle + NULL); + + ntstatus = ZwOpenKey(&handleRegKey, KEY_READ, &ObjectAttributes); + // If we can't open the key, we're done. Worst case, we cannot stream. + if(NT_SUCCESS(ntstatus)) + { + // Get the location from the registry key + RtlInitUnicodeString(&ValueName, L"AcquireFailure"); + + // Figure out how big keyInfo needs to be + ntstatus = ZwQueryValueKey(handleRegKey, + &ValueName, + KeyValueFullInformation, + pKeyInfo, + 0, + &ulKeyInfoSizeNeeded ); + + // We expect one of these errors + if( (ntstatus == STATUS_BUFFER_TOO_SMALL) || (ntstatus == STATUS_BUFFER_OVERFLOW) ) + { + // Allocate the memory needed for the key + pKeyInfo = (PKEY_VALUE_FULL_INFORMATION) new (NonPagedPoolNx) BYTE[ulKeyInfoSizeNeeded]; + RtlZeroMemory(pKeyInfo, ulKeyInfoSizeNeeded); + + // Now get the actual key data + ntstatus = ZwQueryValueKey( handleRegKey, + &ValueName, + KeyValueFullInformation, + pKeyInfo, + ulKeyInfoSizeNeeded, + &ulKeyInfoSizeNeeded ); + + if(ntstatus == STATUS_SUCCESS) + { + acquireFailure = true; + } + + delete[] pKeyInfo; + } + } + + // All done with the registry + if (NULL != handleRegKey) + { + ZwClose(handleRegKey); + } + + return acquireFailure; +} + + +NTSTATUS +CCapturePin:: +DispatchClose( + _In_ PKSPIN Pin, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static thunking function to forward Close + +Arguments: + + Pin - + Our pin + Irp - + The IRP the request came in on. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context); + Status = pPin->Close( Irp ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +NTSTATUS +CCapturePin:: +DispatchSetState( + _In_ PKSPIN Pin, + _In_ KSSTATE ToState, + _In_ KSSTATE FromState +) +/*++ + +Routine Description: + + Static thunking function to forward SetState + +Arguments: + + Pin - + Our pin + Irp - + The IRP the request came in on. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context); + Status = pPin->SetState( ToState, FromState ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +NTSTATUS +CCapturePin:: +DispatchSetFormat( + _In_ PKSPIN Pin, + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange +) + +/*++ + +Routine Description: + + This is the set data format dispatch for the capture pin. It is called + in two circumstances. + + 1: Before Pin's creation dispatch has been made to verify that + Pin->ConnectionFormat is an acceptable format for the range + DataRange. In this case OldFormat is NULL. + + 2: After Pin's creation dispatch has been made and an initial format + selected in order to change the format for the pin. In this case, + OldFormat will not be NULL. + + Validate that the format is acceptible and perform the actions necessary + to change format if appropriate. + +Arguments: + + Pin - + The pin this format is being set on. The format itself will be in + Pin->ConnectionFormat. + + OldFormat - + The previous format used on this pin. If this is NULL, it is an + indication that Pin's creation dispatch has not yet been made and + that this is a request to validate the initial format and not to + change formats. + + OldAttributeList - + The old attribute list for the prior format + + DataRange - + A range out of our list of data ranges which was determined to be + at least a partial match for Pin->ConnectionFormat. If the format + there is unacceptable for the range, STATUS_NO_MATCH should be + returned. + + AttributeRange - + The attribute range + +Return Value: + + Success / Failure + + STATUS_SUCCESS - + The format is acceptable / the format has been changed + + STATUS_NO_MATCH - + The format is not-acceptable / the format has not been changed + +--*/ + +{ + + PAGED_CODE( ); + + DBG_ENTER( "(Pin->Id=%d)", Pin->Id ); + + NTSTATUS Status = STATUS_NO_MATCH; + const GUID ImageInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) }; + const GUID VideoInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) }; + + // + // Find the pin, if it exists yet. OldFormat will be an indication of + // this. If we're changing formats, OldFormat will be non-NULL. + // + // You cannot use Pin->Context to make the determination. AVStream + // preinitializes this to the filter's context. + // + CCapturePin *CapPin = reinterpret_cast <CCapturePin *> (Pin->Context); + + // For debugging... + UNICODE_STRING NewFormat; + UNICODE_STRING RefFormat; + RtlStringFromGUID( Pin->ConnectionFormat->SubFormat, &NewFormat ); + RtlStringFromGUID( DataRange->SubFormat, &RefFormat ); + + if( IsEqualGUID( Pin->ConnectionFormat->Specifier, ImageInfoSpecifier ) && + Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_IMAGEINFO) ) + { + PKS_DATAFORMAT_IMAGEINFO ConnectionFormat = reinterpret_cast <PKS_DATAFORMAT_IMAGEINFO> (Pin->ConnectionFormat); + + // + // DataRange comes out of OUR data range list. I know the range + // is valid as such. + // + const KS_DATARANGE_IMAGE *VIRange = reinterpret_cast <const KS_DATARANGE_IMAGE *> (DataRange); + + // + // Check that bmiHeader.biSize is valid since we use it later. + // + ULONG ImageHeaderSize = ConnectionFormat->ImageInfoHeader.biSize; + ULONG DataFormatSize = sizeof (KSDATAFORMAT) +sizeof(KS_BITMAPINFOHEADER); + + if( ImageHeaderSize < ConnectionFormat->ImageInfoHeader.biSize || + DataFormatSize < ImageHeaderSize || + DataFormatSize > ConnectionFormat->DataFormat.FormatSize ) + { + Status = STATUS_INVALID_PARAMETER; + } + + // + // Check that the format is a match for the selected range. + // + else if( (ConnectionFormat->ImageInfoHeader.biWidth != VIRange->ImageInfoHeader.biWidth) || + (ConnectionFormat->ImageInfoHeader.biHeight != VIRange->ImageInfoHeader.biHeight) || + !IsEqualGUID( Pin->ConnectionFormat->SubFormat, DataRange->SubFormat ) ) + { + Status = STATUS_NO_MATCH; + } + else + { + + ULONG ImageSize; + + if( !MultiplyCheckOverflow( (ULONG) ConnectionFormat->ImageInfoHeader.biWidth, (ULONG) abs( ConnectionFormat->ImageInfoHeader.biHeight ), &ImageSize ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + + else if( !MultiplyCheckOverflow( ImageSize, (ULONG) (ConnectionFormat->ImageInfoHeader.biBitCount/8), &ImageSize ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + + // + // Valid for the formats we use. Otherwise, this would be + // checked later. + // + else if( ConnectionFormat->ImageInfoHeader.biSizeImage < ImageSize ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + // + // We can accept the format. + // + Status = STATUS_SUCCESS; + + // + // OldFormat is an indication that this is a format change. + // Since I do not implement the + // KSPROPERTY_CONNECTION_PROPOSEDATAFORMAT, by default, I do + // not handle dynamic format changes. + // + // If something changes while we're in the stop state, we're + // fine to handle it since we haven't "configured the hardware" + // yet. + // + if( OldFormat && CapPin ) + { + // + // If we're in the stop state, we can handle just about any + // change. We don't support dynamic format changes. + // + if( Pin->DeviceState == KSSTATE_STOP ) + { + Status = CapPin->CaptureBitmapInfoHeader(); + } + else + { + // + // Because we don't accept dynamic format changes, we + // should never get here. Just being over-protective. + // + Status = STATUS_INVALID_DEVICE_STATE; + } + } + } + } + } + + else if( IsEqualGUID( Pin->ConnectionFormat->Specifier, + VideoInfoSpecifier ) && + Pin->ConnectionFormat->FormatSize >= + sizeof (KS_DATAFORMAT_VIDEOINFOHEADER) ) + { + + PKS_DATAFORMAT_VIDEOINFOHEADER ConnectionFormat = + reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER> + (Pin->ConnectionFormat); + + // + // DataRange comes out of OUR data range list. I know the range + // is valid as such. + // + const KS_DATARANGE_VIDEO *VIRange = + reinterpret_cast <const KS_DATARANGE_VIDEO *> + (DataRange); + + // + // Check that bmiHeader.biSize is valid since we use it later. + // + ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER( + &ConnectionFormat->VideoInfoHeader + ); + + ULONG DataFormatSize = FIELD_OFFSET( + KS_DATAFORMAT_VIDEOINFOHEADER, VideoInfoHeader + ) + VideoHeaderSize; + + if( VideoHeaderSize < ConnectionFormat->VideoInfoHeader.bmiHeader.biSize || + DataFormatSize < VideoHeaderSize || + DataFormatSize > ConnectionFormat->DataFormat.FormatSize ) + { + Status = STATUS_INVALID_PARAMETER; + } + + // + // Check that the format is a match for the selected range. + // + else if( + (ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth != + VIRange->VideoInfoHeader.bmiHeader.biWidth) || + (ConnectionFormat->VideoInfoHeader.bmiHeader.biHeight != + VIRange->VideoInfoHeader.bmiHeader.biHeight) || + !IsEqualGUID( Pin->ConnectionFormat->SubFormat, DataRange->SubFormat ) ) + { + DBG_TRACE("FAILED: Height=(%d,%d), Width=(%d,%d), SubFormat=(%wZ,%wZ)", + ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth, + VIRange->VideoInfoHeader.bmiHeader.biWidth, + ConnectionFormat->VideoInfoHeader.bmiHeader.biHeight, + VIRange->VideoInfoHeader.bmiHeader.biHeight, + &NewFormat, &RefFormat ); + Status = STATUS_NO_MATCH; + } + else + { + DBG_TRACE("MATCH: Height=(%d,%d), Width=(%d,%d), SubFormat=(%wZ,%wZ)", + ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth, + VIRange->VideoInfoHeader.bmiHeader.biWidth, + ConnectionFormat->VideoInfoHeader.bmiHeader.biHeight, + VIRange->VideoInfoHeader.bmiHeader.biHeight, + &NewFormat, &RefFormat ); + + // + // Compute the minimum size of our buffers to validate against. + // The image synthesis routines synthesize |biHeight| rows of + // biWidth pixels in either RGB24 or UYVY. In order to ensure + // safe synthesis into the buffer, we need to know how large an + // image this will produce. + // + // I do this explicitly because of the method that the data is + // synthesized. A variation of this may or may not be necessary + // depending on the mechanism the driver in question fills the + // capture buffers. The important thing is to ensure that they + // aren't overrun during capture. + // + ULONG ImageSize; + + if( !MultiplyCheckOverflow( + (ULONG) ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth, + (ULONG) abs( ConnectionFormat-> + VideoInfoHeader.bmiHeader.biHeight ), + &ImageSize + ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + + // + // We only support KS_BI_RGB (24) and KS_BI_YUV422 (16), so + // this is valid for those formats. + // + else if( !MultiplyCheckOverflow( + ImageSize, + (ULONG) (ConnectionFormat-> + VideoInfoHeader.bmiHeader.biBitCount / 8), + &ImageSize + ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + + // + // Valid for the formats we use. Otherwise, this would be + // checked later. + // + else if( ConnectionFormat->VideoInfoHeader.bmiHeader.biSizeImage < + ImageSize ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + // + // We can accept the format. + // + Status = STATUS_SUCCESS; + + // + // OldFormat is an indication that this is a format change. + // Since I do not implement the + // KSPROPERTY_CONNECTION_PROPOSEDATAFORMAT, by default, I do + // not handle dynamic format changes. + // + // If something changes while we're in the stop state, we're + // fine to handle it since we haven't "configured the hardware" + // yet. + // + if( OldFormat && CapPin ) + { + // + // If we're in the stop state, we can handle just about any + // change. We don't support dynamic format changes. + // + if( Pin->DeviceState == KSSTATE_STOP ) + { + if( !CapPin->CaptureBitmapInfoHeader( ) ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + // + // Because we don't accept dynamic format changes, we + // should never get here. Just being over-protective. + // + Status = STATUS_INVALID_DEVICE_STATE; + } + } + } + } + } + + RtlFreeUnicodeString(&NewFormat); + RtlFreeUnicodeString(&RefFormat); + + DBG_LEAVE( "(Pin->Id=%d)=0x%08X", Pin->Id, Status ); + return Status; +} + +NTSTATUS +CCapturePin:: +IntersectHandler( + _In_ PKSFILTER Filter, + _In_ PIRP Irp, + _In_ PKSP_PIN PinInstance, + _In_ PKSDATARANGE CallerDataRange, + _In_ PKSDATARANGE DescriptorDataRange, + _In_ ULONG BufferSize, + _Out_opt_ PVOID Data OPTIONAL, + _Out_ PULONG DataSize +) + +/*++ + +Routine Description: + + This routine handles video pin intersection queries by determining the + intersection between two data ranges. + +Arguments: + + Filter - + Contains a void pointer to the filter structure. + + Irp - + Contains a pointer to the data intersection property request. + + PinInstance - + Contains a pointer to a structure indicating the pin in question. + + CallerDataRange - + Contains a pointer to one of the data ranges supplied by the client + in the data intersection request. The format type, subtype and + specifier are compatible with the DescriptorDataRange. + + DescriptorDataRange - + Contains a pointer to one of the data ranges from the pin descriptor + for the pin in question. The format type, subtype and specifier are + compatible with the CallerDataRange. + + BufferSize - + Contains the size in bytes of the buffer pointed to by the Data + argument. For size queries, this value will be zero. + + Data - + Optionally contains a pointer to the buffer to contain the data + format structure representing the best format in the intersection + of the two data ranges. For size queries, this pointer will be + NULL. + + DataSize - + Contains a pointer to the location at which to deposit the size + of the data format. This information is supplied by the function + when the format is actually delivered and in response to size + queries. + +Return Value: + + STATUS_SUCCESS if there is an intersection and it fits in the supplied + buffer, STATUS_BUFFER_OVERFLOW for successful size queries, + STATUS_NO_MATCH if the intersection is empty, or + STATUS_BUFFER_TOO_SMALL if the supplied buffer is too small. + +--*/ + +{ + PAGED_CODE( ); + + const GUID ImageInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) }; + const GUID VideoInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) }; + ULONG DataFormatSize; + + NT_ASSERT( Filter ); + NT_ASSERT( Irp ); + NT_ASSERT( PinInstance ); + NT_ASSERT( CallerDataRange ); + NT_ASSERT( DescriptorDataRange ); + NT_ASSERT( DataSize ); + + // + // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER + // + if( IsEqualGUID( CallerDataRange->Specifier, ImageInfoSpecifier ) && + CallerDataRange->FormatSize >= sizeof (KS_DATARANGE_IMAGE) ) + { + + PKS_DATARANGE_IMAGE callerDataRange = + reinterpret_cast <PKS_DATARANGE_IMAGE> (CallerDataRange); + + PKS_DATARANGE_IMAGE descriptorDataRange = + reinterpret_cast <PKS_DATARANGE_IMAGE> (DescriptorDataRange); + + // + // Check that the other fields match + // + if( (RtlCompareMemory( &callerDataRange->ConfigCaps, &descriptorDataRange->ConfigCaps, sizeof (KS_VIDEO_STREAM_CONFIG_CAPS) ) + != sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) ) + { + return STATUS_NO_MATCH; + } + + { + ULONG ImageHeaderSize = ((PKS_DATARANGE_IMAGE) callerDataRange)->ImageInfoHeader.biSize; + ULONG DataRangeSize = FIELD_OFFSET( KS_DATARANGE_IMAGE, ImageInfoHeader ) + ImageHeaderSize; + + if( ImageHeaderSize < callerDataRange->ImageInfoHeader.biSize || + DataRangeSize < ImageHeaderSize || + DataRangeSize > callerDataRange->DataRange.FormatSize ) + { + return STATUS_INVALID_PARAMETER; + } + } + + DataFormatSize = sizeof (KSDATAFORMAT) +sizeof(KS_BITMAPINFOHEADER); + // + // If the passed buffer size is 0, it indicates that this is a size + // only query. Return the size of the intersecting data format and + // pass back STATUS_BUFFER_OVERFLOW. + // + if( BufferSize == 0 ) + { + *DataSize = DataFormatSize; + return STATUS_BUFFER_OVERFLOW; + } + + // + // Verify that the provided structure is large enough to + // accept the result. + // + if( BufferSize < DataFormatSize ) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy over the KSDATAFORMAT, followed by the actual VideoInfoHeader + // + *DataSize = DataFormatSize; + + PKS_DATAFORMAT_IMAGEINFO pFormatImageInfoHeader = PKS_DATAFORMAT_IMAGEINFO( Data ); + + // + // Copy over the KSDATAFORMAT. This is precisely the same as the + // KSDATARANGE (it's just the GUIDs, etc... not the format information + // following any data format. + // + RtlCopyMemory( pFormatImageInfoHeader, DescriptorDataRange, sizeof (KSDATAFORMAT) ); + pFormatImageInfoHeader->DataFormat.FormatSize = DataFormatSize; + + // + // Copy over the callers requested KS_BITMAPINFOHEADER + // + RtlCopyMemory( &pFormatImageInfoHeader->ImageInfoHeader, &callerDataRange->ImageInfoHeader, sizeof(KS_BITMAPINFOHEADER) ); + return STATUS_SUCCESS; + } + else if( IsEqualGUID( CallerDataRange->Specifier, VideoInfoSpecifier ) && + CallerDataRange->FormatSize >= sizeof (KS_DATARANGE_VIDEO) ) + { + + PKS_DATARANGE_VIDEO callerDataRange = + reinterpret_cast <PKS_DATARANGE_VIDEO> (CallerDataRange); + + PKS_DATARANGE_VIDEO descriptorDataRange = + reinterpret_cast <PKS_DATARANGE_VIDEO> (DescriptorDataRange); + + PKS_DATAFORMAT_VIDEOINFOHEADER FormatVideoInfoHeader; + + // + // Check that the other fields match + // + if( (callerDataRange->bFixedSizeSamples != + descriptorDataRange->bFixedSizeSamples) || + (callerDataRange->bTemporalCompression != + descriptorDataRange->bTemporalCompression) || + (callerDataRange->StreamDescriptionFlags != + descriptorDataRange->StreamDescriptionFlags) || + (callerDataRange->MemoryAllocationFlags != + descriptorDataRange->MemoryAllocationFlags) || + (RtlCompareMemory( &callerDataRange->ConfigCaps, + &descriptorDataRange->ConfigCaps, + sizeof (KS_VIDEO_STREAM_CONFIG_CAPS) ) != + sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) ) + { + return STATUS_NO_MATCH; + } + + // + // KS_SIZE_VIDEOHEADER() below is relying on bmiHeader.biSize from + // the caller's data range. This **MUST** be validated; the + // extended bmiHeader size (biSize) must not extend past the end + // of the range buffer. Possible arithmetic overflow is also + // checked for. + // + ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER( + &callerDataRange->VideoInfoHeader + ); + + ULONG DataRangeSize = + FIELD_OFFSET( KS_DATARANGE_VIDEO, VideoInfoHeader ) + + VideoHeaderSize; + + // + // Check that biSize does not extend past the buffer. The + // first two checks are for arithmetic overflow on the + // operations to compute the alleged size. (On unsigned + // math, a+b < a iff an arithmetic overflow occurred). + // + if( VideoHeaderSize < callerDataRange->VideoInfoHeader.bmiHeader.biSize || + DataRangeSize < VideoHeaderSize || + DataRangeSize > callerDataRange->DataRange.FormatSize ) + { + return STATUS_INVALID_PARAMETER; + } + + DataFormatSize = + sizeof (KSDATAFORMAT) + + KS_SIZE_VIDEOHEADER( &callerDataRange->VideoInfoHeader ); + + + // + // If the passed buffer size is 0, it indicates that this is a size + // only query. Return the size of the intersecting data format and + // pass back STATUS_BUFFER_OVERFLOW. + // + if( BufferSize == 0 ) + { + *DataSize = DataFormatSize; + return STATUS_BUFFER_OVERFLOW; + } + + // + // Verify that the provided structure is large enough to + // accept the result. + // + if( BufferSize < DataFormatSize ) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy over the KSDATAFORMAT, followed by the actual VideoInfoHeader + // + *DataSize = DataFormatSize; + + FormatVideoInfoHeader = PKS_DATAFORMAT_VIDEOINFOHEADER( Data ); + + // + // Copy over the KSDATAFORMAT. This is precisely the same as the + // KSDATARANGE (it's just the GUIDs, etc... not the format information + // following any data format. + // + RtlCopyMemory( + &FormatVideoInfoHeader->DataFormat, + DescriptorDataRange, + sizeof (KSDATAFORMAT) ); + + FormatVideoInfoHeader->DataFormat.FormatSize = DataFormatSize; + + // + // Copy over the callers requested VIDEOINFOHEADER + // + + RtlCopyMemory( + &FormatVideoInfoHeader->VideoInfoHeader, + &callerDataRange->VideoInfoHeader, + KS_SIZE_VIDEOHEADER( &callerDataRange->VideoInfoHeader ) + ); + + // + // Calculate biSizeImage for this request, and put the result in both + // the biSizeImage field of the bmiHeader AND in the SampleSize field + // of the DataFormat. + // + // Note that for compressed sizes, this calculation will probably not + // be just width * height * bitdepth + // + FormatVideoInfoHeader->VideoInfoHeader.bmiHeader.biSizeImage = + FormatVideoInfoHeader->DataFormat.SampleSize = + KS_DIBSIZE( FormatVideoInfoHeader->VideoInfoHeader.bmiHeader ); + + // + // REVIEW - Perform other validation such as cropping and scaling checks + // + + return STATUS_SUCCESS; + + } // End of VIDEOINFOHEADER specifier + + return STATUS_NO_MATCH; +} + +NTSTATUS +CCapturePin:: +DispatchProcess( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Process + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context); + Status = pPin->Process(); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCapturePin:: +DispatchReset( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Reset + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context); + pPin->Reset( ); + } + DBG_LEAVE("()"); +} + +NTSTATUS +CCapturePin:: +DispatchConnect( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Reset + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context); + Status = pPin->Connect( ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCapturePin:: +DispatchDisconnect( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Disconnect + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast <CCapturePin *> (Pin->Context); + pPin->Disconnect( ); + } + DBG_LEAVE("()"); +} + + +NTSTATUS +CCapturePin:: +Close( + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Close handler. + + Replace or overload if you need special handling. + +Arguments: + + Irp - + The IRP the request came in on. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior. + return STATUS_SUCCESS; +} + +NTSTATUS +CCapturePin:: +SetFormat( + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange +) +/*++ + +Routine Description: + + SetFormat handler. + + Replace or overload if you need special handling. + +Arguments: + + OldFormat - + The previous format used on this pin. If this is NULL, it is an + indication that Pin's creation dispatch has not yet been made and + that this is a request to validate the initial format and not to + change formats. + + OldAttributeList - + The old attribute list for the prior format + + DataRange - + A range out of our list of data ranges which was determined to be + at least a partial match for Pin->ConnectionFormat. If the format + there is unacceptable for the range, STATUS_NO_MATCH should be + returned. + + AttributeRange - + The attribute range + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. + return STATUS_SUCCESS; +} + +void +CCapturePin:: +Reset() +/*++ + +Routine Description: + + Reset handler. + + Replace or overload if you need special handling. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. + m_Sensor->Reset(m_Pin); +} + +NTSTATUS +CCapturePin:: +Connect() +/*++ + +Routine Description: + + Connect handler. + + Replace or overload if you need special handling. + +Arguments: + + None. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. + return STATUS_SUCCESS; +} + +void +CCapturePin:: +Disconnect() +/*++ + +Routine Description: + + Disconnect handler. + + Replace or overload if you need special handling. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. +} + +// Update the Pin's Allocator Framing +NTSTATUS +CCapturePin:: +UpdateAllocatorFraming() +{ + PAGED_CODE(); + + // Acquire the lock and update the Pin's allocator framing. + NTSTATUS Status = + KsEdit( m_Pin, &m_Pin->Descriptor, AVSHWS_POOLTAG); + + if( NT_SUCCESS( Status ) ) + { + // + // If the edits proceeded without running out of memory, adjust + // the framing based on the video info header. + // + Status = KsEdit( m_Pin, &m_Pin->Descriptor->AllocatorFraming, AVSHWS_POOLTAG ); + + if( NT_SUCCESS( Status ) ) + { + // + // We've KsEdit'ed this... I'm safe to cast away constness as + // long as the edit succeeded. + // + PKSALLOCATOR_FRAMING_EX Framing = + const_cast <PKSALLOCATOR_FRAMING_EX> ( + m_Pin->Descriptor->AllocatorFraming + ); + + Framing->FramingItem[0].Frames = m_DesiredFrames; + Framing->FramingItem[0].PhysicalRange.MinFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage; + Framing->FramingItem[0].PhysicalRange.MaxFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage; + Framing->FramingItem [0].FramingRange.Range.MinFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage; + Framing->FramingItem [0].FramingRange.Range.MaxFrameSize = m_VideoInfoHeader->bmiHeader.biSizeImage; + + Framing->FramingItem [0].PhysicalRange.Stepping = 0; + Framing->FramingItem [0].FramingRange.Range.Stepping = 0; + + DBG_TRACE( "Advertising Frame requirement: %d", m_DesiredFrames ); + DBG_TRACE("Image size estimate of: %d bytes", m_VideoInfoHeader->bmiHeader.biSizeImage ); + } + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + diff --git a/AVStream/avscamera/sys/Capture.h b/AVStream/avscamera/sys/Capture.h new file mode 100644 index 00000000..0fd87101 --- /dev/null +++ b/AVStream/avscamera/sys/Capture.h @@ -0,0 +1,305 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + capture.h + + Abstract: + + This file contains header for the base capture pin class for a filter. + The camera sample performs "fake" DMA directly into the capture + buffers. Common buffer DMA will work slightly differently. + + For common buffer DMA, the general technique would be DPC schedules + processing with KsPinAttemptProcessing. The processing routine grabs + the leading edge, copies data out of the common buffer and advances. + Cloning would not be necessary with this technique. It would be + similiar to the way "AVSSamp" works, but it would be pin-centric. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#pragma once +#include <initguid.h> + +#define DMAX_X 640 +#define DMAX_Y 480 +#define D_X 320 +#define D_Y 240 + +#define DMAX_X_OS 728 +#define DMAX_Y_OS 576 +#define D_X_OS 384 +#define D_Y_OS 240 + +/////////////////////////////////////////////////////////////////////////////// +// GUID for primary and FFC cameras +/////////////////////////////////////////////////////////////////////////////// +// {9D9A0F5E-CDE9-49f5-B464-BF13429DDEB5} +DEFINE_GUID(PRIMARY_CAMERA_GUID, + 0x9d9a0f5e, 0xcde9, 0x49f5, 0xb4, 0x64, 0xbf, 0x13, 0x42, 0x9d, 0xde, 0xb5); + +// {75D53C4E-963E-4415-AA88-89107F08D1EE} +DEFINE_GUID(FRONT_FACING_CAMERA_GUID, + 0x75d53c4e, 0x963e, 0x4415, 0xaa, 0x88, 0x89, 0x10, 0x7f, 0x8, 0xd1, 0xee); + +/* +#define Mpx12_X 4160 +#define Mpx12_Y 3120 +#define Mpx5_X 2592 +#define Mpx5_Y 1944 +#define Mpx8_X 3264 +#define Mpx8_Y 2448 +*/ +// +// STREAM_POINTER_CONTEXT: +// +// This is the context structure we associate with all clone stream pointers. +// It allows the mapping code to rip apart the buffer into chunks the same +// size as the scatter/gather mappings in order to fake scatter / gather +// bus-master DMA. +// +typedef struct _STREAM_POINTER_CONTEXT +{ + + PUCHAR BufferVirtual; + +} STREAM_POINTER_CONTEXT, *PSTREAM_POINTER_CONTEXT; + +// +// CCapturePin: +// +// The video capture pin class. +// +class CCapturePin : + public ICapturePin, + public CNonCopyable +{ +protected: + PKSPIN m_Pin; // The AVStream pin we're associated with. + CCaptureDevice *m_Device; // Pointer to the internal device object for our capture device. + CSensor *m_Sensor; // The h/w sensor simulation for the filter. + CHardwareSimulation *m_HardwareSimulation; // The h/w isp simulation for this pin, if the pin has been through KSSTATE_ACQUIRE. + PIN_STATE m_PinState; // The state we've put the hardware into. + PIKSREFERENCECLOCK m_Clock; // The clock we've been assigned. + BOOLEAN m_PendIo; // An indication of whether or not we pended I/O for some reason. + BOOLEAN m_AcquiredResources; // An indication of whether or not this pin has acquired the necessary hardware resources to operate. + PKS_VIDEOINFOHEADER m_VideoInfoHeader; + PKS_BITMAPINFOHEADER m_pBitmapInfoHeader; // Optional info for image formats. + + // + // If we are unable to insert all of the mappings in a stream pointer into + // the "fake" hardware's scatter / gather table, we set this to the + // stream pointer that's incomplete. This is done both to make the + // relasing easier and to make it easier to fake the scatter / gather + // hardware. + // + PKSSTREAM_POINTER m_PreviousStreamPointer; + LONGLONG m_PresentationTime; + LONGLONG m_FrameNumber; + LONGLONG m_DroppedFrames; + + ULONG m_DesiredFrames; + + // + // ReleaseAllFrames(): + // + // Clean up any references we hold on frames in the queue. This is called + // when we abruptly stop the fake hardware. + // + NTSTATUS + ReleaseAllFrames (); + + static + void + Cleanup ( + _In_ CCapturePin *Pin) + { + delete Pin; + } + + // + // Emit metadata here for video or preview pin. + // + virtual + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + + // Helper function for finding our filter object. + CCaptureFilter * + GetFilter() + { + return reinterpret_cast <CCaptureFilter *> + (KsPinGetParentFilter(m_Pin)->Context); + } + + virtual + NTSTATUS + Close( + _In_ PIRP Irp + ); + + // Intended to give the pin an opportunity to do something extra with the selected format. + virtual + NTSTATUS + SetFormat( + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange + ); + + virtual + NTSTATUS + Process( ); + + virtual + NTSTATUS + SetState( + _In_ KSSTATE ToState, + _In_ KSSTATE FromState + ); + + virtual + void + Reset(); + + virtual + NTSTATUS + Connect(); // Pin Connect + + virtual + void + Disconnect(); // Pin Disconnect + + virtual + NTSTATUS + CaptureBitmapInfoHeader( ); + + virtual + NTSTATUS + Initialize(); + +public: + // + // CCapturePin(): + // + // The capture pin's constructor. Initialize any non-0, non-NULL fields + // (since new will have zero'ed the memory anyway) and set up our + // device level pointers for access during capture routines. + // + CCapturePin( _In_ PKSPIN Pin); + + // + // ~CCapturePin(): + // + // The capture pin's destructor. + // + virtual + ~CCapturePin(); + + // + // ICapturePin::CompleteMapping() + // + // This is the capture pin notification mechanism for mapping completion. + // When the device detects that frames have been completed by the fake + // hardware, it informs the capture sink through this method. + // + virtual + NTSTATUS + CompleteMapping( + _In_ PKSSTREAM_POINTER Clone=nullptr + ); + + void + SetDesiredFrames( ULONG n ) + { + m_DesiredFrames = n; + } + + // Update the Pin's Allocator Framing + virtual + NTSTATUS + UpdateAllocatorFraming(); + + // TEST: Move this to a derived CCapturePin class - one + // specifically for testing, not as a sample. + static + bool + GetAcquireFailureKey(); + + PIN_STATE GetState() const + { + return m_PinState; + } + + static + NTSTATUS + DispatchClose( + _In_ PKSPIN Pin, + _In_ PIRP Irp + ); + static NTSTATUS DispatchSetState( + _In_ PKSPIN Pin, + _In_ KSSTATE ToState, + _In_ KSSTATE FromState + ); + + static NTSTATUS DispatchSetFormat( + _In_ PKSPIN Pin, + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange + ); + + static NTSTATUS DispatchProcess( _In_ PKSPIN Pin ); + + static void DispatchReset( _In_ PKSPIN ); + static NTSTATUS DispatchConnect( _In_ PKSPIN Pin ); // Pin Connect + static void DispatchDisconnect( _In_ PKSPIN Pin ); // Pin Disconnect + + static + NTSTATUS + IntersectHandler( + _In_ PKSFILTER Filter, + _In_ PIRP Irp, + _In_ PKSP_PIN PinInstance, + _In_ PKSDATARANGE CallerDataRange, + _In_ PKSDATARANGE DescriptorDataRange, + _In_ ULONG BufferSize, + _Out_opt_ PVOID Data OPTIONAL, + _Out_ PULONG DataSize + ); +}; + +// +// Define a dispatch table for a capture pin. It provides notifications +// about creation, closure, processing, data formats, etc. using naming +// conventions shared by all CCapturePin derived classes. +// +#define DEFINE_CAMERA_KSPIN_DISPATCH( Table, Class ) \ +const \ +KSPIN_DISPATCH \ +Table ={ \ + Class::DispatchCreate, /* Pin Create */ \ + Class::DispatchClose, /* Pin Close */ \ + Class::DispatchProcess, /* Pin Process */ \ + Class::DispatchReset, /* Pin Reset */ \ + Class::DispatchSetFormat, /* Pin Set Data Format */ \ + Class::DispatchSetState, /* Pin Set Device State */ \ + Class::DispatchConnect, /* Pin Connect */ \ + Class::DispatchDisconnect, /* Pin Disconnect */ \ + NULL, /* Clock Dispatch */ \ + NULL /* Allocator Dispatch */ \ +}; + diff --git a/AVStream/avscamera/sys/Common.h b/AVStream/avscamera/sys/Common.h new file mode 100644 index 00000000..deb6bf4c --- /dev/null +++ b/AVStream/avscamera/sys/Common.h @@ -0,0 +1,703 @@ + +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + Common.h + + Abstract: + + Common project header. + + History: + + created 02/18/2015 + +**************************************************************************/ + +#pragma once + +#define _NO_SYS_GUID_OPERATOR_EQ_ + +#include <stdio.h> +#include <stdlib.h> + +#include <wdm.h> +#include <windef.h> +#include <unknown.h> +#include <ks.h> +#include <ksmedia.h> +#include <kcom.h> +#include <stdunk.h> +#include <ntstrsafe.h> +#define NOBITMAP +#include <mmreg.h> +#undef NOBITMAP +#include <acpitabl.h> + +#include "MetadataInternal.h" +#include "CustomProperties.h" +#include "Dbg.h" + +/************************************************* + + Add definitions that are missing for C++14. + +*************************************************/ + +/*++ + +Routine Description: + + Array new() operator for creating objects with a specified allocation tag. + +Arguments: + + iSize - + The size of the entire allocation. + + poolType - + The type of allocation. Ex: PagedPool or NonPagedPoolNx + + tag - + A 4-byte allocation identifier. + +Return Value: + + None + +--*/ +inline +PVOID +operator new[]( + size_t iSize, + _When_((poolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + POOL_TYPE poolType, + ULONG tag +) +{ + PVOID result = ExAllocatePoolWithTag(poolType, iSize, tag); + + if (result) + { + RtlZeroMemory(result, iSize); + } + + return result; +} + +/*++ + +Routine Description: + + Array delete() operator. + +Arguments: + + pVoid - + The memory to free. + +Return Value: + + None + +--*/ +inline +void +__cdecl +operator delete[]( + PVOID pVoid +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete +( + void *pVoid, + size_t /*size*/ +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete[]() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete[] +( + void *pVoid, + size_t /*size*/ +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/************************************************* + + Misc Definitions + +*************************************************/ +#pragma warning (disable : 4100 4127 4131 4189 4701 4706) +#define STR_MODULENAME "AvsCamera: " + +#define IFFAILED_EXIT(status) \ + if (!NT_SUCCESS( Status = (status) )) \ + { \ + goto done; \ + } + +#define IFNULL_EXIT(exp) \ + if ( (exp) == nullptr ) \ + { \ + Status = STATUS_INSUFFICIENT_RESOURCES; \ + goto done; \ + } + +// +// Number of 100ns in one second +// +#define ONESECOND 10000000 + +#define ABS(x) ((x) < 0 ? (-(x)) : (x)) + +#ifndef mmioFOURCC +#define mmioFOURCC( ch0, ch1, ch2, ch3 ) \ + ( (DWORD)(BYTE)(ch0) | ( (DWORD)(BYTE)(ch1) << 8 ) | \ + ( (DWORD)(BYTE)(ch2) << 16 ) | ( (DWORD)(BYTE)(ch3) << 24 ) ) +#endif + +#define FOURCC_YUY2 mmioFOURCC('Y', 'U', 'Y', '2') +#define FOURCC_NV12 mmioFOURCC('N', 'V', '1', '2') + +// RGB24 +#define STATIC_KSDATAFORMAT_SUBTYPE_RGB24\ + 0xe436eb7d, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 +DEFINE_GUIDSTRUCT("e436eb7d-524f-11ce-9f53-0020af0ba770", KSDATAFORMAT_SUBTYPE_RGB24); +#define KSDATAFORMAT_SUBTYPE_RGB24 DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_RGB24) + +// RGB32 +#define STATIC_KSDATAFORMAT_SUBTYPE_RGB32\ + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 +DEFINE_GUIDSTRUCT("e436eb7e-524f-11ce-9f53-0020af0ba770", KSDATAFORMAT_SUBTYPE_RGB32); +#define KSDATAFORMAT_SUBTYPE_RGB32 DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_RGB32) + +// NV12 +#define STATIC_KSDATAFORMAT_SUBTYPE_NV12\ + 0x3231564E, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +DEFINE_GUIDSTRUCT("3231564E-0000-0010-8000-00AA00389B71", KSDATAFORMAT_SUBTYPE_NV12); +#define KSDATAFORMAT_SUBTYPE_NV12 DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_NV12) + +// YUY2 +#define STATIC_KSDATAFORMAT_SUBTYPE_YUY2\ + 0x32595559, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +DEFINE_GUIDSTRUCT("32595559-0000-0010-8000-00AA00389B71", KSDATAFORMAT_SUBTYPE_YUY2); +#define KSDATAFORMAT_SUBTYPE_YUY2 DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_YUY2) + +#define CAPTURE_FILTER_COUNT 2 + +//filter index +#define CAPTURE_FILTER_RFC 0 +#define CAPTURE_FILTER_FFC 1 + +//Arbitrary Maximum faces +#define MAX_FACES 15 + +// +// CAPTURE_PIN_DATA_RANGE_COUNT:(formats) +// +// The number of ranges supported on the capture pin. +// +#define IMAGE_CAPTURE_PIN_DATA_RANGE_COUNT 2 +#define VIDEO_CAPTURE_PIN_DATA_RANGE_COUNT 30 +#define VIDEO_PREVIEW_PIN_DATA_RANGE_COUNT 15 + +// +// Define the number of frames needed in the queue for each pin. +// +#define IMAGE_CAPTURE_PIN_MINIMUM_FRAMES 3 +#define VIDEO_CAPTURE_PIN_MINIMUM_FRAMES 4 +#define PREVIEW_PIN_MINIMUM_FRAMES 6 +#define IMAGE_CAPTURE_PIN_MAXIMUM_FRAMES 20 +#define IMAGE_CAPTURE_PIN_MAXIMUM_HISTORY_FRAMES 10 + +// +// CAPTURE_FILTER_CATEGORIES_COUNT: +// +// The number of categories for the capture filter. +// +#define CAPTURE_FILTER_CATEGORIES_COUNT 3 + +#define AVSHWS_POOLTAG 'hSVA' + +// +// Focal length definitions +// + +// These are the definitions found in MSDN for the legacy zoom controls. +// +//#define FOCALLENGTH_OCULAR 50 +//#define FOCALLENGTH_OPTICAL_MIN 10 +//#define FOCALLENGTH_OPTICAL_MAX 600 + +//#define FOCALLENGTH_OCULAR 50 +//#define FOCALLENGTH_OPTICAL_MIN 10 +//#define FOCALLENGTH_OPTICAL_MAX 600 + +// Note: Despite what MSDN says, they are NOT limits as far as the +// pipeline is conerned. You should supply values here that are +// appropriate to your hardware - whatever that may be. +// +// Also note that since both optical and ocular focal length are +// nomally measured in millimeters, and since the optical length is +// always the numerator and ocular is used as the denominator, the +// units (millimeters) cancel out. This means you can stick any set +// of values you like here that can be used to represent the min and +// maximum zoom value of your hardware as a simple rational number. +// +// Again, the rational number is: (Objective length / Ocular length) +// +// We use the following definitions in this simulation in order to have the +// values produced and used in the legacy control be the same as the ones +// in the new extended zoom control. +// +#define FOCALLENGTH_OCULAR (1<<16) // Denominator of 2^16 makes this the same as Q16 format in extended. +#define FOCALLENGTH_OPTICAL_MIN (FOCALLENGTH_OCULAR/16) // Allow a wide-field view of 1/16th. +#define FOCALLENGTH_OPTICAL_MAX (FOCALLENGTH_OCULAR*32) // Allow a zoom-in view of 32x. + +// Values for Power Line Frequency +#define POWERLINEFREQ_DISABLED 0 +#define POWERLINEFREQ_50HZ 1 +#define POWERLINEFREQ_60HZ 2 +#define POWERLINEFREQ_AUTO 3 +#define POWERLINEFREQ_DEFAULT POWERLINEFREQ_DISABLED + + +// +// Q-format definitions. +// +#define BASE_Q(n) (((ULONGLONG)1)<<n) +#define TO_Q31(_x_) (LONGLONG)( (_x_) * BASE_Q(31) ) +#define TO_Q16(_x_) (LONGLONG)( (_x_) * BASE_Q(16) ) +#define FROM_Q16(_x_, _denominator_) ((LONG)( (LONGLONG)(_x_) / ( TO_Q16(1) / (LONGLONG)(_denominator_)) )) + +#define MULT_Q16(_x_, _y_) (LONGLONG)((((LONGLONG)(_x_) * (LONGLONG)(_y_)) + (BASE_Q(15)))>>16) +#define DIV_Q16(_x_, _y_) (LONGLONG)((((LONGLONG)(_x_)<<16)+(_y_/2))/(LONGLONG)(_y_)) + +#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8)) +#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16)) + +// +// Copy of histogram channel bitmask definitions from mfapi.h (not available in wdk). +// +#define MF_HISTOGRAM_CHANNEL_Y 0x00000001 +#define MF_HISTOGRAM_CHANNEL_R 0x00000002 +#define MF_HISTOGRAM_CHANNEL_G 0x00000004 +#define MF_HISTOGRAM_CHANNEL_B 0x00000008 +#define MF_HISTOGRAM_CHANNEL_Cb 0x00000010 +#define MF_HISTOGRAM_CHANNEL_Cr 0x00000020 + +/************************************************* + + Externed information + +*************************************************/ +// +// filter.cpp externs: +// +extern +const +KSFILTER_DISPATCH +AvsCameraFilterDispatch; + +extern +const +KSFILTER_DESCRIPTOR +AvsCameraFilterDescriptor; + +extern +const +KSFILTER_DESCRIPTOR +AvsCameraFilterDescriptorFFC; + +extern +const +GUID +AvsCameraFilterCategories [CAPTURE_FILTER_CATEGORIES_COUNT]; + +// +// capture.cpp externs: +// +extern +const +KSALLOCATOR_FRAMING_EX +VideoCapturePinAllocatorFraming; + +extern +const +KSALLOCATOR_FRAMING_EX +ImageCapturePinAllocatorFraming; + +extern +const +KSPIN_DISPATCH +VideoCapturePinDispatch; + +extern +const +KSPIN_DISPATCH +ImageCapturePinDispatch; + +/************************************************* + + Enums / Typedefs + +*************************************************/ + +typedef enum _PIN_STATE +{ + + PinStopped = 0, + PinPaused, + PinRunning + +} PIN_STATE, *PPIN_STATE; + +typedef enum _PIN_MODE +{ + + PinNormalMode = 0, + PinBurstMode + +} PIN_MODE, *PPIN_MODE; + +//Panel surface bits 115:118 +enum AcpiPldRotation +{ + AcpiPldRotation0 = 0, + AcpiPldRotation45 = 1, + AcpiPldRotation90 = 2, + AcpiPldRotation135 = 3, + AcpiPldRotation180 = 4, + AcpiPldRotation225 = 5, + AcpiPldRotation270 = 6, + AcpiPldRotation315 = 7, +}; + + +/************************************************* + + Class Definitions + +*************************************************/ + +struct ISP_FRAME_SETTINGS +{ + ULONGLONG ISOMode; + ULONG ISOValue; // for Manual ISO settings. + KSCAMERA_EXTENDEDPROP_EVCOMPENSATION EVCompensation; + ULONGLONG WhiteBalanceMode; + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING WhiteBalanceSetting; + ULONGLONG ExposureMode; + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING ExposureSetting; + ULONGLONG FocusMode; + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING FocusSetting; + ULONGLONG FocusPriority; + ULONGLONG FlashMode; + ULONG FlashValue; // Adjustable power setting (0-100) + BOOLEAN bPhotoConfirmation; +} ; + +struct SOC_CAP_WITH_STEPPING +{ + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Hdr; + KSPROPERTY_STEPPING_LONG Stepping; +} ; + +struct SOC_CAP_WITH_STEPPING_LONGLONG +{ + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Hdr; + KSPROPERTY_STEPPING_LONGLONG Stepping; +} ; + +// +// ICapturePin: +// +// This is a capture pin interface. The device level calls back the +// CompleteMapping method passing the number of completed mappings for +// the capture pin. +// +class ICapturePin +{ + +public: + + virtual + NTSTATUS + CompleteMapping( + _In_ PKSSTREAM_POINTER Clone=nullptr + ) = 0; + +}; + +class LockDevice +{ +public: + + LockDevice(PKSDEVICE device) : m_device(device) + { + NT_ASSERT(m_device); + KsAcquireDevice(m_device); + } + + ~LockDevice() + { + KsReleaseDevice(m_device); + } +private: + PKSDEVICE m_device; +}; + +class LockFilter +{ +public: + + LockFilter(PKSFILTER filter) : m_filter(filter) + { + NT_ASSERT(m_filter); + KsFilterAcquireControl(m_filter); + } + + ~LockFilter() + { + KsFilterReleaseControl(m_filter); + } +private: + PKSFILTER m_filter; +}; + +// The following class definitions and ctors are used solely +// to simplify initialization of the parent metadata structures. +// They are not here for any other purpose. + +template <class T, class U> +class CMetaRational : public U +{ +public: + CMetaRational( T Num=1, T Denom=1 ) + { + Set=TRUE; + Numerator = Num; + Denominator = Denom; + } +}; + +typedef CMetaRational<UINT32, METADATA_RATIONAL> CMetadataRational; +typedef CMetaRational<INT32, METADATA_SRATIONAL> CMetadataSRational; + +template <class T, class U> +class CMetaPrimative : public U +{ +public: + CMetaPrimative( T n ) + { + Set = TRUE; + Value = n; + } +}; + +typedef CMetaPrimative<UINT16, METADATA_UINT16> CMetadataShort; +typedef CMetaPrimative<UINT32, METADATA_UINT32> CMetadataLong; +typedef CMetaPrimative<INT64, METADATA_INT64> CMetadataLongLong; +typedef CMetaPrimative<UINT64, METADATA_UINT64> CMetadataULongLong; + +class CMetadataShortString : public METADATA_SHORTSTRING +{ +public: + CMetadataShortString( + _In_z_ const CHAR *str + ) + { + Length = (UINT32) min(strlen(str), sizeof(String)); + strncpy_s( String, str, Length ); + } +}; + +class CMetadataEVCompensation : public METADATA_EVCOMP +{ +public: + CMetadataEVCompensation( + _In_ UINT64 flags, + _In_ UINT32 value + ) + { + Set = TRUE; + Flags = flags; + Value = value; + } +}; + +typedef struct +{ + LONG Value; + ULONG Flags; + ULONG Capabilities; +} VIDCAP_PROPERTY, *PVIDCAP_PROPERTY; + +typedef struct +{ + LONG lOcularFocalLength; + LONG lObjectiveFocalLengthMin; + LONG lObjectiveFocalLengthMax; +} FOCAL_LENGTH_PROPERTY, *PFOCAL_LENGTH_PROPERTY; + +const ULONG KSCAMERA_EXTENDEDPROP_VERSION = 1; + +// +// Note: I cannot simplify this down to just the FrameIndex. +// Past frames and non-VPS photos can have a photo confirmation set +// and those images will not have an index associated with them. +// +class PHOTOCONFIRMATION_INFO +{ +public: + PHOTOCONFIRMATION_INFO( + _In_ ULONG FrameIndex, + _In_ LONGLONG Time + ) + : m_Index( FrameIndex ) + , m_Time(Time) + , m_bRequired(TRUE) + {} + + PHOTOCONFIRMATION_INFO() + : m_Index(0) + , m_Time(0) + , m_bRequired( FALSE ) + {} + + BOOL + isRequired() + { + return m_bRequired; + } + + ULONG + getIndex() + { + return m_Index; + } + + LONGLONG + getTime() + { + return m_Time; + } + +private: + BOOL m_bRequired; + ULONG m_Index; + LONGLONG m_Time; +}; + +/************************************************* + + Global Functions + +*************************************************/ + +/************************************************* + + Internal Includes + +*************************************************/ + +#include "util.h" +#include "NonCopyable.h" +#include "ref.h" +#include "Mutex.h" +#include "Waitable.h" +#include "WorkItem.h" +#include "Timer.h" +#include "ExtendedProperty.h" +#include "ExtendedVidProcSetting.h" +#include "ExtendedEvComp.h" +#include "ExtendedMetadata.h" +#include "ExtendedPhotoMode.h" +#include "ExtendedMaxVideoFpsForPhotoRes.h" +#include "ExtendedFieldOfView.h" +#include "ExtendedCameraAngleOffset.h" +#include "CameraProfile.h" +#include "Synthesizer.h" +#include "XRGBSynthesizer.h" +#include "RGB24Synthesizer.h" +#include "YUVSynthesizer.h" +#include "YUY2Synthesizer.h" +#include "NV12Synthesizer.h" +#include "hwsim.h" +#include "ImageHwSim.h" +#include "PreviewHwSim.h" +#include "VideoHwSim.h" +#include "device.h" +#include "AvsCameraDevice.h" +#include "Roi.h" +#include "Sensor.h" +#include "SensorSimulation.h" +#include "filter.h" +#include "AvsCameraFilter.h" +#include "capture.h" +#include "videocapture.h" +#include "imagecapture.h" +#include "formats.h" +#include <uuids.h> +#include <devpkey.h> diff --git a/AVStream/avscamera/sys/Dbg.h b/AVStream/avscamera/sys/Dbg.h new file mode 100644 index 00000000..b0bc6e80 --- /dev/null +++ b/AVStream/avscamera/sys/Dbg.h @@ -0,0 +1,81 @@ +/************************************************************************** + + AVStream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Dbg.h + + Abstract: + + Debug definitions. + + History: + + created 2/18/2015 + +**************************************************************************/ + +#pragma once + +#define DEBUGLVL_VERBOSE 2 +#define DEBUGLVL_TERSE 1 +#define DEBUGLVL_ERROR 0 + +const int DebugLevel = DEBUGLVL_TERSE; + +#if (DBG) +#define _DbgPrintF(lvl, strings) \ +{ \ + if (lvl <= DebugLevel) {\ + DbgPrint(STR_MODULENAME);\ + DbgPrint##strings;\ + DbgPrint("\n");\ + if ((lvl) == DEBUGLVL_ERROR) {\ + NT_ASSERT(0);\ + } \ + }\ +} +#else // !DBG +#define _DbgPrintF(lvl, strings) +#endif // !DBG + +// This is for debug output; but we're going to leave it in the +// driver for now. We can always turn off the Windbg output at +// the debugger. +// Enable via debugger: ed Kd_IHVSTREAMING_Mask 0xFFFFFFFF +#define ENABLE_TRACING 1 +#if ENABLE_TRACING + +#define DBG_OUT( FMT, ... ) \ + { \ + LARGE_INTEGER SysTime; \ + LARGE_INTEGER LocalTime; \ + TIME_FIELDS TimeFields; \ + \ + KeQuerySystemTimePrecise( &SysTime ); \ + ExSystemTimeToLocalTime( &SysTime, &LocalTime ); \ + RtlTimeToTimeFields( &LocalTime, &TimeFields ); \ + DbgPrintEx( DPFLTR_IHVSTREAMING_ID, DPFLTR_MASK|DPFLTR_TRACE_LEVEL, \ + "%02d:%02d:%02d.%03d [%p] " ## FMT ## "\n", \ + TimeFields.Hour, TimeFields.Minute, TimeFields.Second, TimeFields.Milliseconds, \ + KeGetCurrentThread(), __VA_ARGS__ ); \ + } + +#define DBG_ENTER( FMT, ... ) \ + DBG_OUT( "Entering: " __FUNCTION__ ## FMT, __VA_ARGS__ ); + +#define DBG_LEAVE( FMT, ... ) \ + DBG_OUT( " Leaving: " __FUNCTION__ ## FMT, __VA_ARGS__ ); + +#define DBG_TRACE( FMT, ... ) \ + DBG_OUT( " In: " __FUNCTION__ ## " " ## FMT, __VA_ARGS__ ); + +#else // !ENABLE_TRACING +#define DBG_ENTER( ... ) +#define DBG_LEAVE( ... ) +#define DBG_TRACE( ... ) +#endif // !ENABLE_TRACING + diff --git a/AVStream/avscamera/sys/Device.cpp b/AVStream/avscamera/sys/Device.cpp new file mode 100644 index 00000000..107cdafc --- /dev/null +++ b/AVStream/avscamera/sys/Device.cpp @@ -0,0 +1,966 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + device.cpp + + Abstract: + + This file contains the implementation of CCaptureDevice. + + CCaptureDevice is the base device class object. It handles basic PnP + requests and constructs the filter factories. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +///// +/// Structures... +//// Profile 0 +static KSCAMERA_PROFILE_MEDIAINFO s_Profile0_PreviewMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 }, +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile0_RecordMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 3840, 2160 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 120, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 90, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 60, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 }, +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile0_PhotoMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + + +//// Profile 1 +static KSCAMERA_PROFILE_MEDIAINFO s_Profile1_PreviewMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile1_PhotoMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 7680, 4320 }, { 0, 1 }, 0, 0, 0, 0, 0 }, + { { 3840, 2160 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +//// Profile 2 +static KSCAMERA_PROFILE_MEDIAINFO s_Profile2_PreviewMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile2_RecordMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +static KSCAMERA_PROFILE_PININFO s_Profile0_PinInfo[] = +{ + { {STATIC_PINNAME_VIDEO_PREVIEW}, 0,0, ARRAYSIZE(s_Profile0_PreviewMediaInfo), s_Profile0_PreviewMediaInfo }, + { {STATIC_PINNAME_VIDEO_CAPTURE}, 0,0, ARRAYSIZE(s_Profile0_RecordMediaInfo), s_Profile0_RecordMediaInfo }, + { {STATIC_PINNAME_IMAGE}, 0,0, ARRAYSIZE(s_Profile0_PhotoMediaInfo), s_Profile0_PhotoMediaInfo } +}; + +static KSCAMERA_PROFILE_PININFO s_Profile1_PinInfo[] = +{ + { {STATIC_PINNAME_VIDEO_PREVIEW}, 0,0, ARRAYSIZE(s_Profile1_PreviewMediaInfo), s_Profile1_PreviewMediaInfo }, + { {STATIC_PINNAME_IMAGE}, 0,0, ARRAYSIZE(s_Profile1_PhotoMediaInfo), s_Profile1_PhotoMediaInfo } +}; + +static KSCAMERA_PROFILE_PININFO s_Profile2_PinInfo[] = +{ + { {STATIC_PINNAME_VIDEO_PREVIEW}, 0,0, ARRAYSIZE(s_Profile2_PreviewMediaInfo), s_Profile2_PreviewMediaInfo }, + { {STATIC_PINNAME_VIDEO_CAPTURE}, 0,0, ARRAYSIZE(s_Profile2_RecordMediaInfo), s_Profile2_RecordMediaInfo } +}; + +static KSCAMERA_PROFILE_INFO s_Profiles[] = +{ + { {STATIC_KSCAMERAPROFILE_BalancedVideoAndPhoto}, 0, ARRAYSIZE(s_Profile0_PinInfo), s_Profile0_PinInfo }, + { {STATIC_KSCAMERAPROFILE_HighQualityPhoto}, 0, ARRAYSIZE(s_Profile1_PinInfo), s_Profile1_PinInfo }, + { {STATIC_KSCAMERAPROFILE_VideoRecording}, 0, ARRAYSIZE(s_Profile2_PinInfo), s_Profile2_PinInfo } +}; + +// The front camera is concurrent with the rear camera, so the front camera concurrency +// has the back camera's reference GUID and vice versa. +static KSCAMERA_PROFILE_CONCURRENCYINFO s_Profile2_ConcurrencyInfoFront[] = +{ + { {STATIC_RearCamera_Filter}, 0, 1, &s_Profiles[2] } +}; + +static KSCAMERA_PROFILE_CONCURRENCYINFO s_Profile2_ConcurrencyInfoBack[] = +{ + { {STATIC_FrontCamera_Filter}, 0, 1, &s_Profiles[2] } +}; + +const UINT32 s_ProfileCount = 3; + +UINT32 +InitializeDeviceProfiles( + _In_ BOOL fFrontCamera, + _Outptr_result_maybenull_ + PKSDEVICE_PROFILE_INFO *ppDeviceProfiles +) +{ + UINT32 uiProfileCount = 0; + PKSDEVICE_PROFILE_INFO pDeviceProfiles = NULL; + + pDeviceProfiles = (PKSDEVICE_PROFILE_INFO)ExAllocatePoolWithTag( PagedPool, sizeof(KSDEVICE_PROFILE_INFO) * ARRAYSIZE(s_Profiles) , 'fpSC'); + if( !pDeviceProfiles ) + { + // Can't publish, we're out of memory, silently fail here. + *ppDeviceProfiles = nullptr; + goto Exit; + } + + for (UINT32 i = 0; i < ARRAYSIZE(s_Profiles); i++) + { + pDeviceProfiles[i].Type = KSDEVICE_PROFILE_TYPE_CAMERA; + pDeviceProfiles[i].Size = sizeof(KSDEVICE_PROFILE_INFO); + pDeviceProfiles[i].Camera.Info = s_Profiles[i]; + pDeviceProfiles[i].Camera.Reserved = 0; + pDeviceProfiles[i].Camera.ConcurrencyCount = 0; + pDeviceProfiles[i].Camera.Concurrency = NULL; + } + + pDeviceProfiles[2].Camera.ConcurrencyCount = 1; + if (fFrontCamera) + { + pDeviceProfiles[2].Camera.Concurrency = (PKSCAMERA_PROFILE_CONCURRENCYINFO)&s_Profile2_ConcurrencyInfoFront; + } + else + { + pDeviceProfiles[2].Camera.Concurrency = (PKSCAMERA_PROFILE_CONCURRENCYINFO)&s_Profile2_ConcurrencyInfoBack; + } + + uiProfileCount = ARRAYSIZE(s_Profiles); + *ppDeviceProfiles = pDeviceProfiles; + pDeviceProfiles = NULL; + +Exit: + return uiProfileCount; +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CCaptureDevice:: +CCaptureDevice ( + _In_ PKSDEVICE Device +) + : m_Device (Device) + , m_FilterDescriptorCount(0) + , m_Sensor(nullptr) + , m_Context(nullptr) +{ + PAGED_CODE(); +} + +CCaptureDevice:: +~CCaptureDevice() +{ + PAGED_CODE(); + + if( m_Sensor ) + { + for( size_t i=0; i<m_FilterDescriptorCount; i++ ) + { + SAFE_DELETE( m_Sensor[i] ); + } + + SAFE_DELETE_ARRAY( m_Sensor ); + } +} + +NTSTATUS +CCaptureDevice:: +Initialize() +{ + PAGED_CODE(); + + m_Sensor = new (NonPagedPoolNx, 'iveD') CSensor *[m_FilterDescriptorCount]; + + if( !m_Sensor ) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + return STATUS_SUCCESS; +} + +ULONG +CCaptureDevice:: +GetFilterIndex(PKSFILTER Filter) +{ + PAGED_CODE(); + ULONG i; + + for( i=0; i<m_FilterDescriptorCount; i++ ) + { + if( Filter->Descriptor->ReferenceGuid && + IsEqualGUID(*(m_Context[i].Descriptor->ReferenceGuid), *Filter->Descriptor->ReferenceGuid)) + { + break; + } + } + + // This is a complete failure. We really need to bail. + NT_ASSERT(i<m_FilterDescriptorCount); + + return i; +} + +NTSTATUS +CCaptureDevice:: +Prepare() +{ + PAGED_CODE(); + + NTSTATUS Status = Initialize(); + + if( !NT_SUCCESS(Status) ) + { + return Status; + } + + // + // Add the item to the object bag if we were successful. + // Whenever the device goes away, the bag is cleaned up and + // we will be freed. + // + // For backwards compatibility with DirectX 8.0, we must grab + // the device mutex before doing this. For Windows XP, this is + // not required, but it is still safe. + // + PKSDEVICE Device = m_Device; + LockDevice Lock(Device); + + Status = KsAddItemToObjectBag ( + Device -> Bag, + reinterpret_cast <PVOID> (this), + reinterpret_cast <PFNKSFREE> (CCaptureDevice::Cleanup) + ); + + if( NT_SUCCESS (Status) ) + { + Device->Context=this; + } + return Status; +} + +NTSTATUS +CCaptureDevice:: +DispatchCreate ( + _In_ PKSDEVICE Device +) + +/*++ + +Routine Description: + + Create the capture device. This is the creation dispatch for the + capture device. + +Arguments: + + Device - + The AVStream device being created. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + CCaptureDevice *CapDevice = new (NonPagedPoolNx, 'eviD') CCaptureDevice (Device); + if( !CapDevice) + { + // + // Return failure if we couldn't create the device. + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + NTSTATUS Status = CapDevice->Prepare(); + if (!NT_SUCCESS (Status)) + { + // Init failure. + delete CapDevice; + } + + DBG_LEAVE("()"); + return Status; +} + +// +// DispatchStart(): +// +// This is the Pnp Start dispatch for the capture device. It simply +// bridges to PnpStart() in the context of the CCaptureDevice. +// +NTSTATUS +CCaptureDevice:: +DispatchStart ( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ PCM_RESOURCE_LIST TranslatedResourceList, + _In_ PCM_RESOURCE_LIST UntranslatedResourceList +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)-> + PnpStart ( + TranslatedResourceList, + UntranslatedResourceList + ); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// +// DispatchStop(): +// +// This is the Pnp stop dispatch for the capture device. It simply +// bridges to PnpStop() in the context of the CCaptureDevice. +// +void +CCaptureDevice:: +DispatchStop ( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpStop(Irp); + DBG_LEAVE("()"); +} + +NTSTATUS +CCaptureDevice:: +DispatchPostStart( + _In_ PKSDEVICE Device +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)->PnpPostStart(); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +NTSTATUS +CCaptureDevice:: +DispatchQueryStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)->PnpQueryStop(Irp); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCaptureDevice:: +DispatchCancelStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpCancelStop(Irp); + DBG_LEAVE("()"); +} + +NTSTATUS +CCaptureDevice:: +DispatchQueryRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)->PnpQueryRemove(Irp); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCaptureDevice:: +DispatchCancelRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpCancelRemove(Irp); + DBG_LEAVE("()"); +} + +void +CCaptureDevice:: +DispatchRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpRemove(Irp); + DBG_LEAVE("()"); +} + +void +CCaptureDevice:: +DispatchSurpriseRemoval( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpSurpriseRemoval(Irp); + DBG_LEAVE("()"); +} + +NTSTATUS +CCaptureDevice:: +DispatchQueryCapabilities( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)-> + PnpQueryCapabilities( Irp, Capabilities ); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + + +// Make Power callbacks non-pageable. +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +DispatchQueryPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action +) +{ + // If this device supports the the DO_POWER_INRUSH flag, this function + // can be called at DISPATCH. + + DBG_ENTER("()"); + NTSTATUS Status = + Recast(Device)-> + PnpQueryPower( + Irp, + DeviceTo, + DeviceFrom, + SystemTo, + SystemFrom, + Action ); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCaptureDevice:: +DispatchSetPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From +) +{ + // If this device supports the the DO_POWER_INRUSH flag, this function + // can be called at DISPATCH. + + DBG_ENTER("()"); + Recast(Device)-> + PnpSetPower( Irp, To, From ); + DBG_LEAVE("()"); +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +DispatchQueryInterface( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)-> + PnpQueryInterface(Irp); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + + +/*************************************************/ + + +NTSTATUS +CCaptureDevice:: +PnpStart ( + _In_ PCM_RESOURCE_LIST TranslatedResourceList, + _In_ PCM_RESOURCE_LIST UntranslatedResourceList +) + +/*++ + +Routine Description: + + Called at Pnp start. We start up our hardware simulation. + +Arguments: + + TranslatedResourceList - + The translated resource list from Pnp + + UntranslatedResourceList - + The untranslated resource list from Pnp + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + // + // Normally, we'd do things here like parsing the resource lists and + // connecting our interrupt. Since this is a simulation, there isn't + // much to parse. The parsing and connection should be the same as + // any WDM driver. The sections that will differ are illustrated below + // in setting up a simulated DMA. + // + + NTSTATUS Status = STATUS_SUCCESS; + + // + // By PnP, it's possible to receive multiple starts without an intervening + // stop (to reevaluate resources, for example). Thus, we only perform + // creations of the simulation on the initial start and ignore any + // subsequent start. Hardware drivers with resources should evaluate + // resources and make changes on 2nd start. + // + // Walk the list of descriptors and enumerate the h/w sims described. + // + + if (!m_Device -> Started) + { + // Create the Filter for the device + for( size_t i=0; i<m_FilterDescriptorCount; i++ ) + { + PKSFILTERFACTORY FilterFactory=nullptr; + IFFAILED_EXIT( + KsCreateFilterFactory( m_Device->FunctionalDeviceObject, + m_Context[i].Descriptor, + m_Context[i].Name, + NULL, + KSCREATE_ITEM_FREEONSTOP, + NULL, + NULL, + &FilterFactory ) + ); + + // Set primary camera to its interface, this allows device to be queried by GUID + if( FilterFactory ) + { + PKSDEVICE_PROFILE_INFO pDeviceProfiles = nullptr; + UINT32 uiProfileCount = 0; + PUNICODE_STRING SymbolicLinkName = KsFilterFactoryGetSymbolicLink(FilterFactory); + BOOL fFrontCamera = FALSE; + ACPI_PLD_V2_BUFFER pld = {0}; + pld.Revision = 2; + pld.Panel = m_Context[i].AcpiPosition; + pld.Rotation = m_Context[i].AcpiRotation; + static + const + GUID FFC_Filter = {STATIC_FrontCamera_Filter}; + + IFFAILED_EXIT( + IoSetDeviceInterfacePropertyData(SymbolicLinkName, + &DEVPKEY_Device_PhysicalDeviceLocation, + LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, + DEVPROP_TYPE_BINARY, + sizeof(ACPI_PLD_BUFFER), + (PVOID)&pld) + ); + + if( m_Context[i].Descriptor->ReferenceGuid && + IsEqualGUID(*(m_Context[i].Descriptor->ReferenceGuid), FFC_Filter)) + { + fFrontCamera = TRUE; + } + + // Publish our profile here. + uiProfileCount = InitializeDeviceProfiles(fFrontCamera, &pDeviceProfiles); + if( uiProfileCount > 0 ) + { + if( NT_SUCCESS(KsInitializeDeviceProfile(FilterFactory)) ) + { + for( UINT32 j=0; j<uiProfileCount; j++ ) + { + if( !NT_SUCCESS(KsPublishDeviceProfile(FilterFactory, &pDeviceProfiles[j])) ) + { + // Bail... + break; + } + } + if( !NT_SUCCESS(KsPersistDeviceProfile(FilterFactory)) ) + { + // Trace here? + } + } + + ExFreePool(pDeviceProfiles); + pDeviceProfiles = NULL; + } + } + + // On a real device this object would be constructed whenever the sensor hardware is ready... + m_Sensor[i] = CreateSensor( m_Context[i].Descriptor ); + if( !m_Sensor[i] ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + IFFAILED_EXIT(m_Sensor[i]->Initialize()); + + m_Sensor[i]->SetMountingOrientation( m_Context[i].AcpiRotation ); + } + } + +done: + if( !NT_SUCCESS(Status) ) + { + // Free any sensor objects we created during this failed attempt. + for( size_t i=0; i<m_FilterDescriptorCount; i++ ) + { + SAFE_DELETE( m_Sensor[i] ); + } + } + return Status; +} + +CSensor * +CCaptureDevice:: +CreateSensor( + _In_ const KSFILTER_DESCRIPTOR *Descriptors +) +{ + PAGED_CODE(); + + return new (NonPagedPoolNx, 'sneS') CSensorSimulation( this, Descriptors ); +} + +PDEVICE_OBJECT +CCaptureDevice:: +GetDeviceObject() +/*++ + +Routine Description: + + Return the pointer to the FDO for our device. + +Arguments: + + [none] + +Return Value: + + PDEVICE_OBJECT + +--*/ +{ + PAGED_CODE(); + + return m_Device->FunctionalDeviceObject; +} + +/*************************************************/ + + +void +CCaptureDevice:: +PnpStop ( + _In_ PIRP Irp +) + +/*++ + +Routine Description: + + This is the pnp stop dispatch for the capture device. It releases any + adapter object previously allocated by IoGetDmaAdapter during Pnp Start. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + UNREFERENCED_PARAMETER(Irp); + PAGED_CODE(); + + if (m_DmaAdapterObject) + { + // + // Return the DMA adapter back to the system. + // + m_DmaAdapterObject -> DmaOperations -> + PutDmaAdapter (m_DmaAdapterObject); + + m_DmaAdapterObject = NULL; + } +} + +NTSTATUS +CCaptureDevice:: +PnpPostStart() +{ + PAGED_CODE(); + return STATUS_SUCCESS; +} + +NTSTATUS +CCaptureDevice:: +PnpQueryStop( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + return STATUS_SUCCESS; +} + +void +CCaptureDevice:: +PnpCancelStop( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +NTSTATUS +CCaptureDevice:: +PnpQueryRemove( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + return STATUS_SUCCESS; +} + +void +CCaptureDevice:: +PnpCancelRemove( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +void +CCaptureDevice:: +PnpRemove( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +void +CCaptureDevice:: +PnpSurpriseRemoval( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +NTSTATUS +CCaptureDevice:: +PnpQueryCapabilities( + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(Capabilities); + return STATUS_SUCCESS; +} + +// Make Power callbacks non-pageable. +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +PnpQueryPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action +) +{ + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(DeviceTo); + UNREFERENCED_PARAMETER(DeviceFrom); + UNREFERENCED_PARAMETER(SystemTo); + UNREFERENCED_PARAMETER(SystemFrom); + UNREFERENCED_PARAMETER(Action); + return STATUS_SUCCESS; +} + +void +CCaptureDevice:: +PnpSetPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From +) +{ + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(To); + UNREFERENCED_PARAMETER(From); +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +PnpQueryInterface( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + return STATUS_SUCCESS; +} + +/*************************************************/ + diff --git a/AVStream/avscamera/sys/Device.h b/AVStream/avscamera/sys/Device.h new file mode 100644 index 00000000..6a3133d6 --- /dev/null +++ b/AVStream/avscamera/sys/Device.h @@ -0,0 +1,450 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + device.h + + Abstract: + + This file contains the declaration of CCaptureDevice. + + CCaptureDevice is the base device class object. It handles basic PnP + requests and constructs the filter factories. + + History: + + created 3/9/2001 + +**************************************************************************/ + +// +// Forward references... +// +class CSensor; + +struct CSensorContext +{ + PWSTR Name; // What to call this class of filter. + const KSFILTER_DESCRIPTOR *Descriptor; // The description of the filter. + AcpiPldPanel AcpiPosition; // Where the device is located. + AcpiPldRotation AcpiRotation; // How the device is oriented with respect to the panel. +}; + +class CCaptureDevice +{ +protected: + + // + // The AVStream device we're associated with. + // + PKSDEVICE m_Device; + + // + // Since we don't have physical hardware, this provides the hardware + // simulation. m_HardwareSimulation provides the fake ISR, fake DPC, + // etc... m_Synthesizer provides RGB24 and UYVY image synthesis and + // overlay in software. + // + + // + // Number of pins with resources acquired. This is used as a locking + // mechanism for resource acquisition on the device. + // + + // The capture pin. When we complete scatter / gather mappings, we + // notify the capture pin. + // + + // Number of Filter descriptors & filter factories. + size_t m_FilterDescriptorCount; + + // Pointer to an array of filter descriptor pointers. + // Typically it's one sensor for each filter factory. + CSensorContext *m_Context; + + // The sensor state for each camera. + CSensor **m_Sensor; + + // + // The Dma adapter object we acquired through IoGetDmaAdapter() during + // Pnp start. This must be initialized with AVStream in order to perform + // Dma directly into the capture buffers. + // + PADAPTER_OBJECT m_DmaAdapterObject; + + // + // The number of map registers returned from IoGetDmaAdapter(). + // + ULONG m_NumberOfMapRegisters; + + // + // Cleanup(): + // + // This is the free callback for the bagged capture device. Not providing + // one will call ExFreePool, which is not what we want for a constructed + // C++ object. This simply deletes the capture device. + // + static + void + Cleanup ( + _In_ CCaptureDevice *CapDevice + ) + { + delete CapDevice; + } + + // + // Prepare(): + // + // Called by CCaptureDevice::DispatchCreate to finish AddDevice processing, + // once the object is created. + // + // This is a seperate helper function to allow the derived DispatchCreate + // implementation to be as simple as is feasible. + // + NTSTATUS + Prepare(); + +public: + // + // DispatchCreate(): + // + // This is the Add Device dispatch for the capture device. It creates + // the CCaptureDevice and associates it with the device via the bag. + // + static + NTSTATUS + DispatchCreate ( + _In_ PKSDEVICE Device + ); + + // + // DispatchStart(): + // + // This is the Pnp Start dispatch for the capture device. It simply + // bridges to PnpStart() in the context of the CCaptureDevice. + // + static + NTSTATUS + DispatchStart ( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ PCM_RESOURCE_LIST TranslatedResourceList, + _In_ PCM_RESOURCE_LIST UntranslatedResourceList + ); + + // + // DispatchStop(): + // + // This is the Pnp stop dispatch for the capture device. It simply + // bridges to PnpStop() in the context of the CCaptureDevice. + // + static + void + DispatchStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + NTSTATUS + DispatchPostStart( + _In_ PKSDEVICE Device + ); + + static + NTSTATUS + DispatchQueryStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchCancelStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + NTSTATUS + DispatchQueryRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchCancelRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchSurpriseRemoval( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + NTSTATUS + DispatchQueryCapabilities( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities + ); + + static + NTSTATUS + DispatchQueryPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action + ); + + static + void + DispatchSetPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From + ); + + static + NTSTATUS + DispatchQueryInterface( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + +protected: + // + // PnpStart(): + // + // This is the Pnp start routine for our simulated hardware. Note that + // DispatchStart bridges to here in the context of the CCaptureDevice. + // + virtual + NTSTATUS + PnpStart ( + _In_ PCM_RESOURCE_LIST TranslatedResourceList, + _In_ PCM_RESOURCE_LIST UntranslatedResourceList + ); + + // + // PnpStop(): + // + // This is the Pnp stop routine for our simulated hardware. Note that + // DispatchStop bridges to here in the context of the CCaptureDevice. + // + virtual + void + PnpStop ( + _In_ PIRP Irp + ); + + virtual + NTSTATUS + PnpPostStart(); + + virtual + NTSTATUS + PnpQueryStop( + _In_ PIRP Irp + ); + + virtual + void + PnpCancelStop( + _In_ PIRP Irp + ); + + virtual + NTSTATUS + PnpQueryRemove( + _In_ PIRP Irp + ); + + virtual + void + PnpCancelRemove( + _In_ PIRP Irp + ); + + virtual + void + PnpRemove( + _In_ PIRP Irp + ); + + virtual + void + PnpSurpriseRemoval( + _In_ PIRP Irp + ); + + virtual + NTSTATUS + PnpQueryCapabilities( + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities + ); + + virtual + NTSTATUS + PnpQueryPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action + ); + + virtual + void + PnpSetPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From + ); + + virtual + NTSTATUS + PnpQueryInterface( + _In_ PIRP Irp + ); + +protected: + virtual + CSensor * + CreateSensor( + _In_ const KSFILTER_DESCRIPTOR *Descriptors + ); + +public: + // + // CCaptureDevice(): + // + // The capture device class constructor. Since everything should have + // been zero'ed by the new operator, don't bother setting anything to + // zero or NULL. Only initialize non-NULL, non-0 fields. + // + CCaptureDevice ( + _In_ PKSDEVICE Device + ); + + // + // ~CCaptureDevice(): + // + // The capture device destructor. + // + virtual + ~CCaptureDevice(); + + virtual + NTSTATUS + Initialize(); + + // + // Recast(): + // + // Helper function. Used to convert from a PKSDEVICE to a CCaptureDevice * + // + static + inline + CCaptureDevice * + Recast( + _In_ PKSDEVICE Device + ) + { + return reinterpret_cast <CCaptureDevice *> (Device -> Context); + } + + // + // QueryInterruptTime(): + // + // Determine the frame number that this frame corresponds to. + // + ULONG + QueryInterruptTime (LONG ID, + _In_ PKSPIN Pin + ); + + virtual + PDEVICE_OBJECT + GetDeviceObject(); + + PKSDEVICE + GetKsDevice() + { + return m_Device; + } + + ULONG + GetFilterIndex(PKSFILTER Filter); + + ULONG + GetFilterCount() + { + return (ULONG) m_FilterDescriptorCount; + } + + PCWSTR + GetFilterName(ULONG FilterIndex) + { + return (FilterIndex < m_FilterDescriptorCount) ? m_Context[FilterIndex].Name : nullptr; + } + + CSensor * + GetSensor( PKSFILTER Filter ) + { + ULONG Index = GetFilterIndex(Filter); + + return ( Index < m_FilterDescriptorCount ) ? m_Sensor[ Index ] : nullptr; + } + +}; + +// +// Define a dispatch table for a capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +#define DEFINE_CAMERA_KSDEVICE_DISPATCH( Table, Class ) \ +const \ +KSDEVICE_DISPATCH \ +Table = \ +{ \ + Class::DispatchCreate, /* Pnp Add Device */ \ + Class::DispatchStart, /* Pnp Start */ \ + Class::DispatchPostStart, /* Post-Start */ \ + Class::DispatchQueryStop, /* Pnp Query Stop */ \ + Class::DispatchCancelStop, /* Pnp Cancel Stop */ \ + Class::DispatchStop, /* Pnp Stop */ \ + Class::DispatchQueryRemove, /* Pnp Query Remove */ \ + Class::DispatchCancelRemove, /* Pnp Cancel Remove */ \ + Class::DispatchRemove, /* Pnp Remove */ \ + Class::DispatchQueryCapabilities, /* Pnp Query Capabilities */ \ + Class::DispatchSurpriseRemoval, /* Pnp Surprise Removal */ \ + Class::DispatchQueryPower, /* Power Query Power */ \ + Class::DispatchSetPower, /* Power Set Power */ \ + Class::DispatchQueryInterface /* Pnp Query Interface */ \ +}; + diff --git a/AVStream/avscamera/sys/ExtendedCameraAngleOffset.h b/AVStream/avscamera/sys/ExtendedCameraAngleOffset.h new file mode 100644 index 00000000..dca18ee1 --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedCameraAngleOffset.h @@ -0,0 +1,67 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedCameraAngleOffset.h + + Abstract: + + This file contains the helper class CExtendedCameraAngleOffset. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_CAMERAOFFSET. + + History: + + created 11/26/2014 + +**************************************************************************/ + +// +// Helper class for Extended Camera Angle Offset. +// +class CExtendedCameraAngleOffset + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_CAMERAOFFSET m_Params; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedCameraAngleOffset( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + CExtendedCameraAngleOffset( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + // Access functions let you more easily fetch the embedded field. + LONG & + PitchAngle() + { + return m_Params.PitchAngle; + } + + LONG & + YawAngle() + { + return m_Params.YawAngle; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/AVStream/avscamera/sys/ExtendedEVComp.h b/AVStream/avscamera/sys/ExtendedEVComp.h new file mode 100644 index 00000000..31f7916b --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedEVComp.h @@ -0,0 +1,105 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedEvComp.h + + Abstract: + + This file contains the helper class CExtendedEvCompensation. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_EVCOMPENSATION. + + History: + + created 6/3/2014 + +**************************************************************************/ + +// +// Helper class for Extended Properties. +// +class CExtendedEvCompensation + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_EVCOMPENSATION m_EvComp; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedEvCompensation( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + :CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_EvComp, sizeof(m_EvComp) ); + } + + CExtendedEvCompensation( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_EvComp, sizeof(m_EvComp) ); + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } + + // Access operators let you fetch the value from the value field. + operator LONG() + { + return m_EvComp.Value; + } + + // Assignment operators let you assign a value to the property. + CExtendedEvCompensation & + operator =( LONG x ) + { + Value() = x; + return *this; + } + + CExtendedEvCompensation & + operator =( const KSCAMERA_EXTENDEDPROP_EVCOMPENSATION &x ) + { + m_EvComp = x; + return *this; + } + + LONG & + Min() + { + return m_EvComp.Min; + } + + LONG & + Max() + { + return m_EvComp.Max; + } + + ULONG & + Mode() + { + return m_EvComp.Mode; + } + + LONG & + Value() + { + return m_EvComp.Value; + } + + LONG & + GetLONG() + { + return m_EvComp.Value; + } +}; + diff --git a/AVStream/avscamera/sys/ExtendedFieldOfView.h b/AVStream/avscamera/sys/ExtendedFieldOfView.h new file mode 100644 index 00000000..388356a6 --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedFieldOfView.h @@ -0,0 +1,67 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedFieldOfView.h + + Abstract: + + This file contains the helper class CExtendedFieldOfView. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_FIELDOFVIEW. + + History: + + created 11/26/2014 + +**************************************************************************/ + +// +// Helper class for Extended Field of View. +// +class CExtendedFieldOfView + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_FIELDOFVIEW m_Params; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedFieldOfView( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + CExtendedFieldOfView( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + // Access functions let you more easily fetch the embedded field. + ULONG & + NormalizedFocalLengthX() + { + return m_Params.NormalizedFocalLengthX; + } + + ULONG & + NormalizedFocalLengthY() + { + return m_Params.NormalizedFocalLengthY; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/AVStream/avscamera/sys/ExtendedMaxVideoFpsForPhotoRes.h b/AVStream/avscamera/sys/ExtendedMaxVideoFpsForPhotoRes.h new file mode 100644 index 00000000..29013c82 --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedMaxVideoFpsForPhotoRes.h @@ -0,0 +1,91 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedMaxVideoFpsForPhotoRes.h + + Abstract: + + This file contains the helper class CExtendedMaxVideoFpsForPhotoRes. + The class is provides initialization and accessor functions for + KSCAMERA_MAXVIDEOFPS_FORPHOTORES. + + History: + + created 11/26/2014 + +**************************************************************************/ + +// +// Helper class for Extended Max Video Fps for Photo Resolution. +// +class CExtendedMaxVideoFpsForPhotoRes + : public CExtendedHeader +{ +public: + KSCAMERA_MAXVIDEOFPS_FORPHOTORES m_Params; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedMaxVideoFpsForPhotoRes( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + CExtendedMaxVideoFpsForPhotoRes( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + // Access functions let you more easily fetch the embedded field. + ULONG & + PhotoResWidth() + { + return m_Params.PhotoResWidth; + } + + ULONG & + PhotoResHeight() + { + return m_Params.PhotoResHeight; + } + + ULONG & + PreviewFPSNum() + { + return m_Params.PreviewFPSNum; + } + + ULONG & + PreviewFPSDenom() + { + return m_Params.PreviewFPSDenom; + } + + ULONG & + CaptureFPSNum() + { + return m_Params.CaptureFPSNum; + } + + ULONG & + CaptureFPSDenom() + { + return m_Params.CaptureFPSDenom; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/AVStream/avscamera/sys/ExtendedMetadata.h b/AVStream/avscamera/sys/ExtendedMetadata.h new file mode 100644 index 00000000..27ef4a79 --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedMetadata.h @@ -0,0 +1,68 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedMetadata.h + + Abstract: + + This file contains the helper class CExtendedMetadata. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_METADATAINFO. + + History: + + created 6/20/2014 + +**************************************************************************/ + +// +// Helper class for Extended Metadata. +// +class CExtendedMetadata + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_METADATAINFO m_Info; + + // Maximum metadata required ... except for Histogram + static const ULONG METADATA_MAX=4096; + + // Standardized ctors to help ensure a consistant definition. + CExtendedMetadata( + _In_ ULONG pinId=0, // Just so we have a default ctor. + _In_ ULONG size=METADATA_MAX, + _In_ ULONG align=KSCAMERA_EXTENDEDPROP_MetadataAlignment_32, + _In_ ULONGLONG flags=(KSCAMERA_EXTENDEDPROP_METADATA_ALIGNMENTREQUIRED | KSCAMERA_EXTENDEDPROP_METADATA_SYSTEMMEMORY), + _In_ ULONG result=STATUS_SUCCESS + ) + : CExtendedHeader( flags, result ) + { + PinId = pinId; + Size = sizeof(*this); + m_Info.BufferAlignment = align; + m_Info.MaxMetadataBufferSize= size; + } + + // Used to modify the metadata allocation size. + CExtendedMetadata & + operator =( ULONG x ) + { + m_Info.MaxMetadataBufferSize = x; + return *this; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size == sizeof(*this)) && + ((Flags & ~(KSCAMERA_EXTENDEDPROP_METADATA_SYSTEMMEMORY | KSCAMERA_EXTENDEDPROP_METADATA_ALIGNMENTREQUIRED)) == 0) && // Only valid flags + ((m_Info.BufferAlignment-1) & m_Info.BufferAlignment); // Not more than 1 bit set. + } + +}; + diff --git a/AVStream/avscamera/sys/ExtendedPhotoMode.h b/AVStream/avscamera/sys/ExtendedPhotoMode.h new file mode 100644 index 00000000..daa7c127 --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedPhotoMode.h @@ -0,0 +1,73 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedPhotoMode.h + + Abstract: + + This file contains the helper class CExtendedPhotoMode. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_PHOTOMODE. + + History: + + created 11/20/2014 + +**************************************************************************/ + +// +// Helper class for Extended Photo Mode. +// +class CExtendedPhotoMode + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_PHOTOMODE m_PhotoMode; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedPhotoMode( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_PhotoMode, sizeof(m_PhotoMode) ); + } + + CExtendedPhotoMode( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_PhotoMode, sizeof(m_PhotoMode) ); + } + + // Access functions let you more easily fetch the embedded field. + ULONG & + RequestedHistoryFrames() + { + return m_PhotoMode.RequestedHistoryFrames; + } + + ULONG & + MaxHistoryFrames() + { + return m_PhotoMode.MaxHistoryFrames; + } + + ULONG & + SubMode() + { + return m_PhotoMode.SubMode; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/AVStream/avscamera/sys/ExtendedProperty.h b/AVStream/avscamera/sys/ExtendedProperty.h new file mode 100644 index 00000000..32affaa6 --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedProperty.h @@ -0,0 +1,142 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + ExtendedProperty.h + + Abstract: + + This file contains the helper class CExtendedProperty and the coommon + base class CExtendedHeader. + + CExtendedHeader provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_HEADER. + + CExtendedProperty provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_VALUE. + + CExtendedProperty also combines KSCAMERA_EXTENDEDPROP_HEADER and + KSCAMERA_EXTENDEDPROP_VALUE into a single object for use in the + macros and templates used by our filter and sensor classes. + + History: + + created 5/21/2013 + +**************************************************************************/ + +// +// Helper class for Extended Properties. +// +class CExtendedHeader + : public KSCAMERA_EXTENDEDPROP_HEADER +{ +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedHeader( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + { + Version = KSCAMERA_EXTENDEDPROP_VERSION; + PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + Size = sizeof(*this); + Result = result; + Capability = 0; + Flags = flags; + } + + CExtendedHeader( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : KSCAMERA_EXTENDEDPROP_HEADER(hdr) + { + Size = sizeof(*this); + } + + bool isValid() + { + return Version == KSCAMERA_EXTENDEDPROP_VERSION; + } +}; + +class CExtendedProperty + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_VALUE m_Value; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedProperty( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + m_Value.Value.ull = 0; + } + + CExtendedProperty( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + m_Value.Value.ull = 0; + } + + // Access operators let you fetch the value from the value field. + operator LONG() + { + return m_Value.Value.l; + } + operator ULONG() + { + return m_Value.Value.ul; + } + operator LONGLONG() + { + return m_Value.Value.ll; + } + operator ULONGLONG() + { + return m_Value.Value.ull; + } + operator KSCAMERA_EXTENDEDPROP_VALUE() + { + return m_Value; + } + + // Assignment operators let you assign a value to the property. + CExtendedProperty & + operator =( LONG x ) + { + m_Value.Value.l = x; + return *this; + } + + CExtendedProperty & + operator =( LONGLONG x ) + { + m_Value.Value.ll = x; + return *this; + } + + CExtendedProperty & + operator =( ULONG x ) + { + m_Value.Value.ul = x; + return *this; + } + + CExtendedProperty & + operator =( ULONGLONG x ) + { + m_Value.Value.ull = x; + return *this; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } + +}; + diff --git a/AVStream/avscamera/sys/ExtendedVidProcSetting.h b/AVStream/avscamera/sys/ExtendedVidProcSetting.h new file mode 100644 index 00000000..11c22caa --- /dev/null +++ b/AVStream/avscamera/sys/ExtendedVidProcSetting.h @@ -0,0 +1,164 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + ExtendedProperty.h + + Abstract: + + This file contains the helper class CExtendedVidProcSetting. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING. + + History: + + created 5/21/2013 + +**************************************************************************/ + +// +// Helper class for Extended Properties KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING. +// +class CExtendedVidProcSetting + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING m_Setting; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedVidProcSetting( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Setting, sizeof(m_Setting) ); + } + + CExtendedVidProcSetting( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Setting, sizeof(m_Setting) ); + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } + + template<class T> + NTSTATUS + BoundsCheck( T Value ) + { + return ::BoundsCheck( Value, m_Setting ); + } + + // Access operators let you fetch the value from the value field. + operator LONG() + { + return m_Setting.VideoProc.Value.l; + } + operator ULONG() + { + return m_Setting.VideoProc.Value.ul; + } + operator LONGLONG() + { + return m_Setting.VideoProc.Value.ll; + } + operator ULONGLONG() + { + return m_Setting.VideoProc.Value.ull; + } + + // Assignment operators let you assign a value to the property. + CExtendedVidProcSetting & + operator =( LONG x ) + { + m_Setting.VideoProc.Value.l = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( LONGLONG x ) + { + m_Setting.VideoProc.Value.ll = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( ULONG x ) + { + m_Setting.VideoProc.Value.ul = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( ULONGLONG x ) + { + m_Setting.VideoProc.Value.ull = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( const KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING &x ) + { + m_Setting = x; + return *this; + } + + LONG & + Min() + { + return m_Setting.Min; + } + + LONG & + Max() + { + return m_Setting.Max; + } + + LONG & + Step() + { + return m_Setting.Step; + } + + ULONG & + Mode() + { + return m_Setting.Mode; + } + + LONG & + GetLONG() + { + return m_Setting.VideoProc.Value.l; + } + + LONGLONG & + GetLONGLONG() + { + return m_Setting.VideoProc.Value.ll; + } + + ULONG & + GetULONG() + { + return m_Setting.VideoProc.Value.ul; + } + + ULONGLONG & + GetULONGLONG() + { + return m_Setting.VideoProc.Value.ull; + } + +}; + diff --git a/AVStream/avscamera/sys/Mutex.cpp b/AVStream/avscamera/sys/Mutex.cpp new file mode 100644 index 00000000..1dd3d88c --- /dev/null +++ b/AVStream/avscamera/sys/Mutex.cpp @@ -0,0 +1,70 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Mutex.cpp + + Abstract: + + This file provides the implementation of KMutex and KScopedMutex. + + KMutex provides a wrapper around a KMUTEX object. KScopedMutex is + provided as a helper class to ensure all exits release the lock. + + History: + + created 7/16/2013 + +**************************************************************************/ + +#include "Common.h" + +// +// Dummy dtor. Required because the dtor is virtual. +// +KMutex::~KMutex() +{} + +// +// Preferred lock method. +// +_IRQL_requires_min_(PASSIVE_LEVEL) +_When_((Timeout==NULL || Timeout->QuadPart!=0), _IRQL_requires_max_(APC_LEVEL)) +_When_((Timeout!=NULL &&Timeout->QuadPart==0), _IRQL_requires_max_(DISPATCH_LEVEL)) +NTSTATUS +KMutex::Lock( + _In_opt_ PLARGE_INTEGER Timeout +) +{ + //DBG_ENTER(": Thread Owner = %p", m_Lock.OwnerThread); + + NTSTATUS status = + KeWaitForSingleObject( + &m_Lock, + Executive, + KernelMode, + FALSE, + Timeout ); + + //DBG_LEAVE(": Status = %d Thread Owner = %p", status, m_Lock.OwnerThread); + + return status; +} + +// +// Preferred unlock method. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +void +KMutex::Unlock() +{ + //DBG_ENTER(": Thread Owner = %p", m_Lock.OwnerThread); + + KeReleaseMutex( &m_Lock, FALSE ); + + //DBG_LEAVE(": Thread Owner = %p", m_Lock.OwnerThread); +} diff --git a/AVStream/avscamera/sys/Mutex.h b/AVStream/avscamera/sys/Mutex.h new file mode 100644 index 00000000..a8157da5 --- /dev/null +++ b/AVStream/avscamera/sys/Mutex.h @@ -0,0 +1,81 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Mutex.h + + Abstract: + + This file provides the declaration of KMutex and KScopedMutex. + + KMutex provides a wrapper around a KMUTEX object. KScopedMutex is + provided as a helper class to ensure all exits release the lock. + + History: + + created 7/16/2013 + +**************************************************************************/ +#pragma once + +// +// Specialized class for KMUTEX +// +class KMutex : CNonCopyable +{ + KMUTEX m_Lock; + +public: + KMutex() + { + KeInitializeMutex( &m_Lock, 0 ); + } + + virtual ~KMutex(); + + // + // Preferred lock method. + // + _IRQL_requires_min_(PASSIVE_LEVEL) + _When_((Timeout==NULL || Timeout->QuadPart!=0), _IRQL_requires_max_(APC_LEVEL)) + _When_((Timeout!=NULL &&Timeout->QuadPart==0), _IRQL_requires_max_(DISPATCH_LEVEL)) + NTSTATUS + Lock( + _In_opt_ PLARGE_INTEGER Timeout=nullptr + ); + + // + // Preferred unlock method. + // + _IRQL_requires_max_(DISPATCH_LEVEL) + void + Unlock(); +}; + +// +// Use this class to hold a spinlock through a particular scope +// +class KScopedMutex : CNonCopyable +{ + KMutex &m_Lock; + +public: + _IRQL_requires_min_(PASSIVE_LEVEL) + _IRQL_requires_max_(APC_LEVEL) + KScopedMutex( KMutex &lock ) + : m_Lock(lock) + { + m_Lock.Lock(); + } + + _IRQL_requires_max_(DISPATCH_LEVEL) + ~KScopedMutex(void) + { + m_Lock.Unlock(); + } +}; + diff --git a/AVStream/avscamera/sys/NV12Synthesizer.cpp b/AVStream/avscamera/sys/NV12Synthesizer.cpp new file mode 100644 index 00000000..2cdbf71b --- /dev/null +++ b/AVStream/avscamera/sys/NV12Synthesizer.cpp @@ -0,0 +1,183 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + CNV12Synthesizer.h + + Abstract: + + This file contains the implementation of CNV12Synthesizer. + + CNV12Synthesizer is derived from CYUVSynthesizer. It uses the YUV color + space and defines a Commit() function that reformats pixels into the + NV12 format. From MSDN: + + A format in which all Y samples are found first in memory as an + array of unsigned char with an even number of lines (possibly with + a larger stride for memory alignment). This is followed immediately + by an array of unsigned char containing interleaved Cb and Cr + samples. If these samples are addressed as a little-endian WORD + type, Cb would be in the least significant bits and Cr would be in + the most significant bits with the same total stride as the Y + samples. NV12 is the preferred 4:2:0 pixel format. + + A visual representation of the layout: + YYYY + YYYY + UVUV + + + History: + created 4/14/2013 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:26019 ) +_Success_(return > 0) +ULONG +CNV12Synthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + // We assume the output stride is the same as the original width here. + UNREFERENCED_PARAMETER(Stride); + + // The code actually handles this gracefully. It rounds down. + NT_ASSERT( (m_Width&1) == 0 ); + NT_ASSERT( (m_Height&1) == 0 ); + + // These are impossible conditions. + if( !( Buffer && + m_Buffer && + (Size%6)==0 && // Should be evenly divisible by 1 macropixel + Size>=6 // At least 1 macropixel + ) ) + { + NT_ASSERT(FALSE); + return 0; + } + + ULONG MacroPixelsWide = m_Width/2; + + // Impossible. + if( MacroPixelsWide==0 ) + { + NT_ASSERT(FALSE); + return 0; + } + + // At most, 2/3rds of the available space is used by the Y plane. + // Notice that we limit the number of macro pixel rows to what will + // fit in the space available, no matter what is in the original. + ULONG Y_limit = (Size /3) * 2; + ULONG MacroPixelsHigh = min( m_Height, Y_limit/m_Width)/2; + + // Impossible. + if( MacroPixelsHigh==0 ) + { + NT_ASSERT(FALSE); + return 0; + } + + // Now we work back to arrive at the Y & UV plane sizes. + ULONG UV_size = MacroPixelsWide * MacroPixelsHigh *2; + ULONG Y_size = MacroPixelsWide * MacroPixelsHigh *4; + ULONG YUV_size = Y_size + UV_size; + + // Impossible. + if( Size < YUV_size ) + { + NT_ASSERT(FALSE); + return 0; + } + + PWORD pY = (PWORD) Buffer; + PWORD pUV = (PWORD) (Buffer + Y_size); + + for(ULONG row = 0; row < MacroPixelsHigh; row++) + { + PKS_RGBQUAD pSrc = (PKS_RGBQUAD) GetImageLocation(0, row*2); + for(ULONG col = 0; col < MacroPixelsWide; col++) + { + KS_RGBQUAD TL = pSrc[0]; + KS_RGBQUAD TR = pSrc[1]; + KS_RGBQUAD BL = pSrc[m_Width+0]; + KS_RGBQUAD BR = pSrc[m_Width+1]; + pSrc += 2; + + // Copy luma first + pY[0] = MAKEWORD(TL.rgbGreen, TR.rgbGreen); + pY[MacroPixelsWide] = MAKEWORD(BL.rgbGreen, BR.rgbGreen); + pY++; + + LONG + tU = TL.rgbBlue; //top left + tU += BL.rgbBlue; //bottom left + tU += TR.rgbBlue; //top right + tU += BR.rgbBlue; //bottom right + + LONG + tV = TL.rgbRed; //top left + tV += BL.rgbRed; //bottom left + tV += TR.rgbRed; //top right + tV += BR.rgbRed; //bottom right + + // Copy decimated chroma + *pUV++ = MAKEWORD(((tU+2)>>2), ((tV+2)>>2)); + } + // Skip a scan line, since we've already copied it. + pY += MacroPixelsWide; + } + + return (ULONG) (((PUCHAR) pUV) - Buffer); +} +// suppressed due to Esp:773 +#pragma warning (pop) + diff --git a/AVStream/avscamera/sys/NV12Synthesizer.h b/AVStream/avscamera/sys/NV12Synthesizer.h new file mode 100644 index 00000000..27bc6ccb --- /dev/null +++ b/AVStream/avscamera/sys/NV12Synthesizer.h @@ -0,0 +1,73 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + CNV12Synthesizer.h + + Abstract: + + This file defines a class that implements NV12 image synthesis. + + CNV12Synthesis is derived from CYUVSynthesis. It uses the YUV color + space and defines a Commit() function that reformats pixels into the + NV12 format. From MSDN: + + A format in which all Y samples are found first in memory as an + array of unsigned char with an even number of lines (possibly with + a larger stride for memory alignment). This is followed immediately + by an array of unsigned char containing interleaved Cb and Cr + samples. If these samples are addressed as a little-endian WORD + type, Cb would be in the least significant bits and Cr would be in + the most significant bits with the same total stride as the Y + samples. NV12 is the preferred 4:2:0 pixel format. + + A visual representation of the layout: + YYYY + YYYY + UVUV + + + History: + created 4/16/2013 + +**************************************************************************/ + +#pragma once + +class CNV12Synthesizer : + public CYUVSynthesizer +{ +public: + // + // DEFAULT CONSTRUCTOR + // + CNV12Synthesizer ( + ULONG Width=0, + ULONG Height=0 + ) : + CYUVSynthesizer("NV12", Width, Height) + { + NT_ASSERT( ( Width % 2 ) == 0 ); + NT_ASSERT( ( Height % 2 ) == 0 ); + } + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); +};
\ No newline at end of file diff --git a/AVStream/avscamera/sys/NonCopyable.h b/AVStream/avscamera/sys/NonCopyable.h new file mode 100644 index 00000000..98d7bad4 --- /dev/null +++ b/AVStream/avscamera/sys/NonCopyable.h @@ -0,0 +1,48 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + NonCopyable.h + + Abstract: + + Simple base class that hides the copy ctor and assignment operators. + + Derive from this class any time its not safe (or reasonable) to + permit copies of an object. + + History: + + created 5/8/2013 + +**************************************************************************/ +#pragma once + +// +// General base class for noncopyable objects +// +class CNonCopyable +{ +protected: + CNonCopyable() {} + ~CNonCopyable() {} + +private: + CNonCopyable( + _In_ const CNonCopyable & + ) + {} + + const CNonCopyable & + operator =( + _In_ const CNonCopyable & + ) + { + return *this; + } +}; + diff --git a/AVStream/avscamera/sys/PreviewHwSim.cpp b/AVStream/avscamera/sys/PreviewHwSim.cpp new file mode 100644 index 00000000..dfe2dc1a --- /dev/null +++ b/AVStream/avscamera/sys/PreviewHwSim.cpp @@ -0,0 +1,263 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + PreviewHwSim.cpp + + Abstract: + + This file contains the implementation of the CPreviewHardwareSimulation + class. + + This is a specialization of CHardwareSimulation that provides preview- + specific metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CPreviewHardwareSimulation::CPreviewHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : CHardwareSimulation( Sensor, PinID ) +{ + PAGED_CODE(); +} + + +CPreviewHardwareSimulation::~CPreviewHardwareSimulation() +{ + PAGED_CODE(); +} + +void +CPreviewHardwareSimulation:: +FakeHardware() +/*++ + +Routine Description: + + Simulate an interrupt and what the hardware would have done in the + time since the previous interrupt. + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + // Do normal timer handling. + CHardwareSimulation::FakeHardware(); + + // Update the zoom. + m_Sensor->UpdateZoom(); +} + +// +// Helper function that collects the current ISP settings into our metadata +// structure. +// +METADATA_PREVIEWAGGREGATION +CPreviewHardwareSimulation:: +GetMetadata() +{ + PAGED_CODE(); + + METADATA_PREVIEWAGGREGATION Metadata; + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + // Wipe the metadata so all settings will default to "Not Set". + RtlZeroMemory( &Metadata, sizeof(Metadata) ); + + // FocusState; + m_Sensor->GetFocusState( (KSCAMERA_EXTENDEDPROP_FOCUSSTATE *) &Metadata.FocusState ); + + // ExposureTime; + Metadata.ExposureTime = + CMetadataLongLong( GetCurrentExposureTime() ); + + // EVCompensation; + Metadata.EVCompensation = CMetadataEVCompensation(pSettings->EVCompensation.Mode, pSettings->EVCompensation.Value); + + // ISOSpeed; + Metadata.ISOSpeed = CMetadataLong(GetCurrentISOSpeed()); + DBG_TRACE("ISO=%d, ISO Flags=0x%016llX", Metadata.ISOSpeed.Value, pSettings->ISOMode); + + // LensPosition; + Metadata.LensPosition = CMetadataLong( pSettings->FocusSetting.VideoProc.Value.ul ); + + // FlashOn; + Metadata.FlashOn = CMetadataLong((ULONG) pSettings->FlashMode); + + // WhiteBalanceMode; + Metadata.WhiteBalanceMode = CMetadataLong((ULONG) pSettings->WhiteBalanceMode); + + // IsoGains; + Metadata.IsoAnalogGain = + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + Metadata.IsoDigitalGain = + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + + // SensorFrameRate; + ULARGE_INTEGER FrameRate; + FrameRate.LowPart = 60 * 60 *24; + FrameRate.HighPart = (ULONG) ((ONESECOND * FrameRate.LowPart) / m_TimePerFrame); // compute number of frames per day. + Metadata.SensorFrameRate = CMetadataULongLong( FrameRate.QuadPart ); + + // WhiteBalanceGains; + Metadata.WhiteBalanceGain_R = // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + Metadata.WhiteBalanceGain_G = // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + Metadata.WhiteBalanceGain_B = // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + + return Metadata; +} + +// +// Emit metadata here for preview pin. +// +void +CPreviewHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +/*++ + +Routine Description: + + Emit metadata for a photo. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + + // Add the normal frame info to the metadata + // Note: This call ensures the KSSTREAM_METADATA_INFO is properly initialized. + CHardwareSimulation::EmitMetadata( pStreamHeader ); + + 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; + + if(m_PhotoConfirmationEntry.isRequired()) + { + if(BytesLeft < sizeof(KSCAMERA_METADATA_PHOTOCONFIRMATION)) + { + //Fatal driver error, should never come here. + NT_ASSERT(FALSE); + return; + } + PKSCAMERA_METADATA_PHOTOCONFIRMATION pPhotoConfirmation = + (PKSCAMERA_METADATA_PHOTOCONFIRMATION) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize); + pPhotoConfirmation->Header.MetadataId = MetadataId_PhotoConfirmation; + pPhotoConfirmation->Header.Size = sizeof(*pPhotoConfirmation); + pPhotoConfirmation->PhotoConfirmationIndex = m_PhotoConfirmationEntry.getIndex(); + pMetadata->UsedSize += sizeof(*pPhotoConfirmation); + BytesLeft -= sizeof(*pPhotoConfirmation); + + DBG_TRACE("PhotoConfirmation Header.MetadataId = %u", pPhotoConfirmation->Header.MetadataId); + DBG_TRACE("PhotoConfirmation Header.Size = %u", pPhotoConfirmation->Header.Size); + DBG_TRACE("PhotoConfirmation Index = %u", pPhotoConfirmation->PhotoConfirmationIndex); + } + else + { + DBG_TRACE("Normal frame; no photo confirmation metadata."); + } + + if( BytesLeft >= sizeof(CAMERA_METADATA_PREVIEWAGGREGATION) ) + { + PCAMERA_METADATA_PREVIEWAGGREGATION pPreviewAggregation = + (PCAMERA_METADATA_PREVIEWAGGREGATION) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize); + pPreviewAggregation->Header.MetadataId = (ULONG) MetadataId_Custom_PreviewAggregation; + pPreviewAggregation->Header.Size = sizeof(*pPreviewAggregation); + pPreviewAggregation->Data = GetMetadata(); + pMetadata->UsedSize += sizeof(*pPreviewAggregation); + BytesLeft -= sizeof(*pPreviewAggregation); + } + + // MF_CAPTURE_METADATA_HISTOGRAM + // + // Generate the entire Histogram metadata blob here. + // + if( BytesLeft >= sizeof(CAMERA_METADATA_HISTOGRAM) ) + { + PCAMERA_METADATA_HISTOGRAM pHistogram = + (PCAMERA_METADATA_HISTOGRAM) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize); + ULONG ChannelMask = m_Synthesizer->GetChannelMask(); + CExtendedProperty Setting; + + m_Sensor->GetHistogram( &Setting ); + if( Setting.Flags & KSCAMERA_EXTENDEDPROP_HISTOGRAM_ON && + ChannelMask != 0 ) + { + // Just checking to see if we messed something up here. + NT_ASSERT( pMetadata->BufferSize >= CExtendedMetadata::METADATA_MAX + sizeof(CAMERA_METADATA_HISTOGRAM) ); + + pHistogram->Header.MetadataId = (ULONG) MetadataId_Custom_Histogram; + pHistogram->Header.Size = sizeof(*pHistogram); + + // Acquire the data for every channel. + pHistogram->Data.ChannelMask = ChannelMask; + pHistogram->Data.Height = m_Height; + pHistogram->Data.Width = m_Width; + pHistogram->Data.FourCC = m_Sensor->GetFourCC( m_PinID ); + m_Synthesizer->Histogram( pHistogram->Data.P0Data, pHistogram->Data.P1Data, pHistogram->Data.P2Data ); + pMetadata->UsedSize += sizeof(*pHistogram); + BytesLeft -= sizeof(*pHistogram); + } + } + + CExtendedVidProcSetting FaceDetect; + m_Sensor->GetFaceDetection(&FaceDetect); + + if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_PREVIEW ) + { + DBG_TRACE("PREVIEW"); + EmitFaceMetadata( + pStreamHeader, + FaceDetect.GetULONG(), + FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK); + } + } +} + diff --git a/AVStream/avscamera/sys/PreviewHwSim.h b/AVStream/avscamera/sys/PreviewHwSim.h new file mode 100644 index 00000000..92d3eae7 --- /dev/null +++ b/AVStream/avscamera/sys/PreviewHwSim.h @@ -0,0 +1,57 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + PreviewHwSim.h + + Abstract: + + This file contains the definition of the CPreviewHardwareSimulation class. + + This is a specialization of CHardwareSimulation that provides preview- + specific metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#pragma once + +// forward references +struct ISP_FRAME_SETTINGS; + +class CPreviewHardwareSimulation : + public CHardwareSimulation +{ +public: + CPreviewHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + virtual ~CPreviewHardwareSimulation(); + + virtual + void + FakeHardware(); + +protected: + METADATA_PREVIEWAGGREGATION + CPreviewHardwareSimulation:: + GetMetadata(); + + // Inject preview pin-specific metadata. + virtual + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + +}; + diff --git a/AVStream/avscamera/sys/RGB24Synthesizer.cpp b/AVStream/avscamera/sys/RGB24Synthesizer.cpp new file mode 100644 index 00000000..21c6e054 --- /dev/null +++ b/AVStream/avscamera/sys/RGB24Synthesizer.cpp @@ -0,0 +1,133 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + RGB24Synthesizer.cpp + + Abstract: + + This file contains the implementation of CRGB24Synthesizer. + + CRGB24Synthesizer is derived from CRGBSynthesizer. It uses the RGB + color space and defines a Commit() function that reformats pixels into + the RGB24 format. RGB24 uses 8 bits per primary and 3 bytes per pixel + in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:26019 ) +_Success_(return > 0) +ULONG +CRGB24Synthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + // In case stride isn't initialized. + if( Stride==0 ) + { + Stride = m_OutputStride; + } + + // These are impossible conditions. + if( !( Buffer && + m_Buffer && + (Size&3)==0 && // the rows are dword aligned, so the size must be too. + Size>3 && // there must be room for at least 1 pixel + (Stride&3)==0 && // rows should be DWORD aligned + Stride>3 && // rows should contain at least 1 pixel + m_Height>0 && + m_Width>0 + ) ) + { + NT_ASSERT(FALSE); + return 0; + } + + ULONG limit = min( Size/Stride, m_Height ); + for( ULONG row=0; row<limit; row++ ) + { + PDWORD pSrc = (PDWORD) GetImageLocation( 0, m_FlipVertical ? (m_Height - row -1) : row ); + PUCHAR pDst = Buffer + (row * Stride); + ULONG col = min( m_Width, Stride/3 ); + + while( col ) + { + // This optimizes the copy fairly well on most machines. + if( col>=4 ) + { + DWORD P0 = pSrc[0]; + DWORD P1 = pSrc[1]; + DWORD P2 = pSrc[2]; + DWORD P3 = pSrc[3]; + ((PDWORD) pDst)[0] = ((P0 ) & 0x00FFFFFF) | (P1 << 24); + ((PDWORD) pDst)[1] = ((P1 >> 8) & 0x0000FFFF) | (P2 << 16); + ((PDWORD) pDst)[2] = ((P2 >> 16) & 0x000000FF) | (P3 << 8); + pDst += 4 * 3; + pSrc += 4; + col -= 4; + } + else + { + DWORD P0 = *pSrc++; + *pDst++ = (UCHAR) (P0); + *pDst++ = (UCHAR) (P0 >> 8); + *pDst++ = (UCHAR) (P0 >> 16); + col--; + } + } + } + + return limit * Stride; +} +// suppressed due to Esp:773 +#pragma warning (pop) diff --git a/AVStream/avscamera/sys/RGB24Synthesizer.h b/AVStream/avscamera/sys/RGB24Synthesizer.h new file mode 100644 index 00000000..5a5d8f70 --- /dev/null +++ b/AVStream/avscamera/sys/RGB24Synthesizer.h @@ -0,0 +1,69 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + RGB24Synthesizer.h + + Abstract: + + This file contains the definition of CRGB24Synthesizer. + + CRGB24Synthesizer is derived from CXRGBSynthesizer. It uses the RGB + color space and defines a Commit() function that reformats pixels into + the RGB24 format. RGB24 uses 8 bits per primary and 3 bytes per pixel + in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +/************************************************* + + CRGB24Synthesizer + + Image synthesizer for RGB24 format. + +*************************************************/ + +class CRGB24Synthesizer + : public CXRGBSynthesizer +{ +public: + + // + // Default constructor + // + CRGB24Synthesizer( + LONG Width=0, + LONG Height=0 + ) + : CXRGBSynthesizer(Width, Height) + { + // Round up to the next full dword for a row on output. + m_OutputStride = (( Width * 3 ) + 3) & ((LONG) ~3); + m_FormatName = "RGB24"; + } + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); +}; + diff --git a/AVStream/avscamera/sys/Ref.h b/AVStream/avscamera/sys/Ref.h new file mode 100644 index 00000000..c2c4d76c --- /dev/null +++ b/AVStream/avscamera/sys/Ref.h @@ -0,0 +1,185 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ref.h + + Abstract: + + Implement a reference counting base class. Also implement something + similar to ComPtr<>, called a CRefPtr<>. The implementation may not + be as robust as ComPtr<>, but it should do the job here. + + History: + + created 12/8/2014 + +**************************************************************************/ + +#pragma once + +class CRef : public CNonCopyable +{ +private: + EX_RUNDOWN_REF m_Rundown; + +public: + // Initialize. + CRef() + { + Reset(); + } + + // Prevent deletion until the last reference is gone. + ~CRef() + { + Wait(); + } + + // Increment the reference. + BOOLEAN + Acquire() + { + return ExAcquireRundownProtection( &m_Rundown ); + } + + // Decrement the reference. + void + Release() + { + return ExReleaseRundownProtection( &m_Rundown ); + } + + // Wait for the last reference to go away. + // Note: This must be called by the child object's destructor. + void + Wait() + { + ExWaitForRundownProtectionRelease( &m_Rundown ); + } + + // Reinitialize to reuse the associated object. + void + Reset() + { + ExInitializeRundownProtection( &m_Rundown ); + } +}; + +template<class T> +class CRefPtr +{ +private: + T *m_ptr; + +public: + CRefPtr() + : m_ptr(nullptr) + {} + + // Copy constructor. + CRefPtr( const CRefPtr<T> &ptr ) + : m_ptr(nullptr) + { + // Make an initial assignment. + if( ptr && ptr->Acquire() ) + { + m_ptr = ptr; + } + } + + // Initial construction with a T object. + CRefPtr( T *ptr ) + : m_ptr(nullptr) + { + // Make an initial assignment. + if( ptr && ptr->Acquire() ) + { + m_ptr = ptr; + } + } + + // Decrement the reference on destruction. + ~CRefPtr() + { + // Free any ptr. + if( m_ptr ) + { + m_ptr->Release(); + } + } + + // Assignment + CRefPtr<T> & + operator = ( T *ptr ) + { + if( m_ptr ) + { + m_ptr->Release(); + } + + if( ptr ) + { + ptr->Acquire(); + } + + m_ptr = ptr; + return *this; + } + + // Assignment + CRefPtr<T> & + operator = ( CRefPtr<T> &ptr ) + { + return + (*this = ptr.m_ptr); + } + + // Dereference the pointer. + T * + operator -> () + { + return m_ptr; + } + + // Test for nullptr. + operator bool() + { + return m_ptr != nullptr; + } + + // Test for !nullptr. + bool + operator !() + { + return !m_ptr; + } + + // Test for equality. + bool + operator ==( T *ptr ) + { + return m_ptr == ptr; + } + + // Unsafe accessor. Use sparingly. + T * + get() + { + return m_ptr; + } +}; + +// Test for equality. +template <class T> +inline +bool +operator ==( T *ptr1, CRefPtr<T> &ptr2 ) +{ + return ptr2 == ptr1; +} + diff --git a/AVStream/avscamera/sys/Roi.cpp b/AVStream/avscamera/sys/Roi.cpp new file mode 100644 index 00000000..e1118996 --- /dev/null +++ b/AVStream/avscamera/sys/Roi.cpp @@ -0,0 +1,629 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Roi.cpp + + Abstract: + + This file contains the implemention of CRoiConfig, CRoiProperty and + related classes used in the filter to implement ROI handling. + + History: + + created 6/27/2013 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// Helper function to diff ptrs. +inline +UINT_PTR +ByteDiffPtrs( PVOID pBase, PVOID pCurrent ) +{ + PAGED_CODE(); + + return reinterpret_cast<PBYTE>(pCurrent) - reinterpret_cast<PBYTE>(pBase) ; +} + +// Helper function to get the size of an ROI control based just on the ID. +static +ULONG +GetSizeOfRoiInfo( ULONG ControlId ) +{ + PAGED_CODE(); + + switch( ControlId ) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + return sizeof(KSCAMERA_EXTENDEDPROP_ROI_WHITEBALANCE); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + return sizeof(KSCAMERA_EXTENDEDPROP_ROI_EXPOSURE); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + return sizeof(KSCAMERA_EXTENDEDPROP_ROI_FOCUS); + break; + + default: + NT_ASSERT(FALSE); + return 0; + } +} + +// Helper function to get the size of an ROI control. +static +ULONG +GetSize( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl +) +{ + PAGED_CODE(); + + // This actually needs to return the type based on the ROI_INFO size + ULONG RoiInfoSize = GetSizeOfRoiInfo( pCtrl->ControlId ); + + if( RoiInfoSize ) + { + return + sizeof(KSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL) + + (pCtrl->ROICount * RoiInfoSize); + } + else + { + NT_ASSERTMSG("GetSizeOfRoiInfo( pCtrl->ControlId ) returned 0! Should never happen!", FALSE); + return 0; + } +} + +// Helper function to walk to the next ROI control. +static +PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +NextCtrl( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl +) +{ + PAGED_CODE(); + + ULONG Size = GetSize( pCtrl ); + + if( Size ) + { + return + reinterpret_cast<PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL> + (((PBYTE) pCtrl) + Size); + } + else + { + NT_ASSERTMSG("GetSize( pCtrl ) returned 0! Should never happen!", FALSE); + return nullptr; + } +} + +PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +CRoiProperty:: +Find( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property +) +/*++ + +Routine Description: + + Search this ROI property for a property ID. + +Arguments: + + Property - + Which control to find. + +Return Value: + + A pointer to the ROI control structure. + +--*/ +{ + PAGED_CODE(); + + // We assume the controls have been validated first. + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pIspCtrl = + reinterpret_cast<PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL> (this+1); + + // Loop thru the controls. + for( ULONG i=0; i<m_Hdr.ControlCount; i++ ) + { + if( pIspCtrl->ControlId == (ULONG) Property ) + { + return pIspCtrl; + } + + // Advance to the next control. + pIspCtrl = NextCtrl( pIspCtrl ); + } + + return nullptr; +} + +PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +CRoiProperty:: +Add( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl +) +/*++ + +Routine Description: + + Add a control to the end of this header. + + Note: This function does not reallocate memory. Be careful to ensure + there is space available. + +Arguments: + + pCtrl - + An ROI control to append. + +Return Value: + + A pointer to the appended ROI control structure. + +--*/ +{ + PAGED_CODE(); + + // Only bother to add if there are ROIs. + if( pCtrl->ROICount > 0 ) + { + // We assume the controls have been validated first. + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pIspCtrl = + reinterpret_cast<PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL> (this+1); + + // Loop thru all the controls. + for( ULONG i=0; i<m_Hdr.ControlCount; i++ ) + { + // Advance to the next control. + pIspCtrl = NextCtrl( pIspCtrl ); + } + + ULONG SizeToCopy = ::GetSize(pCtrl); + + memcpy( pIspCtrl, + pCtrl, + SizeToCopy ) ; + + Size += SizeToCopy ; // Adjust our size. + m_Hdr.Size += SizeToCopy ; + m_Hdr.ControlCount++ ; // Add one to the control count. + } + return nullptr; +} + +bool +CRoiProperty:: +isValid() +/*++ + +Routine Description: + + Parse the current ROI Property list and make sure all the control + structures are self-consistent. + + Note: This is a very non-trivial problem. + +Arguments: + + None + +Return Value: + + A pointer to the appended ROI control structure. + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER( "()" ); + + ULONGLONG FocusFlags = 0; + ULONGLONG ExposureFlags = 0; + ULONGLONG WhiteBalanceFlags = 0; + + if( Version < KSCAMERA_EXTENDEDPROP_VERSION || + PinId != KSCAMERA_EXTENDEDPROP_FILTERSCOPE || + (Capability & ~(KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_CAPS_CANCELLABLE)) != 0 || + (Flags & ~KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION) != 0 || // Allow cancellation of ROI. + Size < sizeof(*this) ) + { + //NT_ASSERT(FALSE); + if( Version < KSCAMERA_EXTENDEDPROP_VERSION ) + { + DBG_TRACE( "Failed: Verison=%d", Version ); + } + if( PinId != KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + DBG_TRACE( "Failed: PinId=%d", PinId ); + } + if( (Capability & ~(KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_CAPS_CANCELLABLE)) != 0 ) + { + DBG_TRACE( "Failed: Capability=0x%016llX", Capability ); + } + if( (Flags & ~KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION) != 0 ) + { + DBG_TRACE( "Failed: Flags=0x%016llX", Flags ); + } + if( Size < sizeof(*this) ) + { + DBG_TRACE( "Failed(0): Size=%d, should be at least %Iu", Size, sizeof(*this) ); + } + + DBG_LEAVE( "()=false" ); + return false; + } + + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pIspCtrl = + reinterpret_cast<PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL> (this+1); + + // Loop thru the controls. + for( ULONG i=0; i<m_Hdr.ControlCount; i++ ) + { + // Make sure there is room to inspect this control + if( Size < ByteDiffPtrs( this, pIspCtrl+1 ) || + m_Hdr.Size < ByteDiffPtrs( &m_Hdr, pIspCtrl+1 ) ) + { + //NT_ASSERT(FALSE); + DBG_TRACE( "Failed(1): Size=%d, should be at least %Iu", Size, ByteDiffPtrs( this, pIspCtrl+1 ) ); + DBG_TRACE( " Or: m_Hdr.Size=%d, should be at least %Iu", m_Hdr.Size, ByteDiffPtrs( &m_Hdr, pIspCtrl+1 ) ); + DBG_LEAVE( "()=false" ); + return false; + } + + // Make sure the control is of a known type + switch( pIspCtrl->ControlId ) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + break; + + default: + //NT_ASSERT(FALSE); + DBG_LEAVE( "()=false : Invalid ControlId = 0x%08X", pIspCtrl->ControlId ); + return false; + } + + // Make sure there is room for the # of ROIs reported + if( Size < ByteDiffPtrs( this, NextCtrl(pIspCtrl) ) || + m_Hdr.Size < ByteDiffPtrs( &m_Hdr, NextCtrl(pIspCtrl) ) ) + { + //NT_ASSERT(FALSE); + DBG_TRACE( "Failed(2): Size=%d, should be at least %Iu", Size, ByteDiffPtrs( this, NextCtrl(pIspCtrl) ) ); + DBG_TRACE( " Or: m_Hdr.Size=%d, should be at least %Iu", m_Hdr.Size, ByteDiffPtrs( &m_Hdr, NextCtrl(pIspCtrl) ) ); + DBG_LEAVE( "()=false" ); + return false; + } + + // Validate the ROIs + for( ULONG j=0; j<pIspCtrl->ROICount; j++ ) + { + // Index into to the control's ROI list. Get the equivilent of "pIspCtrl->RoiInfo[j]" + PKSCAMERA_EXTENDEDPROP_ROI_INFO pRoiInfo = + reinterpret_cast<PKSCAMERA_EXTENDEDPROP_ROI_INFO> + (((PBYTE) (pIspCtrl+1)) + (j * GetSizeOfRoiInfo(pIspCtrl->ControlId) )); + + // Validate the cooridinates + if( pRoiInfo->Region.top < (LONG) TO_Q31(0) || + pRoiInfo->Region.bottom < (LONG) TO_Q31(0) || + pRoiInfo->Region.left < (LONG) TO_Q31(0) || + pRoiInfo->Region.right < (LONG) TO_Q31(0) ) + { + //NT_ASSERT(FALSE); + DBG_TRACE( "Failed: Region out of range." ); + DBG_TRACE( " (top=0x%08X, left=0x%08X, bottom=0x%08X, right=0x%08X)" + , pRoiInfo->Region.top + , pRoiInfo->Region.bottom + , pRoiInfo->Region.left + , pRoiInfo->Region.right ); + DBG_LEAVE( "()=false" ); + return false; + } + + // Validate the RegionOfInterestType + switch( pRoiInfo->RegionOfInterestType ) + { + case KSCAMERA_EXTENDEDPROP_ROITYPE_UNKNOWN: + case KSCAMERA_EXTENDEDPROP_ROITYPE_FACE: + break; + default: + //NT_ASSERT(FALSE); + DBG_LEAVE( "()=false Failed: RegionOfInterestType=%d", pRoiInfo->RegionOfInterestType ); + return false; + } + + // Validate the Weight + if( pRoiInfo->Weight > 100 ) + { + //NT_ASSERT(FALSE); + DBG_LEAVE( "()=false Failed: Weight=%d", pRoiInfo->Weight ); + return false; + } + + // Validate the Flags. + // Note: the driver doesn't allow mis-matched ROI Flags. + ULONGLONG RoiFlags = pRoiInfo->Flags & ~KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION; + + switch( pIspCtrl->ControlId ) + { + // Exposure & WhiteBalance only support Auto, Manual & Lock. + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + if( j!=0 ) + { + if( RoiFlags != ExposureFlags ) + { + DBG_LEAVE( "()=false : Mismatched Exposure Flags" ); + return false; + } + } + else + { + switch( RoiFlags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO| + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid Exposure Flags" ); + return false; + } + ExposureFlags = RoiFlags; + } + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + if( j!=0 ) + { + if( RoiFlags != WhiteBalanceFlags ) + { + DBG_LEAVE( "()=false : Mismatched WhiteBalance Flags" ); + return false; + } + } + else + { + switch( RoiFlags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO| + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid WhiteBalance Flags" ); + return false; + } + WhiteBalanceFlags = RoiFlags; + } + break; + + // Focus supports additional flags... + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + if( j!=0 ) + { + if( RoiFlags != FocusFlags ) + { + DBG_LEAVE( "()=false : Mismatched Focus Flags" ); + return false; + } + } + else + { + switch( RoiFlags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK) ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid Focus Flags" ); + return false; + } + + switch( RoiFlags & KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE: + // Depreciated cases are not supported: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_INFINITY: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_HYPERFOCAL: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid Focus Range" ); + return false; + } + FocusFlags = RoiFlags; + } + break; + } + } + + // Advance to the next control. + pIspCtrl = NextCtrl( pIspCtrl ) ; + } + + DBG_LEAVE( "()=true" ); + return true; +} + +// Log to the debug output. +void +CRoiProperty:: +Log() +{ + PAGED_CODE(); + + DBG_TRACE("Version=%d, PinId=%d, Capability=0x%016llX, Flags=0x%016llX, Size=%d", + Version, PinId, Capability, Flags, Size ); + + CRoiIspControl *pIspCtrl = reinterpret_cast<CRoiIspControl *> (this+1); + + // Loop thru the controls. + for( ULONG i=0; i<m_Hdr.ControlCount; i++ ) + { + pIspCtrl->Log(); + + // Advance to the next control. + pIspCtrl = reinterpret_cast<CRoiIspControl *>( NextCtrl( pIspCtrl ) ) ; + } +} + +// +// Initialize a block of memory from another ROI control +// +// Note: This function assumes enough ROI_INFO structures follow +// such that this copy will not overrun. +// +BOOL +CRoiIspControl:: +Init( + _In_ CRoiProperty *pRoiProperty, + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property +) +/*++ + +Routine Description: + + Initialize a block of memory from another ROI control + + Note: This function assumes enough ROI_INFO structures follow + such that this copy will not overrun. + +Arguments: + + pRoiProperty - + The extended ROI control payload. + + Property - + Which ISP control to copy from the payload. + +Return Value: + + TRUE - The ROI was set. + FALSE - The ROI was cleared. + +--*/ +{ + PAGED_CODE(); + + CRoiIspControl * + pCtrl = reinterpret_cast<CRoiIspControl *> (pRoiProperty->Find( Property )); + + if( pCtrl ) + { + memcpy( this, + pCtrl, + pCtrl->GetSize() ); + return TRUE; + } + + ControlId = Property; + ROICount = 0; + return FALSE; +} + +// Log to the debug output. +void +CRoiIspControl:: +Log() +{ + PAGED_CODE(); + + switch( ControlId ) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + DBG_TRACE("WhiteBalance"); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + DBG_TRACE("Exposure"); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + DBG_TRACE("Focus"); + break; + + default: + DBG_TRACE("[Unknown]"); + NT_ASSERT(FALSE); + break; + } + + DBG_TRACE("Result=0x%08X", Result); + + for( ULONG i=0; i<ROICount; i++ ) + { + // Index into to the control's ROI list. Get the equivilent of "pIspCtrl->RoiInfo[j]" + PKSCAMERA_EXTENDEDPROP_ROI_INFO pRoiInfo = + reinterpret_cast<PKSCAMERA_EXTENDEDPROP_ROI_INFO> + (((PBYTE) (this+1)) + (i * GetSizeOfRoiInfo(ControlId) )); + + DBG_TRACE(" | Flags=0x%016llX [Top=0x%08X, Left=0x%08X, Bottom=0x%08X, Right=0x%08X]", + pRoiInfo->Flags, + pRoiInfo->Region.top, + pRoiInfo->Region.left, + pRoiInfo->Region.bottom, + pRoiInfo->Region.right ); + } +} + +// +// Initialize a block of memory with an empty control list. +// +CRoiIspControl:: +CRoiIspControl( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property +) +{ + PAGED_CODE(); + + ControlId = Property; + ROICount = 0; + Result = 0; + Reserved = 0; +} + +ULONG +CRoiIspControl:: +GetSize() +{ + PAGED_CODE(); + + return (ROICount>0) ? ::GetSize( this ) : 0; +} + diff --git a/AVStream/avscamera/sys/Roi.h b/AVStream/avscamera/sys/Roi.h new file mode 100644 index 00000000..5149bf24 --- /dev/null +++ b/AVStream/avscamera/sys/Roi.h @@ -0,0 +1,279 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Roi.h + + Abstract: + + This file contains the definition of CRoiConfig, CRoiProperty and + related classes used in the filter to implement ROI handling. + + History: + + created 6/27/2013 + +**************************************************************************/ + +const ULONG MAX_ROI = 4; + +class CRoiIspControl ; + +class CRoiConfigCaps : public KSCAMERA_EXTENDEDPROP_ROI_CONFIGCAPS +{ +public: + CRoiConfigCaps( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Id, + _In_ ULONG MaxROI, + _In_ ULONGLONG Caps=0 + ) + { + ControlId = Id; + MaxNumberOfROIs = MaxROI; + Capability = Caps; + } + + static const ULONGLONG FOCUS_CAPS = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + + static const ULONGLONG EXPOSURE_CAPS = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + + static const ULONGLONG WHITEBALANCE_CAPS = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; +}; + +class CRoiConfigCapsHdr : public KSCAMERA_EXTENDEDPROP_ROI_CONFIGCAPSHEADER +{ + CRoiConfigCaps FocusCaps; + CRoiConfigCaps ExposureCaps; + CRoiConfigCaps WhiteBalanceCaps; + +public: + CRoiConfigCapsHdr() + : FocusCaps( + KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE, + MAX_ROI, + CRoiConfigCaps::FOCUS_CAPS) + , ExposureCaps( + KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE, + MAX_ROI, + CRoiConfigCaps::EXPOSURE_CAPS ) + , WhiteBalanceCaps( + KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE, + MAX_ROI, + CRoiConfigCaps::WHITEBALANCE_CAPS ) + { + ConfigCapCount = 3; + Reserved = 0; + Size = sizeof(*this); + } +}; + +class CRoiConfig : public KSCAMERA_EXTENDEDPROP_HEADER +{ + CRoiConfigCapsHdr CapsHdr; +public: + CRoiConfig() + { + Version = 1; + PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + Size = sizeof(*this); + Result = 0; + Capability = 0; + Flags = 0; + } + CRoiConfig( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : KSCAMERA_EXTENDEDPROP_HEADER(hdr) + { + Size = sizeof(*this); + } + + bool isValid() + { + return (Version == KSCAMERA_EXTENDEDPROP_VERSION) && + (Size >= sizeof(*this)) ; + } +}; + +// +// A wrapper for the KSCAMERA_EXTENDEDPROP_ROI payload +// +// The payload extends beyond the end of this class definition. However the +// payload is variable in length, so this class definition contains member +// functions that can be used to access and append items to the payload. +// +class CRoiProperty : public KSCAMERA_EXTENDEDPROP_HEADER +{ + KSCAMERA_EXTENDEDPROP_ROI_ISPCONTROLHEADER m_Hdr; + +public: + // Default ctor - used to init the buffer when returning ROI info. + CRoiProperty() + { + Version = KSCAMERA_EXTENDEDPROP_VERSION; + PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + Size = sizeof(*this); + Result = 0; + Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_CAPS_CANCELLABLE; + Flags = 0; + + m_Hdr.ControlCount = 0; + m_Hdr.Size = sizeof(m_Hdr); + m_Hdr.Reserved = 0; + } + + // Validate the CRoiProperty + bool + isValid(); + + // Get Size attribute. + ULONG + GetSize() + { + return Size; + } + + // Get number of controls. (1-3) + ULONG + GetControlCount() + { + return m_Hdr.ControlCount; + } + + // Find a given control after the property header. + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL + Find( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property + ); + + // + // Add a control to the end of this header. + // + // Note: This function does not reallocate memory. Be careful to ensure + // there is space available. + // + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL + Add( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl + ); + + void + Log(); +}; + +// Note: No initializer classes are provided for the following DDI +// structure definitions since there is no practical difference in their +// definitions. However the CRoiIspControl::GetSize() method is aware +// of them and will report the correct size of the structure if there were +// any difference in size. +// +// - KSCAMERA_EXTENDEDPROP_ROI_WHITEBALANCE +// - KSCAMERA_EXTENDEDPROP_ROI_EXPOSURE +// - KSCAMERA_EXTENDEDPROP_ROI_FOCUS +// +class CRoiIspControl : public KSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +{ +public: + BOOL + Init( + _In_ CRoiProperty *pRoiProperty, + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property + ); + + CRoiIspControl( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property= + KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE + ); + + void + Log(); + + ULONG + GetSize(); +}; + +// Simple classes used by the driver to hold the maximum allowed number of ROIs. +class CWhiteBalanceRoiIspControl : public CRoiIspControl +{ +private: + KSCAMERA_EXTENDEDPROP_ROI_WHITEBALANCE ROI[MAX_ROI]; + +public: + CWhiteBalanceRoiIspControl( + _In_ CRoiProperty *pRoiProperty + ) + { + Init( pRoiProperty, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ); + } + + CWhiteBalanceRoiIspControl() + : CRoiIspControl( KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ) + {} + + ULONGLONG + GetFlags() + { + return ( ROICount>0 ) ? ROI[0].ROIInfo.Flags : 0; + } +}; + +class CExposureRoiIspControl : public CRoiIspControl +{ +private: + KSCAMERA_EXTENDEDPROP_ROI_EXPOSURE ROI[MAX_ROI]; + +public: + CExposureRoiIspControl( + _In_ CRoiProperty *pRoiProperty + ) + { + Init( pRoiProperty, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ); + } + + CExposureRoiIspControl() + : CRoiIspControl( KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ) + {} + + ULONGLONG + GetFlags() + { + return ( ROICount>0 ) ? ROI[0].ROIInfo.Flags : 0; + } +}; + +class CFocusRoiIspControl : public CRoiIspControl +{ +private: + KSCAMERA_EXTENDEDPROP_ROI_FOCUS ROI[MAX_ROI]; + +public: + CFocusRoiIspControl( + _In_ CRoiProperty *pRoiProperty + ) + { + Init( pRoiProperty, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE ); + } + + CFocusRoiIspControl() + : CRoiIspControl( KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ) + {} + + ULONGLONG + GetFlags() + { + return ( ROICount>0 ) ? ROI[0].ROIInfo.Flags : 0; + } +}; + diff --git a/AVStream/avscamera/sys/Sensor.cpp b/AVStream/avscamera/sys/Sensor.cpp new file mode 100644 index 00000000..cb310dcc --- /dev/null +++ b/AVStream/avscamera/sys/Sensor.cpp @@ -0,0 +1,1365 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Sensor.cpp + + Abstract: + + Base Sensor class implementation. + + This class also controls access to the pin simulations. Most cameras + have a limited set of ISP resources and can only instantiate a fixed + number of pins. This class grants access to those resources. + + History: + + created 5/5/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CSensor::CSensor( + _In_ CCaptureDevice *Device, + _In_ ULONG PinCount +) + : m_Device(Device) + , m_PinCount(PinCount) + , m_HardwareSimulation(nullptr) + , m_Synthesizer(nullptr) + , m_CapturePin(nullptr) + , m_VideoInfoHeader(nullptr) + , m_InterruptTime(nullptr) + , m_LastMappingsCompleted(nullptr) + , m_PreviewMask(INVALID_PIN_MASK) + , m_StillMask(INVALID_PIN_MASK) + , m_VideoMask(INVALID_PIN_MASK) + , m_FilterInstanceCount(0) + , m_pIspSettings(nullptr) + , m_PfsFrameLimit(0) + , m_PfsLoopLimit(0) + , m_MountingOrientation(AcpiPldRotation0) +{ + PAGED_CODE(); + NT_ASSERT(Device); + NT_ASSERT(PinCount); +} + + +CSensor::~CSensor() +{ + PAGED_CODE(); + for(ULONG i=0; i < m_PinCount; i++) + { + if( m_HardwareSimulation) + { + SAFE_DELETE( m_HardwareSimulation[i] ); + } + if( m_Synthesizer ) + { + SAFE_DELETE( m_Synthesizer[i] ); + } + if( m_CapturePin ) + { + m_CapturePin[i] = nullptr; + } + if( m_VideoInfoHeader ) + { + SAFE_DELETE( m_VideoInfoHeader[i] ); + } + } + + SAFE_DELETE_ARRAY( m_pIspSettings ); + SAFE_DELETE_ARRAY( m_HardwareSimulation ); + SAFE_DELETE_ARRAY( m_Synthesizer ); + SAFE_DELETE_ARRAY( m_CapturePin ); + SAFE_DELETE_ARRAY( m_VideoInfoHeader ); + SAFE_DELETE_ARRAY( m_InterruptTime ); + SAFE_DELETE_ARRAY( m_LastMappingsCompleted); +} + + +NTSTATUS +CSensor:: +AddFilter(PKSFILTER pFilter) +{ + PAGED_CODE(); + + // If your sensor object needs to keep track of the Filters that are + // attached to it, you would do that here. We only want to keep an + // outstanding filter count so we know when to reset our defaults. + UNREFERENCED_PARAMETER(pFilter); + + LONG Count = IncrementFilterCount(); + NTSTATUS Status= STATUS_SUCCESS; + + // If this is the first filter, reprogram the sensor. + if( Count==1 ) + { + Status = ProgramDefaults(); + if( !NT_SUCCESS(Status) ) + { + Count = DecrementFilterCount(); + } + } + DBG_LEAVE("(Count=%d)=0x%08X", Count, Status); + return Status; +} + +NTSTATUS +CSensor:: +RemoveFilter(PKSFILTER pFilter) +{ + PAGED_CODE(); + + // If your sensor object needs to keep track of the Filters that are + // attached to it, you would clean up here. We only want to keep an + // outstanding filter count so we know when to reset our defaults. + UNREFERENCED_PARAMETER(pFilter); + + LONG Count = DecrementFilterCount(); + NT_ASSERT( Count>=0 ); + + NTSTATUS Status = Count>=0 ? STATUS_SUCCESS : STATUS_INVALID_DEVICE_STATE; + + DBG_LEAVE("(Count=%d)=0x%08X", Count, Status); + return Status; +} + +NTSTATUS +CSensor:: +ProgramDefaults() +{ + PAGED_CODE(); + return STATUS_SUCCESS; +} + +NTSTATUS +CSensor:: +Initialize() +{ + PAGED_CODE(); + + m_HardwareSimulation = new (NonPagedPoolNx, 'sneS') CHardwareSimulation *[m_PinCount]; + m_Synthesizer = new (NonPagedPoolNx, 'sneS') CSynthesizer *[m_PinCount]; + m_CapturePin = new (NonPagedPoolNx, 'sneS') ICapturePin *[m_PinCount]; + m_VideoInfoHeader = new (NonPagedPoolNx, 'sneS') PKS_VIDEOINFOHEADER[m_PinCount]; + m_InterruptTime = new (NonPagedPoolNx, 'sneS') ULONG[m_PinCount]; + m_LastMappingsCompleted = new (NonPagedPoolNx, 'sneS') ULONG[m_PinCount]; + + if( !m_HardwareSimulation || + !m_Synthesizer || + !m_CapturePin || + !m_VideoInfoHeader || + !m_InterruptTime || + !m_LastMappingsCompleted ) + { + SAFE_DELETE_ARRAY( m_HardwareSimulation ); + SAFE_DELETE_ARRAY( m_Synthesizer ); + SAFE_DELETE_ARRAY( m_CapturePin ); + SAFE_DELETE_ARRAY( m_VideoInfoHeader ); + SAFE_DELETE_ARRAY( m_InterruptTime ); + SAFE_DELETE_ARRAY( m_LastMappingsCompleted); + return STATUS_INSUFFICIENT_RESOURCES; + } + + for(ULONG i=0; i<m_PinCount; i++) + { + m_HardwareSimulation[i] = nullptr; + m_Synthesizer[i] = nullptr; + m_CapturePin[i] = nullptr; + m_VideoInfoHeader[i] = nullptr; + m_InterruptTime[i] = 0; + m_LastMappingsCompleted[i] = 0; + } + return STATUS_SUCCESS; +} + +void +CSensor:: +Interrupt( + _In_ LONG PinIndex +) + +/*++ + +Routine Description: + + This is the "faked" interrupt service routine for this device. It + is called at dispatch level by the hardware simulation. + +Arguments: + + PinIndex - + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + if( IsValidIndex((ULONG)PinIndex) ) + { + DBG_ENTER("(PinIndex=%d): m_LastMappingsCompleted[%d]=%d", PinIndex, + PinIndex, m_LastMappingsCompleted[PinIndex]); + + m_InterruptTime[PinIndex]++; + + // + // Realistically, we'd do some hardware manipulation here and then queue + // a DPC. Since this is fake hardware, we do what's necessary here. This + // is pretty much what the DPC would look like short of the access + // of hardware registers (ReadNumberOfMappingsCompleted) which would likely + // be done in the ISR. + // + ULONG LastMappingCompleted = m_LastMappingsCompleted[PinIndex]; + if( !IsStillIndex(PinIndex) ) + { + while( LastMappingCompleted < + m_HardwareSimulation[PinIndex]->ReadNumberOfMappingsCompleted() ) + { + // Complete a frame. + if( !NT_SUCCESS(m_CapturePin[PinIndex]->CompleteMapping()) ) + { + break; + } + LastMappingCompleted++; + } + } + else + { + CImageHardwareSimulation *pImageHw = (CImageHardwareSimulation *) m_HardwareSimulation[PinIndex]; + PKSSTREAM_POINTER pClone = pImageHw->m_pClone; + + // Just make sure the image pin actually generate a frame. + if( pClone ) + { + // Emit a photo confirmation image (we don't send off a copy of this image), + // but only if we need one for this photo and if the timestamp is valid. + if( pImageHw->m_PhotoConfirmationInfo.isRequired() && + (pClone->StreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) ) + { + // Generate a photo confirmation on any running preview pins. + for( ULONG Index=0; IsValidIndex(Index); Index++ ) + { + if( IsPreviewIndex(Index) ) + { + m_HardwareSimulation[Index]-> + GeneratePhotoConfirmation( + pImageHw->m_PhotoConfirmationInfo.getIndex(), + pImageHw->m_PhotoConfirmationInfo.getTime() + ); + } + } + } + + // + // Inform the capture pin that a given number of scatter / gather + // mappings have completed. + // + if( NT_SUCCESS(m_CapturePin[PinIndex]->CompleteMapping(pClone)) ) + { + LastMappingCompleted++; + } + + pImageHw->m_pClone = NULL; + } + pImageHw->m_PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO(); + } + + m_LastMappingsCompleted[PinIndex] = LastMappingCompleted; + DBG_LEAVE("(PinIndex=%d): LastMappingCompleted=%d", PinIndex, LastMappingCompleted); + } +} + + +NTSTATUS +CSensor:: +CreateSynthesizer( + _In_ PKSPIN Pin, + _In_ PKS_VIDEOINFOHEADER VideoInfoHeader + ) +// +// Create the necessary type of image synthesizer. +// +{ + NTSTATUS Status = STATUS_SUCCESS; + LONG Width = VideoInfoHeader->bmiHeader.biWidth; + LONG Height = VideoInfoHeader->bmiHeader.biHeight; + + if( VideoInfoHeader->bmiHeader.biBitCount == 24 && + VideoInfoHeader->bmiHeader.biCompression == KS_BI_RGB ) + { + // + // If we're RGB24, create a new RGB24 synth. RGB24 surfaces + // can be in either orientation. The origin is lower left if + // height < 0. Otherwise, it's upper left. + // + m_Synthesizer[ Pin->Id ] = new (NonPagedPoolNx, 'RysI') + CRGB24Synthesizer( Width, Height ); + DBG_TRACE( "Creating CRGB24Synthesizer..." ); + } + else if( VideoInfoHeader->bmiHeader.biBitCount == 32 && + VideoInfoHeader->bmiHeader.biCompression == KS_BI_RGB ) + { + // + // If we're RGB32, create a new RGB32 synth. RGB32 surfaces + // can be in either orientation. The origin is lower left if + // height < 0. Otherwise, it's upper left. + // + m_Synthesizer[ Pin->Id ] = new (NonPagedPoolNx, '23RI') + CXRGBSynthesizer( Width, Height ); + DBG_TRACE( "Creating CXRGBSynthesizer..." ); + } + else if( VideoInfoHeader->bmiHeader.biBitCount == 16 && + (VideoInfoHeader->bmiHeader.biCompression == FOURCC_YUY2) ) + { + // + // If we're YUY2, create the YUY2 synth. + // + m_Synthesizer[ Pin->Id ] = new(NonPagedPoolNx, 'YysI') CYUY2Synthesizer( Width, Height ); + DBG_TRACE( "Creating CYUY2Synthesizer..." ); + + } + else if( VideoInfoHeader->bmiHeader.biBitCount == 12 && + (VideoInfoHeader->bmiHeader.biCompression == FOURCC_NV12) ) + { + + m_Synthesizer[ Pin->Id ] = new(NonPagedPoolNx, 'Nv12') CNV12Synthesizer( Width, Height ); + DBG_TRACE( "Creating CNV12Synthesizer..." ); + + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + + if( NT_SUCCESS( Status ) ) + { + DBG_TRACE( "Setting Mounting Orientation to %d�", DbgRotation2Degrees(m_MountingOrientation) ) + m_Synthesizer[ Pin->Id ]->SetRotation( m_MountingOrientation ); + } + + return Status; +} + +NTSTATUS +CSensor:: +AcquireHardwareResources( + _In_ PKSPIN Pin, + _In_ ICapturePin *CapturePin, + _In_ PKS_VIDEOINFOHEADER VideoInfoHeader, + _Out_ CHardwareSimulation **pSim +) + +/*++ + +Routine Description: + + Acquire hardware resources for the capture hardware. If the + resources are already acquired, this will return an error. + The hardware configuration must be passed as a VideoInfoHeader. + +Arguments: + + Pin - + The pin to acquire. + + CapturePin - + The capture pin attempting to acquire resources. When scatter / + gather mappings are completed, the capture pin specified here is + what is notified of the completions. + + VideoInfoHeader - + Information about the capture stream. This **MUST** remain + stable until the caller releases hardware resources. Note + that this could also be guaranteed by bagging it in the device + object bag as well. + + pSim - + A location to store a pointer to the simulation object for this pin. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER( "(Pin=%d, CapturePin=%p, VideoInfoHeader=%p)", Pin->Id, CapturePin, VideoInfoHeader ); + + NTSTATUS Status = STATUS_SUCCESS; + LONG lPindex = Pin->Id; + // + // 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) + { + m_VideoInfoHeader[lPindex] = VideoInfoHeader; + + // + // If there's an old hardware simulation sitting around for some + // reason, blow it away. + // + SAFE_DELETE( m_Synthesizer[lPindex] ); + + DBG_TRACE( "biBitCount =%d", VideoInfoHeader->bmiHeader.biBitCount ); + DBG_TRACE( "biWidth =%d", VideoInfoHeader->bmiHeader.biWidth ); + DBG_TRACE( "biHeight =%d", VideoInfoHeader->bmiHeader.biHeight ); + DBG_TRACE( "biCompression=0x%08X", VideoInfoHeader->bmiHeader.biCompression ); + DBG_TRACE( "biCompression='%04s'", (PSTR) &VideoInfoHeader->bmiHeader.biCompression ); + DBG_TRACE( "AvgTimePerFrame=%lld", VideoInfoHeader->AvgTimePerFrame ); + + Status = CreateSynthesizer( Pin, + VideoInfoHeader ); + + if (NT_SUCCESS(Status) && !m_Synthesizer[lPindex]) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS (Status)) + { + // + // If everything has succeeded thus far, set the capture pin. + // + m_CapturePin[lPindex] = CapturePin; + *pSim = m_HardwareSimulation[lPindex]; + + } + else + { + // + // If anything failed in here, we release the resources we've + // acquired. + // + ReleaseHardwareResources(Pin); + } + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + DBG_LEAVE( "(Pin=%d, CapturePin=%p, VideoInfoHeader=%p) = 0x%08X", Pin->Id, CapturePin, VideoInfoHeader, Status ); + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +Start ( + _In_ PKSPIN Pin +) + +/*++ + +Routine Description: + + Start the capture device based on the video info header we were told + about when resources were acquired. + +Arguments: + + Pin - + The pin to start + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER( "( Pin=%d )\n", Pin->Id ) ; + + LONG lPindex = Pin->Id; + + m_LastMappingsCompleted[lPindex] = 0; + m_InterruptTime[lPindex] = 0; + + NT_ASSERT( m_VideoInfoHeader[lPindex] != nullptr ); + if( !m_VideoInfoHeader[lPindex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // Ideally we'd do this when the filter is constructed; but the + // simulation is constructed first and these values aren't used until + // we call start. + + if( !IsStillIndex(lPindex) ) + { + return + m_HardwareSimulation[lPindex] -> Start ( + m_Synthesizer[lPindex], + m_VideoInfoHeader[lPindex] -> AvgTimePerFrame, + m_VideoInfoHeader[lPindex] -> bmiHeader.biWidth, + ABS (m_VideoInfoHeader[lPindex] -> bmiHeader.biHeight), + m_VideoInfoHeader[lPindex] -> bmiHeader.biSizeImage + ); + } + else + { + CImageHardwareSimulation *pHwSim = (CImageHardwareSimulation *) m_HardwareSimulation[lPindex] ; + + // The following is necessary if Per-Frame Settings are active. + if( IsVPSActive() ) + { + // Program the simulation with our settings + pHwSim->SetPFS( m_pIspSettings, m_PfsFrameLimit, m_PfsLoopLimit ); + } + else + { + // Clear out any previous settings, if they still exist. + pHwSim->SetPFS(nullptr, 0, 0); + } + + // Init to the default frame rate. + LONGLONG TimePerFrame = m_VideoInfoHeader[lPindex]->AvgTimePerFrame; + DBG_TRACE("Image Pin's AvgTimePerFrame=%lld", TimePerFrame); + + // Query our control for the max frame rate and convert it into a "PERFORMACE TIME". + CExtendedProperty MaxFrameRate; + GetPhotoMaxFrameRate( &MaxFrameRate ); + LARGE_INTEGER PerformanceTime = { MaxFrameRate.m_Value.Value.ratio.LowPart }; + LONGLONG Frequency = MaxFrameRate.m_Value.Value.ratio.HighPart; + + // Handle an user-specified frame-rate override. + if( Frequency != 0 ) + { + DBG_TRACE( "MaxFrameRate = %lld/%lld", PerformanceTime.QuadPart, Frequency ); + + SetPhotoFrameRate( + lPindex, + KSCONVERT_PERFORMANCE_TIME( Frequency, PerformanceTime ) + ); + } + + CExtendedPhotoMode Mode; + GetPhotoMode( &Mode ); + + NTSTATUS status = pHwSim -> Start ( + m_Synthesizer[lPindex], + m_VideoInfoHeader[lPindex] -> bmiHeader.biWidth, + ABS (m_VideoInfoHeader[lPindex] -> bmiHeader.biHeight), + m_VideoInfoHeader[lPindex] -> bmiHeader.biSizeImage, + (Mode.Flags == 0 ? PinNormalMode : PinBurstMode) + ); + + if(NT_SUCCESS(status)) + { + status = pHwSim -> SetClock(Pin); + } + + DBG_LEAVE( "( Pin=%d )=0x%08X\n", Pin->Id, status ); + + return status; + } +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +Pause ( + _In_ PKSPIN Pin, + _In_ BOOLEAN Pausing +) + +/*++ + +Routine Description: + + Pause or unpause the hardware simulation. This is an effective start + or stop without resetting counters and formats. Note that this can + only be called to transition from started -> paused -> started. Calling + this without starting the hardware with Start() does nothing. + +Arguments: + + Pin - + The pin to pause. + + Pausing - + An indicatation of whether we are pausing or unpausing + + TRUE - + Pause the hardware simulation + + FALSE - + Unpause the hardware simulation + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + return + m_HardwareSimulation[Pin->Id]->Pause(Pausing); +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +Stop ( + _In_ PKSPIN Pin +) + +/*++ + +Routine Description: + + Stop the capture device. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + LONG lPindex = Pin->Id; + CHardwareSimulation *pHwSim = m_HardwareSimulation[lPindex]; + + if( !pHwSim ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + NTSTATUS Status = pHwSim->Stop(); + + if( IsStillIndex(lPindex) ) + { + // Clear out any previous Per Frame Settings, if they still exist. + ((CImageHardwareSimulation *)pHwSim)->SetPFS(nullptr, 0, 0) ; + } + + return Status; +} + +NTSTATUS +CSensor:: +Reset( + _In_ PKSPIN Pin +) +{ + PAGED_CODE(); + + if( !IsValidIndex( Pin->Id ) ) + { + return STATUS_INVALID_PARAMETER; + } + + if( !IsStillIndex(Pin->Id) ) + { + return STATUS_SUCCESS; + } + + if( !m_HardwareSimulation[Pin->Id] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + CHardwareSimulation *pHwSim = m_HardwareSimulation[Pin->Id]; + NTSTATUS Status = pHwSim->Reset(); + + m_LastMappingsCompleted[Pin->Id] = pHwSim->ReadNumberOfMappingsCompleted(); + return Status; +} + +//Resets both filter's first PIN +NTSTATUS +CSensor:: +Reset( +) +{ + PAGED_CODE(); + + NTSTATUS Status=STATUS_SUCCESS; + + for( ULONG Index=GetNextStillIndex(); IsValidIndex(Index) && NT_SUCCESS( Status ); Index=GetNextStillIndex(Index) ) + { + if( !m_HardwareSimulation[Index] ) + { + Status = STATUS_INVALID_DEVICE_STATE; + } + else + { + CHardwareSimulation *pHwSim = m_HardwareSimulation[Index]; + + Status = pHwSim->Reset(); + m_LastMappingsCompleted[Index] = pHwSim->ReadNumberOfMappingsCompleted(); + } + } + + for( ULONG i=0; i<m_PinCount; i++ ) + { + DBG_TRACE("m_LastMappingsCompleted[%d] = %d", i, m_LastMappingsCompleted[i] ); + } + return Status; +} + + +NTSTATUS +CSensor:: +Trigger ( + _In_ ULONG PinId, + _In_ LONG mode +) +/*++ + +Routine Description: + + For a photo pin, take a picture or begin or end a photo sequence. + +Arguments: + + PinId - + This must be a photo pin. + + mode - + The trigger mode, ie: normal or start/stop sequence. + +Return Value: + + Success / Failure + +--*/ +{ + + PAGED_CODE(); + + if( !IsStillIndex(PinId) || + !m_HardwareSimulation[PinId] || + m_HardwareSimulation[PinId]->GetState() != PinRunning ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + return + ((CImageHardwareSimulation *)m_HardwareSimulation[PinId])->Trigger(mode); + +} + +LONG +CSensor:: +GetTriggerMode( + _In_ ULONG PinId +) +/*++ + + Routine Description: + + For a photo pin, take a picture or begin or end a photo sequence. + + Arguments: + + PinId - + This must be a photo pin. + + mode - + The trigger mode, ie: normal or start/stop sequence. + + Return Value: + + Success / Failure + +--*/ +{ + + PAGED_CODE(); + + if( !IsStillIndex(PinId) || + !m_HardwareSimulation[PinId] || + m_HardwareSimulation[PinId]->GetState() != PinRunning ) + { + return 0; + } + + return + ((CImageHardwareSimulation *)m_HardwareSimulation[PinId])->GetTriggerMode(); + +} + + +NTSTATUS +CSensor:: +SetPhotoFrameRate( + _In_ ULONG StillIndex, + _In_ ULONGLONG TimePerFrame +) +/*++ + +Routine Description: + + For a photo pin, set the desired frame rate. + +Arguments: + + TimePerFrame - + Time in 100ns between each frame. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + // For now, we only handle 1 photo pin. + + if( !IsValidIndex(StillIndex) || + !m_VideoInfoHeader[StillIndex] || + !m_HardwareSimulation[StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + ULONGLONG AvgTimePerFrame = + (ULONGLONG) m_VideoInfoHeader[StillIndex]->AvgTimePerFrame; + + DBG_TRACE( "TimePerFrame=%lld, AvgTimePerFrame=%lld", TimePerFrame, AvgTimePerFrame ); + + // Handle a reset back to the standard frame rate. + if( TimePerFrame == 0 ) + { + TimePerFrame = AvgTimePerFrame; + } + // Handle an user-specified frame-rate override. + else + { + // It is unrealistic to allow a frame rate faster than the rate specified for this format. + TimePerFrame = max( TimePerFrame, AvgTimePerFrame ); + } + + DBG_TRACE( "Setting new TimePerFrame=%lld", TimePerFrame ); + + return + ((CImageHardwareSimulation *) m_HardwareSimulation[StillIndex])->SetPhotoFrameRate( TimePerFrame ); +} + +ULONGLONG +CSensor:: +GetPhotoFrameRate( + _In_ ULONG StillIndex +) +/*++ + +Routine Description: + + Get the desired frame rate for a photo pin. + +Arguments: + + None + +Return Value: + + Time in 100ns between each frame. + +--*/ +{ + PAGED_CODE(); + + if( !IsStillIndex(StillIndex) || + !m_HardwareSimulation[StillIndex] ) + { + NT_ASSERT(FALSE); + return 0; + } + + CImageHardwareSimulation *pSim = (CImageHardwareSimulation *) (m_HardwareSimulation[StillIndex]); + return pSim->GetPhotoFrameRate( ); +} + +NTSTATUS +CSensor:: +SetFlashStatus( + _In_ ULONGLONG ulFlashStatus +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + for( ULONG StillIndex = GetNextStillIndex(); + IsValidIndex( StillIndex ) && NT_SUCCESS(Status); + StillIndex = GetNextStillIndex( StillIndex ) ) + { + if( !m_HardwareSimulation[ StillIndex ] ) + { + NT_ASSERT(FALSE); + return STATUS_INVALID_DEVICE_STATE; + } + Status = ((CImageHardwareSimulation *) m_HardwareSimulation[StillIndex])->SetFlashStatus (ulFlashStatus); + } + + return Status; +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +SetPinMode( + _In_ ULONG StillIndex, + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers +) +{ + PAGED_CODE(); + + if( !IsStillIndex(StillIndex) || + !m_HardwareSimulation[StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + return + ((CImageHardwareSimulation *) m_HardwareSimulation[StillIndex])-> + SetMode( Flags, PastBuffers ); +} + +NTSTATUS +CSensor:: +GetQPC( + _In_ ULONG StillIndex, + _Out_ PULONGLONG TriggerTime +) +{ + PAGED_CODE(); + + if( !IsStillIndex(StillIndex) || + !m_HardwareSimulation[StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + *TriggerTime = + ((CImageHardwareSimulation *) m_HardwareSimulation[StillIndex])-> + GetTriggerTime(); + + return STATUS_SUCCESS; +} + +NTSTATUS +CSensor:: +SetQPC( + _In_ ULONG StillIndex, + _In_ ULONGLONG TriggerTime +) +{ + PAGED_CODE(); + + if( !IsStillIndex(StillIndex) || + !m_HardwareSimulation[StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + ((CImageHardwareSimulation *) m_HardwareSimulation[StillIndex])-> + SetTriggerTime( TriggerTime ); + + return STATUS_SUCCESS; +} + +// +// Program the Per Frame Settings for simulation +// We do it before calling start so we can be ready +// to program our simulation's hardware. +// +// Note: We make a local copy. +// +NTSTATUS +CSensor:: +SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit +) +/*++ + +Routine Description: + + Program the Per Frame Settings for simulation + We do it before calling start so we can be ready + to program our simulation's hardware. + + Note: We make a local copy. + +Arguments: + + pIspSettings - + The Per-Frame Settings to use. + + FrameLimit - + The number of frames to capture. + + LoopLimit - + The number of times to loop over the sequence. + +Return Value: + + Success / Failure. + +--*/ +{ + PAGED_CODE(); + KScopedMutex Lock(m_SensorMutex); + + // Set the current ISP settings and free any prior. + // This has to be done at simulation start or stop or we have + // a synchronization problem. + SAFE_DELETE_ARRAY( m_pIspSettings ); + + if( pIspSettings ) + { + m_pIspSettings = new (NonPagedPoolNx) ISP_FRAME_SETTINGS[FrameLimit]; + if( !m_pIspSettings ) + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory( m_pIspSettings, pIspSettings, FrameLimit*sizeof(ISP_FRAME_SETTINGS) ); + m_PfsLoopLimit = LoopLimit; + m_PfsFrameLimit = FrameLimit; + } + else + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + } + + return STATUS_SUCCESS; +} + +/*************************************************/ + + +void +CSensor:: +ReleaseHardwareResources ( + _In_ PKSPIN Pin +) + +/*++ + +Routine Description: + + Release hardware resources. This should only be called by + an object which has acquired them. + +Arguments: + + Pin - + The pin to acquire. + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + DBG_ENTER( "( Pin=%d )\n", Pin->Id ) ; + + LONG lPindex = Pin->Id; + // + // Blow away the image synth. + // + m_HardwareSimulation[lPindex]->Reset(); + + SAFE_DELETE( m_Synthesizer[lPindex] ); + m_VideoInfoHeader[lPindex] = NULL; + + // + // Release our "lock" on hardware resources. This will allow another + // pin (perhaps in another graph) to acquire them. + // + InterlockedExchangePointer( + (PVOID *) &(m_CapturePin[lPindex]), + nullptr + ); + + DBG_LEAVE( "( Pin=%d )\n", Pin->Id ) ; +} + +ULONG +CSensor:: +ProgramScatterGatherMappings ( + _In_ PKSPIN Pin, + _In_ PKSSTREAM_POINTER *Clone, + _In_ PUCHAR *Buffer, + _In_ PKSMAPPING Mappings, + _In_ ULONG MappingsCount +) + +/*++ + +Routine Description: + + Program the scatter / gather mappings for the "fake" hardware. + + Punts the request to the H/W simulation (CHardwareSimulation) object for + that pin. + +Arguments: + + Pin - + The pin to program. + + Clone - + A stream pointer. + + Buffer - + Points to a pointer to the virtual address of the topmost + scatter / gather chunk. The pointer will be updated as the + device "programs" mappings. Reason for this is that we get + the physical addresses and sizes, but must calculate the virtual + addresses... This is used as scratch space for that. + + Mappings - + An array of mappings to program + + MappingsCount - + The count of mappings in the array + +Return Value: + + The number of mappings successfully programmed + +--*/ + +{ + PAGED_CODE(); + + return + m_HardwareSimulation[Pin->Id]->ProgramScatterGatherMappings ( + Clone, + Buffer, + Mappings, + MappingsCount, + sizeof (KSMAPPING) + ); +} + +// Expose a pointer to the global ISP_FRAME_SETTINGS +ISP_FRAME_SETTINGS * +CSensor:: +GetGlobalIspSettings() +{ + PAGED_CODE(); + return &m_GlobalIspSettings; +} + +void +CSensor:: +UpdateZoom(void) +{ + PAGED_CODE(); +} + +// +// This "Null" definition fails the request as if it doesn't exist in the +// automation table. This is the default behavior for most properties in +// CSensor. +// +#define DEFINE_NULL_PROPERTY_FUNC( _sal_, type, func ) \ +NTSTATUS \ +func( \ + _sal_ type *Value \ + ) \ +{ \ + PAGED_CODE(); \ + UNREFERENCED_PARAMETER(Value); \ + DBG_OUT("DEFINE_NULL_PROPERTY_FUNC defined %s - STATUS_NOT_FOUND(0x%x)", #func, STATUS_NOT_FOUND);\ + return STATUS_NOT_FOUND; \ +} + +#define DEFINE_NULL_SIZEOF_FUNC( func ) \ +ULONG \ +func() \ +{ \ + PAGED_CODE(); \ + DBG_OUT("DEFINE_NULL_SIZEOF_FUNC defined %s - STATUS_SUCCESS(0x%x)", #func, 0);\ + return 0; \ +} + +#define DEFINE_NULL_PROPERTY_GET( T, type, name ) \ + DEFINE_NULL_PROPERTY_FUNC( _Inout_, type, T::Get##name ) + +#define DEFINE_NULL_PROPERTY_SET( T, type, name ) \ + DEFINE_NULL_PROPERTY_FUNC( _In_, type, T::Set##name ) + +#define DEFINE_NULL_PROPERTY( T, type, name ) \ + DEFINE_NULL_PROPERTY_GET( T, type, name ) \ + DEFINE_NULL_PROPERTY_SET( T, type, name ) + +#define DEFINE_NULL_PROPERTY_ASYNC( T, type, name ) \ + DEFINE_NULL_PROPERTY_GET( T, type, name ) \ + \ +NTSTATUS \ +T:: \ +Set##name##Async( \ + _In_ type *pProperty, \ + _In_ CNotifier *Notifier \ + ) \ +{ \ + PAGED_CODE(); \ + UNREFERENCED_PARAMETER(pProperty); \ + UNREFERENCED_PARAMETER(Notifier); \ + DBG_OUT("Set##name##Async defined null property for Set%sAsync - STATUS_NOT_FOUND(0x%x)", #name, STATUS_NOT_FOUND);\ + return STATUS_NOT_FOUND; \ +} \ + \ +NTSTATUS \ +T:: \ +Cancel##name() \ +{ \ + PAGED_CODE(); \ + DBG_OUT("Cancel##name defined null property for Cancel%s - STATUS_UNSUCCESSFUL(0x%x)", #name, STATUS_UNSUCCESSFUL);\ + return STATUS_UNSUCCESSFUL; \ +} + +#define DEFINE_NULL_PROPERTY_VARSIZE_ASYNC( T, type, name ) \ + DEFINE_NULL_PROPERTY_ASYNC( T, type, name ) \ + \ +ULONG \ +T:: \ +SizeOf##name() \ +{ \ + PAGED_CODE(); \ + DBG_OUT("SizeOf##name defined null property for SizeOf%s - STATUS_SUCCESS(0x%x)", #name, STATUS_SUCCESS);\ + return 0; \ +} + +// +// Define a bunch of "not implemented" function stubs. +// +DEFINE_NULL_PROPERTY_ASYNC(CSensor, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedPhotoMode, PhotoMode) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode) +DEFINE_NULL_PROPERTY_GET(CSensor, KSPROPERTY_VIDEOCONTROL_CAPS_S, VideoControlCaps) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_FLASH_S, Flash) +DEFINE_NULL_PROPERTY_GET(CSensor, KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence) + +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, TriggerTime) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, TorchMode) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, ExtendedFlash) + +DEFINE_NULL_PROPERTY_GET(CSensor, CExtendedProperty, PhotoFrameRate) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, PhotoMaxFrameRate) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, WarmStart) +DEFINE_NULL_PROPERTY(CSensor, CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes) +DEFINE_NULL_PROPERTY_GET(CSensor, CExtendedFieldOfView, FieldOfView) +DEFINE_NULL_PROPERTY_GET(CSensor, CExtendedCameraAngleOffset, CameraAngleOffset) +DEFINE_NULL_PROPERTY_GET(CSensor, KSCAMERA_EXTENDEDPROP_FOCUSSTATE, FocusState) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, Focus) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, Iso) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, IsoAdvanced) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedEvCompensation, EvCompensation) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, WhiteBalance) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, Exposure) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, SceneMode) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, Thumbnail) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, PhotoConfirmation) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, FocusPriority) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, VideoHDR) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, VFR) +DEFINE_NULL_PROPERTY(CSensor, CExtendedVidProcSetting, Zoom) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, VideoStabilization) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, Histogram) +DEFINE_NULL_PROPERTY(CSensor, CExtendedMetadata, Metadata) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, OpticalImageStabilization) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, OptimizationHint) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, AdvancedPhoto) +DEFINE_NULL_PROPERTY(CSensor, CExtendedVidProcSetting, FaceDetection) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Exposure) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Focus) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Zoom) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, ZoomRelative) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Pan) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Roll) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Tilt) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, Brightness); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, Contrast); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, Hue); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + +DEFINE_NULL_PROPERTY_GET(CSensor, CRoiConfig, RoiConfigCaps); +DEFINE_NULL_PROPERTY_VARSIZE_ASYNC(CSensor, CRoiProperty, Roi); + +_Success_(return == 0) +NTSTATUS +CSensor:: +GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Caps); + *Size = 0; + return STATUS_NOT_FOUND; +} diff --git a/AVStream/avscamera/sys/Sensor.h b/AVStream/avscamera/sys/Sensor.h new file mode 100644 index 00000000..061e6ed5 --- /dev/null +++ b/AVStream/avscamera/sys/Sensor.h @@ -0,0 +1,605 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Sensor.h + + Abstract: + + Base Sensor class. Derive from this class to implement an interface + to your hardware sensor object. + + This class provides a set of "knobs" to control a model camera. + + This class also controls access to the pin simulations. Most cameras + have a limited set of ISP resources and can only instantiate a fixed + number of pins. This class grants access to those resources. Other + than that, it's just an interface class. + + History: + + created 5/5/2014 + +**************************************************************************/ + +#pragma once + +// +// The following DECLARE_PROPERTY_XXX() macros are used to create consistent +// function names for each control. +// +#define DECLARE_PROPERTY_GET( type, name ) \ + virtual \ + NTSTATUS \ + Get##name( \ + _Inout_ type *pProperty \ + ); \ + +#define DECLARE_PROPERTY_SIZEOF( name ) \ + virtual \ + ULONG \ + SizeOf##name(); + +#define DECLARE_PROPERTY_SET( type, name ) \ + virtual \ + NTSTATUS \ + Set##name( \ + _In_ type *pProperty \ + ); + +#define DECLARE_PROPERTY_SET_ASYNC( type, name ) \ + virtual \ + NTSTATUS \ + Set##name##Async( \ + _In_ type *pProperty, \ + _In_ CNotifier *Notifier \ + ); + +#define DECLARE_PROPERTY_CANCEL( type, name ) \ + virtual \ + NTSTATUS \ + Cancel##name(); + +#define DECLARE_PROPERTY( type, name ) \ + DECLARE_PROPERTY_GET( type, name ) \ + DECLARE_PROPERTY_SET( type, name ) + +#define DECLARE_PROPERTY_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_GET( type, name ) \ + DECLARE_PROPERTY_SET_ASYNC( type, name ) + +#define DECLARE_PROPERTY_ASYNC( type, name ) \ + DECLARE_PROPERTY_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_CANCEL( type, name ) + +#define DECLARE_PROPERTY_VARSIZE_ASYNC( type, name ) \ + DECLARE_PROPERTY_ASYNC( type, name ) \ + DECLARE_PROPERTY_SIZEOF( name ) + +#define DECLARE_PROPERTY_VARSIZE_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_SIZEOF( name ) + + +// +// CNotifer +// +// A CNotiver is just a wrapper on a KS event. It holds the parameters +// needed to invoke the KS event successfully and it is referenced counted +// to make sure the filter does not go away before your sensor is done with +// the notifier. +// +// These objects should be instantiated in the filter and a reference +// held in your sensor object. +// +class CNotifier : public CRef +{ +private: + PVOID m_Object; + GUID m_EventSet; + ULONG m_EventId; + ULONG m_DataSize; + PVOID m_Data; + PFNKSGENERATEEVENTCALLBACK m_Callback; + PVOID m_CallbackContext; + +public: + CNotifier( + _In_ PKSFILTER Filter, + _In_ const GUID *EventSet, + _In_ ULONG EventId, + _In_ ULONG DataSize=0, + _In_opt_ PVOID Data=nullptr, + _In_opt_ PFNKSGENERATEEVENTCALLBACK Callback=nullptr, + _In_opt_ PVOID CallbackContext=nullptr + ) + : m_Object(Filter) + , m_EventSet(*EventSet) + , m_EventId(EventId) + , m_DataSize(DataSize) + , m_Data(Data) + , m_Callback(Callback) + , m_CallbackContext(CallbackContext) + {} + + CNotifier( + _In_ PKSPIN Pin, + _In_ const GUID *EventSet, + _In_ ULONG EventId, + _In_ ULONG DataSize=0, + _In_opt_ PVOID Data=nullptr, + _In_opt_ PFNKSGENERATEEVENTCALLBACK Callback=nullptr, + _In_opt_ PVOID CallbackContext=nullptr + ) + : m_Object(Pin) + , m_EventSet(*EventSet) + , m_EventId(EventId) + , m_DataSize(DataSize) + , m_Data(Data) + , m_Callback(Callback) + , m_CallbackContext(CallbackContext) + {} + + void + Set() + { + KsGenerateEvents(m_Object, &m_EventSet, m_EventId, m_DataSize, m_Data, m_Callback, m_CallbackContext); + } +}; + +class CSensor +{ +protected: + + CCaptureDevice *m_Device; + + ULONG m_PinCount; + LONG m_FilterInstanceCount; + + // + // The AVStream filter object associated with this CCaptureFilter. + // + CHardwareSimulation **m_HardwareSimulation; + CSynthesizer **m_Synthesizer; + ICapturePin **m_CapturePin; + + PKS_VIDEOINFOHEADER *m_VideoInfoHeader; + + // + // The number of ISR's that have occurred since capture started. + // + ULONG *m_InterruptTime; + + // + // The last reading of mappings completed. + // + ULONG *m_LastMappingsCompleted; + + static const ULONG INVALID_PIN_INDEX = 0xFFFFFFFF; + static const ULONG INVALID_PIN_MASK = 0; + + ULONG m_PreviewMask; + ULONG m_StillMask; + ULONG m_VideoMask; + + // Access control mutex. + KMutex m_SensorMutex; + + ULONG m_PfsLoopLimit; + ULONG m_PfsFrameLimit; + ISP_FRAME_SETTINGS *m_pIspSettings; + + ISP_FRAME_SETTINGS m_GlobalIspSettings; + + AcpiPldRotation m_MountingOrientation; + +public: + + CSensor( + _In_ CCaptureDevice *Device, + _In_ ULONG PinCount + ); + + // + // ~CSensor(): + // + // The h/w Sensor destructor. + // + virtual + ~CSensor( ); + + virtual + NTSTATUS + Initialize(); + + virtual + NTSTATUS + ProgramDefaults(); + + virtual + void + Interrupt( + _In_ LONG PinIndex + ); + + virtual + PDEVICE_OBJECT + GetDeviceObject() + { + return m_Device->GetDeviceObject(); + } + + virtual + NTSTATUS + CreateSynthesizer( + _In_ PKSPIN Pin, + _In_ PKS_VIDEOINFOHEADER VideoInfoHeader + ); + + // + // AcquireHardwareResources(): + // + // Called to acquire hardware resources for the device based on a given + // video info header. This will fail if another object has already + // acquired hardware resources since we emulate a single capture + // device. + // + virtual + NTSTATUS + AcquireHardwareResources( + _In_ PKSPIN Pin, + _In_ ICapturePin *CaptureSink, + _In_ PKS_VIDEOINFOHEADER VideoInfoHeader, + _Out_ CHardwareSimulation **pSim + ); + + // + // ReleaseHardwareResources(): + // + // Called to release hardware resources for the device. + // + virtual + void + ReleaseHardwareResources( + _In_ PKSPIN Pin + //_In_ CHardwareSimulation *pSim + ); + + // + // Start(): + // + // Called to start the hardware simulation. This causes us to simulate + // interrupts, simulate filling buffers with synthesized data, etc... + // + NTSTATUS + Start ( + _In_ PKSPIN Pin + ); + + // + // Pause(): + // + // Called to pause or unpause the hardware simulation. This will be + // indentical to a start or stop but it will not reset formats and + // counters. + // + NTSTATUS + Pause( + _In_ PKSPIN Pin, + _In_ BOOLEAN Pausing + ); + + NTSTATUS + Trigger( + _In_ ULONG PinId, + _In_ LONG mode + ); + + NTSTATUS + SetQPC( + _In_ ULONG StillIndex, + _In_ ULONGLONG QPC + ); + + NTSTATUS + GetQPC( + _In_ ULONG StillIndex, + _Out_ PULONGLONG QPC + ); + + NTSTATUS + SetPinMode( + _In_ ULONG StillIndex, + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers + ); + + NTSTATUS + SetPhotoFrameRate( + _In_ ULONG StillIndex, + _In_ ULONGLONG TimePerFrame + ); + + ULONGLONG + GetPhotoFrameRate( + _In_ ULONG StillIndex + ); + + NTSTATUS + SetFlashStatus( + _In_ ULONGLONG ulFlashStatus + ); + + bool + IsPreviewIndex( + _In_ ULONG Index + ) + { + return !!(m_PreviewMask & (1<<Index)) ; + } + + bool + IsStillIndex( + _In_ ULONG Index + ) + { + return !!(m_StillMask & (1<<Index)) ; + } + + bool + IsVideoIndex( + _In_ ULONG Index + ) + { + return !!(m_VideoMask & (1<<Index)) ; + } + + bool + IsValidIndex( + _In_ ULONG Index + ) + { + return Index < GetPinCount() ; + } + + inline ULONG GetNextPreviewIndex(ULONG Index=INVALID_PIN_INDEX) + { + while( IsValidIndex(++Index) ) + { + if( IsPreviewIndex(Index) ) + { + return Index; + } + } + return INVALID_PIN_INDEX; + } + + inline ULONG GetNextStillIndex(ULONG Index=INVALID_PIN_INDEX) + { + while( IsValidIndex(++Index) ) + { + if( IsStillIndex(Index) ) + { + return Index; + } + } + return INVALID_PIN_INDEX; + } + + inline ULONG GetNextVideoIndex(ULONG Index=INVALID_PIN_INDEX) + { + while( IsValidIndex(++Index) ) + { + if( IsVideoIndex(Index) ) + { + return Index; + } + } + return INVALID_PIN_INDEX; + } + + LONG + GetTriggerMode( + _In_ ULONG PinId + ); + + //Not thread safe, for User mode notification only. + LONG + GetFilterCount() + { + return m_FilterInstanceCount; + } + + NTSTATUS + AddFilter(PKSFILTER pFilter); + + NTSTATUS + RemoveFilter(PKSFILTER pFilter); + + ULONG + GetPinCount() + { + return m_PinCount; + } + + // + // GetFourCC + // + // Return the FourCC code for the current pin. + // + ULONG + GetFourCC( + _In_ ULONG Index + ) + { + return Index<m_PinCount ? m_VideoInfoHeader[Index]->bmiHeader.biCompression : 0; + } + + // + // Stop(): + // + // Called to stop the hardware simulation. This causes interrupts to + // stop issuing. When this call returns, the "fake" hardware has + // stopped accessing all s/g buffers, etc... + // + NTSTATUS + Stop ( + _In_ PKSPIN + ); + + NTSTATUS + Reset( + _In_ PKSPIN + ); + + NTSTATUS + Reset( + ); + + + // + // ProgramScatterGatherMappings(): + // + // Called to program the hardware simulation's scatter / gather table. + // This synchronizes with the "fake" ISR and hardware simulation via + // a spinlock. + // + ULONG + ProgramScatterGatherMappings ( + _In_ PKSPIN Pin, + _In_ PKSSTREAM_POINTER *Clone, + _In_ PUCHAR *Buffer, + _In_ PKSMAPPING Mappings, + _In_ ULONG MappingsCount + ); + + // Lets you know if Variable Photo Sequence is active. + // Note: This doesn't tell you if the pin is running. + virtual + BOOLEAN + IsVPSActive() + { + // VPS isn't supported by default. + return FALSE; + } + + // Fetch a pointer to the global ISP_FRAME_SETTINGS + virtual + ISP_FRAME_SETTINGS * + GetGlobalIspSettings(); + + // Program the Per Frame Settings for simulation + // We do it before calling start so we can be ready + // to program our simulation's hardware. + // Note: We make a local copy. + virtual + NTSTATUS + SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit + ); + + virtual + void + UpdateZoom(void); + + void + SetMountingOrientation( + _In_ AcpiPldRotation Orientation + ) + { + m_MountingOrientation = Orientation; + } + +protected: + LONG + IncrementFilterCount() + { + return InterlockedIncrement(&m_FilterInstanceCount); + } + + LONG + DecrementFilterCount() + { + return InterlockedDecrement(&m_FilterInstanceCount); + } + +public: + + DECLARE_PROPERTY( KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode ); + DECLARE_PROPERTY_GET( KSPROPERTY_VIDEOCONTROL_CAPS_S, VideoControlCaps ); + + DECLARE_PROPERTY_GET( KSCAMERA_EXTENDEDPROP_FOCUSSTATE, FocusState ); + DECLARE_PROPERTY_ASYNC( KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ); + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ); + DECLARE_PROPERTY_GET( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence ); + + DECLARE_PROPERTY_ASYNC( CExtendedPhotoMode, PhotoMode ); + DECLARE_PROPERTY_GET( CExtendedProperty, PhotoFrameRate ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, PhotoMaxFrameRate ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, WarmStart ); + DECLARE_PROPERTY( CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ); + DECLARE_PROPERTY_GET( CExtendedFieldOfView, FieldOfView ); + DECLARE_PROPERTY_GET( CExtendedCameraAngleOffset, CameraAngleOffset ); + DECLARE_PROPERTY( CExtendedProperty, ExtendedFlash ); + + DECLARE_PROPERTY( CExtendedProperty, TriggerTime ); + DECLARE_PROPERTY( CExtendedProperty, TorchMode ); + + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, Focus ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, Iso ); + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, IsoAdvanced ); + DECLARE_PROPERTY_ASYNC( CExtendedEvCompensation, EvCompensation ); + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, WhiteBalance ); + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, Exposure ); + DECLARE_PROPERTY( CExtendedProperty, PhotoConfirmation ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, SceneMode ); + DECLARE_PROPERTY( CExtendedProperty, FocusPriority ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, Thumbnail ); + DECLARE_PROPERTY( CExtendedProperty, VideoHDR ); + DECLARE_PROPERTY( CExtendedProperty, VFR ); + DECLARE_PROPERTY( CExtendedVidProcSetting, Zoom ); + DECLARE_PROPERTY( CExtendedProperty, VideoStabilization ); + DECLARE_PROPERTY( CExtendedProperty, Histogram ); + DECLARE_PROPERTY( CExtendedMetadata, Metadata ); + DECLARE_PROPERTY( CExtendedProperty, OpticalImageStabilization ); + DECLARE_PROPERTY( CExtendedProperty, OptimizationHint ); + DECLARE_PROPERTY( CExtendedProperty, AdvancedPhoto ); + DECLARE_PROPERTY( CExtendedVidProcSetting, FaceDetection ); + + DECLARE_PROPERTY_VARSIZE_ASYNC( CRoiProperty, Roi ); + + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ); + + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Exposure); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Focus); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Zoom); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, ZoomRelative); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Pan); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Roll); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Tilt); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength); + + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Brightness); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Contrast); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Hue); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + + DECLARE_PROPERTY_GET( CRoiConfig, RoiConfigCaps ); + + _Success_(return == 0) + virtual + NTSTATUS + GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size + ); +}; + diff --git a/AVStream/avscamera/sys/SensorSimulation.cpp b/AVStream/avscamera/sys/SensorSimulation.cpp new file mode 100644 index 00000000..3ebeb56b --- /dev/null +++ b/AVStream/avscamera/sys/SensorSimulation.cpp @@ -0,0 +1,3537 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + SensorSimulation.cpp + + Abstract: + + Simulation class implementation. Derived from the base CSensor object. + + This class simulates a theoretical model camera. The camera supports + every modern camera control available and produces a simulation of its + behavior. In most cases this simulation is very very basic because we + have no actual hardware to work with and there producing a visible + effect in the simulation would be difficult or expensive to do. + + History: + + created 5/8/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// +// The following macro is used to define a legacy control's range and +// default value. We use the macro simply to minimize the verbosity of +// our declarations. +// +#define DEFINE_LEGACY_CTRL_CAPS( Control, Minimum, Maximum, SteppingDelta, DefValue ) \ +/*-------------------------------------------------------------- \ + This structure defines what the control is capable of. \ +--------------------------------------------------------------*/ \ +KSPROPERTY_STEPPING_LONG Control##RangeAndStep[] = \ +{ \ + { \ + SteppingDelta, /* SteppingDelta */ \ + 0, /* Reserved */ \ + Minimum, /* Minimum */ \ + Maximum /* Maximum */ \ + } \ +}; \ + \ +/* This is the default control value. */ \ +const LONG Control##Default = DefValue; \ + \ +KSPROPERTY_MEMBERSLIST Control##MembersList[] = \ +{ \ + { \ + { \ + KSPROPERTY_MEMBER_STEPPEDRANGES, \ + sizeof (Control##RangeAndStep), \ + SIZEOF_ARRAY(Control##RangeAndStep), \ + 0 \ + }, \ + (PVOID)Control##RangeAndStep, \ + }, \ + { \ + { \ + KSPROPERTY_MEMBER_VALUES, \ + sizeof (Control##Default), \ + 1, \ + KSPROPERTY_MEMBER_FLAG_DEFAULT \ + }, \ + (PVOID)&Control##Default \ + } \ +}; \ + \ +KSPROPERTY_VALUES Control##Values = \ +{ \ + { \ + STATICGUIDOF(KSPROPTYPESETID_General), \ + VT_I4, \ + 0 \ + }, \ + SIZEOF_ARRAY(Control##MembersList), \ + Control##MembersList \ +}; + +DEFINE_LEGACY_CTRL_CAPS( ZoomRelative, -2, +2, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Zoom, FOCALLENGTH_OPTICAL_MIN, FOCALLENGTH_OPTICAL_MAX, 1, FOCALLENGTH_OCULAR ) +DEFINE_LEGACY_CTRL_CAPS( Pan, -180, 180, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Roll, -180, 180, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Tilt, -180, 180, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( BacklightCompensation, 0, 1, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Brightness, -10000, 10000, 1, 750 ) +DEFINE_LEGACY_CTRL_CAPS( Contrast, -10000, 10000, 1, 100 ) +DEFINE_LEGACY_CTRL_CAPS( Hue, -18000, 18000, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( PLF, POWERLINEFREQ_DISABLED, POWERLINEFREQ_AUTO, 1, POWERLINEFREQ_DEFAULT ) +DEFINE_LEGACY_CTRL_CAPS( WhiteBalance, WHITEBALANCE_MIN, WHITEBALANCE_MAX, WHITEBALANCE_STEP, WHITEBALANCE_DEF ) +DEFINE_LEGACY_CTRL_CAPS( Exposure, EXPOSURE_BILOG_MIN, EXPOSURE_BILOG_MAX, EXPOSURE_BILOG_STEP, EXPOSURE_BILOG_DEF) +DEFINE_LEGACY_CTRL_CAPS( Focus, 1, 1200, 1, 100 ); + +void +CSensorSimulation:: +FocusConvergence( + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + A timer callback that simulates focus convergence. + + Update the focus state to focused. + + Update the position to a random location that is in bounds if we are in + an auto or continuous auto mode. The new location is probably not + realistic; but it is important to simulate the change in location for + testing. + + Notice that we do no kick off the timer again on continuous auto focus. + A camera that is performing continuous auto focus in software would + probably do something there. + +Arguments: + + Context - + In our case, this is a pointer to our simulation object. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + if( Context ) + { + CSensorSimulation *Sensor = (CSensorSimulation *) Context; + KScopedMutex lock( Sensor->m_SensorMutex ); + + if( Sensor->m_FocusNotifier ) + { + Sensor->m_FocusNotifier->Set(); + Sensor->m_FocusNotifier = nullptr; + } + + // Let's just fake a focus change if we get here from somewhere else. + // This only applies to auto focus modes and only if we're not focused. + if( (Sensor->m_GlobalIspSettings.FocusMode & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS)) && + Sensor->m_FocusState != KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED ) + { + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + GetRandom( Sensor->m_GlobalIspSettings.FocusSetting.Min, + Sensor->m_GlobalIspSettings.FocusSetting.Max ); + } + else if( Sensor->m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + switch( Sensor->m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_INFINITY: + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + Sensor->m_GlobalIspSettings.FocusSetting.Max; + break; + + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_HYPERFOCAL: + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + Sensor->m_GlobalIspSettings.FocusSetting.Max - Sensor->m_GlobalIspSettings.FocusSetting.Step; + break; + + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_NEAREST: + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + Sensor->m_GlobalIspSettings.FocusSetting.Min; + break; + + } + } + + // TODO: + // If we're continuous focus, we should probably kick off another focus change here... + // ... but that tends to mess with testing. + + // We always want to transition to here. + Sensor->m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED; + } +} + +void +CSensorSimulation:: +FocusRectConvergence( + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + A timer callback that simulates FocusRect convergence. + +Arguments: + + Context - + In our case, this is a pointer to our simulation object. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + if( Context ) + { + CSensorSimulation *Sensor = (CSensorSimulation *) Context; + KScopedMutex lock( Sensor->m_SensorMutex ); + + if( Sensor->m_FocusRectNotifier ) + { + Sensor->m_FocusRectNotifier->Set(); + Sensor->m_FocusRectNotifier = nullptr; + } + + // Not much else to do here. + } +} + +CSensorSimulation::CSensorSimulation( + _In_ CCaptureDevice *Device, + _In_ const KSFILTER_DESCRIPTOR *Descriptors +) + : CSensor( Device, Descriptors->PinDescriptorsCount ) + , m_Descriptors( Descriptors ) + , m_MetadataInfo( nullptr ) + , m_FocusTimer( nullptr ) + , m_FocusRectTimer( nullptr ) + +{ + PAGED_CODE(); + + KeQuerySystemTime (&m_StartTime); +} + + +CSensorSimulation::~CSensorSimulation() +{ + PAGED_CODE(); + + delete m_FocusTimer; + delete m_FocusRectTimer; + delete [] m_MetadataInfo; + delete [] (PBYTE) m_pPerFrameSettings; + delete [] m_WarmStartEnabled; +} + +NTSTATUS +CSensorSimulation:: +ProgramDefaults() +/*++ + +Routine Description: + + Reset our simulation back to the DDI-specified defaults. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + m_FocusNotifier = nullptr; + m_pPerFrameSettings = nullptr; + + + m_FaceDetectionMax = MAX_FACES; + m_FaceDetectionFlags = KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF; + m_FaceDetectionCurrentMax = 0; + m_AutoFocusLock = false; + m_AutoExposureLock = false; + m_AutoWhitebalanceLock = false; + m_Flash = 0; + m_VideoStabMode = 0; + m_VFR = KSCAMERA_EXTENDEDPROP_VFR_ON; + m_VideoHDR = KSCAMERA_EXTENDEDPROP_VIDEOHDR_OFF; + m_VideoStabilization = KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF; + m_Histogram = KSCAMERA_EXTENDEDPROP_HISTOGRAM_OFF; + m_OpticalImageStabilization = KSCAMERA_EXTENDEDPROP_OIS_AUTO; + + m_AdvancedPhoto.PinId = GetNextStillIndex(); + m_AdvancedPhoto.Flags = KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF; + m_AdvancedPhoto.Capability = KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT; + + m_FocusRect.left = 0; + m_FocusRect.top = 0; + m_FocusRect.right = 0; + m_FocusRect.bottom = 0; + + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED; + + m_PhotoMode = CExtendedPhotoMode(); + m_PhotoMode.Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE; + m_PhotoMode.PinId = GetNextStillIndex(); + m_PhotoMode.MaxHistoryFrames() = IMAGE_CAPTURE_PIN_MAXIMUM_HISTORY_FRAMES; + m_PhotoMode.RequestedHistoryFrames() = 0; + m_PhotoMode.SubMode() = KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_NONE; + + m_MaxFrameRate = CExtendedProperty(); // assume nothing. + + for( ULONG Index = GetNextStillIndex(); IsValidIndex( Index ); Index = GetNextStillIndex( Index ) ) + { + SetQPC( Index, 0 ); + } + m_TorchMode.Flags = KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF; + m_TorchMode = 50UL; + + m_OptimizationHint.Flags = KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO; + + m_SceneMode = KSCAMERA_EXTENDEDPROP_SCENEMODE_AUTO; + + // Wipe the global settings to zeros for now. + RtlFillBytes(&m_GlobalIspSettings, sizeof(m_GlobalIspSettings), 0); + + m_GlobalIspSettings.FlashMode = KSCAMERA_EXTENDEDPROP_FLASH_OFF; + m_GlobalIspSettings.FlashValue = 50; + + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.ISOValue = 200; + + m_GlobalIspSettings.EVCompensation.Mode = 0; + m_GlobalIspSettings.EVCompensation.Min = -2; + m_GlobalIspSettings.EVCompensation.Max = 2; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.WhiteBalanceSetting.Max = WHITEBALANCE_MAX; + m_GlobalIspSettings.WhiteBalanceSetting.Min = WHITEBALANCE_MIN; + m_GlobalIspSettings.WhiteBalanceSetting.Step = WHITEBALANCE_STEP; + m_GlobalIspSettings.WhiteBalanceSetting.Reserved = 0; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.Max = (MAX_EXPOSURE_TIME>LONG_MAX) ? LONG_MAX : (LONG)(LONG_MAX & MAX_EXPOSURE_TIME); + m_GlobalIspSettings.ExposureSetting.Min = (LONG)MIN_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Step = 1; + m_GlobalIspSettings.ExposureSetting.Reserved = 0; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.Max = 1200; + m_GlobalIspSettings.FocusSetting.Min = 1; + m_GlobalIspSettings.FocusSetting.Step = 1; + m_GlobalIspSettings.FocusSetting.Reserved = 0; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + m_GlobalIspSettings.FocusSetting.Mode = 0; + m_GlobalIspSettings.FocusPriority = KSCAMERA_EXTENDEDPROP_FOCUSPRIORITY_ON; + + m_GlobalIspSettings.bPhotoConfirmation = TRUE; + + // Defaults to fall back to if we get a cancel. + m_LastFocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_LastFocusSetting = 100; + + + // Specifies the dynamic range of our fictious camera's lens system + // Note: Magnification = Lobjective / Locular + + // The following values set only the minimum and maximum for the rational number expressing the zoom. + + // This specifies the denominator of the rational value used to specify magnification. + m_FocalLength.lOcularFocalLength = FOCALLENGTH_OCULAR; + // If lObjectiveFocalLengthMin < lOcularFocalLength, then camera allows Zoom out: aka, wide-field view. + m_FocalLength.lObjectiveFocalLengthMin = FOCALLENGTH_OPTICAL_MIN; + // If lObjectiveFocalLengthMax > lOcularFocalLength, then camera allows Zoom in: aka, Magnified, narrow-field view. + m_FocalLength.lObjectiveFocalLengthMax = FOCALLENGTH_OPTICAL_MAX; + + // Cache values for the new extended zoom property. + m_ExtendedZoom.Flags = KSCAMERA_EXTENDEDPROP_ZOOM_DIRECT; + m_ExtendedZoom.Min() = (LONG)(TO_Q16(FOCALLENGTH_OPTICAL_MIN) / FOCALLENGTH_OCULAR); + m_ExtendedZoom.Max() = (LONG)(TO_Q16(FOCALLENGTH_OPTICAL_MAX) / FOCALLENGTH_OCULAR); + m_ExtendedZoom.Step() = 1; // This avoids the rounding issue. + m_ExtendedZoom = ZoomDefault; + //TO_Q16(ZoomRangeAndStep[0].SteppingDelta) / FOCALLENGTH_OCULAR; + + // Set up our current zoom. + m_Zoom.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE | + KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE; + m_Zoom.Value = ZoomDefault; + m_Zoom.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + // Set up our relative zoom. + m_ZoomRelative.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_ZoomRelative.Value = ZoomRelativeDefault; + m_ZoomRelative.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_Pan.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_Pan.Value = PanDefault; + m_Pan.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_Roll.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_Roll.Value = RollDefault; + m_Roll.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_Tilt.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_Tilt.Value = TiltDefault; + m_Tilt.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_BacklightCompensation.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_BacklightCompensation.Value = BacklightCompensationDefault; + m_BacklightCompensation.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + m_Brightness.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_Brightness.Value = BrightnessDefault; + m_Brightness.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + m_Contrast.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_Contrast.Value = ContrastDefault; + m_Contrast.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + m_Hue.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_Hue.Value = HueDefault; + m_Hue.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + // Set up our Power Line Frequency defaults. + m_PowerLineFreq.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + m_PowerLineFreq.Value = POWERLINEFREQ_DEFAULT; + m_PowerLineFreq.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + + m_IsoResult = STATUS_SUCCESS; + m_EvCompResult = STATUS_SUCCESS; + m_WhiteBalanceResult= STATUS_SUCCESS; + m_ExposureResult = STATUS_SUCCESS; + m_SceneModeResult = STATUS_SUCCESS; + m_FaceDetectionResult = STATUS_SUCCESS; + m_FocusResult = STATUS_SUCCESS; + + for( ULONG PinIndex=0; PinIndex<GetPinCount(); PinIndex++ ) + { + // Reset the metadata control. + m_MetadataInfo[PinIndex] = CExtendedMetadata(PinIndex); + + // Reset the warmstart control. + m_WarmStartEnabled[PinIndex] = CExtendedProperty(KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_DISABLED); + m_WarmStartEnabled[PinIndex].PinId = PinIndex; + } + + DBG_LEAVE("()"); + return STATUS_SUCCESS; +} + +NTSTATUS +CSensorSimulation:: +Initialize() +/*++ + +Routine Description: + + Perform memory allocations for the simulation. Inspect the descriptors + for pins and allocate appropriate simulation objects based on the pin's + type. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER("()"); + NTSTATUS Status = STATUS_SUCCESS; + IFFAILED_EXIT( Status = CSensor::Initialize() ); + + IFNULL_EXIT( m_FocusTimer = new (NonPagedPoolNx, 'emiT') KPassiveTimer( m_Device->GetDeviceObject() ) ); + IFNULL_EXIT( m_FocusRectTimer = new (NonPagedPoolNx, 'emiT') KPassiveTimer( m_Device->GetDeviceObject() ) ); + IFNULL_EXIT( m_MetadataInfo = new (PagedPool, 'ateM') CExtendedMetadata[GetPinCount()] ); + IFNULL_EXIT( m_WarmStartEnabled = new (PagedPool, 'mraW' ) CExtendedProperty[GetPinCount()] ); + + for( ULONG PinIndex=0; + NT_SUCCESS(Status) && PinIndex<m_Descriptors->PinDescriptorsCount; + PinIndex++ ) + { + const KSPIN_DESCRIPTOR_EX *PinDescriptors = m_Descriptors->PinDescriptors; + CHardwareSimulation *pSim=nullptr; + + NT_ASSERT( m_Descriptors->PinDescriptorSize == sizeof( *PinDescriptors ) ); + + // Video pin type + if( IsEqualGUID( *PinDescriptors[PinIndex].PinDescriptor.Category, PIN_CATEGORY_CAPTURE ) ) + { + pSim = new (NonPagedPoolNx, 'ediV') CVideoHardwareSimulation( this, PinIndex ); + m_VideoMask |= 1<<PinIndex; + } + else if( IsEqualGUID( *PinDescriptors[PinIndex].PinDescriptor.Category, PIN_CATEGORY_PREVIEW ) ) + { + pSim = new (NonPagedPoolNx, 'verP') CPreviewHardwareSimulation( this, PinIndex ); + m_PreviewMask |= 1<<PinIndex; // must set to enable photo confirmation... + } + else if( IsEqualGUID( *PinDescriptors[PinIndex].PinDescriptor.Category, PINNAME_IMAGE ) ) + { + pSim = new (NonPagedPoolNx, 'litS') CImageHardwareSimulation( this, PinIndex ); + m_StillMask |= 1<<PinIndex; + } + else + { + // We don't know this pin type, so skip it. + m_HardwareSimulation[PinIndex] = nullptr; + continue; + } + + if( pSim ) + { + if( pSim->Initialize() ) + { + m_HardwareSimulation[PinIndex] = pSim; + } + else + { + // + // If we couldn't create the hardware simulation, fail. + // + delete pSim; + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + +done: + DBG_ENTER("()=0x%08X", Status); + return Status; +} + +/************************************************************************** + + Sensor-level control simulations + + 1. Query capabilities and state of the device. + 2. Issue a command or set state on the device. + +**************************************************************************/ + +// Get for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +GetFocusRect( + _Inout_ PKSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S pRoi +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pRoi->FocusRect = m_FocusRect; + pRoi->AutoFocusLock = m_AutoFocusLock; + pRoi->AutoExposureLock = m_AutoExposureLock; + pRoi->AutoWhitebalanceLock = m_AutoWhitebalanceLock; + pRoi->Capabilities = KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_FLAGS_ASYNC | KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_CONFIG_FOCUS | + KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_CONFIG_EXPOSURE; + return STATUS_SUCCESS; +} + +// Set for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +SetFocusRectAsync( + _In_ PKSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S pRoi, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + if( m_FocusRectNotifier ) + { + // The last operation is still pending. + return STATUS_INVALID_DEVICE_STATE; + } + + m_FocusRectNotifier = Notifier; + + m_FocusRect = pRoi->FocusRect; + m_AutoFocusLock = pRoi->AutoFocusLock; + m_AutoExposureLock = pRoi->AutoExposureLock; + m_AutoWhitebalanceLock = pRoi->AutoWhitebalanceLock; + + if(m_AutoFocusLock) + { + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + } + if(m_AutoExposureLock) + { + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll = DEF_EXPOSURE_TIME; + DBG_TRACE("ExposureMode=KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO, ExposureTime=%lld00ns", + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + } + if(m_AutoWhitebalanceLock) + { + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + } + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -2500000LL, // 250ms + FocusRectConvergence, + this ); + + return STATUS_SUCCESS; +} + +// Cancel an outstanding KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID Set +NTSTATUS +CSensorSimulation:: +CancelFocusRect() +{ + PAGED_CODE(); + + // Per spec, this control cannot be cancelled. So we block until complete. + // Even if we didn't block here, the CNotifier object is reference counted, + // so the CCaptureFilter would block until the operation completes. + if( m_FocusRectNotifier ) + { + m_FocusRectTimer->Wait(); + } + return STATUS_UNSUCCESSFUL; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CSensorSimulation:: +GetFaceDetection( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedVidProcSetting( m_FaceDetectionFlags ); + + pProperty->Capability = + KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_PREVIEW | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_VIDEO | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_PHOTO | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_SMILE ; + + pProperty->Max() = m_FaceDetectionMax; + pProperty->Min() = 1; + pProperty->Step() = 1; + *pProperty = m_FaceDetectionCurrentMax; + pProperty->Result = m_FaceDetectionResult; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CSensorSimulation:: +SetFaceDetection( + _In_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_FaceDetectionCurrentMax = + ( KSCAMERA_EXTENDEDPROP_FACEDETECTION_MASK & pProperty->Flags ) ? + pProperty->GetULONG() : 0; + m_FaceDetectionFlags = pProperty->Flags; + m_FaceDetectionResult = STATUS_SUCCESS; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSSTATE +NTSTATUS +CSensorSimulation:: +GetFocusState( + _Out_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE *FocusState +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *FocusState = m_FocusState; + + DBG_LEAVE("(%s)", FocusStateDbgTxt(m_FocusState)); + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CSensorSimulation:: +GetFocus( + _Inout_ CExtendedVidProcSetting *pSetting +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Mode + pSetting->Flags = m_GlobalIspSettings.FocusMode; + + // capabilities. + pSetting->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_CAPS_CANCELLABLE | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF | + KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_INFINITY | + KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_HYPERFOCAL | + KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_NEAREST + ; + + pSetting->Result = m_FocusResult; + + // min/max/step/etc. + pSetting->m_Setting = m_GlobalIspSettings.FocusSetting; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CSensorSimulation:: +SetFocusAsync( + _In_ CExtendedVidProcSetting *pSetting, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Signal immediately if the we are continuous focus. + bool bSignalImmediately = false; + ULONGLONG Flags = pSetting->Flags; + + // Cache our current focus settings in case we get a cancel request. + m_LastFocusMode = m_GlobalIspSettings.FocusMode; + m_LastFocusSetting = m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul; + + // If we've been asked to unlock, remove any lock flags. + if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK ) + { + // Mark us as signalling immediately if Focus was not locked. + bSignalImmediately = + ( m_GlobalIspSettings.FocusMode & + ( KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK ) ) == 0; + + // Change our operation so we go back to the previous state. + Flags = m_GlobalIspSettings.FocusMode & + ~( KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK ); + } + + // Handle the stand-alone lock case by setting the appropriate flags. + if( ( Flags & + (KSCAMERA_EXTENDEDPROP_FOCUS_MODE_MASK | + KSCAMERA_EXTENDEDPROP_FOCUS_MODE_ADVANCED_MASK) ) == + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Flags = + m_GlobalIspSettings.FocusMode | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + } + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS ) + { + Flags = + m_GlobalIspSettings.FocusMode |= KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK; + } + } + + // Record the Focus mode. + DBG_TRACE("Recording FocusMode = 0x%016llX", Flags); + m_GlobalIspSettings.FocusMode = Flags; + + if(m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL) + { + m_GlobalIspSettings.FocusSetting.VideoProc.Value.l = pSetting->GetLONG(); + } + + if( bSignalImmediately ) + // Unlock, but nothing really to do. + { + Notifier->Set(); + m_FocusNotifier = nullptr; + } + else if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS ) + // Continuous case: Signal immediately. + // Then prime the timer to switch the state to focused. + { + m_FocusNotifier = nullptr; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + Notifier->Set(); + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -4000000LL, // 400ms + FocusConvergence, + this ); + } + else if( Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + // Normal [auto?] focus operation. + // Autofocus should be SLOW. + { + m_FocusNotifier = Notifier; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -4000000LL, // 400ms + FocusConvergence, + this ); + } + else + // Handle Manual, lock & unlock cases FAST ~1 frame time. + { + m_FocusNotifier = Notifier; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -330000LL, // 33ms + FocusConvergence, + this ); + } + + m_FocusResult = STATUS_SUCCESS; + return STATUS_SUCCESS; +} + +// Cancel oustanding set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CSensorSimulation:: +CancelFocus() +{ + PAGED_CODE(); + + // Cancel our timer to prevent a race. + m_FocusTimer->Cancel(); + + // Acquire the lock after the cancel to avoid a potential deadlock. + KScopedMutex lock( m_SensorMutex ); + + // Now check to see if an operation was still outstanding. + // We could just check the status from the timer's Cancel(), but I + // prefer checking to see if the handler cleared the notifier instead. + if( m_FocusNotifier ) + { + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_LOST; + + // Cancel behavior isn't well-defined in the spec. Do something reasonable. + // In our case, we'll put the focus mode and value back to the last setting. + m_GlobalIspSettings.FocusMode = m_LastFocusMode; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = m_LastFocusSetting; + + m_FocusNotifier->Set(); + m_FocusNotifier = nullptr; + m_FocusResult = (ULONG) STATUS_CANCELLED; + return STATUS_SUCCESS; + } + return STATUS_UNSUCCESSFUL; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CSensorSimulation:: +GetExtendedFlash( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_GlobalIspSettings.FlashMode ); + *pProperty = m_GlobalIspSettings.FlashValue; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_FLASH_ON | KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_AUTO | KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_REDEYEREDUCTION | KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH | + KSCAMERA_EXTENDEDPROP_FLASH_MULTIFLASHSUPPORTED | + KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_ON | KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_OFF | + KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_AUTO + ; + + DBG_TRACE("FlashMode=0x%016llX, FlashValue=%d", + m_GlobalIspSettings.FlashMode, + m_GlobalIspSettings.FlashValue ); + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CSensorSimulation:: +SetExtendedFlash( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + if(pProperty->Flags & (KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER) ) + { + m_GlobalIspSettings.FlashValue = pProperty->m_Value.Value.ul; + } + else + { + m_GlobalIspSettings.FlashValue = 50; + } + + m_GlobalIspSettings.FlashMode = pProperty->Flags; + NTSTATUS Status = SetFlashStatus(pProperty->Flags); + + DBG_TRACE("FlashMode=0x%016llX, FlashValue=%d, Status=0x%08X", + m_GlobalIspSettings.FlashMode, + m_GlobalIspSettings.FlashValue, + Status ); + + pProperty->Result = (ULONG) Status; + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CSensorSimulation:: +GetIso( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pProperty->Flags = IsoModeValue2Preset( + m_GlobalIspSettings.ISOMode, + m_GlobalIspSettings.ISOValue ); + pProperty->Result = STATUS_SUCCESS; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_ISO_AUTO | + KSCAMERA_EXTENDEDPROP_ISO_50 | KSCAMERA_EXTENDEDPROP_ISO_80 | + KSCAMERA_EXTENDEDPROP_ISO_100 | KSCAMERA_EXTENDEDPROP_ISO_200 | + KSCAMERA_EXTENDEDPROP_ISO_400 | KSCAMERA_EXTENDEDPROP_ISO_800 | + KSCAMERA_EXTENDEDPROP_ISO_1600 | KSCAMERA_EXTENDEDPROP_ISO_3200 | + KSCAMERA_EXTENDEDPROP_ISO_6400 | KSCAMERA_EXTENDEDPROP_ISO_12800 | + KSCAMERA_EXTENDEDPROP_ISO_25600 | + KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pProperty->Result = m_IsoResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CSensorSimulation:: +SetIsoAsync( + _In_ CExtendedProperty *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_GlobalIspSettings.ISOMode = (ULONG)pProperty->Flags; + m_GlobalIspSettings.ISOValue = 0; + + // This could be done in a separate work item after the scene mode has been programmed... + m_IsoResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +#define KSCAMERA_EXTENDEDPROP_ISO_ADVANCED_MASK (KSCAMERA_EXTENDEDPROP_ISO_AUTO | KSCAMERA_EXTENDEDPROP_ISO_MANUAL) + +// Create a set of default settings... +static +const +KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING g_IsoAdvancedSettings = +{ + 0, // Mode - Unused + 50, // Min + 25600, // Max + 1 // Step (advertised doesn't have to be actual) +}; + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CSensorSimulation:: +GetIsoAdvanced( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Initialize the property with defaults + (AUTO | MANUAL) + pProperty->Flags = + m_GlobalIspSettings.ISOMode & + KSCAMERA_EXTENDEDPROP_ISO_ADVANCED_MASK; + pProperty->Result = STATUS_SUCCESS; + + // Advertise basic caps... + pProperty->Capability = + KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | + KSCAMERA_EXTENDEDPROP_ISO_AUTO | + KSCAMERA_EXTENDEDPROP_ISO_MANUAL ; + + // Advertise the manual caps... + *pProperty = g_IsoAdvancedSettings; + + // If ISO is MANUAL, set the value. + if( m_GlobalIspSettings.ISOMode == KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + *pProperty = m_GlobalIspSettings.ISOValue; + } + else if( !(m_GlobalIspSettings.ISOMode == KSCAMERA_EXTENDEDPROP_ISO_AUTO) ) + { + // Try converting any legacy presets to a manual value. + pProperty->Flags = KSCAMERA_EXTENDEDPROP_ISO_MANUAL; + // Convert the ISO setting + *pProperty = IsoPreset2Value( m_GlobalIspSettings.ISOMode ); + // If we weren't able to report a valid number, just report something reasonable... + if( pProperty->GetLONG() > pProperty->Max() ) + { + *pProperty = pProperty->Min(); + } + } + pProperty->Result = m_IsoResult; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CSensorSimulation:: +SetIsoAdvancedAsync( + _In_ CExtendedVidProcSetting *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + + KScopedMutex lock( m_SensorMutex ); + + // Just save the parameters + if( pProperty->Flags & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + m_GlobalIspSettings.ISOMode = pProperty->Flags; + m_GlobalIspSettings.ISOValue= pProperty->GetULONG(); + } + else if( pProperty->Flags & KSCAMERA_EXTENDEDPROP_ISO_AUTO ) + { + m_GlobalIspSettings.ISOMode = pProperty->Flags; + } + + // Set regardless - it takes effect immediately for us. + m_IsoResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CSensorSimulation:: +GetEvCompensation( + _Inout_ CExtendedEvCompensation *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedEvCompensation( m_GlobalIspSettings.EVCompensation.Mode ); + + // Return our cached copy; but update the value from the global setting. + *pProperty = m_GlobalIspSettings.EVCompensation; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP | KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP | + KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP | KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP | + KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP | KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pProperty->Result = m_EvCompResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CSensorSimulation:: +SetEvCompensationAsync( + _In_ CExtendedEvCompensation *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Update the Globals + m_GlobalIspSettings.EVCompensation.Mode = (ULONG) pProperty->Flags; + m_GlobalIspSettings.EVCompensation.Value = pProperty->Value(); + + // Update the current setting in case we're queried for it later. + m_GlobalIspSettings.EVCompensation.Value = pProperty->Value(); + + // This could be done in a separate work item after the device has been programmed... + m_EvCompResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CSensorSimulation:: +GetWhiteBalance( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedVidProcSetting( m_GlobalIspSettings.WhiteBalanceMode ); + + // Return our cached copy; but update the value from the global setting. + *pProperty = m_GlobalIspSettings.WhiteBalanceSetting; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + pProperty->Result = m_WhiteBalanceResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CSensorSimulation:: +SetWhiteBalanceAsync( + _In_ CExtendedVidProcSetting *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_GlobalIspSettings.WhiteBalanceMode = pProperty->Flags; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = pProperty->Mode(); + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = pProperty->GetULONG(); + + // This could be done in a separate work item after the device has been programmed... + m_WhiteBalanceResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSURE +NTSTATUS +CSensorSimulation:: +GetExposure( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedVidProcSetting( m_GlobalIspSettings.ExposureMode ); + + // Return our cached copy; but update the value from the global setting. + *pProperty = m_GlobalIspSettings.ExposureSetting; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + pProperty->Result = m_ExposureResult; + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + pProperty->Flags, pProperty->GetLONGLONG()); + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSURE +NTSTATUS +CSensorSimulation:: +SetExposureAsync( + _In_ CExtendedVidProcSetting *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + pProperty->Flags, pProperty->GetLONGLONG()); + if( pProperty->Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ull = pProperty->GetULONGLONG(); + } + m_GlobalIspSettings.ExposureMode = pProperty->Flags; + + // This could be done in a separate work item after the device has been programmed... + m_ExposureResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CSensorSimulation:: +GetPhotoConfirmation( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_GlobalIspSettings.bPhotoConfirmation ); + DBG_TRACE( "Is %s", m_GlobalIspSettings.bPhotoConfirmation ? "On" : "Off" ); + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CSensorSimulation:: +SetPhotoConfirmation( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Return our cached copy; but update the value from the global setting. + m_GlobalIspSettings.bPhotoConfirmation = + BOOLEAN( (pProperty->Flags & KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON) + == KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON); + DBG_TRACE( "Is %s", m_GlobalIspSettings.bPhotoConfirmation ? "On" : "Off" ); + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CSensorSimulation:: +GetSceneMode( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_SceneMode ); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_SCENEMODE_MACRO | + KSCAMERA_EXTENDEDPROP_SCENEMODE_PORTRAIT | KSCAMERA_EXTENDEDPROP_SCENEMODE_SPORT | + KSCAMERA_EXTENDEDPROP_SCENEMODE_SNOW | KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHT | + KSCAMERA_EXTENDEDPROP_SCENEMODE_BEACH | KSCAMERA_EXTENDEDPROP_SCENEMODE_SUNSET | + KSCAMERA_EXTENDEDPROP_SCENEMODE_CANDLELIGHT | KSCAMERA_EXTENDEDPROP_SCENEMODE_LANDSCAPE | + KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHTPORTRAIT | KSCAMERA_EXTENDEDPROP_SCENEMODE_BACKLIT; + pProperty->Result = m_SceneModeResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CSensorSimulation:: +SetSceneModeAsync( + _In_ CExtendedProperty *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_SceneMode = (ULONG)pProperty->Flags; + + UpdateSettings( m_SceneMode ); + + // This could be done in a separate work item after the scene mode has been programmed... + m_SceneModeResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Apply scene mode changes. +NTSTATUS +CSensorSimulation:: +UpdateSettings( + _In_ ULONGLONG ullSceneMode +) +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + KScopedMutex lock( m_SensorMutex ); + + if( KSCAMERA_EXTENDEDPROP_SCENEMODE_AUTO==ullSceneMode ) + { + // + // If the mode was auto, let's randomly replace it with another valid value. + // + static + ULONGLONG ValidModes[] = + { + KSCAMERA_EXTENDEDPROP_SCENEMODE_MACRO, + KSCAMERA_EXTENDEDPROP_SCENEMODE_PORTRAIT, KSCAMERA_EXTENDEDPROP_SCENEMODE_SPORT, + KSCAMERA_EXTENDEDPROP_SCENEMODE_SNOW, KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHT, + KSCAMERA_EXTENDEDPROP_SCENEMODE_BEACH, KSCAMERA_EXTENDEDPROP_SCENEMODE_SUNSET, + KSCAMERA_EXTENDEDPROP_SCENEMODE_CANDLELIGHT, KSCAMERA_EXTENDEDPROP_SCENEMODE_LANDSCAPE, + KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHTPORTRAIT, KSCAMERA_EXTENDEDPROP_SCENEMODE_BACKLIT + }; + + ullSceneMode = + ValidModes[ (ULONG) (1 << GetRandom( (ULONG)0, SIZEOF_ARRAY(ValidModes)-1 )) ]; + } + + switch(ullSceneMode) + { + case KSCAMERA_EXTENDEDPROP_SCENEMODE_MACRO: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_PORTRAIT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_SPORT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_SNOW: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_BEACH: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_SUNSET: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_CANDLELIGHT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_LANDSCAPE: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHTPORTRAIT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_BACKLIT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + default: + break; + } + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + m_GlobalIspSettings.ExposureMode, m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + return status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CSensorSimulation:: +GetFocusPriority( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty(m_GlobalIspSettings.FocusPriority); + DBG_TRACE("Is %s", m_GlobalIspSettings.FocusPriority ? "On" : "Off"); + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CSensorSimulation:: +SetFocusPriority( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_GlobalIspSettings.FocusPriority = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CSensorSimulation:: +GetVideoHDR( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pProperty->Flags = m_VideoHDR; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_VIDEOHDR_OFF | + KSCAMERA_EXTENDEDPROP_VIDEOHDR_ON | + KSCAMERA_EXTENDEDPROP_VIDEOHDR_AUTO; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CSensorSimulation:: +SetVideoHDR( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_VideoHDR = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CSensorSimulation:: +GetVFR( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pProperty->Flags = m_VFR; + pProperty->Capability = 0; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CSensorSimulation:: +SetVFR( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_VFR = pProperty->Flags; + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Get handler +NTSTATUS +CSensorSimulation:: +GetZoom( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pProperty = CExtendedVidProcSetting(m_ExtendedZoom); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_ZOOM_DEFAULT | + KSCAMERA_EXTENDEDPROP_ZOOM_DIRECT | + KSCAMERA_EXTENDEDPROP_ZOOM_SMOOTH ; + + // Update the zoom factor. + // + // I am using the same pFilter->m_Zoom value here just to keep the extended and legacy + // controls tied together. It is not necessary to implement both in a real driver. + // However the legacy control was implemented first in this simulation and leaving both + // in place might have some value. But supporting both adds the question of whether they + // should both present a consistent view of zoom or if they should just save and report + // the values set for their own respective properties. + // + // I have chosen to support a consistent view of the zoom factor between the extended and + // legacy controls. Given that the legacy control was implemented first, I have chosen + // to keep that storage and rescale the value set by the extended zoom to match the + // legacy settings. + // + // But to make the two controls more consistent, I have changed the reported Ocular Focal + // Length to 2^16, which matches the implied denominator in Q16 format - the format used + // by this extended property. So as long as lOcularFocalLength is 0x10000, the following + // function should not change the reported value at all. This should simplify any + // debugging or tracing. + // + DBG_TRACE("Legacy Zoom Value = %d", m_Zoom.Value); + + // To ensure we clear out the high order value in the VideoProc + // union, we'll assign the LONG to LONGLONG field. + pProperty->m_Setting.VideoProc.Value.ll = (LONG) (TO_Q16(m_Zoom.Value) / m_FocalLength.lOcularFocalLength); + + DBG_TRACE("Extended Zoom Value = %d", pProperty->GetLONG()); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Set handler +NTSTATUS +CSensorSimulation:: +SetZoom( + _In_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + LONG Zoom = pProperty->GetLONG(); + DBG_TRACE("Extended Zoom Value = %d", Zoom); + + m_ExtendedZoom = *pProperty; + DBG_TRACE("Zoom Flags = 0x%016llX", m_ExtendedZoom.Flags); + + // Take us directly to the position if we're doing a direct zoom. + // Depends on IHVs, KSCAMERA_EXTENDEDPROP_ZOOM_DEFAULT may be either direct zoom or + // smooth zoom. Here direct zoom will be performed when DEFAULT is specified. + if( pProperty->Flags != KSCAMERA_EXTENDEDPROP_ZOOM_SMOOTH ) + { + // Convert back to legacy notation... + // + // I am using the same pFilter->m_Zoom value here just to keep the extended and legacy + // controls tied together. It is not necessary to implement both in a real driver. + // However the legacy control was implemented first in this simulation and leaving both + // in place might have some value. But supporting both adds the question of whether they + // should both present a consistent view of zoom or if they should just save and report + // the values set for their own respective properties. + // + // I have chosen to support a consistent view of the zoom factor between the extended and + // legacy controls. Given that the legacy control was implemented first, I have chosen + // to keep that storage and rescale the value set by the extended zoom to match the + // legacy settings. + // + // But to make the two controls more consistent, I have changed the reported Ocular Focal + // Length to 2^16, which matches the implied denominator in Q16 format - the format used + // by this extended property. So as long as lOcularFocalLength is 0x10000, the following + // function should not change the reported value at all. This should simplify any + // debugging or tracing. + // + m_Zoom.Value = FROM_Q16(Zoom, m_FocalLength.lOcularFocalLength); + DBG_TRACE("Legacy Zoom Value = %d", m_Zoom.Value); + } + + // It's always success; this is a sync control, so this isn't really used. + m_ExtendedZoom.Result = ntStatus; + + DBG_LEAVE("()=0x%08X", ntStatus); + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CSensorSimulation:: +GetVideoStabilization( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pProperty->Flags = m_VideoStabilization; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF | + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_ON | + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_AUTO; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CSensorSimulation:: +SetVideoStabilization( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + m_VideoStabilization = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CSensorSimulation:: +GetOpticalImageStabilization( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pProperty = CExtendedProperty(m_OpticalImageStabilization); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_OIS_OFF | + KSCAMERA_EXTENDEDPROP_OIS_ON | + KSCAMERA_EXTENDEDPROP_OIS_AUTO; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CSensorSimulation:: +SetOpticalImageStabilization( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + m_OpticalImageStabilization = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CSensorSimulation:: +GetAdvancedPhoto( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pProperty->Flags = m_AdvancedPhoto.Flags; + pProperty->Capability = m_AdvancedPhoto.Capability; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CSensorSimulation:: +SetAdvancedPhoto( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + m_AdvancedPhoto = *pProperty; + return STATUS_SUCCESS; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CSensorSimulation:: +GetFocus( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + // If we're currently set to AUTO or CONTINUOUS, report AUTO. + if (m_GlobalIspSettings.FocusMode & + ( KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS )) + { + pKsCameraControl->Flags |= KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else if (m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL) + { + pKsCameraControl->Flags |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + pKsCameraControl->Value = m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CSensorSimulation:: +SetFocus( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = pKsCameraControl->Value; + m_GlobalIspSettings.FocusMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + + m_FocusNotifier = nullptr; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -330000LL, // 33ms + FocusConvergence, + this); + } + + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_GlobalIspSettings.FocusMode = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + + m_FocusNotifier = nullptr; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -4000000LL, // 400ms + FocusConvergence, + this ); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CSensorSimulation:: +GetExposure( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + if (m_GlobalIspSettings.ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO) + { + pKsCameraControl->Flags |= KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else if (m_GlobalIspSettings.ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL) + { + pKsCameraControl->Flags |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + + pKsCameraControl->Value = Exposure100nsToBinaryLog(m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + + DBG_TRACE("ExposureValue=%d, ExposureMode=0x%016llX, ExposureTime=%lld00ns", + pKsCameraControl->Value, m_GlobalIspSettings.ExposureMode, m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CSensorSimulation:: +SetExposure( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = BoundsCheckSigned( pKsCameraControl->Value, ExposureRangeAndStep[0] ); + + if( NT_SUCCESS(ntStatus) ) + { + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE || !ExposureBinaryLogIsValid(pKsCameraControl->Value)) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll = ExposureBinaryLogTo100ns(pKsCameraControl->Value); + m_GlobalIspSettings.ExposureMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_GlobalIspSettings.ExposureMode = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + DBG_TRACE("ExposureValue=%d, ExposureMode=0x%016llX, ExposureTime=%lld00ns", + pKsCameraControl->Value, m_GlobalIspSettings.ExposureMode, m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + + return ntStatus; +} + +// Update the zoom factor over time. +void +CSensorSimulation:: +SmoothZoom() +{ + PAGED_CODE(); + +#define ZOOM_SPEED (LONG) (TO_Q16(1) * 0.07f) +#define ZOOM_SPEED_VIDEO_RECORDING (LONG) (TO_Q16(1) * 0.02f) +#define ZOOM_EPSILON (LONG)(TO_Q16(1) * 0.005f) + + LONG currentZoom = (LONG) + (TO_Q16(m_Zoom.Value) / + m_FocalLength.lOcularFocalLength); + LONG targetZoom = m_ExtendedZoom.GetLONG(); + + LONG ZoomDelta = abs(currentZoom - targetZoom); + + // Jump to target if we are very close + if( ZoomDelta <= ZOOM_EPSILON ) + { + currentZoom = targetZoom; + } + else + { + // Zoom direction + bool bZoomIn = (targetZoom > currentZoom); + LONG OpModeZoomSpeed = ZOOM_SPEED; + + // Use lower zoom speed for video, if any record pin is running. + for( ULONG VideoIndex=GetNextVideoIndex(); IsValidIndex(VideoIndex); VideoIndex=GetNextVideoIndex(VideoIndex) ) + { + if( PinRunning == m_HardwareSimulation[VideoIndex]->GetState() ) + { + OpModeZoomSpeed = ZOOM_SPEED_VIDEO_RECORDING; + } + } + + // Calculate zoom step that is relative to current zoom factor. + // the bigger the current zoom factor the bigger the step. + LONG ZoomStep; + if( bZoomIn ) + { + ZoomStep = MULT_Q16(OpModeZoomSpeed, currentZoom); + } + else + { + ZoomStep = currentZoom - DIV_Q16(currentZoom, (TO_Q16(1) + OpModeZoomSpeed)); + } + + if( ZoomDelta < 2 * ZoomStep) + { + // decrease step when close to target + ZoomStep = MULT_Q16(ZoomDelta, ((LONG)(TO_Q16(1) * 0.4f))); + } + + if (bZoomIn) + { + currentZoom += ZoomStep; + } + else + { + currentZoom -= ZoomStep; + } + } + + // Convert back to the zoom factor. + m_Zoom.Value = FROM_Q16(currentZoom, m_FocalLength.lOcularFocalLength); + + if (!NT_SUCCESS(BoundsCheckSigned(m_Zoom.Value, ZoomRangeAndStep[0]))) + { + m_Zoom.Value = FROM_Q16(targetZoom, m_FocalLength.lOcularFocalLength); + } + + DBG_LEAVE(" m_Zoom.Value=%d, FocusMode=0x%016llX", m_Zoom.Value, m_GlobalIspSettings.FocusMode); +} + + +// Adjust the zoom for each preview frame if the zoom is in motion. +// This function is used by both the legacy relative zoom control +// and the extended property smooth zoom control. +void +CSensorSimulation:: +UpdateZoom(void) +{ + PAGED_CODE(); + + // Legacy zoom relative adjustment. + LONG Value = m_Zoom.Value + m_ZoomRelative.Value; + + if (NT_SUCCESS(BoundsCheckSigned(Value, ZoomRangeAndStep[0]))) + { + m_Zoom.Value = Value; + } + else + { + m_ZoomRelative.Value = 0; + } + + // Extended property zoom / smooth zoom adjustment. + SmoothZoom(); +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CSensorSimulation:: +GetZoom( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Zoom.Capabilities; + pKsCameraControl->Flags = m_Zoom.Flags; + pKsCameraControl->Value = m_Zoom.Value; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CSensorSimulation:: +SetZoom( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + pKsCameraControl->Value += m_Zoom.Value; + } + + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, ZoomRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Zoom.Value = pKsCameraControl->Value; + m_Zoom.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Zoom.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CSensorSimulation:: +GetZoomRelative( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_ZoomRelative.Capabilities; + pKsCameraControl->Flags = m_ZoomRelative.Flags; + pKsCameraControl->Value = m_ZoomRelative.Value; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CSensorSimulation:: +SetZoomRelative( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, ZoomRelativeRangeAndStep[0]); + } + + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_ZoomRelative.Value = pKsCameraControl->Value; + m_ZoomRelative.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CSensorSimulation:: +GetPan( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Pan.Capabilities; + pKsCameraControl->Flags = m_Pan.Flags; + pKsCameraControl->Value = m_Pan.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CSensorSimulation:: +SetPan( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(ntStatus)) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, PanRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Pan.Value = pKsCameraControl->Value; + m_Pan.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Pan.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CSensorSimulation:: +GetRoll( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Roll.Capabilities; + pKsCameraControl->Flags = m_Roll.Flags; + pKsCameraControl->Value = m_Roll.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CSensorSimulation:: +SetRoll( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + if (NT_SUCCESS(ntStatus)) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, RollRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Roll.Value = pKsCameraControl->Value; + m_Roll.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Roll.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CSensorSimulation:: +GetTilt( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Tilt.Capabilities; + pKsCameraControl->Flags = m_Tilt.Flags; + pKsCameraControl->Value = m_Tilt.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CSensorSimulation:: +SetTilt( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + if (NT_SUCCESS(ntStatus)) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, TiltRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Tilt.Value = pKsCameraControl->Value; + m_Tilt.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Tilt.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CSensorSimulation:: +GetFocalLength( + _Inout_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->lOcularFocalLength = m_FocalLength.lOcularFocalLength; + pKsCameraControl->lObjectiveFocalLengthMax = m_FocalLength.lObjectiveFocalLengthMax; + pKsCameraControl->lObjectiveFocalLengthMin = m_FocalLength.lObjectiveFocalLengthMin; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CSensorSimulation:: +SetFocalLength( + _In_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pKsCameraControl +) +{ + PAGED_CODE(); + return STATUS_NOT_FOUND; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CSensorSimulation:: +GetBacklightCompensation( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_BacklightCompensation.Capabilities; + pKsCameraControl->Flags = m_BacklightCompensation.Flags; + pKsCameraControl->Value = m_BacklightCompensation.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CSensorSimulation:: +SetBacklightCompensation( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, BacklightCompensationRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_BacklightCompensation.Value = pKsCameraControl->Value; + m_BacklightCompensation.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_BacklightCompensation.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CSensorSimulation:: +GetBrightness( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Brightness.Capabilities; + pKsCameraControl->Flags = m_Brightness.Flags; + pKsCameraControl->Value = m_Brightness.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CSensorSimulation:: +SetBrightness( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, BrightnessRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Brightness.Value = pKsCameraControl->Value; + m_Brightness.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Brightness.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CSensorSimulation:: +GetContrast( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Contrast.Capabilities; + pKsCameraControl->Flags = m_Contrast.Flags; + pKsCameraControl->Value = m_Contrast.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CSensorSimulation:: +SetContrast( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, ContrastRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Contrast.Value = pKsCameraControl->Value; + m_Contrast.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Contrast.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CSensorSimulation:: +GetHue( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Hue.Capabilities; + pKsCameraControl->Flags = m_Hue.Flags; + pKsCameraControl->Value = m_Hue.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CSensorSimulation:: +SetHue( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, HueRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Hue.Value = pKsCameraControl->Value; + m_Hue.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Hue.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CSensorSimulation:: +GetPowerlineFreq( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_PowerLineFreq.Capabilities; + pKsCameraControl->Flags = m_PowerLineFreq.Flags; + pKsCameraControl->Value = m_PowerLineFreq.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CSensorSimulation:: +SetPowerlineFreq( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, PLFRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_PowerLineFreq.Value = pKsCameraControl->Value; + m_PowerLineFreq.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_PowerLineFreq.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + m_PowerLineFreq.Value = POWERLINEFREQ_AUTO; // Auto is auto. + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CSensorSimulation:: +GetWhiteBalance( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + if (m_GlobalIspSettings.WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO) + { + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + + ULONG temperature = m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul; + if (m_GlobalIspSettings.WhiteBalanceSetting.Mode != KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE) + { + temperature = WhiteBalancePreset2Temperature(m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul); + } + pKsCameraControl->Value = (LONG)temperature; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CSensorSimulation:: +SetWhiteBalance( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // We report these changes in the next metadata payload. + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = pKsCameraControl->Value; + m_GlobalIspSettings.WhiteBalanceMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_GlobalIspSettings.WhiteBalanceMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CSensorSimulation:: +GetHistogram( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pProperty->Flags = m_Histogram; + pProperty->m_Value.Value.ull = 0; + pProperty->Capability = 0; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CSensorSimulation:: +SetHistogram( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_Histogram = pProperty->Flags; + + // Adjust the reported metadata buffer size for preview. + m_MetadataInfo[pProperty->PinId] = + CExtendedMetadata::METADATA_MAX + + (( m_Histogram == KSCAMERA_EXTENDEDPROP_HISTOGRAM_ON ) ? sizeof(CAMERA_METADATA_HISTOGRAM) : 0 ) ; + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CSensorSimulation:: +GetMetadata( + _Inout_ CExtendedMetadata *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = m_MetadataInfo[pProperty->PinId]; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CSensorSimulation:: +SetMetadata( + _In_ CExtendedMetadata *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = m_MetadataInfo[pProperty->PinId]; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CSensorSimulation:: +GetOptimizationHint( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = m_OptimizationHint; + pProperty->Capability = + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_DEFAULT; + + DBG_TRACE( "Hint = 0x%016llX", m_OptimizationHint.Flags ); + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CSensorSimulation:: +SetOptimizationHint( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_OptimizationHint = *pProperty; + + DBG_TRACE( "Hint = 0x%016llX", m_OptimizationHint.Flags ); + + return STATUS_SUCCESS; +} + +// Get for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CSensorSimulation:: +GetVideoStabMode( + _Inout_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pMode->Capabilities = + KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_MANUAL ; + pMode->VideoStabilizationMode = m_VideoStabMode; + return STATUS_SUCCESS; +} + +// Set for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CSensorSimulation:: +SetVideoStabMode( + _In_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_VideoStabMode = pMode->VideoStabilizationMode; + return STATUS_SUCCESS; +} + +// Get for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +GetFlash( + _Inout_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pFlash->Capabilities = + KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_MANUAL ; + pFlash->Flash = m_Flash; + return STATUS_SUCCESS; +} + +// Set for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +SetFlash( + _In_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_Flash = pFlash->Flash; + return STATUS_SUCCESS; +} + +// Get for KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +GetPinDependence( + _Inout_ KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S *pCaps +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Limit capabilities to the valid set. + pCaps->Capabilities &= + ( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_EXCLUSIVE_WITH_RECORD | + KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_SEQUENCE_EXCLUSIVE_WITH_RECORD ); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CSensorSimulation:: +GetTriggerTime( + _Inout_ CExtendedProperty *pQPC +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pQPC->Flags = 0; + NTSTATUS Status = GetQPC( pQPC->PinId, &pQPC->m_Value.Value.ull ); + + if( NT_SUCCESS(Status) ) + { + pQPC->Flags = ( pQPC->m_Value.Value.ull != 0 ) ? + KSPROPERTY_CAMERA_PHOTOTRIGGERTIME_SET : + KSPROPERTY_CAMERA_PHOTOTRIGGERTIME_CLEAR; + } + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CSensorSimulation:: +SetTriggerTime( + _In_ CExtendedProperty *pQPC +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + ULONGLONG Time = ( pQPC->Flags & KSPROPERTY_CAMERA_PHOTOTRIGGERTIME_SET ) ? *pQPC : 0; + return SetQPC( pQPC->PinId, Time ); +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CSensorSimulation:: +GetTorchMode( + _Inout_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pTorchMode = m_TorchMode; + pTorchMode->Capability = + KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF | + KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON | + KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON_ADJUSTABLEPOWER; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CSensorSimulation:: +SetTorchMode( + _In_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_TorchMode = *pTorchMode; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CSensorSimulation:: +GetPhotoMode( + _Inout_ CExtendedPhotoMode *pPhotoMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pPhotoMode->Flags = m_PhotoMode.Flags; + pPhotoMode->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE; + pPhotoMode->MaxHistoryFrames() = IMAGE_CAPTURE_PIN_MAXIMUM_HISTORY_FRAMES; + pPhotoMode->RequestedHistoryFrames() = m_PhotoMode.RequestedHistoryFrames(); + pPhotoMode->SubMode() = m_PhotoMode.SubMode(); + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CSensorSimulation:: +SetPhotoModeAsync( + _In_ CExtendedPhotoMode *pPhotoMode, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + DBG_ENTER("()"); + + m_PhotoMode = *pPhotoMode; + + NTSTATUS Status = + SetPinMode( pPhotoMode->PinId, pPhotoMode->Flags, pPhotoMode->RequestedHistoryFrames() ); + + // Tell the caller that we're done. + Notifier->Set(); + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Get Handler + */ +NTSTATUS +CSensorSimulation:: +GetPhotoMaxFrameRate( + _Inout_ CExtendedProperty *pPhotoMaxFrameRate +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pPhotoMaxFrameRate->Flags = m_MaxFrameRate.Flags; + pPhotoMaxFrameRate->m_Value = m_MaxFrameRate.m_Value; + // If max frame rate is 0,0, we want to indicate 30fps to the caller. + if ((*pPhotoMaxFrameRate).m_Value.Value.ull == 0) + { + (*pPhotoMaxFrameRate).m_Value.Value.ratio.HighPart = 30000; + (*pPhotoMaxFrameRate).m_Value.Value.ratio.LowPart = 1000; + } + pPhotoMaxFrameRate->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Set Handler + */ +NTSTATUS +CSensorSimulation:: +SetPhotoMaxFrameRateAsync( + _In_ CExtendedProperty *pPhotoMaxFrameRate, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + + ULONGLONG TimePerFrame = 0; + { + // Note: Don't lock around SetPhotoFrameRate() to avoid a priority inversion. + KScopedMutex lock( m_SensorMutex ); + + m_MaxFrameRate = *pPhotoMaxFrameRate; + + // Try to apply the setting immediately. + LARGE_INTEGER PerformanceTime = { m_MaxFrameRate.m_Value.Value.ratio.LowPart }; + LONGLONG Frequency = m_MaxFrameRate.m_Value.Value.ratio.HighPart; + + if( Frequency != 0 ) + { + TimePerFrame = KSCONVERT_PERFORMANCE_TIME( Frequency, PerformanceTime ); + } + } + + DBG_TRACE( "SetPhotoFrameRate(%lld)", TimePerFrame ); + + // Consume any error; we'll set the rate again when the pin starts. + SetPhotoFrameRate( pPhotoMaxFrameRate->PinId, TimePerFrame ); + + // Tell the caller that we're done. + Notifier->Set(); + + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART + */ +NTSTATUS +CSensorSimulation:: +GetWarmStart( + _Inout_ CExtendedProperty *pWarmStart +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pWarmStart = m_WarmStartEnabled[pWarmStart->PinId]; + pWarmStart->Capability = + KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | + KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_DISABLED | + KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_ENABLED ; + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART + */ +NTSTATUS +CSensorSimulation:: +SetWarmStartAsync( + _In_ CExtendedProperty *pWarmStart, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_WarmStartEnabled[pWarmStart->PinId] = *pWarmStart; + + // Tell the caller that we're done. + Notifier->Set(); + + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Get Handler + */ +NTSTATUS +CSensorSimulation:: +GetMaxVideoFpsForPhotoRes( + _Inout_ CExtendedMaxVideoFpsForPhotoRes *pPhotoRes +) +{ + PAGED_CODE(); + + // We're just a simulation and have no constraints - set 30FPS. + pPhotoRes->PreviewFPSNum() =30; + pPhotoRes->PreviewFPSDenom() = 1; + pPhotoRes->CaptureFPSNum() = 30; + pPhotoRes->CaptureFPSDenom() = 1; + + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Set Handler + */ +NTSTATUS +CSensorSimulation:: +SetMaxVideoFpsForPhotoRes( + _In_ CExtendedMaxVideoFpsForPhotoRes *pPhotoRes +) +{ + PAGED_CODE(); + + // We don't really use this call. + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW +NTSTATUS +CSensorSimulation:: +GetFieldOfView( + _Inout_ CExtendedFieldOfView *pFieldOfView +) +{ + PAGED_CODE(); + + // Report dummy values. + *pFieldOfView = CExtendedFieldOfView(); + pFieldOfView->NormalizedFocalLengthX() = 35; + pFieldOfView->NormalizedFocalLengthY() = 35; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW +NTSTATUS +CSensorSimulation:: +GetCameraAngleOffset( + _Inout_ CExtendedCameraAngleOffset *pAngle +) +{ + PAGED_CODE(); + + // Just return zeros. We're dead-on, no tilt. + *pAngle = CExtendedCameraAngleOffset(); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_CONFIGCAPS Get handler +NTSTATUS +CSensorSimulation:: +GetRoiConfigCaps( + _Inout_ CRoiConfig *pCaps +) +{ + PAGED_CODE(); + // No state accessed... + //KScopedMutex lock( m_SensorMutex ); + + // Our CRoiConfig ctor defines our capabilities. + *pCaps = CRoiConfig(); + + return STATUS_SUCCESS; +} + +// Per Frame Settings Caps +struct SOC_PFS_CAPS +{ + KSCAMERA_PERFRAMESETTING_CAP_HEADER Hdr; + SOC_CAP_WITH_STEPPING_LONGLONG ExposureTime; + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Flash; + SOC_CAP_WITH_STEPPING EVCompensation; + SOC_CAP_WITH_STEPPING Iso; + SOC_CAP_WITH_STEPPING Focus; + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER StillConfirmation; +} ; + +const ULONG SOC_PFS_NUMCAPS = 6; + +// Our predefined Variable Photo Sequence capabilities. +SOC_PFS_CAPS g_PFSCaps = +{ + // KSCAMERA_PERFRAMESETTING_CAP_HEADER + { sizeof(SOC_PFS_CAPS), SOC_PFS_NUMCAPS, 0 }, //Hdr + // ExposureTime + { + // SOC_CAP_WITH_STEPPING_LONGLONG + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.ExposureTime), //Size + KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_TIME, //Type + KSCAMERA_PERFRAMESETTING_AUTO //Flags + | KSCAMERA_PERFRAMESETTING_MANUAL + }, + { + // KSPROPERTY_STEPPING_LONGLONG + 1000, //SteppingDelta (in us) + { + // KSPROPERTY_BOUNDS_LONGLONG + MIN_EXPOSURE_TIME, //SignedMinimum + MAX_EXPOSURE_TIME //SignedMaximum (arbitrary - 1 hour) + } + } + }, + // Flash + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.Flash), + KSCAMERA_PERFRAMESETTING_ITEM_FLASH, + KSCAMERA_EXTENDEDPROP_FLASH_OFF + | KSCAMERA_EXTENDEDPROP_FLASH_ON + | KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER + | KSCAMERA_EXTENDEDPROP_FLASH_AUTO + | KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER + | KSCAMERA_EXTENDEDPROP_FLASH_REDEYEREDUCTION + }, + // ExposureCompensation + { + // SOC_CAP_WITH_STEPPING + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.EVCompensation), //Size + KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_COMPENSATION, //Type + KSCAMERA_PERFRAMESETTING_AUTO //Flags + | KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP + }, + { + // KSPROPERTY_STEPPING_LONG + 1, //SteppingDelta + 0, //Reserved + { + // KSPROPERTY_BOUNDS_LONG + -2, //SignedMinimum + 2 //SignedMaximum + } + } + }, + // Iso + { + // SOC_CAP_WITH_STEPPING + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.Iso), + KSCAMERA_PERFRAMESETTING_ITEM_ISO, + KSCAMERA_EXTENDEDPROP_ISO_AUTO + | KSCAMERA_EXTENDEDPROP_ISO_MANUAL + }, + { + // KSPROPERTY_STEPPING_LONG + 50, //SteppingDelta + 0, //Reserved + { + // KSPROPERTY_BOUNDS_LONG + 50, //SignedMinimum + 256000 //SignedMaximum + } + } + }, + // Focus + { + // SOC_CAP_WITH_STEPPING + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.Focus), //Size + KSCAMERA_PERFRAMESETTING_ITEM_FOCUS, //Type + KSCAMERA_PERFRAMESETTING_AUTO //Flags + | KSCAMERA_PERFRAMESETTING_MANUAL + }, + { + // KSPROPERTY_STEPPING_LONG + 10, //SteppingDelta (We'll use centimeters for kicks) + 0, //Reserved + { + // KSPROPERTY_BOUNDS_LONG + 0, //SignedMinimum + 10000 //SignedMaximum (100 meters max) + } + } + }, + // StillConfirmation + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.StillConfirmation), + KSCAMERA_PERFRAMESETTING_ITEM_PHOTOCONFIRMATION, + 0 + }, +}; + +// Get KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CAPABILITY +_Success_(return == 0) +NTSTATUS +CSensorSimulation:: +GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size +) +{ + PAGED_CODE(); + + if( Caps ) + { + size_t Length = sizeof(g_PFSCaps); + if( Size && *Size < Length ) + { + Length = *Size; + } + RtlCopyMemory( Caps, &g_PFSCaps, Length ); + } + + *Size = sizeof(g_PFSCaps); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Get handler +NTSTATUS +CSensorSimulation:: +GetRoi( + _Inout_ CRoiProperty *pRoi +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // *pRoiProperty = ... + *pRoi = CRoiProperty(); // initialize it. + + // Add control properties to it. + pRoi->Add( &m_RoiWhiteBalance ) ; + pRoi->Add( &m_RoiExposureMode ) ; + pRoi->Add( &m_RoiFocusMode ) ; + + // Log what we created. + pRoi->Log(); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Set handler +NTSTATUS +CSensorSimulation:: +SetRoiAsync( + _In_ CRoiProperty *pRoi, + _In_ CNotifier *Notifier +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + if( pRoi->GetControlCount() == 0 ) + { + // Clear all settings. + DBG_TRACE("Clearing all settings..."); + m_RoiWhiteBalance = CWhiteBalanceRoiIspControl(); + m_RoiExposureMode = CExposureRoiIspControl(); + m_RoiFocusMode = CFocusRoiIspControl(); + } + else + { + // Parse our settings. + DBG_TRACE("Parsing new settings..."); + if( m_RoiWhiteBalance. + Init( pRoi, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ) ) + { + m_GlobalIspSettings.WhiteBalanceMode = m_RoiWhiteBalance.GetFlags(); + m_RoiWhiteBalance.Log(); + } + if( m_RoiExposureMode. + Init( pRoi, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ) ) + { + m_GlobalIspSettings.ExposureMode = m_RoiExposureMode.GetFlags(); + m_RoiExposureMode.Log(); + } + if( m_RoiFocusMode. + Init( pRoi, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE ) ) + { + ULONGLONG Flags = m_RoiFocusMode.GetFlags(); + DBG_TRACE("Requesting FocusMode = 0x%016llX", Flags); + + if( Flags ) + { + // If we've been asked to unlock, remove any lock flags. + if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK ) + { + // Change our operation so we go back to the previous state. + Flags = m_GlobalIspSettings.FocusMode & + ~( KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK ); + } + + // Handle the stand-alone lock case by setting the appropriate flags. + if( ( Flags & + (KSCAMERA_EXTENDEDPROP_FOCUS_MODE_MASK | + KSCAMERA_EXTENDEDPROP_FOCUS_MODE_ADVANCED_MASK) ) == + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Flags = + m_GlobalIspSettings.FocusMode | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + } + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS ) + { + Flags = + m_GlobalIspSettings.FocusMode |= KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK; + } + } + // Note: We do not simulate deferred completion. + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED; + + // Record the Focus mode. + DBG_TRACE("Recording FocusMode = 0x%016llX", Flags); + m_GlobalIspSettings.FocusMode = Flags; + } + + m_RoiFocusMode.Log(); + } + } + + Notifier->Set(); + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL size handler +ULONG +CSensorSimulation:: +SizeOfRoi() +{ + PAGED_CODE(); + + return + sizeof( CRoiProperty ) + + m_RoiWhiteBalance.GetSize() + + m_RoiExposureMode.GetSize() + + m_RoiFocusMode.GetSize() ; +} + + +// Get KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CSensorSimulation:: +GetVideoControlMode( + _Inout_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pMode->Mode = GetTriggerMode(pMode->StreamIndex); + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CSensorSimulation:: +SetVideoControlMode( + _In_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + + // Note: Trigger will acquire a lock on the hwsim. + return Trigger( pMode->StreamIndex, pMode->Mode ); +} + +// Get KSPROPERTY_VIDEOCONTROL_CAPS. +NTSTATUS +CSensorSimulation:: +GetVideoControlCaps( + _Inout_ KSPROPERTY_VIDEOCONTROL_CAPS_S *pCaps +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pCaps->VideoControlCaps = 0; + if( IsStillIndex( pCaps->StreamIndex ) ) + { + pCaps->VideoControlCaps = + KS_VideoControlFlag_IndependentImagePin | + KS_VideoControlFlag_Trigger | + KS_VideoControlFlag_StartPhotoSequenceCapture | + KS_VideoControlFlag_StopPhotoSequenceCapture; + } + + return STATUS_SUCCESS; +} + diff --git a/AVStream/avscamera/sys/SensorSimulation.h b/AVStream/avscamera/sys/SensorSimulation.h new file mode 100644 index 00000000..4b07f84e --- /dev/null +++ b/AVStream/avscamera/sys/SensorSimulation.h @@ -0,0 +1,281 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + SensorSimulation.h + + Abstract: + + Simulation class declaration. Derived from the base CSensor object. + + This class simulates a theoretical model camera. The camera supports + every modern camera control available and produces a simulation of its + behavior. In most cases this simulation is very very basic because we + have no actual hardware to work with and there producing a visible + effect in the simulation would be difficult or expensive to do. + + History: + + created 5/8/2014 + +**************************************************************************/ + +#pragma once +class CSensorSimulation : + public CSensor +{ +protected: + const + KSFILTER_DESCRIPTOR *m_Descriptors; + + LARGE_INTEGER m_StartTime; + + // State for the various controls. + RECT m_FocusRect; + BOOL m_AutoFocusLock; + BOOL m_AutoExposureLock; + BOOL m_AutoWhitebalanceLock; + ULONG m_Flash; + ULONG m_VideoStabMode; + CExtendedProperty *m_WarmStartEnabled; + CExtendedPhotoMode m_PhotoMode; + ULONG m_RequestedHistoryFrames; + CExtendedProperty m_MaxFrameRate; + ULONGLONG m_FaceDetectionFlags; + ULONG m_FaceDetectionMax; + ULONG m_FaceDetectionCurrentMax; + ULONGLONG m_SceneMode; + CExtendedProperty m_TorchMode; + CExtendedProperty m_OptimizationHint; + CExtendedMetadata *m_MetadataInfo; + PKSCAMERA_PERFRAMESETTING_HEADER m_pPerFrameSettings; + ULONG m_PFSSize; + KSCAMERA_EXTENDEDPROP_FOCUSSTATE m_FocusState; + ULONGLONG m_VFR; + ULONGLONG m_VideoHDR; + ULONGLONG m_VideoStabilization; + ULONGLONG m_Histogram; + ULONGLONG m_OpticalImageStabilization; + CExtendedProperty m_AdvancedPhoto; + + CWhiteBalanceRoiIspControl m_RoiWhiteBalance; + CExposureRoiIspControl m_RoiExposureMode; + CFocusRoiIspControl m_RoiFocusMode; + + VIDCAP_PROPERTY m_Pan; + VIDCAP_PROPERTY m_Roll; + VIDCAP_PROPERTY m_Tilt; + + // Zoom parameters + FOCAL_LENGTH_PROPERTY m_FocalLength; // A description of the zoom limits. + VIDCAP_PROPERTY m_Zoom; // Absolute zoom position / mode. + VIDCAP_PROPERTY m_ZoomRelative; // Relative zoom position / mode. + + // Cached Q16-adjusted bounds. I do not use the zoom value stored below. + // + // I am using the same m_Zoom value here to keep both the extended and legacy + // controls tied together. It is not necessary to implement both in a real driver. + // However the legacy control was implemented first in this simulation and leaving both + // in place might have some value. But supporting both adds the question of whether they + // should both present a consistent view of zoom or if they should just save and report + // the values set for their own respective properties. + // + // I have chosen to support a consistent view of the zoom factor between the extended and + // legacy controls. Given that the legacy control was implemented first, I have chosen + // to keep that storage and rescale the value set by the extended zoom to match the + // legacy settings. + // + // But to make the two controls more consistent, I have changed the reported Ocular Focal + // Length to 2^16, which matches the implied denominator in Q16 format - the format used + // by this extended property. So as long as lOcularFocalLength is 0x10000, the following + // function should not change the reported value at all. This should simplify any + // debugging or tracing. + + CExtendedVidProcSetting m_ExtendedZoom; // Must update zoom value on the fly. + + VIDCAP_PROPERTY m_PowerLineFreq; // Power Line Frequency setting. + VIDCAP_PROPERTY m_BacklightCompensation; + VIDCAP_PROPERTY m_Brightness; + VIDCAP_PROPERTY m_Contrast; + VIDCAP_PROPERTY m_Hue; + + ULONG m_IsoResult; + ULONG m_EvCompResult; + ULONG m_WhiteBalanceResult; + ULONG m_ExposureResult; + ULONG m_SceneModeResult; + ULONG m_FaceDetectionResult; + ULONG m_FocusResult; + + ULONGLONG m_LastFocusMode; + ULONG m_LastFocusSetting; + + CRefPtr<CNotifier> m_FocusNotifier; // A reference to the CNotifier for any Focus in flight. + KPassiveTimer *m_FocusTimer; // A timer object used to simulate a focus event. + CRefPtr<CNotifier> m_FocusRectNotifier; + KPassiveTimer *m_FocusRectTimer; + + static const ULONG METADATA_MAX=4096; + +protected: + // + // Deal with scene mode changes. + // + NTSTATUS + UpdateSettings( + _In_ ULONGLONG ullSceneMode + ); + + // + // Put us back to the defaults specified in the DDI. + // + virtual + NTSTATUS + ProgramDefaults(); + +public: + // + // This simulation is based solely on the Descriptors. Yours might + // need additional information. + // + CSensorSimulation( + _In_ CCaptureDevice *Device, + _In_ const KSFILTER_DESCRIPTOR *Descriptors + ); + + virtual ~CSensorSimulation(); + + // + // Parse descriptors to determine what pin simulations are required. + // Do memory allocations, etc. + // + virtual + NTSTATUS + Initialize(); + + virtual + NTSTATUS + GetFocusState( + _Out_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE *FocusState + ); + + // Lets you know if Variable Photo Sequence is active. + // Note: This doesn't tell you if the pin is running. + virtual + BOOLEAN + IsVPSActive() + { + return + KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_VARIABLE==m_PhotoMode.SubMode() && + m_pIspSettings ; + } + + // Callback to handle focus convergence. + static + void + FocusConvergence( + _In_opt_ PVOID Context + ); + + // Callback to handle focus convergence. + static + void + FocusRectConvergence( + _In_opt_ PVOID Context + ); + + // Declare the property controls handled by this sensor. + DECLARE_PROPERTY( KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode ); + DECLARE_PROPERTY_GET( KSPROPERTY_VIDEOCONTROL_CAPS_S, VideoControlCaps ); + + DECLARE_PROPERTY_ASYNC( KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ); + + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ); + DECLARE_PROPERTY_GET( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence ); + DECLARE_PROPERTY( CExtendedProperty, TriggerTime ); + DECLARE_PROPERTY( CExtendedProperty, TorchMode ); + + DECLARE_PROPERTY( CExtendedProperty, ExtendedFlash ); + + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, Focus ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, Iso ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedVidProcSetting, IsoAdvanced ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedEvCompensation, EvCompensation ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedVidProcSetting, WhiteBalance ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedVidProcSetting, Exposure ); + DECLARE_PROPERTY( CExtendedProperty, PhotoConfirmation ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, SceneMode ); + DECLARE_PROPERTY( CExtendedProperty, FocusPriority ); + DECLARE_PROPERTY( CExtendedProperty, VideoHDR ); + DECLARE_PROPERTY( CExtendedProperty, VFR ); + DECLARE_PROPERTY( CExtendedVidProcSetting, Zoom ); + DECLARE_PROPERTY( CExtendedProperty, VideoStabilization ); + DECLARE_PROPERTY( CExtendedProperty, Histogram ); + DECLARE_PROPERTY( CExtendedMetadata, Metadata ); + DECLARE_PROPERTY( CExtendedProperty, OpticalImageStabilization ); + DECLARE_PROPERTY( CExtendedProperty, OptimizationHint ); + DECLARE_PROPERTY( CExtendedProperty, AdvancedPhoto ); + DECLARE_PROPERTY( CExtendedVidProcSetting, FaceDetection ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedPhotoMode, PhotoMode ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, PhotoMaxFrameRate ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, WarmStart ); + DECLARE_PROPERTY( CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ); + DECLARE_PROPERTY_GET( CExtendedFieldOfView, FieldOfView ); + DECLARE_PROPERTY_GET( CExtendedCameraAngleOffset, CameraAngleOffset ); + + DECLARE_PROPERTY_VARSIZE_ASYNC_NOCANCEL( CRoiProperty, Roi ); + + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ); + + // Update the zoom factor over time. + void + SmoothZoom(); + + // Apply a zoom factor. + virtual + void + UpdateZoom(void); + + // Declare legacy controls that are needed. + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Exposure); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Focus); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Zoom); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, ZoomRelative); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Pan); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Roll); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Tilt); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength); + + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Brightness); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Contrast); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Hue); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + + DECLARE_PROPERTY_GET( CRoiConfig, RoiConfigCaps ); + + // Special-case Per-Frame settings since it requires a variable length structure. + _Success_(return == 0) + virtual + NTSTATUS + GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size + ); +}; + +// Constants... +const LONG WHITEBALANCE_MIN = 0; +const LONG WHITEBALANCE_MAX = 15000; +const LONG WHITEBALANCE_STEP= 1; +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 diff --git a/AVStream/avscamera/sys/Sources.dep b/AVStream/avscamera/sys/Sources.dep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/AVStream/avscamera/sys/Sources.dep @@ -0,0 +1 @@ + diff --git a/AVStream/avscamera/sys/Synthesizer.cpp b/AVStream/avscamera/sys/Synthesizer.cpp new file mode 100644 index 00000000..4f591424 --- /dev/null +++ b/AVStream/avscamera/sys/Synthesizer.cpp @@ -0,0 +1,1558 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Synthesizer.cpp + + Abstract: + + This file contains the implementation of CSynthesizer. + + The base image synthesis and overlay class. These classes provide + image synthesis (pixel, color-bar, etc...) onto buffers of various + formats. + + Internally, all CSynthesizer objects represent a pixel as a 32 bit + quantity with 8 bits per sample. The base CSynthesizer implements + all rendering functionality with this assumption in mind. It + simplifies rendering substantially and means we do not need to re- + implement rendering functions for each format. + + Most end formats can be synthesized from this uncompressed format + with overhead that is slightly worse than a copy. Color spaces are + handled by using a rendering palette that is unique for each. + + History: + + created 04/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +/************************************************************************** + + Constants + +**************************************************************************/ + +// +// Standard definition of EIA-189-A color bars. The actual color definitions +// are either in CRGB24Synthesizer or CYUVSynthesizer. +// + +const COLOR CSynthesizer::m_ColorBars[8] = +{MAGENTA, BLUE, GREEN, BLACK, WHITE, YELLOW, RED, CYAN }; + + +// +// Corner registration block colors. +// +const COLOR g_TopLeft = GREY; +const COLOR g_TopRight = BLUE; +const COLOR g_BotLeft = RED; +const COLOR g_BotRight = GREEN; + +// +// Persistent stats. +// +LONGLONG CSynthesizer::m_RelPts=0; +LONGLONG CSynthesizer::m_QpcTime=0; +ULONG CSynthesizer::m_Frame=0; + +// +// The following is an 8x8 bitmapped font for use in the text overlay +// code. +// +const UCHAR +CSynthesizer:: +m_FontData [2][256][8] = +{ + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e}, + {0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e}, + {0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, + {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, + {0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c}, + {0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c}, + {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00}, + {0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff}, + {0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00}, + {0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff}, + {0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78}, + {0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18}, + {0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0}, + {0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0}, + {0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99}, + {0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00}, + {0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00}, + {0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18}, + {0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, + {0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00}, + {0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78}, + {0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00}, + {0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff}, + {0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00}, + {0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00}, + {0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00}, + {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00}, + {0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, + {0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00}, + {0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00}, + {0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, + {0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00}, + {0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00}, + {0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00}, + {0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00}, + {0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00}, + {0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00}, + {0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60}, + {0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00}, + {0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00}, + {0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00}, + {0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00}, + {0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00}, + {0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00}, + {0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00}, + {0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00}, + {0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00}, + {0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00}, + {0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00}, + {0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00}, + {0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60}, + {0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00}, + {0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00}, + {0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00}, + {0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, + {0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00}, + {0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00}, + {0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00}, + {0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00}, + {0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00}, + {0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00}, + {0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00}, + {0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00}, + {0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00}, + {0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00}, + {0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, + {0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, + {0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00}, + {0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00}, + {0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00}, + {0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00}, + {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00}, + {0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, + {0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00}, + {0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00}, + {0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00}, + {0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00}, + {0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00}, + {0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00}, + {0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, + {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00}, + {0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00}, + {0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00}, + {0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00}, + {0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78}, + {0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00}, + {0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00}, + {0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0}, + {0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e}, + {0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00}, + {0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00}, + {0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00}, + {0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00}, + {0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00}, + {0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00}, + {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, + {0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, + {0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00}, + {0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78}, + {0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00}, + {0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38}, + {0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00}, + {0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, + {0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, + {0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00}, + {0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00}, + {0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00}, + {0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00}, + {0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00}, + {0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00}, + {0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18}, + {0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00}, + {0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30}, + {0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7}, + {0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70}, + {0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00}, + {0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00}, + {0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00}, + {0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00}, + {0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00}, + {0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f}, + {0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03}, + {0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00}, + {0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00}, + {0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88}, + {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa}, + {0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee}, + {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36}, + {0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36}, + {0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}, + {0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0}, + {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00}, + {0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0}, + {0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00}, + {0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00}, + {0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00}, + {0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00}, + {0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0}, + {0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc}, + {0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00}, + {0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00}, + {0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00}, + {0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0}, + {0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00}, + {0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00}, + {0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00}, + {0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00}, + {0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00}, + {0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70}, + {0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00}, + {0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00}, + {0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00}, + {0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c}, + {0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00}, + {0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7e, 0x81, 0xa9, 0x8d, 0x8d, 0xa9, 0x81, 0x7e}, + {0x7e, 0xff, 0xd7, 0xf3, 0xf3, 0xd7, 0xff, 0x7e}, + {0x00, 0x70, 0xf8, 0xfc, 0x7e, 0xfc, 0xf8, 0x70}, + {0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10}, + {0x00, 0x18, 0x5d, 0xff, 0xff, 0xff, 0x5d, 0x18}, + {0x00, 0x08, 0x1d, 0x3f, 0xff, 0x3f, 0x1d, 0x08}, + {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00}, + {0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff}, + {0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00}, + {0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff}, + {0xf0, 0xe0, 0xfe, 0xbf, 0x11, 0x11, 0x1f, 0x0e}, + {0x00, 0x72, 0xfa, 0x8f, 0x8f, 0xfa, 0x72, 0x00}, + {0xe0, 0xe0, 0xa0, 0xa0, 0xfe, 0xff, 0x07, 0x03}, + {0xfc, 0xfe, 0xa6, 0xa0, 0xa0, 0xfe, 0xff, 0x03}, + {0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99}, + {0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe}, + {0x00, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10}, + {0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00}, + {0x00, 0xfa, 0xfa, 0x00, 0x00, 0xfa, 0xfa, 0x00}, + {0xfe, 0xfe, 0x80, 0xfe, 0xfe, 0x90, 0xf0, 0x60}, + {0x40, 0xc0, 0x9a, 0xbf, 0xa5, 0xfd, 0x5b, 0x02}, + {0x00, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x00}, + {0x01, 0x29, 0x6d, 0xff, 0xff, 0x6d, 0x29, 0x01}, + {0x00, 0x20, 0x60, 0xfe, 0xfe, 0x60, 0x20, 0x00}, + {0x00, 0x08, 0x0c, 0xfe, 0xfe, 0x0c, 0x08, 0x00}, + {0x00, 0x10, 0x38, 0x7c, 0x54, 0x10, 0x10, 0x10}, + {0x00, 0x10, 0x10, 0x10, 0x54, 0x7c, 0x38, 0x10}, + {0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x3c, 0x3c}, + {0x10, 0x38, 0x7c, 0x10, 0x10, 0x7c, 0x38, 0x10}, + {0x0c, 0x1c, 0x3c, 0x7c, 0x7c, 0x3c, 0x1c, 0x0c}, + {0x60, 0x70, 0x78, 0x7c, 0x7c, 0x78, 0x70, 0x60}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x60, 0xfa, 0xfa, 0x60, 0x00}, + {0x00, 0x00, 0xe0, 0xe0, 0x00, 0xe0, 0xe0, 0x00}, + {0x00, 0x28, 0xfe, 0xfe, 0x28, 0xfe, 0xfe, 0x28}, + {0x00, 0x00, 0x48, 0x5c, 0xd6, 0xd6, 0x74, 0x24}, + {0x00, 0x46, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x62}, + {0x00, 0x12, 0x5e, 0xec, 0xba, 0xf2, 0x5e, 0x0c}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xe0, 0x20}, + {0x00, 0x00, 0x00, 0x82, 0xc6, 0x7c, 0x38, 0x00}, + {0x00, 0x00, 0x00, 0x38, 0x7c, 0xc6, 0x82, 0x00}, + {0x10, 0x54, 0x7c, 0x38, 0x38, 0x7c, 0x54, 0x10}, + {0x00, 0x00, 0x10, 0x10, 0x7c, 0x7c, 0x10, 0x10}, + {0x00, 0x00, 0x00, 0x00, 0x06, 0x07, 0x01, 0x00}, + {0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + {0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00}, + {0x00, 0x80, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06}, + {0x00, 0x7c, 0xfe, 0xb2, 0x9a, 0x8e, 0xfe, 0x7c}, + {0x00, 0x00, 0x02, 0x02, 0xfe, 0xfe, 0x42, 0x02}, + {0x00, 0x00, 0x66, 0xf6, 0x92, 0x9a, 0xce, 0x46}, + {0x00, 0x00, 0x6c, 0xfe, 0x92, 0x92, 0xc6, 0x44}, + {0x00, 0x0a, 0xfe, 0xfe, 0xca, 0x68, 0x38, 0x18}, + {0x00, 0x00, 0x9c, 0xbe, 0xa2, 0xa2, 0xe6, 0xe4}, + {0x00, 0x00, 0x0c, 0x9e, 0x92, 0xd2, 0x7e, 0x3c}, + {0x00, 0x00, 0xe0, 0xf0, 0x9e, 0x8e, 0xc0, 0xc0}, + {0x00, 0x00, 0x6c, 0xfe, 0x92, 0x92, 0xfe, 0x6c}, + {0x00, 0x00, 0x78, 0xfc, 0x96, 0x92, 0xf2, 0x60}, + {0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x66, 0x67, 0x01, 0x00}, + {0x00, 0x00, 0x00, 0x82, 0xc6, 0x6c, 0x38, 0x10}, + {0x00, 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24}, + {0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x82, 0x00}, + {0x00, 0x00, 0x60, 0xf0, 0x9a, 0x8a, 0xc0, 0x40}, + {0x00, 0x78, 0xf8, 0xba, 0xba, 0x82, 0xfe, 0x7c}, + {0x00, 0x00, 0x3e, 0x7e, 0xc8, 0xc8, 0x7e, 0x3e}, + {0x00, 0x6c, 0xfe, 0x92, 0x92, 0xfe, 0xfe, 0x82}, + {0x00, 0x44, 0xc6, 0x82, 0x82, 0xc6, 0x7c, 0x38}, + {0x00, 0x38, 0x7c, 0xc6, 0x82, 0xfe, 0xfe, 0x82}, + {0x00, 0xc6, 0x82, 0xba, 0x92, 0xfe, 0xfe, 0x82}, + {0x00, 0xc0, 0x80, 0xb8, 0x92, 0xfe, 0xfe, 0x82}, + {0x00, 0x4e, 0xce, 0x8a, 0x82, 0xc6, 0x7c, 0x38}, + {0x00, 0x00, 0xfe, 0xfe, 0x10, 0x10, 0xfe, 0xfe}, + {0x00, 0x00, 0x00, 0x82, 0xfe, 0xfe, 0x82, 0x00}, + {0x00, 0x80, 0xfc, 0xfe, 0x82, 0x02, 0x0e, 0x0c}, + {0x00, 0xc6, 0xee, 0x38, 0x10, 0xfe, 0xfe, 0x82}, + {0x00, 0x0e, 0x06, 0x02, 0x82, 0xfe, 0xfe, 0x82}, + {0x00, 0xfe, 0xfe, 0x70, 0x38, 0x70, 0xfe, 0xfe}, + {0x00, 0xfe, 0xfe, 0x18, 0x30, 0x60, 0xfe, 0xfe}, + {0x00, 0x38, 0x7c, 0xc6, 0x82, 0xc6, 0x7c, 0x38}, + {0x00, 0x60, 0xf0, 0x90, 0x92, 0xfe, 0xfe, 0x82}, + {0x00, 0x00, 0x7a, 0xfe, 0x8e, 0x84, 0xfc, 0x78}, + {0x00, 0x66, 0xfe, 0x98, 0x90, 0xfe, 0xfe, 0x82}, + {0x00, 0x00, 0x4c, 0xce, 0x9a, 0xb2, 0xf6, 0x64}, + {0x00, 0x00, 0xc0, 0x82, 0xfe, 0xfe, 0x82, 0xc0}, + {0x00, 0x00, 0xfe, 0xfe, 0x02, 0x02, 0xfe, 0xfe}, + {0x00, 0x00, 0xf8, 0xfc, 0x06, 0x06, 0xfc, 0xf8}, + {0x00, 0xfe, 0xfe, 0x0c, 0x18, 0x0c, 0xfe, 0xfe}, + {0x00, 0xc2, 0xe6, 0x3c, 0x18, 0x3c, 0xe6, 0xc2}, + {0x00, 0x00, 0xe0, 0xf2, 0x1e, 0x1e, 0xf2, 0xe0}, + {0x00, 0xce, 0xe6, 0xb2, 0x9a, 0x8e, 0xc6, 0xe2}, + {0x00, 0x00, 0x00, 0x82, 0x82, 0xfe, 0xfe, 0x00}, + {0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80}, + {0x00, 0x00, 0x00, 0xfe, 0xfe, 0x82, 0x82, 0x00}, + {0x00, 0x10, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x10}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + {0x00, 0x00, 0x00, 0x20, 0xe0, 0xc0, 0x00, 0x00}, + {0x00, 0x02, 0x1e, 0x3c, 0x2a, 0x2a, 0x2e, 0x04}, + {0x00, 0x0c, 0x1e, 0x12, 0x12, 0xfc, 0xfe, 0x82}, + {0x00, 0x00, 0x14, 0x36, 0x22, 0x22, 0x3e, 0x1c}, + {0x00, 0x02, 0xfe, 0xfc, 0x92, 0x12, 0x1e, 0x0c}, + {0x00, 0x00, 0x18, 0x3a, 0x2a, 0x2a, 0x3e, 0x1c}, + {0x00, 0x00, 0x40, 0xc0, 0x92, 0xfe, 0x7e, 0x12}, + {0x00, 0x20, 0x3e, 0x1f, 0x25, 0x25, 0x3d, 0x19}, + {0x00, 0x1e, 0x3e, 0x20, 0x10, 0xfe, 0xfe, 0x82}, + {0x00, 0x00, 0x00, 0x02, 0xbe, 0xbe, 0x22, 0x00}, + {0x00, 0x00, 0xbe, 0xbf, 0x01, 0x01, 0x07, 0x06}, + {0x00, 0x22, 0x36, 0x1c, 0x08, 0xfe, 0xfe, 0x82}, + {0x00, 0x00, 0x00, 0x02, 0xfe, 0xfe, 0x82, 0x00}, + {0x00, 0x1e, 0x3e, 0x38, 0x1c, 0x18, 0x3e, 0x3e}, + {0x00, 0x00, 0x1e, 0x3e, 0x20, 0x20, 0x3e, 0x3e}, + {0x00, 0x00, 0x1c, 0x3e, 0x22, 0x22, 0x3e, 0x1c}, + {0x00, 0x18, 0x3c, 0x24, 0x25, 0x1f, 0x3f, 0x21}, + {0x00, 0x21, 0x3f, 0x1f, 0x25, 0x24, 0x3c, 0x18}, + {0x00, 0x18, 0x38, 0x20, 0x32, 0x1e, 0x3e, 0x22}, + {0x00, 0x00, 0x24, 0x2e, 0x2a, 0x2a, 0x3a, 0x12}, + {0x00, 0x00, 0x24, 0x22, 0xfe, 0x7c, 0x20, 0x00}, + {0x00, 0x02, 0x3e, 0x3c, 0x02, 0x02, 0x3e, 0x3c}, + {0x00, 0x00, 0x38, 0x3c, 0x06, 0x06, 0x3c, 0x38}, + {0x00, 0x3c, 0x3e, 0x0e, 0x1c, 0x0e, 0x3e, 0x3c}, + {0x00, 0x22, 0x36, 0x1c, 0x08, 0x1c, 0x36, 0x22}, + {0x00, 0x00, 0x3e, 0x3f, 0x05, 0x05, 0x3d, 0x39}, + {0x00, 0x00, 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x32}, + {0x00, 0x00, 0x82, 0x82, 0xee, 0x7c, 0x10, 0x10}, + {0x00, 0x00, 0x00, 0xee, 0xee, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x10, 0x10, 0x7c, 0xee, 0x82, 0x82}, + {0x00, 0x80, 0xc0, 0x40, 0xc0, 0x80, 0xc0, 0x40}, + {0x00, 0x0e, 0x1e, 0x32, 0x62, 0x32, 0x1e, 0x0e}, + {0x00, 0x00, 0x52, 0xdf, 0x8d, 0x89, 0xf9, 0x70}, + {0x00, 0x02, 0x5e, 0x5e, 0x02, 0x02, 0x5e, 0x5c}, + {0x00, 0x00, 0x98, 0xba, 0xaa, 0x2a, 0x3e, 0x1c}, + {0x42, 0xde, 0xbe, 0xaa, 0xaa, 0xae, 0xc4, 0x40}, + {0x00, 0x02, 0x9e, 0xbe, 0x2a, 0x2a, 0xae, 0x84}, + {0x00, 0x02, 0x1e, 0x3e, 0x2a, 0xaa, 0xae, 0x84}, + {0x00, 0x02, 0x1e, 0x3e, 0xea, 0xea, 0x2e, 0x04}, + {0x00, 0x00, 0x02, 0x27, 0x25, 0x25, 0x3c, 0x18}, + {0x40, 0xd8, 0xba, 0xaa, 0xaa, 0xbe, 0xdc, 0x40}, + {0x00, 0x00, 0x98, 0xba, 0x2a, 0x2a, 0xbe, 0x9c}, + {0x00, 0x00, 0x18, 0x3a, 0x2a, 0xaa, 0xbe, 0x9c}, + {0x00, 0x00, 0x80, 0x82, 0x3e, 0x3e, 0xa2, 0x80}, + {0x00, 0x40, 0xc2, 0xbe, 0xbe, 0xa2, 0xc0, 0x40}, + {0x00, 0x00, 0x00, 0x02, 0x3e, 0xbe, 0xa2, 0x80}, + {0x00, 0x9e, 0xbe, 0x68, 0x48, 0x68, 0xbe, 0x9e}, + {0x00, 0x00, 0x0e, 0x1e, 0xd4, 0xd4, 0x1e, 0x0e}, + {0x00, 0x00, 0xa2, 0xaa, 0xaa, 0x3e, 0x3e, 0x22}, + {0x2a, 0x2a, 0x3e, 0x3e, 0x2a, 0x2a, 0x2e, 0x04}, + {0x00, 0x92, 0xfe, 0xfe, 0x90, 0xd0, 0x7e, 0x3e}, + {0x00, 0x00, 0x4c, 0xde, 0x92, 0x92, 0xde, 0x4c}, + {0x00, 0x00, 0x4c, 0x5e, 0x12, 0x12, 0x5e, 0x4c}, + {0x00, 0x00, 0x0c, 0x1e, 0x12, 0x52, 0x5e, 0x4c}, + {0x00, 0x02, 0x5e, 0xde, 0x82, 0x82, 0xde, 0x5c}, + {0x00, 0x02, 0x1e, 0x1e, 0x02, 0x42, 0x5e, 0x5c}, + {0x00, 0x00, 0x5e, 0x5f, 0x05, 0x05, 0x5d, 0x59}, + {0x80, 0x98, 0x3c, 0x66, 0x66, 0x3c, 0x98, 0x80}, + {0x00, 0x00, 0xbc, 0xbe, 0x02, 0x02, 0xbe, 0xbc}, + {0x00, 0x24, 0x24, 0xe7, 0xe7, 0x24, 0x3c, 0x18}, + {0x00, 0x04, 0x66, 0xc2, 0x92, 0xfe, 0x7e, 0x16}, + {0x00, 0x00, 0xd4, 0xf4, 0x3f, 0x3f, 0xf4, 0xd4}, + {0x05, 0x1f, 0x6f, 0xf4, 0x90, 0x90, 0xff, 0xff}, + {0x40, 0xc0, 0x90, 0xfe, 0x7f, 0x11, 0x03, 0x02}, + {0x00, 0x02, 0x9e, 0xbe, 0xaa, 0x2a, 0x2e, 0x04}, + {0x00, 0x00, 0x00, 0x82, 0xbe, 0xbe, 0x22, 0x00}, + {0x00, 0x00, 0x4c, 0x5e, 0x52, 0x12, 0x1e, 0x0c}, + {0x00, 0x02, 0x5e, 0x5e, 0x42, 0x02, 0x1e, 0x1c}, + {0x00, 0x00, 0x0e, 0x5e, 0x50, 0x50, 0x5e, 0x5e}, + {0x00, 0x00, 0xbe, 0xbe, 0x8c, 0x98, 0xbe, 0xbe}, + {0x00, 0x14, 0xf4, 0xf4, 0x94, 0xf4, 0x64, 0x00}, + {0x00, 0x00, 0x64, 0xf4, 0x94, 0xf4, 0x64, 0x00}, + {0x00, 0x00, 0x04, 0x06, 0xa2, 0xb2, 0x1e, 0x0c}, + {0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x1c, 0x1c}, + {0x00, 0x00, 0x1c, 0x1c, 0x10, 0x10, 0x10, 0x10}, + {0x89, 0xdd, 0x77, 0x33, 0x18, 0x0c, 0xf6, 0xf2}, + {0x9f, 0xdf, 0x6e, 0x36, 0x18, 0x0c, 0xf6, 0xf2}, + {0x00, 0x00, 0x00, 0xde, 0xde, 0x00, 0x00, 0x00}, + {0x44, 0x6c, 0x38, 0x10, 0x44, 0x6c, 0x38, 0x10}, + {0x10, 0x38, 0x6c, 0x44, 0x10, 0x38, 0x6c, 0x44}, + {0x00, 0xaa, 0x00, 0x55, 0x00, 0xaa, 0x00, 0x55}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0xee, 0xff, 0x55, 0xbb, 0xee, 0x55, 0xff, 0xbb}, + {0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0xff, 0xff, 0x08, 0x08, 0x08}, + {0x00, 0x00, 0x00, 0xff, 0xff, 0x28, 0x28, 0x28}, + {0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x08, 0x08}, + {0x00, 0x0f, 0x0f, 0x08, 0x0f, 0x0f, 0x08, 0x08}, + {0x00, 0x00, 0x00, 0x3f, 0x3f, 0x28, 0x28, 0x28}, + {0x00, 0xff, 0xff, 0x00, 0xef, 0xef, 0x28, 0x28}, + {0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00}, + {0x00, 0x3f, 0x3f, 0x20, 0x2f, 0x2f, 0x28, 0x28}, + {0x00, 0xf8, 0xf8, 0x08, 0xe8, 0xe8, 0x28, 0x28}, + {0x00, 0xf8, 0xf8, 0x08, 0xf8, 0xf8, 0x08, 0x08}, + {0x00, 0x00, 0x00, 0xf8, 0xf8, 0x28, 0x28, 0x28}, + {0x00, 0x00, 0x00, 0x0f, 0x0f, 0x08, 0x08, 0x08}, + {0x08, 0x08, 0x08, 0xf8, 0xf8, 0x00, 0x00, 0x00}, + {0x08, 0x08, 0x08, 0xf8, 0xf8, 0x08, 0x08, 0x08}, + {0x08, 0x08, 0x08, 0x0f, 0x0f, 0x08, 0x08, 0x08}, + {0x08, 0x08, 0x08, 0xff, 0xff, 0x00, 0x00, 0x00}, + {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, + {0x08, 0x08, 0x08, 0xff, 0xff, 0x08, 0x08, 0x08}, + {0x28, 0x28, 0x28, 0xff, 0xff, 0x00, 0x00, 0x00}, + {0x08, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00}, + {0x28, 0xe8, 0xe8, 0x08, 0xf8, 0xf8, 0x00, 0x00}, + {0x28, 0x2f, 0x2f, 0x20, 0x3f, 0x3f, 0x00, 0x00}, + {0x28, 0xe8, 0xe8, 0x08, 0xe8, 0xe8, 0x28, 0x28}, + {0x28, 0x2f, 0x2f, 0x20, 0x2f, 0x2f, 0x28, 0x28}, + {0x28, 0xef, 0xef, 0x00, 0xff, 0xff, 0x00, 0x00}, + {0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28}, + {0x28, 0xef, 0xef, 0x00, 0xef, 0xef, 0x28, 0x28}, + {0x28, 0x28, 0x28, 0xe8, 0xe8, 0x28, 0x28, 0x28}, + {0x08, 0xf8, 0xf8, 0x08, 0xf8, 0xf8, 0x08, 0x08}, + {0x28, 0x28, 0x28, 0x2f, 0x2f, 0x28, 0x28, 0x28}, + {0x08, 0x0f, 0x0f, 0x08, 0x0f, 0x0f, 0x08, 0x08}, + {0x08, 0xf8, 0xf8, 0x08, 0xf8, 0xf8, 0x00, 0x00}, + {0x28, 0x28, 0x28, 0xf8, 0xf8, 0x00, 0x00, 0x00}, + {0x28, 0x28, 0x28, 0x3f, 0x3f, 0x00, 0x00, 0x00}, + {0x08, 0x0f, 0x0f, 0x08, 0x0f, 0x0f, 0x00, 0x00}, + {0x08, 0xff, 0xff, 0x08, 0xff, 0xff, 0x08, 0x08}, + {0x28, 0x28, 0x28, 0xff, 0xff, 0x28, 0x28, 0x28}, + {0x00, 0x00, 0x00, 0xf8, 0xf8, 0x08, 0x08, 0x08}, + {0x08, 0x08, 0x08, 0x0f, 0x0f, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, + {0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0}, + {0x00, 0x22, 0x36, 0x1c, 0x36, 0x22, 0x3e, 0x1c}, + {0x00, 0x00, 0x28, 0x7c, 0x54, 0x54, 0x7f, 0x3f}, + {0x00, 0x00, 0x60, 0x60, 0x40, 0x40, 0x7e, 0x7e}, + {0x00, 0x40, 0x7e, 0x7e, 0x40, 0x7e, 0x7e, 0x40}, + {0x00, 0x00, 0xc6, 0xc6, 0x92, 0xba, 0xee, 0xc6}, + {0x00, 0x20, 0x20, 0x3c, 0x3e, 0x22, 0x3e, 0x1c}, + {0x00, 0x78, 0x7c, 0x04, 0x04, 0x7e, 0x7f, 0x01}, + {0x00, 0x40, 0x60, 0x3e, 0x7e, 0x40, 0x60, 0x20}, + {0x00, 0x00, 0x99, 0xbd, 0xe7, 0xe7, 0xbd, 0x99}, + {0x00, 0x38, 0x7c, 0xd6, 0x92, 0xd6, 0x7c, 0x38}, + {0x00, 0x32, 0x7e, 0xce, 0x80, 0xce, 0x7e, 0x32}, + {0x00, 0x00, 0x9c, 0xbe, 0xf2, 0x52, 0x1e, 0x0c}, + {0x18, 0x3c, 0x24, 0x3c, 0x3c, 0x24, 0x3c, 0x18}, + {0x18, 0xbc, 0xe4, 0x7c, 0x3c, 0x26, 0x3f, 0x19}, + {0x00, 0x00, 0x00, 0x92, 0x92, 0xd6, 0x7c, 0x38}, + {0x00, 0x00, 0x7e, 0xfe, 0x80, 0x80, 0xfe, 0x7e}, + {0x00, 0x00, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54}, + {0x00, 0x00, 0x22, 0x22, 0xfa, 0xfa, 0x22, 0x22}, + {0x00, 0x00, 0x02, 0x22, 0x72, 0xda, 0x8a, 0x02}, + {0x00, 0x00, 0x02, 0x8a, 0xda, 0x72, 0x22, 0x02}, + {0x60, 0xe0, 0x80, 0xff, 0x7f, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x07, 0x06}, + {0x00, 0x00, 0x10, 0x10, 0xd6, 0xd6, 0x10, 0x10}, + {0x00, 0x48, 0x6c, 0x24, 0x6c, 0x48, 0x6c, 0x24}, + {0x00, 0x00, 0x60, 0xf0, 0x90, 0xf0, 0x60, 0x00}, + {0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00}, + {0x80, 0x80, 0xff, 0xff, 0x03, 0x0e, 0x0c, 0x08}, + {0x00, 0x00, 0x78, 0xf8, 0x80, 0xf8, 0xf8, 0x00}, + {0x00, 0x00, 0x00, 0x48, 0xe8, 0xb8, 0x98, 0x00}, + {0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + } +}; + +BOOLEAN +CSynthesizer:: +Initialize() +/*++ + +Routine Description: + + Class initialization. + + Set the buffer the synthesizer generates images to. + Override to do any additional processing you think is needed. + +Arguments: + + none + +Return Value: + + TRUE - success. + +--*/ +{ + PAGED_CODE(); + + // Capture the working palette. + // Recast it to a point to an array of primaries. + if( !(m_Colors = GetPalette()) ) + { + return FALSE; + } + + // Clear the globals. This occurs redundantly, but hey ... + // ... test output doesn't make sense if we don't do this. + m_RelPts=0; + m_QpcTime=0; + m_Frame=0; + + // + // Allocate a scratch buffer for the synthesizer. + // + NT_ASSERT(m_Length); + if( !m_Length ) + { + return FALSE; + } + + m_Buffer = new (PagedPool) UCHAR[m_Length]; + NT_ASSERT(m_Buffer); + if( !m_Buffer ) + { + return FALSE; + } + + // + // Allocate a scratch buffer for the Gradient. + // + m_GradientBmp = new (PagedPool) CKsRgbQuad[m_Width * MAX_COLOR]; + NT_ASSERT(m_GradientBmp); + if( !m_GradientBmp ) + { + SAFE_DELETE_ARRAY( m_Buffer ); + return FALSE; + } + + return TRUE; +} + +LONGLONG +ConvertPerfTime( + _In_ LONGLONG Freq, + _In_ LONGLONG Time +) +{ + PAGED_CODE(); + + LARGE_INTEGER tL ; + tL.QuadPart = Time; + return KSCONVERT_PERFORMANCE_TIME( Freq, tL ); +} + +void +CSynthesizer:: +Destroy() +/*++ + +Routine Description: + + Clean up from initialize. + + Note: A good place to generate synthesis stats. + +Arguments: + + none + +Return Value: + + void. + +--*/ +{ + PAGED_CODE(); + + SAFE_DELETE_ARRAY( m_Buffer ); + SAFE_DELETE_ARRAY( m_GradientBmp ); + + // Report rendering times. + DBG_TRACE( "Synthesis Time - Total = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_SynthesisTime ) ); + DBG_TRACE( " Count = %d", m_SynthesisCount ); + DBG_TRACE( " Avg = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_SynthesisCount ? m_SynthesisTime / m_SynthesisCount : 0 ) ); + DBG_TRACE( " Commit Time - Total = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_CommitTime ) ); + DBG_TRACE( " Count = %d", m_CommitCount ); + DBG_TRACE( " Avg = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_CommitCount ? m_CommitTime / m_CommitCount : 0 ) ); +} + +NTSTATUS +CSynthesizer:: +SynthesizeBars() +/*++ + +Routine Description: + + Synthesize EIA-189-A standard color bars onto the Image. The image + in question is the current synthesis buffer. + +Arguments: + + None + +Return Value: + + success / failure + +--*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + const COLOR *CurColor = m_ColorBars; + ULONG ColorCount = SIZEOF_ARRAY (m_ColorBars); + + // + // Set the default cursor... + // + GetImageLocation (0, 0); + + // + // Synthesize a single line. + // + PUCHAR ImageStart = m_Cursor; + for (ULONG x = 0; x < m_Width; x++) + { + PutPixel (m_ColorBars [((x * ColorCount) / m_Width)]); + } + + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the synthesized line to all subsequent lines. + // + for (ULONG line = 0; line < m_Height; line++) + { + GetImageLocation (0, line); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } + + // Paint the top left and top right registrations boxes. + GetImageLocation(0,0); + + ImageStart = m_Cursor; + for(ULONG x =0; x < m_Width; x++) + { + if(x < (m_Height/16)) + { + PutPixel(g_TopLeft); + } + else if(x > (m_Width - (m_Height / 16))) + { + PutPixel(g_TopRight); + } + else + { + PutPixel (m_ColorBars [((x * ColorCount) / m_Width)]); + } + } + + ImageEnd = m_Cursor; + + for (ULONG line = 0; line < m_Height/16; line++) + { + GetImageLocation (0, line); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } + + // Paint the bottom left and bottom right registrations boxes. + GetImageLocation(0, (15*m_Height) / 16); + ImageStart = m_Cursor; + for(ULONG x =0; x < m_Width; x++) + { + if(x < (m_Height/16)) + { + PutPixel(g_BotLeft); + } + else if(x > (m_Width - (m_Height / 16))) + { + PutPixel(g_BotRight); + } + else + { + PutPixel (m_ColorBars [((x * ColorCount) / m_Width)]); + } + } + + ImageEnd = m_Cursor; + + for (ULONG line = 0; line < m_Height/16; line++) + { + GetImageLocation (0, line+(15*m_Height) / 16); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } + + return STATUS_SUCCESS; +} + +void +CSynthesizer:: +EncodeNumber( + _In_ ULONG LocY, + _In_ UINT32 Number, + _In_ COLOR LowColor, + _In_ COLOR HighColor +) +/* +Routine Description: + + Overlays an encoded number over the synthesized image. The image buffer + used is the set synthesis buffer. The pattern is scaled to fit the image. + +Arguments: + + LocY - + The row on the image to begin the overlay. This must + be inside the image. + + Number - + The number to encode + + LowColor - + The color to represent 0 (a clear bit) + + HighColor - + The color to represent 1 (a set bit) + +Return Value: + + void + +*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return; + } + + NT_ASSERT(LocY <= m_Height); + + GetImageLocation(0, LocY); + UINT32 mask = 0x1; + + PUCHAR ImageStart = m_Cursor; + for(ULONG i = 0; i < 32; i++) + { + for(ULONG j = 0; j < m_Width/32; j++) + { + if(Number & mask) + { + PutPixel(HighColor); + } + else + { + PutPixel(LowColor); + } + } + mask = mask << 1; + } + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the synthesized line to all subsequent lines. + // + for (ULONG line = 1; line < m_Height/16; line++) + { + RtlCopyMemory ( + GetImageLocation (0, line + LocY), + ImageStart, + ImageEnd - ImageStart + ); + } +} + +void +CSynthesizer:: +OverlayText ( + _In_ ULONG LocX, + _In_ ULONG LocY, + _In_ ULONG Scaling, + _In_ LPSTR Text, + _In_ COLOR BgColor, + _In_ COLOR FgColor +) +/*++ + +Routine Description: + + Overlay text onto the synthesized image. Clip to fit the image + if the overlay does not fit. The image buffer used is the set + synthesis buffer. + +Arguments: + + LocX - + The X location on the image to begin the overlay. This MUST + be inside the image or no text will be rendered. + POSITION_CENTER may be used to indicate horizontal centering. + + LocY - + The Y location on the image to begin the overlay. This MUST + be inside the image or no text will be rendered. + POSITION_CENTER may be used to indicate vertical centering. + + Scaling - + Normally, the overlay is done in an 8x8 font. A scaling of + 2 indicates 16x16, 3 indicates 24x24 and so forth. + + Text - + A null terminated character string containing the information to + overlay + + BgColor - + The background color of the overlay window. For transparency, + indicate TRANSPARENT here. + + FgColor - + The foreground color for the text overlay. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // Validate internal state. + if( !m_Buffer ) + { + return; + } + + NT_ASSERT( (LocX <= m_Width || LocX == POSITION_CENTER) && + (LocY <= m_Height || LocY == POSITION_CENTER) && + Text !=nullptr && + (BgColor < MAX_COLOR || BgColor == TRANSPARENT) && + (FgColor < MAX_COLOR || FgColor == TRANSPARENT) + ); + + // Validate parameters. + if( !( (LocX <= m_Width || LocX == POSITION_CENTER) && + (LocY <= m_Height || LocY == POSITION_CENTER) && + Text !=nullptr && + (BgColor < MAX_COLOR || BgColor == TRANSPARENT) && + (FgColor < MAX_COLOR || FgColor == TRANSPARENT) ) ) + { + return; + } + + size_t StrLen = 0; + PUCHAR CurChar; + + // + // Determine the character length of the string. + // + if( !NT_SUCCESS( RtlStringCchLengthA( Text, m_Width, &StrLen ) ) ) + { + // Invalid parameter. + return; + } + + // + // Determine the physical size of the string plus border. There is + // a definable NO_CHARACTER_SEPARATION. If this is defined, there will + // be no added space between font characters. Otherwise, one empty pixel + // column is added between characters. + // +#ifndef NO_CHARACTER_SEPARATION + ULONG LenX = (ULONG) ((StrLen * (((size_t)Scaling) << 3)) + 1 + StrLen); +#else // NO_CHARACTER_SEPARATION + ULONG LenX = (ULONG) ((StrLen * (((size_t_)Scaling) << 3)) + 2); +#endif // NO_CHARACTER_SEPARATION + + ULONG LenY = 2 + (Scaling << 3); + + // + // Adjust for center overlays. + // + // NOTE: If the overlay doesn't fit into the synthesis buffer, this + // merely left aligns the overlay and clips off the right side. + // + if (LocX == POSITION_CENTER) + { + //LocX = min( 0, ((LONG)m_Width - (LONG)LenX) >> 1); + if (LenX >= m_Width) + { + LocX = 0; + } + else + { + LocX = (m_Width - LenX) >> 1; + } + } + + if (LocY == POSITION_CENTER) + { + //LocY = min( 0, ((LONG)m_Width - (LONG)LenY) >> 1); + if (LenY >= m_Height) + { + LocY = 0; + } + else + { + LocY = (m_Height - LenY) >> 1; + } + } + + // + // Determine the amount of space available on the synthesis buffer. + // We will clip anything that finds itself outside the synthesis buffer. + // + ULONG SpaceX = m_Width - LocX; + ULONG SpaceY = m_Height - LocY; + + // + // Set the default cursor position. + // + GetImageLocation (LocX, LocY); + + // + // Overlay a background color row. + // + if( SpaceY ) + { + if( BgColor != TRANSPARENT ) + { + for (ULONG x = 0; x < LenX && x < SpaceX; x++) + { + PutPixel (BgColor); + } + } + SpaceY--; + } + LocY++; + + // + // Loop across each row of the image. + // + for (ULONG row = 0; row < 8 && SpaceY; row++) + { + // + // Generate a line. + // + GetImageLocation (LocX, LocY++); + + PUCHAR ImageStart = m_Cursor; + + ULONG CurSpaceX = SpaceX; + if (CurSpaceX) + { + PutPixel (BgColor); + CurSpaceX--; + } + + // + // Generate the row'th row of the overlay. + // + CurChar = (PUCHAR) Text; + while( *CurChar ) + { + UCHAR CharBase = m_FontData [m_Rotation==AcpiPldRotation90][*CurChar++][row]; + for (ULONG mask = 0x80; mask && CurSpaceX; mask >>= 1) + { + for (ULONG scale = 0; scale < Scaling && CurSpaceX; scale++) + { + if (CharBase & mask) + { + PutPixel (FgColor); + } + else + { + PutPixel (BgColor); + } + CurSpaceX--; + } + } + + // + // Separate each character by one space. Account for the border + // space at the end by placing the separator after the last + // character also. + // +#ifndef NO_CHARACTER_SEPARATION + if (CurSpaceX) + { + PutPixel (BgColor); + CurSpaceX--; + } +#endif // NO_CHARACTER_SEPARATION + } + + // + // If there is no separation character defined, account for the + // border. + // +#ifdef NO_CHARACTER_SEPARATION + if (CurSpaceX) + { + PutPixel (BgColor); + CurSpaceX--; + } +#endif // NO_CHARACTER_SEPARATION + + + PUCHAR ImageEnd = m_Cursor; + // + // Copy the line downward scale times. + // + for (ULONG scale = 1; scale < Scaling && SpaceY; scale++) + { + GetImageLocation (LocX, LocY++); + RtlCopyMemory (m_Cursor, ImageStart, ImageEnd - ImageStart); + SpaceY--; + } + } + + // + // Add the bottom section of the overlay. + // + GetImageLocation (LocX, LocY); + if (BgColor != TRANSPARENT && SpaceY) + { + for (ULONG x = 0; x < LenX && x < SpaceX; x++) + { + PutPixel (BgColor); + } + } +} + +void +CSynthesizer::ApplyGradient( + _In_ ULONG LocY, + _In_ COLOR Gradient +) +/*++ + +Routine Description: + + Synthesize a horizontal gradient bar from a given color + +Arguments: + + LocY - + row + + Gradient - + A starting color + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return; + } + + NT_ASSERT(LocY <= m_Height); + + PUCHAR Image = GetImageLocation(0, LocY); + PUCHAR RowBmp = reinterpret_cast<PUCHAR>(&m_GradientBmp[Gradient*m_Width]); + ULONG Stride = m_Width * sizeof(CKsRgbQuad); + + // + // Copy the synthesized line to all lines. + // + for (ULONG line = 0; line < m_Height/16; line++) + { + RtlCopyMemory ( + Image, + RowBmp, + Stride + ); + Image += Stride; + } +} + +// +// Synthesize +// +// Fill the image buffer with some base image. All h/w simulations will +// call this function to generate a base image - even for jpeg. +// +NTSTATUS +CSynthesizer:: +Synthesize() +/*++ + +Routine Description: + + Fill the image buffer with some base image. All h/w simulations will + call this function to generate a base image. + +Arguments: + + None + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Generate a "time stamp" just to overlay it onto the capture image. + // It makes it more exciting than bars that do nothing. + // + SynthesizeBars(); + + ApplyGradient( (m_Height)/16, RED); + ApplyGradient( (2*m_Height)/16, GREEN); + ApplyGradient( (3*m_Height)/16, BLUE); + ApplyGradient( (4*m_Height)/16, WHITE); + + EncodeNumber((5*m_Height)/16, (UINT32)m_Frame, BLACK, WHITE); + EncodeNumber((6*m_Height)/16, (UINT32)(m_QpcTime), BLACK, WHITE); + + CHAR Text [256]; + RtlStringCbPrintfA(Text, sizeof(Text), "Frame: %ld", m_Frame); + + // + // Overlay the frame # onto the scratch space image. + // + OverlayText ( + POSITION_CENTER, + (m_Height - 28), + 1, + Text, + BLACK, + TEXT_COLOR + ); + + // + // Add a description of the frame type/width/height. + // + RtlStringCbPrintfA(Text, sizeof(Text), "%s (%dx%d)", m_FormatName, m_Width, m_Height); + OverlayText ( + 0, + (m_Height - 28), + 1, + Text, + TRANSPARENT, + TEXT_COLOR + ); + + // + // Add a description of the mounting. + // + RtlStringCbPrintfA(Text, sizeof(Text), "%d\370 Mounting", DbgRotation2Degrees(m_Rotation)); + size_t len = 0; + RtlStringCchLengthA(Text, sizeof(Text), &len); + OverlayText ( + (m_Width - (((ULONG)len*8))), // right-adjust text. + (m_Height - 28), + 1, + Text, + TRANSPARENT, + TEXT_COLOR + ); + + return STATUS_SUCCESS; +} + +void +CSynthesizer::PutPixel ( + COLOR Color +) +/*++ + +Routine Description: + + Place a pixel at the default cursor location. The cursor location + must be set via GetImageLocation(x, y). + +Arguments: + + Color - + The pixel color to render + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + PKS_RGBQUAD &pPixel = (PKS_RGBQUAD &) m_Cursor; + + if (Color != TRANSPARENT) + { + *pPixel = CKsRgbQuad( m_Colors[Color][2], m_Colors[Color][1], m_Colors[Color][0] ); + } + + // Next pixel + pPixel++; +} + +// +// PutPixel(): +// +// Place a pixel at the default cursor location. The cursor location +// must be set via GetImageLocation(x, y). +// +void +CSynthesizer::PutPixel ( + UCHAR colorR, + UCHAR colorB, + UCHAR colorG +) +/*++ + +Routine Description: + + Place a pixel at the default cursor location. The cursor location + must be set via GetImageLocation(x, y). + + TODO: Remove. This function appears to be dead code. + +Arguments: + + colorR - + The first color primary of the pixel. + + colorB - + The third color primary of the pixel. + + colorG - + The second color primary of the pixel. + + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + PKS_RGBQUAD &pPixel = (PKS_RGBQUAD &) m_Cursor; + + *pPixel++ = CKsRgbQuad( colorR, colorG, colorB ); +} + + +// +// Synthesize +// +// Fill the image buffer with some base image. All h/w simulations will +// call this function to generate a base image - even for jpeg. +// +NTSTATUS +CSynthesizer:: +DoSynthesize() +{ + PAGED_CODE(); + + LARGE_INTEGER StartTime = {0}; + LARGE_INTEGER EndTime = {0}; + + StartTime = KeQueryPerformanceCounter(NULL); + + NTSTATUS Status = Synthesize(); + + m_SynthesisTime += KeQueryPerformanceCounter(NULL).QuadPart - StartTime.QuadPart; + m_SynthesisCount++; + + return Status; +} + +// +// Commit +// +// Copy (and reformat, if necessary) pixels from the internal scratch +// buffer. If the output format decimates chrominance, do it here. +// +_Success_(return > 0) +ULONG +CSynthesizer:: +DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +{ + PAGED_CODE(); + + LARGE_INTEGER StartTime = KeQueryPerformanceCounter(NULL); + + ULONG n = Commit( Buffer, Size, Stride ); + + m_CommitTime += KeQueryPerformanceCounter(NULL).QuadPart - StartTime.QuadPart; + m_CommitCount++; + + return n; +} + +// +// DoCommit +// +// Note: The stride needs to be a function of the bits per pixel of the +// OUTPUT format - not the format we store it in. The class ctor +// must initialize m_OutputStride to a default stride value. +// +_Success_(return > 0) +ULONG +CSynthesizer:: +DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size +) +{ + PAGED_CODE(); + + LARGE_INTEGER StartTime = KeQueryPerformanceCounter(NULL); + + ULONG n = Commit( Buffer, Size ); + + m_CommitTime += KeQueryPerformanceCounter(NULL).QuadPart - StartTime.QuadPart; + m_CommitCount++; + + return n; +} + +#define CLEAR_HISTOGRAM( H ) \ + if( H ) \ + { \ + RtlZeroMemory( H, 256*sizeof(*H) ); \ + } + +#define BUMP_HISTOGRAM_PRIMARY( H, C ) \ + if( H ) \ + { \ + H[C]++; \ + } + +// +// Histogram +// +// Fill an array with histogram data. +// +void +CSynthesizer:: +Histogram( + _Out_writes_opt_(256) + PULONG HistogramP0, // rgbRed + _Out_writes_opt_(256) + PULONG HistogramP1, // rgbGreen + _Out_writes_opt_(256) + PULONG HistogramP2 // rgbBlue +) +/*++ + +Routine Description: + + Fill array(s) with histogram data. + +Arguments: + + HistogramP0 - + Stats on the first primary. + + HistogramP1 - + Stats on the second primary. + + HistogramP2 - + Stats on the third primary. + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + CLEAR_HISTOGRAM( HistogramP0 ); + CLEAR_HISTOGRAM( HistogramP1 ); + CLEAR_HISTOGRAM( HistogramP2 ); + + ULONG row, col; + + for( row=0; row<m_Height; row++ ) + { + PKS_RGBQUAD pPixel = (PKS_RGBQUAD) GetImageLocation(0, row); + + for( col=0; col<m_Width; col++ ) + { + BUMP_HISTOGRAM_PRIMARY( HistogramP0, pPixel->rgbRed ); + BUMP_HISTOGRAM_PRIMARY( HistogramP1, pPixel->rgbGreen ); + BUMP_HISTOGRAM_PRIMARY( HistogramP2, pPixel->rgbBlue ); + } + } +} diff --git a/AVStream/avscamera/sys/Synthesizer.h b/AVStream/avscamera/sys/Synthesizer.h new file mode 100644 index 00000000..8bf730ac --- /dev/null +++ b/AVStream/avscamera/sys/Synthesizer.h @@ -0,0 +1,521 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Synthesizer.h + + Abstract: + + This file contains the definition of CSynthesizer. + + The base image synthesis and overlay class. These classes provide + image synthesis (pixel, color-bar, etc...) onto buffers of various + formats. + + Internally, all CSynthesizer objects represent a pixel as a 32 bit + quantity with 8 bits per sample. The base CSynthesizer implements + all rendering functionality with this assumption in mind. It + simplifies rendering substantially and means we do not need to re- + implement rendering functions for each format. + + Most end formats can be synthesized from this uncompressed format + with overhead that is slightly worse than a copy. Color spaces are + handled by using a rendering palette that is unique for each. + + History: + + created 04/14/2014 + +**************************************************************************/ + +#pragma once + +// +// COLOR: +// +// Pixel color for placement onto the synthesis buffer. +// +typedef enum +{ + + BLACK = 0, + WHITE, + YELLOW, + CYAN, + GREEN, + MAGENTA, + RED, + BLUE, + GREY, + + TEXT_COLOR, // Used to offset text + + MAX_COLOR, + TRANSPARENT, + +} COLOR; + +#define CHANNEL_YCrCb ( MF_HISTOGRAM_CHANNEL_Y | MF_HISTOGRAM_CHANNEL_Cr | MF_HISTOGRAM_CHANNEL_Cb ) +#define CHANNEL_RGB ( MF_HISTOGRAM_CHANNEL_R | MF_HISTOGRAM_CHANNEL_G | MF_HISTOGRAM_CHANNEL_B ) +#define CHANNEL_NONE ( 0 ) + +// +// POSITION_CENTER: +// +// Only useful for text overlay. This can be substituted for LocX or LocY +// in order to center the text screen on the synthesis buffer. +// +#define POSITION_CENTER ((ULONG)-1) + +// +// Initializer class for a KS_RGBQUAD +// +class CKsRgbQuad : public KS_RGBQUAD +{ +public: + // Initializer ctor + CKsRgbQuad( + BYTE r=0, + BYTE g=0, + BYTE b=0 ) + { + rgbBlue = b; + rgbGreen = g; + rgbRed = r; + rgbReserved = 0; + } +}; + +/************************************************* + + CSynthesizer + + This class synthesizes images in various formats for output from the + capture filter. It is capable of performing various text overlays onto + the image surface. + + The base synthesizer is essentially the XRGB Synthesizer. For YUV, we + repurpose the color primaries and provide a different definition for the + color pallette. A call to CSynthesizer::Commit(buffer) will reformat the + internal representation and down-sample the color primaries, as necessary + into the supplied buffer. + +*************************************************/ + +typedef +UCHAR UCHAR4[4]; + +class CSynthesizer : + public CNonCopyable +{ +protected: + + static + const UCHAR m_FontData [2][256][8]; + static + const COLOR m_ColorBars[8]; + + // These values are used by Synthesize() for display purposes. They are + // static so that setting the values on the preview pin affects all pins. + // + // TODO: Change to references to a shared object. Use of static values + // could cause confusion if two (or more) cameras are in use at + // the same time. + static + LONGLONG m_RelPts; + static + LONGLONG m_QpcTime; + static + ULONG m_Frame; + + // This value is used to fudge a rotated image. + enum + AcpiPldRotation + m_Rotation; + + // + // The width and height the synthesizer is set to. + // + ULONG m_Width; + ULONG m_Height; + + // + // The synthesis buffer: + // + // This buffer points to an array of KS_RGBQUAD. Internally we synthesize + // images in an uncompressed format, because it is at least as fast to + // composite an image in such a format and convert to the target compressed + // format in a final pass over the image. + // + // An uncompressed format also has less loss of fidelity. For instance, + // NV12 only contains 1 chroma sample per macro-pixel (2x2). Once this + // macro pixel is rendered, compositing additional color information + // into it results in a loss of color precision / fidelity. + // + PUCHAR m_Buffer; // pointer to pixel data + ULONG m_Length; // size of the buffer in bytes + LONG m_SynthesisStride; // size of scan line in bytes + + // + // The assumed stride of our output format. Currently used by YUY2 and + // all image captures, except NV12. This value should be initialized by + // the derived classes. + // + LONG m_OutputStride; + + // Bitmap with a gradient applied for each color in the color pallet. + CKsRgbQuad *m_GradientBmp; + + // + // The default cursor. This is a pointer into the synthesis buffer where + // a non specific PutPixel will be placed. + // + PUCHAR m_Cursor; + + // + // A printable name identifying this format. + // + PCCHAR m_FormatName; + + // + // A color palette for this colorspace. + // + // Specifying a unique color palette is a cheap method for switching + // between RGB and YUV color spaces. A derived class is free to assign + // this to any table it chooses. It can be used to either change the + // colors used for rendering, or it can be used to support a new (or + // slightly different) color space. + // + const UCHAR4 *m_Colors; + + // + // A bitset specifying the color primaries used by this format. + // (Used by Histogram.) + // + ULONG m_ChannelMask; + + // + // Debugging values + // + LARGE_INTEGER m_Frequency; + LONGLONG m_SynthesisTime; + ULONG m_SynthesisCount; + LONGLONG m_CommitTime; + ULONG m_CommitCount; + +public: + + // + // DEFAULT CONSTRUCTOR + // + CSynthesizer( + PCCHAR Name="[Unknown]", + ULONG ChannelMask=0, + ULONG Width=0, + ULONG Height=0 + ) + : m_Width(Width) + , m_Height(Height) + , m_Buffer(nullptr) + , m_Cursor(nullptr) + , m_GradientBmp(nullptr) + , m_SynthesisStride(m_Width * sizeof(KS_RGBQUAD)) + , m_OutputStride(0) + , m_FormatName(Name) + , m_ChannelMask(ChannelMask) + , m_SynthesisCount(0) + , m_SynthesisTime(0) + , m_CommitCount(0) + , m_CommitTime(0) + , m_Rotation(AcpiPldRotation0) + { + m_Length = Height * m_SynthesisStride; + KeQueryPerformanceCounter(&m_Frequency); + } + + // + // DESTRUCTOR: + // + virtual + ~CSynthesizer() + {} + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual + void + PutPixel ( + COLOR Color + ); + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual + void + PutPixel ( + UCHAR colorR, + UCHAR colorB, + UCHAR colorG + ); + + // + // Create a horizontal color bar that slowly fades from the specified + // color to black. + // + // Note: To generalize, this function could specify the height of the + // color bar instead of inferring it. + // + void + ApplyGradient( + _In_ ULONG LocY, + _In_ COLOR Gradient + ); + + + // + // SetImageSize(): + // + // Set the image size of the synthesis buffer. + // + void + SetImageSize ( + _In_ ULONG Width, + _In_ ULONG Height + ) + { + m_Width = Width; + m_Height = Height; + } + + // + // Initialize() + // + // Set the buffer the synthesizer generates images to. + // Override to do any additional processing you think is needed. + // + virtual + BOOLEAN + Initialize(); + + // + // Destroy() + // + // Clean up from initialize. + // + virtual + void + Destroy(); + + // + // SynthesizeBars(): + // + // Synthesize EIA-189-A standard color bars. + // + virtual + NTSTATUS + SynthesizeBars(); + + // + // Synthesize + // + // Fill the image buffer with some base image. All h/w simulations will + // call this function to generate a base image + // + virtual + NTSTATUS + Synthesize(); + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + )=0; + + // + // Commit + // + // Note: The stride needs to be a function of the bits per pixel of the + // OUTPUT format - not the format we store it in. The class ctor + // must initialize m_OutputStride to a default stride value. + // + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size + ) + { + return Commit( Buffer, Size, m_OutputStride ); + } + + // + // Histogram + // + // Fill an array with histogram data. Each parameter is optional. + // + void + Histogram( + _Out_writes_opt_(256) + PULONG HistogramP0, // rgbRed + _Out_writes_opt_(256) + PULONG HistogramP1, // rgbGreen + _Out_writes_opt_(256) + PULONG HistogramP2 // rgbBlue + ); + + // + // Synthesize + // + // Fill the image buffer with some base image. All h/w simulations will + // call this function to generate a base image + // + NTSTATUS + DoSynthesize(); + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + _Success_(return > 0) + ULONG + DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); + + // + // DoCommit + // + // Note: The stride needs to be a function of the bits per pixel of the + // OUTPUT format - not the format we store it in. The class ctor + // must initialize m_OutputStride to a default stride value. + // + _Success_(return > 0) + ULONG + DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size + ); + + // + // OverlayText(): + // + // Overlay a text string onto the image. + // + void + OverlayText ( + _In_ ULONG LocX, + _In_ ULONG LocY, + _In_ ULONG Scaling, + _In_ LPSTR Text, + _In_ COLOR BgColor, + _In_ COLOR FgColor + ); + + virtual + void + EncodeNumber( + _In_ ULONG LocY, + _In_ UINT32 Number, + _In_ COLOR LowColor, + _In_ COLOR HighColor + ); + + void + SetRelativePts( LONGLONG Pts ) + { + m_RelPts = Pts; + } + + void + SetQpcTime( LONGLONG Pts ) + { + m_QpcTime = Pts; + } + + void + SetFrameNumber( ULONG Frame ) + { + m_Frame = Frame; + } + + void + SetRotation( AcpiPldRotation Rotation ) + { + m_Rotation = Rotation; + } + + // + // GetChannelMask + // + // Get the color channel mask associated with this format. + // + ULONG + GetChannelMask() + { + return m_ChannelMask; + } + +protected: + // + // GetPalette + // + // Get a pointer to an array of palette colors. Used mostly to handle + // different color primaries. Location of the primary must agree with + // Commit(). + // + virtual + const UCHAR4 * + GetPalette() + { + return nullptr; + } + + // + // GetImageLocation + // + // Get the location into the image buffer for a specific X/Y location. + // This also sets the synthesizer's default cursor to the position + // LocX, LocY. + // + PUCHAR + GetImageLocation ( + _In_ ULONG LocX, + _In_ ULONG LocY + ) + { + return + m_Cursor = + (m_Buffer + (sizeof(CKsRgbQuad) * LocX) + (LocY * m_SynthesisStride)); + } + +}; + diff --git a/AVStream/avscamera/sys/Timer.cpp b/AVStream/avscamera/sys/Timer.cpp new file mode 100644 index 00000000..5345a361 --- /dev/null +++ b/AVStream/avscamera/sys/Timer.cpp @@ -0,0 +1,172 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + timer.cpp + + Abstract: + + Contains the implementation of a KTimer and KPassiveTimer. + + KTimer wraps a kernel timer object. KPassiveTimer uses a passive + callback through a KWorkItem. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#include "Common.h" + +KTimer:: +KTimer( TIMER_TYPE type ) + : m_Period(0) + , m_Context(NULL) + , m_Callback(NULL) +{ + KeInitializeTimerEx( &m_Timer,type ) ; + KeInitializeDpc( &m_Dpc, KTimer::DpcThunk, this ) ; +} + +KTimer:: +~KTimer() +{ + Cancel(); +} + +// +// Cancels the timer and ensures no DPC is executing. +// +BOOLEAN +KTimer:: +Cancel() +{ + // Clear the timer and dequeue the DPC. + if( !Reset() ) + { + // If the item wasn't found, flush all the DPC queues. + KeFlushQueuedDpcs() ; + + // Return false because we lost the race. + return FALSE; + } + return TRUE; +} + +// +// Support for waitable class +// +PVOID +KTimer:: +GetDispatchObject() +{ + return &m_Timer; +} + +// +// Dpc Thunking function for KeSetTimerEx +// +_Function_class_(KDEFERRED_ROUTINE) +VOID +KTimer:: +DpcThunk( + _In_ PKDPC /*Dpc*/, + _Inout_opt_ PVOID DeferredContext, + _Inout_opt_ PVOID /*Arg1*/, + _Inout_opt_ PVOID /*Arg2*/ +) +{ + KTimer *pKTimer = (KTimer *) DeferredContext ; + pKTimer->Func() ; +} + +// +// Function invoked when a timer expires +// +// IRQL <= DISPATCH_LEVEL +// +void +KTimer:: +Func() +{ + if( m_Callback ) + { + m_Callback( m_Context ) ; + } +} + +// +// Dtor +// +// Calls Cancel() here to clean up. +// +// Note: KTimer will also call Cancel(), but it will invoke KTimer::Cancel(). +// Since KPassiveTimer::Cancel() first calls KTimer::Cancel() this second +// call is redudant; but preventing it would probably require an explicit +// call to Cancel() before destruction. This way you can just delete +// timers when they are no longer needed, but it make take a bit longer. +// +KPassiveTimer:: +~KPassiveTimer() +{ + Cancel(); +} + +// +// Function invoked when a timer expires +// +// IRQL == DISPATCH_LEVEL +// +void +KPassiveTimer:: +Func() +{ + if( m_Callback ) + { + // Start the work item. If it fails, there's not much we can do since + // it can only fail if the owner of this timer is trying to cancel it. + m_WorkItem.Start(); + } +} + +VOID +KPassiveTimer:: +WorkThunk( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + if( Context ) + { + ((KPassiveTimer *) Context)->KTimer::Func(); + } +} + +// +// Cancels the timer +// +BOOLEAN +KPassiveTimer:: +Cancel() +{ + // Kill the timer. + if( !KTimer::Cancel() ) + { + // Wait until the work item is idle. + m_WorkItem.Wait(); + + // Reset the WorkItem so we can use it again. + m_WorkItem.Reset(); + + return FALSE; + } + return TRUE; +} + diff --git a/AVStream/avscamera/sys/Timer.h b/AVStream/avscamera/sys/Timer.h new file mode 100644 index 00000000..d4e6322e --- /dev/null +++ b/AVStream/avscamera/sys/Timer.h @@ -0,0 +1,199 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + timer.h + + Abstract: + + Contains the definition of a KTimer and KPassiveTimer. + + KTimer wraps a kernel timer object. KPassiveTimer uses a passive + callback through a KWorkItem. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#pragma once + +typedef void (*PKTimerCallback)( PVOID Context ) ; + +class KTimer : public KWaitable, public Cancelable +{ +public: + KTimer( TIMER_TYPE type=NotificationTimer ); + + virtual + ~KTimer(); + +public: + // + // Override function that is invoked when a timer expires + // + virtual + void + Func() ; + + // + // Cancels the timer + // + virtual + BOOLEAN + Cancel(); + + // + // Returns TRUE is if the timer is signalled + // + BOOLEAN + State() + { + return KeReadStateTimer( &m_Timer ) ; + } + + // + // Resets (Cancels) the timer and DPC. + // + // Reset() returns TRUE if the KTIMER or the KDPC are dequeued. + // If Reset() returns FALSE the DPC may still be executing on another + // processor. + // + BOOLEAN + Reset() + { + // Try cancelling the timer. + if( !KeCancelTimer( &m_Timer ) ) + { + // If the timer couldn't be cancelled, try removing the DPC. + return KeRemoveQueueDpc( &m_Dpc ) ; + } + return TRUE; + } + + // + // Sets the timer + // + BOOLEAN + Set( + _In_ LARGE_INTEGER DueTime, + _In_opt_ PKTimerCallback Callback=NULL, + _In_opt_ PVOID Context=NULL, + _In_ LONG Period=0 + ) + { + m_Callback = Callback ; + m_Context = Context ; + m_Period = Period ; + return KeSetTimerEx( &m_Timer, DueTime, Period, &m_Dpc ) ; + } + + // + // Sets the timer + // + BOOLEAN + Set( + _In_ LONGLONG DueTime, + _In_opt_ PKTimerCallback Callback=NULL, + _In_opt_ PVOID Context=NULL, + _In_ LONG Period=0 + ) + { + m_Callback = Callback ; + m_Context = Context ; + m_Period = Period ; + LARGE_INTEGER Due; + Due.QuadPart = DueTime; + return KeSetTimerEx( &m_Timer, Due, Period, &m_Dpc ) ; + } + + // + // Support for waitable class + // + virtual + PVOID + GetDispatchObject(); + +private: + // + // Dpc Thunking function for KeSetTimerEx + // + _Function_class_(KDEFERRED_ROUTINE) + static + VOID + DpcThunk( + _In_ PKDPC Dpc, + _Inout_opt_ PVOID DeferredContext, + _Inout_opt_ PVOID Arg1, + _Inout_opt_ PVOID Arg2 + ) ; + + TIMER_TYPE GetType() + { + return m_type; + } + +private: + KTIMER m_Timer ; + TIMER_TYPE m_type; + + KDPC m_Dpc ; + LONG m_Period ; + +protected: + PKTimerCallback m_Callback ; + PVOID m_Context ; +}; + +class KPassiveTimer : public KTimer +{ +public: + KPassiveTimer( + _In_ PDEVICE_OBJECT DeviceObj, + _In_ TIMER_TYPE type=NotificationTimer + ) + : KTimer( type ) + , m_WorkItem( DeviceObj, WorkThunk, this ) + {} + + virtual + ~KPassiveTimer(); + + // + // Override function that is invoked when a timer expires + // + virtual + void + Func() ; + + // + // Cancels the timer + // + virtual + BOOLEAN + Cancel(); + + // + // Make sure it's in a valid state. + // + virtual + BOOLEAN + IsValid() + { + return m_WorkItem.IsValid(); + } +private: + KWorkItem m_WorkItem; + + // + // Dpc Thunking function for KeSetTimerEx + // + static + IO_WORKITEM_ROUTINE + WorkThunk; +};
\ No newline at end of file diff --git a/AVStream/avscamera/sys/Util.cpp b/AVStream/avscamera/sys/Util.cpp new file mode 100644 index 00000000..7c788990 --- /dev/null +++ b/AVStream/avscamera/sys/Util.cpp @@ -0,0 +1,491 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + util.cpp + + Abstract: + + Contains miscellaneous helper functions such as random number + generation and bounds checking functions. + + History: + + created 7/12/2013 + +**************************************************************************/ + +#include "Common.h" +#include <bcrypt.h> + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +ULONG +GetRandom( + _In_ ULONG Minimum, + _In_ ULONG Maximum +) +{ + ULONG Value=0; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + if( KeGetCurrentIrql()==PASSIVE_LEVEL ) + { + Status= + BCryptGenRandom( NULL, (PUCHAR) &Value, sizeof(Value), BCRYPT_USE_SYSTEM_PREFERRED_RNG ); + } + + // Fallback to QPC if BCRYPT isn't available. + if( !NT_SUCCESS(Status) ) + { + LARGE_INTEGER QPC = KeQueryPerformanceCounter(NULL); + Value = QPC.u.LowPart; + } + + // Make sure we don't divide by 0. + if( Minimum < Maximum ) + { + Value = ((Value - Minimum) % (Maximum - Minimum +1)) + Minimum; + } + else + { + Value = Minimum ; + } + + return Value; +} + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +LONG +GetRandom( + _In_ LONG Minimum, + _In_ LONG Maximum +) +{ + ULONG Value=0; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + if( KeGetCurrentIrql()==PASSIVE_LEVEL ) + { + Status= + BCryptGenRandom( NULL, (PUCHAR) &Value, sizeof(Value), BCRYPT_USE_SYSTEM_PREFERRED_RNG ); + } + + // Fallback to QPC if BCRYPT isn't available. + if( !NT_SUCCESS(Status) ) + { + LARGE_INTEGER QPC = KeQueryPerformanceCounter(NULL); + Value = QPC.u.LowPart; + } + + // Make sure we don't divide by 0. + if( Minimum < Maximum ) + { + Value = ((Value - Minimum) % (Maximum - Minimum +1)) + Minimum; + } + else + { + Value = Minimum ; + } + + return Value; +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// +// Convert an EV Compensation flag into the denominator of a fraction. +// +LONG +EVFlags2Denominator( + _In_ ULONGLONG Flags +) +{ + PAGED_CODE( ); + + static + LONG val[] = {1,2,3,4,6}; + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP: + return 6; + case KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP: + return 4; + case KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP: + return 3; + case KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP: + return 2; + case KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP: + return 1; + case KSCAMERA_PERFRAMESETTING_AUTO: + return val[GetRandom( (ULONG) 0, (ULONG) SIZEOF_ARRAY(val)-1)]; + } + + return 0; +} + +// Convert white balance presets to a temperature. +ULONG +WhiteBalancePreset2Temperature( + _In_ ULONG WBPreset +) +{ + PAGED_CODE(); + + ULONG temp = 0; + + switch (WBPreset) + { + case KSCAMERA_EXTENDEDPROP_WBPRESET_CLOUDY: + temp = 6000; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_DAYLIGHT: + temp = 5200; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLASH: + temp = 6000; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLUORESCENT: + temp = 4000; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_TUNGSTEN: + temp = 3200; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_CANDLELIGHT: + temp = 1000; + break; + default: + temp = 5000; + break; + } + + return temp; +} + +typedef struct +{ + LONG exposureBinaryLog; + LONGLONG exposure100ns; +} ExposurePresetStruct; + +const ExposurePresetStruct ExposurePresets[] = +{ + { -10, 10000 }, // 1/1024s -> 9765.625 (100 ns) (Min is 1 ms) + { -9, 19531 }, // 1/512s -> 19531.25 (100 ns) + { -8, 39063 }, // 1/256s -> 39062.5 (100 ns) + { -7, 78125 }, // 1/128s -> 78125 (100 ns) + { -6, 196250 }, // 1/64s -> 196250 (100 ns) + { -5, 312500 }, // 1/32s -> 312500 (100 ns) + { -4, 625000 }, // 1/16s -> 625000 (100 ns) + { -3, 1250000 }, // 1/8s -> 1250000 (100 ns) + { -2, 2500000 }, // 1/4s -> 2500000 (100 ns) + { -1, 5000000 }, // 1/2s -> 5000000 (100 ns) + { 0, 10000000 },// 1s -> 1 * 10000000 (100 ns) + { 1, 20000000 },// 2s -> 2 * 10000000 (100 ns) + { 2, 40000000 },// 4s -> 4 * 10000000 (100 ns) + { 3, 80000000 },// 8s -> 8 * 10000000 (100 ns) + { 4, 160000000 },// 16s -> 16 * 10000000 (100 ns) + { 5, 320000000 },// 32s -> 32 * 10000000 (100 ns) + { 6, 640000000 },// 64s -> 64 * 10000000 (100 ns) + { 7, 1280000000 },// 128s -> 128 * 10000000 (100 ns) + { 8, 2560000000 },// 256s -> 256 * 10000000 (100 ns) + { 9, 3600000000 } // 512s -> 512 * 10000000 (100 ns) (Max is 360 s) +}; + +const ExposurePresetStruct ExposureMidpoints[] = +{ + { -10, 13811 }, // 2^(-9.5) -> .001381067 + { -9, 27621 }, // 2^(-8.5) -> .002762136 + { -8, 55243 }, // 2^(-7.5) -> .005524271 + { -7, 110485 }, // 2^(-6.5) -> .011048543 + { -6, 220970 }, // 2^(-5.5) -> .022097086 + { -5, 441941 }, // 2^(-4.5) -> .044194174 + { -4, 883883 }, // 2^(-3.5) -> .0883883476 + { -3, 1767767 }, // 2^(-2.5) -> .1767766953 + { -2, 3535534 }, // 2^(-1.5) -> .3535533905 + { -1, 7071067 }, // 2^(-0.5) -> .7071067811 + { 0, 14142136 },// 2^(0.5) -> 1.414213562 + { 1, 28284271 },// 2^(1.5) -> 2.828427125 + { 2, 56568542 },// 2^(2.5) -> 5.656854250 + { 3, 113137085 },// 2^(3.5) -> 11.31370850 + { 4, 226274170 },// 2^(4.5) -> 22.62741700 + { 5, 452548340 },// 2^(5.5) -> 45.25483400 + { 6, 905096680 },// 2^(6.5) -> 90.50966799 + { 7, 1810193360 },// 2^(7.5) -> 181.0193360 + { 8, 3080000000 },// Halfway between 8 and Max: 30800 + { 9, 3600000000 } // Above +}; + +BOOL +ExposureBinaryLogIsValid( + _In_ LONG ExposureBL +) +{ + PAGED_CODE(); + + return + ExposurePresets[0].exposureBinaryLog <= ExposureBL && + ExposurePresets[_countof(ExposurePresets) - 1].exposureBinaryLog >= ExposureBL; +} + +// Changes a Legacy Preset value into 100 ns units for Exposure. If the +// preset is out of range of what we support, return 0 to indicate failure. +LONGLONG +ExposureBinaryLogTo100ns( + _In_ LONG ExposureBL +) +{ + PAGED_CODE( ); + + for( LONG i = 0; i < _countof(ExposurePresets); i++ ) + { + if (ExposureBL == ExposurePresets[i].exposureBinaryLog) + { + return ExposurePresets[i].exposure100ns; + } + } + + return 0; +} + +LONG +Exposure100nsToBinaryLog( + _In_ LONGLONG Exposure100ns +) +{ + PAGED_CODE( ); + + for( LONG i = 0; i < _countof(ExposureMidpoints) - 1; i++ ) + { + if(Exposure100ns <= ExposureMidpoints[i].exposure100ns) + { + return ExposureMidpoints[i].exposureBinaryLog; + } + } + + return ExposureMidpoints[_countof(ExposureMidpoints) - 1].exposureBinaryLog; +} + + +static +ULONGLONG PotentialIsoSetting[] = +{ + KSCAMERA_EXTENDEDPROP_ISO_50 + , KSCAMERA_EXTENDEDPROP_ISO_80 + , KSCAMERA_EXTENDEDPROP_ISO_100 + , KSCAMERA_EXTENDEDPROP_ISO_200 + , KSCAMERA_EXTENDEDPROP_ISO_400 + , KSCAMERA_EXTENDEDPROP_ISO_800 + , KSCAMERA_EXTENDEDPROP_ISO_1600 + , KSCAMERA_EXTENDEDPROP_ISO_3200 + , KSCAMERA_EXTENDEDPROP_ISO_6400 + , KSCAMERA_EXTENDEDPROP_ISO_12800 + , KSCAMERA_EXTENDEDPROP_ISO_25600 +}; + +// +// Convert ISO preset flags to a preset number +// +ULONG +IsoPreset2Value( + _In_ ULONGLONG Flags +) +{ + PAGED_CODE( ); + + static + ULONG IsoValue[] = + { + 50 + , 80 + , 100 + , 200 + , 400 + , 800 + , 1600 + , 3200 + , 6400 + , 12800 + , 25600 + }; + + // Simple scan for min + for( ULONG i=0; i<_countof(PotentialIsoSetting); i++ ) + { + if( PotentialIsoSetting[i] & Flags ) + { + return IsoValue[i]; + } + } + return MAXULONG; +} + +// +// Convert ISO Mode & Value to legacy presets. +// +// Note: This function will simply pass legacy presets through. +// +ULONGLONG +IsoModeValue2Preset( + _In_ ULONGLONG Mode, + _In_ ULONG Value +) +{ + PAGED_CODE( ); + + // These values are staggerred midpoints along a logrithmic scale. + // Using these pre-calculated values simplifies our search to a + // simple set of comparisons. + static + ULONG IsoValue[] = + { + 64 //50 //KSCAMERA_EXTENDEDPROP_ISO_50 + ,90 //, 80 //KSCAMERA_EXTENDEDPROP_ISO_80 + , 142 //, 100 //KSCAMERA_EXTENDEDPROP_ISO_100 + , 283 //, 200 //KSCAMERA_EXTENDEDPROP_ISO_200 + , 566 //, 400 //KSCAMERA_EXTENDEDPROP_ISO_400 + , 1132 //, 800 //KSCAMERA_EXTENDEDPROP_ISO_800 + , 2263 //, 1600 //KSCAMERA_EXTENDEDPROP_ISO_1600 + , 4526 //, 3200 //KSCAMERA_EXTENDEDPROP_ISO_3200 + , 9051 //, 6400 //KSCAMERA_EXTENDEDPROP_ISO_6400 + , 18102 //, 12800 //KSCAMERA_EXTENDEDPROP_ISO_12800 + }; + + if( Mode & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + // Simple scan for min + for( ULONG i=0; i<_countof(PotentialIsoSetting)-1; i++ ) + { + if( Value < IsoValue[i] ) + { + return PotentialIsoSetting[i]; + } + } + return KSCAMERA_EXTENDEDPROP_ISO_25600; + } + return Mode; +} + +LPCSTR +KSStateToStateName( + _In_ KSSTATE state +) +{ + PAGED_CODE( ); + + static const CHAR *txt[] = + { + "STOP", + "ACQUIRE", + "PAUSE", + "RUN" + }; + + return ((KSSTATE_STOP <= state && KSSTATE_RUN >= state) ? txt[state] : "UNK"); +} + +// Debugging helper. +PCSTR +FocusStateDbgTxt( + _In_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE State +) +{ + PAGED_CODE(); + + switch( State ) + { + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED: + return "FOCUSSTATE_UNINITIALIZED"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_LOST: + return "FOCUSSTATE_LOST"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING: + return "FOCUSSTATE_SEARCHING"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED: + return "FOCUSSTATE_FOCUSED"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FAILED: + return "FOCUSSTATE_FAILED"; + default: + return "FOCUSSTATE_unknown"; + } +} + +BOOL +MultiplyCheckOverflow ( + ULONG a, + ULONG b, + ULONG *pab +) + +/*++ + +Routine Description: + + Perform a 32 bit unsigned multiplication and check for arithmetic overflow. + +Arguments: + + a - + First operand + + b - + Second operand + + pab - + Result + +Return Value: + + TRUE - + no overflow + + FALSE - + overflow occurred + +--*/ + +{ + PAGED_CODE(); + + *pab = a * b; + if ((a == 0) || (((*pab) / a) == b)) + { + return TRUE; + } + return FALSE; +} + +int +DbgRotation2Degrees( + AcpiPldRotation r +) +{ + switch( r ) + { + case AcpiPldRotation0 : return 0; + case AcpiPldRotation45 : return 45; + case AcpiPldRotation90 : return 90; + case AcpiPldRotation135: return 135; + case AcpiPldRotation180: return 180; + case AcpiPldRotation225: return 225; + case AcpiPldRotation270: return 270; + case AcpiPldRotation315: return 315; + } + return -1; +} + diff --git a/AVStream/avscamera/sys/Util.h b/AVStream/avscamera/sys/Util.h new file mode 100644 index 00000000..f235a42d --- /dev/null +++ b/AVStream/avscamera/sys/Util.h @@ -0,0 +1,298 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + util.h + + Abstract: + + Contains miscellaneous helper functions such as random number + generation and bounds checking functions. + + History: + + created 7/12/2013 + +**************************************************************************/ + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +ULONG +GetRandom( + _In_ ULONG Minimum, + _In_ ULONG Maximum +); + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +LONG +GetRandom( + _In_ LONG Minimum, + _In_ LONG Maximum +); + +// +// Convert an EV Compensation flag into the denominator of a fraction. +// +LONG +EVFlags2Denominator( + _In_ ULONGLONG Flags +); + +// +// Convert a WhiteBalance Extended Property Enum to temperature +// +ULONG +WhiteBalancePreset2Temperature( + _In_ ULONG WBPreset +); + +// +// Checks if a legacy Exposure setting is in range +// +BOOL +ExposureBinaryLogIsValid( + _In_ LONG ExposureBL +); + +// +// Converts a legacy Exposure setting into 100 ns +// +LONGLONG +ExposureBinaryLogTo100ns( + _In_ LONG ExposureBL +); + +// +// Converts a Exposure setting into a Binary log preset +// +LONG +Exposure100nsToBinaryLog( + _In_ LONGLONG Exposure100ns +); + +// +// Convert ISO preset flags to a preset number +// +ULONG +IsoPreset2Value( + _In_ ULONGLONG Flags +); + +// +// Convert ISO Mode & Value to legacy presets. +// +// Note: This function will simply pass legacy presets through. +// +ULONGLONG +IsoModeValue2Preset( + _In_ ULONGLONG Mode, + _In_ ULONG Value +); + +LPCSTR +KSStateToStateName( + _In_ KSSTATE state +); + +BOOL +MultiplyCheckOverflow ( + ULONG a, + ULONG b, + ULONG *pab +); + +// Note: I overload this function intentionally. Just use +// the correct type as the first parameter and the +// compiler figures out the function to use. +template<class T> +inline +NTSTATUS +BoundsCheck( + _In_ T Value, + _In_ const KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - T(Bounds.Min) ) % T(Bounds.Step)) != 0 || + Value > T(Bounds.Max) || + Value < T(Bounds.Min) ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +inline +NTSTATUS +BoundsCheckSigned( + _In_ LONGLONG Value, + _In_ const KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING &Bounds +) +{ + return BoundsCheck<LONGLONG>( Value, Bounds ); +} + +inline +NTSTATUS +BoundsCheckSigned( + _In_ LONG Value, + _In_ const KSPROPERTY_STEPPING_LONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.SignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.SignedMaximum || + Value < Bounds.Bounds.SignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +inline +NTSTATUS +BoundsCheckSigned( + _In_ LONGLONG Value, + _In_ const KSPROPERTY_STEPPING_LONGLONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.SignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.SignedMaximum || + Value < Bounds.Bounds.SignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Note: I overload this function intentionally. Just use +// the correct type as the first parameter and the +// compiler figures out the function to use. +inline +NTSTATUS +BoundsCheckUnsigned( + _In_ ULONG Value, + _In_ const KSPROPERTY_STEPPING_LONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.UnsignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.UnsignedMaximum || + Value < Bounds.Bounds.UnsignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +inline +NTSTATUS +BoundsCheckUnsigned( + _In_ ULONGLONG Value, + _In_ const KSPROPERTY_STEPPING_LONGLONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.UnsignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.UnsignedMaximum || + Value < Bounds.Bounds.UnsignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// +// This function is used internally to translate KSCAMERA_PERFRAMESETTING +// AUTO & MANUAL flags to KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG AUTO & MANUAL +// flags. There is no need to translate them back. +// +inline +ULONGLONG +TranslatePFS2VideoProcFlags( + ULONGLONG FromMode +) +{ + // The PFS settings and VideoProc flags are mixed. + // To handle that, we'll first remove any PFS flags + // and hang onto the rest. + ULONGLONG ToMode= + FromMode & ~(KSCAMERA_PERFRAMESETTING_AUTO|KSCAMERA_PERFRAMESETTING_MANUAL); + + if( FromMode & KSCAMERA_PERFRAMESETTING_AUTO ) + { + ToMode |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + } + if( FromMode & KSCAMERA_PERFRAMESETTING_MANUAL ) + { + ToMode |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + + return ToMode; +} + +// +// Debugging Helper +// +PCSTR +FocusStateDbgTxt( + _In_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE State +); + +// +// Debugging Helper - translate ACPI rotation enum to degrees. +// +int +DbgRotation2Degrees( + AcpiPldRotation r +); + + +#define SAFE_FREE(_x_) \ + if( _x_ ) \ + { \ + ExFreePool( _x_ ); \ + _x_ = nullptr; \ + } + +#define SAFE_DELETE(_x_) \ + if( _x_ ) \ + { \ + delete _x_; \ + _x_ = nullptr; \ + } + +#define SAFE_DELETE_ARRAY(_x_) \ + if( _x_ ) \ + { \ + delete [] _x_; \ + _x_ = nullptr; \ + } + +#define SAFE_CLOSE_HANDLE(_x_) \ + if( (_x_ != NULL) && (_x_ != INVALID_HANDLE_VALUE) ) \ + { \ + ZwClose( _x_ ); \ + _x_ = INVALID_HANDLE_VALUE; \ + } + diff --git a/AVStream/avscamera/sys/VideoCapture.cpp b/AVStream/avscamera/sys/VideoCapture.cpp new file mode 100644 index 00000000..92bf4b51 --- /dev/null +++ b/AVStream/avscamera/sys/VideoCapture.cpp @@ -0,0 +1,130 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + videocapture.cpp + + Abstract: + + Video Capture Pin implementation. + + Handles construction and defines pin data ranges. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "Common.h" +#include "ntintsafe.h" + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CVideoCapturePin::CVideoCapturePin(IN PKSPIN Pin) : + CCapturePin (Pin) +{ + PAGED_CODE(); + +} + + +/************************************************* + +Routine Description: + + Create a new capture pin. This is the creation dispatch for + the video capture pin. + +Arguments: + + Pin - + The pin being created + + Irp - + The creation Irp + +Return Value: + + Success / Failure + +**************************************************/ + +NTSTATUS +CVideoCapturePin:: +DispatchCreate( + IN PKSPIN Pin, + IN PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("(Pin=%d)", Pin->Id); + + NTSTATUS Status = STATUS_SUCCESS; + + CCaptureFilter* pFilter = reinterpret_cast <CCaptureFilter*>(KsPinGetParentFilter(Pin)->Context); + CVideoCapturePin *CapPin = new (NonPagedPoolNx) CVideoCapturePin (Pin); + + if( !CapPin ) + { + // Fail if we couldn't create the pin. + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + Status = + CapPin->Initialize(); + } + + if( NT_SUCCESS (Status) ) + { + // + // Adjust the stream header size. The video packets have extended + // header info (KS_FRAME_INFO). + // + pFilter->setPin(CapPin, Pin->Id); + } + else + { + // Clean up. + delete CapPin; + } + + DBG_LEAVE("(Pin=%d)=0x%08X", Pin->Id, Status); + return Status; +} + +// +// CapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +DEFINE_CAMERA_KSPIN_DISPATCH( VideoCapturePinDispatch, CVideoCapturePin ); + +// +// CapturePinAllocatorFraming: +// +// This is the simple framing structure for the capture pin. Note that this +// will be modified via KsEdit when the actual capture format is determined. +// +DECLARE_SIMPLE_FRAMING_EX( + VideoCapturePinAllocatorFraming, + STATICGUIDOF( KSMEMORY_TYPE_KERNEL_NONPAGED ), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 2, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE +); + + diff --git a/AVStream/avscamera/sys/VideoCapture.h b/AVStream/avscamera/sys/VideoCapture.h new file mode 100644 index 00000000..da08b0a8 --- /dev/null +++ b/AVStream/avscamera/sys/VideoCapture.h @@ -0,0 +1,34 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + videocapture.h + + Abstract: + + Video Capture Pin definition. + + CVideoCapturePin derived from CCapturePin. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#pragma once + +#include "capture.h" + +class CVideoCapturePin : public CCapturePin +{ +public: + static NTSTATUS DispatchCreate (IN PKSPIN Pin, IN PIRP Irp); + +private: + CVideoCapturePin(IN PKSPIN Pin); +}; diff --git a/AVStream/avscamera/sys/VideoHwSim.cpp b/AVStream/avscamera/sys/VideoHwSim.cpp new file mode 100644 index 00000000..33b6de90 --- /dev/null +++ b/AVStream/avscamera/sys/VideoHwSim.cpp @@ -0,0 +1,66 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + VideoHwSim.cpp + + Abstract: + + A fake h/w simulation for the video pin. Provides Video pin specific + metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#include "Common.h" + + +CVideoHardwareSimulation::CVideoHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : CHardwareSimulation( Sensor, PinID ) +{} + + +CVideoHardwareSimulation::~CVideoHardwareSimulation() +{} + +// +// Emit metadata here for video pin. +// +void +CVideoHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +{ + NT_ASSERT(pStreamHeader); + + // Add the normal frame info to the metadata + // Note: This call ensures the KSSTREAM_METADATA_INFO is properly initialized. + CHardwareSimulation::EmitMetadata( pStreamHeader ); + + if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA)) + { + CExtendedVidProcSetting FaceDetect; + m_Sensor->GetFaceDetection(&FaceDetect); + + if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_VIDEO ) + { + DBG_TRACE("VIDEO"); + EmitFaceMetadata( + pStreamHeader, + FaceDetect.GetULONG(), + FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK); + } + } +} + diff --git a/AVStream/avscamera/sys/VideoHwSim.h b/AVStream/avscamera/sys/VideoHwSim.h new file mode 100644 index 00000000..40b0b24b --- /dev/null +++ b/AVStream/avscamera/sys/VideoHwSim.h @@ -0,0 +1,42 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + VideoHwSim.h + + Abstract: + + A fake h/w simulation for the video pin. Provides Video pin specific + metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#pragma once +class CVideoHardwareSimulation : + public CHardwareSimulation +{ +public: + CVideoHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + virtual ~CVideoHardwareSimulation(); + + // + // Emit metadata here for video pin. + // + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); +}; + diff --git a/AVStream/avscamera/sys/Waitable.cpp b/AVStream/avscamera/sys/Waitable.cpp new file mode 100644 index 00000000..1de5827a --- /dev/null +++ b/AVStream/avscamera/sys/Waitable.cpp @@ -0,0 +1,69 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + waitable.cpp + + Abstract: + + Base interface for any object that is waitable. + In otherwords, it contains a kernel dispatch object. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#include "Common.h" + +KWaitable:: +KWaitable() +{} + + +KWaitable:: +~KWaitable() +{} + +NTSTATUS +KWaitable:: +Wait( + _In_opt_ PLARGE_INTEGER Timeout +) +/*++ + +Routine Description: + + Default Wait implementation. + +Arguments: + + Timeout - + An optional timeout value in 100ns units. Negative is relative time. + If NULL, wait forever. + +Return Value: + + Success / Failure. + +--*/ +{ + PVOID pObj = GetDispatchObject() ; + + if(pObj != NULL) + { + return + KeWaitForSingleObject( + pObj, + Executive, + KernelMode, + FALSE, + Timeout); + } + return STATUS_SUCCESS; +} diff --git a/AVStream/avscamera/sys/Waitable.h b/AVStream/avscamera/sys/Waitable.h new file mode 100644 index 00000000..1f092657 --- /dev/null +++ b/AVStream/avscamera/sys/Waitable.h @@ -0,0 +1,58 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + waitable.h + + Abstract: + + Base interface for any object that is waitable. + In otherwords, it contains a kernel dispatch object. + + Also implements a Cancelable interface class. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#pragma once + +// +// Interface definitions for KWaitable and Cancelable +// +class KWaitable : + public CNonCopyable +{ +public: + KWaitable(); + + virtual + ~KWaitable(); + + virtual + NTSTATUS + Wait( + _In_opt_ PLARGE_INTEGER Timeout = NULL + ); + + // Must implement + virtual + PVOID + GetDispatchObject() = 0; +}; + +class Cancelable +{ +public: + // Must implement + virtual + BOOLEAN + Cancel() = 0; +}; + diff --git a/AVStream/avscamera/sys/WorkItem.cpp b/AVStream/avscamera/sys/WorkItem.cpp new file mode 100644 index 00000000..9bb0d795 --- /dev/null +++ b/AVStream/avscamera/sys/WorkItem.cpp @@ -0,0 +1,167 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + workitem.cpp + + Abstract: + + This module contains an implementation for KWorkItem. + + A KWorkItem inherits from CRef and is reference counted. The object + cannot be destroyed until all references are released. This includes + the reference held when the workitem is in flight. This allows you + to store the workitem in an object that has a lifecycle shorter than + the parent device object. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#include "Common.h" + +KWorkItem:: +KWorkItem( + _In_ PDEVICE_OBJECT DeviceObj, + _In_ PIO_WORKITEM_ROUTINE Callback, + _In_ PVOID Context, + _In_ WORK_QUEUE_TYPE QueueType +) + : m_Callback( Callback ) + , m_Context ( Context ) + , m_QueueType( QueueType ) +/*++ + +Routine Description: + + Constructor. + + Sets up callback handler for the workitem and performs initialization. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + m_WorkItem = IoAllocateWorkItem( DeviceObj ); +} + + +KWorkItem:: +~KWorkItem() +/*++ + +Routine Description: + + Destructor. + + Blocks until all references are released, then frees the workitem. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + // Make sure the work item has been run down. + Wait(); + + // Now delete it. + if( m_WorkItem ) + { + IoFreeWorkItem( m_WorkItem ); + } +} + +BOOLEAN +KWorkItem:: +Start() +/*++ + +Routine Description: + + Enqueue this workitem and acquires a reference on the object. + +Arguments: + + None. + +Return Value: + + TRUE - a workitem was scheduled. + +--*/ +{ + // Make sure construction succeeded. + if( m_WorkItem ) + { + // Make sure the work item isn't already in flight and acquire the lock. + // This is to make sure this KWorkItem doesn't get destroyed until + // the callback is complete. + if( Acquire() ) + { + // Schedule the work item. + IoQueueWorkItem( m_WorkItem, &KWorkItem::Handler, m_QueueType, this ); + + // Note: Release() is called in Handler. + return TRUE; + } + } + return FALSE; +} + +VOID +KWorkItem:: +Handler( + _In_ PDEVICE_OBJECT IoObject, + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + Callback thunking function. + + Calls the callback handler registered in the constructor, if one exists. + Releases the reference on this object. + +Arguments: + + IoObject - + The parent device object. + Context - + An optional context object. + +Return Value: + + void + +--*/ +{ + // Get our "this" pointer (Me). + KWorkItem *Me = (KWorkItem *) Context; + + // Invoke the callback if one exists. + if( Me->m_Callback ) + { + Me->m_Callback( IoObject, Me->m_Context ); + } + + // Unlock the work item; allow for destruction or re-enqueing. + Me->Release(); +} diff --git a/AVStream/avscamera/sys/WorkItem.h b/AVStream/avscamera/sys/WorkItem.h new file mode 100644 index 00000000..dfce3b91 --- /dev/null +++ b/AVStream/avscamera/sys/WorkItem.h @@ -0,0 +1,63 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + workitem.h + + Abstract: + + This module contains a class wrapper definition for an IO_WORKITEM. + + A KWorkItem inherits from CRef and is reference counted. The object + cannot be destroyed until all references are released. This includes + the reference held when the workitem is in flight. This allows you + to store the workitem in an object that has a lifecycle shorter than + the parent device object. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#pragma once +class KWorkItem : + public CRef // Reference counted... +{ +private: + PIO_WORKITEM m_WorkItem; + PIO_WORKITEM_ROUTINE m_Callback; + PVOID m_Context; + WORK_QUEUE_TYPE m_QueueType; + +public: + KWorkItem( + _In_ PDEVICE_OBJECT DeviceObj, + _In_ PIO_WORKITEM_ROUTINE Callback, + _In_ PVOID Context=nullptr, + _In_ WORK_QUEUE_TYPE QueueType=NormalWorkQueue + ); + + virtual + ~KWorkItem(); + + BOOLEAN + IsValid() + { + return m_WorkItem != nullptr; + } + + // Returns FALSE if failed to start. + BOOLEAN + Start(); + +private: + static + IO_WORKITEM_ROUTINE + Handler; +}; + diff --git a/AVStream/avscamera/sys/XRGBSynthesizer.cpp b/AVStream/avscamera/sys/XRGBSynthesizer.cpp new file mode 100644 index 00000000..1aee170c --- /dev/null +++ b/AVStream/avscamera/sys/XRGBSynthesizer.cpp @@ -0,0 +1,247 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + XRGBSynthesizer.cpp + + Abstract: + + This file contains the implementation of CXRGBSynthesizer. + + CXRGBSynthesizer is derived from CSynthesizer. It uses the RGB + color space and defines a Commit() function. XRGB is known also + known as RGB32. It uses 8 bits per primary plus 8 bits of padding + for 4 bytes per pixel in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:6101 ) +_Success_(return > 0) +ULONG +CXRGBSynthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + PAGED_CODE(); + + // In case stride isn't initialized. + if( Stride==0 ) + { + Stride = m_OutputStride; + } + + if( !( Buffer && + m_Buffer && + Size >= sizeof(KS_RGBQUAD) && + m_Length >= sizeof(KS_RGBQUAD) && + (Stride%4)==0 && + (Size%4)==0 + ) ) + { + NT_ASSERT(FALSE); + return 0; + } + + ULONG OutputStride = min( Stride, (ULONG) m_OutputStride ); + ULONG OutputSize = min( Size, m_Length ); + ULONG Limit = min( m_Height, OutputSize/Stride ); + + // Is the row order in the bitmap inverted? + if( m_FlipVertical ) + { + PUCHAR Start = Buffer; + for( ULONG y=Limit; y; y-- ) + { + // Inverted row copy, with possible stride, width or height difference. + RtlCopyMemory( Buffer, GetImageLocation( 0, y-1 ), OutputStride ); + Buffer += Stride; + } + return (ULONG) (Buffer - Start); + } + else + { + PUCHAR Start = Buffer; + + if( Limit * OutputStride == OutputSize ) + { + // 1:1 copy case. (Path normally taken, so fastest.) + RtlCopyMemory( Buffer, m_Buffer, OutputSize ); + return OutputSize; + } + else + { + for( ULONG y=0; y<Limit; y++ ) + { + // Normal copy, with possible stride, width or height difference. + RtlCopyMemory( Buffer, GetImageLocation( 0, y ), OutputStride ); + Buffer += Stride; + } + return (ULONG) (Buffer - Start); + } + } +} +// suppressed due to Esp:773 +#pragma warning (pop) + +const UCHAR4 * +CXRGBSynthesizer:: +GetPalette() +/*++ + +Routine Description: + + Get a pointer to an array of palette colors. Used mostly to handle + different color primaries. Location of the primary must agree with + Commit(). + +Arguments: + + none + +Return Value: + + A pointer to an array of color primaries used for rendering. + +--*/ +{ + PAGED_CODE(); + + static + UCHAR4 Colors [MAX_COLOR] = + { + {0, 0, 0}, // BLACK + {255, 255, 255}, // WHITE + {0, 255, 255}, // YELLOW + {255, 255, 0}, // CYAN + {0, 255, 0}, // GREEN + {255, 0, 255}, // MAGENTA + {0, 0, 255}, // RED + {255, 0, 0}, // BLUE + {128, 128, 128}, // GREY + {255, 255, 255}, // TEXT_COLOR + }; + + return Colors; +} + +// Macros used to do fixed-point arithmetic in initialization. +#define TO_Q24_8bit_to_32bit(_x_) ( ( (ULONG) (_x_) ) << 24 ) +#define FROM_Q24_32bit_to_8bit(_x_) ((UCHAR) (( (ULONG) (_x_) ) >> 24 )) + +BOOLEAN +CXRGBSynthesizer:: +Initialize() +/*++ + +Routine Description: + + Class initialization. + + Used to pre-initialize a bitmap that contains a gradient bar that starts + with one of our rendering colors and fades to black. Each row will contain + the gradient for a single starting color. To paint a gradient bar, simply + replicate the row as many times as necessary. + + This front-loads all gradient calculations to initialization and caches the + result, allowing us to render a gradient bar in the time it takes to copy + the pixels even on a relatively slow machine. + +Arguments: + + none + +Return Value: + + TRUE - success. + +--*/ +{ + PAGED_CODE(); + + // First, do the base class initialization. + BOOLEAN Status = CSynthesizer::Initialize(); + + if( Status ) + { + // Now initialize the Gradient bmp... + CKsRgbQuad *Bmp = m_GradientBmp; + + for( ULONG color=BLACK; color<MAX_COLOR; color++ ) + { + ULONG HALF = TO_Q24_8bit_to_32bit(1)>>1; // round, don't truncate. + ULONG B = TO_Q24_8bit_to_32bit(m_Colors[color][0])+HALF; + ULONG G = TO_Q24_8bit_to_32bit(m_Colors[color][1])+HALF; + ULONG R = TO_Q24_8bit_to_32bit(m_Colors[color][2])+HALF; + ULONG BDelta = B-TO_Q24_8bit_to_32bit(m_Colors[BLACK][0]); + ULONG GDelta = G-TO_Q24_8bit_to_32bit(m_Colors[BLACK][1]); + ULONG RDelta = R-TO_Q24_8bit_to_32bit(m_Colors[BLACK][2]); + + BDelta /= (ULONG) m_Width; + GDelta /= (ULONG) m_Width; + RDelta /= (ULONG) m_Width; + + for(ULONG i = 0; i < m_Width; i++) + { + *Bmp++ = CKsRgbQuad( + FROM_Q24_32bit_to_8bit(R), + FROM_Q24_32bit_to_8bit(G), + FROM_Q24_32bit_to_8bit(B) + ); + B -= BDelta; + G -= GDelta; + R -= RDelta; + } + } + } + return Status; +} diff --git a/AVStream/avscamera/sys/XRGBSynthesizer.h b/AVStream/avscamera/sys/XRGBSynthesizer.h new file mode 100644 index 00000000..5e7ddd46 --- /dev/null +++ b/AVStream/avscamera/sys/XRGBSynthesizer.h @@ -0,0 +1,97 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + XRGBSynthesizer.h + + Abstract: + + This file contains the definition of CXRGBSynthesizer. + + CXRGBSynthesizer is derived from CSynthesizer. It uses the RGB + color space and defines a Commit() function. XRGB is known also + known as RGB32. It uses 8 bits per primary plus 8 bits of padding + for 4 bytes per pixel in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +/************************************************* + + CXRGBSynthesizer + + Image synthesizer for XRGB (aka RGB32) format. + +*************************************************/ + +class CXRGBSynthesizer + : public CSynthesizer +{ +protected: + BOOLEAN m_FlipVertical; + +public: + // + // DEFAULT CONSTRUCTOR: + // + CXRGBSynthesizer ( + LONG Width=0, + LONG Height=0 + ) : + CSynthesizer("RGB32", CHANNEL_RGB, Width, ABS(Height)) + , m_FlipVertical(Height==ABS(Height)) + { + m_OutputStride = Width * sizeof(KS_RGBQUAD); + } + + // + // DESTRUCTOR: + // + virtual + ~CXRGBSynthesizer () + {} + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); + + // + // Initialize() + // + // Initialize the Gradient bmp for RGB color space. + // + virtual + BOOLEAN + Initialize(); + +protected: + // + // GetPalette + // + // Get a pointer to an array of palette colors. Used mostly to handle + // different color primaries. Location of the primary must agree with + // Commit(). + virtual + const UCHAR4 * + GetPalette(); +}; + diff --git a/AVStream/avscamera/sys/YUVSynthesizer.cpp b/AVStream/avscamera/sys/YUVSynthesizer.cpp new file mode 100644 index 00000000..27b4c7e9 --- /dev/null +++ b/AVStream/avscamera/sys/YUVSynthesizer.cpp @@ -0,0 +1,150 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUVSynthesizer.cpp + + Abstract: + + This file contains the implementation of CYUVSynthesizer. + + A Base image synthesizer for all YUV formats. It provides a YUV color + palette and sets up cached gradient bars. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +const UCHAR4 * +CYUVSynthesizer:: +GetPalette() +/*++ + +Routine Description: + + Get a pointer to an array of palette colors. Used mostly to handle + different color primaries. Location of the primary must agree with + Commit(). + +Arguments: + + none + +Return Value: + + A pointer to an array of color primaries used for rendering. + +--*/ +{ + PAGED_CODE(); + + static + UCHAR4 Colors [MAX_COLOR] = + { + {128, 16, 128}, // BLACK + {128, 235, 128}, // WHITE + {16, 211, 146}, // YELLOW + {166, 170, 16}, // CYAN + {54, 145, 34}, // GREEN + {202, 106, 222}, // MAGENTA + {90, 81, 240}, // RED + {240, 41, 109}, // BLUE + {128, 125, 128}, // GREY + {128, 235, 128} // TEXT_COLOR + }; + + return Colors; +}; + +// Macros used to do fixed-point arithmetic in initialization. +#define TO_Q24_8bit_to_32bit_signed(_x_) ( ( ( LONG) (_x_) ) << 24 ) +#define FROM_Q24_32bit_to_8bit_signed(_x_) (( CHAR) (( ( LONG) (_x_) ) >> 24 )) +#define TO_Q24_8bit_to_32bit_unsigned(_x_) ( ( (ULONG) (_x_) ) << 24 ) +#define FROM_Q24_32bit_to_8bit_unsigned(_x_) ((UCHAR) (( (ULONG) (_x_) ) >> 24 )) + +BOOLEAN +CYUVSynthesizer:: +Initialize() +/*++ + +Routine Description: + + Class initialization. + + Used to pre-initialize a bitmap that contains a gradient bar that starts + with one of our rendering colors and fades to black. Each row will contain + the gradient for a single starting color. To paint a gradient bar, simply + replicate the row as many times as necessary. + + This front-loads all gradient calculations to initialization and caches the + result, allowing us to render a gradient bar in the time it takes to copy + the pixels even on a relatively slow machine. + +Arguments: + + none + +Return Value: + + TRUE - success. + +--*/ +{ + PAGED_CODE(); + + // First, do the base class initialization. + BOOLEAN Status = CSynthesizer::Initialize(); + + if( Status ) + { + // Now initialize the Gradient bmp... + CKsRgbQuad *Bmp = m_GradientBmp; + // {54, 145, 34}, // GREEN to {128, 16, 128}, // BLACK + for( ULONG color=BLACK; color<MAX_COLOR; color++ ) + { + // Must treat chroma as signed & luma as unsigned here. + ULONG HALF = TO_Q24_8bit_to_32bit_unsigned(1)>>1; // round, don't truncate. + LONG U = TO_Q24_8bit_to_32bit_signed (m_Colors[color][0])+HALF; + ULONG Y = TO_Q24_8bit_to_32bit_unsigned(m_Colors[color][1])+HALF; + LONG V = TO_Q24_8bit_to_32bit_signed (m_Colors[color][2])+HALF; + LONG UDelta = U-TO_Q24_8bit_to_32bit_signed (m_Colors[BLACK][0]); + ULONG YDelta = Y-TO_Q24_8bit_to_32bit_unsigned(m_Colors[BLACK][1]); + LONG VDelta = V-TO_Q24_8bit_to_32bit_signed (m_Colors[BLACK][2]); + + UDelta /= (LONG) m_Width; + YDelta /= (LONG) m_Width; + VDelta /= (LONG) m_Width; + + for(ULONG i = 0; i < m_Width; i++) + { + *Bmp++ = CKsRgbQuad( + FROM_Q24_32bit_to_8bit_signed (V), + FROM_Q24_32bit_to_8bit_unsigned(Y), + FROM_Q24_32bit_to_8bit_signed (U) + ); + U -= UDelta; + Y -= YDelta; + V -= VDelta; + } + } + } + return Status; +} diff --git a/AVStream/avscamera/sys/YUVSynthesizer.h b/AVStream/avscamera/sys/YUVSynthesizer.h new file mode 100644 index 00000000..819139e4 --- /dev/null +++ b/AVStream/avscamera/sys/YUVSynthesizer.h @@ -0,0 +1,69 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUVSynthesizer.h + + Abstract: + + This file contains the definition of CYUVSynthesizer. + + A Base image synthesizer for all YUV formats. It provides a YUV color + palette and sets up cached gradient bars. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#pragma once + +/************************************************* + + CYUVSynthesizer + + Image synthesizer for YUV format. + +*************************************************/ + + +class CYUVSynthesizer : + public CSynthesizer +{ +protected: + // + // GetPalette + // + // Get a pointer to an array of palette colors. Used mostly to handle + // different color primaries. Location of the primary must agree with + // Commit(). + virtual + const UCHAR4 * + GetPalette(); + + // + // Initialize() + // + // Initialize the Gradient bmp for YUV color space. + // + virtual + BOOLEAN + Initialize(); + +public: + + //Default constructor + CYUVSynthesizer ( + PCCHAR Name="[Something YUV]", + ULONG Width=0, + ULONG Height=0 + ) + : CSynthesizer(Name, CHANNEL_YCrCb, Width, Height) + {} +}; + diff --git a/AVStream/avscamera/sys/YUY2Synthesizer.cpp b/AVStream/avscamera/sys/YUY2Synthesizer.cpp new file mode 100644 index 00000000..14a76262 --- /dev/null +++ b/AVStream/avscamera/sys/YUY2Synthesizer.cpp @@ -0,0 +1,137 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUVSynthesizer.cpp + + Abstract: + + This file contains the implementation of CYUY2Synthesizer. + + CYUY2Synthesizer is derived from CYUVSynthesizer. An image + synthesizer for YUY2 format. It uses the YUV color space and defines + a Commit() function. + + YUY2 is a 4:2:2 format that uses a single plane that decimates chroma + samples on the x axis with an effective bit rate of 16 bits per pixel. + A plane is an array of rows. Rows consist of pairs of pixels in the + sequence: Y0|U|Y1|V. + + The synthesizer stores the YUV information in 32 bit format internally + for speed and accuracy. The Commit function does the format conversion. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:26019 ) +_Success_(return > 0) +ULONG +CYUY2Synthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT( Buffer ); + NT_ASSERT( m_Buffer ); + NT_ASSERT( Size >= Stride * m_Height ); + NT_ASSERT( (Stride&3)==0 ); + NT_ASSERT( (m_Width&1) == 0 ); + NT_ASSERT( (m_Height&1) == 0 ); + NT_ASSERT( m_Width>1 && m_Height>1 ); + NT_ASSERT( (Size&1)==0 ); + + // In most cases, stride isn't initialized. + // If so, just assume its the Width. + if( Stride==0 ) + { + Stride = m_OutputStride; + } + + if( !( Buffer && + m_Buffer && + Size >= Stride * m_Height && + (Stride&3)==0 && + (m_Width&1) == 0 && + (m_Height&1) == 0 && + m_Width>1 && m_Height>1 && + (Size&1)==0 + ) ) + { + // Wipe the destination before returning failure. + return 0; + } + + PKS_RGBQUAD pSrc = (PKS_RGBQUAD) m_Buffer; + ULONG RowLimit = min( m_Height, Size / Stride ); + ULONG ColLimit = min( m_Width, Stride/sizeof(WORD) ) & ~0x1; + + for(ULONG row = 0; row < RowLimit; row++) + { + PDWORD pY = (PDWORD) &Buffer[ row * Stride ]; + + for(ULONG col = 0; col < ColLimit; col+=2) + { + KS_RGBQUAD L = pSrc[col+0]; + KS_RGBQUAD R = pSrc[col+1]; + + *pY++ = + MAKELONG( MAKEWORD( L.rgbGreen, (ULONG(L.rgbBlue) + R.rgbBlue)/2), + MAKEWORD( R.rgbGreen, (ULONG(L.rgbRed ) + R.rgbRed )/2) ); + } + pSrc = (PKS_RGBQUAD) (((PUCHAR) pSrc) + m_SynthesisStride); + } + + return RowLimit * Stride; +} +// suppressed due to Esp:773 +#pragma warning (pop) diff --git a/AVStream/avscamera/sys/YUY2Synthesizer.h b/AVStream/avscamera/sys/YUY2Synthesizer.h new file mode 100644 index 00000000..a304fb43 --- /dev/null +++ b/AVStream/avscamera/sys/YUY2Synthesizer.h @@ -0,0 +1,68 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUY2Synthesizer.h + + Abstract: + + This file contains the definition of CYUY2Synthesizer. + + CYUY2Synthesizer is derived from CYUVSynthesizer. An image + synthesizer for YUY2 format. It uses the YUV color space and defines + a Commit() function. + + YUY2 is a 4:2:2 format that uses a single plane that decimates chroma + samples on the x axis with an effective bit rate of 16 bits per pixel. + A plane is an array of rows. Rows consist of pairs of pixels in the + sequence: Y0|U|Y1|V. + + The synthesizer stores the YUV information in 32 bit format internally + for speed, accuracy and simplicity. The Commit function does the + format conversion. + + History: + + created 4/14/2014 + +**************************************************************************/ + +class CYUY2Synthesizer : public CYUVSynthesizer +{ +public: + // + // DEFAULT CONSTRUCTOR + // + CYUY2Synthesizer ( + ULONG Width=0, + ULONG Height=0 + ) : + CYUVSynthesizer("YUY2", Width, Height) + { + NT_ASSERT( ( Width % 2 ) == 0 ); + m_OutputStride = Width * sizeof(WORD); + } + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); + +}; + + diff --git a/AVStream/avscamera/sys/avscamera.inf b/AVStream/avscamera/sys/avscamera.inf Binary files differnew file mode 100644 index 00000000..d855cc6f --- /dev/null +++ b/AVStream/avscamera/sys/avscamera.inf diff --git a/AVStream/avscamera/sys/filter.cpp b/AVStream/avscamera/sys/filter.cpp new file mode 100644 index 00000000..cb28775e --- /dev/null +++ b/AVStream/avscamera/sys/filter.cpp @@ -0,0 +1,4080 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + filter.cpp + + Abstract: + + This file contains the filter level implementation for the base capture + filter class. It provides a wrapper on KSFILTER. + + CCaptureFilter holds state information for the filter and contains + implementations for standard camera controls. These implementations + validate arguments and capabilities of the control according to DDI + definitions. Once validated, control is passes to a CSensor object + to manipulate the state of camera. + + To add a new control, derive from CCaptureFilter and use one of the + DECLARE_PROPERTY_XXX() macros below to declare the property handlers. + You can also use the DEFINE_PROP_ITEM_XXX() and DEFINE_STD_EVENT_ITEM() + macros as aids in constructing a filter's automation table. + + History: + + created 3/12/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CCaptureFilter::CCaptureFilter ( + _In_ PKSFILTER Filter +): +/*++ + +Routine Description: + + CCaptureFilter object construction. + +Arguments: + + Filter - + The AVStream filter being wrapped. + +Return Value: + + None + +--*/ + m_pCaptureDevice(NULL), + m_pPerFrameSettings(nullptr), + m_pinArray(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 ), + m_FocusRectNotifier( Filter, &EVENTSETID_VIDCAP_CAMERACONTROL_REGION_OF_INTEREST, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID ), + m_IsoNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO ), + m_IsoAdvancedNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED ), + m_EvCompensationNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION ), + m_WhiteBalanceNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ), + m_ExposureNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ), + m_SceneModeNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE ), + m_ThumbnailNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL ), + m_WarmStartNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART ), + m_RoiNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL ), + m_ProfileNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE ) +{ + + PAGED_CODE(); + + DBG_ENTER("(Filter=%p)", Filter); + + m_pKSFilter = Filter; + + m_pCaptureDevice = CCaptureDevice::Recast(KsFilterGetDevice(Filter)); + + KeQuerySystemTime (&m_StartTime); + + m_Profile.ProfileId = KSCAMERAPROFILE_Legacy; + m_Profile.Index = 0; + m_Profile.Reserved = 0; + + DBG_ENTER("(Filter=%p)", Filter); +} + +NTSTATUS +CCaptureFilter:: +Initialize() +/*++ + +Routine Description: + + Post construction initialization. + +Arguments: + + [None] + +Return Value: + + Success / failure + +--*/ +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + DBG_ENTER("(Filter=%S)", GetFilterName()); + + // Get Sensor object from our parent device object. + + m_Sensor = m_pCaptureDevice->GetSensor(m_pKSFilter); + NT_ASSERT(m_Sensor); + + // We get a nullptr back if the Filter doesn't match one of our's. + if( !m_Sensor ) + { + Status = STATUS_INVALID_PARAMETER; + goto done; + } + + // In reality we're just adding a reference to the sensor for this filter. + // The sensor has no back pointer to the filter since more than one filter + // can be open at a time. + IFFAILED_EXIT( m_Sensor->AddFilter(m_pKSFilter) ); + + IFNULL_EXIT(m_pinArray = new (PagedPool, 'sniP') CCapturePin *[m_Sensor->GetPinCount()]); + + for( ULONG i=0; i<m_Sensor->GetPinCount(); i++ ) + { + m_pinArray[i] = nullptr; + } + +done: + DBG_LEAVE("(Filter=%S)=0x%08X", GetFilterName(), Status); + return Status; +} + +void +CCaptureFilter::Cleanup() +/*++ + +Routine Description: + + Clean up. Cancels any outstanding operations on the sensor. + +Arguments: + + [None] + +Return Value: + + [None] + +--*/ +{ + PAGED_CODE(); + + // Cancel all Async operations. Ignore any failures. + m_Sensor->CancelPhotoMode(); + m_Sensor->CancelPhotoMaxFrameRate(); + m_Sensor->CancelFocus(); + m_Sensor->CancelFocusRect(); + m_Sensor->CancelIso(); + m_Sensor->CancelIsoAdvanced(); + m_Sensor->CancelEvCompensation(); + m_Sensor->CancelWhiteBalance(); + m_Sensor->CancelExposure(); + m_Sensor->CancelSceneMode(); + m_Sensor->CancelThumbnail(); + m_Sensor->CancelWarmStart(); + m_Sensor->CancelRoi(); +} + +// +// ~CCaptureFilter(): +// +// The capture filter destructor. +// +CCaptureFilter::~CCaptureFilter() +{ + PAGED_CODE(); + + LPCWSTR Name = GetFilterName(); + DBG_ENTER("(%S)", Name); + + delete [] ((PBYTE) m_pPerFrameSettings); + + // In reality we're just removing a reference to the sensor for this filter. + m_Sensor->RemoveFilter(m_pKSFilter); + + // The sensor may need to do some cleanup. In particular, the image pin + // may need to have some things reset. + m_Sensor->Reset(); + + delete [] m_pinArray; + + DBG_LEAVE("(%S)", Name); +} + + +NTSTATUS +CCaptureFilter:: +DispatchCreate ( + _In_ PKSFILTER Filter, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static function for creation dispatch for the capture filter. It creates + the CCaptureFilter object. + +Arguments: + + Filter - + The AVStream filter being created + + Irp - + The creation Irp + +Return Value: + + Success / failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("(Filter=0x%p)", Filter); + + NTSTATUS Status = STATUS_SUCCESS; + + CCaptureFilter *CapFilter = new (NonPagedPoolNx, 'rtlf') CCaptureFilter (Filter); + + if (!CapFilter) + { + // + // Return failure if we couldn't create the filter. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + else + { + // Allocate memory or whatever. + Status = CapFilter->Initialize(); + + if (!NT_SUCCESS (Status)) + { + delete CapFilter; + } + else + { + Filter -> Context = reinterpret_cast <PVOID> (CapFilter); + } + + } + + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +NTSTATUS +CCaptureFilter::DispatchClose( + _In_ PKSFILTER Filter, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static function for the close dispatch for the capture filter. It handles + filter cleanup. + +Arguments: + + Filter - + The AVStream filter being closed + + Irp - + The creation Irp + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + + CCaptureFilter *pFilter = reinterpret_cast <CCaptureFilter *>(Filter->Context); + + if( pFilter ) + { + pFilter->Cleanup(); + delete pFilter; + } + return STATUS_SUCCESS; +} + +void +CCaptureFilter:: +setPin( + _In_ CCapturePin *pPin, + _In_ unsigned Id +) +/*++ + +Routine Description: + + Attach a CCapturePin to the filter. + +Arguments: + + pPin - + The pin. + Id - + The pin index. + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE( ); + + if( Id >= m_Sensor->GetPinCount() ) + { + __debugbreak(); + return; + } + + // Take out a weak reference to the pin object. + m_pinArray[Id] = pPin; +} + +CCapturePin * +CCaptureFilter:: +getPin( + _In_ unsigned Id +) +/*++ + +Routine Description: + + Get a pointer to an attached CCapturePin. + +Arguments: + + Id - + The pin index. + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE( ); + + if( Id >= m_Sensor->GetPinCount() ) + { + __debugbreak(); + return nullptr; + } + + // Convert back to a strong reference before returning. + return m_pinArray[Id] ; +} + +NTSTATUS +CCaptureFilter:: +UpdateAllocatorFraming( + _In_ ULONG PinId +) +/*++ + +Routine Description: + + Update the Pin's Allocator Framing. + +Arguments: + + PinId - + A pin's index + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Acquire the lock and update the Pin's allocator framing. + LockFilter Lock(m_pKSFilter); + + if( m_pinArray[PinId] ) + { + Status = m_pinArray[PinId]->UpdateAllocatorFraming(); + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +/************************************************************************** + + Control validation and forwarding. + +**************************************************************************/ + +// Get for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +GetFlash( + _Inout_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + + return + m_Sensor->GetFlash( pFlash ); +} + +// Set for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +SetFlash( + _In_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + + KSPROPERTY_CAMERACONTROL_FLASH_S Caps; + + RtlZeroMemory( &Caps, sizeof(Caps) ); + NTSTATUS Status = m_Sensor->GetFlash(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + // Validate the parameters against our caps. + switch( pFlash->Flash ) + { + case KSPROPERTY_CAMERACONTROL_FLASH_OFF: + case KSPROPERTY_CAMERACONTROL_FLASH_ON: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_MANUAL ) + { + Status = STATUS_SUCCESS; + } + break; + + case KSPROPERTY_CAMERACONTROL_FLASH_AUTO: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_AUTO ) + { + Status = STATUS_SUCCESS; + } + break; + } + } + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetFlash( pFlash ); + } + + return Status; +} + +// Get for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CCaptureFilter:: +GetVideoStabMode( + _Inout_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pVideoStab +) +{ + PAGED_CODE(); + + return + m_Sensor->GetVideoStabMode( pVideoStab ); +} + +// Set for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CCaptureFilter:: +SetVideoStabMode( + _In_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pVideoStab +) +{ + PAGED_CODE(); + + KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S Caps; + + RtlZeroMemory( &Caps, sizeof(Caps) ); + NTSTATUS Status = m_Sensor->GetVideoStabMode( &Caps ); + + DBG_ENTER("(Mode=0x%08X, Caps=0x%08X)", pVideoStab->VideoStabilizationMode, Caps.Capabilities ); + + if( NT_SUCCESS(Status) ) + { + Status = STATUS_INVALID_PARAMETER; + switch( pVideoStab->VideoStabilizationMode ) + { + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_OFF: + Status = STATUS_SUCCESS; + break; + + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_AUTO: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_AUTO ) + { + Status = STATUS_SUCCESS; + } + break; + + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_LOW: + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_MEDIUM: + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_HIGH: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_MANUAL ) + { + Status = STATUS_SUCCESS; + } + break; + } + } + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetVideoStabMode( pVideoStab ); + } + DBG_LEAVE("(Mode=0x%08X, Caps=0x%08X)=0x%08X", pVideoStab->VideoStabilizationMode, Caps.Capabilities, Status ); + return Status; +} + +// Get for KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +GetPinDependence( + _Inout_ KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S *pPinDependence +) +{ + PAGED_CODE(); + + return + m_Sensor->GetPinDependence( pPinDependence ); +} + +// +// A macro used for consistent asynchronous SET behavior. +// +#define INVOKE_SET_ASYNC( SENSOR, PROPERTY, OUTPUT, NOTIFIER ) \ + ( OUTPUT->Flags & KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION ) ? SENSOR->Cancel ## PROPERTY () : \ + SENSOR->Set ## PROPERTY ## Async( OUTPUT, &NOTIFIER ); + +#define INVOKE_SET_ASYNC_NOT_CANCELLABLE( SENSOR, PROPERTY, OUTPUT, NOTIFIER ) \ + ( OUTPUT->Flags & KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION ) ? STATUS_INVALID_PARAMETER : \ + SENSOR->Set ## PROPERTY ## Async( OUTPUT, &NOTIFIER ); + +// Get for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CCaptureFilter::GetFocusRect( + _Inout_ KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S *pRoi +) +{ + PAGED_CODE(); + return m_Sensor->GetFocusRect( pRoi ); +} + +// Set for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +SetFocusRect( + _In_ KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S *pRoi +) +{ + PAGED_CODE(); + + return m_Sensor->SetFocusRectAsync( pRoi, &m_FocusRectNotifier ); +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CCaptureFilter::GetPhotoMode( + _Inout_ CExtendedPhotoMode *pMode +) +{ + PAGED_CODE(); + + DBG_ENTER( "()" ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pMode->isValid() && + m_Sensor->IsStillIndex( pMode->PinId ) ) + { + Status = m_Sensor->GetPhotoMode( pMode ); + } + + DBG_TRACE( "Capability=0x%016llX", pMode->Capability ); + DBG_TRACE( "PinId=%d", pMode->PinId ); + DBG_TRACE( "MaxHistoryFrames=%d", pMode->MaxHistoryFrames() ); + + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CCaptureFilter:: +SetPhotoMode( + _In_ CExtendedPhotoMode *pMode +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER( "()" ); + + if( pMode->isValid() && + m_Sensor->IsStillIndex( pMode->PinId ) && + (pMode->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedPhotoMode Caps(*pMode); + Status = m_Sensor->GetPhotoMode( &Caps ); + CCapturePin *pPin = getPin(pMode->PinId); + + // Should always be valid. + NT_ASSERT(pPin); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter again. + Status = STATUS_INVALID_PARAMETER; + + if( (pMode->Flags & Caps.Capability) == pMode->Flags ) + { + // We don't support cancel here. + switch( pMode->Flags ) + { + case KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE: + { + // We cache the Per-Frame Settings in the filter as a simplification for the getter. + // But we can also use it here to tell us basic info about the current PFS. + PKSCAMERA_PERFRAMESETTING_HEADER pPFS = m_pPerFrameSettings; + if( pMode->SubMode() == KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_VARIABLE && + !pPFS ) + { + DBG_TRACE( "VPS requested without Per Frame Settings." ); + break; + } + + // Reject any request for too many history frames. + if( pMode->RequestedHistoryFrames() > Caps.MaxHistoryFrames() ) + { + DBG_TRACE( "Too many history frames requested." ); + break; + } + + DBG_TRACE( "Flags=0x%08X, RequestedHistoryFrames=%d, SubMode=0x%08X", + (ULONG) pMode->Flags, + pMode->RequestedHistoryFrames(), + pMode->SubMode() ); + + // 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; + + // Modify TotalFrames for the VPS case. + // For VPS, we need LoopCount * FrameCount; but limit by the minimum(3) and maximum (20) frames. + if( KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_VARIABLE==pMode->SubMode() ) + { + // Assume at least one loop, if the loop is infinite. + ULONG LoopCount = max( pPFS->LoopCount, 1 ); + ULONG FrameCount= pPFS->FrameCount; + + // Add in the number of future frames expected. + TotalFrames+= (LoopCount * FrameCount) -1; + + DBG_TRACE( "TotalFrames=%d, LoopCount=%d, FrameCount=%d", TotalFrames, LoopCount, FrameCount ); + } + + // Bound the frame count to minimum and maximums. + TotalFrames = + min( max( TotalFrames, IMAGE_CAPTURE_PIN_MINIMUM_FRAMES ), IMAGE_CAPTURE_PIN_MAXIMUM_FRAMES ); + 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; + } + } + + // Update allocator framing. + if( NT_SUCCESS( Status ) ) + { + Status = UpdateAllocatorFraming( pMode->PinId ); + } + + // Update the sensor object... + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetPhotoModeAsync( pMode, &m_PhotoModeNotifier ); + } + } + } + } + + DBG_TRACE( "Capability=0x%016llX", pMode->Capability ); + DBG_TRACE( "Flags=0x%016llX", pMode->Flags ); + DBG_TRACE( "PinId=%d", pMode->PinId ); + DBG_TRACE( "MaxHistoryFrames=%d", pMode->MaxHistoryFrames() ); + DBG_TRACE( "RequestedHistoryFrames=%d", pMode->RequestedHistoryFrames() ); + DBG_TRACE( "SubMode=0x%08X", pMode->SubMode() ); + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE Get Handler + */ +NTSTATUS +CCaptureFilter::GetPhotoFrameRate( + _Inout_ CExtendedProperty *pFrameRate +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFrameRate->isValid() && + m_Sensor->IsStillIndex(pFrameRate->PinId) ) + { + ULONGLONG TimePerFrame = m_Sensor->GetPhotoFrameRate(pFrameRate->PinId); + if( TimePerFrame > ULONG_MAX ) + { + // Handle very large (slow) frame rates in seconds. + pFrameRate->m_Value.Value.ratio.HighPart = 1; + pFrameRate->m_Value.Value.ratio.LowPart = (ULONG) (TimePerFrame / ONESECOND); + } + else + { + // Handle smaller frame rates as 100ns units. + pFrameRate->m_Value.Value.ratio.HighPart = ONESECOND; + pFrameRate->m_Value.Value.ratio.LowPart = (ULONG) TimePerFrame; + } + Status = STATUS_SUCCESS; + } + + DBG_LEAVE("(PinId=%d, estimated FrameRate=%d/%d)=0x%08X", + pFrameRate->PinId, + pFrameRate->m_Value.Value.ratio.HighPart, + pFrameRate->m_Value.Value.ratio.LowPart, + Status ); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Get Handler + */ +NTSTATUS +CCaptureFilter:: +GetMaxVideoFpsForPhotoRes( + _Inout_ CExtendedMaxVideoFpsForPhotoRes *pFps +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFps->isValid() && + pFps->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFps->Capability == 0 && + pFps->Flags == 0 ) + { + Status = m_Sensor->GetMaxVideoFpsForPhotoRes( pFps ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Set Handler + */ +NTSTATUS +CCaptureFilter::SetMaxVideoFpsForPhotoRes( + _In_ CExtendedMaxVideoFpsForPhotoRes *pFps +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFps->isValid() && + pFps->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFps->Capability == 0 && + pFps->Flags == 0 ) + { + Status = m_Sensor->SetMaxVideoFpsForPhotoRes( pFps ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Get Handler + */ +NTSTATUS +CCaptureFilter::GetPhotoMaxFrameRate( + _Inout_ CExtendedProperty *pFrameRate +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFrameRate->isValid() && + m_Sensor->IsStillIndex(pFrameRate->PinId) ) + { + Status = m_Sensor->GetPhotoMaxFrameRate( pFrameRate ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Set Handler + */ +NTSTATUS +CCaptureFilter:: +SetPhotoMaxFrameRate( + _In_ CExtendedProperty *pFrameRate +) +{ + PAGED_CODE(); + + DBG_ENTER("(MaxFrameRate = %d/%d)", + pFrameRate->m_Value.Value.ratio.HighPart, + pFrameRate->m_Value.Value.ratio.LowPart ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFrameRate->isValid() && + m_Sensor->IsStillIndex( pFrameRate->PinId ) && + (pFrameRate->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + // Don't allow either the numerator or denominator alone to be 0. + // We don't want a rate of 0 fps or undefined. + if( pFrameRate->m_Value.Value.ll == 0 || + ( pFrameRate->m_Value.Value.ratio.LowPart != 0 && + pFrameRate->m_Value.Value.ratio.HighPart != 0 ) ) + { + // Take what we're given here; SetPhotoFrameRate handles default setting. + DBG_TRACE("[Override] MaxFrameRate = %d/%d", + pFrameRate->m_Value.Value.ratio.HighPart, + pFrameRate->m_Value.Value.ratio.LowPart ); + + Status = m_Sensor->SetPhotoMaxFrameRateAsync( pFrameRate, &m_PhotoMaxFrameRateNotifier ); + } + } + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CCaptureFilter:: +GetTriggerTime( + _Inout_ CExtendedProperty *pTriggerTime +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pTriggerTime->isValid() && + m_Sensor->IsStillIndex(pTriggerTime->PinId) ) + { + Status = m_Sensor->GetTriggerTime( pTriggerTime ); + } + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CCaptureFilter:: +SetTriggerTime( + _In_ CExtendedProperty *pTriggerTime +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pTriggerTime->isValid() && + pTriggerTime->Capability == 0 && + m_Sensor->IsStillIndex(pTriggerTime->PinId) ) + { + Status = m_Sensor->SetTriggerTime( pTriggerTime ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART + */ +NTSTATUS +CCaptureFilter:: +GetWarmStart( + _Inout_ CExtendedProperty *pWarmStart +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pWarmStart->isValid() && + m_Sensor->IsValidIndex( pWarmStart->PinId ) ) + { + Status = m_Sensor->GetWarmStart( pWarmStart ); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pWarmStart->PinId, pWarmStart->Flags, pWarmStart->Capability, Status ); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART Set Handler + */ +NTSTATUS +CCaptureFilter::SetWarmStart( + _In_ CExtendedProperty *pWarmStart +) +{ + PAGED_CODE(); + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)", + pWarmStart->PinId, pWarmStart->Flags, pWarmStart->Capability ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pWarmStart->isValid() && + m_Sensor->IsValidIndex( pWarmStart->PinId ) && + (pWarmStart->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedProperty Caps(*pWarmStart); + Status = m_Sensor->GetWarmStart( &Caps ); + + if( NT_SUCCESS(Status ) ) + { + if( pWarmStart->Flags == (pWarmStart->Flags & Caps.Capability) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, WarmStart, pWarmStart, m_WarmStartNotifier ); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + + DBG_LEAVE("()=0x%08X", Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL +NTSTATUS +CCaptureFilter:: +SetThumbnail( + _In_ CExtendedProperty *pThumbnail +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Validate + if( (pThumbnail->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pThumbnail->isValid() && + m_Sensor->IsStillIndex(pThumbnail->PinId) ) + { + CExtendedProperty Caps; + Status = m_Sensor->GetThumbnail( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( pThumbnail->Flags == (Caps.Capability & pThumbnail->Flags) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Thumbnail, pThumbnail, m_ThumbnailNotifier ); + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL +NTSTATUS +CCaptureFilter:: +GetThumbnail( + _Inout_ CExtendedProperty *pThumbnail +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Validate + if( pThumbnail->isValid() && + m_Sensor->IsStillIndex(pThumbnail->PinId) ) + { + Status = m_Sensor->GetThumbnail( pThumbnail ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CCaptureFilter:: +GetSceneMode( + _Inout_ CExtendedProperty *pSceneMode +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pSceneMode->isValid() && + pSceneMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetSceneMode( pSceneMode ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CCaptureFilter:: +SetSceneMode( + _In_ CExtendedProperty *pSceneMode +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + if( (pSceneMode->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pSceneMode->isValid() && + pSceneMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedProperty Caps; + Status = m_Sensor->GetSceneMode( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( pSceneMode->Flags == (Caps.Capability & pSceneMode->Flags) ) + { + Status = INVOKE_SET_ASYNC_NOT_CANCELLABLE( m_Sensor, SceneMode, pSceneMode, m_SceneModeNotifier ); + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CCaptureFilter:: +GetTorchMode( + _Inout_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pTorchMode->isValid() && + pTorchMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetTorchMode( pTorchMode ); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pTorchMode->PinId, pTorchMode->Flags, pTorchMode->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CCaptureFilter:: +SetTorchMode( + _In_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + + 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 ) + { + CExtendedProperty Caps(*pTorchMode); + Status = m_Sensor->GetTorchMode(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pTorchMode->Flags == (pTorchMode->Flags & Caps.Capability) ) + { + switch( pTorchMode->Flags ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON: + case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON_ADJUSTABLEPOWER: + 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 ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CCaptureFilter:: +GetExtendedFlash( + _Inout_ CExtendedProperty *pFlash +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFlash->isValid() && + pFlash->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE) + { + Status = m_Sensor->GetExtendedFlash( pFlash ); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pFlash->PinId, pFlash->Flags, pFlash->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CCaptureFilter:: +SetExtendedFlash( + _In_ CExtendedProperty *pFlash +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( !(pFlash->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pFlash->isValid() && + pFlash->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE) + { + CExtendedProperty Caps; + Status = m_Sensor->GetExtendedFlash( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pFlash->Flags == (pFlash->Flags & Caps.Capability) ) + { + // Validate flash settings. + // First validate the basic flash controls + switch( pFlash->Flags & KSCAMERA_EXTENDEDPROP_FLASH_MODE_MASK ) + { + // Valid combinations + case KSCAMERA_EXTENDEDPROP_FLASH_OFF: + case KSCAMERA_EXTENDEDPROP_FLASH_ON: + case KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER: + case KSCAMERA_EXTENDEDPROP_FLASH_AUTO: + case KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER: + Status = STATUS_SUCCESS; + break; + } + + // Validate red eye reduction, single flash & multiflash supported. + if( !(pFlash->Flags & + ( KSCAMERA_EXTENDEDPROP_FLASH_REDEYEREDUCTION | + KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH | + KSCAMERA_EXTENDEDPROP_FLASH_MULTIFLASHSUPPORTED )) || + (pFlash->Flags & KSCAMERA_EXTENDEDPROP_FLASH_MODE_MASK) != KSCAMERA_EXTENDEDPROP_FLASH_OFF ) + { + Status = STATUS_SUCCESS; + } + + // Validate Flash Assistant parameters + switch( pFlash->Flags & KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_MASK ) + { + // Valid combinations + case KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_OFF: + case KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_ON: + case KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_AUTO: + Status = STATUS_SUCCESS; + break; + } + } + } + } + + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetExtendedFlash( pFlash ); + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pFlash->PinId, pFlash->Flags, pFlash->Capability, Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CCaptureFilter:: +GetOptimizationHint( + _Inout_ CExtendedProperty *pHint +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pHint->isValid() && + !(pHint->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pHint->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetOptimizationHint(pHint); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pHint->PinId, pHint->Flags, pHint->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CCaptureFilter:: +SetOptimizationHint( + _In_ CExtendedProperty *pHint +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pHint->isValid() && + !(pHint->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pHint->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedProperty Caps(*pHint); + Status = m_Sensor->GetOptimizationHint(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pHint->Flags == (pHint->Flags & Caps.Capability) ) + { + // Inherit the previous primary use flag settings, if they were not set here. + if( KSCAMERA_EXTENDEDPROP_OPTIMIZATION_DEFAULT == pHint->Flags || + 0 == (pHint->Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PRIMARYUSE_MASK) ) + { + pHint->Flags |= (Caps.Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PRIMARYUSE_MASK); + } + else + { + // Inherit the previous perf flag settings, if they were not set here and if DEFAULT is not set. + if( 0 == (pHint->Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PERF_MASK) ) + { + pHint->Flags |= (Caps.Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PERF_MASK); + } + } + + // Make sure the requested flags are a valid capability combination. + switch( pHint->Flags ) + { + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + Status = m_Sensor->SetOptimizationHint(pHint); + break; + } + } + } + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pHint->PinId, pHint->Flags, pHint->Capability, Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW +NTSTATUS +CCaptureFilter:: +GetFieldOfView( + _Inout_ CExtendedFieldOfView *pFieldOfView +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFieldOfView->isValid() && + pFieldOfView->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFieldOfView(pFieldOfView); + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_CAMERAANGLEOFFSET +NTSTATUS +CCaptureFilter:: +GetCameraAngleOffset( + _Inout_ CExtendedCameraAngleOffset *pAngleOffset +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pAngleOffset->isValid() && + pAngleOffset->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetCameraAngleOffset(pAngleOffset); + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CCaptureFilter:: +GetMetadata( + _Inout_ CExtendedMetadata *pMetadata +) +{ + PAGED_CODE(); + + if( pMetadata->PinId >= m_Sensor->GetPinCount() ) + { + return STATUS_INVALID_PARAMETER; + } + + return m_Sensor->GetMetadata( pMetadata ); +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CCaptureFilter:: +SetMetadata( + _In_ CExtendedMetadata *pMetadata +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( !(pMetadata->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pMetadata->isValid() && + m_Sensor->IsValidIndex( pMetadata->PinId ) ) + { + if( m_pinArray[pMetadata->PinId] && + m_pinArray[pMetadata->PinId]->GetState() == PinRunning ) + { + Status = STATUS_INVALID_DEVICE_REQUEST; + } + else + { + Status = m_Sensor->SetMetadata( pMetadata ); + } + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CCaptureFilter:: +GetIso( + _Inout_ CExtendedProperty *pIso +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pIso->isValid() && + m_Sensor->IsStillIndex( pIso->PinId ) ) + { + Status = m_Sensor->GetIso(pIso); + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pIso->PinId, pIso->Flags, pIso->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CCaptureFilter:: +SetIso( + _In_ CExtendedProperty *pIso +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("(Flags=0x%016llX)", pIso->Flags); + + if( (pIso->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pIso->isValid() && + m_Sensor->IsStillIndex( pIso->PinId ) ) + { + CExtendedProperty Caps; + Status = m_Sensor->GetIso( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pIso->Flags == (pIso->Flags & Caps.Capability) ) + { + // Make sure only one preset was set. + ULONGLONG presets = pIso->Flags & + ( KSCAMERA_EXTENDEDPROP_ISO_AUTO | + KSCAMERA_EXTENDEDPROP_ISO_50 | KSCAMERA_EXTENDEDPROP_ISO_80 | + KSCAMERA_EXTENDEDPROP_ISO_100 | KSCAMERA_EXTENDEDPROP_ISO_200 | + KSCAMERA_EXTENDEDPROP_ISO_400 | KSCAMERA_EXTENDEDPROP_ISO_800 | + KSCAMERA_EXTENDEDPROP_ISO_1600 | KSCAMERA_EXTENDEDPROP_ISO_3200 | + KSCAMERA_EXTENDEDPROP_ISO_6400 | KSCAMERA_EXTENDEDPROP_ISO_12800 | + KSCAMERA_EXTENDEDPROP_ISO_25600 ) ; + + DBG_TRACE("presets=0x%016llX", presets); + + // Check to see if there is exactly one set. + if( presets && ( (presets-1) & presets ) == 0 ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Iso, pIso, m_IsoNotifier ); + } + } + } + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pIso->PinId, pIso->Flags, pIso->Capability, Status ); + + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CCaptureFilter:: +GetEvCompensation( + _Inout_ CExtendedEvCompensation *pEvComp +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pEvComp->isValid() && + pEvComp->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetEvCompensation( pEvComp ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CCaptureFilter:: +SetEvCompensation( + _In_ CExtendedEvCompensation *pEvComp +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + if( (pEvComp->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pEvComp->isValid() && + pEvComp->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedEvCompensation Caps; + Status = m_Sensor->GetEvCompensation( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( (pEvComp->Flags & Caps.Capability ) == pEvComp->Flags ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, EvCompensation, pEvComp, m_EvCompensationNotifier ); + } + } + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pEvComp->PinId, pEvComp->Flags, pEvComp->Capability, Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CCaptureFilter:: +GetWhiteBalance( + _Inout_ CExtendedVidProcSetting *pWhiteBalance +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pWhiteBalance->isValid() && + pWhiteBalance->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetWhiteBalance( pWhiteBalance ); + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX, Mode=0x%08X, Val=0x%016llX)=0x%08X", + pWhiteBalance->PinId, pWhiteBalance->Flags, pWhiteBalance->Capability, + pWhiteBalance->Mode(), pWhiteBalance->GetLONGLONG(), Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CCaptureFilter:: +SetWhiteBalance( + _In_ CExtendedVidProcSetting *pWhiteBalance +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + if( (pWhiteBalance->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pWhiteBalance->isValid() && + pWhiteBalance->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedVidProcSetting Caps; + Status = m_Sensor->GetWhiteBalance( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( (pWhiteBalance->Flags & Caps.Capability ) == pWhiteBalance->Flags ) + { + switch( pWhiteBalance->Flags ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + break; + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + pWhiteBalance->Flags = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + break; + + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL: + switch( pWhiteBalance->Mode() ) + { + case KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE: + Status = Caps.BoundsCheck( pWhiteBalance->GetLONGLONG() ); + break; + + case KSCAMERA_EXTENDEDPROP_WHITEBALANCE_PRESET: + switch( pWhiteBalance->GetLONGLONG() ) + { + case KSCAMERA_EXTENDEDPROP_WBPRESET_CLOUDY: + case KSCAMERA_EXTENDEDPROP_WBPRESET_DAYLIGHT: + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLASH: + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLUORESCENT: + case KSCAMERA_EXTENDEDPROP_WBPRESET_TUNGSTEN: + case KSCAMERA_EXTENDEDPROP_WBPRESET_CANDLELIGHT: + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, WhiteBalance, pWhiteBalance, m_WhiteBalanceNotifier ); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX, Mode=0x%08X, Val=0x%016llX)=0x%08X", + pWhiteBalance->PinId, pWhiteBalance->Flags, pWhiteBalance->Capability, + pWhiteBalance->Mode(), pWhiteBalance->GetLONGLONG(), Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE +NTSTATUS +CCaptureFilter:: +GetExposure( + _Inout_ CExtendedVidProcSetting *pExposure +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pExposure->isValid() && + pExposure->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetExposure( pExposure ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE +NTSTATUS +CCaptureFilter:: +SetExposure( + _In_ CExtendedVidProcSetting *pExposure +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + if( (pExposure->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pExposure->isValid() && + pExposure->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedVidProcSetting Caps; + Status = m_Sensor->GetExposure( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( (pExposure->Flags & Caps.Capability ) == pExposure->Flags ) + { + if( pExposure->Flags == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Status = Caps.BoundsCheck( pExposure->GetULONGLONG() ); + } + + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Exposure, pExposure, m_ExposureNotifier ); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CCaptureFilter:: +GetFocus( + _Inout_ CExtendedVidProcSetting *pFocus +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFocus->isValid() && + pFocus->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFocus( pFocus ); + } + + DBG_LEAVE("(FocusMode=0x%016llX)=0x%08X", pFocus->Flags, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CCaptureFilter:: +SetFocus( + _In_ CExtendedVidProcSetting *pFocus +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + ULONGLONG Flags = pFocus->Flags; + + DBG_ENTER("(Preset Flags=0x%016llX)", Flags); + + CExtendedVidProcSetting Caps(*pFocus); + + // This part is just setting validation. + if( (pFocus->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pFocus->isValid() && + pFocus->Mode() == 0 && + pFocus->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFocus( &Caps ); + } + + if( NT_SUCCESS(Status) ) + { + // Make sure we have a valid combinations of Flags + switch( Flags & + (KSCAMERA_EXTENDEDPROP_FOCUS_MODE_MASK | + KSCAMERA_EXTENDEDPROP_FOCUS_MODE_ADVANCED_MASK) ) + { + // Auto-focus combinations: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + break; + + // Continuous-focus combinations: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + break; + + // Manual stands alone. + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL: + Status = BoundsCheckSigned( + pFocus->GetLONG(), + Caps.m_Setting ); + + if( !NT_SUCCESS(Status) ) + { + DBG_TRACE( "ERROR: Focus request out of bounds. Value=%d", pFocus->GetLONG() ); + } + break; + + // Unlock must stand alone. + // Also note that by definition unlock is accepted regardless of our current state. + case KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK: + break; + + // Lock can stand alone. + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + // Make sure we are actually being asked during an auto or continuous. + if( !(Caps.Flags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS)) ) + { + // This is an ambiguous case under the WinBlue DDI. However, + // producing an error code here will cause a test failure. + // Instead we will silently consume the error and just skip this + // request. + // + // Actually the WinBlue WinRT layer interprets a "Manual Preset" as + // a LOCK-only request when there is no value involved. That is kind + // rediculous since the value is sent without considering the previous + // mode. Instead, it thinks that just setting a LOCK-only mode is + // synonymous with "Manual at the current position." + // + // Therefore, our interpretation will be, If we're in manual, we can + // just silently consume the entire request and do nothing. + if( Caps.Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Status = STATUS_INVALID_LOCK_SEQUENCE; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + break; + + // Anything else is just wrong. + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + } + + // Validate focus ranges. + if( NT_SUCCESS(Status) ) + { + // If AUTO or CONTINUOUS is requested, we may support RANGE flags. + if( (Flags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS)) ) + { + switch( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE: + // Depreciated flags are not supported: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_INFINITY: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_HYPERFOCAL: + case 0: + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + } + // Otherwise no RANGE flags are permitted. + else if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MASK ) + { + Status = STATUS_INVALID_PARAMETER; + } + } + + // Validate focus distances. + if( NT_SUCCESS(Status) ) + { + // If MANUAL is requested, we may support DISTANCE flags. + if( Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + switch( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_INFINITY: + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_HYPERFOCAL: + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_NEAREST: + case 0: + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + } + // Otherwise no DISTANCE flags are permitted. + else if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_MASK ) + { + Status = STATUS_INVALID_PARAMETER; + } + } + + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Focus, pFocus, m_FocusNotifier ); + } + // This is the failure case. In this case, we don't change state; but + // if we were ASYNC, we need to signal the client that we completed. + else + { + // Don't need to set the event if this was [erroneously] an sync request. + if( pFocus->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL ) + { + m_FocusNotifier.Set(); + } + + // Silently consume Lock after Manual requests: + if( STATUS_INVALID_LOCK_SEQUENCE == Status ) + { + Status = STATUS_SUCCESS; + } + } + DBG_LEAVE("(Preset Flags=0x%016llX)=0x%08X", Flags, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CCaptureFilter:: +GetPhotoConfirmation( + _Inout_ CExtendedProperty *pConfirmation +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pConfirmation->isValid() && + pConfirmation->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetPhotoConfirmation( pConfirmation ); + } + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CCaptureFilter:: +SetPhotoConfirmation( + _In_ CExtendedProperty *pConfirmation +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( !(pConfirmation->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pConfirmation->isValid() && + pConfirmation->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + switch( pConfirmation->Flags ) + { + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON: + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_OFF: + Status = m_Sensor->SetPhotoConfirmation( pConfirmation ); + break; + } + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CAPABILITY +NTSTATUS +CCaptureFilter::GetPFSCaps( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast <CCaptureFilter *>(KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG RequiredLength = 0; + + NTSTATUS Status = + pFilter->m_Sensor->GetPfsCaps( nullptr, &RequiredLength ); + + // Note: We may want to determine capabilities at runtime in the future. + + // We only handle GETs + if( NT_SUCCESS(Status) ) + { + if (ulOutputBufferLength == 0) + { + pIrp->IoStatus.Information = RequiredLength; + Status = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < RequiredLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else if (pData && ulOutputBufferLength >= RequiredLength) + { + Status = + pFilter->m_Sensor->GetPfsCaps( + (KSCAMERA_PERFRAMESETTING_CAP_HEADER *) pData, + &RequiredLength ); + pIrp->IoStatus.Information = RequiredLength; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_SET +NTSTATUS +CCaptureFilter::GetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast <CCaptureFilter *>(KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG ulOutputSize = + pFilter->m_pPerFrameSettings ? pFilter->m_pPerFrameSettings->Size : 0; + + // Only handle GETs + if (ulOutputBufferLength == 0) + { + pIrp->IoStatus.Information = ulOutputSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < ulOutputSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else if (pData && ulOutputBufferLength >= ulOutputSize) + { + // Return the cached copy. + RtlCopyMemory( pData, pFilter->m_pPerFrameSettings, ulOutputSize ); + pIrp->IoStatus.Information = ulOutputSize; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Set KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_SET +NTSTATUS +CCaptureFilter:: +SetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_SUCCESS; + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast <CCaptureFilter *>(KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + // Only handle SETs + if (ulOutputBufferLength == 0) + { + // This is pretty meaningless. + // We can't really tell the caller the size of this buffer. + pIrp->IoStatus.Information = + sizeof(KSCAMERA_PERFRAMESETTING_HEADER) + + sizeof(KSCAMERA_PERFRAMESETTING_FRAME_HEADER) + + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ; + Status = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < sizeof(KSCAMERA_PERFRAMESETTING_HEADER)) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + PKSCAMERA_PERFRAMESETTING_HEADER pPFS = + (PKSCAMERA_PERFRAMESETTING_HEADER) pData ; + ISP_FRAME_SETTINGS *pSettings = nullptr; + + // Parse, but don't save the PerFrameSettings + Status = pFilter->ParsePFSBuffer( pPFS, ulOutputBufferLength, &pSettings ); + + // Cache a copy of the unparsed PFS for the GET. + // Pass the parsed settings down to the sensor. + if( NT_SUCCESS(Status) ) + { + delete [] ((PBYTE) pFilter->m_pPerFrameSettings); + pFilter->m_pPerFrameSettings = (PKSCAMERA_PERFRAMESETTING_HEADER) + new (PagedPool, '-SFP') BYTE [pPFS->Size]; + + if( pFilter->m_pPerFrameSettings ) + { + // Cache a copy of the settings for the GET. + RtlCopyMemory( pFilter->m_pPerFrameSettings, pPFS, pPFS->Size ); + + // Program the sensor with a copy of the parsed settings. + // The sensor object should copy what it needs. + pFilter->m_Sensor->SetPFS( pSettings, pPFS->FrameCount, pPFS->LoopCount ); + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Delete the settings buffer ParsePFSBuffer created. + delete [] pSettings; + } + + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CLEAR +NTSTATUS +CCaptureFilter::ClearPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast <CCaptureFilter *>(KsGetFilterFromIrp(pIrp)->Context); + + // Only handle SETs + delete [] ((PBYTE) pFilter->m_pPerFrameSettings); + pFilter->m_pPerFrameSettings = nullptr; + pFilter->m_Sensor->SetPFS( nullptr, 0, 0 ); + + return ntStatus; +} + +PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER +Find( + _In_ PKSCAMERA_PERFRAMESETTING_CAP_HEADER Caps, + _In_ ULONG Type +) +/*++ + +Routine Description: + + Helper function that finds a specific PFS capability. + +Arguments: + + Caps - + A list of PFS capabilities. + Type - + The capability type to search for. + +Return Value: + + A pointer to the capability item header. + +--*/ +{ + PAGED_CODE(); + + if( Caps ) + { + PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Item = + (PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER) (Caps+1); + + for( ULONG i=0; i<Caps->ItemCount; i++ ) + { + if( Item->Type == Type ) + { + return Item; + } + Item = (PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER) (((PBYTE) Item) + Item->Size); + } + } + return nullptr; +} + +// This function can be used to validate PerFrameSettings, or +// it can be used to populate a buffer containing PFS settings. +_Success_(return == 0) +NTSTATUS +CCaptureFilter:: +ParsePFSBuffer( + _In_reads_bytes_(BufferLimit) + PKSCAMERA_PERFRAMESETTING_HEADER pPFS, + _In_ ULONG BufferLimit, + _Outptr_opt_result_maybenull_ + ISP_FRAME_SETTINGS **ppSettings +) +/*++ + +Routine Description: + + Helper function that parses Per-Frame Settings. + + This function validates the API's PerFrameSettings list and optionally + returns an array of ISP_FRAME_SETTINGS for each frame. + + Any returned array of ISP_FRAME_SETTINGS must be deleted by the caller. + +Arguments: + + pPFS - + A variable length list of per frame settings. + BufferLimit - + The maximum amount of space, in bytes, used by the PFS list. + ppSettings - + An optional pointer to an array of frame settings to be populated. + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_SUCCESS; + + DBG_ENTER("[%S]()", GetFilterName()); + + NT_ASSERT(pPFS); + NT_ASSERT(BufferLimit!=0); + + if( !pPFS || + BufferLimit == 0 || + pPFS->Size > BufferLimit || + pPFS->Size < sizeof(KSCAMERA_PERFRAMESETTING_HEADER) || + pPFS->LoopCount!=1 || // Hard-coded to 1, per spec. + pPFS->FrameCount > MAX_FRAME_COUNT || + pPFS->FrameCount == 0 ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + ULONG i=0 ; + ISP_FRAME_SETTINGS *pSettings = nullptr; + LPVOID pEnd = ((LPBYTE) pPFS) + BufferLimit ; + PKSCAMERA_PERFRAMESETTING_FRAME_HEADER pFrame = + (PKSCAMERA_PERFRAMESETTING_FRAME_HEADER) &pPFS[1]; + + // Make sure we have at least one frame in the array. + ULONG FrameCount = pPFS->FrameCount ? pPFS->FrameCount : 1 ; + + // Query the PFS Caps from the Sensor + PKSCAMERA_PERFRAMESETTING_CAP_HEADER Caps = nullptr; + ULONG RequiredSize = 0; + Status = m_Sensor->GetPfsCaps( nullptr, &RequiredSize ); + + if( NT_SUCCESS(Status) ) + { + Caps = (PKSCAMERA_PERFRAMESETTING_CAP_HEADER) new (PagedPool, '-SFP') BYTE[RequiredSize]; + + if( Caps ) + { + RtlZeroMemory( Caps, RequiredSize ); + Status = m_Sensor->GetPfsCaps( Caps, &RequiredSize ); + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if( NT_SUCCESS(Status) ) + { + // Allocate an array + pSettings = new (NonPagedPoolNx, '-SFP') ISP_FRAME_SETTINGS[FrameCount]; + if( !pSettings ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // Initialize the array to our globals + pSettings[0] = *m_Sensor->GetGlobalIspSettings(); + for( i=1; i<FrameCount; i++ ) + { + pSettings[i] = pSettings[0]; + } + + while( ((pFrame+1)<=pEnd) ) + { + PKSCAMERA_PERFRAMESETTING_FRAME_HEADER pNextFrame = + (PKSCAMERA_PERFRAMESETTING_FRAME_HEADER) + (((LPBYTE)pFrame) + pFrame->Size); + PKSCAMERA_PERFRAMESETTING_ITEM_HEADER pItem = + (PKSCAMERA_PERFRAMESETTING_ITEM_HEADER) &pFrame[1]; + + // Verify that the frame is within the buffer. + if( pFrame->Id >= pPFS->FrameCount || + pNextFrame > pEnd || + pFrame->Size < sizeof(KSCAMERA_PERFRAMESETTING_FRAME_HEADER) ) + { + DBG_TRACE( "Malformed PFS Buffer. pPFS=%p pFrame=%p", + pPFS, pFrame ); + + Status=STATUS_INVALID_PARAMETER; + break; + } + + DBG_TRACE("Frame Id=%d",pFrame->Id); + + ISP_FRAME_SETTINGS &FrameSetting = pSettings[pFrame->Id] ; + + // Scan for at most n Items regardless. + for( i=0; + i < pFrame->ItemCount && + (pItem+1) <= (PVOID)pNextFrame; + i++ ) + { + PKSCAMERA_PERFRAMESETTING_ITEM_HEADER pNextItem = + (PKSCAMERA_PERFRAMESETTING_ITEM_HEADER) + (((LPBYTE)pItem) + pItem->Size); + + PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER ItemCap = + Find( Caps, pItem->Type ); + + // Verify that the item is within the frame ... + // ... and a supported capability. + if( pNextItem>(LPVOID)pNextFrame || + pItem->Size<sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) || + !ItemCap ) + { + Status=STATUS_INVALID_PARAMETER; + break; + } + + switch( pItem->Type ) + { + case KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_TIME: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_TIME"); + + FrameSetting.ExposureMode = + TranslatePFS2VideoProcFlags( pItem->Flags ); + + if( pItem->Flags & KSCAMERA_PERFRAMESETTING_MANUAL ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.ExposureSetting.VideoProc.Value = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value; + // Make sure the setting is in range. + Status = + BoundsCheckSigned( + FrameSetting.ExposureSetting.VideoProc.Value.ll, + ((SOC_CAP_WITH_STEPPING_LONGLONG *) ItemCap)->Stepping ); + DBG_TRACE("Status=0x%08X, ExposureMode=KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL, ExposureTime=%lldns, element=%d", + Status, FrameSetting.ExposureSetting.VideoProc.Value.ll, i); + } + else + { + Status=STATUS_INVALID_PARAMETER; + DBG_TRACE("Status=STATUS_INVALID_PARAMETER"); + } + } + else if( pItem->Size!=sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + Status=STATUS_INVALID_PARAMETER; + DBG_TRACE("Status=STATUS_INVALID_PARAMETER"); + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_FLASH: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_FLASH"); + FrameSetting.FlashMode = pItem->Flags; + if( pItem->Flags & + (KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER) ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.FlashValue = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value.ul; + + // Make sure the setting is in range. + if( FrameSetting.FlashValue > 100 ) + { + Status=STATUS_INVALID_PARAMETER; + } + else + { + DBG_TRACE("FlashPower (%d%) accepted...", FrameSetting.FlashValue); + } + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + else if( pItem->Size!=sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + Status=STATUS_INVALID_PARAMETER; + } + + if( NT_SUCCESS(Status) ) + { + DBG_TRACE("FlashMode=0x%016llX, FlashPower=%d", FrameSetting.FlashMode, FrameSetting.FlashValue); + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_COMPENSATION: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_COMPENSATION"); + { + LONG Denominator = EVFlags2Denominator( pItem->Flags ); + KSPROPERTY_STEPPING_LONG + Bounds = ((SOC_CAP_WITH_STEPPING *) ItemCap)->Stepping; + + // Adjust the bounds for the step size... (A quirk of EV) + Bounds.Bounds.SignedMinimum *= Denominator; + Bounds.Bounds.SignedMaximum *= Denominator; + + FrameSetting.EVCompensation.Mode = (ULONG) + pItem->Flags & ~(KSCAMERA_PERFRAMESETTING_AUTO); + + // Validate manual setting + if( (pItem->Flags != KSCAMERA_PERFRAMESETTING_AUTO) ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) && + (Denominator != 0) ) + { + FrameSetting.EVCompensation.Value = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value.l; + + // Make sure the setting is in range. + Status = + BoundsCheckSigned( + FrameSetting.EVCompensation.Value, + Bounds ); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + // Auto can be the only flag, if auto is enabled. + else //if( pItem->Flags == KSCAMERA_PERFRAMESETTING_AUTO ) + { + if( pItem->Size==sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + static + ULONG step[] = + { + 0, + KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP, //1 + KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP, //2 + KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP, //3 + KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP, //4 + 0, //5 + KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP //6 + }; + + // Work back from the randomly selected denominator ... + /// ... to the step flag. + FrameSetting.EVCompensation.Mode = step[Denominator]; + + // Now pick a "random" numerator that is in range. + FrameSetting.EVCompensation.Value = (LONG) + GetRandom( Bounds.Bounds.SignedMinimum, + Bounds.Bounds.SignedMaximum ); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_ISO: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_ISO: Flags=0x%016llX", pItem->Flags); + + FrameSetting.ISOMode = pItem->Flags; + // Handle Auto. + if( pItem->Flags == KSCAMERA_EXTENDEDPROP_ISO_AUTO ) + { + if( pItem->Size==sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + FrameSetting.ISOValue = 0; + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + // Handle manual settings. + else if( pItem->Flags == KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.ISOValue = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value.ul; + // Make sure the setting is in range. + Status = + BoundsCheckUnsigned( + FrameSetting.ISOValue, + ((SOC_CAP_WITH_STEPPING *)ItemCap)->Stepping ); + + DBG_TRACE("ISOValue=%d", FrameSetting.ISOValue); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_FOCUS: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_FOCUS"); + if( pItem->Flags == KSCAMERA_PERFRAMESETTING_MANUAL ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.FocusMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + FrameSetting.FocusSetting.VideoProc = + *((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1])); + // Make sure the setting is in range. + Status = + BoundsCheckUnsigned( + FrameSetting.FocusSetting.VideoProc.Value.ul, + ((SOC_CAP_WITH_STEPPING *)ItemCap)->Stepping ); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_PHOTOCONFIRMATION: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_PHOTOCONFIRMATION"); + + if( pItem->Size!=sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + Status=STATUS_INVALID_PARAMETER; + } + else + { + switch( pItem->Flags ) + { + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON: + FrameSetting.bPhotoConfirmation = TRUE; + DBG_TRACE("PFS Photo Confirmation ON for frame #%d", pFrame->Id); + break; + + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_OFF: + FrameSetting.bPhotoConfirmation = FALSE; + DBG_TRACE("PFS Photo Confirmation OFF for frame #%d", pFrame->Id); + break; + + default: + Status=STATUS_INVALID_PARAMETER; + break; + } + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_CUSTOM: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_CUSTOM"); + break; + + default: + // We don't understand this item. Do nothing. + NT_ASSERT(FALSE); + break; + } + + // Bail here (a little early) if any of the parsing failed. + if( !NT_SUCCESS(Status) ) + { + break; + } + + // Move to the next Item + pItem = pNextItem; + } + + // Bail here (a little early) if any of the parsing failed. + if( !NT_SUCCESS(Status) ) + { + DBG_TRACE( "Malformed PFS Buffer. pPFS=%p pItem=%p", + pPFS, pItem ); + break; + } + + // Move to next Frame + pFrame = pNextFrame; + } + + // We need to log where in the buffer we encountered an error. + // For now we just emit info to the debugger. + } + + if( NT_SUCCESS(Status) && ppSettings ) + { + *ppSettings = pSettings; + } + else + { + delete [] pSettings; + } + } + + delete [] ((PBYTE) Caps); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSSTATE +NTSTATUS +CCaptureFilter::GetFocusState( + _Inout_ CExtendedProperty *pState +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pState->isValid() && + pState->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + KSCAMERA_EXTENDEDPROP_FOCUSSTATE FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED; + Status = m_Sensor->GetFocusState( &FocusState ); + if( NT_SUCCESS(Status) ) + { + *pState = CExtendedProperty( (ULONGLONG) FocusState ); + pState->Capability = 0; + } + } + DBG_LEAVE("(PinId=%u, Flags=0x%016llX, Version=%u)=0x%08X", + pState->PinId, pState->Flags, pState->Version, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CCaptureFilter:: +GetFocusPriority( + _Inout_ CExtendedProperty *pPriority +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pPriority->isValid() && + pPriority->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFocusPriority( pPriority ); + } + DBG_LEAVE("(PinId=%u, Flags=0x%016llX, Version=%u)=0x%08X", + pPriority->PinId, pPriority->Flags, pPriority->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CCaptureFilter:: +SetFocusPriority( + _In_ CExtendedProperty *pPriority +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pPriority->isValid() && + pPriority->Capability == 0 && + pPriority->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + (pPriority->Flags & ~KSCAMERA_EXTENDEDPROP_FOCUSPRIORITY_ON) == 0 ) + { + Status = m_Sensor->SetFocusPriority( pPriority ); + } + DBG_LEAVE("(PinId=%u, Flags=0x%016llX, Version=%u)=0x%08X", + pPriority->PinId, pPriority->Flags, pPriority->Version, Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_CONFIGCAPS Get handler +NTSTATUS +CCaptureFilter:: +GetRoiConfigCaps( + _Inout_ CRoiConfig *pOutput +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pOutput->isValid() && + pOutput->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetRoiConfigCaps( pOutput ); + } + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Get handler +// +// Note: ROI handlers are left as static member functions because +// it wasn't worth the code needed to generalize handling of a +// variable length parameter. +NTSTATUS +CCaptureFilter:: +GetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast <CCaptureFilter *>(KsGetFilterFromIrp(pIrp)->Context); + CSensor *pSensor = pFilter->m_Sensor; + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + CRoiProperty *pRoi = (reinterpret_cast<CRoiProperty *>(pData)); + ULONG RequiredSize = pSensor->SizeOfRoi(); + + // Only handle GETs + if( ulOutputBufferLength == 0 ) + { + pIrp->IoStatus.Information = RequiredSize;; + Status = STATUS_BUFFER_OVERFLOW; + } + else if( ulOutputBufferLength < RequiredSize ) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else if( pData ) + { + Status = pSensor->GetRoi( pRoi ); + if( NT_SUCCESS(Status) ) + { + pIrp->IoStatus.Information = pRoi->GetSize(); + } + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Set handler +NTSTATUS +CCaptureFilter:: +SetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast <CCaptureFilter *>(KsGetFilterFromIrp(pIrp)->Context); + CSensor *pSensor = pFilter->m_Sensor; + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + CRoiProperty *pRoi = (reinterpret_cast<CRoiProperty *>(pData)); + ULONG RequiredSize = sizeof(*pRoi); + + // Only handle SETs + if( ulOutputBufferLength == 0 ) + { + // Minimum size for this control. + pIrp->IoStatus.Information = RequiredSize; + Status = STATUS_BUFFER_OVERFLOW; + } + else if( ulOutputBufferLength < RequiredSize ) + { + DBG_TRACE("***BUFFER TOO SMALL*** ulOutputBufferLength=%d, Size=%d", + ulOutputBufferLength, pRoi->Size ); + Status = STATUS_BUFFER_TOO_SMALL; + } + else if( pData && ulOutputBufferLength >= pRoi->Size ) + { + // Validate the data blob. + if( pRoi->isValid() && + pRoi->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pRoi->Flags == 0 ) + { + Status = INVOKE_SET_ASYNC( pSensor, Roi, pRoi, pFilter->m_RoiNotifier ); + } + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CCaptureFilter:: +GetIsoAdvanced( + _Inout_ CExtendedVidProcSetting *pIso +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pIso->isValid() && + m_Sensor->IsStillIndex(pIso->PinId) ) + { + Status = m_Sensor->GetIsoAdvanced( pIso ); + } + + // Report to the debug output... + DBG_TRACE(" Flags = 0x%016llX", pIso->Flags); + DBG_TRACE(" Min = %d", pIso->m_Setting.Min); + DBG_TRACE(" Max = %d", pIso->m_Setting.Max); + DBG_TRACE(" Step = %d", pIso->m_Setting.Step); + DBG_TRACE("VideoProc.Value = %lld", pIso->m_Setting.VideoProc.Value.ll); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CCaptureFilter:: +SetIsoAdvanced( + _In_ CExtendedVidProcSetting *pIso +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Report to the debug output... + DBG_TRACE(" Flags = 0x%016llX", pIso->Flags); + DBG_TRACE(" Min = %d", pIso->m_Setting.Min); + DBG_TRACE(" Max = %d", pIso->m_Setting.Max); + DBG_TRACE(" Step = %d", pIso->m_Setting.Step); + DBG_TRACE("VideoProc.Value = %lld", pIso->m_Setting.VideoProc.Value.ll); + + // Validate + switch( pIso->Flags ) + { + case KSCAMERA_EXTENDEDPROP_ISO_AUTO: + case KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION: + case KSCAMERA_EXTENDEDPROP_ISO_AUTO | KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION: + case KSCAMERA_EXTENDEDPROP_ISO_MANUAL: + case KSCAMERA_EXTENDEDPROP_ISO_MANUAL | KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION: + if( pIso->isValid() && + (pIso->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + m_Sensor->IsStillIndex(pIso->PinId) ) + { + CExtendedVidProcSetting Caps; + Status = m_Sensor->GetIsoAdvanced(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Just save the parameters + if( pIso->Flags & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + Status = BoundsCheckSigned( pIso->GetLONGLONG(), Caps.m_Setting ); + } + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, IsoAdvanced, pIso, m_IsoAdvancedNotifier ) + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + break; + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CCaptureFilter:: +GetVFR( + _Inout_ CExtendedProperty *pVFR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsVideoIndex(pVFR->PinId) && + pVFR->isValid() ) + { + //Get the current VFR state + Status = m_Sensor->GetVFR( pVFR ); + } + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVFR->PinId, m_Sensor->GetNextVideoIndex(), pVFR->Flags, pVFR->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CCaptureFilter:: +SetVFR( + _In_ CExtendedProperty *pVFR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsVideoIndex(pVFR->PinId) && + pVFR->isValid() && + pVFR->Capability == 0 && + (KSCAMERA_EXTENDEDPROP_VFR_OFF == pVFR->Flags || + KSCAMERA_EXTENDEDPROP_VFR_ON == pVFR->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOHDR_AUTO == pVFR->Flags ) ) + { + //Set the current VFR state + Status = m_Sensor->SetVFR( pVFR ); + } + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVFR->PinId, m_Sensor->GetNextVideoIndex(), pVFR->Flags, pVFR->Version, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CCaptureFilter:: +GetVideoHDR( + _Inout_ CExtendedProperty *pVideoHDR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsVideoIndex(pVideoHDR->PinId) && + pVideoHDR->isValid() ) + { + //Get the current Video HDR state + Status = m_Sensor->GetVideoHDR(pVideoHDR); + } + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVideoHDR->PinId, m_Sensor->GetNextVideoIndex(), pVideoHDR->Flags, pVideoHDR->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CCaptureFilter:: +SetVideoHDR( + _In_ CExtendedProperty *pVideoHDR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( m_Sensor->IsVideoIndex(pVideoHDR->PinId) && + pVideoHDR->isValid() && + !(pVideoHDR->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_VIDEOHDR_OFF == pVideoHDR->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOHDR_ON == pVideoHDR->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOHDR_AUTO== pVideoHDR->Flags) ) + { + if( !m_pinArray[pVideoHDR->PinId] || + m_pinArray[pVideoHDR->PinId]->GetState() == PinStopped ) + { + CExtendedProperty Caps(*pVideoHDR); + Status = m_Sensor->GetVideoHDR( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( pVideoHDR->Flags == (pVideoHDR->Flags & Caps.Capability) ) + { + Status = m_Sensor->SetVideoHDR(pVideoHDR); + } + } + } + else + { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVideoHDR->PinId, m_Sensor->GetNextVideoIndex(), pVideoHDR->Flags, pVideoHDR->Version, Status); + return Status; +} + + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CCaptureFilter:: +GetHistogram( + _Inout_ CExtendedProperty *pHistogram +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsPreviewIndex(pHistogram->PinId) && + pHistogram->isValid() && + pHistogram->Capability == 0 ) + { + //Get the histogram state + Status = m_Sensor->GetHistogram( pHistogram ); + } + DBG_TRACE("pHistogram->PinId = %u, m_sensor->PreviewIndex = %u, pHistogram->Flags = %016llx, pHistogram->Version = %u", + pHistogram->PinId, m_Sensor->GetNextPreviewIndex(), pHistogram->Flags, pHistogram->Version); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CCaptureFilter:: +SetHistogram( + _In_ CExtendedProperty *pHistogram +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsPreviewIndex(pHistogram->PinId) && + pHistogram->isValid() && + !(pHistogram->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_HISTOGRAM_OFF == pHistogram->Flags || + KSCAMERA_EXTENDEDPROP_HISTOGRAM_ON == pHistogram->Flags ) ) + { + if( m_pinArray[pHistogram->PinId] && + m_pinArray[pHistogram->PinId]->GetState() != PinStopped ) + { + Status = STATUS_INVALID_DEVICE_STATE; + } + else + { + //Set the histogram state. + Status = m_Sensor->SetHistogram( pHistogram ); + } + } + + DBG_TRACE("pHistogram->PinId = %u, m_sensor->PreviewIndex = %u, pHistogram->Flags = %016llx, pHistogram->Version = %u", + pHistogram->PinId, m_Sensor->GetNextPreviewIndex(), pHistogram->Flags, pHistogram->Version); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CCaptureFilter:: +GetFaceDetection( + _Inout_ CExtendedVidProcSetting *pFaceDetect +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + if( pFaceDetect->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFaceDetect->isValid() ) + { + //Get the FaceDetection state + Status = m_Sensor->GetFaceDetection( pFaceDetect ); + } + + DBG_LEAVE("(): PinId=0x%08X, Flags=0x%016llX, Version=%u, Value=%d, Status=0x%08X", + pFaceDetect->PinId, pFaceDetect->Flags, pFaceDetect->Version, pFaceDetect->GetULONG(), Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CCaptureFilter:: +SetFaceDetection( + _In_ CExtendedVidProcSetting *pFaceDetect +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("(): PinId=0x%08X, Flags=0x%016llX, Version=%u, Value=%d", + pFaceDetect->PinId, pFaceDetect->Flags, pFaceDetect->Version, pFaceDetect->GetULONG()); + + if( pFaceDetect->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFaceDetect->isValid() && + ( KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF == pFaceDetect->Flags || + (KSCAMERA_EXTENDEDPROP_FACEDETECTION_MASK & pFaceDetect->Flags) !=0 ) ) + { + CExtendedVidProcSetting Caps(*pFaceDetect); + Status = m_Sensor->GetFaceDetection( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pFaceDetect->Flags == (pFaceDetect->Flags & Caps.Capability) && + ( KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF == pFaceDetect->Flags || + NT_SUCCESS( Status = Caps.BoundsCheck( pFaceDetect->GetULONG() ) ) ) ) + { + //Set the FaceDetection state. + Status = m_Sensor->SetFaceDetection( pFaceDetect ); + } + } + } + + DBG_LEAVE("(): Status=0x%08X", Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Get handler +NTSTATUS +CCaptureFilter:: +GetZoom( + _Inout_ CExtendedVidProcSetting *pZoom +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pZoom->isValid() && + pZoom->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetZoom(pZoom); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Set handler +NTSTATUS +CCaptureFilter:: +SetZoom( + _In_ CExtendedVidProcSetting *pZoom +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Validate + if( pZoom->isValid() && + pZoom->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + !(pZoom->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedVidProcSetting Caps(*pZoom); + Status = m_Sensor->GetZoom( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Report to the debug output... + DBG_TRACE(" Flags = 0x%016llX", Caps.Flags); + DBG_TRACE(" Min = %d", Caps.Min()); + DBG_TRACE(" Max = %d", Caps.Max()); + DBG_TRACE(" Step = %d", Caps.Step()); + DBG_TRACE("VideoProc.Value = %d", pZoom->GetLONG()); + + if( pZoom->Flags == (pZoom->Flags & Caps.Capability) ) + { + Status = BoundsCheckSigned( pZoom->GetLONG(), Caps.m_Setting ); + + if (NT_SUCCESS(Status)) + { + Status = m_Sensor->SetZoom(pZoom); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("(PinId = %u, Flags = 0x%016llX, Caps = 0x%016llX)=0x%08X", + pZoom->PinId, pZoom->Flags, pZoom->Capability, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CCaptureFilter:: +GetVideoStabilization( + _Inout_ CExtendedProperty *pVideoStab +) +{ + PAGED_CODE(); + + if (m_Sensor->IsVideoIndex(pVideoStab->PinId) && + pVideoStab->isValid()) + { + //Get the current state + return m_Sensor->GetVideoStabilization(pVideoStab); + } + return STATUS_INVALID_PARAMETER; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CCaptureFilter:: +SetVideoStabilization( + _In_ CExtendedProperty *pVideoStab +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( m_Sensor->IsVideoIndex(pVideoStab->PinId) && + pVideoStab->isValid() && + !(pVideoStab->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF == pVideoStab->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_ON == pVideoStab->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_AUTO == pVideoStab->Flags) ) + { + CExtendedProperty Caps(*pVideoStab); + Status = m_Sensor->GetVideoStabilization( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( pVideoStab->Flags == (pVideoStab->Flags & Caps.Capability) ) + { + LockFilter Lock(m_pKSFilter); + + if( m_pinArray[pVideoStab->PinId] && + m_pinArray[pVideoStab->PinId]->GetState() != PinStopped ) + { + Status = STATUS_INVALID_DEVICE_STATE; + } + else + { + Status = m_Sensor->SetVideoStabilization(pVideoStab); + } + } + } + } + DBG_TRACE("pVideoStab = %p, PinId = %u, m_sensor->VideoIndex = %u, Flags = %llu, Version = %u", + pVideoStab, pVideoStab->PinId, m_Sensor->GetNextVideoIndex(), pVideoStab->Flags, pVideoStab->Version); + + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CCaptureFilter:: +GetOpticalImageStabilization( + _Inout_ CExtendedProperty *pOIS +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pOIS->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pOIS->isValid() ) + { + //Get the current state + Status = m_Sensor->GetOpticalImageStabilization(pOIS); + } + + DBG_TRACE("pOIS = %p, PinId = %u, Flags = %llu, Version = %u", + pOIS, pOIS->PinId, pOIS->Flags, pOIS->Version); + + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CCaptureFilter:: +SetOpticalImageStabilization( + _In_ CExtendedProperty *pOIS +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( pOIS->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pOIS->isValid() && + !(pOIS->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_OIS_OFF == pOIS->Flags || + KSCAMERA_EXTENDEDPROP_OIS_ON == pOIS->Flags || + KSCAMERA_EXTENDEDPROP_OIS_AUTO== pOIS->Flags)) + { + CExtendedProperty Caps(*pOIS); + Status = m_Sensor->GetOpticalImageStabilization(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pOIS->Flags == (pOIS->Flags & Caps.Capability) ) + { + Status = m_Sensor->SetOpticalImageStabilization(pOIS); + } + } + } + DBG_TRACE("pOIS = %p, PinId = %u, Flags = %llu, Version = %u", + pOIS, pOIS->PinId, pOIS->Flags, pOIS->Version); + + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CCaptureFilter:: +GetAdvancedPhoto( + _Inout_ CExtendedProperty *pAdvancedPhoto +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsStillIndex(pAdvancedPhoto->PinId) && + pAdvancedPhoto->isValid() ) + { + //Get the current state + Status = m_Sensor->GetAdvancedPhoto(pAdvancedPhoto); + } + + DBG_LEAVE(" PinId=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pAdvancedPhoto->PinId, pAdvancedPhoto->Flags, pAdvancedPhoto->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CCaptureFilter:: +SetAdvancedPhoto( + _In_ CExtendedProperty *pAdvancedPhoto +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Valid request, still pin & no unsupported flags. + if( m_Sensor->IsStillIndex(pAdvancedPhoto->PinId) && + pAdvancedPhoto->isValid() && + !(pAdvancedPhoto->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedProperty Caps(*pAdvancedPhoto); + Status = m_Sensor->GetAdvancedPhoto(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pAdvancedPhoto->Flags == (pAdvancedPhoto->Flags & Caps.Capability) ) + { + // Make sure the requested flags are a valid capability combination. + switch( pAdvancedPhoto->Flags ) + { + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: + Status = m_Sensor->SetAdvancedPhoto(pAdvancedPhoto); + } + } + } + } + + DBG_LEAVE(" PinId=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pAdvancedPhoto->PinId, pAdvancedPhoto->Flags, pAdvancedPhoto->Version, Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CCaptureFilter:: +GetCameraProfile( + _Inout_ CExtendedProfile *pProfile +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_SUCCESS; + + pProfile->Version = KSCAMERA_EXTENDEDPROP_VERSION; + pProfile->PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + pProfile->Size = sizeof(KSCAMERA_EXTENDEDPROP_HEADER) + sizeof (KSCAMERA_EXTENDEDPROP_PROFILE); + pProfile->Result = 0; + pProfile->Flags = 0; + pProfile->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pProfile->m_Profile = m_Profile; + + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE. +NTSTATUS +CCaptureFilter:: +SetCameraProfile( + _In_ CExtendedProfile *pProfile +) +{ + PAGED_CODE(); + + if (pProfile->PinId != KSCAMERA_EXTENDEDPROP_FILTERSCOPE || + !pProfile->isValid() || + 0 == (pProfile->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) || + pProfile->Flags != 0) + { + return STATUS_INVALID_PARAMETER; + } + m_Profile = pProfile->m_Profile; + + m_ProfileNotifier.Set(); + return STATUS_SUCCESS; +} + + +NTSTATUS +CCaptureFilter:: +GetExposure( + _Inout_ KSPROPERTY_CAMERACONTROL_S *pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetExposure(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CCaptureFilter:: +SetExposure( + _In_ KSPROPERTY_CAMERACONTROL_S *pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("(Property->Value=%d)", pProperty->Value); + + NTSTATUS Status = m_Sensor->SetExposure(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CCaptureFilter:: +GetFocus( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetFocus(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CCaptureFilter:: +SetFocus( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + m_Sensor->CancelFocus(); + + NTSTATUS Status = m_Sensor->SetFocus(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CCaptureFilter:: +GetZoom( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetZoom(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CCaptureFilter:: +SetZoom( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetZoom(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CCaptureFilter:: +GetZoomRelative( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetZoomRelative(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CCaptureFilter:: +SetZoomRelative( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetZoomRelative(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CCaptureFilter:: +GetPan( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetPan(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CCaptureFilter:: +SetPan( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetPan(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CCaptureFilter:: +GetRoll( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetRoll(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CCaptureFilter:: +SetRoll( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetRoll(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CCaptureFilter:: +GetTilt( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetTilt(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CCaptureFilter:: +SetTilt( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetTilt(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CCaptureFilter:: +GetFocalLength( + _Inout_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetFocalLength(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CCaptureFilter:: +SetFocalLength( + _In_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetFocalLength(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CCaptureFilter:: +GetBacklightCompensation( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetBacklightCompensation(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CCaptureFilter:: +SetBacklightCompensation( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetBacklightCompensation(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CCaptureFilter:: +GetBrightness( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetBrightness(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CCaptureFilter:: +SetBrightness( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetBrightness(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CCaptureFilter:: +GetContrast( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetContrast(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CCaptureFilter:: +SetContrast( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetContrast(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CCaptureFilter:: +GetHue( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetHue(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CCaptureFilter:: +SetHue( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetHue(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CCaptureFilter:: +GetWhiteBalance( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetWhiteBalance(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CCaptureFilter:: +SetWhiteBalance( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetWhiteBalance(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CCaptureFilter:: +GetPowerlineFreq( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetPowerlineFreq(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CCaptureFilter:: +SetPowerlineFreq( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetPowerlineFreq(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CCaptureFilter:: +GetVideoControlMode( + _Inout_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsStillIndex(pMode->StreamIndex) ) + { + Status = m_Sensor->GetVideoControlMode( pMode ); + } + + DBG_LEAVE("( Pin=%d, Mode=0x%08X )=0x%08X", + pMode->StreamIndex, pMode->Mode, Status ); + return Status; +} + +// Set KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CCaptureFilter:: +SetVideoControlMode( + _In_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsStillIndex(pMode->StreamIndex) ) + { + KSPROPERTY_VIDEOCONTROL_CAPS_S Caps; + RtlZeroMemory( &Caps, sizeof(Caps) ); + Caps.StreamIndex = pMode->StreamIndex; + + Status = m_Sensor->GetVideoControlCaps( &Caps ); + if( NT_SUCCESS(Status) && + pMode->Mode == (pMode->Mode & (LONG)Caps.VideoControlCaps ) ) + { + DBG_TRACE("Mode=0x%08X, Caps.Mode=0x%08X", pMode->Mode, Caps.VideoControlCaps); + Status = m_Sensor->SetVideoControlMode( pMode ); + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + + DBG_LEAVE("( Pin=%d, Mode=0x%08X )=0x%08X", + pMode->StreamIndex, pMode->Mode, Status ); + return Status; +} + +// Get KSPROPERTY_VIDEOCONTROL_CAPS. +NTSTATUS +CCaptureFilter:: +GetVideoControlCaps( + _Inout_ KSPROPERTY_VIDEOCONTROL_CAPS_S *pCaps +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsValidIndex(pCaps->StreamIndex) ) + { + Status = m_Sensor->GetVideoControlCaps( pCaps ); + } + + DBG_LEAVE("( Pin=%d, Caps=0x%08X )=0x%08X", + pCaps->StreamIndex, pCaps->VideoControlCaps, Status ); + return Status; +} + diff --git a/AVStream/avscamera/sys/filter.h b/AVStream/avscamera/sys/filter.h new file mode 100644 index 00000000..58a0e9e8 --- /dev/null +++ b/AVStream/avscamera/sys/filter.h @@ -0,0 +1,483 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + filter.h + + Abstract: + + This file contains the filter level header for the base capture filter + class. It provides a wrapper on KSFILTER. + + CCaptureFilter holds state information for the filter and contains + implementations for standard camera controls. These implementations + validate arguments and capabilities of the control according to DDI + definitions. Once validated, control is passes to a CSensor object + to manipulate the state of camera. + + CCaptureFilter holds a reference to any active CCapturePins that may + have been created on this filter. The reference is used to obtain + the state of the pin and potentially modify the pin's allocator + framing. + + To add a new control, derive from CCaptureFilter and use one of the + DECLARE_PROPERTY_XXX() macros below to declare the property handlers. + You can also use the DEFINE_PROP_ITEM_XXX() and DEFINE_STD_EVENT_ITEM() + macros as aids in constructing a filter's automation table. + + History: + + created 3/12/2001 + +**************************************************************************/ + +extern KSPROPERTY_VALUES ZoomRelativeValues; +extern KSPROPERTY_VALUES ZoomValues; +extern KSPROPERTY_VALUES PanValues; +extern KSPROPERTY_VALUES RollValues; +extern KSPROPERTY_VALUES TiltValues; +extern KSPROPERTY_VALUES BacklightCompensationValues; +extern KSPROPERTY_VALUES BrightnessValues; +extern KSPROPERTY_VALUES ContrastValues; +extern KSPROPERTY_VALUES HueValues; +extern KSPROPERTY_VALUES PLFValues; +extern KSPROPERTY_VALUES WhiteBalanceValues; +extern KSPROPERTY_VALUES ExposureValues; +extern KSPROPERTY_VALUES FocusValues; + +class CCapturePin; +class CCaptureFilter +{ +private: + + // + // The AVStream filter object associated with this CCaptureFilter. + // + CCapturePin **m_pinArray; + +protected: + // + // Setup, memory allocation, etc. + // + NTSTATUS + Initialize(); + + // + // Cleanup + // + void + Cleanup(); + +public: + + // + // setPin() + // + // Attach a CCapturePin. + // + void + setPin( + _In_ CCapturePin *pPin, + _In_ unsigned Id + ) ; + + // + // getPin() + // + // Query an attached CCapturePin. + // + CCapturePin * + getPin( + _In_ unsigned Id + ) ; + + // + // CCaptureFilter() + // + // Constructor. + // + CCaptureFilter ( + _In_ PKSFILTER Filter + ); + + // + // ~CCaptureFilter(): + // + // The capture filter destructor. + // + virtual + ~CCaptureFilter (); + + // + // DispatchCreate(): + // + // This is the filter creation dispatch for the capture filter. It + // creates the CCaptureFilter object, associates it with the AVStream + // object, and bags it for easy cleanup later. + // + static + NTSTATUS + DispatchCreate ( + _In_ PKSFILTER Filter, + _In_ PIRP Irp + ); + + // + // Close(): Called in response to IRP_MJ_CLOSE. + // + static + NTSTATUS + DispatchClose( + _In_ PKSFILTER Filter, + _In_ PIRP Irp + ); + + LARGE_INTEGER m_StartTime; + CCaptureDevice *m_pCaptureDevice; + CSensor *m_Sensor; + PKSFILTER m_pKSFilter; + PKSCAMERA_PERFRAMESETTING_HEADER m_pPerFrameSettings; + ULONG m_PFSSize; + + CNotifier m_PhotoModeNotifier; + CNotifier m_PhotoMaxFrameRateNotifier; + CNotifier m_FocusNotifier; + CNotifier m_FocusRectNotifier; + CNotifier m_IsoNotifier; + CNotifier m_IsoAdvancedNotifier; + CNotifier m_EvCompensationNotifier; + CNotifier m_WhiteBalanceNotifier; + CNotifier m_ExposureNotifier; + CNotifier m_SceneModeNotifier; + CNotifier m_ThumbnailNotifier; + CNotifier m_WarmStartNotifier; + CNotifier m_RoiNotifier; + CNotifier m_ProfileNotifier; + + // + // The following static member template function is used to validate + // most KSPROPERTYs and thunk that call down to a handler that is a non- + // static member of CCaptureFilter (or a derived class). + // + // Note: A separate instance of this thunking function is instantiated for + // each control, so code space is not conserved. However, performing + // this logic in only one place ensures consistant behavior across + // controls and reduces the chances for mistakes. + // + template + <typename OUTPUT, // type of data consumed by the handler. + typename FILTER, + NTSTATUS (FILTER::*pFun)( // The local handling function. + _Inout_ OUTPUT *pOutput )> + __declspec(code_seg("PAGE")) // Put this function into paged code. + static + NTSTATUS + Prop( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData) + { + PAGED_CODE(); + UNREFERENCED_PARAMETER(pProperty); + + NTSTATUS Status = STATUS_SUCCESS; + NT_ASSERT(pIrp); + + FILTER *pFilter = reinterpret_cast <FILTER *>(KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + OUTPUT *pOutput = reinterpret_cast<OUTPUT *>(pData); + + if (ulOutputBufferLength == 0) + { + pIrp->IoStatus.Information = sizeof(OUTPUT); + Status = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < sizeof(OUTPUT)) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else if (pData && ulOutputBufferLength >= sizeof(OUTPUT)) + { + Status = (pFilter->*pFun)( pOutput ); + if( NT_SUCCESS(Status) ) + { + pIrp->IoStatus.Information = sizeof(OUTPUT); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + + return Status; + } + + // Get Per Frame Setting Caps + static + NTSTATUS + GetPFSCaps( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // Get current Per Frame Settings + static + NTSTATUS + GetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // Set new Per Frame Settings + static + NTSTATUS + SetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // Clear Per Frame Settings + static + NTSTATUS + ClearPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + +// +// DECLARE_PROPERTY_XXX() macros. +// +// The follow macros declare a property handler using a common naming +// convention. This convention is unique to this sample code. The +// convention is used again in the DEFINE_PROP_ITEM_XXX() macros during +// construction of the automation tables. +// +// Note: These definitions are non-static members of the filter. +// + +// Declare a property's GET handler. +#define DECLARE_PROPERTY_GET_HANDLER( type, name ) \ + NTSTATUS \ + Get##name( \ + _Inout_ type *pOutput \ + ); + +// Declare a property's SET handler. +#define DECLARE_PROPERTY_SET_HANDLER( type, name ) \ + NTSTATUS \ + Set##name( \ + _In_ type *pOutput \ + ); + +// Declare a property's GET and SET handlers. +#define DECLARE_PROPERTY_HANDLERS( type, name ) \ +DECLARE_PROPERTY_GET_HANDLER( type, name ) \ +DECLARE_PROPERTY_SET_HANDLER( type, name ) + + // + // Declaration of all the standard control handlers that use fixed structures. + // + DECLARE_PROPERTY_HANDLERS( CExtendedPhotoMode, PhotoMode ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedProperty, PhotoFrameRate ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, PhotoMaxFrameRate ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, WarmStart ); + DECLARE_PROPERTY_HANDLERS( CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedFieldOfView, FieldOfView ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedCameraAngleOffset, CameraAngleOffset ); + DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ); + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Focus ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedProperty, FocusState ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, FocusPriority ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, ExtendedFlash ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Iso ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, IsoAdvanced ) + DECLARE_PROPERTY_HANDLERS( CExtendedEvCompensation, EvCompensation ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, WhiteBalance ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Exposure ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, FaceDetection ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, PhotoConfirmation ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, SceneMode ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VFR ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoHDR ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Zoom ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoStabilization) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Histogram ) + DECLARE_PROPERTY_HANDLERS( CExtendedMetadata, Metadata ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, OpticalImageStabilization ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, OptimizationHint ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, AdvancedPhoto ) + DECLARE_PROPERTY_HANDLERS( CExtendedProfile, CameraProfile ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Thumbnail ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TriggerTime ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TorchMode ) + + DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ) + DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ) + DECLARE_PROPERTY_GET_HANDLER( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence ) + + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Exposure); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Focus); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Zoom); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, ZoomRelative); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Pan); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Roll); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Tilt); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength); + + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Brightness); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Contrast); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Hue); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + + DECLARE_PROPERTY_GET_HANDLER( CRoiConfig, RoiConfigCaps ); + + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode); + DECLARE_PROPERTY_GET_HANDLER(KSPROPERTY_VIDEOCONTROL_CAPS_S, VideoControlCaps); + + // + // The following are property handlers that use an atypical variable + // length (not known at compile-time) parameter list. + // + // Since we only have a few, declare them explicitly. + // + + // KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Get handler + static + NTSTATUS + GetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Set handler + static + NTSTATUS + SetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData); + + // Make sure the PERFRAMESETTINGs passed to us are valid. + _Success_(return == 0) + NTSTATUS + CCaptureFilter::ParsePFSBuffer( + _In_reads_bytes_(BufferLimit) + PKSCAMERA_PERFRAMESETTING_HEADER pPFS, + _In_ ULONG BufferLimit, + _Outptr_opt_result_maybenull_ + ISP_FRAME_SETTINGS **ppSettings ); + + // Helper function for finding our filter object. + static + CCaptureFilter * + GetFilter( + _In_ PKSPIN Pin + ) + { + return reinterpret_cast <CCaptureFilter *> + (KsPinGetParentFilter(Pin)->Context); + } + + // Accessor to convert a reference to a CCaptureFilter to a PKSFILTER. + operator PKSFILTER() + { + return m_pKSFilter; + } + + // Get the name of the filter from the context structure passed in at initialization. + LPCWSTR GetFilterName() + { + return m_pCaptureDevice->GetFilterName(m_pCaptureDevice->GetFilterIndex(m_pKSFilter)); + } + + // Update the pin's allocator to a specific frame count. + NTSTATUS + UpdateAllocatorFraming( + _In_ ULONG PinId + ); + +private: + // An arbitrary limit on Per-Frame settings. + static + const ULONG MAX_FRAME_COUNT=512; + + // Filter level state information. + KSCAMERA_EXTENDEDPROP_PROFILE m_Profile; +}; + +// +// Helper definitions for creating custom property sets +// +// Note: Property automation tables require a statically defined accessor +// functions, however our properties functions are (mostly) implemented +// as non-static member functions. The following macros provide a +// static member thunking function by invoking a member template +// function called Prop<>. The Prop<> performs limited validation on +// the buffer, provides back the size if the buffer size was 0, picks +// up a pointer to the filter through the IRP and calls the member +// function that handles the property. +// + +#define DEFINE_PROP_ITEM_NO_SET( T, ctrl, type, name ) \ + DEFINE_KSPROPERTY_ITEM( \ + ctrl, \ + (&T::Prop<type, T, &T::Get##name>), \ + sizeof(KSPROPERTY), \ + 0, \ + NULL, \ + NULL, 0, NULL, NULL, 0 \ + ) + +#define DEFINE_PROP_ITEM_WITH_VALUES( T, ctrl, type, name, values ) \ + DEFINE_KSPROPERTY_ITEM( \ + ctrl, \ + (&T::Prop<type, T, &T::Get##name>), \ + sizeof(KSPROPERTY), \ + 0, \ + (&T::Prop<type, T, &T::Set##name>), \ + values, 0, NULL, NULL, 0 \ + ) + +#define DEFINE_PROP_ITEM( T, ctrl, type, name ) \ + DEFINE_PROP_ITEM_WITH_VALUES( T, ctrl, type, name, NULL ) + +// +// A simplification for defining a KSEVENT. We only need to specify one parameter. +// +#define DEFINE_STD_EVENT_ITEM(EVENTID) \ + DEFINE_KSEVENT_ITEM \ + ( \ + EVENTID, \ + sizeof(KSEVENTDATA), \ + 0, \ + NULL, \ + NULL, \ + NULL \ + ) + +// +// A simplification for defining a KSPROPERTY_SET. We only need to specify two parameters. +// +#define DEFINE_STD_PROPERTY_SET(SET, PROPERTIES) \ + DEFINE_KSPROPERTY_SET( \ + &SET, \ + SIZEOF_ARRAY(PROPERTIES), \ + PROPERTIES, \ + 0, \ + NULL) + +const LONGLONG MAX_EXPOSURE_TIME = 10000000ULL * 60 * 60; // 1 min +const LONG MIN_EXPOSURE_TIME=10000; // 1 ms +const LONG DEF_EXPOSURE_TIME=10000 * 30; // 30 ms. diff --git a/AVStream/avscamera/sys/formats.cpp b/AVStream/avscamera/sys/formats.cpp new file mode 100644 index 00000000..0c03d4ab --- /dev/null +++ b/AVStream/avscamera/sys/formats.cpp @@ -0,0 +1,390 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + Formats.cpp + + Abstract: + + A list of available Image and Video formats. + + History: + + created 2/12/2016 + +**************************************************************************/ + +#include "Common.h" +#include "ntintsafe.h" + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +/************************************************************************** + + DISPATCH AND DESCRIPTOR LAYOUT + +**************************************************************************/ + +DEFINE_DATARANGE_VIDEO( + FormatNV12_CaptureVGA, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_NV12), + DMAX_X, DMAX_Y, + 30, + 1, + 12, + FOURCC_NV12) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureQVGA, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + D_X, D_Y, + 30, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureQVGAOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + D_X_OS, D_Y_OS, + 30, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGA, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X, DMAX_Y, + 30, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGAOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X_OS, DMAX_Y_OS, + 30, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureQVGA, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + D_X, D_Y, + 30, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureQVGAOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + D_X_OS, D_Y_OS, + 30, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGA, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X, DMAX_Y, + 30, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGAOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X_OS, DMAX_Y_OS, + 30, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGA_60fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X, DMAX_Y, + 60, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGA_60fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X_OS, DMAX_Y_OS, + 60, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGA_60fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X, DMAX_Y, + 60, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGA_60fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X_OS, DMAX_Y_OS, + 60, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGA_90fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X, DMAX_Y, + 90, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGA_90fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X_OS, DMAX_Y_OS, + 90, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGA_90fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X, DMAX_Y, + 90, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGA_90fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X_OS, DMAX_Y_OS, + 90, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGA_120fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X, DMAX_Y, + 120, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatYUY2_CaptureVGA_120fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2), + DMAX_X_OS, DMAX_Y_OS, + 120, + 1, + 16, + FOURCC_YUY2) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGA_120fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X, DMAX_Y, + 120, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB24Bpp_CaptureVGA_120fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24), + DMAX_X_OS, DMAX_Y_OS, + 120, + 1, + 24, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGA, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X, DMAX_Y, + 30, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGAOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X_OS, DMAX_Y_OS, + 30, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGA_60fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X, DMAX_Y, + 60, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGA_60fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X_OS, DMAX_Y_OS, + 60, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGA_90fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X, DMAX_Y, + 90, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGA_90fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X_OS, DMAX_Y_OS, + 90, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGA_120fps, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X, DMAX_Y, + 120, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureVGA_120fpsOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + DMAX_X_OS, DMAX_Y_OS, + 120, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureQVGA, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + D_X, D_Y, + 30, + 1, + 32, + KS_BI_RGB) + +DEFINE_DATARANGE_VIDEO( + FormatRGB32Bpp_CaptureQVGAOverscan, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32), + D_X_OS, D_Y_OS, + 30, + 1, + 32, + KS_BI_RGB) + + +// +// CapturePinDataRanges: +// +// This is the list of data ranges supported on the capture pin. We support +// two: one RGB24, and one YUY2. +// +const +PKSDATARANGE +ImageCapturePinDataRanges[ IMAGE_CAPTURE_PIN_DATA_RANGE_COUNT ] = +{ + (PKSDATARANGE) &FormatYUY2_CaptureVGA, + (PKSDATARANGE) &FormatNV12_CaptureVGA +}; + +// +// CapturePinDataRanges: +// +// This is the list of data ranges supported on the capture pin. We support +// RGB24, YUY2, and RGB32 +// +const +PKSDATARANGE +VideoCapturePinDataRanges [VIDEO_CAPTURE_PIN_DATA_RANGE_COUNT] = +{ + (PKSDATARANGE) &FormatYUY2_CaptureQVGA, + (PKSDATARANGE) &FormatYUY2_CaptureQVGAOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA, + (PKSDATARANGE) &FormatYUY2_CaptureVGAOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGAOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGAOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fpsOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fpsOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fpsOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fpsOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fpsOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGAOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGA, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGAOverscan, +}; + +const +PKSDATARANGE +VideoPreviewPinDataRanges[VIDEO_PREVIEW_PIN_DATA_RANGE_COUNT] = +{ + (PKSDATARANGE) &FormatYUY2_CaptureQVGA, + (PKSDATARANGE) &FormatYUY2_CaptureVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGA, +}; diff --git a/AVStream/avscamera/sys/formats.h b/AVStream/avscamera/sys/formats.h new file mode 100644 index 00000000..e967e52b --- /dev/null +++ b/AVStream/avscamera/sys/formats.h @@ -0,0 +1,137 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + Formats.h + + Abstract: + + A list of available Image and Video formats. + + History: + + created 2/12/2016 + +**************************************************************************/ + +#pragma once + +#include "capture.h" + +#define DEFINE_DATARANGE_VIDEO(_RANGE_NAME_, _Subtype_, _X_, _Y_, _Rate_, _Planes_, _BitCount_, _Compression_) \ +const \ +KS_DATARANGE_VIDEO \ +_RANGE_NAME_ = \ +{ \ + \ + /* \ + // KSDATARANGE \ + */ \ + { \ + sizeof( KS_DATARANGE_VIDEO ), /* FormatSize */ \ + 0, /* Flags */ \ + ULONG((ULONGLONG(_X_) *_Y_ * _BitCount_)/8),/* SampleSize */ \ + 0, /* Reserved */ \ + \ + STATICGUIDOF( KSDATAFORMAT_TYPE_VIDEO ), /* aka. MEDIATYPE_Video */ \ + _Subtype_, /* aka. MEDIASUBTYPE_RGB24, */ \ + STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) /* aka. FORMAT_VideoInfo */ \ + }, \ + \ + TRUE, /* BOOL, bFixedSizeSamples (all samples same size?) */ \ + FALSE, /* BOOL, bTemporalCompression (all I frames?) */ \ + 0, /* Reserved (was StreamDescriptionFlags) */ \ + 0, /* Reserved (was MemoryAllocationFlags */ \ + /* (KS_VIDEO_ALLOC_*)) */ \ + /* */ \ + /* _KS_VIDEO_STREAM_CONFIG_CAPS */ \ + /* */ \ + { \ + STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), /* GUID */ \ + KS_AnalogVideo_None, /* AnalogVideoStandard */ \ + _X_,_Y_, /* InputSize, (the inherent size of the incoming signal */ \ + /* with every digitized pixel unique) */ \ + _X_,_Y_, /* MinCroppingSize, smallest rcSrc cropping rect allowed */ \ + _X_,_Y_, /* MaxCroppingSize, largest rcSrc cropping rect allowed */ \ + 8, /* CropGranularityX, granularity of cropping size */ \ + 1, /* CropGranularityY */ \ + 8, /* CropAlignX, alignment of cropping rect */ \ + 1, /* CropAlignY; */ \ + _X_, _Y_, /* MinOutputSize, smallest bitmap stream can produce */ \ + _X_, _Y_, /* MaxOutputSize, largest bitmap stream can produce */ \ + 8, /* OutputGranularityX, granularity of output bitmap size */ \ + 1, /* OutputGranularityY; */ \ + 0, /* StretchTapsX (0 no stretch, 1 pix dup, 2 interp...) */ \ + 0, /* StretchTapsY */ \ + 0, /* ShrinkTapsX */ \ + 0, /* ShrinkTapsY */ \ + (ONESECOND / _Rate_), /* MinFrameInterval, 100 nS units */ \ + 640000000, /* MaxFrameInterval, 100 nS units */ \ + ULONG(min(ULONG_MAX,(ULONGLONG(_X_) *_Y_ * _BitCount_)*_Rate_)),/* MinBitsPerSecond;*/ \ + ULONG(min(ULONG_MAX,(ULONGLONG(_X_) *_Y_ * _BitCount_)*_Rate_)) /* MaxBitsPerSecond;*/ \ + }, \ + \ + /* \ + // KS_VIDEOINFOHEADER (default format) \ + */ \ + { \ + 0,0,0,0, /* RECT rcSource; */ \ + 0,0,0,0, /* RECT rcTarget; */ \ + ULONG(min(ULONG_MAX,(ULONGLONG(_X_) *_Y_ * _BitCount_)*_Rate_)),/* DWORD dwBitRate;*/ \ + 0L, /* DWORD dwBitErrorRate; */ \ + (ONESECOND / _Rate_), /* REFERENCE_TIME AvgTimePerFrame; */ \ + sizeof( KS_BITMAPINFOHEADER ), /* DWORD biSize; */ \ + _X_, /* LONG biWidth; */ \ + _Y_, /* LONG biHeight; */ \ + _Planes_, /* WORD biPlanes; */ \ + _BitCount_, /* WORD biBitCount; */ \ + _Compression_, /* DWORD biCompression; */ \ + ULONG((ULONGLONG(_X_) *_Y_ * _BitCount_)/8),/* DWORD biSizeImage; */ \ + 0, /* LONG biXPelsPerMeter; */ \ + 0, /* LONG biYPelsPerMeter; */ \ + 0, /* DWORD biClrUsed; */ \ + 0 /* DWORD biClrImportant; */ \ + } \ +}; \ + + +extern const KS_DATARANGE_VIDEO FormatYUY2Image_Capture; +extern const KS_DATARANGE_VIDEO FormatNV12Image_Capture; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureQVGA; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureQVGAOverscan; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGA; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGAOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureQVGA; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureQVGAOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGA; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGAOverscan; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGA_60fps; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGA_60fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGA_60fps; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGA_60fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGA_90fps; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGA_90fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGA_90fps; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGA_90fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGA_120fps; +extern const KS_DATARANGE_VIDEO FormatYUY2_CaptureVGA_120fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGA_120fps; +extern const KS_DATARANGE_VIDEO FormatRGB24Bpp_CaptureVGA_120fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGA; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGAOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGA_60fps; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGA_60fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGA_90fps; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGA_90fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGA_120fps; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureVGA_120fpsOverscan; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureQVGA; +extern const KS_DATARANGE_VIDEO FormatRGB32Bpp_CaptureQVGAOverscan; + +extern const PKSDATARANGE VideoCapturePinDataRanges[VIDEO_CAPTURE_PIN_DATA_RANGE_COUNT]; +extern const PKSDATARANGE VideoPreviewPinDataRanges[VIDEO_PREVIEW_PIN_DATA_RANGE_COUNT]; +extern const PKSDATARANGE ImageCapturePinDataRanges[IMAGE_CAPTURE_PIN_DATA_RANGE_COUNT]; diff --git a/AVStream/avscamera/sys/hwsim.cpp b/AVStream/avscamera/sys/hwsim.cpp new file mode 100644 index 00000000..f00a4684 --- /dev/null +++ b/AVStream/avscamera/sys/hwsim.cpp @@ -0,0 +1,1409 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + hwsim.cpp + + Abstract: + + This file contains the implementation of CHardwareSimulation used to + simulate a camera's capture stream. + + Our simulation cannot mimic a real camera with any precision. A real + camera would likely have dedicated hardware to provide interrupts, do + DMA transfers of frames from an internal buffer as well as do scaling + and on-the-fly colorspace and planar format conversions so as to supply + frames for preview, video and photo in varying resolutions and + colorspaces. + + This simulation does not have such hardware, so instead it synthesizes + a frame on the fly for each local pin as required. The frame is + synthesized from common state information, so the images produced + should be comparable from pin to pin. CHardwareSimulation is used as + a base class for each simulated pin / hardware. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +inline +CHwSimTimer:: +CHwSimTimer( + _In_ CHardwareSimulation *Parent +) + : KPassiveTimer( Parent->m_Sensor->GetDeviceObject() ) + , m_Parent( Parent ) +/*++ + +Routine Description: + + CHwSimTimer object construction. + +Arguments: + + Parent - + The parent CHardwareSimulation. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); +} + +void +CHwSimTimer::Handler( + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + Punts to the HardwareSim... + +Arguments: + + Context - + Our parent CHardwareSimulation. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + ((CHardwareSimulation *) Context)->FakeHardware(); +} + +/*************************************************/ + +CHardwareSimulation:: +CHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : m_Sensor (Sensor) + , m_IsrTimer(this) + , m_ScatterGatherMappingsMax (SCATTER_GATHER_MAPPINGS_MAX) + , m_TimePerFrame(ONESECOND/30) + , m_PinID(PinID) + , m_Synthesizer(nullptr) + , m_Width(0) + , m_Height(0) + , m_ImageSize(0) + , m_PinState(PinStopped) + , m_NumMappingsCompleted(0) + , m_ScatterGatherMappingsQueued(0) + , m_ScatterGatherBytesQueued(0) + , m_NumFramesSkipped(0) + , m_InterruptTime(0) + , m_LastReportedExposureTime(DEF_EXPOSURE_TIME) // Assume the default exposure time for now. + , m_LastReportedWhiteBalance(0) + , m_FaceDetectionDelay(1) // Start out reporting immediately. + +/*++ + +Routine Description: + + CHardwareSimulation Construction. + +Arguments: + + Sensor - + The parent CSensor object that contains state for the controls. + PinID - + The pin index. + +Return Value: + + None + +--*/ +{ + + PAGED_CODE(); + + // + // Initialize the timer's, and locks necessary to simulate this hardware. + // + InitializeListHead (&m_ScatterGatherMappings); + + // + // Initialize the entry lookaside. + // + ExInitializeNPagedLookasideList ( + &m_ScatterGatherLookaside, + NULL, + NULL, + POOL_NX_ALLOCATION, + sizeof (SCATTER_GATHER_ENTRY), + 'nEGS', + 0 + ); + + m_StartTime.QuadPart = 0; +} + +CHardwareSimulation:: +~CHardwareSimulation() +{ + PAGED_CODE(); + + // Cancel the timer, just in case its running. + m_IsrTimer.Cancel(); + + // + // Delete the scatter / gather lookaside list. + // + ExDeleteNPagedLookasideList (&m_ScatterGatherLookaside); +} + +/*************************************************/ + +BOOLEAN +CHardwareSimulation:: +Initialize() + +/*++ + +Routine Description: + + Initialize the hardware simulation + +Arguments: + + [none] + +Return Value: + + TRUE - CHardwareSimulation is fully initialized. + +--*/ + +{ + PAGED_CODE(); + + return m_IsrTimer.IsValid() ; +} + +/*************************************************/ + +NTSTATUS +CHardwareSimulation:: +Start ( + IN CSynthesizer *ImageSynth, + IN LONGLONG TimePerFrame, + IN ULONG Width, + IN ULONG Height, + IN ULONG ImageSize +) + +/*++ + +Routine Description: + + Start the hardware simulation. This will kick the interrupts on, + begin issuing DPC's, filling in capture information, etc... + We keep track of starvation starting at this point. + +Arguments: + + ImageSynth - + The image synthesizer to use to generate pictures to display + on the capture buffer. + + TimePerFrame - + The time per frame... we issue interrupts this often. + + Width - + The image width + + Height - + The image height + + ImageSize - + The size of the image. We allocate a temporary scratch buffer + based on this size to fake hardware. + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + NTSTATUS Status = STATUS_SUCCESS; + + m_Synthesizer = ImageSynth; + m_TimePerFrame = TimePerFrame; + m_ImageSize = ImageSize; + m_Height = Height; + m_Width = Width; + + m_NumMappingsCompleted = 0; + m_ScatterGatherMappingsQueued = 0; + m_NumFramesSkipped = 0; + m_InterruptTime = 0; + + KeQuerySystemTime (&m_StartTime); + + if( !m_Synthesizer->Initialize() ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If everything is ok, start issuing interrupts. + // + if (NT_SUCCESS (Status)) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + m_TimePerFrame; + + m_PinState = PinRunning; + m_IsrTimer.Set( NextTime ); + } + + return Status; +} + +/*************************************************/ + +NTSTATUS +CHardwareSimulation:: +Pause ( + BOOLEAN Pausing +) + +/*++ + +Routine Description: + + Pause the hardware simulation... When the hardware simulation is told + to pause, it stops stops the timer, but it does not reset the counters. + +Arguments: + + Pausing - + Indicates whether the hardware is pausing or not. + + TRUE - + Pause the hardware + + FALSE - + Unpause the hardware from a previous pause + + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + { + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + if (Pausing && m_PinState == PinRunning) + { + // + // If we were running, stop completing mappings, etc... + // + m_PinState = PinPaused; + } + else if (!Pausing && m_PinState == PinPaused) + { + // + // For unpausing the hardware, we need to compute the relative time + // and restart interrupts. + // + LARGE_INTEGER UnpauseTime; + + KeQuerySystemTime (&UnpauseTime); + m_InterruptTime = (ULONG) ( + (UnpauseTime.QuadPart - m_StartTime.QuadPart) / + m_TimePerFrame + ); + + UnpauseTime.QuadPart = m_StartTime.QuadPart + + (m_InterruptTime + 1) * m_TimePerFrame; + + m_PinState = PinRunning; + m_IsrTimer.Set( UnpauseTime ); + } + } + + // If we are pausing, it's best to stop the timer and not wait for another frame. + if( Pausing ) + { + m_IsrTimer.Cancel(); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Stop() + +/*++ + +Routine Description: + + Stop the hardware simulation... + + For us, stop the timer, free the synthesizer and flush the queue. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + // + // If the hardware is told to stop while it's running, we need to + // halt the interrupts first. If we're already paused, this has + // already been done. + // + + DBG_ENTER("(): m_PinID=%d", m_PinID); + + BOOLEAN bCancel = FALSE; + + { + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + bCancel = (m_PinState == PinRunning); + m_PinState = PinStopped; + + // Free the synthesis buffer. + m_Synthesizer->Destroy(); + + // + // Free S/G buffer + // + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Stop SG List" ); + + m_ScatterGatherMappingsQueued=0; + m_NumMappingsCompleted = 0; + m_ScatterGatherBytesQueued = 0; + } + + // If running, stop the timer to make sure we don't try to deliver one last frame. + if( bCancel ) + { + m_IsrTimer.Cancel(); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Reset() +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + DBG_ENTER("(): m_PinID=%d", m_PinID); + + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Reset SG List" ); + + m_ScatterGatherMappingsQueued=0; + m_NumMappingsCompleted = 0; + m_ScatterGatherBytesQueued = 0; + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Reset (IN PKSPIN Pin) +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + if(Pin->ResetState == KSRESET_END) + { + KsPinAttemptProcessing(Pin, TRUE); + } + else + { + status = Reset(); + } + + return status; +} + +ULONG +CHardwareSimulation:: +ReadNumberOfMappingsCompleted() + +/*++ + +Routine Description: + + Read the number of scatter / gather mappings which have been + completed since the last reset of the simulated hardware + +Arguments: + + None + +Return Value: + + Total number of completed mappings. + +--*/ + +{ + PAGED_CODE(); + + return m_NumMappingsCompleted; +} + +/*************************************************/ + + +ULONG +CHardwareSimulation:: +ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER *Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount, + IN ULONG MappingStride +) + +/*++ + +Routine Description: + + Add a single entry to our queue. In practice our simulation can only + accept one frame at a time, but we still base this code off of the + avshws sample. + +Arguments: + + Buffer - + The virtual address of the buffer mapped by the mapping list + + ClonePointer + The KSStreamClone Pointer to be associated with the entry item + + Mappings - + The KSMAPPINGS array corresponding to the buffer + + MappingsCount - + The number of mappings in the mappings array + + MappingStride - + The mapping stride used in initialization of AVStream DMA + +Return Value: + + Number of mappings actually inserted. + +--*/ + +{ + PAGED_CODE(); + + ULONG MappingsInserted = 0; + + DBG_ENTER("(Clone=%p, Buffer=%p, MappingsCount=%d)", + Clone, Buffer, MappingsCount); + + // + // Protect our S/G list with a spinlock. + // + KScopedMutex Lock( m_ListLock ); + + // + // Loop through the scatter / gather list and break the buffer up into + // chunks equal to the scatter / gather mappings. Stuff the virtual + // addresses of these chunks on a list somewhere. We update the buffer + // pointer the caller passes as a more convenient way of doing this. + // + // If I could just remap physical in the list to virtual easily here, + // I wouldn't need to do it. + // + do + { + PSCATTER_GATHER_ENTRY Entry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + ExAllocateFromNPagedLookasideList ( + &m_ScatterGatherLookaside + ) + ); + + if (!Entry) + { + break; + } + + Entry ->CloneEntry = nullptr; + Entry -> Virtual = nullptr; + Entry -> ByteCount = 0; + + + Entry ->CloneEntry = *Clone; + Entry -> CloneEntry -> StreamHeader -> PresentationTime.Time = 0; + Entry -> CloneEntry -> StreamHeader -> OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + Entry -> Virtual = *Buffer; + Entry -> ByteCount = MappingsCount; + Entry->PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO(); + + // + // Move forward a specific number of bytes in chunking this into + // mapping sized va buffers. + // + *Buffer += MappingsCount; + Mappings = reinterpret_cast <PKSMAPPING> ( + (reinterpret_cast <PUCHAR> (Mappings) + MappingStride) + ); + + InsertTailList (&m_ScatterGatherMappings, &(Entry -> ListEntry)); + m_ScatterGatherMappingsQueued++; + MappingsInserted = MappingsCount; + DBG_TRACE("m_ScatterGatherMappingsQueued=%d", m_ScatterGatherMappingsQueued); + m_ScatterGatherBytesQueued += MappingsCount; + + } + while(FALSE); + + DBG_LEAVE(" MappingsInserted=%d", MappingsInserted ); + + return MappingsInserted; + +} + +/************************************************************************** + + The following are helper functions used in the production of metadata. + Since this is just a simulation, most "auto" modes get converted into + a random value that is in range. + + While simple, this still requires writing a helper function for each + piece of data. + +**************************************************************************/ + +// +// Translate Exposure settings into a "current value". +// +LONGLONG +CHardwareSimulation:: +GetCurrentExposureTime() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + LONGLONG Value = 0; + LPCSTR Mode = "[UNKNOWN]"; + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + // Get random value in global setting's bound (LONG) + // I'm abandoning the reported min/max and using something more reasonable. + Value = GetRandom( MIN_EXPOSURE_TIME*5, DEF_EXPOSURE_TIME*5 ); + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO"; + } + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = pSettings->ExposureSetting.VideoProc.Value.ll; + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL"; + } + + // Locked just reports the last value set... + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + Value = m_LastReportedExposureTime; + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK"; + } + + DBG_TRACE("ExposureMode=0x%016llX (%s), Time=%llu00ns, LastReported=%llu00ns", + pSettings->ExposureMode, Mode, Value, m_LastReportedExposureTime ); + + m_LastReportedExposureTime = Value; + return Value; +} + +// +// Translate White Balance into a "current value". +// +ULONG +CHardwareSimulation:: +GetCurrentWhiteBalance() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + ULONG Value = 0; + + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Value = (ULONG) GetRandom( pSettings->WhiteBalanceSetting.Min, pSettings->WhiteBalanceSetting.Max ); + } + + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = pSettings->WhiteBalanceSetting.VideoProc.Value.ul; + } + + // Locked just reports the last value set... + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + Value = m_LastReportedWhiteBalance; + } + + m_LastReportedWhiteBalance= Value; + return Value; +} + +// +// Translate ISO settings into a "current value". +// +ULONG +CHardwareSimulation:: +GetCurrentISOSpeed() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + ULONG Value = 0; + + if( pSettings->ISOMode & KSCAMERA_EXTENDEDPROP_ISO_AUTO ) + { + Value = GetRandom( (ULONG) 50, (ULONG) 3200 ); + } + else if( pSettings->ISOMode & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + Value = pSettings->ISOValue; + } + else + { + Value = // Try converting any legacy presets to a manual value. + IsoPreset2Value( pSettings->ISOMode ); + } + return Value; +} + +// +// Translate Exposure Mode to an Exif Exposure Program value +// +USHORT +CHardwareSimulation:: +GetExposureProgram() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + USHORT Value = 0; // Undefined. + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = 1; // Manual + } + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Value = 2; // Normal + } + + // TODO: Extend SceneMode to ISP settings so I can use it here to capture Portrait mode, etc. + return Value; +} + +/*************************************************/ + +// +// Emit metadata here that is common to all pins. +// +// All derived classes' EmitMetadata() function must +// call this function first. +// +void +CHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + + 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); + //PBYTE pData = (PBYTE) pMetadata->SystemVa; + //ULONG BytesLeft = pMetadata->BufferSize; + + // A real driver might write focus state or other info here. We've got nothing. + pMetadata->UsedSize = 0; + } + else + { + DBG_TRACE("Metadata not present..."); + } +} + +void +CHardwareSimulation:: +EmitFaceMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader, + _In_ ULONG Count, + _In_ ULONGLONG Flags, + _In_ ULONG DelayLimit +) +/*++ + +Routine Description: + + Helper function that emits FaceDetection metadata. + + Note: This function assumes a metadata buffer is present. + +Arguments: + + pStreamHeader - + The frame's stream header. + + Count - + The maximum number of faces to detect. + + Flags - + A combination of the KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK and + KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK flags passed to the face + detection control. Used to decide what characterization data to + present in the metadata. + + DelayLimit - + The maximum number of frames to delay before generating the next + face detection. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + NT_ASSERT(Count <= SIZEOF_ARRAY(m_LastFaceDetect.Data)); + + // Do nothing if we're turned on, but they want no faces. + if( Count==0 ) + { + return; + } + + if( --m_FaceDetectionDelay != 0 || // Count down our random delay for new face data. + Count > m_LastFaceDetect.Count ) // Make sure we never emit more faces than requested. + { + ULONG CountReported = GetRandom( 0, Count ); + + DBG_TRACE("Generating new faces (Count=%d, Flags=0x%016llX, Delay=%d)...", + Count, Flags, DelayLimit); + + // Synthesize a new set of "detected" faces... + m_LastFaceDetect.Header.MetadataId = (ULONG) MetadataId_Custom_FaceDetection; + m_LastFaceDetect.Header.Size = + sizeof(CAMERA_METADATA_FACEHEADER) + (sizeof(METADATA_FACEDATA) * CountReported); + + m_LastFaceDetect.Count = CountReported; + m_LastFaceDetect.Flags = Flags; + m_LastFaceDetect.Timestamp = pStreamHeader->PresentationTime.Time ; + + DBG_TRACE("Count=%d, Flags=0x%016llX, Time=0x%016llX", + m_LastFaceDetect.Count, + m_LastFaceDetect.Flags, + m_LastFaceDetect.Timestamp ); + + // Report face data... + for( ULONG i=0; i<CountReported; i++ ) + { + RtlZeroMemory( &m_LastFaceDetect.Data[i], sizeof(m_LastFaceDetect.Data[i]) ); + + RECT &rect = m_LastFaceDetect.Data[i].Region; + rect.top = GetRandom((LONG)0, TO_Q31(1)-2); + rect.left = GetRandom((LONG)0, TO_Q31(1)-2); + rect.bottom = GetRandom(rect.top+1, TO_Q31(1)-1); + rect.right = GetRandom(rect.left+1, TO_Q31(1)-1); + + m_LastFaceDetect.Data[i].confidenceLevel = GetRandom((ULONG)25, 100); // Don't bother reporting really low confidence faces. + + DBG_TRACE(" #%d: [(0x%08X,0x%08X), (0x%08X,0x%08X)](Conf=%d%%)", + i, + rect.top, rect.left, rect.bottom, rect.right, + m_LastFaceDetect.Data[i].confidenceLevel); + + if( Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK ) + { + m_LastFaceDetect.Data[i].BlinkScoreLeft = GetRandom((ULONG)0, 100); // Should really skew these numbers more in favor of no blink. + m_LastFaceDetect.Data[i].BlinkScoreRight = GetRandom((ULONG)0, 100); + + DBG_TRACE(" --- Blink(left=%d%%, right=%d%%)", + m_LastFaceDetect.Data[i].BlinkScoreLeft, + m_LastFaceDetect.Data[i].BlinkScoreRight); + } + + if( Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_SMILE ) + { + m_LastFaceDetect.Data[i].FacialExpression = GetRandom((ULONG)0, 1) ? EXPRESSION_SMILE : 0; + m_LastFaceDetect.Data[i].FacialExpressionScore = GetRandom((ULONG)0, 100); + + DBG_TRACE(" --- Smile(exp=%s, score=%d%%)", + (m_LastFaceDetect.Data[i].FacialExpression==EXPRESSION_SMILE ? "Smile":"[unknown]"), + m_LastFaceDetect.Data[i].FacialExpressionScore); + } + } + + // Randomly choose when to generate new facedetection data. + m_FaceDetectionDelay = GetRandom( 1, DelayLimit ); + DBG_TRACE("Delay to next is %d", m_FaceDetectionDelay); + } + + // The flags can change between face detection updates. + // Drop the additional characterization data immediately, if requested. + // We can hardly add it that fast since it might not be generated straight away. + ULONGLONG LostFlags = m_LastFaceDetect.Flags & ~Flags; + for( ULONG i=0; i<m_LastFaceDetect.Count; i++ ) + { + if( LostFlags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK ) + { + m_LastFaceDetect.Data[i].BlinkScoreLeft = 0; + m_LastFaceDetect.Data[i].BlinkScoreRight = 0; + } + if( LostFlags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_SMILE ) + { + m_LastFaceDetect.Data[i].FacialExpression = 0; + m_LastFaceDetect.Data[i].FacialExpressionScore = 0; + } + } + m_LastFaceDetect.Flags &= Flags; + + PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1); + PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO) (pFrameInfo + 1); + ULONG BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize; + + // Write Face Detection Info here + // Note: If we derived Video and Preview from a common base class, we + // might use that to emit this information for both types. + if( BytesLeft >= m_LastFaceDetect.Header.Size ) + { + // Always write the most recently generated face detection info. + RtlCopyMemory( + ((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize, + &m_LastFaceDetect, + m_LastFaceDetect.Header.Size ); + + DBG_TRACE("Emitting Face metadata..."); + + BytesLeft -= m_LastFaceDetect.Header.Size; + pMetadata->UsedSize += m_LastFaceDetect.Header.Size; + } + else + { + DBG_TRACE("WARNING: Out of metadata buffer..."); + } +} + +// +// PIN_STATE text for debug output. +// +static +const char * +GetPinStateTxt( PIN_STATE state ) +{ + PAGED_CODE(); + + switch( state ) + { + case PinStopped: + return "STOPPED"; + case PinPaused: + return "PAUSED"; + case PinRunning: + return "RUNNING"; + } + return "[UNKNOWN]"; +} + +NTSTATUS +CHardwareSimulation:: +FillScatterGatherBuffers() + +/*++ + +Routine Description: + + The hardware has synthesized a buffer and we're to fill a frame buffer. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("() m_PinState=%s, m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%u, " + "m_ScatterGatherBytesQueue=0x%08X", + GetPinStateTxt(m_PinState), + m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ULONG BufferRemaining = m_ImageSize; + + // + // If there isn't a frame buffer queued, we justskip the frame and consider + // it starvation. + // + while (BufferRemaining && + !IsListEmpty(&m_ScatterGatherMappings) && + m_ScatterGatherBytesQueued >= BufferRemaining) + { + LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + // Deal with cancellation. + PIRP pIrp = KsStreamPointerGetIrp(SGEntry->CloneEntry, FALSE, FALSE); + if (pIrp) + { + if (pIrp->Cancel) + { + DBG_TRACE( "Cancelling..." ); + FreeSGEntry( listEntry, L"StreamPointer Cancel SG List" ); + continue; + } + } + + // + // Since we're software, we'll be accessing this by virtual address... + // + ULONG Stride = 0; + if ( SGEntry->CloneEntry -> StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO)) + { + PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> (SGEntry->CloneEntry->StreamHeader+1); + Stride = (ULONG) ABS(FrameInfo->lSurfacePitch); + } + + // Have the synthesizer output a frame to the buffer. + ULONG BytesCopied = m_Synthesizer->DoCommit( SGEntry->Virtual, SGEntry->ByteCount, Stride ); + NT_ASSERT( BytesCopied ); + DBG_TRACE( "BytesCopied = %d", BytesCopied ); + + //Adding time stamp + if(m_PhotoConfirmationEntry.isRequired()) + { + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = + m_PhotoConfirmationEntry.getTime(); + DBG_TRACE("Using Photo Confirmation time = 0x%016llX", m_PhotoConfirmationEntry.getTime()); + } + else + { + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = ConvertQPCtoTimeStamp(NULL); + DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + } + + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Numerator = + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Denominator = 1; + + SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID ; + + // Add metadata to the sample. + EmitMetadata( SGEntry -> CloneEntry -> StreamHeader ); + + BufferRemaining = 0; //-= BytesCopied; + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast <PVOID> (SGEntry) + ); + + } + + // Report an error if we used the last buffer. + if (BufferRemaining) + { + //DBG_TRACE("BufferRemaining=%u", BufferRemaining); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + DBG_LEAVE("() = 0x%08X", ntStatus); + return ntStatus; +} + +/************************************************************************** + + Debug helpers + +**************************************************************************/ + +PCCHAR +DVS_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_ON: return "On"; + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_AUTO: return "Auto"; + default: + { + static + CHAR buffer[32]; + + RtlStringCbPrintfA(buffer, sizeof(buffer), "Unknown [0x%016llX]", Flags); + return (PCCHAR) buffer; + } + } +} + +PCCHAR +OIS_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_OIS_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_OIS_ON: return "On"; + case KSCAMERA_EXTENDEDPROP_OIS_AUTO: return "Auto"; + default: + { + static + CHAR buffer[32]; + + RtlStringCbPrintfA(buffer, sizeof(buffer), "Unknown [0x%016llX]", Flags); + return (PCCHAR) buffer; + } + } +} + +/**************************************************************************/ + +void +CHardwareSimulation:: +FakeHardware ( +) + +/*++ + +Routine Description: + + Simulate an interrupt and what the hardware would have done in the + time since the previous interrupt. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + + m_InterruptTime++; + + // + // The hardware can be in a pause state in which case, it issues interrupts + // but does not complete mappings. In this case, don't bother synthesizing + // a frame and doing the work of looking through the mappings table. + // + if (m_PinState == PinRunning) + { + // + // Generate a "time stamp" just to overlay it onto the capture image. + // It makes it more exciting than bars that do nothing. + // + ULONGLONG time = ConvertQPCtoTimeStamp(NULL); + DBG_TRACE("QPC=0x%016llX", time); + + m_Synthesizer->SetFrameNumber( m_InterruptTime ); + m_Synthesizer->SetRelativePts( (m_InterruptTime + 1) * m_TimePerFrame ); + m_Synthesizer->SetQpcTime( time ); + + m_Synthesizer->DoSynthesize(); + + CHAR Text[64]; + + CExtendedProperty 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_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 ); + + // + // Fill scatter gather buffers + // + if (!NT_SUCCESS (FillScatterGatherBuffers ())) + { + InterlockedIncrement (PLONG (&m_NumFramesSkipped)); + } + + } + + // + // Issue an interrupt to our hardware sink. This is a "fake" interrupt. + // It will occur at DISPATCH_LEVEL. + // + m_Sensor -> Interrupt (m_PinID); + + // + // Schedule the timer for the next interrupt time, if the pin is still running. + // + if( m_PinState == PinRunning ) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + + (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 ) + { + NextTime.QuadPart = 0LL - m_TimePerFrame ; + } + +#endif + m_IsrTimer.Set( NextTime ); + } +} + +ULONGLONG +CHardwareSimulation:: +ConvertQPCtoTimeStamp( + _In_opt_ PLARGE_INTEGER pQpcTime +) +/*++ + +Routine Description: + + Get the QPC measured in 100ns units. + +Arguments: + + pQpcTime - + An optional time value to convert. If NULL, use the current QPC. + +Return Value: + + ULONLONG - + The time in 100ns increments. + +--*/ +{ + PAGED_CODE(); + + LARGE_INTEGER qpcTime = {0}; + LARGE_INTEGER qpcFrequency; + + qpcTime = KeQueryPerformanceCounter(&qpcFrequency); + + if( pQpcTime ) + { + qpcTime = *pQpcTime; + } + + return KSCONVERT_PERFORMANCE_TIME( qpcFrequency.QuadPart, qpcTime ); +} + +NTSTATUS +CHardwareSimulation:: +GeneratePhotoConfirmation( + _In_ ULONG PfsFrameNumber, + _In_ LONGLONG time +) +/*++ + +Routine Description: + + So a photo sim can ask a preview sim for a confirmation frame. + +Arguments: + + PfsFrameNumber - + The VPS shot number. + time - + The precise time of the associated photo. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + + if( PinRunning != m_PinState ) + { + return STATUS_DEVICE_NOT_READY; + } + + DBG_ENTER("( Index=%d, Time=0x%016llX )", PfsFrameNumber, time ); + + m_PhotoConfirmationEntry = PHOTOCONFIRMATION_INFO( PfsFrameNumber, time ); + + // We basically used the previously synthesized preview image + NTSTATUS status = FillScatterGatherBuffers(); + + //Clean up the photo confiramtion flag + m_PhotoConfirmationEntry = PHOTOCONFIRMATION_INFO(); + + if(NT_SUCCESS(status)) + { + m_Sensor->Interrupt (m_PinID); + } + + DBG_LEAVE("() = 0x%08X", status); + + return status; +} + +void +CHardwareSimulation:: +FreeSGEntry( + _In_ PLIST_ENTRY Entry, + _In_ PCWSTR EventName, + _In_opt_ LPCGUID Activity, + _In_ ULONG DataUsed +) +{ + PAGED_CODE(); + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + Entry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + NT_ASSERT(SGEntry->CloneEntry); + NT_ASSERT(SGEntry->CloneEntry->StreamHeader); + + SGEntry->CloneEntry->StreamHeader->DataUsed = DataUsed; + + KsStreamPointerDelete(SGEntry->CloneEntry); + + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + SGEntry + ); +} + +void +CHardwareSimulation:: +FreeSGList( + _In_ PLIST_ENTRY List, + _In_ PCWSTR EventName, + _In_opt_ LPCGUID Activity +) +{ + PAGED_CODE(); + + while(!IsListEmpty(List)) + { + PLIST_ENTRY head = RemoveHeadList(List); + m_ScatterGatherMappingsQueued--; + FreeSGEntry( head, EventName, Activity ); + } +} + +// +// Get the current frame settings. +// +ISP_FRAME_SETTINGS * +CHardwareSimulation:: +GetIspSettings(void) +{ + PAGED_CODE(); + + return m_Sensor->GetGlobalIspSettings(); +} diff --git a/AVStream/avscamera/sys/hwsim.h b/AVStream/avscamera/sys/hwsim.h new file mode 100644 index 00000000..247de1f0 --- /dev/null +++ b/AVStream/avscamera/sys/hwsim.h @@ -0,0 +1,316 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + hwsim.h + + Abstract: + + This file contains the definition of the base class, CHardwareSimulation, + used to simulate a camera's capture stream. + + Our simulation cannot mimic a real camera with any precision. A real + camera would likely have dedicated hardware to provide interrupts, do + DMA transfers of frames from an internal buffer as well as do scaling + and on-the-fly colorspace and planar format conversions so as to supply + frames for preview, video and photo in varying resolutions and + colorspaces. + + This simulation does not have such hardware, so instead it synthesizes + a frame on the fly for each local pin as required. The frame is + synthesized from common state information, so the images produced + should be comparable from pin to pin. CHardwareSimulation is used as + a base class for each simulated pin / hardware. + + A CHardwareSimulation contains state, a list of frame bufferss and a + timer that is used in place of a hardware interrupt. The timer + schedules a workitem that is used to synthesize an image, select a + buffer and copy that image into the buffer. + + Specializations of CHardwareSimulation provide differing metadata and + implement specialized functionality for photo sequence and variable + photo sequence. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#define SCATTER_GATHER_MAPPINGS_MAX 128 + +// forward class references +class CSensor; +class CHardwareSimulation; +class CHwSimTimer; + +// internal structure definitions + +// Face metadata structure. Face metadata can be synthesized on any pin. +class CAMERA_METADATA_FACE_DETECTION + : public CAMERA_METADATA_FACEHEADER +{ +public: + METADATA_FACEDATA Data[MAX_FACES]; +}; + +// Information about each frame buffer in the queue. +typedef struct _SCATTER_GATHER_ENTRY +{ + LIST_ENTRY ListEntry; + PKSSTREAM_POINTER CloneEntry; + PUCHAR Virtual; + ULONG ByteCount; + PHOTOCONFIRMATION_INFO PhotoConfirmationInfo; +} SCATTER_GATHER_ENTRY, *PSCATTER_GATHER_ENTRY; + +// +// A specialization of a KPassiveTimer that holds a back pointer to the +// CHardwareSimulation that owns it. +// +class CHwSimTimer + : public KPassiveTimer +{ + CHardwareSimulation *m_Parent; + +public: + CHwSimTimer( + _In_ CHardwareSimulation *Parent + ); + + // + // Sets the timer + // + BOOLEAN + Set( + _In_ LARGE_INTEGER DueTime + ); + +private: + static + void + Handler( + _In_opt_ PVOID Context + ); +}; + +class CHardwareSimulation +{ +protected: + CSynthesizer *m_Synthesizer; // Synthesizer for various formats. + LONGLONG m_TimePerFrame; + ULONG m_Width; + ULONG m_Height; + ULONG m_ImageSize; + LONG m_PinID; + KMutex m_ListLock; + LIST_ENTRY m_ScatterGatherMappings; + + NPAGED_LOOKASIDE_LIST m_ScatterGatherLookaside; + PIN_STATE m_PinState; + ULONG m_ScatterGatherMappingsMax; + ULONG m_NumMappingsCompleted; + ULONG m_ScatterGatherMappingsQueued; + ULONG m_ScatterGatherBytesQueued; + ULONG m_NumFramesSkipped; + ULONG m_InterruptTime; + LARGE_INTEGER m_StartTime; + + CSensor *m_Sensor; + CHwSimTimer m_IsrTimer; + + PHOTOCONFIRMATION_INFO m_PhotoConfirmationEntry; + + // Cached values used for Locking simulated state. + LONGLONG m_LastReportedExposureTime; + ULONG m_LastReportedWhiteBalance; + + // Used to simulate the skipping of frames when reporting face detection data. + ULONG m_FaceDetectionDelay; + CAMERA_METADATA_FACE_DETECTION m_LastFaceDetect; + + // Copy a synthesized framebuffer. + virtual + NTSTATUS + FillScatterGatherBuffers(); + + // Overloadable function for attaching metadata to a frame. + virtual + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + + // Helper function to generate random face metadata. + void + EmitFaceMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader, + _In_ ULONG Count, + _In_ ULONGLONG Flags, + _In_ ULONG DelayLimit=30 + ); + + // Release an entry from our queue. + void + FreeSGEntry( + _In_ PLIST_ENTRY Entry, + _In_ PCWSTR EventName=L"StreamPointer freed", + _In_opt_ LPCGUID Activity=NULL, + _In_ ULONG DataUsed=0 + ); + + // Release our entire queue. + void + FreeSGList( + _In_ PLIST_ENTRY List, + _In_ PCWSTR EventName=L"StreamPointer freed", + _In_opt_ LPCGUID Activity=NULL + ); + +public: + + // Debug helper + LONG GetSkippedFrameCount() + { + return m_NumFramesSkipped; + } + + // Constructor + CHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + // Destructor + virtual + ~CHardwareSimulation (); + + // Periodic interrupt handling + virtual + void + FakeHardware ( + ); + + // Start streaming. + virtual + NTSTATUS + Start ( + CSynthesizer *ImageSynth, + IN LONGLONG TimePerFrame, + IN ULONG Width, + IN ULONG Height, + IN ULONG ImageSize + ); + + // Pause streaming + virtual + NTSTATUS + Pause ( + IN BOOLEAN Pausing + ); + + // Stop streaming. + virtual + NTSTATUS + Stop(); + + // Reset streaming. + virtual + NTSTATUS + Reset( + IN PKSPIN Pin + ); + + // Reset streaming. + virtual + NTSTATUS + Reset(); + + // Add a frame buffer to our queue. + virtual + ULONG + ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER *Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount, + IN ULONG MappingStride + ); + + // Initialization. + virtual + BOOLEAN + Initialize(); + + // Number of frames completed during this run. + ULONG + ReadNumberOfMappingsCompleted(); + + // Get the QPC measured in 100ns units. + ULONGLONG + ConvertQPCtoTimeStamp( + _In_opt_ PLARGE_INTEGER pQpcTime + ); + + // Get the current frame settings. + virtual + ISP_FRAME_SETTINGS * + GetIspSettings(void); + + // So a photo sim can ask a preview sim for a confirmation frame. + NTSTATUS + GeneratePhotoConfirmation( + _In_ ULONG PfsFrameNumber, + _In_ LONGLONG time + ); + + // Get the exposure value for the current frame. + LONGLONG + GetCurrentExposureTime(); + + // Get the White balance for the current frame. + ULONG + GetCurrentWhiteBalance(); + + // Get the ISO Speed value for the current frame. + ULONG + GetCurrentISOSpeed(); + + // Get the Lens position for the current frame. + ULONG + GetCurrentLensPosition(); + + // Get the Scene Mode for the current frame. + ULONG + GetCurrentSceneMode(); + + // Translate Exposure Mode to an Exif Exposure Program value + USHORT + GetExposureProgram(); + + // Pin state accessor... + PIN_STATE + GetState() + { + return m_PinState; + } + + friend class CHwSimTimer; +}; + +// +// Sets the timer using our callback and our parent CHardwareSimulation. +// +inline +BOOLEAN +CHwSimTimer:: +Set( + _In_ LARGE_INTEGER DueTime +) +{ + return KPassiveTimer::Set( DueTime, Handler, m_Parent ); +} diff --git a/AVStream/avscamera/sys/imagecapture.cpp b/AVStream/avscamera/sys/imagecapture.cpp new file mode 100644 index 00000000..06d334d3 --- /dev/null +++ b/AVStream/avscamera/sys/imagecapture.cpp @@ -0,0 +1,155 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + imagecapture.cpp + + Abstract: + + An implementation of CImageCapturePin + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "Common.h" + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CImageCapturePin:: +CImageCapturePin( + _In_ PKSPIN Pin +) : CCapturePin (Pin) +{ + PAGED_CODE(); +} + +NTSTATUS +CImageCapturePin:: +DispatchCreate( + _In_ PKSPIN Pin, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static member function called when a pin is created. Instantiate a + CImageCapturePin and attach it to our filter object. + +Arguments: + + Pin - + The KSPIN to wrap. + + Irp - + The IRP assocated with this request. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER("(Pin=%d)", Pin->Id); + + NTSTATUS Status = STATUS_SUCCESS; + + CCaptureFilter* pFilter = reinterpret_cast <CCaptureFilter*>(KsPinGetParentFilter(Pin)->Context); + CImageCapturePin *CapPin = new (NonPagedPoolNx) CImageCapturePin (Pin); + + if( !CapPin ) + { + // Fail if we couldn't create the pin. + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // Query the filter for the PhotoMode so we can figure out how many frames should be allocated. + CExtendedPhotoMode Mode; + Mode.PinId = Pin->Id; // Use Pin ID from KS. + Status = pFilter->GetPhotoMode( &Mode ); + + // CSensor::GetPhotoMode could fail if the framework gives us a Pin ID that doesn't match our descriptors... + if( NT_SUCCESS(Status) ) + { + // Override the number of frames. + CapPin->SetDesiredFrames( + ( (Mode.Flags & KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE) ? + Mode.RequestedHistoryFrames() : 1) + + IMAGE_CAPTURE_PIN_MINIMUM_FRAMES ); + + Status = + CapPin->Initialize(); + } + } + + if (NT_SUCCESS (Status)) + { + // + // Adjust the stream header size. The video packets have extended + // header info (KS_FRAME_INFO). + // + pFilter->setPin(CapPin, Pin->Id); + } + else + { + // Clean up. + delete CapPin; + } + + DBG_LEAVE("(Pin=%d)=0x%08X", Pin->Id, Status); + return Status; +} + + +NTSTATUS +CImageCapturePin:: +Close( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + Reset(); + + return STATUS_SUCCESS; +} + +// ImageCapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// just a copy of CapturePinDispatch for now. +// +DEFINE_CAMERA_KSPIN_DISPATCH( ImageCapturePinDispatch, CImageCapturePin ); + + +// +// ImagePinAllocatorFraming: +// +// This is the simple framing structure for the capture pin. Note that this +// will be modified via KsEdit when the actual capture format is determined. +// just a copy of CapturePinAllocatorFraming for now. +// +DECLARE_SIMPLE_FRAMING_EX ( + ImageCapturePinAllocatorFraming, + STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 4, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE +); + diff --git a/AVStream/avscamera/sys/imagecapture.h b/AVStream/avscamera/sys/imagecapture.h new file mode 100644 index 00000000..1e11c31a --- /dev/null +++ b/AVStream/avscamera/sys/imagecapture.h @@ -0,0 +1,44 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + imagecapture.h + + Abstract: + + Image Capture Pin - A specialization of a capture pin. + + CImageCapturePin defines a unique list of data ranges for the image + pin. + + History: + + created 3/8/2001 + +**************************************************************************/ +#pragma once + +#include "capture.h" + +class CImageCapturePin : public CCapturePin +{ +public: + static NTSTATUS DispatchCreate( + _In_ PKSPIN Pin, + _In_ PIRP Irp + ); + +private: + virtual + NTSTATUS Close( + _In_ PIRP Irp + ); + + CImageCapturePin( + _In_ PKSPIN Pin + ); +}; diff --git a/AVStream/avscamera/sys/imagehwsim.cpp b/AVStream/avscamera/sys/imagehwsim.cpp new file mode 100644 index 00000000..e6256d45 --- /dev/null +++ b/AVStream/avscamera/sys/imagehwsim.cpp @@ -0,0 +1,1509 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + ImageHwSim.cpp + + Abstract: + + This file contains the implementation of the CImageHardwareSimulation + class. + + This is a specialization of CHardwareSimulation that provides photo- + specific metadata and implements specialized functionality for photo, + photo sequence and variable photo sequence. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CImageHardwareSimulation:: +CImageHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : CHardwareSimulation( Sensor, PinID ) + , m_Clock(NULL) + , m_PfsLoopLimit(0) + , m_PfsFrameLimit(0) + , m_pIspSettings(nullptr) + , m_PfsLoopNumber(0) + , m_PfsFrameNumber(0) + , m_GlobalFrameNumber(0) + , m_bEndOfSequence(FALSE) + , m_PastBufferCount(0) // Zero only when the simulation inits. + +/*++ + +Routine Description: + + Construct a hardware simulation + +Arguments: + + Sensor - + The hardware sink interface. This is used to trigger + fake interrupt service routines from. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + InitializeListHead (&m_BurstList); +} + +CImageHardwareSimulation:: +~CImageHardwareSimulation() +{ + + PAGED_CODE(); + + SAFE_DELETE( m_pIspSettings ); + + if (m_Clock) + { + m_Clock -> Release (); + m_Clock = NULL; + } +} + +/*************************************************/ + +NTSTATUS +CImageHardwareSimulation:: +Start ( + _In_ CSynthesizer *ImageSynth, + _In_ ULONG Width, + _In_ ULONG Height, + _In_ ULONG ImageSize, + _In_ PIN_MODE pinMode +) + +/*++ + +Routine Description: + + Start capturing frames. This turns on the timer and begins frame capture, + but it we do not deliver frames until we receive a Trigger. We keep track + of starvation starting at this point. + +Arguments: + + ImageSynth - + The image synthesizer to use to generate pictures to display + on the capture buffer. + + Width - + The image width + + Height - + The image height + + ImageSize - + The size of the image. We allocate a temporary scratch buffer + based on this size to fake hardware. + + PinMode - + Normal or photosequence. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + + PAGED_CODE(); + + DBG_ENTER("(Width=%d, Height=%d, ImageSize=%d, pinMode=%s): m_PinID=%d", + Width, Height, ImageSize, pinMode?"PinBurstMode":"PinNormalMode", m_PinID ); + + NTSTATUS Status = STATUS_SUCCESS; + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_Synthesizer = ImageSynth; + + NT_ASSERT(ImageSize); + m_ImageSize = ImageSize; + m_Height = Height; + m_Width = Width; + + m_NumMappingsCompleted = 0; + m_ScatterGatherMappingsQueued = 0; + m_NumFramesSkipped = 0; + m_InterruptTime = 0; + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_PinMode = pinMode; + m_TriggerTime = 0; + m_bFlashed = FALSE; + + DBG_TRACE("m_bTriggered=FALSE, m_bPastBufferTrigger=FALSE"); + + m_FlashStatus = 0; + + // Initialize VPS counters. + m_PfsLoopNumber = 0 ; + m_PfsFrameNumber= 0 ; + m_GlobalFrameNumber = 0; + + KeQuerySystemTime (&m_StartTime); + + if( !m_Synthesizer->Initialize() ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If everything is ok, start issuing interrupts. + // + if (NT_SUCCESS (Status)) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + m_TimePerFrame; + + m_PinState = PinRunning; + m_IsrTimer.Set( NextTime ); + + } + + DBG_LEAVE("(Width=%d, Height=%d, ImageSize=%d, pinMode=%s): m_PinID=%d, Status=0x%08X", + Width, Height, ImageSize, pinMode?"PinBurstMode":"PinNormalMode", m_PinID, Status ); + + return Status; + +} + +NTSTATUS +CImageHardwareSimulation:: +Trigger( + _In_ LONG mode +) +/*++ + +Routine Description: + + Take a picture / Begin delivering frames. + +Arguments: + + mode - + Normal trigger, start or stop photo sequence. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + + DBG_ENTER( "(mode=0x%08X), m_PinMode=%d", mode, m_PinMode ); + + KScopedMutex Lock( m_ListLock ); + + //Start Trigger for Burst Mode + if(mode & KS_VideoControlFlag_StartPhotoSequenceCapture) + { + if(m_bTriggered == FALSE && m_PinMode == PinBurstMode) + { + m_bPastBufferTrigger = TRUE; + m_bTriggered = TRUE; + DBG_TRACE("m_bTriggered=TRUE, m_bPastBufferTrigger=TRUE"); + + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + //Stop Trigger for Burst Mode + else if(mode & KS_VideoControlFlag_StopPhotoSequenceCapture) + { + if(m_bTriggered == TRUE && m_PinMode == PinBurstMode) + { + m_bTriggered = FALSE; + DBG_TRACE("m_bTriggered=FALSE"); + + // reset the PFS EOS, frame number and loop count. + m_bEndOfSequence = FALSE; + m_PfsFrameNumber = 0; + m_PfsLoopNumber = 0; + + m_bFlashed = FALSE; + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + //Normal Trigger + else if(mode & KS_VideoControlFlag_Trigger ) + { + if(m_PinMode == PinBurstMode) + { + status = STATUS_INVALID_PARAMETER; + } + else + { + m_bTriggered = TRUE; + m_bEndOfSequence = FALSE; + DBG_TRACE("m_bTriggered=TRUE"); + } + } + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; +} + + +LONG +CImageHardwareSimulation:: +GetTriggerMode() +/*++ + +Returns the pin's current trigger mode and state: + + Return Value: + + mode - + Normal trigger, start or stop photo sequence. + +--*/ + +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + LONG mode = 0; + + DBG_ENTER( "(), m_PinMode=%d", m_PinMode ); + + KScopedMutex Lock( m_ListLock ); + + if( m_bTriggered ) + { + mode = (m_PinMode == PinBurstMode) ? + KS_VideoControlFlag_StartPhotoSequenceCapture : + KS_VideoControlFlag_Trigger; + } + + DBG_LEAVE("()=0x%08X", mode); + + return mode; +} + + +NTSTATUS +CImageHardwareSimulation:: +SetMode( + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers +) +/*++ + +Routine Description: + + Set the photo sequence mode. + +Arguments: + + Flags - + Normal or photo sequence. + + PastBuffers - + Number of history frames to gather. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER( "(Flags=0x%016llX, PastBuffers=%d)", Flags, PastBuffers ); + + if(m_bTriggered == TRUE) + { + return STATUS_INVALID_TRANSACTION; + } + + m_PastBufferCount = PastBuffers; + + if(Flags & KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE) + { + m_PinMode = PinBurstMode; + } + else + { + m_PinMode = PinNormalMode; + } + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; +} + +// +// Set the interval between frames here. +// +NTSTATUS +CImageHardwareSimulation:: +SetPhotoFrameRate( + _In_ ULONGLONG TimePerFrame +) +{ + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_TimePerFrame = TimePerFrame; + + // + // Reschedule the timer if the hardware isn't being stopped. + // + if( m_PinState == PinRunning ) // && !m_StopHardware ) + { + // First restart our start time. We can't use the old time. + KeQuerySystemTime( &m_StartTime ); + + // + // Reschedule the timer for the next interrupt time. + // + m_StartTime.QuadPart += m_TimePerFrame; + m_InterruptTime = 0; + + m_IsrTimer.Set( m_StartTime ); + } + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +Stop() + +/*++ + +Routine Description: + + Stop the hardware simulation... + + Wait until the timer has stopped, flush the queue, dereference the clock + and reset our state before returning. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + // If the hardware is told to stop while it's running, we need to + // halt the interrupts first. If we're already paused, this has + // already been done. + // + + DBG_ENTER("(): m_PinID=%d", m_PinID); + + // + // Protect the S/G list + // + KScopedMutex Lock( m_ListLock ); + + CHardwareSimulation::Stop(); + + // + // Free S/G buffer + // + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Stop Burst List" ); + + if (m_Clock) + { + m_Clock -> Release (); + m_Clock = NULL; + } + + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_PinMode = PinNormalMode; + m_TriggerTime = 0; + + DBG_TRACE("m_bTriggered=FALSE, m_bPastBufferTrigger=FALSE"); + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + + return STATUS_SUCCESS; +} + + +/*************************************************/ + + +// +// Helper function that collects current settings into our metadata structure. +// +METADATA_IMAGEAGGREGATION +CImageHardwareSimulation:: +GetMetadata() +{ + PAGED_CODE(); + + METADATA_IMAGEAGGREGATION Metadata; + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + // Wipe the metadata so all settings will default to "Not Set". + RtlZeroMemory( &Metadata, sizeof(Metadata) ); + + // Identify the current PFS frame number. + // If PFS not active, then this item is not present. + Metadata.FrameId.Set = IsPfsActive(); + Metadata.FrameId.Value = (ULONG) m_PfsFrameNumber; + DBG_TRACE("Metadata.FrameId.Set=%s, Metadata.FrameId.Value=%d", + (Metadata.FrameId.Set?"Yes":"No"), Metadata.FrameId.Value); + + // Just reflect the exposure time from the setting. + //Metadata.ExposureTime.Set = TRUE; + //Metadata.ExposureTime.Value = GetCurrentExposureTime(); + + // Just reflect the ISO Speed from the setting. + Metadata.ISOSpeed = CMetadataLong(GetCurrentISOSpeed()); + DBG_TRACE("ISO=%d, ISO Flags=0x%016llX", Metadata.ISOSpeed.Value, pSettings->ISOMode); + + // TODO: Do we need to bracket this by whether or not a flash has been taken? + // Report the current flash mode. + Metadata.FlashOn = CMetadataLong((ULONG) pSettings->FlashMode); + + // Report the current flash power. + Metadata.FlashPower = CMetadataLong(pSettings->FlashValue); + + // Set the White Balance lock state. + Metadata.WhiteBalanceLocked = CMetadataLong( + ( (pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) + == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ); + + // Set the Exposure lock state. + Metadata.ExposureLocked = CMetadataLong( + ( (pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) + == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ); + + Metadata.ExposureTime = + //CMetadataRational(GetCurrentExposureTime(), 10000000); + CMetadataLongLong( GetCurrentExposureTime() ); + + Metadata.LensPosition = CMetadataLong( pSettings->FocusSetting.VideoProc.Value.ul ); + + Metadata.SceneMode = CMetadataULongLong(KSCAMERA_EXTENDEDPROP_SCENEMODE_AUTO); //TODO: Need to fill in real value from CCaptureFilter::m_SceneMode + + Metadata.WhiteBalanceMode = CMetadataLong((ULONG) pSettings->WhiteBalanceMode); + + CExtendedVidProcSetting Zoom; + m_Sensor->GetZoom( &Zoom ); + Metadata.ZoomFactor = CMetadataLong(Zoom.GetLONG()); //TODO: Fill in a real value from zoom simulation. + + Metadata.FocusLocked = CMetadataLong(FALSE); //TODO: Fill in a real value when we complete the focus changes. + + // Add EVCompensation metadata... + Metadata.EVCompensation = CMetadataEVCompensation(pSettings->EVCompensation.Mode, pSettings->EVCompensation.Value); + + Metadata.Orientation = CMetadataShort(Metadata_Orientation_TopBottomLeftRight); //TODO: Randomize? + + { + LARGE_INTEGER SystemTime; + LARGE_INTEGER LocalTime; + + KeQuerySystemTimePrecise( &SystemTime ); + ExSystemTimeToLocalTime( &SystemTime, &LocalTime ); + RtlTimeToTimeFields( &LocalTime, &Metadata.LocalTime.Time ); + Metadata.LocalTime.Set = TRUE; + } + + Metadata.Make = CMetadataShortString("Make: Microsoft SOC Camera"); + Metadata.Model = CMetadataShortString( "Model: AvsCam" ); + Metadata.Software = CMetadataShortString( "Software: Microsoft Camera Sim" ); + + Metadata.ColorSpace.Set = TRUE; + Metadata.ColorSpace.Value = 0xFFFF; // 0xFFFF Means "uncalibrated". Use this value for all non-RGB formats. + + Metadata.Gamma = CMetadataRational(); + + Metadata.MakerNote = CMetadataShortString( "Maker's Note..." ); + + // Just reflect the exposure time from the setting. + //Metadata.ExposureTime = + // CMetadataRational( GetCurrentExposureTime(), 1000 ); // report exposure time as milliseconds. + + Metadata.FNumber = CMetadataRational(4); // Fake an FNumber of 4. // TODO: It looks like we might be able to calculate this. + + Metadata.ExposureProgram.Set = TRUE; + Metadata.ExposureProgram.Value = GetExposureProgram(); + + Metadata.ShutterSpeedValue = CMetadataSRational(); // TODO: Calculate this from the ExposureTime. *** + Metadata.Aperture = CMetadataRational(4); // TODO: Calculate this from the F-Number. (We're currently faking the FNumber.) + Metadata.Brightness = CMetadataSRational(0); // TODO: Find a more reasonable brightness value. + Metadata.ExposureBias = CMetadataSRational(0); // TODO: More reasonable? + Metadata.SubjectDistance = CMetadataRational(0xFFFFFFFF); // Distance in meters. Infinity. (Anything better?) + + Metadata.MeteringMode.Set = TRUE; + Metadata.MeteringMode.Value = (USHORT) GetRandom( (ULONG) 1, (ULONG) 6); // Pick a number ... any number. + + Metadata.LightSource.Set = TRUE; + Metadata.LightSource.Value = 1; // TODO: Pick a random value; but override when a flash occurs. + + Metadata.Flash.Set = TRUE; + Metadata.Flash.Value = (UINT16) pSettings->FlashMode; // We assume that the flash fires when requested! + DBG_TRACE("FlashMode=0x%016llX, FlashPower=%d", pSettings->FlashMode, pSettings->FlashValue); + + Metadata.FocalLength = CMetadataRational(); // TODO: Calculate? + Metadata.FocalPlaneXResolution = CMetadataRational(); // TODO: Calculate? + Metadata.FocalPlaneYResolution = CMetadataRational(); // TODO: Calculate? + Metadata.ExposureIndex = CMetadataRational(); // TODO: Calculate? + + Metadata.ExposureMode.Set = TRUE; + Metadata.ExposureMode.Value = 0 ; // Assume Auto exposure. + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Metadata.ExposureMode.Value = 0 ; // Manual exposure. + } + + Metadata.WhiteBalance.Set = TRUE; + Metadata.WhiteBalance.Value = 0 ; // Assume Auto white balance. + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Metadata.WhiteBalance.Value = 0 ; // Manual while balance. + } + + Metadata.DigitalZoomRatio = CMetadataRational(1); + + Metadata.FocalLengthIn35mmFilm = CMetadataShort(0); + Metadata.SceneCaptureType = CMetadataShort(0); + Metadata.GainControl = CMetadataRational(); + Metadata.Contrast = CMetadataShort(0); + Metadata.Saturation = CMetadataShort(0); + Metadata.Sharpness = CMetadataShort(0); + Metadata.SubjectDistanceRange = CMetadataShort(0); + + // Report (optional) focus state. + KSCAMERA_EXTENDEDPROP_FOCUSSTATE State = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED; + if( NT_SUCCESS(m_Sensor->GetFocusState( &State )) ) + { + Metadata.FocusState = CMetadataLong((UINT32)State); + } + + return Metadata; +} + +// +// Emit metadata here for still pin. +// +void +CImageHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +/*++ + +Routine Description: + + Emit metadata for a photo. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + + // Add the normal frame info to the metadata + CHardwareSimulation::EmitMetadata( pStreamHeader ); + + 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); + PCAMERA_METADATA_IMAGEAGGREGATION pAggregation = + (PCAMERA_METADATA_IMAGEAGGREGATION) (((PBYTE) pMetadata->SystemVa) + pMetadata->UsedSize); + ULONG BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize; + + if( BytesLeft >= sizeof(*pAggregation) ) + { + pAggregation->Header.MetadataId = (ULONG) MetadataId_Custom_ImageAggregation; + pAggregation->Header.Size = sizeof(*pAggregation); + + // Just copy over the current frame's ISP settings for now. + // We still need to develop a contract between the driver and the MFT0 for these settings. + pAggregation->Data = GetMetadata(); + + pMetadata->UsedSize += sizeof(*pAggregation); + BytesLeft -= sizeof(*pAggregation); + } + + CExtendedVidProcSetting FaceDetect; + m_Sensor->GetFaceDetection(&FaceDetect); + + if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_PHOTO ) + { + DBG_TRACE("IMAGE"); + EmitFaceMetadata( + pStreamHeader, + FaceDetect.GetULONG(), + FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK, + 1); + } + } +} + +NTSTATUS +CImageHardwareSimulation:: +FillScatterGatherBuffers() + +/*++ + +Routine Description: + + The hardware has synthesized a buffer in scratch space and we're to + fill scatter / gather buffers. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("() m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%d, " + "m_ScatterGatherBytesQueue=0x%08X", + m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued); + + // + // We're using this list lock to protect our scatter / gather lists instead + // of some hardware mechanism / KeSynchronizeExecution / whatever. + // + //KeAcquireSpinLockAtDpcLevel (&m_ListLock); + + ULONG BufferRemaining = m_ImageSize; + + // + // If there aren't enough scatter / gather buffers queued, consider it starvation. + // + while( BufferRemaining && + !IsListEmpty(&m_ScatterGatherMappings) && + m_ScatterGatherBytesQueued >= BufferRemaining) + { + DBG_TRACE( "BufferRemaining=0x%08X, m_ScatterGatherBytesQueued=0x%08X, m_ScatterGatherMappingsQueued=%d", + BufferRemaining, m_ScatterGatherBytesQueued, m_ScatterGatherMappingsQueued ); + + LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + // Deal with cancellation. + PIRP pIrp = KsStreamPointerGetIrp(SGEntry->CloneEntry, FALSE, FALSE); + if (pIrp) + { + if (pIrp->Cancel) + { + DBG_TRACE( "Cancelling..." ); + FreeSGEntry( listEntry, L"StreamPointer Cancel SG List" ); + continue; + } + } + + // + // Since we're software, we'll be accessing this by virtual address... + // + ULONG BytesToCopy = min( BufferRemaining, SGEntry->ByteCount ); + + // Have the synthesizer output a frame to the buffer. + DBG_TRACE( "DataUsed before Commit() = %d", SGEntry->CloneEntry->StreamHeader->DataUsed ); + ULONG BytesCopied = + m_Synthesizer->DoCommit( SGEntry->Virtual, BytesToCopy ); + NT_ASSERT(BytesCopied); + DBG_TRACE( "BytesCopied = %d", BytesCopied ); + + BufferRemaining = 0; //-= BytesCopied; + + // Add metadata to the sample. + EmitMetadata( SGEntry -> CloneEntry -> StreamHeader ); + + ULONGLONG time = ConvertQPCtoTimeStamp(NULL); + + if (IsPhotoConfirmationNeeded()) + { + DBG_TRACE( "PhotoConfirmation is needed. Frame=%d, Time=0x%016llX", m_PfsFrameNumber, (LONGLONG) time ); + SGEntry->PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO( m_PfsFrameNumber, (LONGLONG) time ); + } + + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = time; + DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + DBG_TRACE("m_FlashStatus=0x%016llX", m_FlashStatus); + + if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER || + m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER) + { + if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH && time >= m_TriggerTime && m_TriggerTime != 0 && !m_bFlashed) + { + m_bFlashed = TRUE; + DBG_TRACE("(Single) FLASHED!!!"); + } + } + + // + // Release the scatter / gather entry back to our lookaside. + // + if( m_bTriggered && !IsPfsEOS() ) + { + DBG_TRACE("m_PinMode=%d", m_PinMode); + m_pClone = SGEntry->CloneEntry; + m_PhotoConfirmationInfo = SGEntry->PhotoConfirmationInfo; + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + DBG_TRACE( "m_NumMappingsCompleted=%d, m_PhotoConfirmationInfo.isRequired()=%s", m_NumMappingsCompleted, m_PhotoConfirmationInfo.isRequired()?"TRUE":"FALSE" ); + + if(m_PinMode != PinBurstMode) + { + m_bTriggered = FALSE; + DBG_TRACE("m_bTriggered=FALSE"); + } + + // Update the VPS frame and loop numbers here. Mark the frame as the EOS + // if we've completed the sequence. + // + // Note: It's actually up to DevProxy to stop feeding us frames! + if( AdvanceFrameCounter() ) + { + // We've reached the end of a VPS sequence! Mark the frame as EOS. + SGEntry->CloneEntry->StreamHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_ENDOFPHOTOSEQUENCE; + m_bEndOfSequence = TRUE; + } + + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast <PVOID> (SGEntry) + ); + } + else + { + InsertTailList( &m_ScatterGatherMappings, listEntry ); + m_ScatterGatherMappingsQueued++; + m_pClone = NULL; + } + } + + DBG_LEAVE("()"); + + if (BufferRemaining) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + else + { + return STATUS_SUCCESS; + } +} + +/************************************************************************** + + Debug helpers + +**************************************************************************/ + +PCCHAR +AdvancedPhoto_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO: return "Auto"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR: return "HDR"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF: return "FNF"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: return "UltraLL"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR: return "Auto|HDR"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF: return "Auto|FNF"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: return "Auto|UltraLL"; + default: + { + static + CHAR buffer[32]; + + RtlStringCbPrintfA(buffer, sizeof(buffer), "Unknown [0x%016llX]", Flags); + return (PCCHAR) buffer; + } + } +} + +/*************************************************/ + +void +CImageHardwareSimulation:: +FakeHardware() + +/*++ + +Routine Description: + + Simulate an interrupt and what the hardware would have done in the + time since the previous interrupt. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_InterruptTime++; + + // + // The hardware can be in a pause state in which case, it issues interrupts + // but does not complete mappings. In this case, don't bother synthesizing + // a frame and doing the work of looking through the mappings table. + // + if( m_PinState == PinRunning ) + { + if(m_PinMode == PinBurstMode && m_bTriggered && m_bPastBufferTrigger) + { + CompletePastBuffers(); + } + + m_Synthesizer->DoSynthesize(); + + CHAR Text[64]; + + CExtendedProperty Control; + m_Sensor->GetAdvancedPhoto(&Control); + RtlStringCbPrintfA(Text, sizeof(Text), "Adv: %s", AdvancedPhoto_Text(Control.Flags)); + m_Synthesizer->OverlayText( 0, m_Height-38, 1, Text, TRANSPARENT, WHITE ); + + // + // Fill scatter gather buffers + // + if (!NT_SUCCESS (FillScatterGatherBuffers ())) + { + InterlockedIncrement (PLONG (&m_NumFramesSkipped)); + } + } + + // + // Issue an interrupt to our hardware sink. This is a "fake" interrupt. + // It will occur at DISPATCH_LEVEL. + // + m_Sensor -> Interrupt (m_PinID); + + // + // Schedule the timer for the next interrupt time, if the pin is still running. + // + if( m_PinState == PinRunning ) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + + (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 ) + { + NextTime.QuadPart = 0LL - m_TimePerFrame ; + } + +#endif + m_IsrTimer.Set( NextTime ); + } +} + +NTSTATUS +CImageHardwareSimulation:: +CompletePastBuffers() + +/*++ + +Routine Description: + + Find and complete any history frames. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + ULONG ulNumBuffers = m_PastBufferCount; + BOOLEAN bContinue = TRUE; + LIST_ENTRY *listEntry = NULL; + + DBG_ENTER("()"); + DBG_TRACE("m_PastBufferCount=%d, m_TriggerTime=0x%016llX", m_PastBufferCount, m_TriggerTime ); + + // If we're in burst mode and have ISP settings, we can't + // really support changing the ISP settings in the past... + // ... so we'll just treat them all past frames. + // Note: The upper layer will determine past frames from the + // metadata FrameId. + if( m_PinMode == PinBurstMode && + !m_pIspSettings ) + { + // Walk through the entire list of buffers, find the most + // recent frame presentation time and put all past buffers + // into another list. + while( !IsListEmpty(&m_ScatterGatherMappings) ) + { + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + NT_ASSERT(SGEntry); + NT_ASSERT(SGEntry->CloneEntry); + NT_ASSERT(SGEntry->CloneEntry->StreamHeader); + + // We've found one that's not stamped. We must be at the end. + // Push it back and exit the loop. + if(SGEntry->CloneEntry->StreamHeader->PresentationTime.Time == 0) + { + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + DBG_TRACE( "No past frames found." ); + bContinue = FALSE; + break; + } + + // Since we're walking from the most recent to least recent frame, + // This one is a valid "future" frame. + DBG_TRACE( "Adding 'future frame' to the list" ); + PushCloneList(SGEntry); + + // If the presentation time is less than the trigger time, stop here + // and use this as the first triggered frame. + if((ULONGLONG)(SGEntry->CloneEntry->StreamHeader->PresentationTime.Time) < m_TriggerTime) + { + DBG_TRACE( "First matching frame time=0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + break; + } + + // Watch out! We might actually need to pick up multiple frames since + // in theory we could have generated several since the trigger time. + } + } + + // Grab N past frames from the queue, if we have them. + while( bContinue && + !IsListEmpty(&m_ScatterGatherMappings) && + ulNumBuffers) + { + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + if(!(SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) ) + { + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + DBG_TRACE( "No more past frames found." ); + bContinue = FALSE; + } + else + { + PushCloneList(SGEntry); + ulNumBuffers--; + DBG_TRACE( "Past frame #%d found (%p)", ulNumBuffers, SGEntry->CloneEntry->StreamHeader ); + } + } + + // If we got all of the past frames we needed and didn't consume the entire queue... + if(bContinue && (m_ScatterGatherMappingsQueued > 0)) + { + //Mark the next tail as Time = 0 so that we don't output any super old frames. + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + // Mark the PTS as invalid. + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time = 0; + SGEntry->CloneEntry->StreamHeader->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + } + + CompleteCloneList(); + + m_bPastBufferTrigger = FALSE; + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; + +} + +// +// Put an item onto the history list. +// +void +CImageHardwareSimulation:: +PushCloneList( + _Inout_ PSCATTER_GATHER_ENTRY SGEntry +) +{ + PAGED_CODE(); + + DBG_TRACE( "Frame %p, PresentationTime=0x%016llX", + SGEntry->CloneEntry->StreamHeader, + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + InsertHeadList( &m_BurstList, &SGEntry->ListEntry ); +} + +// +// Complete the history list. +// +NTSTATUS +CImageHardwareSimulation:: +CompleteCloneList() +{ + PAGED_CODE(); + + int i = 0; + while(!IsListEmpty(&m_BurstList)) + { + LIST_ENTRY *listEntry = RemoveHeadList(&m_BurstList); + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + m_pClone = SGEntry->CloneEntry; + m_PhotoConfirmationInfo = SGEntry->PhotoConfirmationInfo; + + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + DBG_TRACE( "m_NumMappingsCompleted=%d, m_PhotoConfirmationInfo.isRequired()=%s", m_NumMappingsCompleted, m_PhotoConfirmationInfo.isRequired( )?"TRUE":"FALSE" ); + DBG_TRACE( "Frame %p, PresentationTime=0x%016llX", + SGEntry->CloneEntry->StreamHeader, + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + m_Sensor -> Interrupt (m_PinID); + + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast <PVOID> (SGEntry) + ); + i++; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +SetClock(PKSPIN pin) +{ + PAGED_CODE(); + + if(!NT_SUCCESS(KsPinGetReferenceClockInterface(pin, &m_Clock))) + { + m_Clock = NULL; + } + + return STATUS_SUCCESS; +} + +void +CImageHardwareSimulation:: +SetTriggerTime( + _In_ ULONGLONG TriggerTime +) +/*++ + +Routine Description: + + Identify exactly when the user pressed that button. + +Arguments: + + TriggerTime - + The QPC time in 100ns when the user asked for the photo. + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + m_TriggerTime = TriggerTime; + DBG_TRACE( "Setting Trigger Time = 0x%016llX", TriggerTime ); +} + +NTSTATUS +CImageHardwareSimulation:: +Reset() +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + DBG_ENTER("(): m_PinID=%d", m_PinID); + + // Parent class reset first... + CHardwareSimulation::Reset(); + + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Reset Burst List" ); + + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_TriggerTime = 0; + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +SetFlashStatus( + _In_ ULONGLONG ullFlashStatus +) +{ + PAGED_CODE(); + + m_FlashStatus = ullFlashStatus; + + DBG_TRACE("FlashStatus=0x%016llX", ullFlashStatus); + + return STATUS_SUCCESS; +} + +// +// Program the Per Frame Settings for simulation +// We do it before calling start so we can be ready +// to program our simulation's hardware. +// +// Note: We make a local copy. +// +NTSTATUS +CImageHardwareSimulation:: +SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit +) +{ + PAGED_CODE(); + + // Set the current ISP settings and free any prior. + // This has to be done at simulation start or stop or we have + // a synchronization problem. + SAFE_DELETE_ARRAY( m_pIspSettings ); + + if( pIspSettings ) + { + m_pIspSettings = new (NonPagedPoolNx) ISP_FRAME_SETTINGS[FrameLimit]; + if( !m_pIspSettings ) + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory( m_pIspSettings, pIspSettings, FrameLimit*sizeof(ISP_FRAME_SETTINGS) ); + m_PfsLoopLimit = LoopLimit; + m_PfsFrameLimit = FrameLimit; + } + else + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + } + + return STATUS_SUCCESS; +} + +// Function: +// bool CImageHardwareSimulation::AdvanceFrameCounter(void) +// +// Description: +// Advance our frame and loop pointers to the next PFS settings. +// +// Parameters: +// [None] +// +// Returns: +// bool - true if we've reached the end of our Per Frame Settings. +// +bool +CImageHardwareSimulation:: +AdvanceFrameCounter(void) +{ + PAGED_CODE(); + + bool bEOS = false; + + DBG_ENTER( "()" ); + + m_GlobalFrameNumber ++; + DBG_TRACE( "m_GlobalFrameNumber=%lld", m_GlobalFrameNumber ); + + // + // Calculate the PFS frame & loop numbers, but only if we've + // gotten ISP settings. + // + if( m_bTriggered && + m_PinMode == PinBurstMode && + m_pIspSettings ) + { + m_PfsFrameNumber ++; + + if( m_PfsFrameNumber >= m_PfsFrameLimit ) + { + m_PfsFrameNumber = 0; + m_PfsLoopNumber ++; + } + + // Only mark EOS if we're not in an infinite loop. + if( m_PfsLoopLimit != 0 ) + { + NT_ASSERT( !(m_PfsLoopNumber > m_PfsLoopLimit) ); + // Check to see if we've hit our limit. + if( m_PfsLoopNumber >= m_PfsLoopLimit ) + { + DBG_TRACE( "Marking EOS" ); + bEOS = true; + } + } + } + + DBG_TRACE( "m_PfsFrameNumber=%d, m_PfsLoopNumber=%d", m_PfsFrameNumber, m_PfsLoopNumber ); + DBG_TRACE( "m_PfsFrameLimit=%d, m_PfsLoopLimit=%d", m_PfsFrameLimit, m_PfsLoopLimit ); + DBG_LEAVE( "() = %s", bEOS ? "true" : "false" ); + return bEOS; +} + + +// Function: +// bool CImageHardwareSimulation::IsPfsEOS(void) +// +// Description: +// Determine if we're at the EOS. +// +// Parameters: +// [None] +// +// Returns: +// bool - true if we've reached the end of our Per Frame Settings. +// +bool +CImageHardwareSimulation:: +IsPfsEOS(void) +{ + PAGED_CODE(); + + DBG_ENTER( "()" ); + DBG_TRACE( "m_bTriggered=%s, m_bEndOfSequence=%s, m_PinMode=%d, m_pIspSetting=0x%p", + ( m_bTriggered ? "true" : "false" ), + ( m_bEndOfSequence ? "true" : "false" ), + m_PinMode, + m_pIspSettings ); + + // + // Make sure we're in a Variable Photo Sequence. + // + bool result = + bool( m_bTriggered == TRUE && + m_bEndOfSequence && + m_PinMode == PinBurstMode && + m_pIspSettings ); // Must have ISP settings set to be PFS EOS. + + DBG_LEAVE( "() = %s", (result ? "true" : "false") ); + return result; +} + +// Function: +// BOOL CImageHardwareSimulation::IsPfsActive(void) +// +// Description: +// Determine if we're in a Variable Photo Sequence. +// +// Parameters: +// [None] +// +// Returns: +// BOOL - TRUE if we are actively processing Per Frame Settings. +// +BOOL +CImageHardwareSimulation:: +IsPfsActive(void) +{ + PAGED_CODE(); + + return + BOOL( m_bTriggered && !m_bEndOfSequence && + m_PinMode == PinBurstMode && + m_pIspSettings ) ; +} + +// Get the current frame settings. +// +// Note: +// Call this function to acquire ISP settings for the current frame's +// simulation. Initially we'll just use it to report back the ISP +// settings originally requested in the PFS by the user. +// +ISP_FRAME_SETTINGS * +CImageHardwareSimulation:: +GetIspSettings(void) +{ + PAGED_CODE(); + + return + IsPfsActive() + ? &m_pIspSettings[m_PfsFrameNumber] + : CHardwareSimulation::GetIspSettings() ; +} + +BOOLEAN +CImageHardwareSimulation:: +IsPhotoConfirmationNeeded() +/*++ + +Routine Description: + + Check flags for photo confirmation and return + whether driver should issue confirmation. + +--*/ +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + return pSettings ? pSettings->bPhotoConfirmation : FALSE; +} diff --git a/AVStream/avscamera/sys/imagehwsim.h b/AVStream/avscamera/sys/imagehwsim.h new file mode 100644 index 00000000..1355a527 --- /dev/null +++ b/AVStream/avscamera/sys/imagehwsim.h @@ -0,0 +1,205 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + ImageHwSim.h + + Abstract: + + This file contains the definition of the CImageHardwareSimulation class. + + This is a specialization of CHardwareSimulation that provides photo- + specific metadata and implements specialized functionality for photo, + photo sequence and variable photo sequence. + + History: + + created 3/9/2001 + +**************************************************************************/ + +// forward references +struct ISP_FRAME_SETTINGS; + +class CImageHardwareSimulation + : public CHardwareSimulation +{ +private: + LIST_ENTRY m_BurstList; + PWSTR m_szwFramePath; + BOOLEAN m_bTriggered; + BOOLEAN m_bEndOfSequence; + BOOLEAN m_bFlashed; + BOOLEAN m_bPastBufferTrigger; + PIN_MODE m_PinMode; + ULONG m_PastBufferCount; + ULONGLONG m_TriggerTime; + PIKSREFERENCECLOCK m_Clock; + ULONGLONG m_FlashStatus; + + // Settings for VPS simulation. + ULONG m_PfsLoopLimit; + ULONG m_PfsFrameLimit; + ISP_FRAME_SETTINGS *m_pIspSettings; // + ULONG m_PfsLoopNumber; // Number of loops we've done. + ULONG m_PfsFrameNumber; + ULONGLONG m_GlobalFrameNumber; // Informational only. + + // Copy a synthesized framebuffer. + virtual + NTSTATUS + FillScatterGatherBuffers(); + + // Find and complete any history frames. + NTSTATUS + CompletePastBuffers(); + + // Put an item onto the history list. + void + PushCloneList(_Inout_ PSCATTER_GATHER_ENTRY SGEntry); + + // Complete the history list. + NTSTATUS + CompleteCloneList(); + + // Add Photo-specific metadata. + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + + // Does the current frame need a photo confirmation? + BOOLEAN + IsPhotoConfirmationNeeded(); + + // Helper function that gets a buffer full of image metadata. + METADATA_IMAGEAGGREGATION + GetMetadata(); + +public: + PKSSTREAM_POINTER m_pClone; + PHOTOCONFIRMATION_INFO m_PhotoConfirmationInfo; + + // Constructor + CImageHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + // Destructor + virtual + ~CImageHardwareSimulation(); + + // Periodic interrupt handling + virtual + void + FakeHardware(); + + // Start streaming. + virtual + NTSTATUS + Start( + _In_ CSynthesizer *ImageSynth, + _In_ ULONG Width, + _In_ ULONG Height, + _In_ ULONG ImageSize, + _In_ PIN_MODE pinMode + ); + + // Stop streaming. + virtual + NTSTATUS + Stop(); + + virtual + NTSTATUS + Reset(); + + // Take a picture or start a photo sequence. + virtual + NTSTATUS + Trigger( + _In_ LONG mode + ); + + LONG + GetTriggerMode(); + + // Limit the rate of frames produced. + virtual + NTSTATUS + SetPhotoFrameRate ( + _In_ ULONGLONG TimePerFrame + ); + + // Get the rate of frames produced. + virtual + ULONGLONG + GetPhotoFrameRate() + { + return m_TimePerFrame; + } + + // Set the master clock. + NTSTATUS + SetClock( + _In_ PKSPIN pin + ); + + // Identify exactly when the user pressed that button. + void + SetTriggerTime( + _In_ ULONGLONG TriggerTime + ); + + // Report the current trigger time. + ULONGLONG + GetTriggerTime() + { + return m_TriggerTime; + } + + // Normal vs. Photo sequence + number of history frames. + NTSTATUS + SetMode( + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers + ); + + NTSTATUS + SetFlashStatus( + _In_ ULONGLONG ulFlashStatus + ); + + // Program the Per Frame Settings for simulation + // We do it before calling start so we can be ready + // to program our simulation's hardware. + // Note: We make a local copy. + NTSTATUS + SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit + ); + + // Advance our frame and loop pointers to the next PFS settings. + bool + AdvanceFrameCounter(void); + + // Determine if we're at the EOS. + bool + IsPfsEOS(void); + + // Determine if we're in a Variable Photo Sequence. + BOOL + IsPfsActive(void); + + // Get the current frame settings. + virtual + ISP_FRAME_SETTINGS * + GetIspSettings(void); +}; |
