Initial commit of CUDA video decoder stub.
This commit is contained in:
commit
c2f726a7d7
|
@ -0,0 +1,9 @@
|
|||
|
||||
*.user
|
||||
*.suo
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.aps
|
||||
|
||||
/ipch
|
||||
/bin_*
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LAVCUDA", "LAVCUDA.vcxproj", "{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA} = {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "baseclasses", "baseclasses\baseclasses.vcxproj", "{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Debug|x64.Build.0 = Debug|x64
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Release|Win32.Build.0 = Release|Win32
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Release|x64.ActiveCfg = Release|x64
|
||||
{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}.Release|x64.Build.0 = Release|x64
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|x64.Build.0 = Debug|x64
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|Win32.Build.0 = Release|Win32
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|x64.ActiveCfg = Release|x64
|
||||
{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,159 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.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="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{1B77214C-2121-4FFC-8B0F-C0810BC46EF6}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>LAVCUDA</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<Import Project="$(SolutionDir)common.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</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')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</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')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<TargetExt>.ax</TargetExt>
|
||||
<OutDir>$(SolutionDir)bin_$(PlatformName)d\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<TargetExt>.ax</TargetExt>
|
||||
<OutDir>$(SolutionDir)bin_$(PlatformName)d\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetExt>.ax</TargetExt>
|
||||
<OutDir>$(SolutionDir)bin_$(PlatformName)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetExt>.ax</TargetExt>
|
||||
<OutDir>$(SolutionDir)bin_$(PlatformName)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LAVCUDA_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>src\LAVCUDA.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>Winmm.lib;strmbasd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LAVCUDA_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>src\LAVCUDA.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LAVCUDA_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<ModuleDefinitionFile>src\LAVCUDA.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LAVCUDA_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<ModuleDefinitionFile>src\LAVCUDA.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="includes\moreuuids.h" />
|
||||
<ClInclude Include="src\LAVCUDA.h" />
|
||||
<ClInclude Include="src\stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\dllmain.cpp">
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</PrecompiledHeader>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\LAVCUDA.cpp" />
|
||||
<ClCompile Include="src\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\common">
|
||||
<UniqueIdentifier>{3d3fbe09-a351-44a7-b3d2-09b379eed8a6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\LAVCUDA.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="includes\moreuuids.h">
|
||||
<Filter>Header Files\common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\LAVCUDA.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,111 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: AMExtra.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements CRenderedInputPin class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h> // DirectShow base class definitions
|
||||
#include <mmsystem.h> // Needed for definition of timeGetTime
|
||||
#include <limits.h> // Standard data type limit definitions
|
||||
#include <measure.h> // Used for time critical log functions
|
||||
|
||||
#include "amextra.h"
|
||||
|
||||
#pragma warning(disable:4355)
|
||||
|
||||
// Implements CRenderedInputPin class
|
||||
|
||||
CRenderedInputPin::CRenderedInputPin(__in_opt LPCTSTR pObjectName,
|
||||
__in CBaseFilter *pFilter,
|
||||
__in CCritSec *pLock,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR pName) :
|
||||
CBaseInputPin(pObjectName, pFilter, pLock, phr, pName),
|
||||
m_bAtEndOfStream(FALSE),
|
||||
m_bCompleteNotified(FALSE)
|
||||
{
|
||||
}
|
||||
#ifdef UNICODE
|
||||
CRenderedInputPin::CRenderedInputPin(__in_opt LPCSTR pObjectName,
|
||||
__in CBaseFilter *pFilter,
|
||||
__in CCritSec *pLock,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR pName) :
|
||||
CBaseInputPin(pObjectName, pFilter, pLock, phr, pName),
|
||||
m_bAtEndOfStream(FALSE),
|
||||
m_bCompleteNotified(FALSE)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
// Flush end of stream condition - caller should do any
|
||||
// necessary stream level locking before calling this
|
||||
|
||||
STDMETHODIMP CRenderedInputPin::EndOfStream()
|
||||
{
|
||||
HRESULT hr = CheckStreaming();
|
||||
|
||||
// Do EC_COMPLETE handling for rendered pins
|
||||
if (S_OK == hr && !m_bAtEndOfStream) {
|
||||
m_bAtEndOfStream = TRUE;
|
||||
FILTER_STATE fs;
|
||||
EXECUTE_ASSERT(SUCCEEDED(m_pFilter->GetState(0, &fs)));
|
||||
if (fs == State_Running) {
|
||||
DoCompleteHandling();
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
// Called to complete the flush
|
||||
|
||||
STDMETHODIMP CRenderedInputPin::EndFlush()
|
||||
{
|
||||
CAutoLock lck(m_pLock);
|
||||
|
||||
// Clean up renderer state
|
||||
m_bAtEndOfStream = FALSE;
|
||||
m_bCompleteNotified = FALSE;
|
||||
|
||||
return CBaseInputPin::EndFlush();
|
||||
}
|
||||
|
||||
|
||||
// Notify of Run() from filter
|
||||
|
||||
HRESULT CRenderedInputPin::Run(REFERENCE_TIME tStart)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(tStart);
|
||||
m_bCompleteNotified = FALSE;
|
||||
if (m_bAtEndOfStream) {
|
||||
DoCompleteHandling();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
// Clear status on going into paused state
|
||||
|
||||
HRESULT CRenderedInputPin::Active()
|
||||
{
|
||||
m_bAtEndOfStream = FALSE;
|
||||
m_bCompleteNotified = FALSE;
|
||||
return CBaseInputPin::Active();
|
||||
}
|
||||
|
||||
|
||||
// Do stuff to deliver end of stream
|
||||
|
||||
void CRenderedInputPin::DoCompleteHandling()
|
||||
{
|
||||
ASSERT(m_bAtEndOfStream);
|
||||
if (!m_bCompleteNotified) {
|
||||
m_bCompleteNotified = TRUE;
|
||||
m_pFilter->NotifyEvent(EC_COMPLETE, S_OK, (LONG_PTR)(IBaseFilter *)m_pFilter);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: AMExtra.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __AMEXTRA__
|
||||
#define __AMEXTRA__
|
||||
|
||||
// Simple rendered input pin
|
||||
//
|
||||
// NOTE if your filter queues stuff before rendering then it may not be
|
||||
// appropriate to use this class
|
||||
//
|
||||
// In that case queue the end of stream condition until the last sample
|
||||
// is actually rendered and flush the condition appropriately
|
||||
|
||||
class CRenderedInputPin : public CBaseInputPin
|
||||
{
|
||||
public:
|
||||
|
||||
CRenderedInputPin(__in_opt LPCTSTR pObjectName,
|
||||
__in CBaseFilter *pFilter,
|
||||
__in CCritSec *pLock,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CRenderedInputPin(__in_opt LPCSTR pObjectName,
|
||||
__in CBaseFilter *pFilter,
|
||||
__in CCritSec *pLock,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
#endif
|
||||
|
||||
// Override methods to track end of stream state
|
||||
STDMETHODIMP EndOfStream();
|
||||
STDMETHODIMP EndFlush();
|
||||
|
||||
HRESULT Active();
|
||||
HRESULT Run(REFERENCE_TIME tStart);
|
||||
|
||||
protected:
|
||||
|
||||
// Member variables to track state
|
||||
BOOL m_bAtEndOfStream; // Set by EndOfStream
|
||||
BOOL m_bCompleteNotified; // Set when we notify for EC_COMPLETE
|
||||
|
||||
private:
|
||||
void DoCompleteHandling();
|
||||
};
|
||||
|
||||
#endif // __AMEXTRA__
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,275 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: AMVideo.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements helper functions for
|
||||
// bitmap formats.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <limits.h>
|
||||
|
||||
// These are bit field masks for true colour devices
|
||||
|
||||
const DWORD bits555[] = {0x007C00,0x0003E0,0x00001F};
|
||||
const DWORD bits565[] = {0x00F800,0x0007E0,0x00001F};
|
||||
const DWORD bits888[] = {0xFF0000,0x00FF00,0x0000FF};
|
||||
|
||||
// This maps bitmap subtypes into a bits per pixel value and also a
|
||||
// name. unicode and ansi versions are stored because we have to
|
||||
// return a pointer to a static string.
|
||||
const struct {
|
||||
const GUID *pSubtype;
|
||||
WORD BitCount;
|
||||
CHAR *pName;
|
||||
WCHAR *wszName;
|
||||
} BitCountMap[] = { &MEDIASUBTYPE_RGB1, 1, "RGB Monochrome", L"RGB Monochrome",
|
||||
&MEDIASUBTYPE_RGB4, 4, "RGB VGA", L"RGB VGA",
|
||||
&MEDIASUBTYPE_RGB8, 8, "RGB 8", L"RGB 8",
|
||||
&MEDIASUBTYPE_RGB565, 16, "RGB 565 (16 bit)", L"RGB 565 (16 bit)",
|
||||
&MEDIASUBTYPE_RGB555, 16, "RGB 555 (16 bit)", L"RGB 555 (16 bit)",
|
||||
&MEDIASUBTYPE_RGB24, 24, "RGB 24", L"RGB 24",
|
||||
&MEDIASUBTYPE_RGB32, 32, "RGB 32", L"RGB 32",
|
||||
&MEDIASUBTYPE_ARGB32, 32, "ARGB 32", L"ARGB 32",
|
||||
&MEDIASUBTYPE_Overlay, 0, "Overlay", L"Overlay",
|
||||
&GUID_NULL, 0, "UNKNOWN", L"UNKNOWN"
|
||||
};
|
||||
|
||||
// Return the size of the bitmap as defined by this header
|
||||
|
||||
STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader)
|
||||
{
|
||||
return DIBSIZE(*pHeader);
|
||||
}
|
||||
|
||||
|
||||
// This is called if the header has a 16 bit colour depth and needs to work
|
||||
// out the detailed type from the bit fields (either RGB 565 or RGB 555)
|
||||
|
||||
STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader)
|
||||
{
|
||||
BITMAPINFO *pbmInfo = (BITMAPINFO *) pbmiHeader;
|
||||
ASSERT(pbmiHeader->biBitCount == 16);
|
||||
|
||||
// If its BI_RGB then it's RGB 555 by default
|
||||
|
||||
if (pbmiHeader->biCompression == BI_RGB) {
|
||||
return MEDIASUBTYPE_RGB555;
|
||||
}
|
||||
|
||||
// Compare the bit fields with RGB 555
|
||||
|
||||
DWORD *pMask = (DWORD *) pbmInfo->bmiColors;
|
||||
if (pMask[0] == bits555[0]) {
|
||||
if (pMask[1] == bits555[1]) {
|
||||
if (pMask[2] == bits555[2]) {
|
||||
return MEDIASUBTYPE_RGB555;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the bit fields with RGB 565
|
||||
|
||||
pMask = (DWORD *) pbmInfo->bmiColors;
|
||||
if (pMask[0] == bits565[0]) {
|
||||
if (pMask[1] == bits565[1]) {
|
||||
if (pMask[2] == bits565[2]) {
|
||||
return MEDIASUBTYPE_RGB565;
|
||||
}
|
||||
}
|
||||
}
|
||||
return GUID_NULL;
|
||||
}
|
||||
|
||||
|
||||
// Given a BITMAPINFOHEADER structure this returns the GUID sub type that is
|
||||
// used to describe it in format negotiations. For example a video codec fills
|
||||
// in the format block with a VIDEOINFO structure, it also fills in the major
|
||||
// type with MEDIATYPE_VIDEO and the subtype with a GUID that matches the bit
|
||||
// count, for example if it is an eight bit image then MEDIASUBTYPE_RGB8
|
||||
|
||||
STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader)
|
||||
{
|
||||
ASSERT(pbmiHeader);
|
||||
|
||||
// If it's not RGB then create a GUID from the compression type
|
||||
|
||||
if (pbmiHeader->biCompression != BI_RGB) {
|
||||
if (pbmiHeader->biCompression != BI_BITFIELDS) {
|
||||
FOURCCMap FourCCMap(pbmiHeader->biCompression);
|
||||
return (const GUID) FourCCMap;
|
||||
}
|
||||
}
|
||||
|
||||
// Map the RGB DIB bit depth to a image GUID
|
||||
|
||||
switch(pbmiHeader->biBitCount) {
|
||||
case 1 : return MEDIASUBTYPE_RGB1;
|
||||
case 4 : return MEDIASUBTYPE_RGB4;
|
||||
case 8 : return MEDIASUBTYPE_RGB8;
|
||||
case 16 : return GetTrueColorType(pbmiHeader);
|
||||
case 24 : return MEDIASUBTYPE_RGB24;
|
||||
case 32 : return MEDIASUBTYPE_RGB32;
|
||||
}
|
||||
return GUID_NULL;
|
||||
}
|
||||
|
||||
|
||||
// Given a video bitmap subtype we return the number of bits per pixel it uses
|
||||
// We return a WORD bit count as thats what the BITMAPINFOHEADER uses. If the
|
||||
// GUID subtype is not found in the table we return an invalid USHRT_MAX
|
||||
|
||||
STDAPI_(WORD) GetBitCount(const GUID *pSubtype)
|
||||
{
|
||||
ASSERT(pSubtype);
|
||||
const GUID *pMediaSubtype;
|
||||
INT iPosition = 0;
|
||||
|
||||
// Scan the mapping list seeing if the source GUID matches any known
|
||||
// bitmap subtypes, the list is terminated by a GUID_NULL entry
|
||||
|
||||
while (TRUE) {
|
||||
pMediaSubtype = BitCountMap[iPosition].pSubtype;
|
||||
if (IsEqualGUID(*pMediaSubtype,GUID_NULL)) {
|
||||
return USHRT_MAX;
|
||||
}
|
||||
if (IsEqualGUID(*pMediaSubtype,*pSubtype)) {
|
||||
return BitCountMap[iPosition].BitCount;
|
||||
}
|
||||
iPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Given a bitmap subtype we return a description name that can be used for
|
||||
// debug purposes. In a retail build this function still returns the names
|
||||
// If the subtype isn't found in the lookup table we return string UNKNOWN
|
||||
|
||||
int LocateSubtype(const GUID *pSubtype)
|
||||
{
|
||||
ASSERT(pSubtype);
|
||||
const GUID *pMediaSubtype;
|
||||
INT iPosition = 0;
|
||||
|
||||
// Scan the mapping list seeing if the source GUID matches any known
|
||||
// bitmap subtypes, the list is terminated by a GUID_NULL entry
|
||||
|
||||
while (TRUE) {
|
||||
pMediaSubtype = BitCountMap[iPosition].pSubtype;
|
||||
if (IsEqualGUID(*pMediaSubtype,*pSubtype) ||
|
||||
IsEqualGUID(*pMediaSubtype,GUID_NULL)
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
iPosition++;
|
||||
}
|
||||
|
||||
return iPosition;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype)
|
||||
{
|
||||
return BitCountMap[LocateSubtype(pSubtype)].wszName;
|
||||
}
|
||||
|
||||
STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype)
|
||||
{
|
||||
return BitCountMap[LocateSubtype(pSubtype)].pName;
|
||||
}
|
||||
|
||||
#ifndef GetSubtypeName
|
||||
#error wxutil.h should have defined GetSubtypeName
|
||||
#endif
|
||||
#undef GetSubtypeName
|
||||
|
||||
// this is here for people that linked to it directly; most people
|
||||
// would use the header file that picks the A or W version.
|
||||
STDAPI_(CHAR *) GetSubtypeName(const GUID *pSubtype)
|
||||
{
|
||||
return GetSubtypeNameA(pSubtype);
|
||||
}
|
||||
|
||||
|
||||
// The mechanism for describing a bitmap format is with the BITMAPINFOHEADER
|
||||
// This is really messy to deal with because it invariably has fields that
|
||||
// follow it holding bit fields, palettes and the rest. This function gives
|
||||
// the number of bytes required to hold a VIDEOINFO that represents it. This
|
||||
// count includes the prefix information (like the rcSource rectangle) the
|
||||
// BITMAPINFOHEADER field, and any other colour information on the end.
|
||||
//
|
||||
// WARNING If you want to copy a BITMAPINFOHEADER into a VIDEOINFO always make
|
||||
// sure that you use the HEADER macro because the BITMAPINFOHEADER field isn't
|
||||
// right at the start of the VIDEOINFO (there are a number of other fields),
|
||||
//
|
||||
// CopyMemory(HEADER(pVideoInfo),pbmi,sizeof(BITMAPINFOHEADER));
|
||||
//
|
||||
|
||||
STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader)
|
||||
{
|
||||
// Everyone has this to start with this
|
||||
LONG Size = SIZE_PREHEADER + pHeader->biSize;
|
||||
|
||||
ASSERT(pHeader->biSize >= sizeof(BITMAPINFOHEADER));
|
||||
|
||||
// Does this format use a palette, if the number of colours actually used
|
||||
// is zero then it is set to the maximum that are allowed for that colour
|
||||
// depth (an example is 256 for eight bits). Truecolour formats may also
|
||||
// pass a palette with them in which case the used count is non zero
|
||||
|
||||
// This would scare me.
|
||||
ASSERT(pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed == 0);
|
||||
|
||||
if (pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed) {
|
||||
LONG Entries = (DWORD) 1 << pHeader->biBitCount;
|
||||
if (pHeader->biClrUsed) {
|
||||
Entries = pHeader->biClrUsed;
|
||||
}
|
||||
Size += Entries * sizeof(RGBQUAD);
|
||||
}
|
||||
|
||||
// Truecolour formats may have a BI_BITFIELDS specifier for compression
|
||||
// type which means that room for three DWORDs should be allocated that
|
||||
// specify where in each pixel the RGB colour components may be found
|
||||
|
||||
if (pHeader->biCompression == BI_BITFIELDS) {
|
||||
Size += SIZE_MASKS;
|
||||
}
|
||||
|
||||
// A BITMAPINFO for a palettised image may also contain a palette map that
|
||||
// provides the information to map from a source palette to a destination
|
||||
// palette during a BitBlt for example, because this information is only
|
||||
// ever processed during drawing you don't normally store the palette map
|
||||
// nor have any way of knowing if it is present in the data structure
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
|
||||
// Returns TRUE if the VIDEOINFO contains a palette
|
||||
|
||||
STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo)
|
||||
{
|
||||
if (PALETTISED(pVideoInfo) == FALSE) {
|
||||
if (pVideoInfo->bmiHeader.biClrUsed == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Return a pointer to the first entry in a palette
|
||||
|
||||
STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo)
|
||||
{
|
||||
if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {
|
||||
return TRUECOLOR(pVideoInfo)->bmiColors;
|
||||
}
|
||||
return COLORS(pVideoInfo);
|
||||
}
|
|
@ -0,0 +1,360 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: ArithUtil.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements helper classes for building
|
||||
// multimedia filters.
|
||||
//
|
||||
// Copyright (c) 1992-2004 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <streams.h>
|
||||
|
||||
//
|
||||
// Declare function from largeint.h we need so that PPC can build
|
||||
//
|
||||
|
||||
//
|
||||
// Enlarged integer divide - 64-bits / 32-bits > 32-bits
|
||||
//
|
||||
|
||||
#ifndef _X86_
|
||||
|
||||
#define LLtoU64(x) (*(unsigned __int64*)(void*)(&(x)))
|
||||
|
||||
__inline
|
||||
ULONG
|
||||
WINAPI
|
||||
EnlargedUnsignedDivide (
|
||||
IN ULARGE_INTEGER Dividend,
|
||||
IN ULONG Divisor,
|
||||
IN PULONG Remainder
|
||||
)
|
||||
{
|
||||
// return remainder if necessary
|
||||
if (Remainder != NULL)
|
||||
*Remainder = (ULONG)(LLtoU64(Dividend) % Divisor);
|
||||
return (ULONG)(LLtoU64(Dividend) / Divisor);
|
||||
}
|
||||
|
||||
#else
|
||||
__inline
|
||||
ULONG
|
||||
WINAPI
|
||||
EnlargedUnsignedDivide (
|
||||
IN ULARGE_INTEGER Dividend,
|
||||
IN ULONG Divisor,
|
||||
IN PULONG Remainder
|
||||
)
|
||||
{
|
||||
ULONG ulResult;
|
||||
_asm {
|
||||
mov eax,Dividend.LowPart
|
||||
mov edx,Dividend.HighPart
|
||||
mov ecx,Remainder
|
||||
div Divisor
|
||||
or ecx,ecx
|
||||
jz short label
|
||||
mov [ecx],edx
|
||||
label:
|
||||
mov ulResult,eax
|
||||
}
|
||||
return ulResult;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Arithmetic functions to help with time format conversions
|
||||
*/
|
||||
|
||||
#ifdef _M_ALPHA
|
||||
// work around bug in version 12.00.8385 of the alpha compiler where
|
||||
// UInt32x32To64 sign-extends its arguments (?)
|
||||
#undef UInt32x32To64
|
||||
#define UInt32x32To64(a, b) (((ULONGLONG)((ULONG)(a)) & 0xffffffff) * ((ULONGLONG)((ULONG)(b)) & 0xffffffff))
|
||||
#endif
|
||||
|
||||
/* Compute (a * b + d) / c */
|
||||
LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG d)
|
||||
{
|
||||
/* Compute the absolute values to avoid signed arithmetic problems */
|
||||
ULARGE_INTEGER ua, ub;
|
||||
DWORDLONG uc;
|
||||
|
||||
ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);
|
||||
ub.QuadPart = (DWORDLONG)(b >= 0 ? b : -b);
|
||||
uc = (DWORDLONG)(c >= 0 ? c : -c);
|
||||
BOOL bSign = (a < 0) ^ (b < 0);
|
||||
|
||||
/* Do long multiplication */
|
||||
ULARGE_INTEGER p[2];
|
||||
p[0].QuadPart = UInt32x32To64(ua.LowPart, ub.LowPart);
|
||||
|
||||
/* This next computation cannot overflow into p[1].HighPart because
|
||||
the max number we can compute here is:
|
||||
|
||||
(2 ** 32 - 1) * (2 ** 32 - 1) + // ua.LowPart * ub.LowPart
|
||||
(2 ** 32) * (2 ** 31) * (2 ** 32 - 1) * 2 // x.LowPart * y.HighPart * 2
|
||||
|
||||
== 2 ** 96 - 2 ** 64 + (2 ** 64 - 2 ** 33 + 1)
|
||||
== 2 ** 96 - 2 ** 33 + 1
|
||||
< 2 ** 96
|
||||
*/
|
||||
|
||||
ULARGE_INTEGER x;
|
||||
x.QuadPart = UInt32x32To64(ua.LowPart, ub.HighPart) +
|
||||
UInt32x32To64(ua.HighPart, ub.LowPart) +
|
||||
p[0].HighPart;
|
||||
p[0].HighPart = x.LowPart;
|
||||
p[1].QuadPart = UInt32x32To64(ua.HighPart, ub.HighPart) + x.HighPart;
|
||||
|
||||
if (d != 0) {
|
||||
ULARGE_INTEGER ud[2];
|
||||
if (bSign) {
|
||||
ud[0].QuadPart = (DWORDLONG)(-d);
|
||||
if (d > 0) {
|
||||
/* -d < 0 */
|
||||
ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;
|
||||
} else {
|
||||
ud[1].QuadPart = (DWORDLONG)0;
|
||||
}
|
||||
} else {
|
||||
ud[0].QuadPart = (DWORDLONG)d;
|
||||
if (d < 0) {
|
||||
ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;
|
||||
} else {
|
||||
ud[1].QuadPart = (DWORDLONG)0;
|
||||
}
|
||||
}
|
||||
/* Now do extended addition */
|
||||
ULARGE_INTEGER uliTotal;
|
||||
|
||||
/* Add ls DWORDs */
|
||||
uliTotal.QuadPart = (DWORDLONG)ud[0].LowPart + p[0].LowPart;
|
||||
p[0].LowPart = uliTotal.LowPart;
|
||||
|
||||
/* Propagate carry */
|
||||
uliTotal.LowPart = uliTotal.HighPart;
|
||||
uliTotal.HighPart = 0;
|
||||
|
||||
/* Add 2nd most ls DWORDs */
|
||||
uliTotal.QuadPart += (DWORDLONG)ud[0].HighPart + p[0].HighPart;
|
||||
p[0].HighPart = uliTotal.LowPart;
|
||||
|
||||
/* Propagate carry */
|
||||
uliTotal.LowPart = uliTotal.HighPart;
|
||||
uliTotal.HighPart = 0;
|
||||
|
||||
/* Add MS DWORDLONGs - no carry expected */
|
||||
p[1].QuadPart += ud[1].QuadPart + uliTotal.QuadPart;
|
||||
|
||||
/* Now see if we got a sign change from the addition */
|
||||
if ((LONG)p[1].HighPart < 0) {
|
||||
bSign = !bSign;
|
||||
|
||||
/* Negate the current value (ugh!) */
|
||||
p[0].QuadPart = ~p[0].QuadPart;
|
||||
p[1].QuadPart = ~p[1].QuadPart;
|
||||
p[0].QuadPart += 1;
|
||||
p[1].QuadPart += (p[0].QuadPart == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now for the division */
|
||||
if (c < 0) {
|
||||
bSign = !bSign;
|
||||
}
|
||||
|
||||
|
||||
/* This will catch c == 0 and overflow */
|
||||
if (uc <= p[1].QuadPart) {
|
||||
return bSign ? (LONGLONG)0x8000000000000000 :
|
||||
(LONGLONG)0x7FFFFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
DWORDLONG ullResult;
|
||||
|
||||
/* Do the division */
|
||||
/* If the dividend is a DWORD_LONG use the compiler */
|
||||
if (p[1].QuadPart == 0) {
|
||||
ullResult = p[0].QuadPart / uc;
|
||||
return bSign ? -(LONGLONG)ullResult : (LONGLONG)ullResult;
|
||||
}
|
||||
|
||||
/* If the divisor is a DWORD then its simpler */
|
||||
ULARGE_INTEGER ulic;
|
||||
ulic.QuadPart = uc;
|
||||
if (ulic.HighPart == 0) {
|
||||
ULARGE_INTEGER uliDividend;
|
||||
ULARGE_INTEGER uliResult;
|
||||
DWORD dwDivisor = (DWORD)uc;
|
||||
// ASSERT(p[1].HighPart == 0 && p[1].LowPart < dwDivisor);
|
||||
uliDividend.HighPart = p[1].LowPart;
|
||||
uliDividend.LowPart = p[0].HighPart;
|
||||
#ifndef USE_LARGEINT
|
||||
uliResult.HighPart = (DWORD)(uliDividend.QuadPart / dwDivisor);
|
||||
p[0].HighPart = (DWORD)(uliDividend.QuadPart % dwDivisor);
|
||||
uliResult.LowPart = 0;
|
||||
uliResult.QuadPart = p[0].QuadPart / dwDivisor + uliResult.QuadPart;
|
||||
#else
|
||||
/* NOTE - this routine will take exceptions if
|
||||
the result does not fit in a DWORD
|
||||
*/
|
||||
if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {
|
||||
uliResult.HighPart = EnlargedUnsignedDivide(
|
||||
uliDividend,
|
||||
dwDivisor,
|
||||
&p[0].HighPart);
|
||||
} else {
|
||||
uliResult.HighPart = 0;
|
||||
}
|
||||
uliResult.LowPart = EnlargedUnsignedDivide(
|
||||
p[0],
|
||||
dwDivisor,
|
||||
NULL);
|
||||
#endif
|
||||
return bSign ? -(LONGLONG)uliResult.QuadPart :
|
||||
(LONGLONG)uliResult.QuadPart;
|
||||
}
|
||||
|
||||
|
||||
ullResult = 0;
|
||||
|
||||
/* OK - do long division */
|
||||
for (int i = 0; i < 64; i++) {
|
||||
ullResult <<= 1;
|
||||
|
||||
/* Shift 128 bit p left 1 */
|
||||
p[1].QuadPart <<= 1;
|
||||
if ((p[0].HighPart & 0x80000000) != 0) {
|
||||
p[1].LowPart++;
|
||||
}
|
||||
p[0].QuadPart <<= 1;
|
||||
|
||||
/* Compare */
|
||||
if (uc <= p[1].QuadPart) {
|
||||
p[1].QuadPart -= uc;
|
||||
ullResult += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return bSign ? - (LONGLONG)ullResult : (LONGLONG)ullResult;
|
||||
}
|
||||
|
||||
LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG d)
|
||||
{
|
||||
ULARGE_INTEGER ua;
|
||||
DWORD ub;
|
||||
DWORD uc;
|
||||
|
||||
/* Compute the absolute values to avoid signed arithmetic problems */
|
||||
ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);
|
||||
ub = (DWORD)(b >= 0 ? b : -b);
|
||||
uc = (DWORD)(c >= 0 ? c : -c);
|
||||
BOOL bSign = (a < 0) ^ (b < 0);
|
||||
|
||||
/* Do long multiplication */
|
||||
ULARGE_INTEGER p0;
|
||||
DWORD p1;
|
||||
p0.QuadPart = UInt32x32To64(ua.LowPart, ub);
|
||||
|
||||
if (ua.HighPart != 0) {
|
||||
ULARGE_INTEGER x;
|
||||
x.QuadPart = UInt32x32To64(ua.HighPart, ub) + p0.HighPart;
|
||||
p0.HighPart = x.LowPart;
|
||||
p1 = x.HighPart;
|
||||
} else {
|
||||
p1 = 0;
|
||||
}
|
||||
|
||||
if (d != 0) {
|
||||
ULARGE_INTEGER ud0;
|
||||
DWORD ud1;
|
||||
|
||||
if (bSign) {
|
||||
//
|
||||
// Cast d to LONGLONG first otherwise -0x80000000 sign extends
|
||||
// incorrectly
|
||||
//
|
||||
ud0.QuadPart = (DWORDLONG)(-(LONGLONG)d);
|
||||
if (d > 0) {
|
||||
/* -d < 0 */
|
||||
ud1 = (DWORD)-1;
|
||||
} else {
|
||||
ud1 = (DWORD)0;
|
||||
}
|
||||
} else {
|
||||
ud0.QuadPart = (DWORDLONG)d;
|
||||
if (d < 0) {
|
||||
ud1 = (DWORD)-1;
|
||||
} else {
|
||||
ud1 = (DWORD)0;
|
||||
}
|
||||
}
|
||||
/* Now do extended addition */
|
||||
ULARGE_INTEGER uliTotal;
|
||||
|
||||
/* Add ls DWORDs */
|
||||
uliTotal.QuadPart = (DWORDLONG)ud0.LowPart + p0.LowPart;
|
||||
p0.LowPart = uliTotal.LowPart;
|
||||
|
||||
/* Propagate carry */
|
||||
uliTotal.LowPart = uliTotal.HighPart;
|
||||
uliTotal.HighPart = 0;
|
||||
|
||||
/* Add 2nd most ls DWORDs */
|
||||
uliTotal.QuadPart += (DWORDLONG)ud0.HighPart + p0.HighPart;
|
||||
p0.HighPart = uliTotal.LowPart;
|
||||
|
||||
/* Add MS DWORDLONGs - no carry expected */
|
||||
p1 += ud1 + uliTotal.HighPart;
|
||||
|
||||
/* Now see if we got a sign change from the addition */
|
||||
if ((LONG)p1 < 0) {
|
||||
bSign = !bSign;
|
||||
|
||||
/* Negate the current value (ugh!) */
|
||||
p0.QuadPart = ~p0.QuadPart;
|
||||
p1 = ~p1;
|
||||
p0.QuadPart += 1;
|
||||
p1 += (p0.QuadPart == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now for the division */
|
||||
if (c < 0) {
|
||||
bSign = !bSign;
|
||||
}
|
||||
|
||||
|
||||
/* This will catch c == 0 and overflow */
|
||||
if (uc <= p1) {
|
||||
return bSign ? (LONGLONG)0x8000000000000000 :
|
||||
(LONGLONG)0x7FFFFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
/* Do the division */
|
||||
|
||||
/* If the divisor is a DWORD then its simpler */
|
||||
ULARGE_INTEGER uliDividend;
|
||||
ULARGE_INTEGER uliResult;
|
||||
DWORD dwDivisor = uc;
|
||||
uliDividend.HighPart = p1;
|
||||
uliDividend.LowPart = p0.HighPart;
|
||||
/* NOTE - this routine will take exceptions if
|
||||
the result does not fit in a DWORD
|
||||
*/
|
||||
if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {
|
||||
uliResult.HighPart = EnlargedUnsignedDivide(
|
||||
uliDividend,
|
||||
dwDivisor,
|
||||
&p0.HighPart);
|
||||
} else {
|
||||
uliResult.HighPart = 0;
|
||||
}
|
||||
uliResult.LowPart = EnlargedUnsignedDivide(
|
||||
p0,
|
||||
dwDivisor,
|
||||
NULL);
|
||||
return bSign ? -(LONGLONG)uliResult.QuadPart :
|
||||
(LONGLONG)uliResult.QuadPart;
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.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="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}</ProjectGuid>
|
||||
<RootNamespace>BaseClasses</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(SolutionDir)common.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>Windows7.1SDK</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>Windows7.1SDK</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>Windows7.1SDK</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>Windows7.1SDK</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
|
||||
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
|
||||
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
|
||||
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
|
||||
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
||||
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
||||
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">strmbasd</TargetName>
|
||||
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">strmbasd</TargetName>
|
||||
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">strmbase</TargetName>
|
||||
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">strmbase</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)strmbasd.lib</OutputFile>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)strmbase.lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)strmbasd.lib</OutputFile>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)strmbase.lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="amextra.cpp" />
|
||||
<ClCompile Include="amfilter.cpp" />
|
||||
<ClCompile Include="amvideo.cpp" />
|
||||
<ClCompile Include="arithutil.cpp" />
|
||||
<ClCompile Include="combase.cpp" />
|
||||
<ClCompile Include="cprop.cpp" />
|
||||
<ClCompile Include="ctlutil.cpp" />
|
||||
<ClCompile Include="ddmm.cpp" />
|
||||
<ClCompile Include="dllentry.cpp" />
|
||||
<ClCompile Include="dllsetup.cpp" />
|
||||
<ClCompile Include="mtype.cpp" />
|
||||
<ClCompile Include="outputq.cpp" />
|
||||
<ClCompile Include="perflog.cpp" />
|
||||
<ClCompile Include="pstream.cpp" />
|
||||
<ClCompile Include="pullpin.cpp" />
|
||||
<ClCompile Include="refclock.cpp" />
|
||||
<ClCompile Include="renbase.cpp" />
|
||||
<ClCompile Include="schedule.cpp" />
|
||||
<ClCompile Include="seekpt.cpp" />
|
||||
<ClCompile Include="source.cpp" />
|
||||
<ClCompile Include="strmctl.cpp" />
|
||||
<ClCompile Include="sysclock.cpp" />
|
||||
<ClCompile Include="transfrm.cpp" />
|
||||
<ClCompile Include="transip.cpp" />
|
||||
<ClCompile Include="videoctl.cpp" />
|
||||
<ClCompile Include="vtrans.cpp" />
|
||||
<ClCompile Include="winctrl.cpp" />
|
||||
<ClCompile Include="winutil.cpp" />
|
||||
<ClCompile Include="wxdebug.cpp" />
|
||||
<ClCompile Include="wxlist.cpp" />
|
||||
<ClCompile Include="wxutil.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="amextra.h" />
|
||||
<ClInclude Include="amfilter.h" />
|
||||
<ClInclude Include="cache.h" />
|
||||
<ClInclude Include="combase.h" />
|
||||
<ClInclude Include="cprop.h" />
|
||||
<ClInclude Include="ctlutil.h" />
|
||||
<ClInclude Include="ddmm.h" />
|
||||
<ClInclude Include="dllsetup.h" />
|
||||
<ClInclude Include="dxmperf.h" />
|
||||
<ClInclude Include="fourcc.h" />
|
||||
<ClInclude Include="measure.h" />
|
||||
<ClInclude Include="msgthrd.h" />
|
||||
<ClInclude Include="mtype.h" />
|
||||
<ClInclude Include="outputq.h" />
|
||||
<ClInclude Include="perflog.h" />
|
||||
<ClInclude Include="perfstruct.h" />
|
||||
<ClInclude Include="pstream.h" />
|
||||
<ClInclude Include="pullpin.h" />
|
||||
<ClInclude Include="refclock.h" />
|
||||
<ClInclude Include="reftime.h" />
|
||||
<ClInclude Include="renbase.h" />
|
||||
<ClInclude Include="schedule.h" />
|
||||
<ClInclude Include="seekpt.h" />
|
||||
<ClInclude Include="source.h" />
|
||||
<ClInclude Include="streams.h" />
|
||||
<ClInclude Include="strmctl.h" />
|
||||
<ClInclude Include="sysclock.h" />
|
||||
<ClInclude Include="transfrm.h" />
|
||||
<ClInclude Include="transip.h" />
|
||||
<ClInclude Include="videoctl.h" />
|
||||
<ClInclude Include="vtrans.h" />
|
||||
<ClInclude Include="winctrl.h" />
|
||||
<ClInclude Include="winutil.h" />
|
||||
<ClInclude Include="wxdebug.h" />
|
||||
<ClInclude Include="wxlist.h" />
|
||||
<ClInclude Include="wxutil.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,222 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="amextra.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="amfilter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="amvideo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="arithutil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="combase.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cprop.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ctlutil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ddmm.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dllentry.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dllsetup.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="mtype.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="outputq.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="perflog.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pstream.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pullpin.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="refclock.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="renbase.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="schedule.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="seekpt.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="strmctl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sysclock.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="transfrm.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="transip.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="videoctl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vtrans.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="winctrl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="winutil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="wxdebug.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="wxlist.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="wxutil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="amextra.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="amfilter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="combase.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cprop.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ctlutil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ddmm.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dllsetup.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dxmperf.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="fourcc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="measure.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msgthrd.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="mtype.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="outputq.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="perflog.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="perfstruct.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pstream.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pullpin.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="refclock.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="reftime.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="renbase.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="schedule.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="seekpt.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="source.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="streams.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="strmctl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sysclock.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="transfrm.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="transip.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="videoctl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vtrans.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="winctrl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="winutil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="wxdebug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="wxlist.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="wxutil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,74 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Cache.h
|
||||
//
|
||||
// Desc: DirectShow base classes - efines a non-MFC generic cache class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/* This class implements a simple cache. A cache object is instantiated
|
||||
with the number of items it is to hold. An item is a pointer to an
|
||||
object derived from CBaseObject (helps reduce memory leaks). The cache
|
||||
can then have objects added to it and removed from it. The cache size
|
||||
is fixed at construction time and may therefore run out or be flooded.
|
||||
If it runs out it returns a NULL pointer, if it fills up it also returns
|
||||
a NULL pointer instead of a pointer to the object just inserted */
|
||||
|
||||
/* Making these classes inherit from CBaseObject does nothing for their
|
||||
functionality but it allows us to check there are no memory leaks */
|
||||
|
||||
/* WARNING Be very careful when using this class, what it lets you do is
|
||||
store and retrieve objects so that you can minimise object creation
|
||||
which in turns improves efficiency. However the object you store is
|
||||
exactly the same as the object you get back which means that it short
|
||||
circuits the constructor initialisation phase. This means any class
|
||||
variables the object has (eg pointers) are highly likely to be invalid.
|
||||
Therefore ensure you reinitialise the object before using it again */
|
||||
|
||||
|
||||
#ifndef __CACHE__
|
||||
#define __CACHE__
|
||||
|
||||
|
||||
class CCache : CBaseObject {
|
||||
|
||||
/* Make copy constructor and assignment operator inaccessible */
|
||||
|
||||
CCache(const CCache &refCache);
|
||||
CCache &operator=(const CCache &refCache);
|
||||
|
||||
private:
|
||||
|
||||
/* These are initialised in the constructor. The first variable points to
|
||||
an array of pointers, each of which points to a CBaseObject derived
|
||||
object. The m_iCacheSize is the static fixed size for the cache and the
|
||||
m_iUsed defines the number of places filled with objects at any time.
|
||||
We fill the array of pointers from the start (ie m_ppObjects[0] first)
|
||||
and then only add and remove objects from the end position, so in this
|
||||
respect the array of object pointers should be treated as a stack */
|
||||
|
||||
CBaseObject **m_ppObjects;
|
||||
const INT m_iCacheSize;
|
||||
INT m_iUsed;
|
||||
|
||||
public:
|
||||
|
||||
CCache(__in_opt LPCTSTR pName,INT iItems);
|
||||
virtual ~CCache();
|
||||
|
||||
/* Add an item to the cache */
|
||||
CBaseObject *AddToCache(__in CBaseObject *pObject);
|
||||
|
||||
/* Remove an item from the cache */
|
||||
CBaseObject *RemoveFromCache();
|
||||
|
||||
/* Delete all the objects held in the cache */
|
||||
void RemoveAll(void);
|
||||
|
||||
/* Return the cache size which is set during construction */
|
||||
INT GetCacheSize(void) const {return m_iCacheSize;};
|
||||
};
|
||||
|
||||
#endif /* __CACHE__ */
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved.
|
||||
|
||||
#ifndef _CHECKBMI_H_
|
||||
#define _CHECKBMI_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Helper
|
||||
__inline BOOL MultiplyCheckOverflow(DWORD a, DWORD b, __deref_out_range(==, a * b) DWORD *pab) {
|
||||
*pab = a * b;
|
||||
if ((a == 0) || (((*pab) / a) == b)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// Checks if the fields in a BITMAPINFOHEADER won't generate
|
||||
// overlows and buffer overruns
|
||||
// This is not a complete check and does not guarantee code using this structure will be secure
|
||||
// from attack
|
||||
// Bugs this is guarding against:
|
||||
// 1. Total structure size calculation overflowing
|
||||
// 2. biClrUsed > 256 for 8-bit palettized content
|
||||
// 3. Total bitmap size in bytes overflowing
|
||||
// 4. biSize < size of the base structure leading to accessessing random memory
|
||||
// 5. Total structure size exceeding know size of data
|
||||
//
|
||||
|
||||
__success(return != 0) __inline BOOL ValidateBitmapInfoHeader(
|
||||
const BITMAPINFOHEADER *pbmi, // pointer to structure to check
|
||||
__out_range(>=, sizeof(BITMAPINFOHEADER)) DWORD cbSize // size of memory block containing structure
|
||||
)
|
||||
{
|
||||
DWORD dwWidthInBytes;
|
||||
DWORD dwBpp;
|
||||
DWORD dwWidthInBits;
|
||||
DWORD dwHeight;
|
||||
DWORD dwSizeImage;
|
||||
DWORD dwClrUsed;
|
||||
|
||||
// Reject bad parameters - do the size check first to avoid reading bad memory
|
||||
if (cbSize < sizeof(BITMAPINFOHEADER) ||
|
||||
pbmi->biSize < sizeof(BITMAPINFOHEADER) ||
|
||||
pbmi->biSize > 4096) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Reject 0 size
|
||||
if (pbmi->biWidth == 0 || pbmi->biHeight == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Use bpp of 200 for validating against further overflows if not set for compressed format
|
||||
dwBpp = 200;
|
||||
|
||||
if (pbmi->biBitCount > dwBpp) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Strictly speaking abs can overflow so cast explicitly to DWORD
|
||||
dwHeight = (DWORD)abs(pbmi->biHeight);
|
||||
|
||||
if (!MultiplyCheckOverflow(dwBpp, (DWORD)pbmi->biWidth, &dwWidthInBits)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Compute correct width in bytes - rounding up to 4 bytes
|
||||
dwWidthInBytes = (dwWidthInBits / 8 + 3) & ~3;
|
||||
|
||||
if (!MultiplyCheckOverflow(dwWidthInBytes, dwHeight, &dwSizeImage)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Fail if total size is 0 - this catches indivual quantities being 0
|
||||
// Also don't allow huge values > 1GB which might cause arithmetic
|
||||
// errors for users
|
||||
if (dwSizeImage > 0x40000000 ||
|
||||
pbmi->biSizeImage > 0x40000000) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Fail if biClrUsed looks bad
|
||||
if (pbmi->biClrUsed > 256) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pbmi->biClrUsed == 0 && pbmi->biBitCount <= 8 && pbmi->biBitCount > 0) {
|
||||
dwClrUsed = (1 << pbmi->biBitCount);
|
||||
} else {
|
||||
dwClrUsed = pbmi->biClrUsed;
|
||||
}
|
||||
|
||||
// Check total size
|
||||
if (cbSize < pbmi->biSize + dwClrUsed * sizeof(RGBQUAD) +
|
||||
(pbmi->biCompression == BI_BITFIELDS ? 3 * sizeof(DWORD) : 0)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// If it is RGB validate biSizeImage - lots of code assumes the size is correct
|
||||
if (pbmi->biCompression == BI_RGB || pbmi->biCompression == BI_BITFIELDS) {
|
||||
if (pbmi->biSizeImage != 0) {
|
||||
DWORD dwBits = (DWORD)pbmi->biWidth * (DWORD)pbmi->biBitCount;
|
||||
DWORD dwWidthInBytes = ((DWORD)((dwBits+31) & (~31)) / 8);
|
||||
DWORD dwTotalSize = (DWORD)abs(pbmi->biHeight) * dwWidthInBytes;
|
||||
if (dwTotalSize > pbmi->biSizeImage) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CHECKBMI_H_
|
|
@ -0,0 +1,265 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: ComBase.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements class hierarchy for creating
|
||||
// COM objects.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#pragma warning( disable : 4514 ) // Disable warnings re unused inline functions
|
||||
|
||||
|
||||
/* Define the static member variable */
|
||||
|
||||
LONG CBaseObject::m_cObjects = 0;
|
||||
|
||||
|
||||
/* Constructor */
|
||||
|
||||
CBaseObject::CBaseObject(__in_opt LPCTSTR pName)
|
||||
{
|
||||
/* Increment the number of active objects */
|
||||
InterlockedIncrement(&m_cObjects);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#ifdef UNICODE
|
||||
m_dwCookie = DbgRegisterObjectCreation(0, pName);
|
||||
#else
|
||||
m_dwCookie = DbgRegisterObjectCreation(pName, 0);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CBaseObject::CBaseObject(const char *pName)
|
||||
{
|
||||
/* Increment the number of active objects */
|
||||
InterlockedIncrement(&m_cObjects);
|
||||
|
||||
#ifdef DEBUG
|
||||
m_dwCookie = DbgRegisterObjectCreation(pName, 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
HINSTANCE hlibOLEAut32;
|
||||
|
||||
/* Destructor */
|
||||
|
||||
CBaseObject::~CBaseObject()
|
||||
{
|
||||
/* Decrement the number of objects active */
|
||||
if (InterlockedDecrement(&m_cObjects) == 0) {
|
||||
if (hlibOLEAut32) {
|
||||
FreeLibrary(hlibOLEAut32);
|
||||
|
||||
hlibOLEAut32 = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
DbgRegisterObjectDestruction(m_dwCookie);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TCHAR szOle32Aut[] = TEXT("OleAut32.dll");
|
||||
|
||||
HINSTANCE LoadOLEAut32()
|
||||
{
|
||||
if (hlibOLEAut32 == 0) {
|
||||
|
||||
hlibOLEAut32 = LoadLibrary(szOle32Aut);
|
||||
}
|
||||
|
||||
return hlibOLEAut32;
|
||||
}
|
||||
|
||||
|
||||
/* Constructor */
|
||||
|
||||
// We know we use "this" in the initialization list, we also know we don't modify *phr.
|
||||
#pragma warning( disable : 4355 4100 )
|
||||
CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk)
|
||||
: CBaseObject(pName)
|
||||
/* Start the object with a reference count of zero - when the */
|
||||
/* object is queried for it's first interface this may be */
|
||||
/* incremented depending on whether or not this object is */
|
||||
/* currently being aggregated upon */
|
||||
, m_cRef(0)
|
||||
/* Set our pointer to our IUnknown interface. */
|
||||
/* If we have an outer, use its, otherwise use ours. */
|
||||
/* This pointer effectivly points to the owner of */
|
||||
/* this object and can be accessed by the GetOwner() method. */
|
||||
, m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
/* Why the double cast? Well, the inner cast is a type-safe cast */
|
||||
/* to pointer to a type from which we inherit. The second is */
|
||||
/* type-unsafe but works because INonDelegatingUnknown "behaves */
|
||||
/* like" IUnknown. (Only the names on the methods change.) */
|
||||
{
|
||||
// Everything we need to do has been done in the initializer list
|
||||
}
|
||||
|
||||
// This does the same as above except it has a useless HRESULT argument
|
||||
// use the previous constructor, this is just left for compatibility...
|
||||
CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :
|
||||
CBaseObject(pName),
|
||||
m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk)
|
||||
: CBaseObject(pName), m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{ }
|
||||
|
||||
CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :
|
||||
CBaseObject(pName), m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{ }
|
||||
|
||||
#endif
|
||||
|
||||
#pragma warning( default : 4355 4100 )
|
||||
|
||||
|
||||
/* QueryInterface */
|
||||
|
||||
STDMETHODIMP CUnknown::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
|
||||
{
|
||||
CheckPointer(ppv,E_POINTER);
|
||||
ValidateReadWritePtr(ppv,sizeof(PVOID));
|
||||
|
||||
/* We know only about IUnknown */
|
||||
|
||||
if (riid == IID_IUnknown) {
|
||||
GetInterface((LPUNKNOWN) (PNDUNKNOWN) this, ppv);
|
||||
return NOERROR;
|
||||
} else {
|
||||
*ppv = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have to ensure that we DON'T use a max macro, since these will typically */
|
||||
/* lead to one of the parameters being evaluated twice. Since we are worried */
|
||||
/* about concurrency, we can't afford to access the m_cRef twice since we can't */
|
||||
/* afford to run the risk that its value having changed between accesses. */
|
||||
|
||||
template<class T> inline static T ourmax( const T & a, const T & b )
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
/* AddRef */
|
||||
|
||||
STDMETHODIMP_(ULONG) CUnknown::NonDelegatingAddRef()
|
||||
{
|
||||
LONG lRef = InterlockedIncrement( &m_cRef );
|
||||
ASSERT(lRef > 0);
|
||||
DbgLog((LOG_MEMORY,3,TEXT(" Obj %d ref++ = %d"),
|
||||
m_dwCookie, m_cRef));
|
||||
return ourmax(ULONG(m_cRef), 1ul);
|
||||
}
|
||||
|
||||
|
||||
/* Release */
|
||||
|
||||
STDMETHODIMP_(ULONG) CUnknown::NonDelegatingRelease()
|
||||
{
|
||||
/* If the reference count drops to zero delete ourselves */
|
||||
|
||||
LONG lRef = InterlockedDecrement( &m_cRef );
|
||||
ASSERT(lRef >= 0);
|
||||
|
||||
DbgLog((LOG_MEMORY,3,TEXT(" Object %d ref-- = %d"),
|
||||
m_dwCookie, m_cRef));
|
||||
if (lRef == 0) {
|
||||
|
||||
// COM rules say we must protect against re-entrancy.
|
||||
// If we are an aggregator and we hold our own interfaces
|
||||
// on the aggregatee, the QI for these interfaces will
|
||||
// addref ourselves. So after doing the QI we must release
|
||||
// a ref count on ourselves. Then, before releasing the
|
||||
// private interface, we must addref ourselves. When we do
|
||||
// this from the destructor here it will result in the ref
|
||||
// count going to 1 and then back to 0 causing us to
|
||||
// re-enter the destructor. Hence we add an extra refcount here
|
||||
// once we know we will delete the object.
|
||||
// for an example aggregator see filgraph\distrib.cpp.
|
||||
|
||||
m_cRef++;
|
||||
|
||||
delete this;
|
||||
return ULONG(0);
|
||||
} else {
|
||||
// Don't touch m_cRef again even in this leg as the object
|
||||
// may have just been released on another thread too
|
||||
return ourmax(ULONG(lRef), 1ul);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return an interface pointer to a requesting client
|
||||
performing a thread safe AddRef as necessary */
|
||||
|
||||
STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv)
|
||||
{
|
||||
CheckPointer(ppv, E_POINTER);
|
||||
*ppv = pUnk;
|
||||
pUnk->AddRef();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Compares two interfaces and returns TRUE if they are on the same object */
|
||||
|
||||
BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond)
|
||||
{
|
||||
/* Different objects can't have the same interface pointer for
|
||||
any interface
|
||||
*/
|
||||
if (pFirst == pSecond) {
|
||||
return TRUE;
|
||||
}
|
||||
/* OK - do it the hard way - check if they have the same
|
||||
IUnknown pointers - a single object can only have one of these
|
||||
*/
|
||||
LPUNKNOWN pUnknown1; // Retrieve the IUnknown interface
|
||||
LPUNKNOWN pUnknown2; // Retrieve the other IUnknown interface
|
||||
HRESULT hr; // General OLE return code
|
||||
|
||||
ASSERT(pFirst);
|
||||
ASSERT(pSecond);
|
||||
|
||||
/* See if the IUnknown pointers match */
|
||||
|
||||
hr = pFirst->QueryInterface(IID_IUnknown,(void **) &pUnknown1);
|
||||
if (FAILED(hr)) {
|
||||
return FALSE;
|
||||
}
|
||||
ASSERT(pUnknown1);
|
||||
|
||||
/* Release the extra interface we hold */
|
||||
|
||||
pUnknown1->Release();
|
||||
|
||||
hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2);
|
||||
if (FAILED(hr)) {
|
||||
return FALSE;
|
||||
}
|
||||
ASSERT(pUnknown2);
|
||||
|
||||
/* Release the extra interface we hold */
|
||||
|
||||
pUnknown2->Release();
|
||||
return (pUnknown1 == pUnknown2);
|
||||
}
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: ComBase.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class hierarchy for creating
|
||||
// COM objects.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
|
||||
a. Derive your COM object from CUnknown
|
||||
|
||||
b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
|
||||
and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
|
||||
to. The HRESULT * allows error codes to be passed around constructors and
|
||||
the TCHAR * is a descriptive name that can be printed on the debugger.
|
||||
|
||||
It is important that constructors only change the HRESULT * if they have
|
||||
to set an ERROR code, if it was successful then leave it alone or you may
|
||||
overwrite an error code from an object previously created.
|
||||
|
||||
When you call a constructor the descriptive name should be in static store
|
||||
as we do not copy the string. To stop large amounts of memory being used
|
||||
in retail builds by all these static strings use the NAME macro,
|
||||
|
||||
CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
|
||||
knows not to do anything with objects that don't have a name.
|
||||
|
||||
c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
|
||||
TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an
|
||||
error, or just simply pass it through to the constructor.
|
||||
|
||||
The object creation will fail in the class factory if the HRESULT indicates
|
||||
an error (ie FAILED(HRESULT) == TRUE)
|
||||
|
||||
d. Create a FactoryTemplate with your object's class id and CreateInstance
|
||||
function.
|
||||
|
||||
Then (for each interface) either
|
||||
|
||||
Multiple inheritance
|
||||
|
||||
1. Also derive it from ISomeInterface
|
||||
2. Include DECLARE_IUNKNOWN in your class definition to declare
|
||||
implementations of QueryInterface, AddRef and Release that
|
||||
call the outer unknown
|
||||
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
||||
code something like
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return GetInterface((ISomeInterface *) this, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
4. Declare and implement the member functions of ISomeInterface.
|
||||
|
||||
or: Nested interfaces
|
||||
|
||||
1. Declare a class derived from CUnknown
|
||||
2. Include DECLARE_IUNKNOWN in your class definition
|
||||
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
||||
code something like
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return GetInterface((ISomeInterface *) this, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
4. Implement the member functions of ISomeInterface. Use GetOwner() to
|
||||
access the COM object class.
|
||||
|
||||
And in your COM object class:
|
||||
|
||||
5. Make the nested class a friend of the COM object class, and declare
|
||||
an instance of the nested class as a member of the COM object class.
|
||||
|
||||
NOTE that because you must always pass the outer unknown and an hResult
|
||||
to the CUnknown constructor you cannot use a default constructor, in
|
||||
other words you will have to make the member variable a pointer to the
|
||||
class and make a NEW call in your constructor to actually create it.
|
||||
|
||||
6. override the NonDelegatingQueryInterface with code like this:
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return m_pImplFilter->
|
||||
NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
You can have mixed classes which support some interfaces via multiple
|
||||
inheritance and some via nested classes
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __COMBASE__
|
||||
#define __COMBASE__
|
||||
|
||||
// Filter Setup data structures no defined in axextend.idl
|
||||
|
||||
typedef REGPINTYPES
|
||||
AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;
|
||||
|
||||
typedef REGFILTERPINS
|
||||
AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;
|
||||
|
||||
typedef struct _AMOVIESETUP_FILTER
|
||||
{
|
||||
const CLSID * clsID;
|
||||
const WCHAR * strName;
|
||||
DWORD dwMerit;
|
||||
UINT nPins;
|
||||
const AMOVIESETUP_PIN * lpPin;
|
||||
}
|
||||
AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;
|
||||
|
||||
/* The DLLENTRY module initialises the module handle on loading */
|
||||
|
||||
extern HINSTANCE g_hInst;
|
||||
|
||||
/* On DLL load remember which platform we are running on */
|
||||
|
||||
extern DWORD g_amPlatform;
|
||||
extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx
|
||||
|
||||
/* Version of IUnknown that is renamed to allow a class to support both
|
||||
non delegating and delegating IUnknowns in the same COM object */
|
||||
|
||||
#ifndef INONDELEGATINGUNKNOWN_DEFINED
|
||||
DECLARE_INTERFACE(INonDelegatingUnknown)
|
||||
{
|
||||
STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
|
||||
STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
|
||||
STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
|
||||
};
|
||||
#define INONDELEGATINGUNKNOWN_DEFINED
|
||||
#endif
|
||||
|
||||
typedef INonDelegatingUnknown *PNDUNKNOWN;
|
||||
|
||||
|
||||
/* This is the base object class that supports active object counting. As
|
||||
part of the debug facilities we trace every time a C++ object is created
|
||||
or destroyed. The name of the object has to be passed up through the class
|
||||
derivation list during construction as you cannot call virtual functions
|
||||
in the constructor. The downside of all this is that every single object
|
||||
constructor has to take an object name parameter that describes it */
|
||||
|
||||
class CBaseObject
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
// Disable the copy constructor and assignment by default so you will get
|
||||
// compiler errors instead of unexpected behaviour if you pass objects
|
||||
// by value or assign objects.
|
||||
CBaseObject(const CBaseObject& objectSrc); // no implementation
|
||||
void operator=(const CBaseObject& objectSrc); // no implementation
|
||||
|
||||
private:
|
||||
static LONG m_cObjects; /* Total number of objects active */
|
||||
|
||||
protected:
|
||||
#ifdef DEBUG
|
||||
DWORD m_dwCookie; /* Cookie identifying this object */
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/* These increment and decrement the number of active objects */
|
||||
|
||||
CBaseObject(__in_opt LPCTSTR pName);
|
||||
#ifdef UNICODE
|
||||
CBaseObject(__in_opt LPCSTR pName);
|
||||
#endif
|
||||
~CBaseObject();
|
||||
|
||||
/* Call this to find if there are any CUnknown derived objects active */
|
||||
|
||||
static LONG ObjectsActive() {
|
||||
return m_cObjects;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* An object that supports one or more COM interfaces will be based on
|
||||
this class. It supports counting of total objects for DLLCanUnloadNow
|
||||
support, and an implementation of the core non delegating IUnknown */
|
||||
|
||||
class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
|
||||
public CBaseObject
|
||||
{
|
||||
private:
|
||||
const LPUNKNOWN m_pUnknown; /* Owner of this object */
|
||||
|
||||
protected: /* So we can override NonDelegatingRelease() */
|
||||
volatile LONG m_cRef; /* Number of reference counts */
|
||||
|
||||
public:
|
||||
|
||||
CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk);
|
||||
virtual ~CUnknown() {};
|
||||
|
||||
// This is redundant, just use the other constructor
|
||||
// as we never touch the HRESULT in this anyway
|
||||
CUnknown(__in_opt LPCTSTR Name, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr);
|
||||
#ifdef UNICODE
|
||||
CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk);
|
||||
CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk,__inout_opt HRESULT *phr);
|
||||
#endif
|
||||
|
||||
/* Return the owner of this object */
|
||||
|
||||
LPUNKNOWN GetOwner() const {
|
||||
return m_pUnknown;
|
||||
};
|
||||
|
||||
/* Called from the class factory to create a new instance, it is
|
||||
pure virtual so it must be overriden in your derived class */
|
||||
|
||||
/* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
|
||||
|
||||
/* Non delegating unknown implementation */
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);
|
||||
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
|
||||
STDMETHODIMP_(ULONG) NonDelegatingRelease();
|
||||
};
|
||||
|
||||
/* Return an interface pointer to a requesting client
|
||||
performing a thread safe AddRef as necessary */
|
||||
|
||||
STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv);
|
||||
|
||||
/* A function that can create a new COM object */
|
||||
|
||||
typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(__in_opt LPUNKNOWN pUnkOuter, __inout_opt HRESULT *phr);
|
||||
|
||||
/* A function (can be NULL) which is called from the DLL entrypoint
|
||||
routine for each factory template:
|
||||
|
||||
bLoading - TRUE on DLL load, FALSE on DLL unload
|
||||
rclsid - the m_ClsID of the entry
|
||||
*/
|
||||
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
|
||||
|
||||
/* Create one of these per object class in an array so that
|
||||
the default class factory code can create new instances */
|
||||
|
||||
class CFactoryTemplate {
|
||||
|
||||
public:
|
||||
|
||||
const WCHAR * m_Name;
|
||||
const CLSID * m_ClsID;
|
||||
LPFNNewCOMObject m_lpfnNew;
|
||||
LPFNInitRoutine m_lpfnInit;
|
||||
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;
|
||||
|
||||
BOOL IsClassID(REFCLSID rclsid) const {
|
||||
return (IsEqualCLSID(*m_ClsID,rclsid));
|
||||
};
|
||||
|
||||
CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) const {
|
||||
CheckPointer(phr,NULL);
|
||||
return m_lpfnNew(pUnk, phr);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* You must override the (pure virtual) NonDelegatingQueryInterface to return
|
||||
interface pointers (using GetInterface) to the interfaces your derived
|
||||
class supports (the default implementation only supports IUnknown) */
|
||||
|
||||
#define DECLARE_IUNKNOWN \
|
||||
STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) { \
|
||||
return GetOwner()->QueryInterface(riid,ppv); \
|
||||
}; \
|
||||
STDMETHODIMP_(ULONG) AddRef() { \
|
||||
return GetOwner()->AddRef(); \
|
||||
}; \
|
||||
STDMETHODIMP_(ULONG) Release() { \
|
||||
return GetOwner()->Release(); \
|
||||
};
|
||||
|
||||
|
||||
|
||||
HINSTANCE LoadOLEAut32();
|
||||
|
||||
|
||||
#endif /* __COMBASE__ */
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,383 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: CProp.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements CBasePropertyPage class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
|
||||
// Constructor for the base property page class. As described in the header
|
||||
// file we must be initialised with dialog and title resource identifiers.
|
||||
// The class supports IPropertyPage and overrides AddRef and Release calls
|
||||
// to keep track of the reference counts. When the last count is released
|
||||
// we call SetPageSite(NULL) and SetObjects(0,NULL) to release interfaces
|
||||
// previously obtained by the property page when it had SetObjects called
|
||||
|
||||
CBasePropertyPage::CBasePropertyPage(__in_opt LPCTSTR pName, // Debug only name
|
||||
__inout_opt LPUNKNOWN pUnk, // COM Delegator
|
||||
int DialogId, // Resource ID
|
||||
int TitleId) : // To get tital
|
||||
CUnknown(pName,pUnk),
|
||||
m_DialogId(DialogId),
|
||||
m_TitleId(TitleId),
|
||||
m_hwnd(NULL),
|
||||
m_Dlg(NULL),
|
||||
m_pPageSite(NULL),
|
||||
m_bObjectSet(FALSE),
|
||||
m_bDirty(FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CBasePropertyPage::CBasePropertyPage(__in_opt LPCSTR pName, // Debug only name
|
||||
__inout_opt LPUNKNOWN pUnk, // COM Delegator
|
||||
int DialogId, // Resource ID
|
||||
int TitleId) : // To get tital
|
||||
CUnknown(pName,pUnk),
|
||||
m_DialogId(DialogId),
|
||||
m_TitleId(TitleId),
|
||||
m_hwnd(NULL),
|
||||
m_Dlg(NULL),
|
||||
m_pPageSite(NULL),
|
||||
m_bObjectSet(FALSE),
|
||||
m_bDirty(FALSE)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
// Increment our reference count
|
||||
|
||||
STDMETHODIMP_(ULONG) CBasePropertyPage::NonDelegatingAddRef()
|
||||
{
|
||||
LONG lRef = InterlockedIncrement(&m_cRef);
|
||||
ASSERT(lRef > 0);
|
||||
return max(ULONG(m_cRef),1ul);
|
||||
}
|
||||
|
||||
|
||||
// Release a reference count and protect against reentrancy
|
||||
|
||||
STDMETHODIMP_(ULONG) CBasePropertyPage::NonDelegatingRelease()
|
||||
{
|
||||
// If the reference count drops to zero delete ourselves
|
||||
|
||||
LONG lRef = InterlockedDecrement(&m_cRef);
|
||||
if (lRef == 0) {
|
||||
m_cRef++;
|
||||
SetPageSite(NULL);
|
||||
SetObjects(0,NULL);
|
||||
delete this;
|
||||
return ULONG(0);
|
||||
} else {
|
||||
// Don't touch m_cRef again here!
|
||||
return max(ULONG(lRef),1ul);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Expose our IPropertyPage interface
|
||||
|
||||
STDMETHODIMP
|
||||
CBasePropertyPage::NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv)
|
||||
{
|
||||
if (riid == IID_IPropertyPage) {
|
||||
return GetInterface((IPropertyPage *)this,ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid,ppv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get the page info so that the page site can size itself
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::GetPageInfo(__out LPPROPPAGEINFO pPageInfo)
|
||||
{
|
||||
CheckPointer(pPageInfo,E_POINTER);
|
||||
WCHAR wszTitle[STR_MAX_LENGTH];
|
||||
WideStringFromResource(wszTitle,m_TitleId);
|
||||
|
||||
// Allocate dynamic memory for the property page title
|
||||
|
||||
LPOLESTR pszTitle;
|
||||
HRESULT hr = AMGetWideString(wszTitle, &pszTitle);
|
||||
if (FAILED(hr)) {
|
||||
NOTE("No caption memory");
|
||||
return hr;
|
||||
}
|
||||
|
||||
pPageInfo->cb = sizeof(PROPPAGEINFO);
|
||||
pPageInfo->pszTitle = pszTitle;
|
||||
pPageInfo->pszDocString = NULL;
|
||||
pPageInfo->pszHelpFile = NULL;
|
||||
pPageInfo->dwHelpContext = 0;
|
||||
|
||||
// Set defaults in case GetDialogSize fails
|
||||
pPageInfo->size.cx = 340;
|
||||
pPageInfo->size.cy = 150;
|
||||
|
||||
GetDialogSize(m_DialogId, DialogProc,0L,&pPageInfo->size);
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Handles the messages for our property window
|
||||
|
||||
INT_PTR CALLBACK CBasePropertyPage::DialogProc(HWND hwnd,
|
||||
UINT uMsg,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
CBasePropertyPage *pPropertyPage;
|
||||
|
||||
switch (uMsg) {
|
||||
|
||||
case WM_INITDIALOG:
|
||||
|
||||
_SetWindowLongPtr(hwnd, DWLP_USER, lParam);
|
||||
|
||||
// This pointer may be NULL when calculating size
|
||||
|
||||
pPropertyPage = (CBasePropertyPage *) lParam;
|
||||
if (pPropertyPage == NULL) {
|
||||
return (LRESULT) 1;
|
||||
}
|
||||
pPropertyPage->m_Dlg = hwnd;
|
||||
}
|
||||
|
||||
// This pointer may be NULL when calculating size
|
||||
|
||||
pPropertyPage = _GetWindowLongPtr<CBasePropertyPage*>(hwnd, DWLP_USER);
|
||||
if (pPropertyPage == NULL) {
|
||||
return (LRESULT) 1;
|
||||
}
|
||||
return pPropertyPage->OnReceiveMessage(hwnd,uMsg,wParam,lParam);
|
||||
}
|
||||
|
||||
|
||||
// Tells us the object that should be informed of the property changes
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::SetObjects(ULONG cObjects,__in_ecount_opt(cObjects) LPUNKNOWN *ppUnk)
|
||||
{
|
||||
if (cObjects == 1) {
|
||||
|
||||
if ((ppUnk == NULL) || (*ppUnk == NULL)) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
// Set a flag to say that we have set the Object
|
||||
m_bObjectSet = TRUE ;
|
||||
return OnConnect(*ppUnk);
|
||||
|
||||
} else if (cObjects == 0) {
|
||||
|
||||
// Set a flag to say that we have not set the Object for the page
|
||||
m_bObjectSet = FALSE ;
|
||||
return OnDisconnect();
|
||||
}
|
||||
|
||||
DbgBreak("No support for more than one object");
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
|
||||
// Create the window we will use to edit properties
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::Activate(HWND hwndParent,
|
||||
LPCRECT pRect,
|
||||
BOOL fModal)
|
||||
{
|
||||
CheckPointer(pRect,E_POINTER);
|
||||
|
||||
// Return failure if SetObject has not been called.
|
||||
if (m_bObjectSet == FALSE) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (m_hwnd) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
m_hwnd = CreateDialogParam(g_hInst,
|
||||
MAKEINTRESOURCE(m_DialogId),
|
||||
hwndParent,
|
||||
DialogProc,
|
||||
(LPARAM) this);
|
||||
if (m_hwnd == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
OnActivate();
|
||||
Move(pRect);
|
||||
return Show(SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
|
||||
// Set the position of the property page
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::Move(LPCRECT pRect)
|
||||
{
|
||||
CheckPointer(pRect,E_POINTER);
|
||||
|
||||
if (m_hwnd == NULL) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
MoveWindow(m_hwnd, // Property page handle
|
||||
pRect->left, // x coordinate
|
||||
pRect->top, // y coordinate
|
||||
WIDTH(pRect), // Overall window width
|
||||
HEIGHT(pRect), // And likewise height
|
||||
TRUE); // Should we repaint it
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Display the property dialog
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::Show(UINT nCmdShow)
|
||||
{
|
||||
// Have we been activated yet
|
||||
|
||||
if (m_hwnd == NULL) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Ignore wrong show flags
|
||||
|
||||
if ((nCmdShow != SW_SHOW) && (nCmdShow != SW_SHOWNORMAL) && (nCmdShow != SW_HIDE)) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
ShowWindow(m_hwnd,nCmdShow);
|
||||
InvalidateRect(m_hwnd,NULL,TRUE);
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Destroy the property page dialog
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::Deactivate(void)
|
||||
{
|
||||
if (m_hwnd == NULL) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Remove WS_EX_CONTROLPARENT before DestroyWindow call
|
||||
|
||||
DWORD dwStyle = GetWindowLong(m_hwnd, GWL_EXSTYLE);
|
||||
dwStyle = dwStyle & (~WS_EX_CONTROLPARENT);
|
||||
|
||||
// Set m_hwnd to be NULL temporarily so the message handler
|
||||
// for WM_STYLECHANGING doesn't add the WS_EX_CONTROLPARENT
|
||||
// style back in
|
||||
HWND hwnd = m_hwnd;
|
||||
m_hwnd = NULL;
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, dwStyle);
|
||||
m_hwnd = hwnd;
|
||||
|
||||
OnDeactivate();
|
||||
|
||||
// Destroy the dialog window
|
||||
|
||||
DestroyWindow(m_hwnd);
|
||||
m_hwnd = NULL;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Tells the application property page site
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite)
|
||||
{
|
||||
if (pPageSite) {
|
||||
|
||||
if (m_pPageSite) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
m_pPageSite = pPageSite;
|
||||
m_pPageSite->AddRef();
|
||||
|
||||
} else {
|
||||
|
||||
if (m_pPageSite == NULL) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
m_pPageSite->Release();
|
||||
m_pPageSite = NULL;
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Apply any changes so far made
|
||||
|
||||
STDMETHODIMP CBasePropertyPage::Apply()
|
||||
{
|
||||
// In ActiveMovie 1.0 we used to check whether we had been activated or
|
||||
// not. This is too constrictive. Apply should be allowed as long as
|
||||
// SetObject was called to set an object. So we will no longer check to
|
||||
// see if we have been activated (ie., m_hWnd != NULL), but instead
|
||||
// make sure that m_bObjectSet is TRUE (ie., SetObject has been called).
|
||||
|
||||
if (m_bObjectSet == FALSE) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Must have had a site set
|
||||
|
||||
if (m_pPageSite == NULL) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Has anything changed
|
||||
|
||||
if (m_bDirty == FALSE) {
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Commit derived class changes
|
||||
|
||||
HRESULT hr = OnApplyChanges();
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_bDirty = FALSE;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
// Base class definition for message handling
|
||||
|
||||
INT_PTR CBasePropertyPage::OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
// we would like the TAB key to move around the tab stops in our property
|
||||
// page, but for some reason OleCreatePropertyFrame clears the CONTROLPARENT
|
||||
// style behind our back, so we need to switch it back on now behind its
|
||||
// back. Otherwise the tab key will be useless in every page.
|
||||
//
|
||||
|
||||
CBasePropertyPage *pPropertyPage;
|
||||
{
|
||||
pPropertyPage = _GetWindowLongPtr<CBasePropertyPage*>(hwnd, DWLP_USER);
|
||||
|
||||
if (pPropertyPage->m_hwnd == NULL) {
|
||||
return 0;
|
||||
}
|
||||
switch (uMsg) {
|
||||
case WM_STYLECHANGING:
|
||||
if (wParam == GWL_EXSTYLE) {
|
||||
LPSTYLESTRUCT lpss = (LPSTYLESTRUCT)lParam;
|
||||
lpss->styleNew |= WS_EX_CONTROLPARENT;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd,uMsg,wParam,lParam);
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: CProp.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __CPROP__
|
||||
#define __CPROP__
|
||||
|
||||
// Base property page class. Filters typically expose custom properties by
|
||||
// implementing special control interfaces, examples are IDirectDrawVideo
|
||||
// and IQualProp on renderers. This allows property pages to be built that
|
||||
// use the given interface. Applications such as the ActiveMovie OCX query
|
||||
// filters for the property pages they support and expose them to the user
|
||||
//
|
||||
// This class provides all the framework for a property page. A property
|
||||
// page is a COM object that supports IPropertyPage. We should be created
|
||||
// with a resource ID for the dialog which we will load when required. We
|
||||
// should also be given in the constructor a resource ID for a title string
|
||||
// we will load from the DLLs STRINGTABLE. The property page titles must be
|
||||
// stored in resource files so that they can be easily internationalised
|
||||
//
|
||||
// We have a number of virtual methods (not PURE) that may be overriden in
|
||||
// derived classes to query for interfaces and so on. These functions have
|
||||
// simple implementations here that just return NOERROR. Derived classes
|
||||
// will almost definately have to override the message handler method called
|
||||
// OnReceiveMessage. We have a static dialog procedure that calls the method
|
||||
// so that derived classes don't have to fiddle around with the this pointer
|
||||
|
||||
class AM_NOVTABLE CBasePropertyPage : public IPropertyPage, public CUnknown
|
||||
{
|
||||
protected:
|
||||
|
||||
LPPROPERTYPAGESITE m_pPageSite; // Details for our property site
|
||||
HWND m_hwnd; // Window handle for the page
|
||||
HWND m_Dlg; // Actual dialog window handle
|
||||
BOOL m_bDirty; // Has anything been changed
|
||||
int m_TitleId; // Resource identifier for title
|
||||
int m_DialogId; // Dialog resource identifier
|
||||
|
||||
static INT_PTR CALLBACK DialogProc(HWND hwnd,
|
||||
UINT uMsg,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam);
|
||||
|
||||
private:
|
||||
BOOL m_bObjectSet ; // SetObject has been called or not.
|
||||
public:
|
||||
|
||||
CBasePropertyPage(__in_opt LPCTSTR pName, // Debug only name
|
||||
__inout_opt LPUNKNOWN pUnk, // COM Delegator
|
||||
int DialogId, // Resource ID
|
||||
int TitleId); // To get tital
|
||||
|
||||
#ifdef UNICODE
|
||||
CBasePropertyPage(__in_opt LPCSTR pName,
|
||||
__inout_opt LPUNKNOWN pUnk,
|
||||
int DialogId,
|
||||
int TitleId);
|
||||
#endif
|
||||
virtual ~CBasePropertyPage() { };
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// Override these virtual methods
|
||||
|
||||
virtual HRESULT OnConnect(IUnknown *pUnknown) { return NOERROR; };
|
||||
virtual HRESULT OnDisconnect() { return NOERROR; };
|
||||
virtual HRESULT OnActivate() { return NOERROR; };
|
||||
virtual HRESULT OnDeactivate() { return NOERROR; };
|
||||
virtual HRESULT OnApplyChanges() { return NOERROR; };
|
||||
virtual INT_PTR OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
|
||||
|
||||
// These implement an IPropertyPage interface
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
STDMETHODIMP_(ULONG) NonDelegatingRelease();
|
||||
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
|
||||
STDMETHODIMP SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite);
|
||||
STDMETHODIMP Activate(HWND hwndParent, LPCRECT prect,BOOL fModal);
|
||||
STDMETHODIMP Deactivate(void);
|
||||
STDMETHODIMP GetPageInfo(__out LPPROPPAGEINFO pPageInfo);
|
||||
STDMETHODIMP SetObjects(ULONG cObjects, __in_ecount_opt(cObjects) LPUNKNOWN *ppUnk);
|
||||
STDMETHODIMP Show(UINT nCmdShow);
|
||||
STDMETHODIMP Move(LPCRECT prect);
|
||||
STDMETHODIMP IsPageDirty(void) { return m_bDirty ? S_OK : S_FALSE; }
|
||||
STDMETHODIMP Apply(void);
|
||||
STDMETHODIMP Help(LPCWSTR lpszHelpDir) { return E_NOTIMPL; }
|
||||
STDMETHODIMP TranslateAccelerator(__inout LPMSG lpMsg) { return E_NOTIMPL; }
|
||||
};
|
||||
|
||||
#endif // __CPROP__
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,923 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: CtlUtil.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Base classes implementing IDispatch parsing for the basic control dual
|
||||
// interfaces. Derive from these and implement just the custom method and
|
||||
// property methods. We also implement CPosPassThru that can be used by
|
||||
// renderers and transforms to pass by IMediaPosition and IMediaSeeking
|
||||
|
||||
#ifndef __CTLUTIL__
|
||||
#define __CTLUTIL__
|
||||
|
||||
// OLE Automation has different ideas of TRUE and FALSE
|
||||
|
||||
#define OATRUE (-1)
|
||||
#define OAFALSE (0)
|
||||
|
||||
|
||||
// It's possible that we could replace this class with CreateStdDispatch
|
||||
|
||||
class CBaseDispatch
|
||||
{
|
||||
ITypeInfo * m_pti;
|
||||
|
||||
public:
|
||||
|
||||
CBaseDispatch() : m_pti(NULL) {}
|
||||
~CBaseDispatch();
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
REFIID riid,
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaControl :
|
||||
public IMediaControl,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CMediaControl(const TCHAR *, LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaEvent :
|
||||
public IMediaEventEx,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CMediaEvent(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaPosition :
|
||||
public IMediaPosition,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT *phr);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// OA-compatibility means that we must use double as the RefTime value,
|
||||
// and REFERENCE_TIME (essentially a LONGLONG) within filters.
|
||||
// this class converts between the two
|
||||
|
||||
class COARefTime : public CRefTime {
|
||||
public:
|
||||
|
||||
COARefTime() {
|
||||
};
|
||||
|
||||
COARefTime(CRefTime t)
|
||||
: CRefTime(t)
|
||||
{
|
||||
};
|
||||
|
||||
COARefTime(REFERENCE_TIME t)
|
||||
: CRefTime(t)
|
||||
{
|
||||
};
|
||||
|
||||
COARefTime(double d) {
|
||||
m_time = (LONGLONG) (d * 10000000);
|
||||
};
|
||||
|
||||
operator double() {
|
||||
return double(m_time) / 10000000;
|
||||
};
|
||||
|
||||
operator REFERENCE_TIME() {
|
||||
return m_time;
|
||||
};
|
||||
|
||||
COARefTime& operator=(const double& rd) {
|
||||
m_time = (LONGLONG) (rd * 10000000);
|
||||
return *this;
|
||||
}
|
||||
|
||||
COARefTime& operator=(const REFERENCE_TIME& rt) {
|
||||
m_time = rt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline BOOL operator==(const COARefTime& rt)
|
||||
{
|
||||
return m_time == rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator!=(const COARefTime& rt)
|
||||
{
|
||||
return m_time != rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator < (const COARefTime& rt)
|
||||
{
|
||||
return m_time < rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator > (const COARefTime& rt)
|
||||
{
|
||||
return m_time > rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator >= (const COARefTime& rt)
|
||||
{
|
||||
return m_time >= rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator <= (const COARefTime& rt)
|
||||
{
|
||||
return m_time <= rt.m_time;
|
||||
};
|
||||
|
||||
inline COARefTime operator+(const COARefTime& rt)
|
||||
{
|
||||
return COARefTime(m_time + rt.m_time);
|
||||
};
|
||||
|
||||
inline COARefTime operator-(const COARefTime& rt)
|
||||
{
|
||||
return COARefTime(m_time - rt.m_time);
|
||||
};
|
||||
|
||||
inline COARefTime operator*(LONG l)
|
||||
{
|
||||
return COARefTime(m_time * l);
|
||||
};
|
||||
|
||||
inline COARefTime operator/(LONG l)
|
||||
{
|
||||
return COARefTime(m_time / l);
|
||||
};
|
||||
|
||||
private:
|
||||
// Prevent bugs from constructing from LONG (which gets
|
||||
// converted to double and then multiplied by 10000000
|
||||
COARefTime(LONG);
|
||||
LONG operator=(LONG);
|
||||
};
|
||||
|
||||
|
||||
// A utility class that handles IMediaPosition and IMediaSeeking on behalf
|
||||
// of single-input pin renderers, or transform filters.
|
||||
//
|
||||
// Renderers will expose this from the filter; transform filters will
|
||||
// expose it from the output pin and not the renderer.
|
||||
//
|
||||
// Create one of these, giving it your IPin* for your input pin, and delegate
|
||||
// all IMediaPosition methods to it. It will query the input pin for
|
||||
// IMediaPosition and respond appropriately.
|
||||
//
|
||||
// Call ForceRefresh if the pin connection changes.
|
||||
//
|
||||
// This class no longer caches the upstream IMediaPosition or IMediaSeeking
|
||||
// it acquires it on each method call. This means ForceRefresh is not needed.
|
||||
// The method is kept for source compatibility and to minimise the changes
|
||||
// if we need to put it back later for performance reasons.
|
||||
|
||||
class CPosPassThru : public IMediaSeeking, public CMediaPosition
|
||||
{
|
||||
IPin *m_pPin;
|
||||
|
||||
HRESULT GetPeer(__deref_out IMediaPosition **ppMP);
|
||||
HRESULT GetPeerSeeking(__deref_out IMediaSeeking **ppMS);
|
||||
|
||||
public:
|
||||
|
||||
CPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
HRESULT ForceRefresh() {
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
// override to return an accurate current position
|
||||
virtual HRESULT GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);
|
||||
|
||||
// IMediaSeeking methods
|
||||
STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );
|
||||
STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );
|
||||
STDMETHODIMP SetTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP GetTimeFormat(__out GUID *pFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP IsFormatSupported( const GUID * pFormat);
|
||||
STDMETHODIMP QueryPreferredFormat( __out GUID *pFormat);
|
||||
STDMETHODIMP ConvertTimeFormat(__out LONGLONG * pTarget,
|
||||
__in_opt const GUID * pTargetFormat,
|
||||
LONGLONG Source,
|
||||
__in_opt const GUID * pSourceFormat );
|
||||
STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags
|
||||
, __inout_opt LONGLONG * pStop, DWORD StopFlags );
|
||||
|
||||
STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );
|
||||
STDMETHODIMP GetCurrentPosition( __out LONGLONG * pCurrent );
|
||||
STDMETHODIMP GetStopPosition( __out LONGLONG * pStop );
|
||||
STDMETHODIMP SetRate( double dRate);
|
||||
STDMETHODIMP GetRate( __out double * pdRate);
|
||||
STDMETHODIMP GetDuration( __out LONGLONG *pDuration);
|
||||
STDMETHODIMP GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest );
|
||||
STDMETHODIMP GetPreroll( __out LONGLONG *pllPreroll );
|
||||
|
||||
// IMediaPosition properties
|
||||
STDMETHODIMP get_Duration(__out REFTIME * plength);
|
||||
STDMETHODIMP put_CurrentPosition(REFTIME llTime);
|
||||
STDMETHODIMP get_StopTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_StopTime(REFTIME llTime);
|
||||
STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_PrerollTime(REFTIME llTime);
|
||||
STDMETHODIMP get_Rate(__out double * pdRate);
|
||||
STDMETHODIMP put_Rate(double dRate);
|
||||
STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime);
|
||||
STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);
|
||||
STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);
|
||||
|
||||
private:
|
||||
HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ),
|
||||
__out LONGLONG * pll );
|
||||
};
|
||||
|
||||
|
||||
// Adds the ability to return a current position
|
||||
|
||||
class CRendererPosPassThru : public CPosPassThru
|
||||
{
|
||||
CCritSec m_PositionLock; // Locks access to our position
|
||||
LONGLONG m_StartMedia; // Start media time last seen
|
||||
LONGLONG m_EndMedia; // And likewise the end media
|
||||
BOOL m_bReset; // Have media times been set
|
||||
|
||||
public:
|
||||
|
||||
// Used to help with passing media times through graph
|
||||
|
||||
CRendererPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);
|
||||
HRESULT RegisterMediaTime(IMediaSample *pMediaSample);
|
||||
HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime);
|
||||
HRESULT GetMediaTime(__out LONGLONG *pStartTime,__out_opt LONGLONG *pEndTime);
|
||||
HRESULT ResetMediaTime();
|
||||
HRESULT EOS();
|
||||
};
|
||||
|
||||
STDAPI CreatePosPassThru(
|
||||
__in_opt LPUNKNOWN pAgg,
|
||||
BOOL bRenderer,
|
||||
IPin *pPin,
|
||||
__deref_out IUnknown **ppPassThru
|
||||
);
|
||||
|
||||
// A class that handles the IDispatch part of IBasicAudio and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBasicAudio(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
// A class that handles the IDispatch part of IBasicVideo and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBaseBasicVideo(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
|
||||
STDMETHODIMP GetPreferredAspectRatio(
|
||||
__out long *plAspectX,
|
||||
__out long *plAspectY)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A class that handles the IDispatch part of IVideoWindow and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBaseVideoWindow(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
// abstract class to help source filters with their implementation
|
||||
// of IMediaPosition. Derive from this and set the duration (and stop
|
||||
// position). Also override NotifyChange to do something when the properties
|
||||
// change.
|
||||
|
||||
class AM_NOVTABLE CSourcePosition : public CMediaPosition
|
||||
{
|
||||
|
||||
public:
|
||||
CSourcePosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);
|
||||
|
||||
// IMediaPosition methods
|
||||
STDMETHODIMP get_Duration(__out REFTIME * plength);
|
||||
STDMETHODIMP put_CurrentPosition(REFTIME llTime);
|
||||
STDMETHODIMP get_StopTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_StopTime(REFTIME llTime);
|
||||
STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_PrerollTime(REFTIME llTime);
|
||||
STDMETHODIMP get_Rate(__out double * pdRate);
|
||||
STDMETHODIMP put_Rate(double dRate);
|
||||
STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);
|
||||
STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);
|
||||
|
||||
// override if you can return the data you are actually working on
|
||||
STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime) {
|
||||
return E_NOTIMPL;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// we call this to notify changes. Override to handle them
|
||||
virtual HRESULT ChangeStart() PURE;
|
||||
virtual HRESULT ChangeStop() PURE;
|
||||
virtual HRESULT ChangeRate() PURE;
|
||||
|
||||
COARefTime m_Duration;
|
||||
COARefTime m_Start;
|
||||
COARefTime m_Stop;
|
||||
double m_Rate;
|
||||
|
||||
CCritSec * m_pLock;
|
||||
};
|
||||
|
||||
class AM_NOVTABLE CSourceSeeking :
|
||||
public IMediaSeeking,
|
||||
public CUnknown
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_IUNKNOWN;
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
// IMediaSeeking methods
|
||||
|
||||
STDMETHODIMP IsFormatSupported(const GUID * pFormat);
|
||||
STDMETHODIMP QueryPreferredFormat(__out GUID *pFormat);
|
||||
STDMETHODIMP SetTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP GetTimeFormat(__out GUID *pFormat);
|
||||
STDMETHODIMP GetDuration(__out LONGLONG *pDuration);
|
||||
STDMETHODIMP GetStopPosition(__out LONGLONG *pStop);
|
||||
STDMETHODIMP GetCurrentPosition(__out LONGLONG *pCurrent);
|
||||
STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );
|
||||
STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );
|
||||
STDMETHODIMP ConvertTimeFormat( __out LONGLONG * pTarget,
|
||||
__in_opt const GUID * pTargetFormat,
|
||||
LONGLONG Source,
|
||||
__in_opt const GUID * pSourceFormat );
|
||||
|
||||
STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags
|
||||
, __inout_opt LONGLONG * pStop, DWORD StopFlags );
|
||||
|
||||
STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );
|
||||
|
||||
STDMETHODIMP GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest );
|
||||
STDMETHODIMP SetRate( double dRate);
|
||||
STDMETHODIMP GetRate( __out double * pdRate);
|
||||
STDMETHODIMP GetPreroll(__out LONGLONG *pPreroll);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// ctor
|
||||
CSourceSeeking(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);
|
||||
|
||||
// we call this to notify changes. Override to handle them
|
||||
virtual HRESULT ChangeStart() PURE;
|
||||
virtual HRESULT ChangeStop() PURE;
|
||||
virtual HRESULT ChangeRate() PURE;
|
||||
|
||||
CRefTime m_rtDuration; // length of stream
|
||||
CRefTime m_rtStart; // source will start here
|
||||
CRefTime m_rtStop; // source will stop here
|
||||
double m_dRateSeeking;
|
||||
|
||||
// seeking capabilities
|
||||
DWORD m_dwSeekingCaps;
|
||||
|
||||
CCritSec * m_pLock;
|
||||
};
|
||||
|
||||
|
||||
// Base classes supporting Deferred commands.
|
||||
|
||||
// Deferred commands are queued by calls to methods on the IQueueCommand
|
||||
// interface, exposed by the filtergraph and by some filters. A successful
|
||||
// call to one of these methods will return an IDeferredCommand interface
|
||||
// representing the queued command.
|
||||
//
|
||||
// A CDeferredCommand object represents a single deferred command, and exposes
|
||||
// the IDeferredCommand interface as well as other methods permitting time
|
||||
// checks and actual execution. It contains a reference to the CCommandQueue
|
||||
// object on which it is queued.
|
||||
//
|
||||
// CCommandQueue is a base class providing a queue of CDeferredCommand
|
||||
// objects, and methods to add, remove, check status and invoke the queued
|
||||
// commands. A CCommandQueue object would be part of an object that
|
||||
// implemented IQueueCommand.
|
||||
|
||||
class CCmdQueue;
|
||||
|
||||
// take a copy of the params and store them. Release any allocated
|
||||
// memory in destructor
|
||||
|
||||
class CDispParams : public DISPPARAMS
|
||||
{
|
||||
public:
|
||||
CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr = NULL);
|
||||
~CDispParams();
|
||||
};
|
||||
|
||||
|
||||
// CDeferredCommand lifetime is controlled by refcounts. Caller of
|
||||
// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue
|
||||
// object also holds a refcount on us. Calling Cancel or Invoke takes
|
||||
// us off the CCmdQueue and thus reduces the refcount by 1. Once taken
|
||||
// off the queue we cannot be put back on the queue.
|
||||
|
||||
class CDeferredCommand
|
||||
: public CUnknown,
|
||||
public IDeferredCommand
|
||||
{
|
||||
public:
|
||||
|
||||
CDeferredCommand(
|
||||
__inout CCmdQueue * pQ,
|
||||
__in_opt LPUNKNOWN pUnk, // aggregation outer unk
|
||||
__inout HRESULT * phr,
|
||||
__in LPUNKNOWN pUnkExecutor, // object that will execute this cmd
|
||||
REFTIME time,
|
||||
__in GUID* iid,
|
||||
long dispidMethod,
|
||||
short wFlags,
|
||||
long cArgs,
|
||||
__in_ecount(cArgs) VARIANT* pDispParams,
|
||||
__out VARIANT* pvarResult,
|
||||
__out short* puArgErr,
|
||||
BOOL bStream
|
||||
);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __out void **ppv);
|
||||
|
||||
// IDeferredCommand methods
|
||||
STDMETHODIMP Cancel();
|
||||
STDMETHODIMP Confidence(
|
||||
__out LONG* pConfidence);
|
||||
STDMETHODIMP Postpone(
|
||||
REFTIME newtime);
|
||||
STDMETHODIMP GetHResult(
|
||||
__out HRESULT* phrResult);
|
||||
|
||||
// other public methods
|
||||
|
||||
HRESULT Invoke();
|
||||
|
||||
// access methods
|
||||
|
||||
// returns TRUE if streamtime, FALSE if presentation time
|
||||
BOOL IsStreamTime() {
|
||||
return m_bStream;
|
||||
};
|
||||
|
||||
CRefTime GetTime() {
|
||||
return m_time;
|
||||
};
|
||||
|
||||
REFIID GetIID() {
|
||||
return *m_iid;
|
||||
};
|
||||
|
||||
long GetMethod() {
|
||||
return m_dispidMethod;
|
||||
};
|
||||
|
||||
short GetFlags() {
|
||||
return m_wFlags;
|
||||
};
|
||||
|
||||
DISPPARAMS* GetParams() {
|
||||
return &m_DispParams;
|
||||
};
|
||||
|
||||
VARIANT* GetResult() {
|
||||
return m_pvarResult;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
CCmdQueue* m_pQueue;
|
||||
|
||||
// pUnk for the interface that we will execute the command on
|
||||
LPUNKNOWN m_pUnk;
|
||||
|
||||
// stored command data
|
||||
REFERENCE_TIME m_time;
|
||||
GUID* m_iid;
|
||||
long m_dispidMethod;
|
||||
short m_wFlags;
|
||||
VARIANT* m_pvarResult;
|
||||
BOOL m_bStream;
|
||||
CDispParams m_DispParams;
|
||||
DISPID m_DispId; // For get and put
|
||||
|
||||
// we use this for ITypeInfo access
|
||||
CBaseDispatch m_Dispatch;
|
||||
|
||||
// save retval here
|
||||
HRESULT m_hrResult;
|
||||
};
|
||||
|
||||
|
||||
// a list of CDeferredCommand objects. this is a base class providing
|
||||
// the basics of access to the list. If you want to use CDeferredCommand
|
||||
// objects then your queue needs to be derived from this class.
|
||||
|
||||
class AM_NOVTABLE CCmdQueue
|
||||
{
|
||||
public:
|
||||
CCmdQueue(__inout_opt HRESULT *phr = NULL);
|
||||
virtual ~CCmdQueue();
|
||||
|
||||
// returns a new CDeferredCommand object that will be initialised with
|
||||
// the parameters and will be added to the queue during construction.
|
||||
// returns S_OK if successfully created otherwise an error and
|
||||
// no object has been queued.
|
||||
virtual HRESULT New(
|
||||
__out CDeferredCommand **ppCmd,
|
||||
__in LPUNKNOWN pUnk,
|
||||
REFTIME time,
|
||||
__in GUID* iid,
|
||||
long dispidMethod,
|
||||
short wFlags,
|
||||
long cArgs,
|
||||
__in_ecount(cArgs) VARIANT* pDispParams,
|
||||
__out VARIANT* pvarResult,
|
||||
__out short* puArgErr,
|
||||
BOOL bStream
|
||||
);
|
||||
|
||||
// called by the CDeferredCommand object to add and remove itself
|
||||
// from the queue
|
||||
virtual HRESULT Insert(__in CDeferredCommand* pCmd);
|
||||
virtual HRESULT Remove(__in CDeferredCommand* pCmd);
|
||||
|
||||
// Command-Due Checking
|
||||
//
|
||||
// There are two schemes of synchronisation: coarse and accurate. In
|
||||
// coarse mode, you wait till the time arrives and then execute the cmd.
|
||||
// In accurate mode, you wait until you are processing the sample that
|
||||
// will appear at the time, and then execute the command. It's up to the
|
||||
// filter which one it will implement. The filtergraph will always
|
||||
// implement coarse mode for commands queued at the filtergraph.
|
||||
//
|
||||
// If you want coarse sync, you probably want to wait until there is a
|
||||
// command due, and then execute it. You can do this by calling
|
||||
// GetDueCommand. If you have several things to wait for, get the
|
||||
// event handle from GetDueHandle() and when this is signalled then call
|
||||
// GetDueCommand. Stream time will only advance between calls to Run and
|
||||
// EndRun. Note that to avoid an extra thread there is no guarantee that
|
||||
// if the handle is set there will be a command ready. Each time the
|
||||
// event is signalled, call GetDueCommand (probably with a 0 timeout);
|
||||
// This may return E_ABORT.
|
||||
//
|
||||
// If you want accurate sync, you must call GetCommandDueFor, passing
|
||||
// as a parameter the stream time of the samples you are about to process.
|
||||
// This will return:
|
||||
// -- a stream-time command due at or before that stream time
|
||||
// -- a presentation-time command due at or before the
|
||||
// time that stream time will be presented (only between Run
|
||||
// and EndRun calls, since outside of this, the mapping from
|
||||
// stream time to presentation time is not known.
|
||||
// -- any presentation-time command due now.
|
||||
// This means that if you want accurate synchronisation on samples that
|
||||
// might be processed during Paused mode, you need to use
|
||||
// stream-time commands.
|
||||
//
|
||||
// In all cases, commands remain queued until Invoked or Cancelled. The
|
||||
// setting and resetting of the event handle is managed entirely by this
|
||||
// queue object.
|
||||
|
||||
// set the clock used for timing
|
||||
virtual HRESULT SetSyncSource(__in_opt IReferenceClock*);
|
||||
|
||||
// switch to run mode. Streamtime to Presentation time mapping known.
|
||||
virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset);
|
||||
|
||||
// switch to Stopped or Paused mode. Time mapping not known.
|
||||
virtual HRESULT EndRun();
|
||||
|
||||
// return a pointer to the next due command. Blocks for msTimeout
|
||||
// milliseconds until there is a due command.
|
||||
// Stream-time commands will only become due between Run and Endrun calls.
|
||||
// The command remains queued until invoked or cancelled.
|
||||
// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
|
||||
// Returns an AddRef-ed object
|
||||
virtual HRESULT GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout);
|
||||
|
||||
// return the event handle that will be signalled whenever
|
||||
// there are deferred commands due for execution (when GetDueCommand
|
||||
// will not block).
|
||||
HANDLE GetDueHandle() {
|
||||
return HANDLE(m_evDue);
|
||||
};
|
||||
|
||||
// return a pointer to a command that will be due for a given time.
|
||||
// Pass in a stream time here. The stream time offset will be passed
|
||||
// in via the Run method.
|
||||
// Commands remain queued until invoked or cancelled.
|
||||
// This method will not block. It will report VFW_E_NOT_FOUND if there
|
||||
// are no commands due yet.
|
||||
// Returns an AddRef-ed object
|
||||
virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, __out CDeferredCommand**ppCmd);
|
||||
|
||||
// check if a given time is due (TRUE if it is due yet)
|
||||
BOOL CheckTime(CRefTime time, BOOL bStream) {
|
||||
|
||||
// if no clock, nothing is due!
|
||||
if (!m_pClock) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// stream time
|
||||
if (bStream) {
|
||||
|
||||
// not valid if not running
|
||||
if (!m_bRunning) {
|
||||
return FALSE;
|
||||
}
|
||||
// add on known stream time offset to get presentation time
|
||||
time += m_StreamTimeOffset;
|
||||
}
|
||||
|
||||
CRefTime Now;
|
||||
m_pClock->GetTime((REFERENCE_TIME*)&Now);
|
||||
return (time <= Now);
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// protect access to lists etc
|
||||
CCritSec m_Lock;
|
||||
|
||||
// commands queued in presentation time are stored here
|
||||
CGenericList<CDeferredCommand> m_listPresentation;
|
||||
|
||||
// commands queued in stream time are stored here
|
||||
CGenericList<CDeferredCommand> m_listStream;
|
||||
|
||||
// set when any commands are due
|
||||
CAMEvent m_evDue;
|
||||
|
||||
// creates an advise for the earliest time required, if any
|
||||
void SetTimeAdvise(void);
|
||||
|
||||
// advise id from reference clock (0 if no outstanding advise)
|
||||
DWORD_PTR m_dwAdvise;
|
||||
|
||||
// advise time is for this presentation time
|
||||
CRefTime m_tCurrentAdvise;
|
||||
|
||||
// the reference clock we are using (addrefed)
|
||||
IReferenceClock* m_pClock;
|
||||
|
||||
// true when running
|
||||
BOOL m_bRunning;
|
||||
|
||||
// contains stream time offset when m_bRunning is true
|
||||
CRefTime m_StreamTimeOffset;
|
||||
};
|
||||
|
||||
#endif // __CTLUTIL__
|
|
@ -0,0 +1,129 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: DDMM.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements routines for using DirectDraw
|
||||
// on a multimonitor system.
|
||||
//
|
||||
// Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <ddraw.h>
|
||||
#include "ddmm.h"
|
||||
|
||||
/*
|
||||
* FindDeviceCallback
|
||||
*/
|
||||
typedef struct {
|
||||
LPSTR szDevice;
|
||||
GUID* lpGUID;
|
||||
GUID GUID;
|
||||
BOOL fFound;
|
||||
} FindDeviceData;
|
||||
|
||||
BOOL CALLBACK FindDeviceCallback(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam)
|
||||
{
|
||||
FindDeviceData *p = (FindDeviceData*)lParam;
|
||||
|
||||
if (lstrcmpiA(p->szDevice, szDevice) == 0) {
|
||||
if (lpGUID) {
|
||||
p->GUID = *lpGUID;
|
||||
p->lpGUID = &p->GUID;
|
||||
} else {
|
||||
p->lpGUID = NULL;
|
||||
}
|
||||
p->fFound = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
BOOL CALLBACK FindDeviceCallbackEx(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam, HMONITOR hMonitor)
|
||||
{
|
||||
FindDeviceData *p = (FindDeviceData*)lParam;
|
||||
|
||||
if (lstrcmpiA(p->szDevice, szDevice) == 0) {
|
||||
if (lpGUID) {
|
||||
p->GUID = *lpGUID;
|
||||
p->lpGUID = &p->GUID;
|
||||
} else {
|
||||
p->lpGUID = NULL;
|
||||
}
|
||||
p->fFound = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DirectDrawCreateFromDevice
|
||||
*
|
||||
* create a DirectDraw object for a particular device
|
||||
*/
|
||||
IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, PDRAWENUM DirectDrawEnumerateP)
|
||||
{
|
||||
IDirectDraw* pdd = NULL;
|
||||
FindDeviceData find;
|
||||
|
||||
if (szDevice == NULL) {
|
||||
DirectDrawCreateP(NULL, &pdd, NULL);
|
||||
return pdd;
|
||||
}
|
||||
|
||||
find.szDevice = szDevice;
|
||||
find.fFound = FALSE;
|
||||
DirectDrawEnumerateP(FindDeviceCallback, (LPVOID)&find);
|
||||
|
||||
if (find.fFound)
|
||||
{
|
||||
//
|
||||
// In 4bpp mode the following DDraw call causes a message box to be popped
|
||||
// up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we
|
||||
// make sure it doesn't happen.
|
||||
//
|
||||
UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
DirectDrawCreateP(find.lpGUID, &pdd, NULL);
|
||||
SetErrorMode(ErrorMode);
|
||||
}
|
||||
|
||||
return pdd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DirectDrawCreateFromDeviceEx
|
||||
*
|
||||
* create a DirectDraw object for a particular device
|
||||
*/
|
||||
IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, LPDIRECTDRAWENUMERATEEXA DirectDrawEnumerateExP)
|
||||
{
|
||||
IDirectDraw* pdd = NULL;
|
||||
FindDeviceData find;
|
||||
|
||||
if (szDevice == NULL) {
|
||||
DirectDrawCreateP(NULL, &pdd, NULL);
|
||||
return pdd;
|
||||
}
|
||||
|
||||
find.szDevice = szDevice;
|
||||
find.fFound = FALSE;
|
||||
DirectDrawEnumerateExP(FindDeviceCallbackEx, (LPVOID)&find,
|
||||
DDENUM_ATTACHEDSECONDARYDEVICES);
|
||||
|
||||
if (find.fFound)
|
||||
{
|
||||
//
|
||||
// In 4bpp mode the following DDraw call causes a message box to be popped
|
||||
// up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we
|
||||
// make sure it doesn't happen.
|
||||
//
|
||||
UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
DirectDrawCreateP(find.lpGUID, &pdd, NULL);
|
||||
SetErrorMode(ErrorMode);
|
||||
}
|
||||
|
||||
return pdd;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: DDMM.h
|
||||
//
|
||||
// Desc: DirectShow base classes - efines routines for using DirectDraw
|
||||
// on a multimonitor system.
|
||||
//
|
||||
// Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Assume C declarations for C++ */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
// DDRAW.H might not include these
|
||||
#ifndef DDENUM_ATTACHEDSECONDARYDEVICES
|
||||
#define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001L
|
||||
#endif
|
||||
|
||||
typedef HRESULT (*PDRAWCREATE)(IID *,LPDIRECTDRAW *,LPUNKNOWN);
|
||||
typedef HRESULT (*PDRAWENUM)(LPDDENUMCALLBACKA, LPVOID);
|
||||
|
||||
IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR, PDRAWCREATE, PDRAWENUM);
|
||||
IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
|
@ -0,0 +1,367 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: DlleEntry.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements classes used to support dll
|
||||
// entry points for COM objects.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <initguid.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef UNICODE
|
||||
#ifndef _UNICODE
|
||||
#define _UNICODE
|
||||
#endif // _UNICODE
|
||||
#endif // UNICODE
|
||||
|
||||
#include <tchar.h>
|
||||
#endif // DEBUG
|
||||
#include <strsafe.h>
|
||||
|
||||
extern CFactoryTemplate g_Templates[];
|
||||
extern int g_cTemplates;
|
||||
|
||||
HINSTANCE g_hInst;
|
||||
DWORD g_amPlatform; // VER_PLATFORM_WIN32_WINDOWS etc... (from GetVersionEx)
|
||||
OSVERSIONINFO g_osInfo;
|
||||
|
||||
//
|
||||
// an instance of this is created by the DLLGetClassObject entrypoint
|
||||
// it uses the CFactoryTemplate object it is given to support the
|
||||
// IClassFactory interface
|
||||
|
||||
class CClassFactory : public IClassFactory, public CBaseObject
|
||||
{
|
||||
|
||||
private:
|
||||
const CFactoryTemplate *const m_pTemplate;
|
||||
|
||||
ULONG m_cRef;
|
||||
|
||||
static int m_cLocked;
|
||||
public:
|
||||
CClassFactory(const CFactoryTemplate *);
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, __deref_out void ** ppv);
|
||||
STDMETHODIMP_(ULONG)AddRef();
|
||||
STDMETHODIMP_(ULONG)Release();
|
||||
|
||||
// IClassFactory
|
||||
STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, __deref_out void **pv);
|
||||
STDMETHODIMP LockServer(BOOL fLock);
|
||||
|
||||
// allow DLLGetClassObject to know about global server lock status
|
||||
static BOOL IsLocked() {
|
||||
return (m_cLocked > 0);
|
||||
};
|
||||
};
|
||||
|
||||
// process-wide dll locked state
|
||||
int CClassFactory::m_cLocked = 0;
|
||||
|
||||
CClassFactory::CClassFactory(const CFactoryTemplate *pTemplate)
|
||||
: CBaseObject(NAME("Class Factory"))
|
||||
, m_cRef(0)
|
||||
, m_pTemplate(pTemplate)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP
|
||||
CClassFactory::QueryInterface(REFIID riid,__deref_out void **ppv)
|
||||
{
|
||||
CheckPointer(ppv,E_POINTER)
|
||||
ValidateReadWritePtr(ppv,sizeof(PVOID));
|
||||
*ppv = NULL;
|
||||
|
||||
// any interface on this object is the object pointer.
|
||||
if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) {
|
||||
*ppv = (LPVOID) this;
|
||||
// AddRef returned interface pointer
|
||||
((LPUNKNOWN) *ppv)->AddRef();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
return ResultFromScode(E_NOINTERFACE);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP_(ULONG)
|
||||
CClassFactory::AddRef()
|
||||
{
|
||||
return ++m_cRef;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG)
|
||||
CClassFactory::Release()
|
||||
{
|
||||
LONG lRef = InterlockedDecrement((volatile LONG *)&m_cRef);
|
||||
if (lRef == 0) {
|
||||
delete this;
|
||||
return 0;
|
||||
} else {
|
||||
return lRef;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP
|
||||
CClassFactory::CreateInstance(
|
||||
LPUNKNOWN pUnkOuter,
|
||||
REFIID riid,
|
||||
__deref_out void **pv)
|
||||
{
|
||||
CheckPointer(pv,E_POINTER)
|
||||
ValidateReadWritePtr(pv,sizeof(void *));
|
||||
*pv = NULL;
|
||||
|
||||
/* Enforce the normal OLE rules regarding interfaces and delegation */
|
||||
|
||||
if (pUnkOuter != NULL) {
|
||||
if (IsEqualIID(riid,IID_IUnknown) == FALSE) {
|
||||
*pv = NULL;
|
||||
return ResultFromScode(E_NOINTERFACE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the new object through the derived class's create function */
|
||||
|
||||
HRESULT hr = NOERROR;
|
||||
CUnknown *pObj = m_pTemplate->CreateInstance(pUnkOuter, &hr);
|
||||
|
||||
if (pObj == NULL) {
|
||||
*pv = NULL;
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Delete the object if we got a construction error */
|
||||
|
||||
if (FAILED(hr)) {
|
||||
delete pObj;
|
||||
*pv = NULL;
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Get a reference counted interface on the object */
|
||||
|
||||
/* We wrap the non-delegating QI with NDAddRef & NDRelease. */
|
||||
/* This protects any outer object from being prematurely */
|
||||
/* released by an inner object that may have to be created */
|
||||
/* in order to supply the requested interface. */
|
||||
pObj->NonDelegatingAddRef();
|
||||
hr = pObj->NonDelegatingQueryInterface(riid, pv);
|
||||
pObj->NonDelegatingRelease();
|
||||
/* Note that if NonDelegatingQueryInterface fails, it will */
|
||||
/* not increment the ref count, so the NonDelegatingRelease */
|
||||
/* will drop the ref back to zero and the object will "self-*/
|
||||
/* destruct". Hence we don't need additional tidy-up code */
|
||||
/* to cope with NonDelegatingQueryInterface failing. */
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ASSERT(*pv);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
STDMETHODIMP
|
||||
CClassFactory::LockServer(BOOL fLock)
|
||||
{
|
||||
if (fLock) {
|
||||
m_cLocked++;
|
||||
} else {
|
||||
m_cLocked--;
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// --- COM entrypoints -----------------------------------------
|
||||
|
||||
//called by COM to get the class factory object for a given class
|
||||
__control_entrypoint(DllExport) STDAPI
|
||||
DllGetClassObject(
|
||||
__in REFCLSID rClsID,
|
||||
__in REFIID riid,
|
||||
__deref_out void **pv)
|
||||
{
|
||||
*pv = NULL;
|
||||
if (!(riid == IID_IUnknown) && !(riid == IID_IClassFactory)) {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
// traverse the array of templates looking for one with this
|
||||
// class id
|
||||
for (int i = 0; i < g_cTemplates; i++) {
|
||||
const CFactoryTemplate * pT = &g_Templates[i];
|
||||
if (pT->IsClassID(rClsID)) {
|
||||
|
||||
// found a template - make a class factory based on this
|
||||
// template
|
||||
|
||||
*pv = (LPVOID) (LPUNKNOWN) new CClassFactory(pT);
|
||||
if (*pv == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
((LPUNKNOWN)*pv)->AddRef();
|
||||
return NOERROR;
|
||||
}
|
||||
}
|
||||
return CLASS_E_CLASSNOTAVAILABLE;
|
||||
}
|
||||
|
||||
//
|
||||
// Call any initialization routines
|
||||
//
|
||||
void
|
||||
DllInitClasses(BOOL bLoading)
|
||||
{
|
||||
int i;
|
||||
|
||||
// traverse the array of templates calling the init routine
|
||||
// if they have one
|
||||
for (i = 0; i < g_cTemplates; i++) {
|
||||
const CFactoryTemplate * pT = &g_Templates[i];
|
||||
if (pT->m_lpfnInit != NULL) {
|
||||
(*pT->m_lpfnInit)(bLoading, pT->m_ClsID);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// called by COM to determine if this dll can be unloaded
|
||||
// return ok unless there are outstanding objects or a lock requested
|
||||
// by IClassFactory::LockServer
|
||||
//
|
||||
// CClassFactory has a static function that can tell us about the locks,
|
||||
// and CCOMObject has a static function that can tell us about the active
|
||||
// object count
|
||||
STDAPI
|
||||
DllCanUnloadNow()
|
||||
{
|
||||
DbgLog((LOG_MEMORY,2,TEXT("DLLCanUnloadNow called - IsLocked = %d, Active objects = %d"),
|
||||
CClassFactory::IsLocked(),
|
||||
CBaseObject::ObjectsActive()));
|
||||
|
||||
if (CClassFactory::IsLocked() || CBaseObject::ObjectsActive()) {
|
||||
return S_FALSE;
|
||||
} else {
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- standard WIN32 entrypoints --------------------------------------
|
||||
|
||||
|
||||
extern "C" void __cdecl __security_init_cookie(void);
|
||||
extern "C" BOOL WINAPI _DllEntryPoint(HINSTANCE, ULONG, __inout_opt LPVOID);
|
||||
#pragma comment(linker, "/merge:.CRT=.rdata")
|
||||
|
||||
extern "C"
|
||||
DECLSPEC_NOINLINE
|
||||
BOOL
|
||||
WINAPI
|
||||
DllEntryPoint(
|
||||
HINSTANCE hInstance,
|
||||
ULONG ulReason,
|
||||
__inout_opt LPVOID pv
|
||||
)
|
||||
{
|
||||
if ( ulReason == DLL_PROCESS_ATTACH ) {
|
||||
// Must happen before any other code is executed. Thankfully - it's re-entrant
|
||||
__security_init_cookie();
|
||||
}
|
||||
return _DllEntryPoint(hInstance, ulReason, pv);
|
||||
}
|
||||
|
||||
|
||||
DECLSPEC_NOINLINE
|
||||
BOOL
|
||||
WINAPI
|
||||
_DllEntryPoint(
|
||||
HINSTANCE hInstance,
|
||||
ULONG ulReason,
|
||||
__inout_opt LPVOID pv
|
||||
)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
extern bool g_fDbgInDllEntryPoint;
|
||||
g_fDbgInDllEntryPoint = true;
|
||||
#endif
|
||||
|
||||
switch (ulReason)
|
||||
{
|
||||
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(hInstance);
|
||||
DbgInitialise(hInstance);
|
||||
|
||||
{
|
||||
// The platform identifier is used to work out whether
|
||||
// full unicode support is available or not. Hence the
|
||||
// default will be the lowest common denominator - i.e. N/A
|
||||
g_amPlatform = VER_PLATFORM_WIN32_WINDOWS; // win95 assumed in case GetVersionEx fails
|
||||
|
||||
g_osInfo.dwOSVersionInfoSize = sizeof(g_osInfo);
|
||||
if (GetVersionEx(&g_osInfo)) {
|
||||
g_amPlatform = g_osInfo.dwPlatformId;
|
||||
} else {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Failed to get the OS platform, assuming Win95")));
|
||||
}
|
||||
}
|
||||
|
||||
g_hInst = hInstance;
|
||||
DllInitClasses(TRUE);
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
DllInitClasses(FALSE);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (CBaseObject::ObjectsActive()) {
|
||||
DbgSetModuleLevel(LOG_MEMORY, 2);
|
||||
TCHAR szInfo[512];
|
||||
extern TCHAR m_ModuleName[]; // Cut down module name
|
||||
|
||||
TCHAR FullName[_MAX_PATH]; // Load the full path and module name
|
||||
TCHAR *pName; // Searches from the end for a backslash
|
||||
|
||||
GetModuleFileName(NULL,FullName,_MAX_PATH);
|
||||
pName = _tcsrchr(FullName,'\\');
|
||||
if (pName == NULL) {
|
||||
pName = FullName;
|
||||
} else {
|
||||
pName++;
|
||||
}
|
||||
|
||||
(void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("Executable: %s Pid %x Tid %x. "),
|
||||
pName, GetCurrentProcessId(), GetCurrentThreadId());
|
||||
|
||||
(void)StringCchPrintf(szInfo+lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), TEXT("Module %s, %d objects left active!"),
|
||||
m_ModuleName, CBaseObject::ObjectsActive());
|
||||
DbgAssert(szInfo, TEXT(__FILE__),__LINE__);
|
||||
|
||||
// If running remotely wait for the Assert to be acknowledged
|
||||
// before dumping out the object register
|
||||
DbgDumpObjectRegister();
|
||||
}
|
||||
DbgTerminate();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
g_fDbgInDllEntryPoint = false;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,693 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: DllSetup.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// defines
|
||||
|
||||
#define MAX_KEY_LEN 260
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// externally defined functions/variable
|
||||
|
||||
extern int g_cTemplates;
|
||||
extern CFactoryTemplate g_Templates[];
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// EliminateSubKey
|
||||
//
|
||||
// Try to enumerate all keys under this one.
|
||||
// if we find anything, delete it completely.
|
||||
// Otherwise just delete it.
|
||||
//
|
||||
// note - this was pinched/duplicated from
|
||||
// Filgraph\Mapper.cpp - so should it be in
|
||||
// a lib somewhere?
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
STDAPI
|
||||
EliminateSubKey( HKEY hkey, LPCTSTR strSubKey )
|
||||
{
|
||||
HKEY hk;
|
||||
if (0 == lstrlen(strSubKey) ) {
|
||||
// defensive approach
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
LONG lreturn = RegOpenKeyEx( hkey
|
||||
, strSubKey
|
||||
, 0
|
||||
, MAXIMUM_ALLOWED
|
||||
, &hk );
|
||||
|
||||
ASSERT( lreturn == ERROR_SUCCESS
|
||||
|| lreturn == ERROR_FILE_NOT_FOUND
|
||||
|| lreturn == ERROR_INVALID_HANDLE );
|
||||
|
||||
if( ERROR_SUCCESS == lreturn )
|
||||
{
|
||||
// Keep on enumerating the first (zero-th)
|
||||
// key and deleting that
|
||||
|
||||
for( ; ; )
|
||||
{
|
||||
TCHAR Buffer[MAX_KEY_LEN];
|
||||
DWORD dw = MAX_KEY_LEN;
|
||||
FILETIME ft;
|
||||
|
||||
lreturn = RegEnumKeyEx( hk
|
||||
, 0
|
||||
, Buffer
|
||||
, &dw
|
||||
, NULL
|
||||
, NULL
|
||||
, NULL
|
||||
, &ft);
|
||||
|
||||
ASSERT( lreturn == ERROR_SUCCESS
|
||||
|| lreturn == ERROR_NO_MORE_ITEMS );
|
||||
|
||||
if( ERROR_SUCCESS == lreturn )
|
||||
{
|
||||
EliminateSubKey(hk, Buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(hk);
|
||||
RegDeleteKey(hkey, strSubKey);
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// AMovieSetupRegisterServer()
|
||||
//
|
||||
// registers specfied file "szFileName" as server for
|
||||
// CLSID "clsServer". A description is also required.
|
||||
// The ThreadingModel and ServerType are optional, as
|
||||
// they default to InprocServer32 (i.e. dll) and Both.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
STDAPI
|
||||
AMovieSetupRegisterServer( CLSID clsServer
|
||||
, LPCWSTR szDescription
|
||||
, LPCWSTR szFileName
|
||||
, LPCWSTR szThreadingModel = L"Both"
|
||||
, LPCWSTR szServerType = L"InprocServer32" )
|
||||
{
|
||||
// temp buffer
|
||||
//
|
||||
TCHAR achTemp[MAX_PATH];
|
||||
|
||||
// convert CLSID uuid to string and write
|
||||
// out subkey as string - CLSID\{}
|
||||
//
|
||||
OLECHAR szCLSID[CHARS_IN_GUID];
|
||||
HRESULT hr = StringFromGUID2( clsServer
|
||||
, szCLSID
|
||||
, CHARS_IN_GUID );
|
||||
ASSERT( SUCCEEDED(hr) );
|
||||
|
||||
// create key
|
||||
//
|
||||
HKEY hkey;
|
||||
(void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("CLSID\\%ls"), szCLSID );
|
||||
LONG lreturn = RegCreateKey( HKEY_CLASSES_ROOT
|
||||
, (LPCTSTR)achTemp
|
||||
, &hkey );
|
||||
if( ERROR_SUCCESS != lreturn )
|
||||
{
|
||||
return AmHresultFromWin32(lreturn);
|
||||
}
|
||||
|
||||
// set description string
|
||||
//
|
||||
|
||||
(void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szDescription );
|
||||
lreturn = RegSetValue( hkey
|
||||
, (LPCTSTR)NULL
|
||||
, REG_SZ
|
||||
, achTemp
|
||||
, sizeof(achTemp) );
|
||||
if( ERROR_SUCCESS != lreturn )
|
||||
{
|
||||
RegCloseKey( hkey );
|
||||
return AmHresultFromWin32(lreturn);
|
||||
}
|
||||
|
||||
// create CLSID\\{"CLSID"}\\"ServerType" key,
|
||||
// using key to CLSID\\{"CLSID"} passed back by
|
||||
// last call to RegCreateKey().
|
||||
//
|
||||
HKEY hsubkey;
|
||||
|
||||
(void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szServerType );
|
||||
lreturn = RegCreateKey( hkey
|
||||
, achTemp
|
||||
, &hsubkey );
|
||||
if( ERROR_SUCCESS != lreturn )
|
||||
{
|
||||
RegCloseKey( hkey );
|
||||
return AmHresultFromWin32(lreturn);
|
||||
}
|
||||
|
||||
// set Server string
|
||||
//
|
||||
(void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szFileName );
|
||||
lreturn = RegSetValue( hsubkey
|
||||
, (LPCTSTR)NULL
|
||||
, REG_SZ
|
||||
, (LPCTSTR)achTemp
|
||||
, sizeof(TCHAR) * (lstrlen(achTemp)+1) );
|
||||
if( ERROR_SUCCESS != lreturn )
|
||||
{
|
||||
RegCloseKey( hkey );
|
||||
RegCloseKey( hsubkey );
|
||||
return AmHresultFromWin32(lreturn);
|
||||
}
|
||||
|
||||
(void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szThreadingModel );
|
||||
lreturn = RegSetValueEx( hsubkey
|
||||
, TEXT("ThreadingModel")
|
||||
, 0L
|
||||
, REG_SZ
|
||||
, (CONST BYTE *)achTemp
|
||||
, sizeof(TCHAR) * (lstrlen(achTemp)+1) );
|
||||
|
||||
// close hkeys
|
||||
//
|
||||
RegCloseKey( hkey );
|
||||
RegCloseKey( hsubkey );
|
||||
|
||||
// and return
|
||||
//
|
||||
return HRESULT_FROM_WIN32(lreturn);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// AMovieSetupUnregisterServer()
|
||||
//
|
||||
// default ActiveMovie dll setup function
|
||||
// - to use must be called from an exported
|
||||
// function named DllRegisterServer()
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
STDAPI
|
||||
AMovieSetupUnregisterServer( CLSID clsServer )
|
||||
{
|
||||
// convert CLSID uuid to string and write
|
||||
// out subkey CLSID\{}
|
||||
//
|
||||
OLECHAR szCLSID[CHARS_IN_GUID];
|
||||
HRESULT hr = StringFromGUID2( clsServer
|
||||
, szCLSID
|
||||
, CHARS_IN_GUID );
|
||||
ASSERT( SUCCEEDED(hr) );
|
||||
|
||||
TCHAR achBuffer[MAX_KEY_LEN];
|
||||
(void)StringCchPrintf( achBuffer, NUMELMS(achBuffer), TEXT("CLSID\\%ls"), szCLSID );
|
||||
|
||||
// delete subkey
|
||||
//
|
||||
|
||||
hr = EliminateSubKey( HKEY_CLASSES_ROOT, achBuffer );
|
||||
ASSERT( SUCCEEDED(hr) );
|
||||
|
||||
// return
|
||||
//
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// AMovieSetupRegisterFilter through IFilterMapper2
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
STDAPI
|
||||
AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata
|
||||
, IFilterMapper2 * pIFM2
|
||||
, BOOL bRegister )
|
||||
{
|
||||
DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));
|
||||
|
||||
// check we've got data
|
||||
//
|
||||
if( NULL == psetupdata ) return S_FALSE;
|
||||
|
||||
|
||||
// unregister filter
|
||||
// (as pins are subkeys of filter's CLSID key
|
||||
// they do not need to be removed separately).
|
||||
//
|
||||
DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));
|
||||
HRESULT hr = pIFM2->UnregisterFilter(
|
||||
0, // default category
|
||||
0, // default instance name
|
||||
*psetupdata->clsID );
|
||||
|
||||
|
||||
if( bRegister )
|
||||
{
|
||||
REGFILTER2 rf2;
|
||||
rf2.dwVersion = 1;
|
||||
rf2.dwMerit = psetupdata->dwMerit;
|
||||
rf2.cPins = psetupdata->nPins;
|
||||
rf2.rgPins = psetupdata->lpPin;
|
||||
|
||||
// register filter
|
||||
//
|
||||
DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));
|
||||
hr = pIFM2->RegisterFilter(*psetupdata->clsID
|
||||
, psetupdata->strName
|
||||
, 0 // moniker
|
||||
, 0 // category
|
||||
, NULL // instance
|
||||
, &rf2);
|
||||
}
|
||||
|
||||
// handle one acceptable "error" - that
|
||||
// of filter not being registered!
|
||||
// (couldn't find a suitable #define'd
|
||||
// name for the error!)
|
||||
//
|
||||
if( 0x80070002 == hr)
|
||||
return NOERROR;
|
||||
else
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// RegisterAllServers()
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
STDAPI
|
||||
RegisterAllServers( LPCWSTR szFileName, BOOL bRegister )
|
||||
{
|
||||
HRESULT hr = NOERROR;
|
||||
|
||||
for( int i = 0; i < g_cTemplates; i++ )
|
||||
{
|
||||
// get i'th template
|
||||
//
|
||||
const CFactoryTemplate *pT = &g_Templates[i];
|
||||
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"),
|
||||
(LPCWSTR)pT->m_Name ));
|
||||
|
||||
// register CLSID and InprocServer32
|
||||
//
|
||||
if( bRegister )
|
||||
{
|
||||
hr = AMovieSetupRegisterServer( *(pT->m_ClsID)
|
||||
, (LPCWSTR)pT->m_Name
|
||||
, szFileName );
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = AMovieSetupUnregisterServer( *(pT->m_ClsID) );
|
||||
}
|
||||
|
||||
// check final error for this pass
|
||||
// and break loop if we failed
|
||||
//
|
||||
if( FAILED(hr) )
|
||||
break;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// AMovieDllRegisterServer2()
|
||||
//
|
||||
// default ActiveMovie dll setup function
|
||||
// - to use must be called from an exported
|
||||
// function named DllRegisterServer()
|
||||
//
|
||||
// this function is table driven using the
|
||||
// static members of the CFactoryTemplate
|
||||
// class defined in the dll.
|
||||
//
|
||||
// it registers the Dll as the InprocServer32
|
||||
// and then calls the IAMovieSetup.Register
|
||||
// method.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
STDAPI
|
||||
AMovieDllRegisterServer2( BOOL bRegister )
|
||||
{
|
||||
HRESULT hr = NOERROR;
|
||||
|
||||
DbgLog((LOG_TRACE, 2, TEXT("AMovieDllRegisterServer2()")));
|
||||
|
||||
// get file name (where g_hInst is the
|
||||
// instance handle of the filter dll)
|
||||
//
|
||||
WCHAR achFileName[MAX_PATH];
|
||||
|
||||
// WIN95 doesn't support GetModuleFileNameW
|
||||
//
|
||||
{
|
||||
char achTemp[MAX_PATH];
|
||||
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- get module file name")));
|
||||
|
||||
// g_hInst handle is set in our dll entry point. Make sure
|
||||
// DllEntryPoint in dllentry.cpp is called
|
||||
ASSERT(g_hInst != 0);
|
||||
|
||||
if( 0 == GetModuleFileNameA( g_hInst
|
||||
, achTemp
|
||||
, sizeof(achTemp) ) )
|
||||
{
|
||||
// we've failed!
|
||||
DWORD dwerr = GetLastError();
|
||||
return AmHresultFromWin32(dwerr);
|
||||
}
|
||||
|
||||
MultiByteToWideChar( CP_ACP
|
||||
, 0L
|
||||
, achTemp
|
||||
, lstrlenA(achTemp) + 1
|
||||
, achFileName
|
||||
, NUMELMS(achFileName) );
|
||||
}
|
||||
|
||||
//
|
||||
// first registering, register all OLE servers
|
||||
//
|
||||
if( bRegister )
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
|
||||
hr = RegisterAllServers( achFileName, TRUE );
|
||||
}
|
||||
|
||||
//
|
||||
// next, register/unregister all filters
|
||||
//
|
||||
|
||||
if( SUCCEEDED(hr) )
|
||||
{
|
||||
// init is ref counted so call just in case
|
||||
// we're being called cold.
|
||||
//
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- CoInitialize")));
|
||||
hr = CoInitialize( (LPVOID)NULL );
|
||||
ASSERT( SUCCEEDED(hr) );
|
||||
|
||||
// get hold of IFilterMapper2
|
||||
//
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- obtain IFilterMapper2")));
|
||||
IFilterMapper2 *pIFM2 = 0;
|
||||
IFilterMapper *pIFM = 0;
|
||||
hr = CoCreateInstance( CLSID_FilterMapper2
|
||||
, NULL
|
||||
, CLSCTX_INPROC_SERVER
|
||||
, IID_IFilterMapper2
|
||||
, (void **)&pIFM2 );
|
||||
if(FAILED(hr))
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- trying IFilterMapper instead")));
|
||||
|
||||
hr = CoCreateInstance(
|
||||
CLSID_FilterMapper,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IFilterMapper,
|
||||
(void **)&pIFM);
|
||||
}
|
||||
if( SUCCEEDED(hr) )
|
||||
{
|
||||
// scan through array of CFactoryTemplates
|
||||
// registering servers and filters.
|
||||
//
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- register Filters")));
|
||||
for( int i = 0; i < g_cTemplates; i++ )
|
||||
{
|
||||
// get i'th template
|
||||
//
|
||||
const CFactoryTemplate *pT = &g_Templates[i];
|
||||
|
||||
if( NULL != pT->m_pAMovieSetup_Filter )
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"), (LPCWSTR)pT->m_Name ));
|
||||
|
||||
if(pIFM2)
|
||||
{
|
||||
hr = AMovieSetupRegisterFilter2( pT->m_pAMovieSetup_Filter, pIFM2, bRegister );
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = AMovieSetupRegisterFilter( pT->m_pAMovieSetup_Filter, pIFM, bRegister );
|
||||
}
|
||||
}
|
||||
|
||||
// check final error for this pass
|
||||
// and break loop if we failed
|
||||
//
|
||||
if( FAILED(hr) )
|
||||
break;
|
||||
}
|
||||
|
||||
// release interface
|
||||
//
|
||||
if(pIFM2)
|
||||
pIFM2->Release();
|
||||
else
|
||||
pIFM->Release();
|
||||
|
||||
}
|
||||
|
||||
// and clear up
|
||||
//
|
||||
CoFreeUnusedLibraries();
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
//
|
||||
// if unregistering, unregister all OLE servers
|
||||
//
|
||||
if( SUCCEEDED(hr) && !bRegister )
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
|
||||
hr = RegisterAllServers( achFileName, FALSE );
|
||||
}
|
||||
|
||||
DbgLog((LOG_TRACE, 2, TEXT("- return %0x"), hr));
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// AMovieDllRegisterServer()
|
||||
//
|
||||
// default ActiveMovie dll setup function
|
||||
// - to use must be called from an exported
|
||||
// function named DllRegisterServer()
|
||||
//
|
||||
// this function is table driven using the
|
||||
// static members of the CFactoryTemplate
|
||||
// class defined in the dll.
|
||||
//
|
||||
// it registers the Dll as the InprocServer32
|
||||
// and then calls the IAMovieSetup.Register
|
||||
// method.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
STDAPI
|
||||
AMovieDllRegisterServer( void )
|
||||
{
|
||||
HRESULT hr = NOERROR;
|
||||
|
||||
// get file name (where g_hInst is the
|
||||
// instance handle of the filter dll)
|
||||
//
|
||||
WCHAR achFileName[MAX_PATH];
|
||||
|
||||
{
|
||||
// WIN95 doesn't support GetModuleFileNameW
|
||||
//
|
||||
char achTemp[MAX_PATH];
|
||||
|
||||
if( 0 == GetModuleFileNameA( g_hInst
|
||||
, achTemp
|
||||
, sizeof(achTemp) ) )
|
||||
{
|
||||
// we've failed!
|
||||
DWORD dwerr = GetLastError();
|
||||
return AmHresultFromWin32(dwerr);
|
||||
}
|
||||
|
||||
MultiByteToWideChar( CP_ACP
|
||||
, 0L
|
||||
, achTemp
|
||||
, lstrlenA(achTemp) + 1
|
||||
, achFileName
|
||||
, NUMELMS(achFileName) );
|
||||
}
|
||||
|
||||
// scan through array of CFactoryTemplates
|
||||
// registering servers and filters.
|
||||
//
|
||||
for( int i = 0; i < g_cTemplates; i++ )
|
||||
{
|
||||
// get i'th template
|
||||
//
|
||||
const CFactoryTemplate *pT = &g_Templates[i];
|
||||
|
||||
// register CLSID and InprocServer32
|
||||
//
|
||||
hr = AMovieSetupRegisterServer( *(pT->m_ClsID)
|
||||
, (LPCWSTR)pT->m_Name
|
||||
, achFileName );
|
||||
|
||||
// instantiate all servers and get hold of
|
||||
// IAMovieSetup, if implemented, and call
|
||||
// IAMovieSetup.Register() method
|
||||
//
|
||||
if( SUCCEEDED(hr) && (NULL != pT->m_lpfnNew) )
|
||||
{
|
||||
// instantiate object
|
||||
//
|
||||
PAMOVIESETUP psetup;
|
||||
hr = CoCreateInstance( *(pT->m_ClsID)
|
||||
, 0
|
||||
, CLSCTX_INPROC_SERVER
|
||||
, IID_IAMovieSetup
|
||||
, reinterpret_cast<void**>(&psetup) );
|
||||
if( SUCCEEDED(hr) )
|
||||
{
|
||||
hr = psetup->Unregister();
|
||||
if( SUCCEEDED(hr) )
|
||||
hr = psetup->Register();
|
||||
psetup->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (E_NOINTERFACE == hr )
|
||||
|| (VFW_E_NEED_OWNER == hr ) )
|
||||
hr = NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// check final error for this pass
|
||||
// and break loop if we failed
|
||||
//
|
||||
if( FAILED(hr) )
|
||||
break;
|
||||
|
||||
} // end-for
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// AMovieDllUnregisterServer()
|
||||
//
|
||||
// default ActiveMovie dll uninstall function
|
||||
// - to use must be called from an exported
|
||||
// function named DllRegisterServer()
|
||||
//
|
||||
// this function is table driven using the
|
||||
// static members of the CFactoryTemplate
|
||||
// class defined in the dll.
|
||||
//
|
||||
// it calls the IAMovieSetup.Unregister
|
||||
// method and then unregisters the Dll
|
||||
// as the InprocServer32
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
STDAPI
|
||||
AMovieDllUnregisterServer()
|
||||
{
|
||||
// initialize return code
|
||||
//
|
||||
HRESULT hr = NOERROR;
|
||||
|
||||
// scan through CFactory template and unregister
|
||||
// all OLE servers and filters.
|
||||
//
|
||||
for( int i = g_cTemplates; i--; )
|
||||
{
|
||||
// get i'th template
|
||||
//
|
||||
const CFactoryTemplate *pT = &g_Templates[i];
|
||||
|
||||
// check method exists
|
||||
//
|
||||
if( NULL != pT->m_lpfnNew )
|
||||
{
|
||||
// instantiate object
|
||||
//
|
||||
PAMOVIESETUP psetup;
|
||||
hr = CoCreateInstance( *(pT->m_ClsID)
|
||||
, 0
|
||||
, CLSCTX_INPROC_SERVER
|
||||
, IID_IAMovieSetup
|
||||
, reinterpret_cast<void**>(&psetup) );
|
||||
if( SUCCEEDED(hr) )
|
||||
{
|
||||
hr = psetup->Unregister();
|
||||
psetup->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (E_NOINTERFACE == hr )
|
||||
|| (VFW_E_NEED_OWNER == hr ) )
|
||||
hr = NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// unregister CLSID and InprocServer32
|
||||
//
|
||||
if( SUCCEEDED(hr) )
|
||||
{
|
||||
hr = AMovieSetupUnregisterServer( *(pT->m_ClsID) );
|
||||
}
|
||||
|
||||
// check final error for this pass
|
||||
// and break loop if we failed
|
||||
//
|
||||
if( FAILED(hr) )
|
||||
break;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: DllSetup.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// To be self registering, OLE servers must
|
||||
// export functions named DllRegisterServer
|
||||
// and DllUnregisterServer. To allow use of
|
||||
// custom and default implementations the
|
||||
// defaults are named AMovieDllRegisterServer
|
||||
// and AMovieDllUnregisterServer.
|
||||
//
|
||||
// To the use the default implementation you
|
||||
// must provide stub functions.
|
||||
//
|
||||
// i.e. STDAPI DllRegisterServer()
|
||||
// {
|
||||
// return AMovieDllRegisterServer();
|
||||
// }
|
||||
//
|
||||
// STDAPI DllUnregisterServer()
|
||||
// {
|
||||
// return AMovieDllUnregisterServer();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// AMovieDllRegisterServer calls IAMovieSetup.Register(), and
|
||||
// AMovieDllUnregisterServer calls IAMovieSetup.Unregister().
|
||||
|
||||
STDAPI AMovieDllRegisterServer2( BOOL );
|
||||
STDAPI AMovieDllRegisterServer();
|
||||
STDAPI AMovieDllUnregisterServer();
|
||||
|
||||
// helper functions
|
||||
STDAPI EliminateSubKey( HKEY, LPCTSTR );
|
||||
|
||||
|
||||
STDAPI
|
||||
AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata
|
||||
, IFilterMapper2 * pIFM2
|
||||
, BOOL bRegister );
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: DXMPerf.h
|
||||
//
|
||||
// Desc: Macros for DirectShow performance logging.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _DXMPERF_H_
|
||||
#define _DXMPERF_H_
|
||||
|
||||
#include <perfstruct.h>
|
||||
#include "perflog.h"
|
||||
|
||||
#ifdef _IA64_
|
||||
extern "C" unsigned __int64 __getReg( int whichReg );
|
||||
#pragma intrinsic(__getReg)
|
||||
#endif // _IA64_
|
||||
|
||||
|
||||
inline ULONGLONG _RDTSC( void ) {
|
||||
#ifdef _X86_
|
||||
LARGE_INTEGER li;
|
||||
__asm {
|
||||
_emit 0x0F
|
||||
_emit 0x31
|
||||
mov li.LowPart,eax
|
||||
mov li.HighPart,edx
|
||||
}
|
||||
return li.QuadPart;
|
||||
|
||||
#if 0 // This isn't tested yet
|
||||
|
||||
#elif defined (_IA64_)
|
||||
|
||||
#define INL_REGID_APITC 3116
|
||||
return __getReg( INL_REGID_APITC );
|
||||
|
||||
#endif // 0
|
||||
|
||||
#else // unsupported platform
|
||||
// not implemented on non x86/IA64 platforms
|
||||
return 0;
|
||||
#endif // _X86_/_IA64_
|
||||
}
|
||||
|
||||
#define DXMPERF_VIDEOREND 0x00000001
|
||||
#define DXMPERF_AUDIOGLITCH 0x00000002
|
||||
//#define GETTIME_BIT 0x00000001
|
||||
//#define AUDIOREND_BIT 0x00000004
|
||||
//#define FRAMEDROP_BIT 0x00000008
|
||||
#define AUDIOBREAK_BIT 0x00000010
|
||||
#define DXMPERF_AUDIORECV 0x00000020
|
||||
#define DXMPERF_AUDIOSLAVE 0x00000040
|
||||
#define DXMPERF_AUDIOBREAK 0x00000080
|
||||
|
||||
#define PERFLOG_CTOR( name, iface )
|
||||
#define PERFLOG_DTOR( name, iface )
|
||||
#define PERFLOG_DELIVER( name, source, dest, sample, pmt )
|
||||
#define PERFLOG_RECEIVE( name, source, dest, sample, pmt )
|
||||
#define PERFLOG_RUN( name, iface, time, oldstate )
|
||||
#define PERFLOG_PAUSE( name, iface, oldstate )
|
||||
#define PERFLOG_STOP( name, iface, oldstate )
|
||||
#define PERFLOG_JOINGRAPH( name, iface, graph )
|
||||
#define PERFLOG_GETBUFFER( allocator, sample )
|
||||
#define PERFLOG_RELBUFFER( allocator, sample )
|
||||
#define PERFLOG_CONNECT( connector, connectee, status, pmt )
|
||||
#define PERFLOG_RXCONNECT( connector, connectee, status, pmt )
|
||||
#define PERFLOG_DISCONNECT( disconnector, disconnectee, status )
|
||||
|
||||
#define PERFLOG_GETTIME( clock, time ) /*{ \
|
||||
PERFINFO_WMI_GETTIME perfData; \
|
||||
if (NULL != g_pTraceEvent) { \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_GETTIME; \
|
||||
perfData.data.cycleCounter = _RDTSC(); \
|
||||
perfData.data.dshowClock = (ULONGLONG) (time); \
|
||||
if (g_perfMasks[GETTIME_INDEX] & GETTIME_BIT) \
|
||||
(*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
|
||||
} \
|
||||
}*/
|
||||
|
||||
#define PERFLOG_AUDIOREND( clocktime, sampletime, psample, bytetime, cbytes ) /*{ \
|
||||
PERFINFO_WMI_AVREND perfData; \
|
||||
if (NULL != g_pTraceEvent) { \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_AUDIOREND; \
|
||||
perfData.data.cycleCounter = _RDTSC(); \
|
||||
perfData.data.dshowClock = (clocktime); \
|
||||
perfData.data.sampleTime = (sampletime); \
|
||||
if (g_perfMasks[AUDIOREND_INDEX] & AUDIOREND_BIT) \
|
||||
(*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
|
||||
} \
|
||||
}*/
|
||||
|
||||
#define PERFLOG_AUDIORECV(StreamTime,SampleStart,SampleStop,Discontinuity,Duration) \
|
||||
if (PerflogEnableFlags & DXMPERF_AUDIORECV) { \
|
||||
PERFINFO_WMI_AUDIORECV perfData; \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_AUDIORECV; \
|
||||
perfData.data.streamTime = StreamTime; \
|
||||
perfData.data.sampleStart = SampleStart; \
|
||||
perfData.data.sampleStop = SampleStop; \
|
||||
perfData.data.discontinuity = Discontinuity; \
|
||||
perfData.data.hwduration = Duration; \
|
||||
PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \
|
||||
}
|
||||
|
||||
#define PERFLOG_AUDIOSLAVE(MasterClock,SlaveClock,ErrorAccum,LastHighErrorSeen,LastLowErrorSeen) \
|
||||
if (PerflogEnableFlags & DXMPERF_AUDIOSLAVE) { \
|
||||
PERFINFO_WMI_AUDIOSLAVE perfData; \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_AUDIOSLAVE; \
|
||||
perfData.data.masterClock = MasterClock; \
|
||||
perfData.data.slaveClock = SlaveClock; \
|
||||
perfData.data.errorAccum = ErrorAccum; \
|
||||
perfData.data.lastHighErrorSeen = LastHighErrorSeen;\
|
||||
perfData.data.lastLowErrorSeen = LastLowErrorSeen; \
|
||||
PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \
|
||||
}
|
||||
|
||||
#define PERFLOG_AUDIOADDBREAK(IterNextWrite,OffsetNextWrite,IterWrite,OffsetWrite) \
|
||||
if (PerflogEnableFlags & DXMPERF_AUDIOBREAK) { \
|
||||
PERFINFO_WMI_AUDIOADDBREAK perfData; \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_AUDIOADDBREAK; \
|
||||
perfData.data.iterNextWrite = IterNextWrite; \
|
||||
perfData.data.offsetNextWrite = OffsetNextWrite; \
|
||||
perfData.data.iterWrite = IterWrite; \
|
||||
perfData.data.offsetWrite = OffsetWrite; \
|
||||
PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \
|
||||
}
|
||||
|
||||
#define PERFLOG_VIDEOREND( sampletime, clocktime, psample ) \
|
||||
if (PerflogEnableFlags & DXMPERF_VIDEOREND) { \
|
||||
PERFINFO_WMI_AVREND perfData; \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_VIDEOREND; \
|
||||
perfData.data.cycleCounter = _RDTSC(); \
|
||||
perfData.data.dshowClock = (clocktime); \
|
||||
perfData.data.sampleTime = (sampletime); \
|
||||
PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \
|
||||
}
|
||||
|
||||
#define PERFLOG_AUDIOGLITCH( instance, glitchtype, currenttime, previoustime ) \
|
||||
if (PerflogEnableFlags & DXMPERF_AUDIOGLITCH) { \
|
||||
PERFINFO_WMI_AUDIOGLITCH perfData; \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_DSOUNDGLITCH; \
|
||||
perfData.data.cycleCounter = _RDTSC(); \
|
||||
perfData.data.glitchType = (glitchtype); \
|
||||
perfData.data.sampleTime = (currenttime); \
|
||||
perfData.data.previousTime = (previoustime); \
|
||||
perfData.data.instanceId = (instance); \
|
||||
PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \
|
||||
}
|
||||
|
||||
#define PERFLOG_FRAMEDROP( sampletime, clocktime, psample, renderer ) /*{ \
|
||||
PERFINFO_WMI_FRAMEDROP perfData; \
|
||||
if (NULL != g_pTraceEvent) { \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_FRAMEDROP; \
|
||||
perfData.data.cycleCounter = _RDTSC(); \
|
||||
perfData.data.dshowClock = (clocktime); \
|
||||
perfData.data.frameTime = (sampletime); \
|
||||
if (g_perfMasks[FRAMEDROP_INDEX] & FRAMEDROP_BIT) \
|
||||
(*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
|
||||
} \
|
||||
}*/
|
||||
|
||||
/*
|
||||
#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs ) { \
|
||||
PERFINFO_WMI_AUDIOBREAK perfData; \
|
||||
if (NULL != g_pTraceEvent) { \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_AUDIOBREAK; \
|
||||
perfData.data.cycleCounter = _RDTSC(); \
|
||||
perfData.data.dshowClock = (writepos); \
|
||||
perfData.data.sampleTime = (nextwrite); \
|
||||
perfData.data.sampleDuration = (msecs); \
|
||||
if (g_perfMasks[AUDIOBREAK_INDEX] & AUDIOBREAK_BIT) \
|
||||
(*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
|
||||
} \
|
||||
}
|
||||
*/
|
||||
|
||||
#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs ) \
|
||||
if (PerflogEnableFlags & AUDIOBREAK_BIT) { \
|
||||
PERFINFO_WMI_AUDIOBREAK perfData; \
|
||||
memset( &perfData, 0, sizeof( perfData ) ); \
|
||||
perfData.header.Size = sizeof( perfData ); \
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
|
||||
perfData.header.Guid = GUID_AUDIOBREAK; \
|
||||
perfData.data.cycleCounter = _RDTSC(); \
|
||||
perfData.data.dshowClock = (writepos); \
|
||||
perfData.data.sampleTime = (nextwrite); \
|
||||
perfData.data.sampleDuration = (msecs); \
|
||||
PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \
|
||||
} \
|
||||
|
||||
|
||||
inline
|
||||
VOID PERFLOG_STREAMTRACE(
|
||||
ULONG Level,
|
||||
ULONG Id,
|
||||
ULONGLONG DShowClock,
|
||||
ULONGLONG Data1,
|
||||
ULONGLONG Data2,
|
||||
ULONGLONG Data3,
|
||||
ULONGLONG Data4
|
||||
)
|
||||
{
|
||||
if (Level <= PerflogModuleLevel)
|
||||
{
|
||||
PERFINFO_WMI_STREAMTRACE perfData;
|
||||
memset( &perfData, 0, sizeof( perfData ) );
|
||||
perfData.header.Size = sizeof( perfData );
|
||||
perfData.header.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
perfData.header.Guid = GUID_STREAMTRACE;
|
||||
perfData.data.dshowClock = DShowClock;
|
||||
perfData.data.id = Id;
|
||||
perfData.data.data[0] = Data1;
|
||||
perfData.data.data[1] = Data2;
|
||||
perfData.data.data[2] = Data3;
|
||||
perfData.data.data[3] = Data4;
|
||||
PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // _DXMPERF_H_
|
|
@ -0,0 +1,101 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: FourCC.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// FOURCCMap
|
||||
//
|
||||
// provides a mapping between old-style multimedia format DWORDs
|
||||
// and new-style GUIDs.
|
||||
//
|
||||
// A range of 4 billion GUIDs has been allocated to ensure that this
|
||||
// mapping can be done straightforwardly one-to-one in both directions.
|
||||
//
|
||||
// January 95
|
||||
|
||||
|
||||
#ifndef __FOURCC__
|
||||
#define __FOURCC__
|
||||
|
||||
|
||||
// Multimedia format types are marked with DWORDs built from four 8-bit
|
||||
// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include
|
||||
// a subtype GUID. In order to simplify the mapping, GUIDs in the range:
|
||||
// XXXXXXXX-0000-0010-8000-00AA00389B71
|
||||
// are reserved for FOURCCs.
|
||||
|
||||
class FOURCCMap : public GUID
|
||||
{
|
||||
|
||||
public:
|
||||
FOURCCMap();
|
||||
FOURCCMap(DWORD Fourcc);
|
||||
FOURCCMap(const GUID *);
|
||||
|
||||
|
||||
DWORD GetFOURCC(void);
|
||||
void SetFOURCC(DWORD fourcc);
|
||||
void SetFOURCC(const GUID *);
|
||||
|
||||
private:
|
||||
void InitGUID();
|
||||
};
|
||||
|
||||
#define GUID_Data2 0
|
||||
#define GUID_Data3 0x10
|
||||
#define GUID_Data4_1 0xaa000080
|
||||
#define GUID_Data4_2 0x719b3800
|
||||
|
||||
inline void
|
||||
FOURCCMap::InitGUID() {
|
||||
Data2 = GUID_Data2;
|
||||
Data3 = GUID_Data3;
|
||||
((DWORD *)Data4)[0] = GUID_Data4_1;
|
||||
((DWORD *)Data4)[1] = GUID_Data4_2;
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap() {
|
||||
InitGUID();
|
||||
SetFOURCC( DWORD(0));
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap(DWORD fourcc)
|
||||
{
|
||||
InitGUID();
|
||||
SetFOURCC(fourcc);
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap(const GUID * pGuid)
|
||||
{
|
||||
InitGUID();
|
||||
SetFOURCC(pGuid);
|
||||
}
|
||||
|
||||
inline void
|
||||
FOURCCMap::SetFOURCC(const GUID * pGuid)
|
||||
{
|
||||
FOURCCMap * p = (FOURCCMap*) pGuid;
|
||||
SetFOURCC(p->GetFOURCC());
|
||||
}
|
||||
|
||||
inline void
|
||||
FOURCCMap::SetFOURCC(DWORD fourcc)
|
||||
{
|
||||
Data1 = fourcc;
|
||||
}
|
||||
|
||||
inline DWORD
|
||||
FOURCCMap::GetFOURCC(void)
|
||||
{
|
||||
return Data1;
|
||||
}
|
||||
|
||||
#endif /* __FOURCC__ */
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Measure.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
The idea is to pepper the source code with interesting measurements and
|
||||
have the last few thousand of these recorded in a circular buffer that
|
||||
can be post-processed to give interesting numbers.
|
||||
|
||||
WHAT THE LOG LOOKS LIKE:
|
||||
|
||||
Time (sec) Type Delta Incident_Name
|
||||
0.055,41 NOTE -. Incident Nine - Another note
|
||||
0.055,42 NOTE 0.000,01 Incident Nine - Another note
|
||||
0.055,44 NOTE 0.000,02 Incident Nine - Another note
|
||||
0.055,45 STOP -. Incident Eight - Also random
|
||||
0.055,47 START -. Incident Seven - Random
|
||||
0.055,49 NOTE 0.000,05 Incident Nine - Another note
|
||||
------- <etc. there is a lot of this> ----------------
|
||||
0.125,60 STOP 0.000,03 Msr_Stop
|
||||
0.125,62 START -. Msr_Start
|
||||
0.125,63 START -. Incident Two - Start/Stop
|
||||
0.125,65 STOP 0.000,03 Msr_Start
|
||||
0.125,66 START -. Msr_Stop
|
||||
0.125,68 STOP 0.000,05 Incident Two - Start/Stop
|
||||
0.125,70 STOP 0.000,04 Msr_Stop
|
||||
0.125,72 START -. Msr_Start
|
||||
0.125,73 START -. Incident Two - Start/Stop
|
||||
0.125,75 STOP 0.000,03 Msr_Start
|
||||
0.125,77 START -. Msr_Stop
|
||||
0.125,78 STOP 0.000,05 Incident Two - Start/Stop
|
||||
0.125,80 STOP 0.000,03 Msr_Stop
|
||||
0.125,81 NOTE -. Incident Three - single Note
|
||||
0.125,83 START -. Incident Four - Start, no stop
|
||||
0.125,85 START -. Incident Five - Single Start/Stop
|
||||
0.125,87 STOP 0.000,02 Incident Five - Single Start/Stop
|
||||
|
||||
Number Average StdDev Smallest Largest Incident_Name
|
||||
10 0.000,58 0.000,10 0.000,55 0.000,85 Incident One - Note
|
||||
50 0.000,05 0.000,00 0.000,05 0.000,05 Incident Two - Start/Stop
|
||||
1 -. -. -. -. Incident Three - single Note
|
||||
0 -. -. -. -. Incident Four - Start, no stop
|
||||
1 0.000,02 -. 0.000,02 0.000,02 Incident Five - Single Start/Stop
|
||||
0 -. -. -. -. Incident Six - zero occurrences
|
||||
100 0.000,25 0.000,12 0.000,02 0.000,62 Incident Seven - Random
|
||||
100 0.000,79 0.000,48 0.000,02 0.001,92 Incident Eight - Also random
|
||||
5895 0.000,01 0.000,01 0.000,01 0.000,56 Incident Nine - Another note
|
||||
10 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Note
|
||||
50 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Start
|
||||
50 0.000,04 0.000,03 0.000,03 0.000,31 Msr_Stop
|
||||
|
||||
WHAT IT MEANS:
|
||||
The log shows what happened and when. Each line shows the time at which
|
||||
something happened (see WHAT YOU CODE below) what it was that happened
|
||||
and (if approporate) the time since the corresponding previous event
|
||||
(that's the delta column).
|
||||
|
||||
The statistics show how many times each event occurred, what the average
|
||||
delta time was, also the standard deviation, largest and smalles delta.
|
||||
|
||||
WHAT YOU CODE:
|
||||
|
||||
Before anything else executes: - register your ids
|
||||
|
||||
int id1 = Msr_Register("Incident One - Note");
|
||||
int id2 = Msr_Register("Incident Two - Start/Stop");
|
||||
int id3 = Msr_Register("Incident Three - single Note");
|
||||
etc.
|
||||
|
||||
At interesting moments:
|
||||
|
||||
// To measure a repetitive event - e.g. end of bitblt to screen
|
||||
Msr_Note(Id9); // e.g. "video frame hiting the screen NOW!"
|
||||
|
||||
or
|
||||
|
||||
// To measure an elapsed time e.g. time taken to decode an MPEG B-frame
|
||||
Msr_Start(Id2); // e.g. "Starting to decode MPEG B-frame"
|
||||
. . .
|
||||
MsrStop(Id2); // "Finished MPEG decode"
|
||||
|
||||
At the end:
|
||||
|
||||
HANDLE hFile;
|
||||
hFile = CreateFile("Perf.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
||||
Msr_Dump(hFile); // This writes the log out to the file
|
||||
CloseHandle(hFile);
|
||||
|
||||
or
|
||||
|
||||
Msr_Dump(NULL); // This writes it to DbgLog((LOG_TRACE,0, ... ));
|
||||
// but if you are writing it out to the debugger
|
||||
// then the times are probably all garbage because
|
||||
// the debugger can make things run awfully slow.
|
||||
|
||||
A given id should be used either for start / stop or Note calls. If Notes
|
||||
are mixed in with Starts and Stops their statistics will be gibberish.
|
||||
|
||||
If you code the calls in upper case i.e. MSR_START(idMunge); then you get
|
||||
macros which will turn into nothing unless PERF is defined.
|
||||
|
||||
You can reset the statistical counts for a given id by calling Reset(Id).
|
||||
They are reset by default at the start.
|
||||
It logs Reset as a special incident, so you can see it in the log.
|
||||
|
||||
The log is a circular buffer in storage (to try to minimise disk I/O).
|
||||
It overwrites the oldest entries once full. The statistics include ALL
|
||||
incidents since the last Reset, whether still visible in the log or not.
|
||||
*/
|
||||
|
||||
#ifndef __MEASURE__
|
||||
#define __MEASURE__
|
||||
|
||||
#ifdef PERF
|
||||
#define MSR_INIT() Msr_Init()
|
||||
#define MSR_TERMINATE() Msr_Terminate()
|
||||
#define MSR_REGISTER(a) Msr_Register(a)
|
||||
#define MSR_RESET(a) Msr_Reset(a)
|
||||
#define MSR_CONTROL(a) Msr_Control(a)
|
||||
#define MSR_START(a) Msr_Start(a)
|
||||
#define MSR_STOP(a) Msr_Stop(a)
|
||||
#define MSR_NOTE(a) Msr_Note(a)
|
||||
#define MSR_INTEGER(a,b) Msr_Integer(a,b)
|
||||
#define MSR_DUMP(a) Msr_Dump(a)
|
||||
#define MSR_DUMPSTATS(a) Msr_DumpStats(a)
|
||||
#else
|
||||
#define MSR_INIT() ((void)0)
|
||||
#define MSR_TERMINATE() ((void)0)
|
||||
#define MSR_REGISTER(a) 0
|
||||
#define MSR_RESET(a) ((void)0)
|
||||
#define MSR_CONTROL(a) ((void)0)
|
||||
#define MSR_START(a) ((void)0)
|
||||
#define MSR_STOP(a) ((void)0)
|
||||
#define MSR_NOTE(a) ((void)0)
|
||||
#define MSR_INTEGER(a,b) ((void)0)
|
||||
#define MSR_DUMP(a) ((void)0)
|
||||
#define MSR_DUMPSTATS(a) ((void)0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// This must be called first - (called by the DllEntry)
|
||||
|
||||
void WINAPI Msr_Init(void);
|
||||
|
||||
|
||||
// Call this last to clean up (or just let it fall off the end - who cares?)
|
||||
|
||||
void WINAPI Msr_Terminate(void);
|
||||
|
||||
|
||||
// Call this to get an Id for an "incident" that you can pass to Start, Stop or Note
|
||||
// everything that's logged is called an "incident".
|
||||
|
||||
int WINAPI Msr_Register(__in LPTSTR Incident);
|
||||
|
||||
|
||||
// Reset the statistical counts for an incident
|
||||
|
||||
void WINAPI Msr_Reset(int Id);
|
||||
|
||||
|
||||
// Reset all the counts for all incidents
|
||||
#define MSR_RESET_ALL 0
|
||||
#define MSR_PAUSE 1
|
||||
#define MSR_RUN 2
|
||||
|
||||
void WINAPI Msr_Control(int iAction);
|
||||
|
||||
|
||||
// log the start of an operation
|
||||
|
||||
void WINAPI Msr_Start(int Id);
|
||||
|
||||
|
||||
// log the end of an operation
|
||||
|
||||
void WINAPI Msr_Stop(int Id);
|
||||
|
||||
|
||||
// log a one-off or repetitive operation
|
||||
|
||||
void WINAPI Msr_Note(int Id);
|
||||
|
||||
|
||||
// log an integer (on which we can see statistics later)
|
||||
void WINAPI Msr_Integer(int Id, int n);
|
||||
|
||||
|
||||
// print out all the vaialable log (it may have wrapped) and then the statistics.
|
||||
// When the log wraps you lose log but the statistics are still complete.
|
||||
// hFIle==NULL => use DbgLog
|
||||
// otherwise hFile must have come from CreateFile or OpenFile.
|
||||
|
||||
void WINAPI Msr_Dump(HANDLE hFile);
|
||||
|
||||
|
||||
// just dump the statistics - never mind the log
|
||||
|
||||
void WINAPI Msr_DumpStats(HANDLE hFile);
|
||||
|
||||
// Type definitions in case you want to declare a pointer to the dump functions
|
||||
// (makes it a trifle easier to do dynamic linking
|
||||
// i.e. LoadModule, GetProcAddress and call that)
|
||||
|
||||
// Typedefs so can declare MSR_DUMPPROC *MsrDumpStats; or whatever
|
||||
typedef void WINAPI MSR_DUMPPROC(HANDLE hFile);
|
||||
typedef void WINAPI MSR_CONTROLPROC(int iAction);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __MEASURE__
|
|
@ -0,0 +1,120 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: MsgThrd.h
|
||||
//
|
||||
// Desc: DirectShow base classes - provides support for a worker thread
|
||||
// class to which one can asynchronously post messages.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Message class - really just a structure.
|
||||
//
|
||||
class CMsg {
|
||||
public:
|
||||
UINT uMsg;
|
||||
DWORD dwFlags;
|
||||
LPVOID lpParam;
|
||||
CAMEvent *pEvent;
|
||||
|
||||
CMsg(UINT u, DWORD dw, __inout_opt LPVOID lp, __in_opt CAMEvent *pEvnt)
|
||||
: uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {}
|
||||
|
||||
CMsg()
|
||||
: uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {}
|
||||
};
|
||||
|
||||
// This is the actual thread class. It exports all the usual thread control
|
||||
// functions. The created thread is different from a normal WIN32 thread in
|
||||
// that it is prompted to perform particaular tasks by responding to messages
|
||||
// posted to its message queue.
|
||||
//
|
||||
class AM_NOVTABLE CMsgThread {
|
||||
private:
|
||||
static DWORD WINAPI DefaultThreadProc(__inout LPVOID lpParam);
|
||||
DWORD m_ThreadId;
|
||||
HANDLE m_hThread;
|
||||
|
||||
protected:
|
||||
|
||||
// if you want to override GetThreadMsg to block on other things
|
||||
// as well as this queue, you need access to this
|
||||
CGenericList<CMsg> m_ThreadQueue;
|
||||
CCritSec m_Lock;
|
||||
HANDLE m_hSem;
|
||||
LONG m_lWaiting;
|
||||
|
||||
public:
|
||||
CMsgThread()
|
||||
: m_ThreadId(0),
|
||||
m_hThread(NULL),
|
||||
m_lWaiting(0),
|
||||
m_hSem(NULL),
|
||||
// make a list with a cache of 5 items
|
||||
m_ThreadQueue(NAME("MsgThread list"), 5)
|
||||
{
|
||||
}
|
||||
|
||||
~CMsgThread();
|
||||
// override this if you want to block on other things as well
|
||||
// as the message loop
|
||||
void virtual GetThreadMsg(__out CMsg *msg);
|
||||
|
||||
// override this if you want to do something on thread startup
|
||||
virtual void OnThreadInit() {
|
||||
};
|
||||
|
||||
BOOL CreateThread();
|
||||
|
||||
BOOL WaitForThreadExit(__out LPDWORD lpdwExitCode) {
|
||||
if (m_hThread != NULL) {
|
||||
WaitForSingleObject(m_hThread, INFINITE);
|
||||
return GetExitCodeThread(m_hThread, lpdwExitCode);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD ResumeThread() {
|
||||
return ::ResumeThread(m_hThread);
|
||||
}
|
||||
|
||||
DWORD SuspendThread() {
|
||||
return ::SuspendThread(m_hThread);
|
||||
}
|
||||
|
||||
int GetThreadPriority() {
|
||||
return ::GetThreadPriority(m_hThread);
|
||||
}
|
||||
|
||||
BOOL SetThreadPriority(int nPriority) {
|
||||
return ::SetThreadPriority(m_hThread, nPriority);
|
||||
}
|
||||
|
||||
HANDLE GetThreadHandle() {
|
||||
return m_hThread;
|
||||
}
|
||||
|
||||
DWORD GetThreadId() {
|
||||
return m_ThreadId;
|
||||
}
|
||||
|
||||
|
||||
void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags,
|
||||
__in_opt LPVOID lpMsgParam, __in_opt CAMEvent *pEvent = NULL) {
|
||||
CAutoLock lck(&m_Lock);
|
||||
CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent);
|
||||
m_ThreadQueue.AddTail(pMsg);
|
||||
if (m_lWaiting != 0) {
|
||||
ReleaseSemaphore(m_hSem, m_lWaiting, 0);
|
||||
m_lWaiting = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This is the function prototype of the function that the client
|
||||
// supplies. It is always called on the created thread, never on
|
||||
// the creator thread.
|
||||
//
|
||||
virtual LRESULT ThreadMessageProc(
|
||||
UINT uMsg, DWORD dwFlags, __inout_opt LPVOID lpParam, __in_opt CAMEvent *pEvent) = 0;
|
||||
};
|
||||
|
|
@ -0,0 +1,478 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: MType.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a class that holds and
|
||||
// manages media type information.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// helper class that derived pin objects can use to compare media
|
||||
// types etc. Has same data members as the struct AM_MEDIA_TYPE defined
|
||||
// in the streams IDL file, but also has (non-virtual) functions
|
||||
|
||||
#include <streams.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
CMediaType::~CMediaType(){
|
||||
FreeMediaType(*this);
|
||||
}
|
||||
|
||||
|
||||
CMediaType::CMediaType()
|
||||
{
|
||||
InitMediaType();
|
||||
}
|
||||
|
||||
|
||||
CMediaType::CMediaType(const GUID * type)
|
||||
{
|
||||
InitMediaType();
|
||||
majortype = *type;
|
||||
}
|
||||
|
||||
|
||||
// copy constructor does a deep copy of the format block
|
||||
|
||||
CMediaType::CMediaType(const AM_MEDIA_TYPE& rt, __out_opt HRESULT* phr)
|
||||
{
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr) && (NULL != phr)) {
|
||||
*phr = hr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CMediaType::CMediaType(const CMediaType& rt, __out_opt HRESULT* phr)
|
||||
{
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr) && (NULL != phr)) {
|
||||
*phr = hr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// this class inherits publicly from AM_MEDIA_TYPE so the compiler could generate
|
||||
// the following assignment operator itself, however it could introduce some
|
||||
// memory conflicts and leaks in the process because the structure contains
|
||||
// a dynamically allocated block (pbFormat) which it will not copy correctly
|
||||
|
||||
CMediaType&
|
||||
CMediaType::operator=(const AM_MEDIA_TYPE& rt)
|
||||
{
|
||||
Set(rt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CMediaType&
|
||||
CMediaType::operator=(const CMediaType& rt)
|
||||
{
|
||||
*this = (AM_MEDIA_TYPE &) rt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::operator == (const CMediaType& rt) const
|
||||
{
|
||||
// I don't believe we need to check sample size or
|
||||
// temporal compression flags, since I think these must
|
||||
// be represented in the type, subtype and format somehow. They
|
||||
// are pulled out as separate flags so that people who don't understand
|
||||
// the particular format representation can still see them, but
|
||||
// they should duplicate information in the format block.
|
||||
|
||||
return ((IsEqualGUID(majortype,rt.majortype) == TRUE) &&
|
||||
(IsEqualGUID(subtype,rt.subtype) == TRUE) &&
|
||||
(IsEqualGUID(formattype,rt.formattype) == TRUE) &&
|
||||
(cbFormat == rt.cbFormat) &&
|
||||
( (cbFormat == 0) ||
|
||||
pbFormat != NULL && rt.pbFormat != NULL &&
|
||||
(memcmp(pbFormat, rt.pbFormat, cbFormat) == 0)));
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
CMediaType::operator != (const CMediaType& rt) const
|
||||
{
|
||||
/* Check to see if they are equal */
|
||||
|
||||
if (*this == rt) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
CMediaType::Set(const CMediaType& rt)
|
||||
{
|
||||
return Set((AM_MEDIA_TYPE &) rt);
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
CMediaType::Set(const AM_MEDIA_TYPE& rt)
|
||||
{
|
||||
if (&rt != this) {
|
||||
FreeMediaType(*this);
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr)) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
CMediaType::IsValid() const
|
||||
{
|
||||
return (!IsEqualGUID(majortype,GUID_NULL));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetType(const GUID* ptype)
|
||||
{
|
||||
majortype = *ptype;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetSubtype(const GUID* ptype)
|
||||
{
|
||||
subtype = *ptype;
|
||||
}
|
||||
|
||||
|
||||
ULONG
|
||||
CMediaType::GetSampleSize() const {
|
||||
if (IsFixedSize()) {
|
||||
return lSampleSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetSampleSize(ULONG sz) {
|
||||
if (sz == 0) {
|
||||
SetVariableSize();
|
||||
} else {
|
||||
bFixedSizeSamples = TRUE;
|
||||
lSampleSize = sz;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetVariableSize() {
|
||||
bFixedSizeSamples = FALSE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetTemporalCompression(BOOL bCompressed) {
|
||||
bTemporalCompression = bCompressed;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::SetFormat(__in_bcount(cb) BYTE * pformat, ULONG cb)
|
||||
{
|
||||
if (NULL == AllocFormatBuffer(cb))
|
||||
return(FALSE);
|
||||
|
||||
ASSERT(pbFormat);
|
||||
memcpy(pbFormat, pformat, cb);
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
// set the type of the media type format block, this type defines what you
|
||||
// will actually find in the format pointer. For example FORMAT_VideoInfo or
|
||||
// FORMAT_WaveFormatEx. In the future this may be an interface pointer to a
|
||||
// property set. Before sending out media types this should be filled in.
|
||||
|
||||
void
|
||||
CMediaType::SetFormatType(const GUID *pformattype)
|
||||
{
|
||||
formattype = *pformattype;
|
||||
}
|
||||
|
||||
|
||||
// reset the format buffer
|
||||
|
||||
void CMediaType::ResetFormatBuffer()
|
||||
{
|
||||
if (cbFormat) {
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
cbFormat = 0;
|
||||
pbFormat = NULL;
|
||||
}
|
||||
|
||||
|
||||
// allocate length bytes for the format and return a read/write pointer
|
||||
// If we cannot allocate the new block of memory we return NULL leaving
|
||||
// the original block of memory untouched (as does ReallocFormatBuffer)
|
||||
|
||||
BYTE*
|
||||
CMediaType::AllocFormatBuffer(ULONG length)
|
||||
{
|
||||
ASSERT(length);
|
||||
|
||||
// do the types have the same buffer size
|
||||
|
||||
if (cbFormat == length) {
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
// allocate the new format buffer
|
||||
|
||||
BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
|
||||
if (pNewFormat == NULL) {
|
||||
if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// delete the old format
|
||||
|
||||
if (cbFormat != 0) {
|
||||
ASSERT(pbFormat);
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
|
||||
cbFormat = length;
|
||||
pbFormat = pNewFormat;
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
|
||||
// reallocate length bytes for the format and return a read/write pointer
|
||||
// to it. We keep as much information as we can given the new buffer size
|
||||
// if this fails the original format buffer is left untouched. The caller
|
||||
// is responsible for ensuring the size of memory required is non zero
|
||||
|
||||
BYTE*
|
||||
CMediaType::ReallocFormatBuffer(ULONG length)
|
||||
{
|
||||
ASSERT(length);
|
||||
|
||||
// do the types have the same buffer size
|
||||
|
||||
if (cbFormat == length) {
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
// allocate the new format buffer
|
||||
|
||||
BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
|
||||
if (pNewFormat == NULL) {
|
||||
if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// copy any previous format (or part of if new is smaller)
|
||||
// delete the old format and replace with the new one
|
||||
|
||||
if (cbFormat != 0) {
|
||||
ASSERT(pbFormat);
|
||||
memcpy(pNewFormat,pbFormat,min(length,cbFormat));
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
|
||||
cbFormat = length;
|
||||
pbFormat = pNewFormat;
|
||||
return pNewFormat;
|
||||
}
|
||||
|
||||
// initialise a media type structure
|
||||
|
||||
void CMediaType::InitMediaType()
|
||||
{
|
||||
ZeroMemory((PVOID)this, sizeof(*this));
|
||||
lSampleSize = 1;
|
||||
bFixedSizeSamples = TRUE;
|
||||
}
|
||||
|
||||
|
||||
// a partially specified media type can be passed to IPin::Connect
|
||||
// as a constraint on the media type used in the connection.
|
||||
// the type, subtype or format type can be null.
|
||||
BOOL
|
||||
CMediaType::IsPartiallySpecified(void) const
|
||||
{
|
||||
if ((majortype == GUID_NULL) ||
|
||||
(formattype == GUID_NULL)) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::MatchesPartial(const CMediaType* ppartial) const
|
||||
{
|
||||
if ((ppartial->majortype != GUID_NULL) &&
|
||||
(majortype != ppartial->majortype)) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((ppartial->subtype != GUID_NULL) &&
|
||||
(subtype != ppartial->subtype)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ppartial->formattype != GUID_NULL) {
|
||||
// if the format block is specified then it must match exactly
|
||||
if (formattype != ppartial->formattype) {
|
||||
return FALSE;
|
||||
}
|
||||
if (cbFormat != ppartial->cbFormat) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((cbFormat != 0) &&
|
||||
(memcmp(pbFormat, ppartial->pbFormat, cbFormat) != 0)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// general purpose function to delete a heap allocated AM_MEDIA_TYPE structure
|
||||
// which is useful when calling IEnumMediaTypes::Next as the interface
|
||||
// implementation allocates the structures which you must later delete
|
||||
// the format block may also be a pointer to an interface to release
|
||||
|
||||
void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
// allow NULL pointers for coding simplicity
|
||||
|
||||
if (pmt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
FreeMediaType(*pmt);
|
||||
CoTaskMemFree((PVOID)pmt);
|
||||
}
|
||||
|
||||
|
||||
// this also comes in useful when using the IEnumMediaTypes interface so
|
||||
// that you can copy a media type, you can do nearly the same by creating
|
||||
// a CMediaType object but as soon as it goes out of scope the destructor
|
||||
// will delete the memory it allocated (this takes a copy of the memory)
|
||||
|
||||
AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc)
|
||||
{
|
||||
ASSERT(pSrc);
|
||||
|
||||
// Allocate a block of memory for the media type
|
||||
|
||||
AM_MEDIA_TYPE *pMediaType =
|
||||
(AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
||||
|
||||
if (pMediaType == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Copy the variable length format block
|
||||
|
||||
HRESULT hr = CopyMediaType(pMediaType,pSrc);
|
||||
if (FAILED(hr)) {
|
||||
CoTaskMemFree((PVOID)pMediaType);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pMediaType;
|
||||
}
|
||||
|
||||
|
||||
// Copy 1 media type to another
|
||||
|
||||
HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)
|
||||
{
|
||||
// We'll leak if we copy onto one that already exists - there's one
|
||||
// case we can check like that - copying to itself.
|
||||
ASSERT(pmtSource != pmtTarget);
|
||||
*pmtTarget = *pmtSource;
|
||||
if (pmtSource->cbFormat != 0) {
|
||||
ASSERT(pmtSource->pbFormat != NULL);
|
||||
pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc(pmtSource->cbFormat);
|
||||
if (pmtTarget->pbFormat == NULL) {
|
||||
pmtTarget->cbFormat = 0;
|
||||
return E_OUTOFMEMORY;
|
||||
} else {
|
||||
CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,
|
||||
pmtTarget->cbFormat);
|
||||
}
|
||||
}
|
||||
if (pmtTarget->pUnk != NULL) {
|
||||
pmtTarget->pUnk->AddRef();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Free an existing media type (ie free resources it holds)
|
||||
|
||||
void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt)
|
||||
{
|
||||
if (mt.cbFormat != 0) {
|
||||
CoTaskMemFree((PVOID)mt.pbFormat);
|
||||
|
||||
// Strictly unnecessary but tidier
|
||||
mt.cbFormat = 0;
|
||||
mt.pbFormat = NULL;
|
||||
}
|
||||
if (mt.pUnk != NULL) {
|
||||
mt.pUnk->Release();
|
||||
mt.pUnk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize a media type from a WAVEFORMATEX
|
||||
|
||||
STDAPI CreateAudioMediaType(
|
||||
const WAVEFORMATEX *pwfx,
|
||||
__out AM_MEDIA_TYPE *pmt,
|
||||
BOOL bSetFormat
|
||||
)
|
||||
{
|
||||
pmt->majortype = MEDIATYPE_Audio;
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
pmt->subtype = ((PWAVEFORMATEXTENSIBLE)pwfx)->SubFormat;
|
||||
} else {
|
||||
pmt->subtype = FOURCCMap(pwfx->wFormatTag);
|
||||
}
|
||||
pmt->formattype = FORMAT_WaveFormatEx;
|
||||
pmt->bFixedSizeSamples = TRUE;
|
||||
pmt->bTemporalCompression = FALSE;
|
||||
pmt->lSampleSize = pwfx->nBlockAlign;
|
||||
pmt->pUnk = NULL;
|
||||
if (bSetFormat) {
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
|
||||
pmt->cbFormat = sizeof(WAVEFORMATEX);
|
||||
} else {
|
||||
pmt->cbFormat = sizeof(WAVEFORMATEX) + pwfx->cbSize;
|
||||
}
|
||||
pmt->pbFormat = (PBYTE)CoTaskMemAlloc(pmt->cbFormat);
|
||||
if (pmt->pbFormat == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
|
||||
CopyMemory(pmt->pbFormat, pwfx, sizeof(PCMWAVEFORMAT));
|
||||
((WAVEFORMATEX *)pmt->pbFormat)->cbSize = 0;
|
||||
} else {
|
||||
CopyMemory(pmt->pbFormat, pwfx, pmt->cbFormat);
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// eliminate very many spurious warnings from MS compiler
|
||||
#pragma warning(disable:4514)
|
|
@ -0,0 +1,89 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: MtType.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class that holds and manages
|
||||
// media type information.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __MTYPE__
|
||||
#define __MTYPE__
|
||||
|
||||
/* Helper class that derived pin objects can use to compare media
|
||||
types etc. Has same data members as the struct AM_MEDIA_TYPE defined
|
||||
in the streams IDL file, but also has (non-virtual) functions */
|
||||
|
||||
class CMediaType : public _AMMediaType {
|
||||
|
||||
public:
|
||||
|
||||
~CMediaType();
|
||||
CMediaType();
|
||||
CMediaType(const GUID * majortype);
|
||||
CMediaType(const AM_MEDIA_TYPE&, __out_opt HRESULT* phr = NULL);
|
||||
CMediaType(const CMediaType&, __out_opt HRESULT* phr = NULL);
|
||||
|
||||
CMediaType& operator=(const CMediaType&);
|
||||
CMediaType& operator=(const AM_MEDIA_TYPE&);
|
||||
|
||||
BOOL operator == (const CMediaType&) const;
|
||||
BOOL operator != (const CMediaType&) const;
|
||||
|
||||
HRESULT Set(const CMediaType& rt);
|
||||
HRESULT Set(const AM_MEDIA_TYPE& rt);
|
||||
|
||||
BOOL IsValid() const;
|
||||
|
||||
const GUID *Type() const { return &majortype;} ;
|
||||
void SetType(const GUID *);
|
||||
const GUID *Subtype() const { return &subtype;} ;
|
||||
void SetSubtype(const GUID *);
|
||||
|
||||
BOOL IsFixedSize() const {return bFixedSizeSamples; };
|
||||
BOOL IsTemporalCompressed() const {return bTemporalCompression; };
|
||||
ULONG GetSampleSize() const;
|
||||
|
||||
void SetSampleSize(ULONG sz);
|
||||
void SetVariableSize();
|
||||
void SetTemporalCompression(BOOL bCompressed);
|
||||
|
||||
// read/write pointer to format - can't change length without
|
||||
// calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer
|
||||
|
||||
BYTE* Format() const {return pbFormat; };
|
||||
ULONG FormatLength() const { return cbFormat; };
|
||||
|
||||
void SetFormatType(const GUID *);
|
||||
const GUID *FormatType() const {return &formattype; };
|
||||
BOOL SetFormat(__in_bcount(length) BYTE *pFormat, ULONG length);
|
||||
void ResetFormatBuffer();
|
||||
BYTE* AllocFormatBuffer(ULONG length);
|
||||
BYTE* ReallocFormatBuffer(ULONG length);
|
||||
|
||||
void InitMediaType();
|
||||
|
||||
BOOL MatchesPartial(const CMediaType* ppartial) const;
|
||||
BOOL IsPartiallySpecified(void) const;
|
||||
};
|
||||
|
||||
|
||||
/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE
|
||||
structure which is useful when using the IEnumMediaFormats interface as
|
||||
the implementation allocates the structures which you must later delete */
|
||||
|
||||
void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt);
|
||||
AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc);
|
||||
HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);
|
||||
void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt);
|
||||
|
||||
// Initialize a media type from a WAVEFORMATEX
|
||||
|
||||
STDAPI CreateAudioMediaType(
|
||||
const WAVEFORMATEX *pwfx,
|
||||
__out AM_MEDIA_TYPE *pmt,
|
||||
BOOL bSetFormat);
|
||||
|
||||
#endif /* __MTYPE__ */
|
||||
|
|
@ -0,0 +1,801 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: OutputQ.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements COutputQueue class used by an
|
||||
// output pin which may sometimes want to queue output samples on a
|
||||
// separate thread and sometimes call Receive() directly on the input
|
||||
// pin.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
|
||||
|
||||
//
|
||||
// COutputQueue Constructor :
|
||||
//
|
||||
// Determines if a thread is to be created and creates resources
|
||||
//
|
||||
// pInputPin - the downstream input pin we're queueing samples to
|
||||
//
|
||||
// phr - changed to a failure code if this function fails
|
||||
// (otherwise unchanges)
|
||||
//
|
||||
// bAuto - Ask pInputPin if it can block in Receive by calling
|
||||
// its ReceiveCanBlock method and create a thread if
|
||||
// it can block, otherwise not.
|
||||
//
|
||||
// bQueue - if bAuto == FALSE then we create a thread if and only
|
||||
// if bQueue == TRUE
|
||||
//
|
||||
// lBatchSize - work in batches of lBatchSize
|
||||
//
|
||||
// bBatchEact - Use exact batch sizes so don't send until the
|
||||
// batch is full or SendAnyway() is called
|
||||
//
|
||||
// lListSize - If we create a thread make the list of samples queued
|
||||
// to the thread have this size cache
|
||||
//
|
||||
// dwPriority - If we create a thread set its priority to this
|
||||
//
|
||||
COutputQueue::COutputQueue(
|
||||
IPin *pInputPin, // Pin to send stuff to
|
||||
__inout HRESULT *phr, // 'Return code'
|
||||
BOOL bAuto, // Ask pin if queue or not
|
||||
BOOL bQueue, // Send through queue
|
||||
LONG lBatchSize, // Batch
|
||||
BOOL bBatchExact, // Batch exactly to BatchSize
|
||||
LONG lListSize,
|
||||
DWORD dwPriority,
|
||||
bool bFlushingOpt // flushing optimization
|
||||
) : m_lBatchSize(lBatchSize),
|
||||
m_bBatchExact(bBatchExact && (lBatchSize > 1)),
|
||||
m_hThread(NULL),
|
||||
m_hSem(NULL),
|
||||
m_List(NULL),
|
||||
m_pPin(pInputPin),
|
||||
m_ppSamples(NULL),
|
||||
m_lWaiting(0),
|
||||
m_evFlushComplete(FALSE, phr),
|
||||
m_pInputPin(NULL),
|
||||
m_bSendAnyway(FALSE),
|
||||
m_nBatched(0),
|
||||
m_bFlushing(FALSE),
|
||||
m_bFlushed(TRUE),
|
||||
m_bFlushingOpt(bFlushingOpt),
|
||||
m_bTerminate(FALSE),
|
||||
m_hEventPop(NULL),
|
||||
m_hr(S_OK)
|
||||
{
|
||||
ASSERT(m_lBatchSize > 0);
|
||||
|
||||
|
||||
if (FAILED(*phr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the input pin is OK and cache its IMemInputPin interface
|
||||
|
||||
*phr = pInputPin->QueryInterface(IID_IMemInputPin, (void **)&m_pInputPin);
|
||||
if (FAILED(*phr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we should ask the downstream pin
|
||||
|
||||
if (bAuto) {
|
||||
HRESULT hr = m_pInputPin->ReceiveCanBlock();
|
||||
if (SUCCEEDED(hr)) {
|
||||
bQueue = hr == S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Create our sample batch
|
||||
|
||||
m_ppSamples = new PMEDIASAMPLE[m_lBatchSize];
|
||||
if (m_ppSamples == NULL) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're queueing allocate resources
|
||||
|
||||
if (bQueue) {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("Creating thread for output pin")));
|
||||
m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
|
||||
if (m_hSem == NULL) {
|
||||
DWORD dwError = GetLastError();
|
||||
*phr = AmHresultFromWin32(dwError);
|
||||
return;
|
||||
}
|
||||
m_List = new CSampleList(NAME("Sample Queue List"),
|
||||
lListSize,
|
||||
FALSE // No lock
|
||||
);
|
||||
if (m_List == NULL) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DWORD dwThreadId;
|
||||
m_hThread = CreateThread(NULL,
|
||||
0,
|
||||
InitialThreadProc,
|
||||
(LPVOID)this,
|
||||
0,
|
||||
&dwThreadId);
|
||||
if (m_hThread == NULL) {
|
||||
DWORD dwError = GetLastError();
|
||||
*phr = AmHresultFromWin32(dwError);
|
||||
return;
|
||||
}
|
||||
SetThreadPriority(m_hThread, dwPriority);
|
||||
} else {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("Calling input pin directly - no thread")));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// COutputQueuee Destructor :
|
||||
//
|
||||
// Free all resources -
|
||||
//
|
||||
// Thread,
|
||||
// Batched samples
|
||||
//
|
||||
COutputQueue::~COutputQueue()
|
||||
{
|
||||
DbgLog((LOG_TRACE, 3, TEXT("COutputQueue::~COutputQueue")));
|
||||
/* Free our pointer */
|
||||
if (m_pInputPin != NULL) {
|
||||
m_pInputPin->Release();
|
||||
}
|
||||
if (m_hThread != NULL) {
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
m_bTerminate = TRUE;
|
||||
m_hr = S_FALSE;
|
||||
NotifyThread();
|
||||
}
|
||||
DbgWaitForSingleObject(m_hThread);
|
||||
EXECUTE_ASSERT(CloseHandle(m_hThread));
|
||||
|
||||
// The thread frees the samples when asked to terminate
|
||||
|
||||
ASSERT(m_List->GetCount() == 0);
|
||||
delete m_List;
|
||||
} else {
|
||||
FreeSamples();
|
||||
}
|
||||
if (m_hSem != NULL) {
|
||||
EXECUTE_ASSERT(CloseHandle(m_hSem));
|
||||
}
|
||||
delete [] m_ppSamples;
|
||||
}
|
||||
|
||||
//
|
||||
// Call the real thread proc as a member function
|
||||
//
|
||||
DWORD WINAPI COutputQueue::InitialThreadProc(__in LPVOID pv)
|
||||
{
|
||||
HRESULT hrCoInit = CAMThread::CoInitializeHelper();
|
||||
|
||||
COutputQueue *pSampleQueue = (COutputQueue *)pv;
|
||||
DWORD dwReturn = pSampleQueue->ThreadProc();
|
||||
|
||||
if(hrCoInit == S_OK) {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return dwReturn;
|
||||
}
|
||||
|
||||
//
|
||||
// Thread sending the samples downstream :
|
||||
//
|
||||
// When there is nothing to do the thread sets m_lWaiting (while
|
||||
// holding the critical section) and then waits for m_hSem to be
|
||||
// set (not holding the critical section)
|
||||
//
|
||||
DWORD COutputQueue::ThreadProc()
|
||||
{
|
||||
while (TRUE) {
|
||||
BOOL bWait = FALSE;
|
||||
IMediaSample *pSample;
|
||||
LONG lNumberToSend; // Local copy
|
||||
NewSegmentPacket* ppacket;
|
||||
|
||||
//
|
||||
// Get a batch of samples and send it if possible
|
||||
// In any case exit the loop if there is a control action
|
||||
// requested
|
||||
//
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
while (TRUE) {
|
||||
|
||||
if (m_bTerminate) {
|
||||
FreeSamples();
|
||||
return 0;
|
||||
}
|
||||
if (m_bFlushing) {
|
||||
FreeSamples();
|
||||
SetEvent(m_evFlushComplete);
|
||||
}
|
||||
|
||||
// Get a sample off the list
|
||||
|
||||
pSample = m_List->RemoveHead();
|
||||
// inform derived class we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
if (pSample != NULL &&
|
||||
!IsSpecialSample(pSample)) {
|
||||
|
||||
// If its just a regular sample just add it to the batch
|
||||
// and exit the loop if the batch is full
|
||||
|
||||
m_ppSamples[m_nBatched++] = pSample;
|
||||
if (m_nBatched == m_lBatchSize) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
||||
// If there was nothing in the queue and there's nothing
|
||||
// to send (either because there's nothing or the batch
|
||||
// isn't full) then prepare to wait
|
||||
|
||||
if (pSample == NULL &&
|
||||
(m_bBatchExact || m_nBatched == 0)) {
|
||||
|
||||
// Tell other thread to set the event when there's
|
||||
// something do to
|
||||
|
||||
ASSERT(m_lWaiting == 0);
|
||||
m_lWaiting++;
|
||||
bWait = TRUE;
|
||||
} else {
|
||||
|
||||
// We break out of the loop on SEND_PACKET unless
|
||||
// there's nothing to send
|
||||
|
||||
if (pSample == SEND_PACKET && m_nBatched == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pSample == NEW_SEGMENT) {
|
||||
// now we need the parameters - we are
|
||||
// guaranteed that the next packet contains them
|
||||
ppacket = (NewSegmentPacket *) m_List->RemoveHead();
|
||||
// we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
ASSERT(ppacket);
|
||||
}
|
||||
// EOS_PACKET falls through here and we exit the loop
|
||||
// In this way it acts like SEND_PACKET
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!bWait) {
|
||||
// We look at m_nBatched from the client side so keep
|
||||
// it up to date inside the critical section
|
||||
lNumberToSend = m_nBatched; // Local copy
|
||||
m_nBatched = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for some more data
|
||||
|
||||
if (bWait) {
|
||||
DbgWaitForSingleObject(m_hSem);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// OK - send it if there's anything to send
|
||||
// We DON'T check m_bBatchExact here because either we've got
|
||||
// a full batch or we dropped through because we got
|
||||
// SEND_PACKET or EOS_PACKET - both of which imply we should
|
||||
// flush our batch
|
||||
|
||||
if (lNumberToSend != 0) {
|
||||
long nProcessed;
|
||||
if (m_hr == S_OK) {
|
||||
ASSERT(!m_bFlushed);
|
||||
HRESULT hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
|
||||
lNumberToSend,
|
||||
&nProcessed);
|
||||
/* Don't overwrite a flushing state HRESULT */
|
||||
CAutoLock lck(this);
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = hr;
|
||||
}
|
||||
ASSERT(!m_bFlushed);
|
||||
}
|
||||
while (lNumberToSend != 0) {
|
||||
m_ppSamples[--lNumberToSend]->Release();
|
||||
}
|
||||
if (m_hr != S_OK) {
|
||||
|
||||
// In any case wait for more data - S_OK just
|
||||
// means there wasn't an error
|
||||
|
||||
DbgLog((LOG_ERROR, 2, TEXT("ReceiveMultiple returned %8.8X"),
|
||||
m_hr));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for end of stream
|
||||
|
||||
if (pSample == EOS_PACKET) {
|
||||
|
||||
// We don't send even end of stream on if we've previously
|
||||
// returned something other than S_OK
|
||||
// This is because in that case the pin which returned
|
||||
// something other than S_OK should have either sent
|
||||
// EndOfStream() or notified the filter graph
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
|
||||
HRESULT hr = m_pPin->EndOfStream();
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Data from a new source
|
||||
|
||||
if (pSample == RESET_PACKET) {
|
||||
m_hr = S_OK;
|
||||
SetEvent(m_evFlushComplete);
|
||||
}
|
||||
|
||||
if (pSample == NEW_SEGMENT) {
|
||||
m_pPin->NewSegment(ppacket->tStart, ppacket->tStop, ppacket->dRate);
|
||||
delete ppacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send batched stuff anyway
|
||||
void COutputQueue::SendAnyway()
|
||||
{
|
||||
if (!IsQueued()) {
|
||||
|
||||
// m_bSendAnyway is a private parameter checked in ReceiveMultiple
|
||||
|
||||
m_bSendAnyway = TRUE;
|
||||
LONG nProcessed;
|
||||
ReceiveMultiple(NULL, 0, &nProcessed);
|
||||
m_bSendAnyway = FALSE;
|
||||
|
||||
} else {
|
||||
CAutoLock lck(this);
|
||||
QueueSample(SEND_PACKET);
|
||||
NotifyThread();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
COutputQueue::NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate)
|
||||
{
|
||||
if (!IsQueued()) {
|
||||
if (S_OK == m_hr) {
|
||||
if (m_bBatchExact) {
|
||||
SendAnyway();
|
||||
}
|
||||
m_pPin->NewSegment(tStart, tStop, dRate);
|
||||
}
|
||||
} else {
|
||||
if (m_hr == S_OK) {
|
||||
//
|
||||
// we need to queue the new segment to appear in order in the
|
||||
// data, but we need to pass parameters to it. Rather than
|
||||
// take the hit of wrapping every single sample so we can tell
|
||||
// special ones apart, we queue special pointers to indicate
|
||||
// special packets, and we guarantee (by holding the
|
||||
// critical section) that the packet immediately following a
|
||||
// NEW_SEGMENT value is a NewSegmentPacket containing the
|
||||
// parameters.
|
||||
NewSegmentPacket * ppack = new NewSegmentPacket;
|
||||
if (ppack == NULL) {
|
||||
return;
|
||||
}
|
||||
ppack->tStart = tStart;
|
||||
ppack->tStop = tStop;
|
||||
ppack->dRate = dRate;
|
||||
|
||||
CAutoLock lck(this);
|
||||
QueueSample(NEW_SEGMENT);
|
||||
QueueSample( (IMediaSample*) ppack);
|
||||
NotifyThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// End of Stream is queued to output device
|
||||
//
|
||||
void COutputQueue::EOS()
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
if (!IsQueued()) {
|
||||
if (m_bBatchExact) {
|
||||
SendAnyway();
|
||||
}
|
||||
if (m_hr == S_OK) {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
|
||||
m_bFlushed = FALSE;
|
||||
HRESULT hr = m_pPin->EndOfStream();
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_hr == S_OK) {
|
||||
m_bFlushed = FALSE;
|
||||
QueueSample(EOS_PACKET);
|
||||
NotifyThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Flush all the samples in the queue
|
||||
//
|
||||
void COutputQueue::BeginFlush()
|
||||
{
|
||||
if (IsQueued()) {
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
|
||||
// block receives -- we assume this is done by the
|
||||
// filter in which we are a component
|
||||
|
||||
// discard all queued data
|
||||
|
||||
m_bFlushing = TRUE;
|
||||
|
||||
// Make sure we discard all samples from now on
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = S_FALSE;
|
||||
}
|
||||
|
||||
// Optimize so we don't keep calling downstream all the time
|
||||
|
||||
if (m_bFlushed && m_bFlushingOpt) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we really wait for the flush to complete
|
||||
m_evFlushComplete.Reset();
|
||||
|
||||
NotifyThread();
|
||||
}
|
||||
|
||||
// pass this downstream
|
||||
|
||||
m_pPin->BeginFlush();
|
||||
} else {
|
||||
// pass downstream first to avoid deadlocks
|
||||
m_pPin->BeginFlush();
|
||||
CAutoLock lck(this);
|
||||
// discard all queued data
|
||||
|
||||
m_bFlushing = TRUE;
|
||||
|
||||
// Make sure we discard all samples from now on
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// leave flush mode - pass this downstream
|
||||
void COutputQueue::EndFlush()
|
||||
{
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
ASSERT(m_bFlushing);
|
||||
if (m_bFlushingOpt && m_bFlushed && IsQueued()) {
|
||||
m_bFlushing = FALSE;
|
||||
m_hr = S_OK;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// sync with pushing thread -- done in BeginFlush
|
||||
// ensure no more data to go downstream -- done in BeginFlush
|
||||
//
|
||||
// Because we are synching here there is no need to hold the critical
|
||||
// section (in fact we'd deadlock if we did!)
|
||||
|
||||
if (IsQueued()) {
|
||||
m_evFlushComplete.Wait();
|
||||
} else {
|
||||
FreeSamples();
|
||||
}
|
||||
|
||||
// Be daring - the caller has guaranteed no samples will arrive
|
||||
// before EndFlush() returns
|
||||
|
||||
m_bFlushing = FALSE;
|
||||
m_bFlushed = TRUE;
|
||||
|
||||
// call EndFlush on downstream pins
|
||||
|
||||
m_pPin->EndFlush();
|
||||
|
||||
m_hr = S_OK;
|
||||
}
|
||||
|
||||
// COutputQueue::QueueSample
|
||||
//
|
||||
// private method to Send a sample to the output queue
|
||||
// The critical section MUST be held when this is called
|
||||
|
||||
void COutputQueue::QueueSample(IMediaSample *pSample)
|
||||
{
|
||||
if (NULL == m_List->AddTail(pSample)) {
|
||||
if (!IsSpecialSample(pSample)) {
|
||||
pSample->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// COutputQueue::Receive()
|
||||
//
|
||||
// Send a single sample by the multiple sample route
|
||||
// (NOTE - this could be optimized if necessary)
|
||||
//
|
||||
// On return the sample will have been Release()'d
|
||||
//
|
||||
|
||||
HRESULT COutputQueue::Receive(IMediaSample *pSample)
|
||||
{
|
||||
LONG nProcessed;
|
||||
return ReceiveMultiple(&pSample, 1, &nProcessed);
|
||||
}
|
||||
|
||||
//
|
||||
// COutputQueue::ReceiveMultiple()
|
||||
//
|
||||
// Send a set of samples to the downstream pin
|
||||
//
|
||||
// ppSamples - array of samples
|
||||
// nSamples - how many
|
||||
// nSamplesProcessed - How many were processed
|
||||
//
|
||||
// On return all samples will have been Release()'d
|
||||
//
|
||||
|
||||
HRESULT COutputQueue::ReceiveMultiple (
|
||||
__in_ecount(nSamples) IMediaSample **ppSamples,
|
||||
long nSamples,
|
||||
__out long *nSamplesProcessed)
|
||||
{
|
||||
if (nSamples < 0) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
CAutoLock lck(this);
|
||||
// Either call directly or queue up the samples
|
||||
|
||||
if (!IsQueued()) {
|
||||
|
||||
// If we already had a bad return code then just return
|
||||
|
||||
if (S_OK != m_hr) {
|
||||
|
||||
// If we've never received anything since the last Flush()
|
||||
// and the sticky return code is not S_OK we must be
|
||||
// flushing
|
||||
// ((!A || B) is equivalent to A implies B)
|
||||
ASSERT(!m_bFlushed || m_bFlushing);
|
||||
|
||||
// We're supposed to Release() them anyway!
|
||||
*nSamplesProcessed = 0;
|
||||
for (int i = 0; i < nSamples; i++) {
|
||||
DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (direct) : Discarding %d samples code 0x%8.8X"),
|
||||
nSamples, m_hr));
|
||||
ppSamples[i]->Release();
|
||||
}
|
||||
|
||||
return m_hr;
|
||||
}
|
||||
//
|
||||
// If we're flushing the sticky return code should be S_FALSE
|
||||
//
|
||||
ASSERT(!m_bFlushing);
|
||||
m_bFlushed = FALSE;
|
||||
|
||||
ASSERT(m_nBatched < m_lBatchSize);
|
||||
ASSERT(m_nBatched == 0 || m_bBatchExact);
|
||||
|
||||
// Loop processing the samples in batches
|
||||
|
||||
LONG iLost = 0;
|
||||
long iDone = 0;
|
||||
for (iDone = 0;
|
||||
iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway);
|
||||
) {
|
||||
|
||||
//pragma message (REMIND("Implement threshold scheme"))
|
||||
ASSERT(m_nBatched < m_lBatchSize);
|
||||
if (iDone < nSamples) {
|
||||
m_ppSamples[m_nBatched++] = ppSamples[iDone++];
|
||||
}
|
||||
if (m_nBatched == m_lBatchSize ||
|
||||
nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) {
|
||||
LONG nDone;
|
||||
DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"),
|
||||
m_nBatched));
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
|
||||
m_nBatched,
|
||||
&nDone);
|
||||
} else {
|
||||
nDone = 0;
|
||||
}
|
||||
iLost += m_nBatched - nDone;
|
||||
for (LONG i = 0; i < m_nBatched; i++) {
|
||||
m_ppSamples[i]->Release();
|
||||
}
|
||||
m_nBatched = 0;
|
||||
}
|
||||
}
|
||||
*nSamplesProcessed = iDone - iLost;
|
||||
if (*nSamplesProcessed < 0) {
|
||||
*nSamplesProcessed = 0;
|
||||
}
|
||||
return m_hr;
|
||||
} else {
|
||||
/* We're sending to our thread */
|
||||
|
||||
if (m_hr != S_OK) {
|
||||
*nSamplesProcessed = 0;
|
||||
DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"),
|
||||
nSamples, m_hr));
|
||||
for (int i = 0; i < nSamples; i++) {
|
||||
ppSamples[i]->Release();
|
||||
}
|
||||
return m_hr;
|
||||
}
|
||||
m_bFlushed = FALSE;
|
||||
for (long i = 0; i < nSamples; i++) {
|
||||
QueueSample(ppSamples[i]);
|
||||
}
|
||||
*nSamplesProcessed = nSamples;
|
||||
if (!m_bBatchExact ||
|
||||
m_nBatched + m_List->GetCount() >= m_lBatchSize) {
|
||||
NotifyThread();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Get ready for new data - cancels sticky m_hr
|
||||
void COutputQueue::Reset()
|
||||
{
|
||||
if (!IsQueued()) {
|
||||
m_hr = S_OK;
|
||||
} else {
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
QueueSample(RESET_PACKET);
|
||||
NotifyThread();
|
||||
}
|
||||
m_evFlushComplete.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove and Release() all queued and Batched samples
|
||||
void COutputQueue::FreeSamples()
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
if (IsQueued()) {
|
||||
while (TRUE) {
|
||||
IMediaSample *pSample = m_List->RemoveHead();
|
||||
// inform derived class we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
if (pSample == NULL) {
|
||||
break;
|
||||
}
|
||||
if (!IsSpecialSample(pSample)) {
|
||||
pSample->Release();
|
||||
} else {
|
||||
if (pSample == NEW_SEGMENT) {
|
||||
// Free NEW_SEGMENT packet
|
||||
NewSegmentPacket *ppacket =
|
||||
(NewSegmentPacket *) m_List->RemoveHead();
|
||||
// inform derived class we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
ASSERT(ppacket != NULL);
|
||||
delete ppacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < m_nBatched; i++) {
|
||||
m_ppSamples[i]->Release();
|
||||
}
|
||||
m_nBatched = 0;
|
||||
}
|
||||
|
||||
// Notify the thread if there is something to do
|
||||
//
|
||||
// The critical section MUST be held when this is called
|
||||
void COutputQueue::NotifyThread()
|
||||
{
|
||||
// Optimize - no need to signal if it's not waiting
|
||||
ASSERT(IsQueued());
|
||||
if (m_lWaiting) {
|
||||
ReleaseSemaphore(m_hSem, m_lWaiting, NULL);
|
||||
m_lWaiting = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// See if there's any work to do
|
||||
// Returns
|
||||
// TRUE if there is nothing on the queue and nothing in the batch
|
||||
// and all data has been sent
|
||||
// FALSE otherwise
|
||||
//
|
||||
BOOL COutputQueue::IsIdle()
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
|
||||
// We're idle if
|
||||
// there is no thread (!IsQueued()) OR
|
||||
// the thread is waiting for more work (m_lWaiting != 0)
|
||||
// AND
|
||||
// there's nothing in the current batch (m_nBatched == 0)
|
||||
|
||||
if (IsQueued() && m_lWaiting == 0 || m_nBatched != 0) {
|
||||
return FALSE;
|
||||
} else {
|
||||
|
||||
// If we're idle it shouldn't be possible for there
|
||||
// to be anything on the work queue
|
||||
|
||||
ASSERT(!IsQueued() || m_List->GetCount() == 0);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COutputQueue::SetPopEvent(HANDLE hEvent)
|
||||
{
|
||||
m_hEventPop = hEvent;
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: OutputQ.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines the COutputQueue class, which
|
||||
// makes a queue of samples and sends them to an output pin. The
|
||||
// class will optionally send the samples to the pin directly.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
typedef CGenericList<IMediaSample> CSampleList;
|
||||
|
||||
class COutputQueue : public CCritSec
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
COutputQueue(IPin *pInputPin, // Pin to send stuff to
|
||||
__inout HRESULT *phr, // 'Return code'
|
||||
BOOL bAuto = TRUE, // Ask pin if blocks
|
||||
BOOL bQueue = TRUE, // Send through queue (ignored if
|
||||
// bAuto set)
|
||||
LONG lBatchSize = 1, // Batch
|
||||
BOOL bBatchExact = FALSE,// Batch exactly to BatchSize
|
||||
LONG lListSize = // Likely number in the list
|
||||
DEFAULTCACHE,
|
||||
DWORD dwPriority = // Priority of thread to create
|
||||
THREAD_PRIORITY_NORMAL,
|
||||
bool bFlushingOpt = false // flushing optimization
|
||||
);
|
||||
~COutputQueue();
|
||||
|
||||
// enter flush state - discard all data
|
||||
void BeginFlush(); // Begin flushing samples
|
||||
|
||||
// re-enable receives (pass this downstream)
|
||||
void EndFlush(); // Complete flush of samples - downstream
|
||||
// pin guaranteed not to block at this stage
|
||||
|
||||
void EOS(); // Call this on End of stream
|
||||
|
||||
void SendAnyway(); // Send batched samples anyway (if bBatchExact set)
|
||||
|
||||
void NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
// do something with these media samples
|
||||
HRESULT ReceiveMultiple (
|
||||
__in_ecount(nSamples) IMediaSample **pSamples,
|
||||
long nSamples,
|
||||
__out long *nSamplesProcessed);
|
||||
|
||||
void Reset(); // Reset m_hr ready for more data
|
||||
|
||||
// See if its idle or not
|
||||
BOOL IsIdle();
|
||||
|
||||
// give the class an event to fire after everything removed from the queue
|
||||
void SetPopEvent(HANDLE hEvent);
|
||||
|
||||
protected:
|
||||
static DWORD WINAPI InitialThreadProc(__in LPVOID pv);
|
||||
DWORD ThreadProc();
|
||||
BOOL IsQueued()
|
||||
{
|
||||
return m_List != NULL;
|
||||
};
|
||||
|
||||
// The critical section MUST be held when this is called
|
||||
void QueueSample(IMediaSample *pSample);
|
||||
|
||||
BOOL IsSpecialSample(IMediaSample *pSample)
|
||||
{
|
||||
return (DWORD_PTR)pSample > (DWORD_PTR)(LONG_PTR)(-16);
|
||||
};
|
||||
|
||||
// Remove and Release() batched and queued samples
|
||||
void FreeSamples();
|
||||
|
||||
// Notify the thread there is something to do
|
||||
void NotifyThread();
|
||||
|
||||
|
||||
protected:
|
||||
// Queue 'messages'
|
||||
#define SEND_PACKET ((IMediaSample *)(LONG_PTR)(-2)) // Send batch
|
||||
#define EOS_PACKET ((IMediaSample *)(LONG_PTR)(-3)) // End of stream
|
||||
#define RESET_PACKET ((IMediaSample *)(LONG_PTR)(-4)) // Reset m_hr
|
||||
#define NEW_SEGMENT ((IMediaSample *)(LONG_PTR)(-5)) // send NewSegment
|
||||
|
||||
// new segment packet is always followed by one of these
|
||||
struct NewSegmentPacket {
|
||||
REFERENCE_TIME tStart;
|
||||
REFERENCE_TIME tStop;
|
||||
double dRate;
|
||||
};
|
||||
|
||||
// Remember input stuff
|
||||
IPin * const m_pPin;
|
||||
IMemInputPin * m_pInputPin;
|
||||
BOOL const m_bBatchExact;
|
||||
LONG const m_lBatchSize;
|
||||
|
||||
CSampleList * m_List;
|
||||
HANDLE m_hSem;
|
||||
CAMEvent m_evFlushComplete;
|
||||
HANDLE m_hThread;
|
||||
__field_ecount_opt(m_lBatchSize) IMediaSample ** m_ppSamples;
|
||||
__range(0, m_lBatchSize) LONG m_nBatched;
|
||||
|
||||
// Wait optimization
|
||||
LONG m_lWaiting;
|
||||
// Flush synchronization
|
||||
BOOL m_bFlushing;
|
||||
|
||||
// flushing optimization. some downstream filters have trouble
|
||||
// with the queue's flushing optimization. other rely on it
|
||||
BOOL m_bFlushed;
|
||||
bool m_bFlushingOpt;
|
||||
|
||||
// Terminate now
|
||||
BOOL m_bTerminate;
|
||||
|
||||
// Send anyway flag for batching
|
||||
BOOL m_bSendAnyway;
|
||||
|
||||
// Deferred 'return code'
|
||||
HRESULT volatile m_hr;
|
||||
|
||||
// an event that can be fired after every deliver
|
||||
HANDLE m_hEventPop;
|
||||
};
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: perflog.cpp
|
||||
//
|
||||
// Desc: Macros for DirectShow performance logging.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma warning (disable:4201)
|
||||
|
||||
#include <streams.h>
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <winperf.h>
|
||||
#include <wmistr.h>
|
||||
#include <evntrace.h>
|
||||
#include <strsafe.h>
|
||||
#include "perflog.h"
|
||||
|
||||
//
|
||||
// Local function prototypes.
|
||||
//
|
||||
|
||||
ULONG
|
||||
WINAPI
|
||||
PerflogCallback (
|
||||
WMIDPREQUESTCODE RequestCode,
|
||||
__in PVOID Context,
|
||||
__out ULONG* BufferSize,
|
||||
__in PVOID Buffer
|
||||
);
|
||||
|
||||
//
|
||||
// Event tracing function pointers.
|
||||
// We have to do this to run on down-level platforms.
|
||||
//
|
||||
|
||||
#ifdef UNICODE
|
||||
|
||||
ULONG
|
||||
(__stdcall * _RegisterTraceGuids) (
|
||||
__in IN WMIDPREQUEST RequestAddress,
|
||||
__in IN PVOID RequestContext,
|
||||
IN LPCGUID ControlGuid,
|
||||
IN ULONG GuidCount,
|
||||
__in IN PTRACE_GUID_REGISTRATION TraceGuidReg,
|
||||
IN LPCWSTR MofImagePath,
|
||||
IN LPCWSTR MofResourceName,
|
||||
OUT PTRACEHANDLE RegistrationHandle
|
||||
);
|
||||
|
||||
#define REGISTERTRACEGUIDS_NAME "RegisterTraceGuidsW"
|
||||
|
||||
#else
|
||||
|
||||
ULONG
|
||||
(__stdcall * _RegisterTraceGuids) (
|
||||
__in IN WMIDPREQUEST RequestAddress,
|
||||
__in IN PVOID RequestContext,
|
||||
IN LPCGUID ControlGuid,
|
||||
IN ULONG GuidCount,
|
||||
__in IN PTRACE_GUID_REGISTRATION TraceGuidReg,
|
||||
IN LPCSTR MofImagePath,
|
||||
IN LPCSTR MofResourceName,
|
||||
__out OUT PTRACEHANDLE RegistrationHandle
|
||||
);
|
||||
|
||||
#define REGISTERTRACEGUIDS_NAME "RegisterTraceGuidsA"
|
||||
|
||||
#endif
|
||||
|
||||
ULONG
|
||||
(__stdcall * _UnregisterTraceGuids) (
|
||||
TRACEHANDLE RegistrationHandle
|
||||
);
|
||||
|
||||
TRACEHANDLE
|
||||
(__stdcall * _GetTraceLoggerHandle) (
|
||||
__in PVOID Buffer
|
||||
);
|
||||
|
||||
UCHAR
|
||||
(__stdcall * _GetTraceEnableLevel) (
|
||||
TRACEHANDLE TraceHandle
|
||||
);
|
||||
|
||||
ULONG
|
||||
(__stdcall * _GetTraceEnableFlags) (
|
||||
TRACEHANDLE TraceHandle
|
||||
);
|
||||
|
||||
ULONG
|
||||
(__stdcall * _TraceEvent) (
|
||||
TRACEHANDLE TraceHandle,
|
||||
__in PEVENT_TRACE_HEADER EventTrace
|
||||
);
|
||||
|
||||
HINSTANCE _Advapi32;
|
||||
|
||||
//
|
||||
// Global variables.
|
||||
//
|
||||
|
||||
BOOL EventTracingAvailable=FALSE;
|
||||
ULONG PerflogEnableFlags;
|
||||
UCHAR PerflogEnableLevel;
|
||||
ULONG PerflogModuleLevel = 0;
|
||||
void (*OnStateChanged)(void);
|
||||
TRACEHANDLE PerflogTraceHandle=NULL;
|
||||
TRACEHANDLE PerflogRegHandle;
|
||||
|
||||
// The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.
|
||||
// See the documentation for wsprintf()'s lpOut parameter for more information.
|
||||
const INT iDEBUGINFO = 1024; // Used to format strings
|
||||
|
||||
//
|
||||
// This routine initializes performance logging.
|
||||
// It should be called from DllMain().
|
||||
//
|
||||
|
||||
|
||||
VOID
|
||||
PerflogReadModuleLevel(
|
||||
HINSTANCE hInstance
|
||||
)
|
||||
{
|
||||
LONG lReturn; // Create key return value
|
||||
TCHAR szInfo[iDEBUGINFO]; // Constructs key names
|
||||
TCHAR szFullName[iDEBUGINFO]; // Load the full path and module name
|
||||
HKEY hModuleKey; // Module key handle
|
||||
LPTSTR pName; // Searches from the end for a backslash
|
||||
DWORD dwKeySize, dwKeyType, dwKeyValue;
|
||||
|
||||
DWORD dwSize = GetModuleFileName(
|
||||
(hInstance ? hInstance : GetModuleHandle( NULL )),
|
||||
szFullName,
|
||||
iDEBUGINFO );
|
||||
|
||||
if (0 == dwSize || iDEBUGINFO == dwSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
pName = _tcsrchr(szFullName,'\\');
|
||||
if (pName == NULL) {
|
||||
pName = szFullName;
|
||||
} else {
|
||||
pName++;
|
||||
}
|
||||
|
||||
/* Construct the base key name */
|
||||
(void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("SOFTWARE\\Debug\\%s"),pName);
|
||||
|
||||
/* Open the key for this module */
|
||||
lReturn =
|
||||
RegOpenKeyEx(
|
||||
HKEY_LOCAL_MACHINE, // Handle of an open key
|
||||
szInfo, // Address of subkey name
|
||||
(DWORD) 0, // Reserved value
|
||||
KEY_QUERY_VALUE, // Desired security access
|
||||
&hModuleKey ); // Opened handle buffer
|
||||
|
||||
if (lReturn != ERROR_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
dwKeySize = sizeof(DWORD);
|
||||
lReturn = RegQueryValueEx(
|
||||
hModuleKey, // Handle to an open key
|
||||
TEXT("PERFLOG"),
|
||||
NULL, // Reserved field
|
||||
&dwKeyType, // Returns the field type
|
||||
(LPBYTE) &dwKeyValue, // Returns the field's value
|
||||
&dwKeySize ); // Number of bytes transferred
|
||||
|
||||
if ((lReturn == ERROR_SUCCESS) && (dwKeyType == REG_DWORD))
|
||||
{
|
||||
PerflogModuleLevel = dwKeyValue;
|
||||
}
|
||||
|
||||
RegCloseKey(hModuleKey);
|
||||
}
|
||||
|
||||
BOOL PerflogInitIfEnabled(
|
||||
IN HINSTANCE hInstance,
|
||||
__in IN PPERFLOG_LOGGING_PARAMS LogParams
|
||||
)
|
||||
{
|
||||
PerflogReadModuleLevel( hInstance );
|
||||
if (PerflogModuleLevel)
|
||||
{
|
||||
return PerflogInitialize( LogParams );
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
PerflogInitialize (
|
||||
__in IN PPERFLOG_LOGGING_PARAMS LogParams
|
||||
)
|
||||
{
|
||||
ULONG status;
|
||||
|
||||
//
|
||||
// If we're running on a recent-enough platform, this will get
|
||||
// pointers to the event tracing routines.
|
||||
//
|
||||
|
||||
_Advapi32 = GetModuleHandle (_T("ADVAPI32.DLL"));
|
||||
if (_Advapi32 == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*((FARPROC*) &_RegisterTraceGuids) = GetProcAddress (_Advapi32, REGISTERTRACEGUIDS_NAME);
|
||||
*((FARPROC*) &_UnregisterTraceGuids) = GetProcAddress (_Advapi32, "UnregisterTraceGuids");
|
||||
*((FARPROC*) &_GetTraceLoggerHandle) = GetProcAddress (_Advapi32, "GetTraceLoggerHandle");
|
||||
*((FARPROC*) &_GetTraceEnableLevel) = GetProcAddress (_Advapi32, "GetTraceEnableLevel");
|
||||
*((FARPROC*) &_GetTraceEnableFlags) = GetProcAddress (_Advapi32, "GetTraceEnableFlags");
|
||||
*((FARPROC*) &_TraceEvent) = GetProcAddress (_Advapi32, "TraceEvent");
|
||||
|
||||
if (_RegisterTraceGuids == NULL ||
|
||||
_UnregisterTraceGuids == NULL ||
|
||||
_GetTraceEnableLevel == NULL ||
|
||||
_GetTraceEnableFlags == NULL ||
|
||||
_TraceEvent == NULL) {
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
EventTracingAvailable = TRUE;
|
||||
|
||||
OnStateChanged = LogParams->OnStateChanged;
|
||||
|
||||
//
|
||||
// Register our GUIDs.
|
||||
//
|
||||
|
||||
status = _RegisterTraceGuids (PerflogCallback,
|
||||
LogParams,
|
||||
&LogParams->ControlGuid,
|
||||
LogParams->NumberOfTraceGuids,
|
||||
LogParams->TraceGuids,
|
||||
NULL,
|
||||
NULL,
|
||||
&PerflogRegHandle);
|
||||
|
||||
return (status == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
//
|
||||
// This routine shuts down performance logging.
|
||||
//
|
||||
|
||||
VOID
|
||||
PerflogShutdown (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
if (!EventTracingAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
_UnregisterTraceGuids (PerflogRegHandle);
|
||||
PerflogRegHandle = NULL;
|
||||
PerflogTraceHandle = NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Event tracing callback routine.
|
||||
// It's called when controllers call event tracing control functions.
|
||||
//
|
||||
|
||||
ULONG
|
||||
WINAPI
|
||||
PerflogCallback (
|
||||
WMIDPREQUESTCODE RequestCode,
|
||||
__in PVOID Context,
|
||||
__out ULONG* BufferSize,
|
||||
__in PVOID Buffer
|
||||
)
|
||||
{
|
||||
ULONG status;
|
||||
|
||||
UNREFERENCED_PARAMETER (Context);
|
||||
|
||||
ASSERT (EventTracingAvailable);
|
||||
|
||||
status = ERROR_SUCCESS;
|
||||
|
||||
switch (RequestCode) {
|
||||
|
||||
case WMI_ENABLE_EVENTS:
|
||||
PerflogTraceHandle = _GetTraceLoggerHandle (Buffer);
|
||||
PerflogEnableFlags = _GetTraceEnableFlags (PerflogTraceHandle);
|
||||
PerflogEnableLevel = _GetTraceEnableLevel (PerflogTraceHandle);
|
||||
break;
|
||||
|
||||
case WMI_DISABLE_EVENTS:
|
||||
PerflogTraceHandle = NULL;
|
||||
PerflogEnableFlags = 0;
|
||||
PerflogEnableLevel = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
status = ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (OnStateChanged != NULL) {
|
||||
OnStateChanged();
|
||||
}
|
||||
|
||||
*BufferSize = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Logging routine.
|
||||
//
|
||||
|
||||
VOID
|
||||
PerflogTraceEvent (
|
||||
__in PEVENT_TRACE_HEADER Event
|
||||
)
|
||||
{
|
||||
if (!EventTracingAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
_TraceEvent (PerflogTraceHandle, Event);
|
||||
}
|
||||
|
||||
VOID
|
||||
PerflogTraceEventLevel(
|
||||
ULONG Level,
|
||||
__in PEVENT_TRACE_HEADER Event
|
||||
)
|
||||
{
|
||||
if ((!EventTracingAvailable) || (Level <= PerflogModuleLevel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_TraceEvent (PerflogTraceHandle, Event);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: perflog.h
|
||||
//
|
||||
// Desc: Performance logging framework.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef struct _PERFLOG_LOGGING_PARAMS {
|
||||
GUID ControlGuid;
|
||||
void (*OnStateChanged)(void);
|
||||
ULONG NumberOfTraceGuids;
|
||||
TRACE_GUID_REGISTRATION TraceGuids[ANYSIZE_ARRAY];
|
||||
} PERFLOG_LOGGING_PARAMS, *PPERFLOG_LOGGING_PARAMS;
|
||||
|
||||
BOOL
|
||||
PerflogInitIfEnabled(
|
||||
IN HINSTANCE hInstance,
|
||||
__in PPERFLOG_LOGGING_PARAMS LogParams
|
||||
);
|
||||
|
||||
BOOL
|
||||
PerflogInitialize (
|
||||
__in PPERFLOG_LOGGING_PARAMS LogParams
|
||||
);
|
||||
|
||||
VOID
|
||||
PerflogShutdown (
|
||||
VOID
|
||||
);
|
||||
|
||||
VOID
|
||||
PerflogTraceEvent (
|
||||
__in PEVENT_TRACE_HEADER Event
|
||||
);
|
||||
|
||||
extern ULONG PerflogEnableFlags;
|
||||
extern UCHAR PerflogEnableLevel;
|
||||
extern ULONG PerflogModuleLevel;
|
||||
extern TRACEHANDLE PerflogTraceHandle;
|
||||
extern TRACEHANDLE PerflogRegHandle;
|
||||
|
||||
#define PerflogTracingEnabled() (PerflogTraceHandle != 0)
|
||||
|
||||
#define PerflogEvent( _x_ ) PerflogTraceEventLevel _x_
|
||||
|
||||
VOID
|
||||
PerflogTraceEventLevel(
|
||||
ULONG Level,
|
||||
__in PEVENT_TRACE_HEADER Event
|
||||
);
|
||||
|
||||
VOID
|
||||
PerflogTraceEvent (
|
||||
__in PEVENT_TRACE_HEADER Event
|
||||
);
|
|
@ -0,0 +1,194 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: PerfStruct.h
|
||||
//
|
||||
// Desc: Structures for DirectShow performance logging.
|
||||
//
|
||||
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _PERFSTRUCT_H_
|
||||
#define _PERFSTRUCT_H_
|
||||
|
||||
#include <wmistr.h>
|
||||
#include <evntrace.h>
|
||||
|
||||
// {28CF047A-2437-4b24-B653-B9446A419A69}
|
||||
DEFINE_GUID(GUID_DSHOW_CTL,
|
||||
0x28cf047a, 0x2437, 0x4b24, 0xb6, 0x53, 0xb9, 0x44, 0x6a, 0x41, 0x9a, 0x69);
|
||||
|
||||
// {D0DA7AD6-AE80-4de5-AAFC-C126711E7593}
|
||||
DEFINE_GUID(GUID_VIDEOREND,
|
||||
0xd0da7ad6, 0xae80, 0x4de5, 0xaa, 0xfc, 0xc1, 0x26, 0x71, 0x1e, 0x75, 0x93);
|
||||
|
||||
// {DC70AC3E-93E5-48db-88AB-E42064EC276A}
|
||||
DEFINE_GUID(GUID_DSOUNDGLITCH,
|
||||
0xdc70ac3e, 0x93e5, 0x48db, 0x88, 0xab, 0xe4, 0x20, 0x64, 0xec, 0x27, 0x6a);
|
||||
|
||||
// {3d7e7d93-2fc8-4a07-a719-e0922ff2899}
|
||||
DEFINE_GUID(GUID_STREAMTRACE,
|
||||
0x3d7e7d93, 0x2fc8, 0x4a07, 0xa7, 0x19, 0xe0, 0x92, 0x2f, 0xf2, 0x89, 0x9e);
|
||||
|
||||
// AZFIX: the following GUIDs aren't useful right now.
|
||||
|
||||
// {3C33F7F5-EE54-493c-BA25-1656539C05AC}
|
||||
DEFINE_GUID(GUID_GETTIME,
|
||||
0x3c33f7f5, 0xee54, 0x493c, 0xba, 0x25, 0x16, 0x56, 0x53, 0x9c, 0x5, 0xac);
|
||||
|
||||
// {CC44B44D-8169-4952-9E4A-A4E13295E492}
|
||||
DEFINE_GUID(GUID_AUDIOREND,
|
||||
0xcc44b44d, 0x8169, 0x4952, 0x9e, 0x4a, 0xa4, 0xe1, 0x32, 0x95, 0xe4, 0x92);
|
||||
|
||||
// {775D19BF-4D8B-4de6-8DC9-66BAC7B310A2}
|
||||
DEFINE_GUID(GUID_FRAMEDROP,
|
||||
0x775d19bf, 0x4d8b, 0x4de6, 0x8d, 0xc9, 0x66, 0xba, 0xc7, 0xb3, 0x10, 0xa2);
|
||||
|
||||
// {56D29065-EFBE-42dc-8C29-E325DC9C27D5}
|
||||
DEFINE_GUID(GUID_AUDIOBREAK,
|
||||
0x56d29065, 0xefbe, 0x42dc, 0x8c, 0x29, 0xe3, 0x25, 0xdc, 0x9c, 0x27, 0xd5);
|
||||
|
||||
// {E1E6EA87-95A8-497e-BFBA-0295AEBCC707}
|
||||
DEFINE_GUID(GUID_AUDIORECV,
|
||||
0xe1e6ea87, 0x95a8, 0x497e, 0xbf, 0xba, 0x2, 0x95, 0xae, 0xbc, 0xc7, 0x7);
|
||||
|
||||
// {10F7768A-B1E7-4242-AD90-A2D44683D9F0}
|
||||
DEFINE_GUID(GUID_AUDIOSLAVE,
|
||||
0x10f7768a, 0xb1e7, 0x4242, 0xad, 0x90, 0xa2, 0xd4, 0x46, 0x83, 0xd9, 0xf0);
|
||||
|
||||
// {8983803D-691A-49bc-8FF6-962A39C0198F}
|
||||
DEFINE_GUID(GUID_AUDIOADDBREAK,
|
||||
0x8983803d, 0x691a, 0x49bc, 0x8f, 0xf6, 0x96, 0x2a, 0x39, 0xc0, 0x19, 0x8f);
|
||||
|
||||
#define GLITCHTYPE_DSOUNDFIRSTGOOD 0
|
||||
#define GLITCHTYPE_DSOUNDFIRSTBAD 1
|
||||
|
||||
typedef struct PERFINFO_DSHOW_AUDIOGLITCH {
|
||||
ULONGLONG cycleCounter;
|
||||
DWORD glitchType;
|
||||
LONGLONG sampleTime;
|
||||
LONGLONG previousTime;
|
||||
ULONG_PTR instanceId;
|
||||
} PERFINFO_DSHOW_AUDIOGLITCH, *PPERFINFO_DSHOW_AUDIOGLITCH;
|
||||
|
||||
typedef struct PERFINFO_WMI_AUDIOGLITCH {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_AUDIOGLITCH data;
|
||||
} PERFINFO_WMI_AUDIO_GLITCH, *PPERFINFO_WMI_AUDIOGLITCH;
|
||||
|
||||
typedef struct PERFINFO_DSHOW_GETTIME {
|
||||
ULONGLONG cycleCounter;
|
||||
ULONGLONG dshowClock;
|
||||
} PERFINFO_DSHOW_GETTIME, *PPERFINFO_DSHOW_GETTIME;
|
||||
|
||||
typedef struct PERFINFO_WMI_GETTIME {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_GETTIME data;
|
||||
} PERFINFO_WMI_GETTIME, *PPERFINFO_WMI_GETTIME;
|
||||
|
||||
typedef struct PERFINFO_DSHOW_AVREND {
|
||||
ULONGLONG cycleCounter;
|
||||
ULONGLONG dshowClock;
|
||||
ULONGLONG sampleTime;
|
||||
} PERFINFO_DSHOW_AVREND, *PPERFINFO_DSHOW_AVREND;
|
||||
|
||||
typedef struct PERFINFO_WMI_AVREND {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_AVREND data;
|
||||
} PERFINFO_WMI_AVREND, *PPERFINFO_WMI_AVREND;
|
||||
|
||||
typedef struct PERFINFO_DSHOW_AUDIOBREAK {
|
||||
ULONGLONG cycleCounter;
|
||||
ULONGLONG dshowClock;
|
||||
ULONGLONG sampleTime;
|
||||
ULONGLONG sampleDuration;
|
||||
} PERFINFO_DSHOW_AUDIOBREAK, *PPERFINFO_DSHOW_AUDIOBREAK;
|
||||
|
||||
typedef struct PERFINFO_WMI_AUDIOBREAK {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_AUDIOBREAK data;
|
||||
} PERFINFO_WMI_AUDIOBREAK, *PPERFINFO_WMI_AUDIOBREAK;
|
||||
|
||||
typedef struct PERFINFO_DSHOW_FRAMEDROP {
|
||||
ULONGLONG cycleCounter;
|
||||
ULONGLONG dshowClock;
|
||||
ULONGLONG frameTime;
|
||||
} PERFINFO_DSHOW_FRAMEDROP, *PPERFINFO_DSHOW_FRAMEDROP;
|
||||
|
||||
typedef struct PERFINFO_WMI_FRAMEDROP {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_FRAMEDROP data;
|
||||
} PERFINFO_WMI_FRAMEDROP, *PPERFINFO_WMI_FRAMEDROP;
|
||||
|
||||
#define PERFINFO_STREAMTRACE_MPEG2DEMUX_PTS_TRANSLATION 1
|
||||
#define PERFINFO_STREAMTRACE_MPEG2DEMUX_SAMPLE_RECEIVED 2
|
||||
#define PERFINFO_STREAMTRACE_VMR_BEGIN_ADVISE 3
|
||||
#define PERFINFO_STREAMTRACE_VMR_END_ADVISE 4
|
||||
#define PERFINFO_STREAMTRACE_VMR_RECEIVE 5
|
||||
#define PERFINFO_STREAMTRACE_VMR_BEGIN_DEINTERLACE 6
|
||||
#define PERFINFO_STREAMTRACE_VMR_END_DEINTERLACE 7
|
||||
#define PERFINFO_STREAMTRACE_VMR_BEGIN_DECODE 8
|
||||
#define PERFINFO_STREAMTRACE_VMR_END_DECODE 9
|
||||
#define PERFINFO_STREAMTRACE_VMR_DROPPED_FRAME 10
|
||||
#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTERINPUT 11
|
||||
#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTEROUTPUT 12
|
||||
#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTERINPUT 13
|
||||
#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTEROUTPUT 14
|
||||
#define PERFINFO_STREAMTRACE_ENCDEC_XDSCODECINPUT 15
|
||||
#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_RECEIVE 16
|
||||
#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_DELIVER 17
|
||||
#define PERFINFO_STREAMTRACE_SBE_DVRINPUTPIN_RECEIVE 18
|
||||
#define PERFINFO_STREAMTRACE_SBE_DVROUTPUTPIN_RECEIVE 19
|
||||
#define PERFINFO_STREAMTRACE_VMR_RENDER_TIME 20
|
||||
|
||||
typedef struct _PERFINFO_DSHOW_STREAMTRACE {
|
||||
ULONG id;
|
||||
ULONG reserved;
|
||||
ULONGLONG dshowClock;
|
||||
ULONGLONG data[ 4 ];
|
||||
} PERFINFO_DSHOW_STREAMTRACE, *PPERFINFO_DSHOW_STREAMTRACE;
|
||||
|
||||
typedef struct _PERFINFO_WMI_STREAMTRACE {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_STREAMTRACE data;
|
||||
} PERFINFO_WMI_STREAMTRACE, *PPERFINFO_WMI_STREAMTRACE;
|
||||
|
||||
|
||||
typedef struct PERFINFO_DSHOW_AUDIORECV {
|
||||
LONGLONG streamTime ;
|
||||
LONGLONG sampleStart ;
|
||||
LONGLONG sampleStop ;
|
||||
LONGLONG hwduration ;
|
||||
BOOL discontinuity ;
|
||||
} PERFINFO_DSHOW_AUDIORECV, *PPERFINFO_DSHOW_AUDIORECV;
|
||||
|
||||
typedef struct PERFINFO_WMI_AUDIORECV {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_AUDIORECV data;
|
||||
} PERFINFO_WMI_AUDIORECV, *PPERFINFO_WMI_AUDIORECV;
|
||||
|
||||
typedef struct PERFINFO_DSHOW_AUDIOSLAVE {
|
||||
LONGLONG masterClock ;
|
||||
LONGLONG slaveClock ;
|
||||
LONGLONG errorAccum ;
|
||||
LONGLONG lastHighErrorSeen ;
|
||||
LONGLONG lastLowErrorSeen ;
|
||||
} PERFINFO_DSHOW_AUDIOSLAVE, *PPERFINFO_DSHOW_AUDIOSLAVE;
|
||||
|
||||
typedef struct PERFINFO_WMI_AUDIOSLAVE {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_AUDIOSLAVE data;
|
||||
} PERFINFO_WMI_AUDIOSLAVE, *PPERFINFO_WMI_AUDIOSLAVE;
|
||||
|
||||
typedef struct PERFINFO_DSHOW_AUDIOADDBREAK {
|
||||
DWORD iterNextWrite ;
|
||||
DWORD offsetNextWrite ;
|
||||
DWORD iterWrite ;
|
||||
DWORD offsetWrite ;
|
||||
} PERFINFO_DSHOW_AUDIOADDBREAK, *PPERFINFO_DSHOW_AUDIOADDBREAK;
|
||||
|
||||
typedef struct PERFINFO_WMI_AUDIOADDBREAK {
|
||||
EVENT_TRACE_HEADER header;
|
||||
PERFINFO_DSHOW_AUDIOADDBREAK data;
|
||||
} PERFINFO_WMI_AUDIOADDBREAK, *PPERFINFO_WMI_AUDIOADDBREAK;
|
||||
|
||||
#endif // _PREFSTRUCT_H_
|
|
@ -0,0 +1,197 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: PStream.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#ifdef PERF
|
||||
#include <measure.h>
|
||||
#endif
|
||||
// #include "pstream.h" in streams.h
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
CPersistStream::CPersistStream(IUnknown *punk, __inout HRESULT *phr)
|
||||
: mPS_fDirty(FALSE)
|
||||
{
|
||||
mPS_dwFileVersion = GetSoftwareVersion();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
CPersistStream::~CPersistStream() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
#if 0
|
||||
SAMPLE CODE TO COPY - not active at the moment
|
||||
|
||||
//
|
||||
// NonDelegatingQueryInterface
|
||||
//
|
||||
// This object supports IPersist & IPersistStream
|
||||
STDMETHODIMP CPersistStream::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
|
||||
{
|
||||
if (riid == IID_IPersist) {
|
||||
return GetInterface((IPersist *) this, ppv); // ???
|
||||
}
|
||||
else if (riid == IID_IPersistStream) {
|
||||
return GetInterface((IPersistStream *) this, ppv);
|
||||
}
|
||||
else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// WriteToStream
|
||||
//
|
||||
// Writes to the stream (default action is to write nothing)
|
||||
HRESULT CPersistStream::WriteToStream(IStream *pStream)
|
||||
{
|
||||
// You can override this to do things like
|
||||
// hr = pStream->Write(MyStructure, sizeof(MyStructure), NULL);
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
HRESULT CPersistStream::ReadFromStream(IStream * pStream)
|
||||
{
|
||||
// You can override this to do things like
|
||||
// hr = pStream->Read(MyStructure, sizeof(MyStructure), NULL);
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Load
|
||||
//
|
||||
// Load all the data from the given stream
|
||||
STDMETHODIMP CPersistStream::Load(LPSTREAM pStm)
|
||||
{
|
||||
HRESULT hr;
|
||||
// Load the version number then the data
|
||||
mPS_dwFileVersion = ReadInt(pStm, hr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return ReadFromStream(pStm);
|
||||
} // Load
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Save
|
||||
//
|
||||
// Save the contents of this Stream.
|
||||
STDMETHODIMP CPersistStream::Save(LPSTREAM pStm, BOOL fClearDirty)
|
||||
{
|
||||
|
||||
HRESULT hr = WriteInt(pStm, GetSoftwareVersion());
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = WriteToStream(pStm);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
mPS_fDirty = !fClearDirty;
|
||||
|
||||
return hr;
|
||||
} // Save
|
||||
|
||||
|
||||
// WriteInt
|
||||
//
|
||||
// Writes an integer to an IStream as 11 UNICODE characters followed by one space.
|
||||
// You could use this for shorts or unsigneds or anything (up to 32 bits)
|
||||
// where the value isn't actually truncated by squeezing it into 32 bits.
|
||||
// Values such as (unsigned) 0x80000000 would come out as -2147483648
|
||||
// but would then load as 0x80000000 through ReadInt. Cast as you please.
|
||||
|
||||
STDAPI WriteInt(IStream *pIStream, int n)
|
||||
{
|
||||
WCHAR Buff[13]; // Allows for trailing null that we don't write
|
||||
(void)StringCchPrintfW(Buff, NUMELMS(Buff),L"%011d ",n);
|
||||
return pIStream->Write(&(Buff[0]), 12*sizeof(WCHAR), NULL);
|
||||
} // WriteInt
|
||||
|
||||
|
||||
// ReadInt
|
||||
//
|
||||
// Reads an integer from an IStream.
|
||||
// Read as 4 bytes. You could use this for shorts or unsigneds or anything
|
||||
// where the value isn't actually truncated by squeezing it into 32 bits
|
||||
// Striped down subset of what sscanf can do (without dragging in the C runtime)
|
||||
|
||||
STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr)
|
||||
{
|
||||
|
||||
int Sign = 1;
|
||||
unsigned int n = 0; // result wil be n*Sign
|
||||
WCHAR wch;
|
||||
|
||||
hr = pIStream->Read( &wch, sizeof(wch), NULL);
|
||||
if (FAILED(hr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wch==L'-'){
|
||||
Sign = -1;
|
||||
hr = pIStream->Read( &wch, sizeof(wch), NULL);
|
||||
if (FAILED(hr)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for( ; ; ) {
|
||||
if (wch>=L'0' && wch<=L'9') {
|
||||
n = 10*n+(int)(wch-L'0');
|
||||
} else if ( wch == L' '
|
||||
|| wch == L'\t'
|
||||
|| wch == L'\r'
|
||||
|| wch == L'\n'
|
||||
|| wch == L'\0'
|
||||
) {
|
||||
break;
|
||||
} else {
|
||||
hr = VFW_E_INVALID_FILE_FORMAT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
hr = pIStream->Read( &wch, sizeof(wch), NULL);
|
||||
if (FAILED(hr)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (n==0x80000000 && Sign==-1) {
|
||||
// This is the negative number that has no positive version!
|
||||
return (int)n;
|
||||
}
|
||||
else return (int)n * Sign;
|
||||
} // ReadInt
|
||||
|
||||
|
||||
// The microsoft C/C++ compile generates level 4 warnings to the effect that
|
||||
// a particular inline function (from some base class) was not needed.
|
||||
// This line gets rid of hundreds of such unwanted messages and makes
|
||||
// -W4 compilation feasible:
|
||||
#pragma warning(disable: 4514)
|
|
@ -0,0 +1,114 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: PStream.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class for persistent properties
|
||||
// of filters.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __PSTREAM__
|
||||
#define __PSTREAM__
|
||||
|
||||
// Base class for persistent properties of filters
|
||||
// (i.e. filter properties in saved graphs)
|
||||
|
||||
// The simplest way to use this is:
|
||||
// 1. Arrange for your filter to inherit this class
|
||||
// 2. Implement in your class WriteToStream and ReadFromStream
|
||||
// These will override the "do nothing" functions here.
|
||||
// 3. Change your NonDelegatingQueryInterface to handle IPersistStream
|
||||
// 4. Implement SizeMax to return the number of bytes of data you save.
|
||||
// If you save UNICODE data, don't forget a char is 2 bytes.
|
||||
// 5. Whenever your data changes, call SetDirty()
|
||||
//
|
||||
// At some point you may decide to alter, or extend the format of your data.
|
||||
// At that point you will wish that you had a version number in all the old
|
||||
// saved graphs, so that you can tell, when you read them, whether they
|
||||
// represent the old or new form. To assist you in this, this class
|
||||
// writes and reads a version number.
|
||||
// When it writes, it calls GetSoftwareVersion() to enquire what version
|
||||
// of the software we have at the moment. (In effect this is a version number
|
||||
// of the data layout in the file). It writes this as the first thing in the data.
|
||||
// If you want to change the version, implement (override) GetSoftwareVersion().
|
||||
// It reads this from the file into mPS_dwFileVersion before calling ReadFromStream,
|
||||
// so in ReadFromStream you can check mPS_dwFileVersion to see if you are reading
|
||||
// an old version file.
|
||||
// Normally you should accept files whose version is no newer than the software
|
||||
// version that's reading them.
|
||||
|
||||
|
||||
// CPersistStream
|
||||
//
|
||||
// Implements IPersistStream.
|
||||
// See 'OLE Programmers Reference (Vol 1):Structured Storage Overview' for
|
||||
// more implementation information.
|
||||
class CPersistStream : public IPersistStream {
|
||||
private:
|
||||
|
||||
// Internal state:
|
||||
|
||||
protected:
|
||||
DWORD mPS_dwFileVersion; // version number of file (being read)
|
||||
BOOL mPS_fDirty;
|
||||
|
||||
public:
|
||||
|
||||
// IPersistStream methods
|
||||
|
||||
STDMETHODIMP IsDirty()
|
||||
{return (mPS_fDirty ? S_OK : S_FALSE);} // note FALSE means clean
|
||||
STDMETHODIMP Load(LPSTREAM pStm);
|
||||
STDMETHODIMP Save(LPSTREAM pStm, BOOL fClearDirty);
|
||||
STDMETHODIMP GetSizeMax(__out ULARGE_INTEGER * pcbSize)
|
||||
// Allow 24 bytes for version.
|
||||
{ pcbSize->QuadPart = 12*sizeof(WCHAR)+SizeMax(); return NOERROR; }
|
||||
|
||||
// implementation
|
||||
|
||||
CPersistStream(IUnknown *punk, __inout HRESULT *phr);
|
||||
~CPersistStream();
|
||||
|
||||
HRESULT SetDirty(BOOL fDirty)
|
||||
{ mPS_fDirty = fDirty; return NOERROR;}
|
||||
|
||||
|
||||
// override to reveal IPersist & IPersistStream
|
||||
// STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
// --- IPersist ---
|
||||
|
||||
// You must override this to provide your own class id
|
||||
STDMETHODIMP GetClassID(__out CLSID *pClsid) PURE;
|
||||
|
||||
// overrideable if you want
|
||||
// file version number. Override it if you ever change format
|
||||
virtual DWORD GetSoftwareVersion(void) { return 0; }
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// OVERRIDE THESE to read and write your data
|
||||
// OVERRIDE THESE to read and write your data
|
||||
// OVERRIDE THESE to read and write your data
|
||||
|
||||
virtual int SizeMax() {return 0;}
|
||||
virtual HRESULT WriteToStream(IStream *pStream);
|
||||
virtual HRESULT ReadFromStream(IStream *pStream);
|
||||
//=========================================================================
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
// --- Useful helpers ---
|
||||
|
||||
|
||||
// Writes an int to an IStream as UNICODE.
|
||||
STDAPI WriteInt(IStream *pIStream, int n);
|
||||
|
||||
// inverse of WriteInt
|
||||
STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr);
|
||||
|
||||
#endif // __PSTREAM__
|
|
@ -0,0 +1,588 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: PullPin.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements CPullPin class that pulls data
|
||||
// from IAsyncReader.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include "pullpin.h"
|
||||
|
||||
#ifdef DXMPERF
|
||||
#include "dxmperf.h"
|
||||
#endif // DXMPERF
|
||||
|
||||
|
||||
CPullPin::CPullPin()
|
||||
: m_pReader(NULL),
|
||||
m_pAlloc(NULL),
|
||||
m_State(TM_Exit)
|
||||
{
|
||||
#ifdef DXMPERF
|
||||
PERFLOG_CTOR( L"CPullPin", this );
|
||||
#endif // DXMPERF
|
||||
|
||||
}
|
||||
|
||||
CPullPin::~CPullPin()
|
||||
{
|
||||
Disconnect();
|
||||
|
||||
#ifdef DXMPERF
|
||||
PERFLOG_DTOR( L"CPullPin", this );
|
||||
#endif // DXMPERF
|
||||
|
||||
}
|
||||
|
||||
// returns S_OK if successfully connected to an IAsyncReader interface
|
||||
// from this object
|
||||
// Optional allocator should be proposed as a preferred allocator if
|
||||
// necessary
|
||||
HRESULT
|
||||
CPullPin::Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync)
|
||||
{
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (m_pReader) {
|
||||
return VFW_E_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
HRESULT hr = pUnk->QueryInterface(IID_IAsyncReader, (void**)&m_pReader);
|
||||
if (FAILED(hr)) {
|
||||
|
||||
#ifdef DXMPERF
|
||||
{
|
||||
AM_MEDIA_TYPE * pmt = NULL;
|
||||
PERFLOG_CONNECT( this, pUnk, hr, pmt );
|
||||
}
|
||||
#endif // DXMPERF
|
||||
|
||||
return(hr);
|
||||
}
|
||||
|
||||
hr = DecideAllocator(pAlloc, NULL);
|
||||
if (FAILED(hr)) {
|
||||
Disconnect();
|
||||
|
||||
#ifdef DXMPERF
|
||||
{
|
||||
AM_MEDIA_TYPE * pmt = NULL;
|
||||
PERFLOG_CONNECT( this, pUnk, hr, pmt );
|
||||
}
|
||||
#endif // DXMPERF
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
LONGLONG llTotal, llAvail;
|
||||
hr = m_pReader->Length(&llTotal, &llAvail);
|
||||
if (FAILED(hr)) {
|
||||
Disconnect();
|
||||
|
||||
#ifdef DXMPERF
|
||||
{
|
||||
AM_MEDIA_TYPE * pmt = NULL;
|
||||
PERFLOG_CONNECT( this, pUnk, hr, pmt );
|
||||
}
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
// convert from file position to reference time
|
||||
m_tDuration = llTotal * UNITS;
|
||||
m_tStop = m_tDuration;
|
||||
m_tStart = 0;
|
||||
|
||||
m_bSync = bSync;
|
||||
|
||||
#ifdef DXMPERF
|
||||
{
|
||||
AM_MEDIA_TYPE * pmt = NULL;
|
||||
PERFLOG_CONNECT( this, pUnk, S_OK, pmt );
|
||||
}
|
||||
#endif // DXMPERF
|
||||
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// disconnect any connection made in Connect
|
||||
HRESULT
|
||||
CPullPin::Disconnect()
|
||||
{
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
StopThread();
|
||||
|
||||
|
||||
#ifdef DXMPERF
|
||||
PERFLOG_DISCONNECT( this, m_pReader, S_OK );
|
||||
#endif // DXMPERF
|
||||
|
||||
|
||||
if (m_pReader) {
|
||||
m_pReader->Release();
|
||||
m_pReader = NULL;
|
||||
}
|
||||
|
||||
if (m_pAlloc) {
|
||||
m_pAlloc->Release();
|
||||
m_pAlloc = NULL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// agree an allocator using RequestAllocator - optional
|
||||
// props param specifies your requirements (non-zero fields).
|
||||
// returns an error code if fail to match requirements.
|
||||
// optional IMemAllocator interface is offered as a preferred allocator
|
||||
// but no error occurs if it can't be met.
|
||||
HRESULT
|
||||
CPullPin::DecideAllocator(
|
||||
IMemAllocator * pAlloc,
|
||||
__inout_opt ALLOCATOR_PROPERTIES * pProps)
|
||||
{
|
||||
ALLOCATOR_PROPERTIES *pRequest;
|
||||
ALLOCATOR_PROPERTIES Request;
|
||||
if (pProps == NULL) {
|
||||
Request.cBuffers = 3;
|
||||
Request.cbBuffer = 64*1024;
|
||||
Request.cbAlign = 0;
|
||||
Request.cbPrefix = 0;
|
||||
pRequest = &Request;
|
||||
} else {
|
||||
pRequest = pProps;
|
||||
}
|
||||
HRESULT hr = m_pReader->RequestAllocator(
|
||||
pAlloc,
|
||||
pRequest,
|
||||
&m_pAlloc);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// start pulling data
|
||||
HRESULT
|
||||
CPullPin::Active(void)
|
||||
{
|
||||
ASSERT(!ThreadExists());
|
||||
return StartThread();
|
||||
}
|
||||
|
||||
// stop pulling data
|
||||
HRESULT
|
||||
CPullPin::Inactive(void)
|
||||
{
|
||||
StopThread();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop)
|
||||
{
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
ThreadMsg AtStart = m_State;
|
||||
|
||||
if (AtStart == TM_Start) {
|
||||
BeginFlush();
|
||||
PauseThread();
|
||||
EndFlush();
|
||||
}
|
||||
|
||||
m_tStart = tStart;
|
||||
m_tStop = tStop;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
if (AtStart == TM_Start) {
|
||||
hr = StartThread();
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::Duration(__out REFERENCE_TIME* ptDuration)
|
||||
{
|
||||
*ptDuration = m_tDuration;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
CPullPin::StartThread()
|
||||
{
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (!m_pAlloc || !m_pReader) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
if (!ThreadExists()) {
|
||||
|
||||
// commit allocator
|
||||
hr = m_pAlloc->Commit();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// start thread
|
||||
if (!Create()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
m_State = TM_Start;
|
||||
hr = (HRESULT) CallWorker(m_State);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::PauseThread()
|
||||
{
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (!ThreadExists()) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// need to flush to ensure the thread is not blocked
|
||||
// in WaitForNext
|
||||
HRESULT hr = m_pReader->BeginFlush();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
m_State = TM_Pause;
|
||||
hr = CallWorker(TM_Pause);
|
||||
|
||||
m_pReader->EndFlush();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::StopThread()
|
||||
{
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (!ThreadExists()) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
// need to flush to ensure the thread is not blocked
|
||||
// in WaitForNext
|
||||
HRESULT hr = m_pReader->BeginFlush();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
m_State = TM_Exit;
|
||||
hr = CallWorker(TM_Exit);
|
||||
|
||||
m_pReader->EndFlush();
|
||||
|
||||
// wait for thread to completely exit
|
||||
Close();
|
||||
|
||||
// decommit allocator
|
||||
if (m_pAlloc) {
|
||||
m_pAlloc->Decommit();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
DWORD
|
||||
CPullPin::ThreadProc(void)
|
||||
{
|
||||
while(1) {
|
||||
DWORD cmd = GetRequest();
|
||||
switch(cmd) {
|
||||
case TM_Exit:
|
||||
Reply(S_OK);
|
||||
return 0;
|
||||
|
||||
case TM_Pause:
|
||||
// we are paused already
|
||||
Reply(S_OK);
|
||||
break;
|
||||
|
||||
case TM_Start:
|
||||
Reply(S_OK);
|
||||
Process();
|
||||
break;
|
||||
}
|
||||
|
||||
// at this point, there should be no outstanding requests on the
|
||||
// upstream filter.
|
||||
// We should force begin/endflush to ensure that this is true.
|
||||
// !!!Note that we may currently be inside a BeginFlush/EndFlush pair
|
||||
// on another thread, but the premature EndFlush will do no harm now
|
||||
// that we are idle.
|
||||
m_pReader->BeginFlush();
|
||||
CleanupCancelled();
|
||||
m_pReader->EndFlush();
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::QueueSample(
|
||||
__inout REFERENCE_TIME& tCurrent,
|
||||
REFERENCE_TIME tAlignStop,
|
||||
BOOL bDiscontinuity
|
||||
)
|
||||
{
|
||||
IMediaSample* pSample;
|
||||
|
||||
HRESULT hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
|
||||
if (tStopThis > tAlignStop) {
|
||||
tStopThis = tAlignStop;
|
||||
}
|
||||
pSample->SetTime(&tCurrent, &tStopThis);
|
||||
tCurrent = tStopThis;
|
||||
|
||||
pSample->SetDiscontinuity(bDiscontinuity);
|
||||
|
||||
hr = m_pReader->Request(
|
||||
pSample,
|
||||
0);
|
||||
if (FAILED(hr)) {
|
||||
pSample->Release();
|
||||
|
||||
CleanupCancelled();
|
||||
OnError(hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::CollectAndDeliver(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop)
|
||||
{
|
||||
IMediaSample* pSample = NULL; // better be sure pSample is set
|
||||
DWORD_PTR dwUnused;
|
||||
HRESULT hr = m_pReader->WaitForNext(
|
||||
INFINITE,
|
||||
&pSample,
|
||||
&dwUnused);
|
||||
if (FAILED(hr)) {
|
||||
if (pSample) {
|
||||
pSample->Release();
|
||||
}
|
||||
} else {
|
||||
hr = DeliverSample(pSample, tStart, tStop);
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
CleanupCancelled();
|
||||
OnError(hr);
|
||||
}
|
||||
return hr;
|
||||
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::DeliverSample(
|
||||
IMediaSample* pSample,
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop
|
||||
)
|
||||
{
|
||||
// fix up sample if past actual stop (for sector alignment)
|
||||
REFERENCE_TIME t1, t2;
|
||||
if (S_OK == pSample->GetTime(&t1, &t2)) {
|
||||
if (t2 > tStop) {
|
||||
t2 = tStop;
|
||||
}
|
||||
|
||||
// adjust times to be relative to (aligned) start time
|
||||
t1 -= tStart;
|
||||
t2 -= tStart;
|
||||
HRESULT hr = pSample->SetTime(&t1, &t2);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DXMPERF
|
||||
{
|
||||
AM_MEDIA_TYPE * pmt = NULL;
|
||||
pSample->GetMediaType( &pmt );
|
||||
PERFLOG_RECEIVE( L"CPullPin", m_pReader, this, pSample, pmt );
|
||||
}
|
||||
#endif
|
||||
|
||||
HRESULT hr = Receive(pSample);
|
||||
pSample->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
void
|
||||
CPullPin::Process(void)
|
||||
{
|
||||
// is there anything to do?
|
||||
if (m_tStop <= m_tStart) {
|
||||
EndOfStream();
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL bDiscontinuity = TRUE;
|
||||
|
||||
// if there is more than one sample at the allocator,
|
||||
// then try to queue 2 at once in order to overlap.
|
||||
// -- get buffer count and required alignment
|
||||
ALLOCATOR_PROPERTIES Actual;
|
||||
HRESULT hr = m_pAlloc->GetProperties(&Actual);
|
||||
|
||||
// align the start position downwards
|
||||
REFERENCE_TIME tStart = AlignDown(m_tStart / UNITS, Actual.cbAlign) * UNITS;
|
||||
REFERENCE_TIME tCurrent = tStart;
|
||||
|
||||
REFERENCE_TIME tStop = m_tStop;
|
||||
if (tStop > m_tDuration) {
|
||||
tStop = m_tDuration;
|
||||
}
|
||||
|
||||
// align the stop position - may be past stop, but that
|
||||
// doesn't matter
|
||||
REFERENCE_TIME tAlignStop = AlignUp(tStop / UNITS, Actual.cbAlign) * UNITS;
|
||||
|
||||
|
||||
DWORD dwRequest;
|
||||
|
||||
if (!m_bSync) {
|
||||
|
||||
// Break out of the loop either if we get to the end or we're asked
|
||||
// to do something else
|
||||
while (tCurrent < tAlignStop) {
|
||||
|
||||
// Break out without calling EndOfStream if we're asked to
|
||||
// do something different
|
||||
if (CheckRequest(&dwRequest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// queue a first sample
|
||||
if (Actual.cBuffers > 1) {
|
||||
|
||||
hr = QueueSample(tCurrent, tAlignStop, TRUE);
|
||||
bDiscontinuity = FALSE;
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// loop queueing second and waiting for first..
|
||||
while (tCurrent < tAlignStop) {
|
||||
|
||||
hr = QueueSample(tCurrent, tAlignStop, bDiscontinuity);
|
||||
bDiscontinuity = FALSE;
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = CollectAndDeliver(tStart, tStop);
|
||||
if (S_OK != hr) {
|
||||
|
||||
// stop if error, or if downstream filter said
|
||||
// to stop.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Actual.cBuffers > 1) {
|
||||
hr = CollectAndDeliver(tStart, tStop);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// sync version of above loop
|
||||
while (tCurrent < tAlignStop) {
|
||||
|
||||
// Break out without calling EndOfStream if we're asked to
|
||||
// do something different
|
||||
if (CheckRequest(&dwRequest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IMediaSample* pSample;
|
||||
|
||||
hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
|
||||
if (FAILED(hr)) {
|
||||
OnError(hr);
|
||||
return;
|
||||
}
|
||||
|
||||
LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
|
||||
if (tStopThis > tAlignStop) {
|
||||
tStopThis = tAlignStop;
|
||||
}
|
||||
pSample->SetTime(&tCurrent, &tStopThis);
|
||||
tCurrent = tStopThis;
|
||||
|
||||
if (bDiscontinuity) {
|
||||
pSample->SetDiscontinuity(TRUE);
|
||||
bDiscontinuity = FALSE;
|
||||
}
|
||||
|
||||
hr = m_pReader->SyncReadAligned(pSample);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
pSample->Release();
|
||||
OnError(hr);
|
||||
return;
|
||||
}
|
||||
|
||||
hr = DeliverSample(pSample, tStart, tStop);
|
||||
if (hr != S_OK) {
|
||||
if (FAILED(hr)) {
|
||||
OnError(hr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EndOfStream();
|
||||
}
|
||||
|
||||
// after a flush, cancelled i/o will be waiting for collection
|
||||
// and release
|
||||
void
|
||||
CPullPin::CleanupCancelled(void)
|
||||
{
|
||||
while (1) {
|
||||
IMediaSample * pSample;
|
||||
DWORD_PTR dwUnused;
|
||||
|
||||
HRESULT hr = m_pReader->WaitForNext(
|
||||
0, // no wait
|
||||
&pSample,
|
||||
&dwUnused);
|
||||
if(pSample) {
|
||||
pSample->Release();
|
||||
} else {
|
||||
// no more samples
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: PullPin.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines CPullPin class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __PULLPIN_H__
|
||||
#define __PULLPIN_H__
|
||||
|
||||
//
|
||||
// CPullPin
|
||||
//
|
||||
// object supporting pulling data from an IAsyncReader interface.
|
||||
// Given a start/stop position, calls a pure Receive method with each
|
||||
// IMediaSample received.
|
||||
//
|
||||
// This is essentially for use in a MemInputPin when it finds itself
|
||||
// connected to an IAsyncReader pin instead of a pushing pin.
|
||||
//
|
||||
|
||||
class CPullPin : public CAMThread
|
||||
{
|
||||
IAsyncReader* m_pReader;
|
||||
REFERENCE_TIME m_tStart;
|
||||
REFERENCE_TIME m_tStop;
|
||||
REFERENCE_TIME m_tDuration;
|
||||
BOOL m_bSync;
|
||||
|
||||
enum ThreadMsg {
|
||||
TM_Pause, // stop pulling and wait for next message
|
||||
TM_Start, // start pulling
|
||||
TM_Exit, // stop and exit
|
||||
};
|
||||
|
||||
ThreadMsg m_State;
|
||||
|
||||
// override pure thread proc from CAMThread
|
||||
DWORD ThreadProc(void);
|
||||
|
||||
// running pull method (check m_bSync)
|
||||
void Process(void);
|
||||
|
||||
// clean up any cancelled i/o after a flush
|
||||
void CleanupCancelled(void);
|
||||
|
||||
// suspend thread from pulling, eg during seek
|
||||
HRESULT PauseThread();
|
||||
|
||||
// start thread pulling - create thread if necy
|
||||
HRESULT StartThread();
|
||||
|
||||
// stop and close thread
|
||||
HRESULT StopThread();
|
||||
|
||||
// called from ProcessAsync to queue and collect requests
|
||||
HRESULT QueueSample(
|
||||
__inout REFERENCE_TIME& tCurrent,
|
||||
REFERENCE_TIME tAlignStop,
|
||||
BOOL bDiscontinuity);
|
||||
|
||||
HRESULT CollectAndDeliver(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop);
|
||||
|
||||
HRESULT DeliverSample(
|
||||
IMediaSample* pSample,
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop);
|
||||
|
||||
protected:
|
||||
IMemAllocator * m_pAlloc;
|
||||
|
||||
public:
|
||||
CPullPin();
|
||||
virtual ~CPullPin();
|
||||
|
||||
// returns S_OK if successfully connected to an IAsyncReader interface
|
||||
// from this object
|
||||
// Optional allocator should be proposed as a preferred allocator if
|
||||
// necessary
|
||||
// bSync is TRUE if we are to use sync reads instead of the
|
||||
// async methods.
|
||||
HRESULT Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync);
|
||||
|
||||
// disconnect any connection made in Connect
|
||||
HRESULT Disconnect();
|
||||
|
||||
// agree an allocator using RequestAllocator - optional
|
||||
// props param specifies your requirements (non-zero fields).
|
||||
// returns an error code if fail to match requirements.
|
||||
// optional IMemAllocator interface is offered as a preferred allocator
|
||||
// but no error occurs if it can't be met.
|
||||
virtual HRESULT DecideAllocator(
|
||||
IMemAllocator* pAlloc,
|
||||
__inout_opt ALLOCATOR_PROPERTIES * pProps);
|
||||
|
||||
// set start and stop position. if active, will start immediately at
|
||||
// the new position. Default is 0 to duration
|
||||
HRESULT Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop);
|
||||
|
||||
// return the total duration
|
||||
HRESULT Duration(__out REFERENCE_TIME* ptDuration);
|
||||
|
||||
// start pulling data
|
||||
HRESULT Active(void);
|
||||
|
||||
// stop pulling data
|
||||
HRESULT Inactive(void);
|
||||
|
||||
// helper functions
|
||||
LONGLONG AlignDown(LONGLONG ll, LONG lAlign) {
|
||||
// aligning downwards is just truncation
|
||||
return ll & ~(lAlign-1);
|
||||
};
|
||||
|
||||
LONGLONG AlignUp(LONGLONG ll, LONG lAlign) {
|
||||
// align up: round up to next boundary
|
||||
return (ll + (lAlign -1)) & ~(lAlign -1);
|
||||
};
|
||||
|
||||
// GetReader returns the (addrefed) IAsyncReader interface
|
||||
// for SyncRead etc
|
||||
IAsyncReader* GetReader() {
|
||||
m_pReader->AddRef();
|
||||
return m_pReader;
|
||||
};
|
||||
|
||||
// -- pure --
|
||||
|
||||
// override this to handle data arrival
|
||||
// return value other than S_OK will stop data
|
||||
virtual HRESULT Receive(IMediaSample*) PURE;
|
||||
|
||||
// override this to handle end-of-stream
|
||||
virtual HRESULT EndOfStream(void) PURE;
|
||||
|
||||
// called on runtime errors that will have caused pulling
|
||||
// to stop
|
||||
// these errors are all returned from the upstream filter, who
|
||||
// will have already reported any errors to the filtergraph.
|
||||
virtual void OnError(HRESULT hr) PURE;
|
||||
|
||||
// flush this pin and all downstream
|
||||
virtual HRESULT BeginFlush() PURE;
|
||||
virtual HRESULT EndFlush() PURE;
|
||||
|
||||
};
|
||||
|
||||
#endif //__PULLPIN_H__
|
|
@ -0,0 +1,402 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: RefClock.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements the IReferenceClock interface.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef DXMPERF
|
||||
#include "dxmperf.h"
|
||||
#endif // DXMPERF
|
||||
|
||||
|
||||
// 'this' used in constructor list
|
||||
#pragma warning(disable:4355)
|
||||
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::NonDelegatingQueryInterface(
|
||||
REFIID riid,
|
||||
__deref_out void ** ppv)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (riid == IID_IReferenceClock)
|
||||
{
|
||||
hr = GetInterface((IReferenceClock *) this, ppv);
|
||||
}
|
||||
else if (riid == IID_IReferenceClockTimerControl)
|
||||
{
|
||||
hr = GetInterface((IReferenceClockTimerControl *) this, ppv);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
CBaseReferenceClock::~CBaseReferenceClock()
|
||||
{
|
||||
#ifdef DXMPERF
|
||||
PERFLOG_DTOR( L"CBaseReferenceClock", (IReferenceClock *) this );
|
||||
#endif // DXMPERF
|
||||
|
||||
if (m_TimerResolution) timeEndPeriod(m_TimerResolution);
|
||||
|
||||
if (m_pSchedule)
|
||||
{
|
||||
m_pSchedule->DumpLinkedList();
|
||||
}
|
||||
|
||||
if (m_hThread)
|
||||
{
|
||||
m_bAbort = TRUE;
|
||||
TriggerThread();
|
||||
WaitForSingleObject( m_hThread, INFINITE );
|
||||
EXECUTE_ASSERT( CloseHandle(m_hThread) );
|
||||
m_hThread = 0;
|
||||
EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
|
||||
delete m_pSchedule;
|
||||
}
|
||||
}
|
||||
|
||||
// A derived class may supply a hThreadEvent if it has its own thread that will take care
|
||||
// of calling the schedulers Advise method. (Refere to CBaseReferenceClock::AdviseThread()
|
||||
// to see what such a thread has to do.)
|
||||
CBaseReferenceClock::CBaseReferenceClock( __in_opt LPCTSTR pName,
|
||||
__inout_opt LPUNKNOWN pUnk,
|
||||
__inout HRESULT *phr,
|
||||
__inout_opt CAMSchedule * pShed )
|
||||
: CUnknown( pName, pUnk )
|
||||
, m_rtLastGotTime(0)
|
||||
, m_TimerResolution(0)
|
||||
, m_bAbort( FALSE )
|
||||
, m_pSchedule( pShed ? pShed : new CAMSchedule(CreateEvent(NULL, FALSE, FALSE, NULL)) )
|
||||
, m_hThread(0)
|
||||
{
|
||||
|
||||
#ifdef DXMPERF
|
||||
PERFLOG_CTOR( pName ? pName : L"CBaseReferenceClock", (IReferenceClock *) this );
|
||||
#endif // DXMPERF
|
||||
|
||||
ASSERT(m_pSchedule);
|
||||
if (!m_pSchedule)
|
||||
{
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set up the highest resolution timer we can manage
|
||||
TIMECAPS tc;
|
||||
m_TimerResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
|
||||
? tc.wPeriodMin
|
||||
: 1;
|
||||
|
||||
timeBeginPeriod(m_TimerResolution);
|
||||
|
||||
/* Initialise our system times - the derived clock should set the right values */
|
||||
m_dwPrevSystemTime = timeGetTime();
|
||||
m_rtPrivateTime = (UNITS / MILLISECONDS) * m_dwPrevSystemTime;
|
||||
|
||||
#ifdef PERF
|
||||
m_idGetSystemTime = MSR_REGISTER(TEXT("CBaseReferenceClock::GetTime"));
|
||||
#endif
|
||||
|
||||
if ( !pShed )
|
||||
{
|
||||
DWORD ThreadID;
|
||||
m_hThread = ::CreateThread(NULL, // Security attributes
|
||||
(DWORD) 0, // Initial stack size
|
||||
AdviseThreadFunction, // Thread start address
|
||||
(LPVOID) this, // Thread parameter
|
||||
(DWORD) 0, // Creation flags
|
||||
&ThreadID); // Thread identifier
|
||||
|
||||
if (m_hThread)
|
||||
{
|
||||
SetThreadPriority( m_hThread, THREAD_PRIORITY_TIME_CRITICAL );
|
||||
}
|
||||
else
|
||||
{
|
||||
*phr = E_FAIL;
|
||||
EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
|
||||
delete m_pSchedule;
|
||||
m_pSchedule = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBaseReferenceClock::Restart (IN REFERENCE_TIME rtMinTime)
|
||||
{
|
||||
Lock();
|
||||
m_rtLastGotTime = rtMinTime ;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::GetTime(__out REFERENCE_TIME *pTime)
|
||||
{
|
||||
HRESULT hr;
|
||||
if (pTime)
|
||||
{
|
||||
REFERENCE_TIME rtNow;
|
||||
Lock();
|
||||
rtNow = GetPrivateTime();
|
||||
if (rtNow > m_rtLastGotTime)
|
||||
{
|
||||
m_rtLastGotTime = rtNow;
|
||||
hr = S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = S_FALSE;
|
||||
}
|
||||
*pTime = m_rtLastGotTime;
|
||||
Unlock();
|
||||
MSR_INTEGER(m_idGetSystemTime, LONG((*pTime) / (UNITS/MILLISECONDS)) );
|
||||
|
||||
#ifdef DXMPERF
|
||||
PERFLOG_GETTIME( (IReferenceClock *) this, *pTime );
|
||||
#endif // DXMPERF
|
||||
|
||||
}
|
||||
else hr = E_POINTER;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Ask for an async notification that a time has elapsed */
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::AdviseTime(
|
||||
REFERENCE_TIME baseTime, // base reference time
|
||||
REFERENCE_TIME streamTime, // stream offset time
|
||||
HEVENT hEvent, // advise via this event
|
||||
__out DWORD_PTR *pdwAdviseCookie)// where your cookie goes
|
||||
{
|
||||
CheckPointer(pdwAdviseCookie, E_POINTER);
|
||||
*pdwAdviseCookie = 0;
|
||||
|
||||
// Check that the event is not already set
|
||||
ASSERT(WAIT_TIMEOUT == WaitForSingleObject(HANDLE(hEvent),0));
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
const REFERENCE_TIME lRefTime = baseTime + streamTime;
|
||||
if ( lRefTime <= 0 || lRefTime == MAX_TIME )
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pdwAdviseCookie = m_pSchedule->AddAdvisePacket( lRefTime, 0, HANDLE(hEvent), FALSE );
|
||||
hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
/* Ask for an asynchronous periodic notification that a time has elapsed */
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::AdvisePeriodic(
|
||||
REFERENCE_TIME StartTime, // starting at this time
|
||||
REFERENCE_TIME PeriodTime, // time between notifications
|
||||
HSEMAPHORE hSemaphore, // advise via a semaphore
|
||||
__out DWORD_PTR *pdwAdviseCookie) // where your cookie goes
|
||||
{
|
||||
CheckPointer(pdwAdviseCookie, E_POINTER);
|
||||
*pdwAdviseCookie = 0;
|
||||
|
||||
HRESULT hr;
|
||||
if (StartTime > 0 && PeriodTime > 0 && StartTime != MAX_TIME )
|
||||
{
|
||||
*pdwAdviseCookie = m_pSchedule->AddAdvisePacket( StartTime, PeriodTime, HANDLE(hSemaphore), TRUE );
|
||||
hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
|
||||
}
|
||||
else hr = E_INVALIDARG;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::Unadvise(DWORD_PTR dwAdviseCookie)
|
||||
{
|
||||
return m_pSchedule->Unadvise(dwAdviseCookie);
|
||||
}
|
||||
|
||||
|
||||
REFERENCE_TIME CBaseReferenceClock::GetPrivateTime()
|
||||
{
|
||||
CAutoLock cObjectLock(this);
|
||||
|
||||
|
||||
/* If the clock has wrapped then the current time will be less than
|
||||
* the last time we were notified so add on the extra milliseconds
|
||||
*
|
||||
* The time period is long enough so that the likelihood of
|
||||
* successive calls spanning the clock cycle is not considered.
|
||||
*/
|
||||
|
||||
DWORD dwTime = timeGetTime();
|
||||
{
|
||||
m_rtPrivateTime += Int32x32To64(UNITS / MILLISECONDS, (DWORD)(dwTime - m_dwPrevSystemTime));
|
||||
m_dwPrevSystemTime = dwTime;
|
||||
}
|
||||
|
||||
return m_rtPrivateTime;
|
||||
}
|
||||
|
||||
|
||||
/* Adjust the current time by the input value. This allows an
|
||||
external time source to work out some of the latency of the clock
|
||||
system and adjust the "current" time accordingly. The intent is
|
||||
that the time returned to the user is synchronised to a clock
|
||||
source and allows drift to be catered for.
|
||||
|
||||
For example: if the clock source detects a drift it can pass a delta
|
||||
to the current time rather than having to set an explicit time.
|
||||
*/
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::SetTimeDelta(const REFERENCE_TIME & TimeDelta)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
|
||||
// Just break if passed an improper time delta value
|
||||
LONGLONG llDelta = TimeDelta > 0 ? TimeDelta : -TimeDelta;
|
||||
if (llDelta > UNITS * 1000) {
|
||||
DbgLog((LOG_TRACE, 0, TEXT("Bad Time Delta")));
|
||||
//DebugBreak();
|
||||
}
|
||||
|
||||
// We're going to calculate a "severity" for the time change. Max -1
|
||||
// min 8. We'll then use this as the debug logging level for a
|
||||
// debug log message.
|
||||
const LONG usDelta = LONG(TimeDelta/10); // Delta in micro-secs
|
||||
|
||||
DWORD delta = abs(usDelta); // varying delta
|
||||
// Severity == 8 - ceil(log<base 8>(abs( micro-secs delta)))
|
||||
int Severity = 8;
|
||||
while ( delta > 0 )
|
||||
{
|
||||
delta >>= 3; // div 8
|
||||
Severity--;
|
||||
}
|
||||
|
||||
// Sev == 0 => > 2 second delta!
|
||||
DbgLog((LOG_TIMING, Severity < 0 ? 0 : Severity,
|
||||
TEXT("Sev %2i: CSystemClock::SetTimeDelta(%8ld us) %lu -> %lu ms."),
|
||||
Severity, usDelta, DWORD(ConvertToMilliseconds(m_rtPrivateTime)),
|
||||
DWORD(ConvertToMilliseconds(TimeDelta+m_rtPrivateTime)) ));
|
||||
|
||||
// Don't want the DbgBreak to fire when running stress on debug-builds.
|
||||
#ifdef BREAK_ON_SEVERE_TIME_DELTA
|
||||
if (Severity < 0)
|
||||
DbgBreakPoint(TEXT("SetTimeDelta > 16 seconds!"),
|
||||
TEXT(__FILE__),__LINE__);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
CAutoLock cObjectLock(this);
|
||||
m_rtPrivateTime += TimeDelta;
|
||||
// If time goes forwards, and we have advises, then we need to
|
||||
// trigger the thread so that it can re-evaluate its wait time.
|
||||
// Since we don't want the cost of the thread switches if the change
|
||||
// is really small, only do it if clock goes forward by more than
|
||||
// 0.5 millisecond. If the time goes backwards, the thread will
|
||||
// wake up "early" (relativly speaking) and will re-evaluate at
|
||||
// that time.
|
||||
if ( TimeDelta > 5000 && m_pSchedule->GetAdviseCount() > 0 ) TriggerThread();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Thread stuff
|
||||
|
||||
DWORD __stdcall CBaseReferenceClock::AdviseThreadFunction(__in LPVOID p)
|
||||
{
|
||||
return DWORD(reinterpret_cast<CBaseReferenceClock*>(p)->AdviseThread());
|
||||
}
|
||||
|
||||
HRESULT CBaseReferenceClock::AdviseThread()
|
||||
{
|
||||
DWORD dwWait = INFINITE;
|
||||
|
||||
// The first thing we do is wait until something interesting happens
|
||||
// (meaning a first advise or shutdown). This prevents us calling
|
||||
// GetPrivateTime immediately which is goodness as that is a virtual
|
||||
// routine and the derived class may not yet be constructed. (This
|
||||
// thread is created in the base class constructor.)
|
||||
|
||||
while ( !m_bAbort )
|
||||
{
|
||||
// Wait for an interesting event to happen
|
||||
DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Delay: %lu ms"), dwWait ));
|
||||
WaitForSingleObject(m_pSchedule->GetEvent(), dwWait);
|
||||
if (m_bAbort) break;
|
||||
|
||||
// There are several reasons why we need to work from the internal
|
||||
// time, mainly to do with what happens when time goes backwards.
|
||||
// Mainly, it stop us looping madly if an event is just about to
|
||||
// expire when the clock goes backward (i.e. GetTime stop for a
|
||||
// while).
|
||||
const REFERENCE_TIME rtNow = GetPrivateTime();
|
||||
|
||||
DbgLog((LOG_TIMING, 3,
|
||||
TEXT("CBaseRefClock::AdviseThread() Woke at = %lu ms"),
|
||||
ConvertToMilliseconds(rtNow) ));
|
||||
|
||||
// We must add in a millisecond, since this is the resolution of our
|
||||
// WaitForSingleObject timer. Failure to do so will cause us to loop
|
||||
// franticly for (approx) 1 a millisecond.
|
||||
m_rtNextAdvise = m_pSchedule->Advise( 10000 + rtNow );
|
||||
LONGLONG llWait = m_rtNextAdvise - rtNow;
|
||||
|
||||
ASSERT( llWait > 0 );
|
||||
|
||||
llWait = ConvertToMilliseconds(llWait);
|
||||
// DON'T replace this with a max!! (The type's of these things is VERY important)
|
||||
dwWait = (llWait > REFERENCE_TIME(UINT_MAX)) ? UINT_MAX : DWORD(llWait);
|
||||
};
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
HRESULT CBaseReferenceClock::SetDefaultTimerResolution(
|
||||
REFERENCE_TIME timerResolution // in 100ns
|
||||
)
|
||||
{
|
||||
CAutoLock cObjectLock(this);
|
||||
if( 0 == timerResolution ) {
|
||||
if( m_TimerResolution ) {
|
||||
timeEndPeriod( m_TimerResolution );
|
||||
m_TimerResolution = 0;
|
||||
}
|
||||
} else {
|
||||
TIMECAPS tc;
|
||||
DWORD dwMinResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
|
||||
? tc.wPeriodMin
|
||||
: 1;
|
||||
DWORD dwResolution = max( dwMinResolution, DWORD(timerResolution / 10000) );
|
||||
if( dwResolution != m_TimerResolution ) {
|
||||
timeEndPeriod(m_TimerResolution);
|
||||
m_TimerResolution = dwResolution;
|
||||
timeBeginPeriod( m_TimerResolution );
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CBaseReferenceClock::GetDefaultTimerResolution(
|
||||
__out REFERENCE_TIME* pTimerResolution // in 100ns
|
||||
)
|
||||
{
|
||||
if( !pTimerResolution ) {
|
||||
return E_POINTER;
|
||||
}
|
||||
CAutoLock cObjectLock(this);
|
||||
*pTimerResolution = m_TimerResolution * 10000;
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: RefClock.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines the IReferenceClock interface.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __BASEREFCLOCK__
|
||||
#define __BASEREFCLOCK__
|
||||
|
||||
#include <Schedule.h>
|
||||
|
||||
const UINT RESOLUTION = 1; /* High resolution timer */
|
||||
const INT ADVISE_CACHE = 4; /* Default cache size */
|
||||
const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */
|
||||
|
||||
inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)
|
||||
{
|
||||
/* This converts an arbitrary value representing a reference time
|
||||
into a MILLISECONDS value for use in subsequent system calls */
|
||||
|
||||
return (RT / (UNITS / MILLISECONDS));
|
||||
}
|
||||
|
||||
/* This class hierarchy will support an IReferenceClock interface so
|
||||
that an audio card (or other externally driven clock) can update the
|
||||
system wide clock that everyone uses.
|
||||
|
||||
The interface will be pretty thin with probably just one update method
|
||||
This interface has not yet been defined.
|
||||
*/
|
||||
|
||||
/* This abstract base class implements the IReferenceClock
|
||||
* interface. Classes that actually provide clock signals (from
|
||||
* whatever source) have to be derived from this class.
|
||||
*
|
||||
* The abstract class provides implementations for:
|
||||
* CUnknown support
|
||||
* locking support (CCritSec)
|
||||
* client advise code (creates a thread)
|
||||
*
|
||||
* Question: what can we do about quality? Change the timer
|
||||
* resolution to lower the system load? Up the priority of the
|
||||
* timer thread to force more responsive signals?
|
||||
*
|
||||
* During class construction we create a worker thread that is destroyed during
|
||||
* destuction. This thread executes a series of WaitForSingleObject calls,
|
||||
* waking up when a command is given to the thread or the next wake up point
|
||||
* is reached. The wakeup points are determined by clients making Advise
|
||||
* calls.
|
||||
*
|
||||
* Each advise call defines a point in time when they wish to be notified. A
|
||||
* periodic advise is a series of these such events. We maintain a list of
|
||||
* advise links and calculate when the nearest event notification is due for.
|
||||
* We then call WaitForSingleObject with a timeout equal to this time. The
|
||||
* handle we wait on is used by the class to signal that something has changed
|
||||
* and that we must reschedule the next event. This typically happens when
|
||||
* someone comes in and asks for an advise link while we are waiting for an
|
||||
* event to timeout.
|
||||
*
|
||||
* While we are modifying the list of advise requests we
|
||||
* are protected from interference through a critical section. Clients are NOT
|
||||
* advised through callbacks. One shot clients have an event set, while
|
||||
* periodic clients have a semaphore released for each event notification. A
|
||||
* semaphore allows a client to be kept up to date with the number of events
|
||||
* actually triggered and be assured that they can't miss multiple events being
|
||||
* set.
|
||||
*
|
||||
* Keeping track of advises is taken care of by the CAMSchedule class.
|
||||
*/
|
||||
|
||||
class CBaseReferenceClock
|
||||
: public CUnknown, public IReferenceClock, public CCritSec, public IReferenceClockTimerControl
|
||||
{
|
||||
protected:
|
||||
virtual ~CBaseReferenceClock(); // Don't let me be created on the stack!
|
||||
public:
|
||||
CBaseReferenceClock(__in_opt LPCTSTR pName,
|
||||
__inout_opt LPUNKNOWN pUnk,
|
||||
__inout HRESULT *phr,
|
||||
__inout_opt CAMSchedule * pSched = 0 );
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
/* IReferenceClock methods */
|
||||
// Derived classes must implement GetPrivateTime(). All our GetTime
|
||||
// does is call GetPrivateTime and then check so that time does not
|
||||
// go backwards. A return code of S_FALSE implies that the internal
|
||||
// clock has gone backwards and GetTime time has halted until internal
|
||||
// time has caught up. (Don't know if this will be much use to folk,
|
||||
// but it seems odd not to use the return code for something useful.)
|
||||
STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime);
|
||||
// When this is called, it sets m_rtLastGotTime to the time it returns.
|
||||
|
||||
/* Provide standard mechanisms for scheduling events */
|
||||
|
||||
/* Ask for an async notification that a time has elapsed */
|
||||
STDMETHODIMP AdviseTime(
|
||||
REFERENCE_TIME baseTime, // base reference time
|
||||
REFERENCE_TIME streamTime, // stream offset time
|
||||
HEVENT hEvent, // advise via this event
|
||||
__out DWORD_PTR *pdwAdviseCookie// where your cookie goes
|
||||
);
|
||||
|
||||
/* Ask for an asynchronous periodic notification that a time has elapsed */
|
||||
STDMETHODIMP AdvisePeriodic(
|
||||
REFERENCE_TIME StartTime, // starting at this time
|
||||
REFERENCE_TIME PeriodTime, // time between notifications
|
||||
HSEMAPHORE hSemaphore, // advise via a semaphore
|
||||
__out DWORD_PTR *pdwAdviseCookie// where your cookie goes
|
||||
);
|
||||
|
||||
/* Cancel a request for notification(s) - if the notification was
|
||||
* a one shot timer then this function doesn't need to be called
|
||||
* as the advise is automatically cancelled, however it does no
|
||||
* harm to explicitly cancel a one-shot advise. It is REQUIRED that
|
||||
* clients call Unadvise to clear a Periodic advise setting.
|
||||
*/
|
||||
|
||||
STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);
|
||||
|
||||
/* Methods for the benefit of derived classes or outer objects */
|
||||
|
||||
// GetPrivateTime() is the REAL clock. GetTime is just a cover for
|
||||
// it. Derived classes will probably override this method but not
|
||||
// GetTime() itself.
|
||||
// The important point about GetPrivateTime() is it's allowed to go
|
||||
// backwards. Our GetTime() will keep returning the LastGotTime
|
||||
// until GetPrivateTime() catches up.
|
||||
virtual REFERENCE_TIME GetPrivateTime();
|
||||
|
||||
/* Provide a method for correcting drift */
|
||||
STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );
|
||||
|
||||
CAMSchedule * GetSchedule() const { return m_pSchedule; }
|
||||
|
||||
// IReferenceClockTimerControl methods
|
||||
//
|
||||
// Setting a default of 0 disables the default of 1ms
|
||||
STDMETHODIMP SetDefaultTimerResolution(
|
||||
REFERENCE_TIME timerResolution // in 100ns
|
||||
);
|
||||
STDMETHODIMP GetDefaultTimerResolution(
|
||||
__out REFERENCE_TIME* pTimerResolution // in 100ns
|
||||
);
|
||||
|
||||
private:
|
||||
REFERENCE_TIME m_rtPrivateTime; // Current best estimate of time
|
||||
DWORD m_dwPrevSystemTime; // Last vaule we got from timeGetTime
|
||||
REFERENCE_TIME m_rtLastGotTime; // Last time returned by GetTime
|
||||
REFERENCE_TIME m_rtNextAdvise; // Time of next advise
|
||||
UINT m_TimerResolution;
|
||||
|
||||
#ifdef PERF
|
||||
int m_idGetSystemTime;
|
||||
#endif
|
||||
|
||||
// Thread stuff
|
||||
public:
|
||||
void TriggerThread() // Wakes thread up. Need to do this if
|
||||
{ // time to next advise needs reevaluating.
|
||||
EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
BOOL m_bAbort; // Flag used for thread shutdown
|
||||
HANDLE m_hThread; // Thread handle
|
||||
|
||||
HRESULT AdviseThread(); // Method in which the advise thread runs
|
||||
static DWORD __stdcall AdviseThreadFunction(__in LPVOID); // Function used to get there
|
||||
|
||||
protected:
|
||||
CAMSchedule * m_pSchedule;
|
||||
|
||||
void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: RefTime.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines CRefTime, a class that manages
|
||||
// reference times.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// CRefTime
|
||||
//
|
||||
// Manage reference times.
|
||||
// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)
|
||||
// functions providing simple comparison, conversion and arithmetic.
|
||||
//
|
||||
// A reference time (at the moment) is a unit of seconds represented in
|
||||
// 100ns units as is used in the Win32 FILETIME structure. BUT the time
|
||||
// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it
|
||||
// will either be stream time or reference time depending upon context
|
||||
//
|
||||
// This class provides simple arithmetic operations on reference times
|
||||
//
|
||||
// keep non-virtual otherwise the data layout will not be the same as
|
||||
// REFERENCE_TIME
|
||||
|
||||
|
||||
// -----
|
||||
// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but
|
||||
// you will need to do so explicitly
|
||||
// -----
|
||||
|
||||
|
||||
#ifndef __REFTIME__
|
||||
#define __REFTIME__
|
||||
|
||||
|
||||
const LONGLONG MILLISECONDS = (1000); // 10 ^ 3
|
||||
const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9
|
||||
const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7
|
||||
|
||||
/* Unfortunately an inline function here generates a call to __allmul
|
||||
- even for constants!
|
||||
*/
|
||||
#define MILLISECONDS_TO_100NS_UNITS(lMs) \
|
||||
Int32x32To64((lMs), (UNITS / MILLISECONDS))
|
||||
|
||||
class CRefTime
|
||||
{
|
||||
public:
|
||||
|
||||
// *MUST* be the only data member so that this class is exactly
|
||||
// equivalent to a REFERENCE_TIME.
|
||||
// Also, must be *no virtual functions*
|
||||
|
||||
REFERENCE_TIME m_time;
|
||||
|
||||
inline CRefTime()
|
||||
{
|
||||
// default to 0 time
|
||||
m_time = 0;
|
||||
};
|
||||
|
||||
inline CRefTime(LONG msecs)
|
||||
{
|
||||
m_time = MILLISECONDS_TO_100NS_UNITS(msecs);
|
||||
};
|
||||
|
||||
inline CRefTime(REFERENCE_TIME rt)
|
||||
{
|
||||
m_time = rt;
|
||||
};
|
||||
|
||||
inline operator REFERENCE_TIME() const
|
||||
{
|
||||
return m_time;
|
||||
};
|
||||
|
||||
inline CRefTime& operator=(const CRefTime& rt)
|
||||
{
|
||||
m_time = rt.m_time;
|
||||
return *this;
|
||||
};
|
||||
|
||||
inline CRefTime& operator=(const LONGLONG ll)
|
||||
{
|
||||
m_time = ll;
|
||||
return *this;
|
||||
};
|
||||
|
||||
inline CRefTime& operator+=(const CRefTime& rt)
|
||||
{
|
||||
return (*this = *this + rt);
|
||||
};
|
||||
|
||||
inline CRefTime& operator-=(const CRefTime& rt)
|
||||
{
|
||||
return (*this = *this - rt);
|
||||
};
|
||||
|
||||
inline LONG Millisecs(void)
|
||||
{
|
||||
return (LONG)(m_time / (UNITS / MILLISECONDS));
|
||||
};
|
||||
|
||||
inline LONGLONG GetUnits(void)
|
||||
{
|
||||
return m_time;
|
||||
};
|
||||
};
|
||||
|
||||
const LONGLONG TimeZero = 0;
|
||||
|
||||
#endif /* __REFTIME__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,478 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: RenBase.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a generic ActiveX base renderer
|
||||
// class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __RENBASE__
|
||||
#define __RENBASE__
|
||||
|
||||
// Forward class declarations
|
||||
|
||||
class CBaseRenderer;
|
||||
class CBaseVideoRenderer;
|
||||
class CRendererInputPin;
|
||||
|
||||
// This is our input pin class that channels calls to the renderer
|
||||
|
||||
class CRendererInputPin : public CBaseInputPin
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseRenderer *m_pRenderer;
|
||||
|
||||
public:
|
||||
|
||||
CRendererInputPin(__inout CBaseRenderer *pRenderer,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR Name);
|
||||
|
||||
// Overriden from the base pin classes
|
||||
|
||||
HRESULT BreakConnect();
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
HRESULT SetMediaType(const CMediaType *pmt);
|
||||
HRESULT CheckMediaType(const CMediaType *pmt);
|
||||
HRESULT Active();
|
||||
HRESULT Inactive();
|
||||
|
||||
// Add rendering behaviour to interface functions
|
||||
|
||||
STDMETHODIMP QueryId(__deref_out LPWSTR *Id);
|
||||
STDMETHODIMP EndOfStream();
|
||||
STDMETHODIMP BeginFlush();
|
||||
STDMETHODIMP EndFlush();
|
||||
STDMETHODIMP Receive(IMediaSample *pMediaSample);
|
||||
|
||||
// Helper
|
||||
IMemAllocator inline *Allocator() const
|
||||
{
|
||||
return m_pAllocator;
|
||||
}
|
||||
};
|
||||
|
||||
// Main renderer class that handles synchronisation and state changes
|
||||
|
||||
class CBaseRenderer : public CBaseFilter
|
||||
{
|
||||
protected:
|
||||
|
||||
friend class CRendererInputPin;
|
||||
|
||||
friend void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier
|
||||
UINT uMsg, // Not currently used
|
||||
DWORD_PTR dwUser, // User information
|
||||
DWORD_PTR dw1, // Windows reserved
|
||||
DWORD_PTR dw2); // Is also reserved
|
||||
|
||||
CRendererPosPassThru *m_pPosition; // Media seeking pass by object
|
||||
CAMEvent m_RenderEvent; // Used to signal timer events
|
||||
CAMEvent m_ThreadSignal; // Signalled to release worker thread
|
||||
CAMEvent m_evComplete; // Signalled when state complete
|
||||
BOOL m_bAbort; // Stop us from rendering more data
|
||||
BOOL m_bStreaming; // Are we currently streaming
|
||||
DWORD_PTR m_dwAdvise; // Timer advise cookie
|
||||
IMediaSample *m_pMediaSample; // Current image media sample
|
||||
BOOL m_bEOS; // Any more samples in the stream
|
||||
BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE
|
||||
CRendererInputPin *m_pInputPin; // Our renderer input pin object
|
||||
CCritSec m_InterfaceLock; // Critical section for interfaces
|
||||
CCritSec m_RendererLock; // Controls access to internals
|
||||
IQualityControl * m_pQSink; // QualityControl sink
|
||||
BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT
|
||||
// Avoid some deadlocks by tracking filter during stop
|
||||
volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive
|
||||
// And actually processing the sample
|
||||
REFERENCE_TIME m_SignalTime; // Time when we signal EC_COMPLETE
|
||||
UINT m_EndOfStreamTimer; // Used to signal end of stream
|
||||
CCritSec m_ObjectCreationLock; // This lock protects the creation and
|
||||
// of m_pPosition and m_pInputPin. It
|
||||
// ensures that two threads cannot create
|
||||
// either object simultaneously.
|
||||
|
||||
public:
|
||||
|
||||
CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer
|
||||
__in_opt LPCTSTR pName, // Debug ONLY description
|
||||
__inout_opt LPUNKNOWN pUnk, // Aggregated owner object
|
||||
__inout HRESULT *phr); // General OLE return code
|
||||
|
||||
~CBaseRenderer();
|
||||
|
||||
// Overriden to say what interfaces we support and where
|
||||
|
||||
virtual HRESULT GetMediaPositionInterface(REFIID riid, __deref_out void **ppv);
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);
|
||||
|
||||
virtual HRESULT SourceThreadCanWait(BOOL bCanWait);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Debug only dump of the renderer state
|
||||
void DisplayRendererState();
|
||||
#endif
|
||||
virtual HRESULT WaitForRenderTime();
|
||||
virtual HRESULT CompleteStateChange(FILTER_STATE OldState);
|
||||
|
||||
// Return internal information about this filter
|
||||
|
||||
BOOL IsEndOfStream() { return m_bEOS; };
|
||||
BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };
|
||||
BOOL IsStreaming() { return m_bStreaming; };
|
||||
void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };
|
||||
virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };
|
||||
CAMEvent *GetRenderEvent() { return &m_RenderEvent; };
|
||||
|
||||
// Permit access to the transition state
|
||||
|
||||
void Ready() { m_evComplete.Set(); };
|
||||
void NotReady() { m_evComplete.Reset(); };
|
||||
BOOL CheckReady() { return m_evComplete.Check(); };
|
||||
|
||||
virtual int GetPinCount();
|
||||
virtual CBasePin *GetPin(int n);
|
||||
FILTER_STATE GetRealState();
|
||||
void SendRepaint();
|
||||
void SendNotifyWindow(IPin *pPin,HWND hwnd);
|
||||
BOOL OnDisplayChange();
|
||||
void SetRepaintStatus(BOOL bRepaint);
|
||||
|
||||
// Override the filter and pin interface functions
|
||||
|
||||
STDMETHODIMP Stop();
|
||||
STDMETHODIMP Pause();
|
||||
STDMETHODIMP Run(REFERENCE_TIME StartTime);
|
||||
STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);
|
||||
STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);
|
||||
|
||||
// These are available for a quality management implementation
|
||||
|
||||
virtual void OnRenderStart(IMediaSample *pMediaSample);
|
||||
virtual void OnRenderEnd(IMediaSample *pMediaSample);
|
||||
virtual HRESULT OnStartStreaming() { return NOERROR; };
|
||||
virtual HRESULT OnStopStreaming() { return NOERROR; };
|
||||
virtual void OnWaitStart() { };
|
||||
virtual void OnWaitEnd() { };
|
||||
virtual void PrepareRender() { };
|
||||
|
||||
#ifdef PERF
|
||||
REFERENCE_TIME m_trRenderStart; // Just before we started drawing
|
||||
// Set in OnRenderStart, Used in OnRenderEnd
|
||||
int m_idBaseStamp; // MSR_id for frame time stamp
|
||||
int m_idBaseRenderTime; // MSR_id for true wait time
|
||||
int m_idBaseAccuracy; // MSR_id for time frame is late (int)
|
||||
#endif
|
||||
|
||||
// Quality management implementation for scheduling rendering
|
||||
|
||||
virtual BOOL ScheduleSample(IMediaSample *pMediaSample);
|
||||
virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,
|
||||
__out REFERENCE_TIME *pStartTime,
|
||||
__out REFERENCE_TIME *pEndTime);
|
||||
|
||||
virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
|
||||
__out REFERENCE_TIME *ptrStart,
|
||||
__out REFERENCE_TIME *ptrEnd);
|
||||
|
||||
// Lots of end of stream complexities
|
||||
|
||||
void TimerCallback();
|
||||
void ResetEndOfStreamTimer();
|
||||
HRESULT NotifyEndOfStream();
|
||||
virtual HRESULT SendEndOfStream();
|
||||
virtual HRESULT ResetEndOfStream();
|
||||
virtual HRESULT EndOfStream();
|
||||
|
||||
// Rendering is based around the clock
|
||||
|
||||
void SignalTimerFired();
|
||||
virtual HRESULT CancelNotification();
|
||||
virtual HRESULT ClearPendingSample();
|
||||
|
||||
// Called when the filter changes state
|
||||
|
||||
virtual HRESULT Active();
|
||||
virtual HRESULT Inactive();
|
||||
virtual HRESULT StartStreaming();
|
||||
virtual HRESULT StopStreaming();
|
||||
virtual HRESULT BeginFlush();
|
||||
virtual HRESULT EndFlush();
|
||||
|
||||
// Deal with connections and type changes
|
||||
|
||||
virtual HRESULT BreakConnect();
|
||||
virtual HRESULT SetMediaType(const CMediaType *pmt);
|
||||
virtual HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
// These look after the handling of data samples
|
||||
|
||||
virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);
|
||||
virtual HRESULT Receive(IMediaSample *pMediaSample);
|
||||
virtual BOOL HaveCurrentSample();
|
||||
virtual IMediaSample *GetCurrentSample();
|
||||
virtual HRESULT Render(IMediaSample *pMediaSample);
|
||||
|
||||
// Derived classes MUST override these
|
||||
virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
|
||||
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
|
||||
|
||||
// Helper
|
||||
void WaitForReceiveToComplete();
|
||||
};
|
||||
|
||||
|
||||
// CBaseVideoRenderer is a renderer class (see its ancestor class) and
|
||||
// it handles scheduling of media samples so that they are drawn at the
|
||||
// correct time by the reference clock. It implements a degradation
|
||||
// strategy. Possible degradation modes are:
|
||||
// Drop frames here (only useful if the drawing takes significant time)
|
||||
// Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.
|
||||
// Signal supplier to change the frame rate - i.e. ongoing skipping.
|
||||
// Or any combination of the above.
|
||||
// In order to determine what's useful to try we need to know what's going
|
||||
// on. This is done by timing various operations (including the supplier).
|
||||
// This timing is done by using timeGetTime as it is accurate enough and
|
||||
// usually cheaper than calling the reference clock. It also tells the
|
||||
// truth if there is an audio break and the reference clock stops.
|
||||
// We provide a number of public entry points (named OnXxxStart, OnXxxEnd)
|
||||
// which the rest of the renderer calls at significant moments. These do
|
||||
// the timing.
|
||||
|
||||
// the number of frames that the sliding averages are averaged over.
|
||||
// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD
|
||||
#define AVGPERIOD 4
|
||||
#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)
|
||||
// Spot the bug in this macro - I can't. but it doesn't work!
|
||||
|
||||
class CBaseVideoRenderer : public CBaseRenderer, // Base renderer class
|
||||
public IQualProp, // Property page guff
|
||||
public IQualityControl // Allow throttling
|
||||
{
|
||||
protected:
|
||||
|
||||
// Hungarian:
|
||||
// tFoo is the time Foo in mSec (beware m_tStart from filter.h)
|
||||
// trBar is the time Bar by the reference clock
|
||||
|
||||
//******************************************************************
|
||||
// State variables to control synchronisation
|
||||
//******************************************************************
|
||||
|
||||
// Control of sending Quality messages. We need to know whether
|
||||
// we are in trouble (e.g. frames being dropped) and where the time
|
||||
// is being spent.
|
||||
|
||||
// When we drop a frame we play the next one early.
|
||||
// The frame after that is likely to wait before drawing and counting this
|
||||
// wait as spare time is unfair, so we count it as a zero wait.
|
||||
// We therefore need to know whether we are playing frames early or not.
|
||||
|
||||
int m_nNormal; // The number of consecutive frames
|
||||
// drawn at their normal time (not early)
|
||||
// -1 means we just dropped a frame.
|
||||
|
||||
#ifdef PERF
|
||||
BOOL m_bDrawLateFrames; // Don't drop any frames (debug and I'm
|
||||
// not keen on people using it!)
|
||||
#endif
|
||||
|
||||
BOOL m_bSupplierHandlingQuality;// The response to Quality messages says
|
||||
// our supplier is handling things.
|
||||
// We will allow things to go extra late
|
||||
// before dropping frames. We will play
|
||||
// very early after he has dropped one.
|
||||
|
||||
// Control of scheduling, frame dropping etc.
|
||||
// We need to know where the time is being spent so as to tell whether
|
||||
// we should be taking action here, signalling supplier or what.
|
||||
// The variables are initialised to a mode of NOT dropping frames.
|
||||
// They will tell the truth after a few frames.
|
||||
// We typically record a start time for an event, later we get the time
|
||||
// again and subtract to get the elapsed time, and we average this over
|
||||
// a few frames. The average is used to tell what mode we are in.
|
||||
|
||||
// Although these are reference times (64 bit) they are all DIFFERENCES
|
||||
// between times which are small. An int will go up to 214 secs before
|
||||
// overflow. Avoiding 64 bit multiplications and divisions seems
|
||||
// worth while.
|
||||
|
||||
|
||||
|
||||
// Audio-video throttling. If the user has turned up audio quality
|
||||
// very high (in principle it could be any other stream, not just audio)
|
||||
// then we can receive cries for help via the graph manager. In this case
|
||||
// we put in a wait for some time after rendering each frame.
|
||||
int m_trThrottle;
|
||||
|
||||
// The time taken to render (i.e. BitBlt) frames controls which component
|
||||
// needs to degrade. If the blt is expensive, the renderer degrades.
|
||||
// If the blt is cheap it's done anyway and the supplier degrades.
|
||||
int m_trRenderAvg; // Time frames are taking to blt
|
||||
int m_trRenderLast; // Time for last frame blt
|
||||
int m_tRenderStart; // Just before we started drawing (mSec)
|
||||
// derived from timeGetTime.
|
||||
|
||||
// When frames are dropped we will play the next frame as early as we can.
|
||||
// If it was a false alarm and the machine is fast we slide gently back to
|
||||
// normal timing. To do this, we record the offset showing just how early
|
||||
// we really are. This will normally be negative meaning early or zero.
|
||||
int m_trEarliness;
|
||||
|
||||
// Target provides slow long-term feedback to try to reduce the
|
||||
// average sync offset to zero. Whenever a frame is actually rendered
|
||||
// early we add a msec or two, whenever late we take off a few.
|
||||
// We add or take off 1/32 of the error time.
|
||||
// Eventually we should be hovering around zero. For a really bad case
|
||||
// where we were (say) 300mSec off, it might take 100 odd frames to
|
||||
// settle down. The rate of change of this is intended to be slower
|
||||
// than any other mechanism in Quartz, thereby avoiding hunting.
|
||||
int m_trTarget;
|
||||
|
||||
// The proportion of time spent waiting for the right moment to blt
|
||||
// controls whether we bother to drop a frame or whether we reckon that
|
||||
// we're doing well enough that we can stand a one-frame glitch.
|
||||
int m_trWaitAvg; // Average of last few wait times
|
||||
// (actually we just average how early
|
||||
// we were). Negative here means LATE.
|
||||
|
||||
// The average inter-frame time.
|
||||
// This is used to calculate the proportion of the time used by the
|
||||
// three operations (supplying us, waiting, rendering)
|
||||
int m_trFrameAvg; // Average inter-frame time
|
||||
int m_trDuration; // duration of last frame.
|
||||
|
||||
#ifdef PERF
|
||||
// Performance logging identifiers
|
||||
int m_idTimeStamp; // MSR_id for frame time stamp
|
||||
int m_idEarliness; // MSR_id for earliness fudge
|
||||
int m_idTarget; // MSR_id for Target fudge
|
||||
int m_idWaitReal; // MSR_id for true wait time
|
||||
int m_idWait; // MSR_id for wait time recorded
|
||||
int m_idFrameAccuracy; // MSR_id for time frame is late (int)
|
||||
int m_idRenderAvg; // MSR_id for Render time recorded (int)
|
||||
int m_idSchLateTime; // MSR_id for lateness at scheduler
|
||||
int m_idQualityRate; // MSR_id for Quality rate requested
|
||||
int m_idQualityTime; // MSR_id for Quality time requested
|
||||
int m_idDecision; // MSR_id for decision code
|
||||
int m_idDuration; // MSR_id for duration of a frame
|
||||
int m_idThrottle; // MSR_id for audio-video throttling
|
||||
//int m_idDebug; // MSR_id for trace style debugging
|
||||
//int m_idSendQuality; // MSR_id for timing the notifications per se
|
||||
#endif // PERF
|
||||
REFERENCE_TIME m_trRememberStampForPerf; // original time stamp of frame
|
||||
// with no earliness fudges etc.
|
||||
#ifdef PERF
|
||||
REFERENCE_TIME m_trRememberFrameForPerf; // time when previous frame rendered
|
||||
|
||||
// debug...
|
||||
int m_idFrameAvg;
|
||||
int m_idWaitAvg;
|
||||
#endif
|
||||
|
||||
// PROPERTY PAGE
|
||||
// This has edit fields that show the user what's happening
|
||||
// These member variables hold these counts.
|
||||
|
||||
int m_cFramesDropped; // cumulative frames dropped IN THE RENDERER
|
||||
int m_cFramesDrawn; // Frames since streaming started seen BY THE
|
||||
// RENDERER (some may be dropped upstream)
|
||||
|
||||
// Next two support average sync offset and standard deviation of sync offset.
|
||||
LONGLONG m_iTotAcc; // Sum of accuracies in mSec
|
||||
LONGLONG m_iSumSqAcc; // Sum of squares of (accuracies in mSec)
|
||||
|
||||
// Next two allow jitter calculation. Jitter is std deviation of frame time.
|
||||
REFERENCE_TIME m_trLastDraw; // Time of prev frame (for inter-frame times)
|
||||
LONGLONG m_iSumSqFrameTime; // Sum of squares of (inter-frame time in mSec)
|
||||
LONGLONG m_iSumFrameTime; // Sum of inter-frame times in mSec
|
||||
|
||||
// To get performance statistics on frame rate, jitter etc, we need
|
||||
// to record the lateness and inter-frame time. What we actually need are the
|
||||
// data above (sum, sum of squares and number of entries for each) but the data
|
||||
// is generated just ahead of time and only later do we discover whether the
|
||||
// frame was actually drawn or not. So we have to hang on to the data
|
||||
int m_trLate; // hold onto frame lateness
|
||||
int m_trFrame; // hold onto inter-frame time
|
||||
|
||||
int m_tStreamingStart; // if streaming then time streaming started
|
||||
// else time of last streaming session
|
||||
// used for property page statistics
|
||||
#ifdef PERF
|
||||
LONGLONG m_llTimeOffset; // timeGetTime()*10000+m_llTimeOffset==ref time
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
|
||||
CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer
|
||||
__in_opt LPCTSTR pName, // Debug ONLY description
|
||||
__inout_opt LPUNKNOWN pUnk, // Aggregated owner object
|
||||
__inout HRESULT *phr); // General OLE return code
|
||||
|
||||
~CBaseVideoRenderer();
|
||||
|
||||
// IQualityControl methods - Notify allows audio-video throttling
|
||||
|
||||
STDMETHODIMP SetSink( IQualityControl * piqc);
|
||||
STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q);
|
||||
|
||||
// These provide a full video quality management implementation
|
||||
|
||||
void OnRenderStart(IMediaSample *pMediaSample);
|
||||
void OnRenderEnd(IMediaSample *pMediaSample);
|
||||
void OnWaitStart();
|
||||
void OnWaitEnd();
|
||||
HRESULT OnStartStreaming();
|
||||
HRESULT OnStopStreaming();
|
||||
void ThrottleWait();
|
||||
|
||||
// Handle the statistics gathering for our quality management
|
||||
|
||||
void PreparePerformanceData(int trLate, int trFrame);
|
||||
virtual void RecordFrameLateness(int trLate, int trFrame);
|
||||
virtual void OnDirectRender(IMediaSample *pMediaSample);
|
||||
virtual HRESULT ResetStreamingTimes();
|
||||
BOOL ScheduleSample(IMediaSample *pMediaSample);
|
||||
HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
|
||||
__inout REFERENCE_TIME *ptrStart,
|
||||
__inout REFERENCE_TIME *ptrEnd);
|
||||
|
||||
virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream);
|
||||
STDMETHODIMP JoinFilterGraph(__inout_opt IFilterGraph * pGraph, __in_opt LPCWSTR pName);
|
||||
|
||||
//
|
||||
// Do estimates for standard deviations for per-frame
|
||||
// statistics
|
||||
//
|
||||
// *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /
|
||||
// (m_cFramesDrawn - 2)
|
||||
// or 0 if m_cFramesDrawn <= 3
|
||||
//
|
||||
HRESULT GetStdDev(
|
||||
int nSamples,
|
||||
__out int *piResult,
|
||||
LONGLONG llSumSq,
|
||||
LONGLONG iTot
|
||||
);
|
||||
public:
|
||||
|
||||
// IQualProp property page support
|
||||
|
||||
STDMETHODIMP get_FramesDroppedInRenderer(__out int *cFramesDropped);
|
||||
STDMETHODIMP get_FramesDrawn(__out int *pcFramesDrawn);
|
||||
STDMETHODIMP get_AvgFrameRate(__out int *piAvgFrameRate);
|
||||
STDMETHODIMP get_Jitter(__out int *piJitter);
|
||||
STDMETHODIMP get_AvgSyncOffset(__out int *piAvg);
|
||||
STDMETHODIMP get_DevSyncOffset(__out int *piDev);
|
||||
|
||||
// Implement an IUnknown interface and expose IQualProp
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv);
|
||||
};
|
||||
|
||||
#endif // __RENBASE__
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Schedule.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
|
||||
// DbgLog values (all on LOG_TIMING):
|
||||
//
|
||||
// 2 for schedulting, firing and shunting of events
|
||||
// 3 for wait delays and wake-up times of event thread
|
||||
// 4 for details of whats on the list when the thread awakes
|
||||
|
||||
/* Construct & destructors */
|
||||
|
||||
CAMSchedule::CAMSchedule( HANDLE ev )
|
||||
: CBaseObject(TEXT("CAMSchedule"))
|
||||
, head(&z, 0), z(0, MAX_TIME)
|
||||
, m_dwNextCookie(0), m_dwAdviseCount(0)
|
||||
, m_pAdviseCache(0), m_dwCacheCount(0)
|
||||
, m_ev( ev )
|
||||
{
|
||||
head.m_dwAdviseCookie = z.m_dwAdviseCookie = 0;
|
||||
}
|
||||
|
||||
CAMSchedule::~CAMSchedule()
|
||||
{
|
||||
m_Serialize.Lock();
|
||||
|
||||
// Delete cache
|
||||
CAdvisePacket * p = m_pAdviseCache;
|
||||
while (p)
|
||||
{
|
||||
CAdvisePacket *const p_next = p->m_next;
|
||||
delete p;
|
||||
p = p_next;
|
||||
}
|
||||
|
||||
ASSERT( m_dwAdviseCount == 0 );
|
||||
// Better to be safe than sorry
|
||||
if ( m_dwAdviseCount > 0 )
|
||||
{
|
||||
DumpLinkedList();
|
||||
while ( !head.m_next->IsZ() )
|
||||
{
|
||||
head.DeleteNext();
|
||||
--m_dwAdviseCount;
|
||||
}
|
||||
}
|
||||
|
||||
// If, in the debug version, we assert twice, it means, not only
|
||||
// did we have left over advises, but we have also let m_dwAdviseCount
|
||||
// get out of sync. with the number of advises actually on the list.
|
||||
ASSERT( m_dwAdviseCount == 0 );
|
||||
|
||||
m_Serialize.Unlock();
|
||||
}
|
||||
|
||||
/* Public methods */
|
||||
|
||||
DWORD CAMSchedule::GetAdviseCount()
|
||||
{
|
||||
// No need to lock, m_dwAdviseCount is 32bits & declared volatile
|
||||
return m_dwAdviseCount;
|
||||
}
|
||||
|
||||
REFERENCE_TIME CAMSchedule::GetNextAdviseTime()
|
||||
{
|
||||
CAutoLock lck(&m_Serialize); // Need to stop the linked list from changing
|
||||
return head.m_next->m_rtEventTime;
|
||||
}
|
||||
|
||||
DWORD_PTR CAMSchedule::AddAdvisePacket
|
||||
( const REFERENCE_TIME & time1
|
||||
, const REFERENCE_TIME & time2
|
||||
, HANDLE h, BOOL periodic
|
||||
)
|
||||
{
|
||||
// Since we use MAX_TIME as a sentry, we can't afford to
|
||||
// schedule a notification at MAX_TIME
|
||||
ASSERT( time1 < MAX_TIME );
|
||||
DWORD_PTR Result;
|
||||
CAdvisePacket * p;
|
||||
|
||||
m_Serialize.Lock();
|
||||
|
||||
if (m_pAdviseCache)
|
||||
{
|
||||
p = m_pAdviseCache;
|
||||
m_pAdviseCache = p->m_next;
|
||||
--m_dwCacheCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = new CAdvisePacket();
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
p->m_rtEventTime = time1; p->m_rtPeriod = time2;
|
||||
p->m_hNotify = h; p->m_bPeriodic = periodic;
|
||||
Result = AddAdvisePacket( p );
|
||||
}
|
||||
else Result = 0;
|
||||
|
||||
m_Serialize.Unlock();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
HRESULT CAMSchedule::Unadvise(DWORD_PTR dwAdviseCookie)
|
||||
{
|
||||
HRESULT hr = S_FALSE;
|
||||
CAdvisePacket * p_prev = &head;
|
||||
CAdvisePacket * p_n;
|
||||
m_Serialize.Lock();
|
||||
while ( p_n = p_prev->Next() ) // The Next() method returns NULL when it hits z
|
||||
{
|
||||
if ( p_n->m_dwAdviseCookie == dwAdviseCookie )
|
||||
{
|
||||
Delete( p_prev->RemoveNext() );
|
||||
--m_dwAdviseCount;
|
||||
hr = S_OK;
|
||||
// Having found one cookie that matches, there should be no more
|
||||
#ifdef DEBUG
|
||||
while (p_n = p_prev->Next())
|
||||
{
|
||||
ASSERT(p_n->m_dwAdviseCookie != dwAdviseCookie);
|
||||
p_prev = p_n;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
p_prev = p_n;
|
||||
};
|
||||
m_Serialize.Unlock();
|
||||
return hr;
|
||||
}
|
||||
|
||||
REFERENCE_TIME CAMSchedule::Advise( const REFERENCE_TIME & rtTime )
|
||||
{
|
||||
REFERENCE_TIME rtNextTime;
|
||||
CAdvisePacket * pAdvise;
|
||||
|
||||
DbgLog((LOG_TIMING, 2,
|
||||
TEXT("CAMSchedule::Advise( %lu ms )"), ULONG(rtTime / (UNITS / MILLISECONDS))));
|
||||
|
||||
CAutoLock lck(&m_Serialize);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (DbgCheckModuleLevel(LOG_TIMING, 4)) DumpLinkedList();
|
||||
#endif
|
||||
|
||||
// Note - DON'T cache the difference, it might overflow
|
||||
while ( rtTime >= (rtNextTime = (pAdvise=head.m_next)->m_rtEventTime) &&
|
||||
!pAdvise->IsZ() )
|
||||
{
|
||||
ASSERT(pAdvise->m_dwAdviseCookie); // If this is zero, its the head or the tail!!
|
||||
|
||||
ASSERT(pAdvise->m_hNotify != INVALID_HANDLE_VALUE);
|
||||
|
||||
if (pAdvise->m_bPeriodic == TRUE)
|
||||
{
|
||||
ReleaseSemaphore(pAdvise->m_hNotify,1,NULL);
|
||||
pAdvise->m_rtEventTime += pAdvise->m_rtPeriod;
|
||||
ShuntHead();
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT( pAdvise->m_bPeriodic == FALSE );
|
||||
EXECUTE_ASSERT(SetEvent(pAdvise->m_hNotify));
|
||||
--m_dwAdviseCount;
|
||||
Delete( head.RemoveNext() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DbgLog((LOG_TIMING, 3,
|
||||
TEXT("CAMSchedule::Advise() Next time stamp: %lu ms, for advise %lu."),
|
||||
DWORD(rtNextTime / (UNITS / MILLISECONDS)), pAdvise->m_dwAdviseCookie ));
|
||||
|
||||
return rtNextTime;
|
||||
}
|
||||
|
||||
/* Private methods */
|
||||
|
||||
DWORD_PTR CAMSchedule::AddAdvisePacket( __inout CAdvisePacket * pPacket )
|
||||
{
|
||||
ASSERT(pPacket->m_rtEventTime >= 0 && pPacket->m_rtEventTime < MAX_TIME);
|
||||
ASSERT(CritCheckIn(&m_Serialize));
|
||||
|
||||
CAdvisePacket * p_prev = &head;
|
||||
CAdvisePacket * p_n;
|
||||
|
||||
const DWORD_PTR Result = pPacket->m_dwAdviseCookie = ++m_dwNextCookie;
|
||||
// This relies on the fact that z is a sentry with a maximal m_rtEventTime
|
||||
for(;;p_prev = p_n)
|
||||
{
|
||||
p_n = p_prev->m_next;
|
||||
if ( p_n->m_rtEventTime >= pPacket->m_rtEventTime ) break;
|
||||
}
|
||||
p_prev->InsertAfter( pPacket );
|
||||
++m_dwAdviseCount;
|
||||
|
||||
DbgLog((LOG_TIMING, 2, TEXT("Added advise %lu, for thread 0x%02X, scheduled at %lu"),
|
||||
pPacket->m_dwAdviseCookie, GetCurrentThreadId(), (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
|
||||
|
||||
// If packet added at the head, then clock needs to re-evaluate wait time.
|
||||
if ( p_prev == &head ) SetEvent( m_ev );
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
void CAMSchedule::Delete( __inout CAdvisePacket * pPacket )
|
||||
{
|
||||
if ( m_dwCacheCount >= dwCacheMax ) delete pPacket;
|
||||
else
|
||||
{
|
||||
m_Serialize.Lock();
|
||||
pPacket->m_next = m_pAdviseCache;
|
||||
m_pAdviseCache = pPacket;
|
||||
++m_dwCacheCount;
|
||||
m_Serialize.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Takes the head of the list & repositions it
|
||||
void CAMSchedule::ShuntHead()
|
||||
{
|
||||
CAdvisePacket * p_prev = &head;
|
||||
CAdvisePacket * p_n;
|
||||
|
||||
m_Serialize.Lock();
|
||||
CAdvisePacket *const pPacket = head.m_next;
|
||||
|
||||
// This will catch both an empty list,
|
||||
// and if somehow a MAX_TIME time gets into the list
|
||||
// (which would also break this method).
|
||||
ASSERT( pPacket->m_rtEventTime < MAX_TIME );
|
||||
|
||||
// This relies on the fact that z is a sentry with a maximal m_rtEventTime
|
||||
for(;;p_prev = p_n)
|
||||
{
|
||||
p_n = p_prev->m_next;
|
||||
if ( p_n->m_rtEventTime > pPacket->m_rtEventTime ) break;
|
||||
}
|
||||
// If p_prev == pPacket then we're already in the right place
|
||||
if (p_prev != pPacket)
|
||||
{
|
||||
head.m_next = pPacket->m_next;
|
||||
(p_prev->m_next = pPacket)->m_next = p_n;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
DbgLog((LOG_TIMING, 2, TEXT("Periodic advise %lu, shunted to %lu"),
|
||||
pPacket->m_dwAdviseCookie, (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
|
||||
#endif
|
||||
m_Serialize.Unlock();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void CAMSchedule::DumpLinkedList()
|
||||
{
|
||||
m_Serialize.Lock();
|
||||
int i=0;
|
||||
DbgLog((LOG_TIMING, 1, TEXT("CAMSchedule::DumpLinkedList() this = 0x%p"), this));
|
||||
for ( CAdvisePacket * p = &head
|
||||
; p
|
||||
; p = p->m_next , i++
|
||||
)
|
||||
{
|
||||
DbgLog((LOG_TIMING, 1, TEXT("Advise List # %lu, Cookie %d, RefTime %lu"),
|
||||
i,
|
||||
p->m_dwAdviseCookie,
|
||||
p->m_rtEventTime / (UNITS / MILLISECONDS)
|
||||
));
|
||||
}
|
||||
m_Serialize.Unlock();
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,128 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Schedule.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __CAMSchedule__
|
||||
#define __CAMSchedule__
|
||||
|
||||
class CAMSchedule : private CBaseObject
|
||||
{
|
||||
public:
|
||||
virtual ~CAMSchedule();
|
||||
// ev is the event we should fire if the advise time needs re-evaluating
|
||||
CAMSchedule( HANDLE ev );
|
||||
|
||||
DWORD GetAdviseCount();
|
||||
REFERENCE_TIME GetNextAdviseTime();
|
||||
|
||||
// We need a method for derived classes to add advise packets, we return the cookie
|
||||
DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic );
|
||||
// And a way to cancel
|
||||
HRESULT Unadvise(DWORD_PTR dwAdviseCookie);
|
||||
|
||||
// Tell us the time please, and we'll dispatch the expired events. We return the time of the next event.
|
||||
// NB: The time returned will be "useless" if you start adding extra Advises. But that's the problem of
|
||||
// whoever is using this helper class (typically a clock).
|
||||
REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime );
|
||||
|
||||
// Get the event handle which will be set if advise time requires re-evaluation.
|
||||
HANDLE GetEvent() const { return m_ev; }
|
||||
|
||||
private:
|
||||
// We define the nodes that will be used in our singly linked list
|
||||
// of advise packets. The list is ordered by time, with the
|
||||
// elements that will expire first at the front.
|
||||
class CAdvisePacket
|
||||
{
|
||||
public:
|
||||
CAdvisePacket()
|
||||
{}
|
||||
|
||||
CAdvisePacket * m_next;
|
||||
DWORD_PTR m_dwAdviseCookie;
|
||||
REFERENCE_TIME m_rtEventTime; // Time at which event should be set
|
||||
REFERENCE_TIME m_rtPeriod; // Periodic time
|
||||
HANDLE m_hNotify; // Handle to event or semephore
|
||||
BOOL m_bPeriodic; // TRUE => Periodic event
|
||||
|
||||
CAdvisePacket( __inout_opt CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time)
|
||||
{}
|
||||
|
||||
void InsertAfter( __inout CAdvisePacket * p )
|
||||
{
|
||||
p->m_next = m_next;
|
||||
m_next = p;
|
||||
}
|
||||
|
||||
int IsZ() const // That is, is it the node that represents the end of the list
|
||||
{ return m_next == 0; }
|
||||
|
||||
CAdvisePacket * RemoveNext()
|
||||
{
|
||||
CAdvisePacket *const next = m_next;
|
||||
CAdvisePacket *const new_next = next->m_next;
|
||||
m_next = new_next;
|
||||
return next;
|
||||
}
|
||||
|
||||
void DeleteNext()
|
||||
{
|
||||
delete RemoveNext();
|
||||
}
|
||||
|
||||
CAdvisePacket * Next() const
|
||||
{
|
||||
CAdvisePacket * result = m_next;
|
||||
if (result->IsZ()) result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD_PTR Cookie() const
|
||||
{ return m_dwAdviseCookie; }
|
||||
};
|
||||
|
||||
// Structure is:
|
||||
// head -> elmt1 -> elmt2 -> z -> null
|
||||
// So an empty list is: head -> z -> null
|
||||
// Having head & z as links makes insertaion,
|
||||
// deletion and shunting much easier.
|
||||
CAdvisePacket head, z; // z is both a tail and a sentry
|
||||
|
||||
volatile DWORD_PTR m_dwNextCookie; // Strictly increasing
|
||||
volatile DWORD m_dwAdviseCount; // Number of elements on list
|
||||
|
||||
CCritSec m_Serialize;
|
||||
|
||||
// AddAdvisePacket: adds the packet, returns the cookie (0 if failed)
|
||||
DWORD_PTR AddAdvisePacket( __inout CAdvisePacket * pPacket );
|
||||
// Event that we should set if the packed added above will be the next to fire.
|
||||
const HANDLE m_ev;
|
||||
|
||||
// A Shunt is where we have changed the first element in the
|
||||
// list and want it re-evaluating (i.e. repositioned) in
|
||||
// the list.
|
||||
void ShuntHead();
|
||||
|
||||
// Rather than delete advise packets, we cache them for future use
|
||||
CAdvisePacket * m_pAdviseCache;
|
||||
DWORD m_dwCacheCount;
|
||||
enum { dwCacheMax = 5 }; // Don't bother caching more than five
|
||||
|
||||
void Delete( __inout CAdvisePacket * pLink );// This "Delete" will cache the Link
|
||||
|
||||
// Attributes and methods for debugging
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
void DumpLinkedList();
|
||||
#else
|
||||
void DumpLinkedList() {}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // __CAMSchedule__
|
|
@ -0,0 +1,83 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: SeekPT.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include "seekpt.h"
|
||||
|
||||
//==================================================================
|
||||
// CreateInstance
|
||||
// This goes in the factory template table to create new instances
|
||||
// If there is already a mapper instance - return that, else make one
|
||||
// and save it in a static variable so that forever after we can return that.
|
||||
//==================================================================
|
||||
|
||||
CUnknown * CSeekingPassThru::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)
|
||||
{
|
||||
return new CSeekingPassThru(NAME("Seeking PassThru"),pUnk, phr);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CSeekingPassThru::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
|
||||
{
|
||||
if (riid == IID_ISeekingPassThru) {
|
||||
return GetInterface((ISeekingPassThru *) this, ppv);
|
||||
} else {
|
||||
if (m_pPosPassThru &&
|
||||
(riid == IID_IMediaSeeking ||
|
||||
riid == IID_IMediaPosition)) {
|
||||
return m_pPosPassThru->NonDelegatingQueryInterface(riid,ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSeekingPassThru::CSeekingPassThru( __in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr )
|
||||
: CUnknown(pName, pUnk, phr),
|
||||
m_pPosPassThru(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CSeekingPassThru::~CSeekingPassThru()
|
||||
{
|
||||
delete m_pPosPassThru;
|
||||
}
|
||||
|
||||
STDMETHODIMP CSeekingPassThru::Init(BOOL bRendererSeeking, IPin *pPin)
|
||||
{
|
||||
HRESULT hr = NOERROR;
|
||||
if (m_pPosPassThru) {
|
||||
hr = E_FAIL;
|
||||
} else {
|
||||
m_pPosPassThru =
|
||||
bRendererSeeking ?
|
||||
new CRendererPosPassThru(
|
||||
NAME("Render Seeking COM object"),
|
||||
(IUnknown *)this,
|
||||
&hr,
|
||||
pPin) :
|
||||
new CPosPassThru(
|
||||
NAME("Render Seeking COM object"),
|
||||
(IUnknown *)this,
|
||||
&hr,
|
||||
pPin);
|
||||
if (!m_pPosPassThru) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
} else {
|
||||
if (FAILED(hr)) {
|
||||
delete m_pPosPassThru;
|
||||
m_pPosPassThru = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: SeekPT.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __seekpt_h__
|
||||
#define __seekpt_h__
|
||||
|
||||
|
||||
class CSeekingPassThru : public ISeekingPassThru, public CUnknown
|
||||
{
|
||||
public:
|
||||
static CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
|
||||
CSeekingPassThru(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
|
||||
~CSeekingPassThru();
|
||||
|
||||
DECLARE_IUNKNOWN;
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
|
||||
|
||||
STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);
|
||||
|
||||
private:
|
||||
CPosPassThru *m_pPosPassThru;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,522 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Source.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements CSource, which is a Quartz
|
||||
// source filter 'template.'
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Locking Strategy.
|
||||
//
|
||||
// Hold the filter critical section (m_pFilter->pStateLock()) to serialise
|
||||
// access to functions. Note that, in general, this lock may be held
|
||||
// by a function when the worker thread may want to hold it. Therefore
|
||||
// if you wish to access shared state from the worker thread you will
|
||||
// need to add another critical section object. The execption is during
|
||||
// the threads processing loop, when it is safe to get the filter critical
|
||||
// section from within FillBuffer().
|
||||
|
||||
#include <streams.h>
|
||||
|
||||
|
||||
//
|
||||
// CSource::Constructor
|
||||
//
|
||||
// Initialise the pin count for the filter. The user will create the pins in
|
||||
// the derived class.
|
||||
CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
|
||||
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
|
||||
m_iPins(0),
|
||||
m_paStreams(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
|
||||
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
|
||||
m_iPins(0),
|
||||
m_paStreams(NULL)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(phr);
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
|
||||
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
|
||||
m_iPins(0),
|
||||
m_paStreams(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
|
||||
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
|
||||
m_iPins(0),
|
||||
m_paStreams(NULL)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(phr);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// CSource::Destructor
|
||||
//
|
||||
CSource::~CSource()
|
||||
{
|
||||
/* Free our pins and pin array */
|
||||
while (m_iPins != 0) {
|
||||
// deleting the pins causes them to be removed from the array...
|
||||
delete m_paStreams[m_iPins - 1];
|
||||
}
|
||||
|
||||
ASSERT(m_paStreams == NULL);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Add a new pin
|
||||
//
|
||||
HRESULT CSource::AddPin(__in CSourceStream *pStream)
|
||||
{
|
||||
CAutoLock lock(&m_cStateLock);
|
||||
|
||||
/* Allocate space for this pin and the old ones */
|
||||
CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];
|
||||
if (paStreams == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
if (m_paStreams != NULL) {
|
||||
CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,
|
||||
m_iPins * sizeof(m_paStreams[0]));
|
||||
paStreams[m_iPins] = pStream;
|
||||
delete [] m_paStreams;
|
||||
}
|
||||
m_paStreams = paStreams;
|
||||
m_paStreams[m_iPins] = pStream;
|
||||
m_iPins++;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Remove a pin - pStream is NOT deleted
|
||||
//
|
||||
HRESULT CSource::RemovePin(__in CSourceStream *pStream)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < m_iPins; i++) {
|
||||
if (m_paStreams[i] == pStream) {
|
||||
if (m_iPins == 1) {
|
||||
delete [] m_paStreams;
|
||||
m_paStreams = NULL;
|
||||
} else {
|
||||
/* no need to reallocate */
|
||||
while (++i < m_iPins)
|
||||
m_paStreams[i - 1] = m_paStreams[i];
|
||||
}
|
||||
m_iPins--;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// FindPin
|
||||
//
|
||||
// Set *ppPin to the IPin* that has the id Id.
|
||||
// or to NULL if the Id cannot be matched.
|
||||
STDMETHODIMP CSource::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)
|
||||
{
|
||||
CheckPointer(ppPin,E_POINTER);
|
||||
ValidateReadWritePtr(ppPin,sizeof(IPin *));
|
||||
// The -1 undoes the +1 in QueryId and ensures that totally invalid
|
||||
// strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
|
||||
int i = WstrToInt(Id) -1;
|
||||
*ppPin = GetPin(i);
|
||||
if (*ppPin!=NULL){
|
||||
(*ppPin)->AddRef();
|
||||
return NOERROR;
|
||||
} else {
|
||||
return VFW_E_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FindPinNumber
|
||||
//
|
||||
// return the number of the pin with this IPin* or -1 if none
|
||||
int CSource::FindPinNumber(__in IPin *iPin) {
|
||||
int i;
|
||||
for (i=0; i<m_iPins; ++i) {
|
||||
if ((IPin *)(m_paStreams[i])==iPin) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// GetPinCount
|
||||
//
|
||||
// Returns the number of pins this filter has
|
||||
int CSource::GetPinCount(void) {
|
||||
|
||||
CAutoLock lock(&m_cStateLock);
|
||||
return m_iPins;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GetPin
|
||||
//
|
||||
// Return a non-addref'd pointer to pin n
|
||||
// needed by CBaseFilter
|
||||
CBasePin *CSource::GetPin(int n) {
|
||||
|
||||
CAutoLock lock(&m_cStateLock);
|
||||
|
||||
// n must be in the range 0..m_iPins-1
|
||||
// if m_iPins>n && n>=0 it follows that m_iPins>0
|
||||
// which is what used to be checked (i.e. checking that we have a pin)
|
||||
if ((n >= 0) && (n < m_iPins)) {
|
||||
|
||||
ASSERT(m_paStreams[n]);
|
||||
return m_paStreams[n];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
// *
|
||||
// * --- CSourceStream ----
|
||||
// *
|
||||
|
||||
//
|
||||
// Set Id to point to a CoTaskMemAlloc'd
|
||||
STDMETHODIMP CSourceStream::QueryId(__deref_out LPWSTR *Id) {
|
||||
CheckPointer(Id,E_POINTER);
|
||||
ValidateReadWritePtr(Id,sizeof(LPWSTR));
|
||||
|
||||
// We give the pins id's which are 1,2,...
|
||||
// FindPinNumber returns -1 for an invalid pin
|
||||
int i = 1+ m_pFilter->FindPinNumber(this);
|
||||
if (i<1) return VFW_E_NOT_FOUND;
|
||||
*Id = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * 12);
|
||||
if (*Id==NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
IntToWstr(i, *Id);
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CSourceStream::Constructor
|
||||
//
|
||||
// increments the number of pins present on the filter
|
||||
CSourceStream::CSourceStream(
|
||||
__in_opt LPCTSTR pObjectName,
|
||||
__inout HRESULT *phr,
|
||||
__inout CSource *ps,
|
||||
__in_opt LPCWSTR pPinName)
|
||||
: CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
|
||||
m_pFilter(ps) {
|
||||
|
||||
*phr = m_pFilter->AddPin(this);
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CSourceStream::CSourceStream(
|
||||
__in_opt LPCSTR pObjectName,
|
||||
__inout HRESULT *phr,
|
||||
__inout CSource *ps,
|
||||
__in_opt LPCWSTR pPinName)
|
||||
: CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
|
||||
m_pFilter(ps) {
|
||||
|
||||
*phr = m_pFilter->AddPin(this);
|
||||
}
|
||||
#endif
|
||||
//
|
||||
// CSourceStream::Destructor
|
||||
//
|
||||
// Decrements the number of pins on this filter
|
||||
CSourceStream::~CSourceStream(void) {
|
||||
|
||||
m_pFilter->RemovePin(this);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CheckMediaType
|
||||
//
|
||||
// Do we support this type? Provides the default support for 1 type.
|
||||
HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
CMediaType mt;
|
||||
GetMediaType(&mt);
|
||||
|
||||
if (mt == *pMediaType) {
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GetMediaType/3
|
||||
//
|
||||
// By default we support only one type
|
||||
// iPosition indexes are 0-n
|
||||
HRESULT CSourceStream::GetMediaType(int iPosition, __inout CMediaType *pMediaType) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
if (iPosition<0) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (iPosition>0) {
|
||||
return VFW_S_NO_MORE_ITEMS;
|
||||
}
|
||||
return GetMediaType(pMediaType);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Active
|
||||
//
|
||||
// The pin is active - start up the worker thread
|
||||
HRESULT CSourceStream::Active(void) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
if (m_pFilter->IsActive()) {
|
||||
return S_FALSE; // succeeded, but did not allocate resources (they already exist...)
|
||||
}
|
||||
|
||||
// do nothing if not connected - its ok not to connect to
|
||||
// all pins of a source filter
|
||||
if (!IsConnected()) {
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
hr = CBaseOutputPin::Active();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
ASSERT(!ThreadExists());
|
||||
|
||||
// start the thread
|
||||
if (!Create()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Tell thread to initialize. If OnThreadCreate Fails, so does this.
|
||||
hr = Init();
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
return Pause();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inactive
|
||||
//
|
||||
// Pin is inactive - shut down the worker thread
|
||||
// Waits for the worker to exit before returning.
|
||||
HRESULT CSourceStream::Inactive(void) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
// do nothing if not connected - its ok not to connect to
|
||||
// all pins of a source filter
|
||||
if (!IsConnected()) {
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// !!! need to do this before trying to stop the thread, because
|
||||
// we may be stuck waiting for our own allocator!!!
|
||||
|
||||
hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (ThreadExists()) {
|
||||
hr = Stop();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = Exit();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
Close(); // Wait for the thread to exit, then tidy up.
|
||||
}
|
||||
|
||||
// hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
|
||||
//if (FAILED(hr)) {
|
||||
// return hr;
|
||||
//}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ThreadProc
|
||||
//
|
||||
// When this returns the thread exits
|
||||
// Return codes > 0 indicate an error occured
|
||||
DWORD CSourceStream::ThreadProc(void) {
|
||||
|
||||
HRESULT hr; // the return code from calls
|
||||
Command com;
|
||||
|
||||
do {
|
||||
com = GetRequest();
|
||||
if (com != CMD_INIT) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
|
||||
Reply((DWORD) E_UNEXPECTED);
|
||||
}
|
||||
} while (com != CMD_INIT);
|
||||
|
||||
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
|
||||
|
||||
hr = OnThreadCreate(); // perform set up tasks
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
|
||||
OnThreadDestroy();
|
||||
Reply(hr); // send failed return code from OnThreadCreate
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialisation suceeded
|
||||
Reply(NOERROR);
|
||||
|
||||
Command cmd;
|
||||
do {
|
||||
cmd = GetRequest();
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case CMD_EXIT:
|
||||
Reply(NOERROR);
|
||||
break;
|
||||
|
||||
case CMD_RUN:
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
|
||||
// !!! fall through???
|
||||
|
||||
case CMD_PAUSE:
|
||||
Reply(NOERROR);
|
||||
DoBufferProcessingLoop();
|
||||
break;
|
||||
|
||||
case CMD_STOP:
|
||||
Reply(NOERROR);
|
||||
break;
|
||||
|
||||
default:
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
|
||||
Reply((DWORD) E_NOTIMPL);
|
||||
break;
|
||||
}
|
||||
} while (cmd != CMD_EXIT);
|
||||
|
||||
hr = OnThreadDestroy(); // tidy up.
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DoBufferProcessingLoop
|
||||
//
|
||||
// Grabs a buffer and calls the users processing function.
|
||||
// Overridable, so that different delivery styles can be catered for.
|
||||
HRESULT CSourceStream::DoBufferProcessingLoop(void) {
|
||||
|
||||
Command com;
|
||||
|
||||
OnThreadStartPlay();
|
||||
|
||||
do {
|
||||
while (!CheckRequest(&com)) {
|
||||
|
||||
IMediaSample *pSample;
|
||||
|
||||
HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
|
||||
if (FAILED(hr)) {
|
||||
Sleep(1);
|
||||
continue; // go round again. Perhaps the error will go away
|
||||
// or the allocator is decommited & we will be asked to
|
||||
// exit soon.
|
||||
}
|
||||
|
||||
// Virtual function user will override.
|
||||
hr = FillBuffer(pSample);
|
||||
|
||||
if (hr == S_OK) {
|
||||
hr = Deliver(pSample);
|
||||
pSample->Release();
|
||||
|
||||
// downstream filter returns S_FALSE if it wants us to
|
||||
// stop or an error if it's reporting an error.
|
||||
if(hr != S_OK)
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} else if (hr == S_FALSE) {
|
||||
// derived class wants us to stop pushing data
|
||||
pSample->Release();
|
||||
DeliverEndOfStream();
|
||||
return S_OK;
|
||||
} else {
|
||||
// derived class encountered an error
|
||||
pSample->Release();
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
|
||||
DeliverEndOfStream();
|
||||
m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// all paths release the sample
|
||||
}
|
||||
|
||||
// For all commands sent to us there must be a Reply call!
|
||||
|
||||
if (com == CMD_RUN || com == CMD_PAUSE) {
|
||||
Reply(NOERROR);
|
||||
} else if (com != CMD_STOP) {
|
||||
Reply((DWORD) E_UNEXPECTED);
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
|
||||
}
|
||||
} while (com != CMD_STOP);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Source.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines classes to simplify creation of
|
||||
// ActiveX source filters that support continuous generation of data.
|
||||
// No support is provided for IMediaControl or IMediaPosition.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// Derive your source filter from CSource.
|
||||
// During construction either:
|
||||
// Create some CSourceStream objects to manage your pins
|
||||
// Provide the user with a means of doing so eg, an IPersistFile interface.
|
||||
//
|
||||
// CSource provides:
|
||||
// IBaseFilter interface management
|
||||
// IMediaFilter interface management, via CBaseFilter
|
||||
// Pin counting for CBaseFilter
|
||||
//
|
||||
// Derive a class from CSourceStream to manage your output pin types
|
||||
// Implement GetMediaType/1 to return the type you support. If you support multiple
|
||||
// types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount.
|
||||
// Implement Fillbuffer() to put data into one buffer.
|
||||
//
|
||||
// CSourceStream provides:
|
||||
// IPin management via CBaseOutputPin
|
||||
// Worker thread management
|
||||
|
||||
#ifndef __CSOURCE__
|
||||
#define __CSOURCE__
|
||||
|
||||
class CSourceStream; // The class that will handle each pin
|
||||
|
||||
|
||||
//
|
||||
// CSource
|
||||
//
|
||||
// Override construction to provide a means of creating
|
||||
// CSourceStream derived objects - ie a way of creating pins.
|
||||
class CSource : public CBaseFilter {
|
||||
public:
|
||||
|
||||
CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr);
|
||||
CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid);
|
||||
#ifdef UNICODE
|
||||
CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr);
|
||||
CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid);
|
||||
#endif
|
||||
~CSource();
|
||||
|
||||
int GetPinCount(void);
|
||||
CBasePin *GetPin(int n);
|
||||
|
||||
// -- Utilities --
|
||||
|
||||
CCritSec* pStateLock(void) { return &m_cStateLock; } // provide our critical section
|
||||
|
||||
HRESULT AddPin(__in CSourceStream *);
|
||||
HRESULT RemovePin(__in CSourceStream *);
|
||||
|
||||
STDMETHODIMP FindPin(
|
||||
LPCWSTR Id,
|
||||
__deref_out IPin ** ppPin
|
||||
);
|
||||
|
||||
int FindPinNumber(__in IPin *iPin);
|
||||
|
||||
protected:
|
||||
|
||||
int m_iPins; // The number of pins on this filter. Updated by CSourceStream
|
||||
// constructors & destructors.
|
||||
CSourceStream **m_paStreams; // the pins on this filter.
|
||||
|
||||
CCritSec m_cStateLock; // Lock this to serialize function accesses to the filter state
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// CSourceStream
|
||||
//
|
||||
// Use this class to manage a stream of data that comes from a
|
||||
// pin.
|
||||
// Uses a worker thread to put data on the pin.
|
||||
class CSourceStream : public CAMThread, public CBaseOutputPin {
|
||||
public:
|
||||
|
||||
CSourceStream(__in_opt LPCTSTR pObjectName,
|
||||
__inout HRESULT *phr,
|
||||
__inout CSource *pms,
|
||||
__in_opt LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CSourceStream(__in_opt LPCSTR pObjectName,
|
||||
__inout HRESULT *phr,
|
||||
__inout CSource *pms,
|
||||
__in_opt LPCWSTR pName);
|
||||
#endif
|
||||
virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too.
|
||||
|
||||
protected:
|
||||
|
||||
CSource *m_pFilter; // The parent of this stream
|
||||
|
||||
// *
|
||||
// * Data Source
|
||||
// *
|
||||
// * The following three functions: FillBuffer, OnThreadCreate/Destroy, are
|
||||
// * called from within the ThreadProc. They are used in the creation of
|
||||
// * the media samples this pin will provide
|
||||
// *
|
||||
|
||||
// Override this to provide the worker thread a means
|
||||
// of processing a buffer
|
||||
virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;
|
||||
|
||||
// Called as the thread is created/destroyed - use to perform
|
||||
// jobs such as start/stop streaming mode
|
||||
// If OnThreadCreate returns an error the thread will exit.
|
||||
virtual HRESULT OnThreadCreate(void) {return NOERROR;};
|
||||
virtual HRESULT OnThreadDestroy(void) {return NOERROR;};
|
||||
virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};
|
||||
|
||||
// *
|
||||
// * Worker Thread
|
||||
// *
|
||||
|
||||
HRESULT Active(void); // Starts up the worker thread
|
||||
HRESULT Inactive(void); // Exits the worker thread.
|
||||
|
||||
public:
|
||||
// thread commands
|
||||
enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};
|
||||
HRESULT Init(void) { return CallWorker(CMD_INIT); }
|
||||
HRESULT Exit(void) { return CallWorker(CMD_EXIT); }
|
||||
HRESULT Run(void) { return CallWorker(CMD_RUN); }
|
||||
HRESULT Pause(void) { return CallWorker(CMD_PAUSE); }
|
||||
HRESULT Stop(void) { return CallWorker(CMD_STOP); }
|
||||
|
||||
protected:
|
||||
Command GetRequest(void) { return (Command) CAMThread::GetRequest(); }
|
||||
BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); }
|
||||
|
||||
// override these if you want to add thread commands
|
||||
virtual DWORD ThreadProc(void); // the thread function
|
||||
|
||||
virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running
|
||||
|
||||
|
||||
// *
|
||||
// * AM_MEDIA_TYPE support
|
||||
// *
|
||||
|
||||
// If you support more than one media type then override these 2 functions
|
||||
virtual HRESULT CheckMediaType(const CMediaType *pMediaType);
|
||||
virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType); // List pos. 0-n
|
||||
|
||||
// If you support only one type then override this fn.
|
||||
// This will only be called by the default implementations
|
||||
// of CheckMediaType and GetMediaType(int, CMediaType*)
|
||||
// You must override this fn. or the above 2!
|
||||
virtual HRESULT GetMediaType(__inout CMediaType *pMediaType) {return E_UNEXPECTED;}
|
||||
|
||||
STDMETHODIMP QueryId(
|
||||
__deref_out LPWSTR * Id
|
||||
);
|
||||
};
|
||||
|
||||
#endif // __CSOURCE__
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Streams.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines overall streams architecture.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __STREAMS__
|
||||
#define __STREAMS__
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable
|
||||
#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter
|
||||
#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
|
||||
#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated
|
||||
#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated
|
||||
#pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed"
|
||||
|
||||
#if _MSC_VER>=1100
|
||||
#define AM_NOVTABLE __declspec(novtable)
|
||||
#else
|
||||
#define AM_NOVTABLE
|
||||
#endif
|
||||
#endif // MSC_VER
|
||||
|
||||
|
||||
// Because of differences between Visual C++ and older Microsoft SDKs,
|
||||
// you may have defined _DEBUG without defining DEBUG. This logic
|
||||
// ensures that both will be set if Visual C++ sets _DEBUG.
|
||||
#ifdef _DEBUG
|
||||
#ifndef DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <olectl.h>
|
||||
#include <ddraw.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
|
||||
#ifndef NUMELMS
|
||||
#if _WIN32_WINNT < 0x0600
|
||||
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
|
||||
#else
|
||||
#define NUMELMS(aa) ARRAYSIZE(aa)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// The following definitions come from the Platform SDK and are required if
|
||||
// the applicaiton is being compiled with the headers from Visual C++ 6.0.
|
||||
/////////////////////////////////////////////////// ////////////////////////
|
||||
#ifndef InterlockedExchangePointer
|
||||
#define InterlockedExchangePointer(Target, Value) \
|
||||
(PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))
|
||||
#endif
|
||||
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
#define _WAVEFORMATEXTENSIBLE_
|
||||
typedef struct {
|
||||
WAVEFORMATEX Format;
|
||||
union {
|
||||
WORD wValidBitsPerSample; /* bits of precision */
|
||||
WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
|
||||
WORD wReserved; /* If neither applies, set to zero. */
|
||||
} Samples;
|
||||
DWORD dwChannelMask; /* which channels are */
|
||||
/* present in stream */
|
||||
GUID SubFormat;
|
||||
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
|
||||
#endif // !_WAVEFORMATEXTENSIBLE_
|
||||
|
||||
#if !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
|
||||
#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
|
||||
#ifndef GetWindowLongPtr
|
||||
#define GetWindowLongPtrA GetWindowLongA
|
||||
#define GetWindowLongPtrW GetWindowLongW
|
||||
#ifdef UNICODE
|
||||
#define GetWindowLongPtr GetWindowLongPtrW
|
||||
#else
|
||||
#define GetWindowLongPtr GetWindowLongPtrA
|
||||
#endif // !UNICODE
|
||||
#endif // !GetWindowLongPtr
|
||||
|
||||
#ifndef SetWindowLongPtr
|
||||
#define SetWindowLongPtrA SetWindowLongA
|
||||
#define SetWindowLongPtrW SetWindowLongW
|
||||
#ifdef UNICODE
|
||||
#define SetWindowLongPtr SetWindowLongPtrW
|
||||
#else
|
||||
#define SetWindowLongPtr SetWindowLongPtrA
|
||||
#endif // !UNICODE
|
||||
#endif // !SetWindowLongPtr
|
||||
|
||||
#ifndef GWLP_WNDPROC
|
||||
#define GWLP_WNDPROC (-4)
|
||||
#endif
|
||||
#ifndef GWLP_HINSTANCE
|
||||
#define GWLP_HINSTANCE (-6)
|
||||
#endif
|
||||
#ifndef GWLP_HWNDPARENT
|
||||
#define GWLP_HWNDPARENT (-8)
|
||||
#endif
|
||||
#ifndef GWLP_USERDATA
|
||||
#define GWLP_USERDATA (-21)
|
||||
#endif
|
||||
#ifndef GWLP_ID
|
||||
#define GWLP_ID (-12)
|
||||
#endif
|
||||
#ifndef DWLP_MSGRESULT
|
||||
#define DWLP_MSGRESULT 0
|
||||
#endif
|
||||
#ifndef DWLP_DLGPROC
|
||||
#define DWLP_DLGPROC DWLP_MSGRESULT + sizeof(LRESULT)
|
||||
#endif
|
||||
#ifndef DWLP_USER
|
||||
#define DWLP_USER DWLP_DLGPROC + sizeof(DLGPROC)
|
||||
#endif
|
||||
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4312 4244)
|
||||
// _GetWindowLongPtr
|
||||
// Templated version of GetWindowLongPtr, to suppress spurious compiler warning.
|
||||
template <class T>
|
||||
T _GetWindowLongPtr(HWND hwnd, int nIndex)
|
||||
{
|
||||
return (T)GetWindowLongPtr(hwnd, nIndex);
|
||||
}
|
||||
|
||||
// _SetWindowLongPtr
|
||||
// Templated version of SetWindowLongPtr, to suppress spurious compiler warning.
|
||||
template <class T>
|
||||
LONG_PTR _SetWindowLongPtr(HWND hwnd, int nIndex, T p)
|
||||
{
|
||||
return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)p);
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// End Platform SDK definitions
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include <strmif.h> // Generated IDL header file for streams interfaces
|
||||
#include <intsafe.h> // required by amvideo.h
|
||||
|
||||
#include <reftime.h> // Helper class for REFERENCE_TIME management
|
||||
#include <wxdebug.h> // Debug support for logging and ASSERTs
|
||||
#include <amvideo.h> // ActiveMovie video interfaces and definitions
|
||||
//include amaudio.h explicitly if you need it. it requires the DX SDK.
|
||||
//#include <amaudio.h> // ActiveMovie audio interfaces and definitions
|
||||
#include <wxutil.h> // General helper classes for threads etc
|
||||
#include <combase.h> // Base COM classes to support IUnknown
|
||||
#include <dllsetup.h> // Filter registration support functions
|
||||
#include <measure.h> // Performance measurement
|
||||
#include <comlite.h> // Light weight com function prototypes
|
||||
|
||||
#include <cache.h> // Simple cache container class
|
||||
#include <wxlist.h> // Non MFC generic list class
|
||||
#include <msgthrd.h> // CMsgThread
|
||||
#include <mtype.h> // Helper class for managing media types
|
||||
#include <fourcc.h> // conversions between FOURCCs and GUIDs
|
||||
#include <control.h> // generated from control.odl
|
||||
#include <ctlutil.h> // control interface utility classes
|
||||
#include <evcode.h> // event code definitions
|
||||
#include <amfilter.h> // Main streams architecture class hierachy
|
||||
#include <transfrm.h> // Generic transform filter
|
||||
#include <transip.h> // Generic transform-in-place filter
|
||||
#include <uuids.h> // declaration of type GUIDs and well-known clsids
|
||||
#include <source.h> // Generic source filter
|
||||
#include <outputq.h> // Output pin queueing
|
||||
#include <errors.h> // HRESULT status and error definitions
|
||||
#include <renbase.h> // Base class for writing ActiveX renderers
|
||||
#include <winutil.h> // Helps with filters that manage windows
|
||||
#include <winctrl.h> // Implements the IVideoWindow interface
|
||||
#include <videoctl.h> // Specifically video related classes
|
||||
#include <refclock.h> // Base clock class
|
||||
#include <sysclock.h> // System clock
|
||||
#include <pstream.h> // IPersistStream helper class
|
||||
#include <vtrans.h> // Video Transform Filter base class
|
||||
#include <amextra.h>
|
||||
#include <cprop.h> // Base property page class
|
||||
#include <strmctl.h> // IAMStreamControl support
|
||||
#include <edevdefs.h> // External device control interface defines
|
||||
#include <audevcod.h> // audio filter device error event codes
|
||||
|
||||
|
||||
|
||||
#else
|
||||
#ifdef DEBUG
|
||||
#pragma message("STREAMS.H included TWICE")
|
||||
#endif
|
||||
#endif // __STREAMS__
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: StrmCtl.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <strmctl.h>
|
||||
|
||||
CBaseStreamControl::CBaseStreamControl(__inout HRESULT *phr)
|
||||
: m_StreamState(STREAM_FLOWING)
|
||||
, m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop
|
||||
, m_tStartTime(MAX_TIME)
|
||||
, m_tStopTime(MAX_TIME)
|
||||
, m_StreamEvent(FALSE, phr)
|
||||
, m_dwStartCookie(0)
|
||||
, m_dwStopCookie(0)
|
||||
, m_pRefClock(NULL)
|
||||
, m_FilterState(State_Stopped)
|
||||
, m_bIsFlushing(FALSE)
|
||||
, m_bStopSendExtra(FALSE)
|
||||
{}
|
||||
|
||||
CBaseStreamControl::~CBaseStreamControl()
|
||||
{
|
||||
// Make sure we release the clock.
|
||||
SetSyncSource(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie)
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
m_bStopSendExtra = FALSE; // reset
|
||||
m_bStopExtraSent = FALSE;
|
||||
if (ptStop)
|
||||
{
|
||||
if (*ptStop == MAX_TIME)
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));
|
||||
CancelStop();
|
||||
// If there's now a command to start in the future, we assume
|
||||
// they want to be stopped when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {
|
||||
m_StreamState = STREAM_DISCARDING;
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),
|
||||
(int)(*ptStop/10000), bSendExtra));
|
||||
// if the first command is to stop in the future, then we assume they
|
||||
// want to be started when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStartTime > *ptStop) {
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
|
||||
}
|
||||
m_bStopSendExtra = bSendExtra;
|
||||
m_tStopTime = *ptStop;
|
||||
m_dwStopCookie = dwCookie;
|
||||
m_StreamStateOnStop = STREAM_DISCARDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));
|
||||
// sending an extra frame when told to stop now would mess people up
|
||||
m_bStopSendExtra = FALSE;
|
||||
m_tStopTime = MAX_TIME;
|
||||
m_dwStopCookie = 0;
|
||||
m_StreamState = STREAM_DISCARDING;
|
||||
m_StreamStateOnStop = STREAM_FLOWING; // no pending stop
|
||||
}
|
||||
// we might change our mind what to do with a sample we're blocking
|
||||
m_StreamEvent.Set();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CBaseStreamControl::StartAt
|
||||
( const REFERENCE_TIME *ptStart, DWORD dwCookie )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
if (ptStart)
|
||||
{
|
||||
if (*ptStart == MAX_TIME)
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));
|
||||
CancelStart();
|
||||
// If there's now a command to stop in the future, we assume
|
||||
// they want to be started when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));
|
||||
// if the first command is to start in the future, then we assume they
|
||||
// want to be stopped when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
|
||||
m_StreamState = STREAM_DISCARDING;
|
||||
}
|
||||
m_tStartTime = *ptStart;
|
||||
m_dwStartCookie = dwCookie;
|
||||
// if (m_tStopTime == m_tStartTime) CancelStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));
|
||||
m_tStartTime = MAX_TIME;
|
||||
m_dwStartCookie = 0;
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
}
|
||||
// we might change our mind what to do with a sample we're blocking
|
||||
m_StreamEvent.Set();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Retrieve information about current settings
|
||||
STDMETHODIMP CBaseStreamControl::GetInfo(__out AM_STREAM_INFO *pInfo)
|
||||
{
|
||||
if (pInfo == NULL)
|
||||
return E_POINTER;
|
||||
|
||||
pInfo->tStart = m_tStartTime;
|
||||
pInfo->tStop = m_tStopTime;
|
||||
pInfo->dwStartCookie = m_dwStartCookie;
|
||||
pInfo->dwStopCookie = m_dwStopCookie;
|
||||
pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;
|
||||
pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;
|
||||
pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;
|
||||
switch (m_StreamState) {
|
||||
default:
|
||||
DbgBreak("Invalid stream state");
|
||||
case STREAM_FLOWING:
|
||||
break;
|
||||
case STREAM_DISCARDING:
|
||||
pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;
|
||||
break;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
void CBaseStreamControl::ExecuteStop()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_StreamState = m_StreamStateOnStop;
|
||||
if (m_dwStopCookie && m_pSink) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),
|
||||
m_dwStopCookie));
|
||||
m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);
|
||||
}
|
||||
CancelStop(); // This will do the tidy up
|
||||
}
|
||||
|
||||
void CBaseStreamControl::ExecuteStart()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
if (m_dwStartCookie) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),
|
||||
m_dwStartCookie));
|
||||
m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);
|
||||
}
|
||||
CancelStart(); // This will do the tidy up
|
||||
}
|
||||
|
||||
void CBaseStreamControl::CancelStop()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_tStopTime = MAX_TIME;
|
||||
m_dwStopCookie = 0;
|
||||
m_StreamStateOnStop = STREAM_FLOWING;
|
||||
}
|
||||
|
||||
void CBaseStreamControl::CancelStart()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_tStartTime = MAX_TIME;
|
||||
m_dwStartCookie = 0;
|
||||
}
|
||||
|
||||
|
||||
// This guy will return one of the three StreamControlState's. Here's what the caller
|
||||
// should do for each one:
|
||||
//
|
||||
// STREAM_FLOWING: Proceed as usual (render or pass the sample on)
|
||||
// STREAM_DISCARDING: Calculate the time 'til *pSampleStart and wait that long
|
||||
// for the event handle (GetStreamEventHandle()). If the
|
||||
// wait expires, throw the sample away. If the event
|
||||
// fires, call me back, I've changed my mind.
|
||||
// I use pSampleStart (not Stop) so that live sources don't
|
||||
// block for the duration of their samples, since the clock
|
||||
// will always read approximately pSampleStart when called
|
||||
|
||||
|
||||
// All through this code, you'll notice the following rules:
|
||||
// - When start and stop time are the same, it's as if start was first
|
||||
// - An event is considered inside the sample when it's >= sample start time
|
||||
// but < sample stop time
|
||||
// - if any part of the sample is supposed to be sent, we'll send the whole
|
||||
// thing since we don't break it into smaller pieces
|
||||
// - If we skip over a start or stop without doing it, we still signal the event
|
||||
// and reset ourselves in case somebody's waiting for the event, and to make
|
||||
// sure we notice that the event is past and should be forgotten
|
||||
// Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):
|
||||
//
|
||||
// 1. xo<--> start then stop
|
||||
// 2. ox<--> stop then start
|
||||
// 3. x<o-> start
|
||||
// 4. o<x-> stop then start
|
||||
// 5. x<-->o start
|
||||
// 6. o<-->x stop
|
||||
// 7. <x->o start
|
||||
// 8. <o->x no change
|
||||
// 9. <xo> start
|
||||
// 10. <ox> stop then start
|
||||
// 11. <-->xo no change
|
||||
// 12. <-->ox no change
|
||||
// 13. x<--> start
|
||||
// 14. <x-> start
|
||||
// 15. <-->x no change
|
||||
// 16. o<--> stop
|
||||
// 17. <o-> no change
|
||||
// 18. <-->o no change
|
||||
// 19. <--> no change
|
||||
|
||||
|
||||
enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes
|
||||
( __in const REFERENCE_TIME * pSampleStart, __in const REFERENCE_TIME * pSampleStop )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
|
||||
ASSERT(!m_bIsFlushing);
|
||||
ASSERT(pSampleStart && pSampleStop);
|
||||
|
||||
// Don't ask me how I came up with the code below to handle all 19 cases
|
||||
// - DannyMi
|
||||
|
||||
if (m_tStopTime >= *pSampleStart)
|
||||
{
|
||||
if (m_tStartTime >= *pSampleStop)
|
||||
return m_StreamState; // cases 8 11 12 15 17 18 19
|
||||
if (m_tStopTime < m_tStartTime)
|
||||
ExecuteStop(); // case 10
|
||||
ExecuteStart(); // cases 3 5 7 9 13 14
|
||||
return m_StreamState;
|
||||
}
|
||||
|
||||
if (m_tStartTime >= *pSampleStop)
|
||||
{
|
||||
ExecuteStop(); // cases 6 16
|
||||
return m_StreamState;
|
||||
}
|
||||
|
||||
if (m_tStartTime <= m_tStopTime)
|
||||
{
|
||||
ExecuteStart();
|
||||
ExecuteStop();
|
||||
return m_StreamState; // case 1
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteStop();
|
||||
ExecuteStart();
|
||||
return m_StreamState; // cases 2 4
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample )
|
||||
{
|
||||
|
||||
REFERENCE_TIME rtBufferStart, rtBufferStop;
|
||||
const BOOL bNoBufferTimes =
|
||||
pSample == NULL ||
|
||||
FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));
|
||||
|
||||
StreamControlState state;
|
||||
LONG lWait;
|
||||
|
||||
do
|
||||
{
|
||||
// something has to break out of the blocking
|
||||
if (m_bIsFlushing || m_FilterState == State_Stopped)
|
||||
return STREAM_DISCARDING;
|
||||
|
||||
if (bNoBufferTimes) {
|
||||
// Can't do anything until we get a time stamp
|
||||
state = m_StreamState;
|
||||
break;
|
||||
} else {
|
||||
state = CheckSampleTimes( &rtBufferStart, &rtBufferStop );
|
||||
if (state == STREAM_FLOWING)
|
||||
break;
|
||||
|
||||
// we aren't supposed to send this, but we've been
|
||||
// told to send one more than we were supposed to
|
||||
// (and the stop isn't still pending and we're streaming)
|
||||
if (m_bStopSendExtra && !m_bStopExtraSent &&
|
||||
m_tStopTime == MAX_TIME &&
|
||||
m_FilterState != State_Stopped) {
|
||||
m_bStopExtraSent = TRUE;
|
||||
DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),
|
||||
m_dwStopCookie));
|
||||
state = STREAM_FLOWING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We're in discarding mode
|
||||
|
||||
// If we've no clock, discard as fast as we can
|
||||
if (!m_pRefClock) {
|
||||
break;
|
||||
|
||||
// If we're paused, we can't discard in a timely manner because
|
||||
// there's no such thing as stream times. We must block until
|
||||
// we run or stop, or we'll end up throwing the whole stream away
|
||||
// as quickly as possible
|
||||
} else if (m_FilterState == State_Paused) {
|
||||
lWait = INFINITE;
|
||||
|
||||
} else {
|
||||
// wait until it's time for the sample until we say "discard"
|
||||
// ("discard in a timely fashion")
|
||||
REFERENCE_TIME rtNow;
|
||||
EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));
|
||||
rtNow -= m_tRunStart; // Into relative ref-time
|
||||
lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms
|
||||
if (lWait < 10) break; // Not worth waiting - discard early
|
||||
}
|
||||
|
||||
} while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
|
||||
// or we will get confused
|
||||
if (m_FilterState == new_state)
|
||||
return;
|
||||
|
||||
switch (new_state)
|
||||
{
|
||||
case State_Stopped:
|
||||
|
||||
DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));
|
||||
|
||||
// execute any pending starts and stops in the right order,
|
||||
// to make sure all notifications get sent, and we end up
|
||||
// in the right state to begin next time (??? why not?)
|
||||
|
||||
if (m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {
|
||||
ExecuteStart();
|
||||
} else if (m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {
|
||||
ExecuteStop();
|
||||
} else if (m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {
|
||||
if (m_tStartTime <= m_tStopTime) {
|
||||
ExecuteStart();
|
||||
ExecuteStop();
|
||||
} else {
|
||||
ExecuteStop();
|
||||
ExecuteStart();
|
||||
}
|
||||
}
|
||||
// always start off flowing when the graph starts streaming
|
||||
// unless told otherwise
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
m_FilterState = new_state;
|
||||
break;
|
||||
|
||||
case State_Running:
|
||||
|
||||
DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));
|
||||
|
||||
m_tRunStart = tStart;
|
||||
// fall-through
|
||||
|
||||
default: // case State_Paused:
|
||||
m_FilterState = new_state;
|
||||
}
|
||||
// unblock!
|
||||
m_StreamEvent.Set();
|
||||
}
|
||||
|
||||
|
||||
void CBaseStreamControl::Flushing(BOOL bInProgress)
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
m_bIsFlushing = bInProgress;
|
||||
m_StreamEvent.Set();
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: StrmCtl.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __strmctl_h__
|
||||
#define __strmctl_h__
|
||||
|
||||
class CBaseStreamControl : public IAMStreamControl
|
||||
{
|
||||
public:
|
||||
// Used by the implementation
|
||||
enum StreamControlState
|
||||
{ STREAM_FLOWING = 0x1000,
|
||||
STREAM_DISCARDING
|
||||
};
|
||||
|
||||
private:
|
||||
enum StreamControlState m_StreamState; // Current stream state
|
||||
enum StreamControlState m_StreamStateOnStop; // State after next stop
|
||||
// (i.e.Blocking or Discarding)
|
||||
|
||||
REFERENCE_TIME m_tStartTime; // MAX_TIME implies none
|
||||
REFERENCE_TIME m_tStopTime; // MAX_TIME implies none
|
||||
DWORD m_dwStartCookie; // Cookie for notification to app
|
||||
DWORD m_dwStopCookie; // Cookie for notification to app
|
||||
volatile BOOL m_bIsFlushing; // No optimization pls!
|
||||
volatile BOOL m_bStopSendExtra; // bSendExtra was set
|
||||
volatile BOOL m_bStopExtraSent; // the extra one was sent
|
||||
|
||||
CCritSec m_CritSec; // CritSec to guard above attributes
|
||||
|
||||
// Event to fire when we can come
|
||||
// out of blocking, or to come out of waiting
|
||||
// to discard if we change our minds.
|
||||
//
|
||||
CAMEvent m_StreamEvent;
|
||||
|
||||
// All of these methods execute immediately. Helpers for others.
|
||||
//
|
||||
void ExecuteStop();
|
||||
void ExecuteStart();
|
||||
void CancelStop();
|
||||
void CancelStart();
|
||||
|
||||
// Some things we need to be told by our owning filter
|
||||
// Your pin must also expose IAMStreamControl when QI'd for it!
|
||||
//
|
||||
IReferenceClock * m_pRefClock; // Need it to set advises
|
||||
// Filter must tell us via
|
||||
// SetSyncSource
|
||||
IMediaEventSink * m_pSink; // Event sink
|
||||
// Filter must tell us after it
|
||||
// creates it in JoinFilterGraph()
|
||||
FILTER_STATE m_FilterState; // Just need it!
|
||||
// Filter must tell us via
|
||||
// NotifyFilterState
|
||||
REFERENCE_TIME m_tRunStart; // Per the Run call to the filter
|
||||
|
||||
// This guy will return one of the three StreamControlState's. Here's what
|
||||
// the caller should do for each one:
|
||||
//
|
||||
// STREAM_FLOWING: Proceed as usual (render or pass the sample on)
|
||||
// STREAM_DISCARDING: Calculate the time 'til *pSampleStop and wait
|
||||
// that long for the event handle
|
||||
// (GetStreamEventHandle()). If the wait
|
||||
// expires, throw the sample away. If the event
|
||||
// fires, call me back - I've changed my mind.
|
||||
//
|
||||
enum StreamControlState CheckSampleTimes( __in const REFERENCE_TIME * pSampleStart,
|
||||
__in const REFERENCE_TIME * pSampleStop );
|
||||
|
||||
public:
|
||||
// You don't have to tell us much when we're created, but there are other
|
||||
// obligations that must be met. See SetSyncSource & NotifyFilterState
|
||||
// below.
|
||||
//
|
||||
CBaseStreamControl(__inout_opt HRESULT *phr = NULL);
|
||||
~CBaseStreamControl();
|
||||
|
||||
// If you want this class to work properly, there are thing you need to
|
||||
// (keep) telling it. Filters with pins that use this class
|
||||
// should ensure that they pass through to this method any calls they
|
||||
// receive on their SetSyncSource.
|
||||
|
||||
// We need a clock to see what time it is. This is for the
|
||||
// "discard in a timely fashion" logic. If we discard everything as
|
||||
// quick as possible, a whole 60 minute file could get discarded in the
|
||||
// first 10 seconds, and if somebody wants to turn streaming on at 30
|
||||
// minutes into the file, and they make the call more than a few seconds
|
||||
// after the graph is run, it may be too late!
|
||||
// So we hold every sample until it's time has gone, then we discard it.
|
||||
// The filter should call this when it gets a SetSyncSource
|
||||
//
|
||||
void SetSyncSource( IReferenceClock * pRefClock )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
if (m_pRefClock) m_pRefClock->Release();
|
||||
m_pRefClock = pRefClock;
|
||||
if (m_pRefClock) m_pRefClock->AddRef();
|
||||
}
|
||||
|
||||
// Set event sink for notifications
|
||||
// The filter should call this in its JoinFilterGraph after it creates the
|
||||
// IMediaEventSink
|
||||
//
|
||||
void SetFilterGraph( IMediaEventSink *pSink ) {
|
||||
m_pSink = pSink;
|
||||
}
|
||||
|
||||
// Since we schedule in stream time, we need the tStart and must track the
|
||||
// state of our owning filter.
|
||||
// The app should call this ever state change
|
||||
//
|
||||
void NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart = 0 );
|
||||
|
||||
// Filter should call Flushing(TRUE) in BeginFlush,
|
||||
// and Flushing(FALSE) in EndFlush.
|
||||
//
|
||||
void Flushing( BOOL bInProgress );
|
||||
|
||||
|
||||
// The two main methods of IAMStreamControl
|
||||
|
||||
// Class adds default values suitable for immediate
|
||||
// muting and unmuting of the stream.
|
||||
|
||||
STDMETHODIMP StopAt( const REFERENCE_TIME * ptStop = NULL,
|
||||
BOOL bSendExtra = FALSE,
|
||||
DWORD dwCookie = 0 );
|
||||
STDMETHODIMP StartAt( const REFERENCE_TIME * ptStart = NULL,
|
||||
DWORD dwCookie = 0 );
|
||||
STDMETHODIMP GetInfo( __out AM_STREAM_INFO *pInfo);
|
||||
|
||||
// Helper function for pin's receive method. Call this with
|
||||
// the sample and we'll tell you what to do with it. We'll do a
|
||||
// WaitForSingleObject within this call if one is required. This is
|
||||
// a "What should I do with this sample?" kind of call. We'll tell the
|
||||
// caller to either flow it or discard it.
|
||||
// If pSample is NULL we evaluate based on the current state
|
||||
// settings
|
||||
enum StreamControlState CheckStreamState( IMediaSample * pSample );
|
||||
|
||||
private:
|
||||
// These don't require locking, but we are relying on the fact that
|
||||
// m_StreamState can be retrieved with integrity, and is a snap shot that
|
||||
// may have just been, or may be just about to be, changed.
|
||||
HANDLE GetStreamEventHandle() const { return m_StreamEvent; }
|
||||
enum StreamControlState GetStreamState() const { return m_StreamState; }
|
||||
BOOL IsStreaming() const { return m_StreamState == STREAM_FLOWING; }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,74 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: SysClock.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a system clock based on
|
||||
// IReferenceClock.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
#ifdef FILTER_DLL
|
||||
|
||||
/* List of class IDs and creator functions for the class factory. This
|
||||
provides the link between the OLE entry point in the DLL and an object
|
||||
being created. The class factory will call the static CreateInstance
|
||||
function when it is asked to create a CLSID_SystemClock object */
|
||||
|
||||
CFactoryTemplate g_Templates[1] = {
|
||||
{&CLSID_SystemClock, CSystemClock::CreateInstance}
|
||||
};
|
||||
|
||||
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
|
||||
#endif
|
||||
|
||||
/* This goes in the factory template table to create new instances */
|
||||
CUnknown * WINAPI CSystemClock::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)
|
||||
{
|
||||
return new CSystemClock(NAME("System reference clock"),pUnk, phr);
|
||||
}
|
||||
|
||||
|
||||
CSystemClock::CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr) :
|
||||
CBaseReferenceClock(pName, pUnk, phr)
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP CSystemClock::NonDelegatingQueryInterface(
|
||||
REFIID riid,
|
||||
__deref_out void ** ppv)
|
||||
{
|
||||
if (riid == IID_IPersist)
|
||||
{
|
||||
return GetInterface(static_cast<IPersist *>(this), ppv);
|
||||
}
|
||||
else if (riid == IID_IAMClockAdjust)
|
||||
{
|
||||
return GetInterface(static_cast<IAMClockAdjust *>(this), ppv);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CBaseReferenceClock::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the clock's clsid */
|
||||
STDMETHODIMP
|
||||
CSystemClock::GetClassID(__out CLSID *pClsID)
|
||||
{
|
||||
CheckPointer(pClsID,E_POINTER);
|
||||
ValidateReadWritePtr(pClsID,sizeof(CLSID));
|
||||
*pClsID = CLSID_SystemClock;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP
|
||||
CSystemClock::SetClockDelta(REFERENCE_TIME rtDelta)
|
||||
{
|
||||
return SetTimeDelta(rtDelta);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: SysClock.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a system clock implementation of
|
||||
// IReferenceClock.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __SYSTEMCLOCK__
|
||||
#define __SYSTEMCLOCK__
|
||||
|
||||
//
|
||||
// Base clock. Uses timeGetTime ONLY
|
||||
// Uses most of the code in the base reference clock.
|
||||
// Provides GetTime
|
||||
//
|
||||
|
||||
class CSystemClock : public CBaseReferenceClock, public IAMClockAdjust, public IPersist
|
||||
{
|
||||
public:
|
||||
// We must be able to create an instance of ourselves
|
||||
static CUnknown * WINAPI CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
|
||||
CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
|
||||
|
||||
// Yield up our class id so that we can be persisted
|
||||
// Implement required Ipersist method
|
||||
STDMETHODIMP GetClassID(__out CLSID *pClsID);
|
||||
|
||||
// IAMClockAdjust methods
|
||||
STDMETHODIMP SetClockDelta(REFERENCE_TIME rtDelta);
|
||||
}; //CSystemClock
|
||||
|
||||
#endif /* __SYSTEMCLOCK__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,304 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Transfrm.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines classes from which simple
|
||||
// transform codecs may be derived.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// It assumes the codec has one input and one output stream, and has no
|
||||
// interest in memory management, interface negotiation or anything else.
|
||||
//
|
||||
// derive your class from this, and supply Transform and the media type/format
|
||||
// negotiation functions. Implement that class, compile and link and
|
||||
// you're done.
|
||||
|
||||
|
||||
#ifndef __TRANSFRM__
|
||||
#define __TRANSFRM__
|
||||
|
||||
// ======================================================================
|
||||
// This is the com object that represents a simple transform filter. It
|
||||
// supports IBaseFilter, IMediaFilter and two pins through nested interfaces
|
||||
// ======================================================================
|
||||
|
||||
class CTransformFilter;
|
||||
|
||||
// ==================================================
|
||||
// Implements the input pin
|
||||
// ==================================================
|
||||
|
||||
class CTransformInputPin : public CBaseInputPin
|
||||
{
|
||||
friend class CTransformFilter;
|
||||
|
||||
protected:
|
||||
CTransformFilter *m_pTransformFilter;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
CTransformInputPin(
|
||||
__in_opt LPCTSTR pObjectName,
|
||||
__inout CTransformFilter *pTransformFilter,
|
||||
__inout HRESULT * phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CTransformInputPin(
|
||||
__in_opt LPCSTR pObjectName,
|
||||
__inout CTransformFilter *pTransformFilter,
|
||||
__inout HRESULT * phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
#endif
|
||||
|
||||
STDMETHODIMP QueryId(__deref_out LPWSTR * Id)
|
||||
{
|
||||
return AMGetWideString(L"In", Id);
|
||||
}
|
||||
|
||||
// Grab and release extra interfaces if required
|
||||
|
||||
HRESULT CheckConnect(IPin *pPin);
|
||||
HRESULT BreakConnect();
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
// check that we can support this output type
|
||||
HRESULT CheckMediaType(const CMediaType* mtIn);
|
||||
|
||||
// set the connection media type
|
||||
HRESULT SetMediaType(const CMediaType* mt);
|
||||
|
||||
// --- IMemInputPin -----
|
||||
|
||||
// here's the next block of data from the stream.
|
||||
// AddRef it yourself if you need to hold it beyond the end
|
||||
// of this call.
|
||||
STDMETHODIMP Receive(IMediaSample * pSample);
|
||||
|
||||
// provide EndOfStream that passes straight downstream
|
||||
// (there is no queued data)
|
||||
STDMETHODIMP EndOfStream(void);
|
||||
|
||||
// passes it to CTransformFilter::BeginFlush
|
||||
STDMETHODIMP BeginFlush(void);
|
||||
|
||||
// passes it to CTransformFilter::EndFlush
|
||||
STDMETHODIMP EndFlush(void);
|
||||
|
||||
STDMETHODIMP NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
// Check if it's OK to process samples
|
||||
virtual HRESULT CheckStreaming();
|
||||
|
||||
// Media type
|
||||
public:
|
||||
CMediaType& CurrentMediaType() { return m_mt; };
|
||||
|
||||
};
|
||||
|
||||
// ==================================================
|
||||
// Implements the output pin
|
||||
// ==================================================
|
||||
|
||||
class CTransformOutputPin : public CBaseOutputPin
|
||||
{
|
||||
friend class CTransformFilter;
|
||||
|
||||
protected:
|
||||
CTransformFilter *m_pTransformFilter;
|
||||
|
||||
public:
|
||||
|
||||
// implement IMediaPosition by passing upstream
|
||||
IUnknown * m_pPosition;
|
||||
|
||||
CTransformOutputPin(
|
||||
__in_opt LPCTSTR pObjectName,
|
||||
__inout CTransformFilter *pTransformFilter,
|
||||
__inout HRESULT * phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CTransformOutputPin(
|
||||
__in_opt LPCSTR pObjectName,
|
||||
__inout CTransformFilter *pTransformFilter,
|
||||
__inout HRESULT * phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
#endif
|
||||
~CTransformOutputPin();
|
||||
|
||||
// override to expose IMediaPosition
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
// --- CBaseOutputPin ------------
|
||||
|
||||
STDMETHODIMP QueryId(__deref_out LPWSTR * Id)
|
||||
{
|
||||
return AMGetWideString(L"Out", Id);
|
||||
}
|
||||
|
||||
// Grab and release extra interfaces if required
|
||||
|
||||
HRESULT CheckConnect(IPin *pPin);
|
||||
HRESULT BreakConnect();
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
// check that we can support this output type
|
||||
HRESULT CheckMediaType(const CMediaType* mtOut);
|
||||
|
||||
// set the connection media type
|
||||
HRESULT SetMediaType(const CMediaType *pmt);
|
||||
|
||||
// called from CBaseOutputPin during connection to ask for
|
||||
// the count and size of buffers we need.
|
||||
HRESULT DecideBufferSize(
|
||||
IMemAllocator * pAlloc,
|
||||
__inout ALLOCATOR_PROPERTIES *pProp);
|
||||
|
||||
// returns the preferred formats for a pin
|
||||
HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType);
|
||||
|
||||
// inherited from IQualityControl via CBasePin
|
||||
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
|
||||
|
||||
// Media type
|
||||
public:
|
||||
CMediaType& CurrentMediaType() { return m_mt; };
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CTransformFilter : public CBaseFilter
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// map getpin/getpincount for base enum of pins to owner
|
||||
// override this to return more specialised pin objects
|
||||
|
||||
virtual int GetPinCount();
|
||||
virtual CBasePin * GetPin(int n);
|
||||
STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);
|
||||
|
||||
// override state changes to allow derived transform filter
|
||||
// to control streaming start/stop
|
||||
STDMETHODIMP Stop();
|
||||
STDMETHODIMP Pause();
|
||||
|
||||
public:
|
||||
|
||||
CTransformFilter(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, REFCLSID clsid);
|
||||
#ifdef UNICODE
|
||||
CTransformFilter(__in_opt LPCSTR , __inout_opt LPUNKNOWN, REFCLSID clsid);
|
||||
#endif
|
||||
~CTransformFilter();
|
||||
|
||||
// =================================================================
|
||||
// ----- override these bits ---------------------------------------
|
||||
// =================================================================
|
||||
|
||||
// These must be supplied in a derived class
|
||||
|
||||
virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
|
||||
|
||||
// check if you can support mtIn
|
||||
virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
|
||||
|
||||
// check if you can support the transform from this input to this output
|
||||
virtual HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) PURE;
|
||||
|
||||
// this goes in the factory template table to create new instances
|
||||
// static CCOMObject * CreateInstance(__inout_opt LPUNKNOWN, HRESULT *);
|
||||
|
||||
// call the SetProperties function with appropriate arguments
|
||||
virtual HRESULT DecideBufferSize(
|
||||
IMemAllocator * pAllocator,
|
||||
__inout ALLOCATOR_PROPERTIES *pprop) PURE;
|
||||
|
||||
// override to suggest OUTPUT pin media types
|
||||
virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType) PURE;
|
||||
|
||||
|
||||
|
||||
// =================================================================
|
||||
// ----- Optional Override Methods -----------------------
|
||||
// =================================================================
|
||||
|
||||
// you can also override these if you want to know about streaming
|
||||
virtual HRESULT StartStreaming();
|
||||
virtual HRESULT StopStreaming();
|
||||
|
||||
// override if you can do anything constructive with quality notifications
|
||||
virtual HRESULT AlterQuality(Quality q);
|
||||
|
||||
// override this to know when the media type is actually set
|
||||
virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
|
||||
|
||||
// chance to grab extra interfaces on connection
|
||||
virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
|
||||
virtual HRESULT BreakConnect(PIN_DIRECTION dir);
|
||||
virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
|
||||
|
||||
// chance to customize the transform process
|
||||
virtual HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
// Standard setup for output sample
|
||||
HRESULT InitializeOutputSample(IMediaSample *pSample, __deref_out IMediaSample **ppOutSample);
|
||||
|
||||
// if you override Receive, you may need to override these three too
|
||||
virtual HRESULT EndOfStream(void);
|
||||
virtual HRESULT BeginFlush(void);
|
||||
virtual HRESULT EndFlush(void);
|
||||
virtual HRESULT NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
#ifdef PERF
|
||||
// Override to register performance measurement with a less generic string
|
||||
// You should do this to avoid confusion with other filters
|
||||
virtual void RegisterPerfId()
|
||||
{m_idTransform = MSR_REGISTER(TEXT("Transform"));}
|
||||
#endif // PERF
|
||||
|
||||
|
||||
// implementation details
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef PERF
|
||||
int m_idTransform; // performance measuring id
|
||||
#endif
|
||||
BOOL m_bEOSDelivered; // have we sent EndOfStream
|
||||
BOOL m_bSampleSkipped; // Did we just skip a frame
|
||||
BOOL m_bQualityChanged; // Have we degraded?
|
||||
|
||||
// critical section protecting filter state.
|
||||
|
||||
CCritSec m_csFilter;
|
||||
|
||||
// critical section stopping state changes (ie Stop) while we're
|
||||
// processing a sample.
|
||||
//
|
||||
// This critical section is held when processing
|
||||
// events that occur on the receive thread - Receive() and EndOfStream().
|
||||
//
|
||||
// If you want to hold both m_csReceive and m_csFilter then grab
|
||||
// m_csFilter FIRST - like CTransformFilter::Stop() does.
|
||||
|
||||
CCritSec m_csReceive;
|
||||
|
||||
// these hold our input and output pins
|
||||
|
||||
friend class CTransformInputPin;
|
||||
friend class CTransformOutputPin;
|
||||
CTransformInputPin *m_pInput;
|
||||
CTransformOutputPin *m_pOutput;
|
||||
};
|
||||
|
||||
#endif /* __TRANSFRM__ */
|
||||
|
||||
|
|
@ -0,0 +1,974 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: TransIP.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements class for simple Transform-
|
||||
// In-Place filters such as audio.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// How allocators are decided.
|
||||
//
|
||||
// An in-place transform tries to do its work in someone else's buffers.
|
||||
// It tries to persuade the filters on either side to use the same allocator
|
||||
// (and for that matter the same media type). In desperation, if the downstream
|
||||
// filter refuses to supply an allocator and the upstream filter offers only
|
||||
// a read-only one then it will provide an allocator.
|
||||
// if the upstream filter insists on a read-only allocator then the transform
|
||||
// filter will (reluctantly) copy the data before transforming it.
|
||||
//
|
||||
// In order to pass an allocator through it needs to remember the one it got
|
||||
// from the first connection to pass it on to the second one.
|
||||
//
|
||||
// It is good if we can avoid insisting on a particular order of connection
|
||||
// (There is a precedent for insisting on the input
|
||||
// being connected first. Insisting on the output being connected first is
|
||||
// not allowed. That would break RenderFile.)
|
||||
//
|
||||
// The base pin classes (CBaseOutputPin and CBaseInputPin) both have a
|
||||
// m_pAllocator member which is used in places like
|
||||
// CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive.
|
||||
// To avoid lots of extra overriding, we should keep these happy
|
||||
// by using these pointers.
|
||||
//
|
||||
// When each pin is connected, it will set the corresponding m_pAllocator
|
||||
// and will have a single ref-count on that allocator.
|
||||
//
|
||||
// Refcounts are acquired by GetAllocator calls which return AddReffed
|
||||
// allocators and are released in one of:
|
||||
// CBaseInputPin::Disconnect
|
||||
// CBaseOutputPin::BreakConect
|
||||
// In each case m_pAllocator is set to NULL after the release, so this
|
||||
// is the last chance to ever release it. If there should ever be
|
||||
// multiple refcounts associated with the same pointer, this had better
|
||||
// be cleared up before that happens. To avoid such problems, we'll
|
||||
// stick with one per pointer.
|
||||
|
||||
|
||||
|
||||
// RECONNECTING and STATE CHANGES
|
||||
//
|
||||
// Each pin could be disconnected, connected with a read-only allocator,
|
||||
// connected with an upstream read/write allocator, connected with an
|
||||
// allocator from downstream or connected with its own allocator.
|
||||
// Five states for each pin gives a data space of 25 states.
|
||||
//
|
||||
// Notation:
|
||||
//
|
||||
// R/W == read/write
|
||||
// R-O == read-only
|
||||
//
|
||||
// <input pin state> <output pin state> <comments>
|
||||
//
|
||||
// 00 means an unconnected pin.
|
||||
// <- means using a R/W allocator from the upstream filter
|
||||
// <= means using a R-O allocator from an upstream filter
|
||||
// || means using our own (R/W) allocator.
|
||||
// -> means using a R/W allocator from a downstream filter
|
||||
// (a R-O allocator from downstream is nonsense, it can't ever work).
|
||||
//
|
||||
//
|
||||
// That makes 25 possible states. Some states are nonsense (two different
|
||||
// allocators from the same place). These are just an artifact of the notation.
|
||||
// <= <- Nonsense.
|
||||
// <- <= Nonsense
|
||||
// Some states are illegal (the output pin never accepts a R-O allocator):
|
||||
// 00 <= !! Error !!
|
||||
// <= <= !! Error !!
|
||||
// || <= !! Error !!
|
||||
// -> <= !! Error !!
|
||||
// Three states appears to be inaccessible:
|
||||
// -> || Inaccessible
|
||||
// || -> Inaccessible
|
||||
// || <- Inaccessible
|
||||
// Some states only ever occur as intermediates with a pending reconnect which
|
||||
// is guaranteed to finish in another state.
|
||||
// -> 00 ?? unstable goes to || 00
|
||||
// 00 <- ?? unstable goes to 00 ||
|
||||
// -> <- ?? unstable goes to -> ->
|
||||
// <- || ?? unstable goes to <- <-
|
||||
// <- -> ?? unstable goes to <- <-
|
||||
// And that leaves 11 possible resting states:
|
||||
// 1 00 00 Nothing connected.
|
||||
// 2 <- 00 Input pin connected.
|
||||
// 3 <= 00 Input pin connected using R-O allocator.
|
||||
// 4 || 00 Needs several state changes to get here.
|
||||
// 5 00 || Output pin connected using our allocator
|
||||
// 6 00 -> Downstream only connected
|
||||
// 7 || || Undesirable but can be forced upon us.
|
||||
// 8 <= || Copy forced. <= -> is preferable
|
||||
// 9 <= -> OK - forced to copy.
|
||||
// 10 <- <- Transform in place (ideal)
|
||||
// 11 -> -> Transform in place (ideal)
|
||||
//
|
||||
// The object of the exercise is to ensure that we finish up in states
|
||||
// 10 or 11 whenever possible. State 10 is only possible if the upstream
|
||||
// filter has a R/W allocator (the AVI splitter notoriously
|
||||
// doesn't) and state 11 is only possible if the downstream filter does
|
||||
// offer an allocator.
|
||||
//
|
||||
// The transition table (entries marked * go via a reconnect)
|
||||
//
|
||||
// There are 8 possible transitions:
|
||||
// A: Connect upstream to filter with R-O allocator that insists on using it.
|
||||
// B: Connect upstream to filter with R-O allocator but chooses not to use it.
|
||||
// C: Connect upstream to filter with R/W allocator and insists on using it.
|
||||
// D: Connect upstream to filter with R/W allocator but chooses not to use it.
|
||||
// E: Connect downstream to a filter that offers an allocator
|
||||
// F: Connect downstream to a filter that does not offer an allocator
|
||||
// G: disconnect upstream
|
||||
// H: Disconnect downstream
|
||||
//
|
||||
// A B C D E F G H
|
||||
// ---------------------------------------------------------
|
||||
// 00 00 1 | 3 3 2 2 6 5 . . |1 00 00
|
||||
// <- 00 2 | . . . . *10/11 10 1 . |2 <- 00
|
||||
// <= 00 3 | . . . . *9/11 *7/8 1 . |3 <= 00
|
||||
// || 00 4 | . . . . *8 *7 1 . |4 || 00
|
||||
// 00 || 5 | 8 7 *10 7 . . . 1 |5 00 ||
|
||||
// 00 -> 6 | 9 11 *10 11 . . . 1 |6 00 ->
|
||||
// || || 7 | . . . . . . 5 4 |7 || ||
|
||||
// <= || 8 | . . . . . . 5 3 |8 <= ||
|
||||
// <= -> 9 | . . . . . . 6 3 |9 <= ->
|
||||
// <- <- 10| . . . . . . *5/6 2 |10 <- <-
|
||||
// -> -> 11| . . . . . . 6 *2/3 |11 -> ->
|
||||
// ---------------------------------------------------------
|
||||
// A B C D E F G H
|
||||
//
|
||||
// All these states are accessible without requiring any filter to
|
||||
// change its behaviour but not all transitions are accessible, for
|
||||
// instance a transition from state 4 to anywhere other than
|
||||
// state 8 requires that the upstream filter first offer a R-O allocator
|
||||
// and then changes its mind and offer R/W. This is NOT allowable - it
|
||||
// leads to things like the output pin getting a R/W allocator from
|
||||
// upstream and then the input pin being told it can only have a R-O one.
|
||||
// Note that you CAN change (say) the upstream filter for a different one, but
|
||||
// only as a disconnect / connect, not as a Reconnect. (Exercise for
|
||||
// the reader is to see how you get into state 4).
|
||||
//
|
||||
// The reconnection stuff goes as follows (some of the cases shown here as
|
||||
// "no reconnect" may get one to finalise media type - an old story).
|
||||
// If there is a reconnect where it says "no reconnect" here then the
|
||||
// reconnection must not change the allocator choice.
|
||||
//
|
||||
// state 2: <- 00 transition E <- <- case C <- <- (no change)
|
||||
// case D -> <- and then to -> ->
|
||||
//
|
||||
// state 2: <- 00 transition F <- <- (no reconnect)
|
||||
//
|
||||
// state 3: <= 00 transition E <= -> case A <= -> (no change)
|
||||
// case B -> ->
|
||||
// transition F <= || case A <= || (no change)
|
||||
// case B || ||
|
||||
//
|
||||
// state 4: || 00 transition E || || case B -> || and then all cases to -> ->
|
||||
// F || || case B || || (no change)
|
||||
//
|
||||
// state 5: 00 || transition A <= || (no reconnect)
|
||||
// B || || (no reconnect)
|
||||
// C <- || all cases <- <-
|
||||
// D || || (unfortunate, but upstream's choice)
|
||||
//
|
||||
// state 6: 00 -> transition A <= -> (no reconnect)
|
||||
// B -> -> (no reconnect)
|
||||
// C <- -> all cases <- <-
|
||||
// D -> -> (no reconnect)
|
||||
//
|
||||
// state 10:<- <- transition G 00 <- case E 00 ->
|
||||
// case F 00 ||
|
||||
//
|
||||
// state 11:-> -> transition H -> 00 case A <= 00 (schizo)
|
||||
// case B <= 00
|
||||
// case C <- 00 (schizo)
|
||||
// case D <- 00
|
||||
//
|
||||
// The Rules:
|
||||
// To sort out media types:
|
||||
// The input is reconnected
|
||||
// if the input pin is connected and the output pin connects
|
||||
// The output is reconnected
|
||||
// If the output pin is connected
|
||||
// and the input pin connects to a different media type
|
||||
//
|
||||
// To sort out allocators:
|
||||
// The input is reconnected
|
||||
// if the output disconnects and the input was using a downstream allocator
|
||||
// The output pin calls SetAllocator to pass on a new allocator
|
||||
// if the output is connected and
|
||||
// if the input disconnects and the output was using an upstream allocator
|
||||
// if the input acquires an allocator different from the output one
|
||||
// and that new allocator is not R-O
|
||||
//
|
||||
// Data is copied (i.e. call getbuffer and copy the data before transforming it)
|
||||
// if the two allocators are different.
|
||||
|
||||
|
||||
|
||||
// CHAINS of filters:
|
||||
//
|
||||
// We sit between two filters (call them A and Z). We should finish up
|
||||
// with the same allocator on both of our pins and that should be the
|
||||
// same one that A and Z would have agreed on if we hadn't been in the
|
||||
// way. Furthermore, it should not matter how many in-place transforms
|
||||
// are in the way. Let B, C, D... be in-place transforms ("us").
|
||||
// Here's how it goes:
|
||||
//
|
||||
// 1.
|
||||
// A connects to B. They agree on A's allocator.
|
||||
// A-a->B
|
||||
//
|
||||
// 2.
|
||||
// B connects to C. Same story. There is no point in a reconnect, but
|
||||
// B will request an input reconnect anyway.
|
||||
// A-a->B-a->C
|
||||
//
|
||||
// 3.
|
||||
// C connects to Z.
|
||||
// C insists on using A's allocator, but compromises by requesting a reconnect.
|
||||
// of C's input.
|
||||
// A-a->B-?->C-a->Z
|
||||
//
|
||||
// We now have pending reconnects on both A--->B and B--->C
|
||||
//
|
||||
// 4.
|
||||
// The A--->B link is reconnected.
|
||||
// A asks B for an allocator. B sees that it has a downstream connection so
|
||||
// asks its downstream input pin i.e. C's input pin for an allocator. C sees
|
||||
// that it too has a downstream connection so asks Z for an allocator.
|
||||
//
|
||||
// Even though Z's input pin is connected, it is being asked for an allocator.
|
||||
// It could refuse, in which case the chain is done and will use A's allocator
|
||||
// Alternatively, Z may supply one. A chooses either Z's or A's own one.
|
||||
// B's input pin gets NotifyAllocator called to tell it the decision and it
|
||||
// propagates this downstream by calling ReceiveAllocator on its output pin
|
||||
// which calls NotifyAllocator on the next input pin downstream etc.
|
||||
// If the choice is Z then it goes:
|
||||
// A-z->B-a->C-a->Z
|
||||
// A-z->B-z->C-a->Z
|
||||
// A-z->B-z->C-z->Z
|
||||
//
|
||||
// And that's IT!! Any further (essentially spurious) reconnects peter out
|
||||
// with no change in the chain.
|
||||
|
||||
#include <streams.h>
|
||||
#include <measure.h>
|
||||
#include <transip.h>
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements the CTransInPlaceFilter class
|
||||
// =================================================================
|
||||
|
||||
CTransInPlaceFilter::CTransInPlaceFilter
|
||||
( __in_opt LPCTSTR pName,
|
||||
__inout_opt LPUNKNOWN pUnk,
|
||||
REFCLSID clsid,
|
||||
__inout HRESULT *phr,
|
||||
bool bModifiesData
|
||||
)
|
||||
: CTransformFilter(pName, pUnk, clsid),
|
||||
m_bModifiesData(bModifiesData)
|
||||
{
|
||||
#ifdef PERF
|
||||
RegisterPerfId();
|
||||
#endif // PERF
|
||||
|
||||
} // constructor
|
||||
|
||||
#ifdef UNICODE
|
||||
CTransInPlaceFilter::CTransInPlaceFilter
|
||||
( __in_opt LPCSTR pName,
|
||||
__inout_opt LPUNKNOWN pUnk,
|
||||
REFCLSID clsid,
|
||||
__inout HRESULT *phr,
|
||||
bool bModifiesData
|
||||
)
|
||||
: CTransformFilter(pName, pUnk, clsid),
|
||||
m_bModifiesData(bModifiesData)
|
||||
{
|
||||
#ifdef PERF
|
||||
RegisterPerfId();
|
||||
#endif // PERF
|
||||
|
||||
} // constructor
|
||||
#endif
|
||||
|
||||
// return a non-addrefed CBasePin * for the user to addref if he holds onto it
|
||||
// for longer than his pointer to us. We create the pins dynamically when they
|
||||
// are asked for rather than in the constructor. This is because we want to
|
||||
// give the derived class an oppportunity to return different pin objects
|
||||
|
||||
// As soon as any pin is needed we create both (this is different from the
|
||||
// usual transform filter) because enumerators, allocators etc are passed
|
||||
// through from one pin to another and it becomes very painful if the other
|
||||
// pin isn't there. If we fail to create either pin we ensure we fail both.
|
||||
|
||||
CBasePin *
|
||||
CTransInPlaceFilter::GetPin(int n)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
// Create an input pin if not already done
|
||||
|
||||
if (m_pInput == NULL) {
|
||||
|
||||
m_pInput = new CTransInPlaceInputPin( NAME("TransInPlace input pin")
|
||||
, this // Owner filter
|
||||
, &hr // Result code
|
||||
, L"Input" // Pin name
|
||||
);
|
||||
|
||||
// Constructor for CTransInPlaceInputPin can't fail
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
// Create an output pin if not already done
|
||||
|
||||
if (m_pInput!=NULL && m_pOutput == NULL) {
|
||||
|
||||
m_pOutput = new CTransInPlaceOutputPin( NAME("TransInPlace output pin")
|
||||
, this // Owner filter
|
||||
, &hr // Result code
|
||||
, L"Output" // Pin name
|
||||
);
|
||||
|
||||
// a failed return code should delete the object
|
||||
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
if (m_pOutput == NULL) {
|
||||
delete m_pInput;
|
||||
m_pInput = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the appropriate pin
|
||||
|
||||
ASSERT (n>=0 && n<=1);
|
||||
if (n == 0) {
|
||||
return m_pInput;
|
||||
} else if (n==1) {
|
||||
return m_pOutput;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // GetPin
|
||||
|
||||
|
||||
|
||||
// dir is the direction of our pin.
|
||||
// pReceivePin is the pin we are connecting to.
|
||||
HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(pReceivePin);
|
||||
ASSERT(m_pInput);
|
||||
ASSERT(m_pOutput);
|
||||
|
||||
// if we are not part of a graph, then don't indirect the pointer
|
||||
// this probably prevents use of the filter without a filtergraph
|
||||
if (!m_pGraph) {
|
||||
return VFW_E_NOT_IN_GRAPH;
|
||||
}
|
||||
|
||||
// Always reconnect the input to account for buffering changes
|
||||
//
|
||||
// Because we don't get to suggest a type on ReceiveConnection
|
||||
// we need another way of making sure the right type gets used.
|
||||
//
|
||||
// One way would be to have our EnumMediaTypes return our output
|
||||
// connection type first but more deterministic and simple is to
|
||||
// call ReconnectEx passing the type we want to reconnect with
|
||||
// via the base class ReconeectPin method.
|
||||
|
||||
if (dir == PINDIR_OUTPUT) {
|
||||
if( m_pInput->IsConnected() ) {
|
||||
return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() );
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
ASSERT(dir == PINDIR_INPUT);
|
||||
|
||||
// Reconnect output if necessary
|
||||
|
||||
if( m_pOutput->IsConnected() ) {
|
||||
|
||||
if ( m_pInput->CurrentMediaType()
|
||||
!= m_pOutput->CurrentMediaType()
|
||||
) {
|
||||
return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() );
|
||||
}
|
||||
}
|
||||
return NOERROR;
|
||||
|
||||
} // ComnpleteConnect
|
||||
|
||||
|
||||
//
|
||||
// DecideBufferSize
|
||||
//
|
||||
// Tell the output pin's allocator what size buffers we require.
|
||||
// *pAlloc will be the allocator our output pin is using.
|
||||
//
|
||||
|
||||
HRESULT CTransInPlaceFilter::DecideBufferSize
|
||||
( IMemAllocator *pAlloc
|
||||
, __inout ALLOCATOR_PROPERTIES *pProperties
|
||||
)
|
||||
{
|
||||
ALLOCATOR_PROPERTIES Request, Actual;
|
||||
HRESULT hr;
|
||||
|
||||
// If we are connected upstream, get his views
|
||||
if (m_pInput->IsConnected()) {
|
||||
// Get the input pin allocator, and get its size and count.
|
||||
// we don't care about his alignment and prefix.
|
||||
|
||||
hr = InputPin()->PeekAllocator()->GetProperties(&Request);
|
||||
if (FAILED(hr)) {
|
||||
// Input connected but with a secretive allocator - enough!
|
||||
return hr;
|
||||
}
|
||||
} else {
|
||||
// Propose one byte
|
||||
// If this isn't enough then when the other pin does get connected
|
||||
// we can revise it.
|
||||
ZeroMemory(&Request, sizeof(Request));
|
||||
Request.cBuffers = 1;
|
||||
Request.cbBuffer = 1;
|
||||
}
|
||||
|
||||
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Setting Allocator Requirements")));
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d"),
|
||||
Request.cBuffers, Request.cbBuffer));
|
||||
|
||||
// Pass the allocator requirements to our output side
|
||||
// but do a little sanity checking first or we'll just hit
|
||||
// asserts in the allocator.
|
||||
|
||||
pProperties->cBuffers = Request.cBuffers;
|
||||
pProperties->cbBuffer = Request.cbBuffer;
|
||||
pProperties->cbAlign = Request.cbAlign;
|
||||
if (pProperties->cBuffers<=0) {pProperties->cBuffers = 1; }
|
||||
if (pProperties->cbBuffer<=0) {pProperties->cbBuffer = 1; }
|
||||
hr = pAlloc->SetProperties(pProperties, &Actual);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Obtained Allocator Requirements")));
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d, Alignment %d"),
|
||||
Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign));
|
||||
|
||||
// Make sure we got the right alignment and at least the minimum required
|
||||
|
||||
if ( (Request.cBuffers > Actual.cBuffers)
|
||||
|| (Request.cbBuffer > Actual.cbBuffer)
|
||||
|| (Request.cbAlign > Actual.cbAlign)
|
||||
) {
|
||||
return E_FAIL;
|
||||
}
|
||||
return NOERROR;
|
||||
|
||||
} // DecideBufferSize
|
||||
|
||||
//
|
||||
// Copy
|
||||
//
|
||||
// return a pointer to an identical copy of pSample
|
||||
__out_opt IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource)
|
||||
{
|
||||
IMediaSample * pDest;
|
||||
|
||||
HRESULT hr;
|
||||
REFERENCE_TIME tStart, tStop;
|
||||
const BOOL bTime = S_OK == pSource->GetTime( &tStart, &tStop);
|
||||
|
||||
// this may block for an indeterminate amount of time
|
||||
hr = OutputPin()->PeekAllocator()->GetBuffer(
|
||||
&pDest
|
||||
, bTime ? &tStart : NULL
|
||||
, bTime ? &tStop : NULL
|
||||
, m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0
|
||||
);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ASSERT(pDest);
|
||||
IMediaSample2 *pSample2;
|
||||
if (SUCCEEDED(pDest->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {
|
||||
HRESULT hrProps = pSample2->SetProperties(
|
||||
FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer),
|
||||
(PBYTE)m_pInput->SampleProps());
|
||||
pSample2->Release();
|
||||
if (FAILED(hrProps)) {
|
||||
pDest->Release();
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (bTime) {
|
||||
pDest->SetTime(&tStart, &tStop);
|
||||
}
|
||||
|
||||
if (S_OK == pSource->IsSyncPoint()) {
|
||||
pDest->SetSyncPoint(TRUE);
|
||||
}
|
||||
if (S_OK == pSource->IsDiscontinuity() || m_bSampleSkipped) {
|
||||
pDest->SetDiscontinuity(TRUE);
|
||||
}
|
||||
if (S_OK == pSource->IsPreroll()) {
|
||||
pDest->SetPreroll(TRUE);
|
||||
}
|
||||
|
||||
// Copy the media type
|
||||
AM_MEDIA_TYPE *pMediaType;
|
||||
if (S_OK == pSource->GetMediaType(&pMediaType)) {
|
||||
pDest->SetMediaType(pMediaType);
|
||||
DeleteMediaType( pMediaType );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_bSampleSkipped = FALSE;
|
||||
|
||||
// Copy the sample media times
|
||||
REFERENCE_TIME TimeStart, TimeEnd;
|
||||
if (pSource->GetMediaTime(&TimeStart,&TimeEnd) == NOERROR) {
|
||||
pDest->SetMediaTime(&TimeStart,&TimeEnd);
|
||||
}
|
||||
|
||||
// Copy the actual data length and the actual data.
|
||||
{
|
||||
const long lDataLength = pSource->GetActualDataLength();
|
||||
if (FAILED(pDest->SetActualDataLength(lDataLength))) {
|
||||
pDest->Release();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Copy the sample data
|
||||
{
|
||||
BYTE *pSourceBuffer, *pDestBuffer;
|
||||
long lSourceSize = pSource->GetSize();
|
||||
long lDestSize = pDest->GetSize();
|
||||
|
||||
ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength);
|
||||
|
||||
if (FAILED(pSource->GetPointer(&pSourceBuffer)) ||
|
||||
FAILED(pDest->GetPointer(&pDestBuffer)) ||
|
||||
lDestSize < lDataLength ||
|
||||
lDataLength < 0) {
|
||||
pDest->Release();
|
||||
return NULL;
|
||||
}
|
||||
ASSERT(lDestSize == 0 || pSourceBuffer != NULL && pDestBuffer != NULL);
|
||||
|
||||
CopyMemory( (PVOID) pDestBuffer, (PVOID) pSourceBuffer, lDataLength );
|
||||
}
|
||||
}
|
||||
|
||||
return pDest;
|
||||
|
||||
} // Copy
|
||||
|
||||
|
||||
// override this to customize the transform process
|
||||
|
||||
HRESULT
|
||||
CTransInPlaceFilter::Receive(IMediaSample *pSample)
|
||||
{
|
||||
/* Check for other streams and pass them on */
|
||||
AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
|
||||
if (pProps->dwStreamId != AM_STREAM_MEDIA) {
|
||||
return m_pOutput->Deliver(pSample);
|
||||
}
|
||||
HRESULT hr;
|
||||
|
||||
// Start timing the TransInPlace (if PERF is defined)
|
||||
MSR_START(m_idTransInPlace);
|
||||
|
||||
if (UsingDifferentAllocators()) {
|
||||
|
||||
// We have to copy the data.
|
||||
|
||||
pSample = Copy(pSample);
|
||||
|
||||
if (pSample==NULL) {
|
||||
MSR_STOP(m_idTransInPlace);
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
// have the derived class transform the data
|
||||
hr = Transform(pSample);
|
||||
|
||||
// Stop the clock and log it (if PERF is defined)
|
||||
MSR_STOP(m_idTransInPlace);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace")));
|
||||
if (UsingDifferentAllocators()) {
|
||||
pSample->Release();
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// the Transform() function can return S_FALSE to indicate that the
|
||||
// sample should not be delivered; we only deliver the sample if it's
|
||||
// really S_OK (same as NOERROR, of course.)
|
||||
if (hr == NOERROR) {
|
||||
hr = m_pOutput->Deliver(pSample);
|
||||
} else {
|
||||
// But it would be an error to return this private workaround
|
||||
// to the caller ...
|
||||
if (S_FALSE == hr) {
|
||||
// S_FALSE returned from Transform is a PRIVATE agreement
|
||||
// We should return NOERROR from Receive() in this cause because
|
||||
// returning S_FALSE from Receive() means that this is the end
|
||||
// of the stream and no more data should be sent.
|
||||
m_bSampleSkipped = TRUE;
|
||||
if (!m_bQualityChanged) {
|
||||
NotifyEvent(EC_QUALITY_CHANGE,0,0);
|
||||
m_bQualityChanged = TRUE;
|
||||
}
|
||||
hr = NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// release the output buffer. If the connected pin still needs it,
|
||||
// it will have addrefed it itself.
|
||||
if (UsingDifferentAllocators()) {
|
||||
pSample->Release();
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
||||
} // Receive
|
||||
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements the CTransInPlaceInputPin class
|
||||
// =================================================================
|
||||
|
||||
|
||||
// constructor
|
||||
|
||||
CTransInPlaceInputPin::CTransInPlaceInputPin
|
||||
( __in_opt LPCTSTR pObjectName
|
||||
, __inout CTransInPlaceFilter *pFilter
|
||||
, __inout HRESULT *phr
|
||||
, __in_opt LPCWSTR pName
|
||||
)
|
||||
: CTransformInputPin(pObjectName,
|
||||
pFilter,
|
||||
phr,
|
||||
pName)
|
||||
, m_bReadOnly(FALSE)
|
||||
, m_pTIPFilter(pFilter)
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2
|
||||
, TEXT("CTransInPlaceInputPin::CTransInPlaceInputPin")));
|
||||
|
||||
} // constructor
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements IMemInputPin interface
|
||||
// =================================================================
|
||||
|
||||
|
||||
// If the downstream filter has one then offer that (even if our own output
|
||||
// pin is not using it yet. If the upstream filter chooses it then we will
|
||||
// tell our output pin to ReceiveAllocator).
|
||||
// Else if our output pin is using an allocator then offer that.
|
||||
// ( This could mean offering the upstream filter his own allocator,
|
||||
// it could mean offerring our own
|
||||
// ) or it could mean offering the one from downstream
|
||||
// Else fail to offer any allocator at all.
|
||||
|
||||
STDMETHODIMP CTransInPlaceInputPin::GetAllocator(__deref_out IMemAllocator ** ppAllocator)
|
||||
{
|
||||
CheckPointer(ppAllocator,E_POINTER);
|
||||
ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
|
||||
CAutoLock cObjectLock(m_pLock);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
if ( m_pTIPFilter->m_pOutput->IsConnected() ) {
|
||||
// Store the allocator we got
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
|
||||
->GetAllocator( ppAllocator );
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_pTIPFilter->OutputPin()->SetAllocator( *ppAllocator );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Help upstream filter (eg TIP filter which is having to do a copy)
|
||||
// by providing a temp allocator here - we'll never use
|
||||
// this allocator because when our output is connected we'll
|
||||
// reconnect this pin
|
||||
hr = CTransformInputPin::GetAllocator( ppAllocator );
|
||||
}
|
||||
return hr;
|
||||
|
||||
} // GetAllocator
|
||||
|
||||
|
||||
|
||||
/* Get told which allocator the upstream output pin is actually going to use */
|
||||
|
||||
|
||||
STDMETHODIMP
|
||||
CTransInPlaceInputPin::NotifyAllocator(
|
||||
IMemAllocator * pAllocator,
|
||||
BOOL bReadOnly)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
CheckPointer(pAllocator,E_POINTER);
|
||||
ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
|
||||
|
||||
CAutoLock cObjectLock(m_pLock);
|
||||
|
||||
m_bReadOnly = bReadOnly;
|
||||
// If we modify data then don't accept the allocator if it's
|
||||
// the same as the output pin's allocator
|
||||
|
||||
// If our output is not connected just accept the allocator
|
||||
// We're never going to use this allocator because when our
|
||||
// output pin is connected we'll reconnect this pin
|
||||
if (!m_pTIPFilter->OutputPin()->IsConnected()) {
|
||||
return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly);
|
||||
}
|
||||
|
||||
// If the allocator is read-only and we're modifying data
|
||||
// and the allocator is the same as the output pin's
|
||||
// then reject
|
||||
if (bReadOnly && m_pTIPFilter->m_bModifiesData) {
|
||||
IMemAllocator *pOutputAllocator =
|
||||
m_pTIPFilter->OutputPin()->PeekAllocator();
|
||||
|
||||
// Make sure we have an output allocator
|
||||
if (pOutputAllocator == NULL) {
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->
|
||||
GetAllocator(&pOutputAllocator);
|
||||
if(FAILED(hr)) {
|
||||
hr = CreateMemoryAllocator(&pOutputAllocator);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator);
|
||||
pOutputAllocator->Release();
|
||||
}
|
||||
}
|
||||
if (pAllocator == pOutputAllocator) {
|
||||
hr = E_FAIL;
|
||||
} else if(SUCCEEDED(hr)) {
|
||||
// Must copy so set the allocator properties on the output
|
||||
ALLOCATOR_PROPERTIES Props, Actual;
|
||||
hr = pAllocator->GetProperties(&Props);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = pOutputAllocator->SetProperties(&Props, &Actual);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
if ( (Props.cBuffers > Actual.cBuffers)
|
||||
|| (Props.cbBuffer > Actual.cbBuffer)
|
||||
|| (Props.cbAlign > Actual.cbAlign)
|
||||
) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the allocator on the output pin
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
|
||||
->NotifyAllocator( pOutputAllocator, FALSE );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
|
||||
->NotifyAllocator( pAllocator, bReadOnly );
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_pTIPFilter->OutputPin()->SetAllocator( pAllocator );
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
|
||||
// It's possible that the old and the new are the same thing.
|
||||
// AddRef before release ensures that we don't unload it.
|
||||
pAllocator->AddRef();
|
||||
|
||||
if( m_pAllocator != NULL )
|
||||
m_pAllocator->Release();
|
||||
|
||||
m_pAllocator = pAllocator; // We have an allocator for the input pin
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
||||
} // NotifyAllocator
|
||||
|
||||
|
||||
// EnumMediaTypes
|
||||
// - pass through to our downstream filter
|
||||
STDMETHODIMP CTransInPlaceInputPin::EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum )
|
||||
{
|
||||
// Can only pass through if connected
|
||||
if( !m_pTIPFilter->m_pOutput->IsConnected() )
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
|
||||
return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes( ppEnum );
|
||||
|
||||
} // EnumMediaTypes
|
||||
|
||||
|
||||
// CheckMediaType
|
||||
// - agree to anything if not connected,
|
||||
// otherwise pass through to the downstream filter.
|
||||
// This assumes that the filter does not change the media type.
|
||||
|
||||
HRESULT CTransInPlaceInputPin::CheckMediaType(const CMediaType *pmt )
|
||||
{
|
||||
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
|
||||
if (hr!=S_OK) return hr;
|
||||
|
||||
if( m_pTIPFilter->m_pOutput->IsConnected() )
|
||||
return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt );
|
||||
else
|
||||
return S_OK;
|
||||
|
||||
} // CheckMediaType
|
||||
|
||||
|
||||
// If upstream asks us what our requirements are, we will try to ask downstream
|
||||
// if that doesn't work, we'll just take the defaults.
|
||||
STDMETHODIMP
|
||||
CTransInPlaceInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES *pProps)
|
||||
{
|
||||
|
||||
if( m_pTIPFilter->m_pOutput->IsConnected() )
|
||||
return m_pTIPFilter->OutputPin()
|
||||
->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps );
|
||||
else
|
||||
return E_NOTIMPL;
|
||||
|
||||
} // GetAllocatorRequirements
|
||||
|
||||
|
||||
// CTransInPlaceInputPin::CompleteConnect() calls CBaseInputPin::CompleteConnect()
|
||||
// and then calls CTransInPlaceFilter::CompleteConnect(). It does this because
|
||||
// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not
|
||||
// want to reconnect a pin if CBaseInputPin::CompleteConnect() fails.
|
||||
HRESULT
|
||||
CTransInPlaceInputPin::CompleteConnect(IPin *pReceivePin)
|
||||
{
|
||||
HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
|
||||
} // CompleteConnect
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements the CTransInPlaceOutputPin class
|
||||
// =================================================================
|
||||
|
||||
|
||||
// constructor
|
||||
|
||||
CTransInPlaceOutputPin::CTransInPlaceOutputPin(
|
||||
__in_opt LPCTSTR pObjectName,
|
||||
__inout CTransInPlaceFilter *pFilter,
|
||||
__inout HRESULT * phr,
|
||||
__in_opt LPCWSTR pPinName)
|
||||
: CTransformOutputPin( pObjectName
|
||||
, pFilter
|
||||
, phr
|
||||
, pPinName),
|
||||
m_pTIPFilter(pFilter)
|
||||
{
|
||||
DbgLog(( LOG_TRACE, 2
|
||||
, TEXT("CTransInPlaceOutputPin::CTransInPlaceOutputPin")));
|
||||
|
||||
} // constructor
|
||||
|
||||
|
||||
// EnumMediaTypes
|
||||
// - pass through to our upstream filter
|
||||
STDMETHODIMP CTransInPlaceOutputPin::EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum )
|
||||
{
|
||||
// Can only pass through if connected.
|
||||
if( ! m_pTIPFilter->m_pInput->IsConnected() )
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
|
||||
return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum );
|
||||
|
||||
} // EnumMediaTypes
|
||||
|
||||
|
||||
|
||||
// CheckMediaType
|
||||
// - agree to anything if not connected,
|
||||
// otherwise pass through to the upstream filter.
|
||||
|
||||
HRESULT CTransInPlaceOutputPin::CheckMediaType(const CMediaType *pmt )
|
||||
{
|
||||
// Don't accept any output pin type changes if we're copying
|
||||
// between allocators - it's too late to change the input
|
||||
// allocator size.
|
||||
if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) {
|
||||
if (*pmt == m_mt) {
|
||||
return S_OK;
|
||||
} else {
|
||||
return VFW_E_TYPE_NOT_ACCEPTED;
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes the type does not change. That's why we're calling
|
||||
// CheckINPUTType here on the OUTPUT pin.
|
||||
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
|
||||
if (hr!=S_OK) return hr;
|
||||
|
||||
if( m_pTIPFilter->m_pInput->IsConnected() )
|
||||
return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt );
|
||||
else
|
||||
return S_OK;
|
||||
|
||||
} // CheckMediaType
|
||||
|
||||
|
||||
/* Save the allocator pointer in the output pin
|
||||
*/
|
||||
void
|
||||
CTransInPlaceOutputPin::SetAllocator(IMemAllocator * pAllocator)
|
||||
{
|
||||
pAllocator->AddRef();
|
||||
if (m_pAllocator) {
|
||||
m_pAllocator->Release();
|
||||
}
|
||||
m_pAllocator = pAllocator;
|
||||
} // SetAllocator
|
||||
|
||||
|
||||
// CTransInPlaceOutputPin::CompleteConnect() calls CBaseOutputPin::CompleteConnect()
|
||||
// and then calls CTransInPlaceFilter::CompleteConnect(). It does this because
|
||||
// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not want to
|
||||
// reconnect a pin if CBaseOutputPin::CompleteConnect() fails.
|
||||
// CBaseOutputPin::CompleteConnect() often fails when our output pin is being connected
|
||||
// to the Video Mixing Renderer.
|
||||
HRESULT
|
||||
CTransInPlaceOutputPin::CompleteConnect(IPin *pReceivePin)
|
||||
{
|
||||
HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
|
||||
} // CompleteConnect
|
|
@ -0,0 +1,250 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: TransIP.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines classes from which simple
|
||||
// Transform-In-Place filters may be derived.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// The difference between this and Transfrm.h is that Transfrm copies the data.
|
||||
//
|
||||
// It assumes the filter has one input and one output stream, and has no
|
||||
// interest in memory management, interface negotiation or anything else.
|
||||
//
|
||||
// Derive your class from this, and supply Transform and the media type/format
|
||||
// negotiation functions. Implement that class, compile and link and
|
||||
// you're done.
|
||||
|
||||
|
||||
#ifndef __TRANSIP__
|
||||
#define __TRANSIP__
|
||||
|
||||
// ======================================================================
|
||||
// This is the com object that represents a simple transform filter. It
|
||||
// supports IBaseFilter, IMediaFilter and two pins through nested interfaces
|
||||
// ======================================================================
|
||||
|
||||
class CTransInPlaceFilter;
|
||||
|
||||
// Several of the pin functions call filter functions to do the work,
|
||||
// so you can often use the pin classes unaltered, just overriding the
|
||||
// functions in CTransInPlaceFilter. If that's not enough and you want
|
||||
// to derive your own pin class, override GetPin in the filter to supply
|
||||
// your own pin classes to the filter.
|
||||
|
||||
// ==================================================
|
||||
// Implements the input pin
|
||||
// ==================================================
|
||||
|
||||
class CTransInPlaceInputPin : public CTransformInputPin
|
||||
{
|
||||
|
||||
protected:
|
||||
CTransInPlaceFilter * const m_pTIPFilter; // our filter
|
||||
BOOL m_bReadOnly; // incoming stream is read only
|
||||
|
||||
public:
|
||||
|
||||
CTransInPlaceInputPin(
|
||||
__in_opt LPCTSTR pObjectName,
|
||||
__inout CTransInPlaceFilter *pFilter,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
|
||||
// --- IMemInputPin -----
|
||||
|
||||
// Provide an enumerator for media types by getting one from downstream
|
||||
STDMETHODIMP EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum );
|
||||
|
||||
// Say whether media type is acceptable.
|
||||
HRESULT CheckMediaType(const CMediaType* pmt);
|
||||
|
||||
// Return our upstream allocator
|
||||
STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator);
|
||||
|
||||
// get told which allocator the upstream output pin is actually
|
||||
// going to use.
|
||||
STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator,
|
||||
BOOL bReadOnly);
|
||||
|
||||
// Allow the filter to see what allocator we have
|
||||
// N.B. This does NOT AddRef
|
||||
__out IMemAllocator * PeekAllocator() const
|
||||
{ return m_pAllocator; }
|
||||
|
||||
// Pass this on downstream if it ever gets called.
|
||||
STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES *pProps);
|
||||
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
inline const BOOL ReadOnly() { return m_bReadOnly ; }
|
||||
|
||||
}; // CTransInPlaceInputPin
|
||||
|
||||
// ==================================================
|
||||
// Implements the output pin
|
||||
// ==================================================
|
||||
|
||||
class CTransInPlaceOutputPin : public CTransformOutputPin
|
||||
{
|
||||
|
||||
protected:
|
||||
// m_pFilter points to our CBaseFilter
|
||||
CTransInPlaceFilter * const m_pTIPFilter;
|
||||
|
||||
public:
|
||||
|
||||
CTransInPlaceOutputPin(
|
||||
__in_opt LPCTSTR pObjectName,
|
||||
__inout CTransInPlaceFilter *pFilter,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR pName);
|
||||
|
||||
|
||||
// --- CBaseOutputPin ------------
|
||||
|
||||
// negotiate the allocator and its buffer size/count
|
||||
// Insists on using our own allocator. (Actually the one upstream of us).
|
||||
// We don't override this - instead we just agree the default
|
||||
// then let the upstream filter decide for itself on reconnect
|
||||
// virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc);
|
||||
|
||||
// Provide a media type enumerator. Get it from upstream.
|
||||
STDMETHODIMP EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum );
|
||||
|
||||
// Say whether media type is acceptable.
|
||||
HRESULT CheckMediaType(const CMediaType* pmt);
|
||||
|
||||
// This just saves the allocator being used on the output pin
|
||||
// Also called by input pin's GetAllocator()
|
||||
void SetAllocator(IMemAllocator * pAllocator);
|
||||
|
||||
__out_opt IMemInputPin * ConnectedIMemInputPin()
|
||||
{ return m_pInputPin; }
|
||||
|
||||
// Allow the filter to see what allocator we have
|
||||
// N.B. This does NOT AddRef
|
||||
__out IMemAllocator * PeekAllocator() const
|
||||
{ return m_pAllocator; }
|
||||
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
}; // CTransInPlaceOutputPin
|
||||
|
||||
|
||||
class AM_NOVTABLE CTransInPlaceFilter : public CTransformFilter
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// map getpin/getpincount for base enum of pins to owner
|
||||
// override this to return more specialised pin objects
|
||||
|
||||
virtual CBasePin *GetPin(int n);
|
||||
|
||||
public:
|
||||
|
||||
// Set bModifiesData == false if your derived filter does
|
||||
// not modify the data samples (for instance it's just copying
|
||||
// them somewhere else or looking at the timestamps).
|
||||
|
||||
CTransInPlaceFilter(__in_opt LPCTSTR, __inout_opt LPUNKNOWN, REFCLSID clsid, __inout HRESULT *,
|
||||
bool bModifiesData = true);
|
||||
#ifdef UNICODE
|
||||
CTransInPlaceFilter(__in_opt LPCSTR, __inout_opt LPUNKNOWN, REFCLSID clsid, __inout HRESULT *,
|
||||
bool bModifiesData = true);
|
||||
#endif
|
||||
// The following are defined to avoid undefined pure virtuals.
|
||||
// Even if they are never called, they will give linkage warnings/errors
|
||||
|
||||
// We override EnumMediaTypes to bypass the transform class enumerator
|
||||
// which would otherwise call this.
|
||||
HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType)
|
||||
{ DbgBreak("CTransInPlaceFilter::GetMediaType should never be called");
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// This is called when we actually have to provide our own allocator.
|
||||
HRESULT DecideBufferSize(IMemAllocator*, __inout ALLOCATOR_PROPERTIES *);
|
||||
|
||||
// The functions which call this in CTransform are overridden in this
|
||||
// class to call CheckInputType with the assumption that the type
|
||||
// does not change. In Debug builds some calls will be made and
|
||||
// we just ensure that they do not assert.
|
||||
HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)
|
||||
{
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
|
||||
// =================================================================
|
||||
// ----- You may want to override this -----------------------------
|
||||
// =================================================================
|
||||
|
||||
HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin);
|
||||
|
||||
// chance to customize the transform process
|
||||
virtual HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
// =================================================================
|
||||
// ----- You MUST override these -----------------------------------
|
||||
// =================================================================
|
||||
|
||||
virtual HRESULT Transform(IMediaSample *pSample) PURE;
|
||||
|
||||
// this goes in the factory template table to create new instances
|
||||
// static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
|
||||
|
||||
|
||||
#ifdef PERF
|
||||
// Override to register performance measurement with a less generic string
|
||||
// You should do this to avoid confusion with other filters
|
||||
virtual void RegisterPerfId()
|
||||
{m_idTransInPlace = MSR_REGISTER(TEXT("TransInPlace"));}
|
||||
#endif // PERF
|
||||
|
||||
|
||||
// implementation details
|
||||
|
||||
protected:
|
||||
|
||||
__out_opt IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource);
|
||||
|
||||
#ifdef PERF
|
||||
int m_idTransInPlace; // performance measuring id
|
||||
#endif // PERF
|
||||
bool m_bModifiesData; // Does this filter change the data?
|
||||
|
||||
// these hold our input and output pins
|
||||
|
||||
friend class CTransInPlaceInputPin;
|
||||
friend class CTransInPlaceOutputPin;
|
||||
|
||||
__out CTransInPlaceInputPin *InputPin() const
|
||||
{
|
||||
return (CTransInPlaceInputPin *)m_pInput;
|
||||
};
|
||||
__out CTransInPlaceOutputPin *OutputPin() const
|
||||
{
|
||||
return (CTransInPlaceOutputPin *)m_pOutput;
|
||||
};
|
||||
|
||||
// Helper to see if the input and output types match
|
||||
BOOL TypesMatch()
|
||||
{
|
||||
return InputPin()->CurrentMediaType() ==
|
||||
OutputPin()->CurrentMediaType();
|
||||
}
|
||||
|
||||
// Are the input and output allocators different?
|
||||
BOOL UsingDifferentAllocators() const
|
||||
{
|
||||
return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator();
|
||||
}
|
||||
}; // CTransInPlaceFilter
|
||||
|
||||
#endif /* __TRANSIP__ */
|
||||
|
|
@ -0,0 +1,746 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: VideoCtl.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include "ddmm.h"
|
||||
|
||||
// Load a string from the resource file string table. The buffer must be at
|
||||
// least STR_MAX_LENGTH bytes. The easiest way to use this is to declare a
|
||||
// buffer in the property page class and use it for all string loading. It
|
||||
// cannot be static as multiple property pages may be active simultaneously
|
||||
|
||||
LPTSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPTSTR pBuffer, int iResourceID)
|
||||
{
|
||||
if (LoadString(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) {
|
||||
return TEXT("");
|
||||
}
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
LPSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPSTR pBuffer, int iResourceID)
|
||||
{
|
||||
if (LoadStringA(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) {
|
||||
return "";
|
||||
}
|
||||
return pBuffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Property pages typically are called through their OLE interfaces. These
|
||||
// use UNICODE strings regardless of how the binary is built. So when we
|
||||
// load strings from the resource file we sometimes want to convert them
|
||||
// to UNICODE. This method is passed the target UNICODE buffer and does a
|
||||
// convert after loading the string (if built UNICODE this is not needed)
|
||||
// On WinNT we can explicitly call LoadStringW which saves two conversions
|
||||
|
||||
#ifndef UNICODE
|
||||
|
||||
LPWSTR WINAPI WideStringFromResource(__out_ecount(STR_MAX_LENGTH) LPWSTR pBuffer, int iResourceID)
|
||||
{
|
||||
*pBuffer = 0;
|
||||
|
||||
if (g_amPlatform == VER_PLATFORM_WIN32_NT) {
|
||||
LoadStringW(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH);
|
||||
} else {
|
||||
|
||||
CHAR szBuffer[STR_MAX_LENGTH];
|
||||
DWORD dwStringLength = LoadString(g_hInst,iResourceID,szBuffer,STR_MAX_LENGTH);
|
||||
// if we loaded a string convert it to wide characters, ensuring
|
||||
// that we also null terminate the result.
|
||||
if (dwStringLength++) {
|
||||
MultiByteToWideChar(CP_ACP,0,szBuffer,dwStringLength,pBuffer,STR_MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Helper function to calculate the size of the dialog
|
||||
|
||||
BOOL WINAPI GetDialogSize(int iResourceID,
|
||||
DLGPROC pDlgProc,
|
||||
LPARAM lParam,
|
||||
__out SIZE *pResult)
|
||||
{
|
||||
RECT rc;
|
||||
HWND hwnd;
|
||||
|
||||
// Create a temporary property page
|
||||
|
||||
hwnd = CreateDialogParam(g_hInst,
|
||||
MAKEINTRESOURCE(iResourceID),
|
||||
GetDesktopWindow(),
|
||||
pDlgProc,
|
||||
lParam);
|
||||
if (hwnd == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GetWindowRect(hwnd, &rc);
|
||||
pResult->cx = rc.right - rc.left;
|
||||
pResult->cy = rc.bottom - rc.top;
|
||||
|
||||
DestroyWindow(hwnd);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Class that aggregates on the IDirectDraw interface. Although DirectDraw
|
||||
// has the ability in its interfaces to be aggregated they're not currently
|
||||
// implemented. This makes it difficult for various parts of Quartz that want
|
||||
// to aggregate these interfaces. In particular the video renderer passes out
|
||||
// media samples that expose IDirectDraw and IDirectDrawSurface. The filter
|
||||
// graph manager also exposes IDirectDraw as a plug in distributor. For these
|
||||
// objects we provide these aggregation classes that republish the interfaces
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
|
||||
// Do we have this interface
|
||||
|
||||
if (riid == IID_IDirectDraw) {
|
||||
return GetInterface((IDirectDraw *)this,ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid,ppv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::Compact()
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->Compact();
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::CreateClipper(DWORD dwFlags, __deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper, __inout_opt IUnknown *pUnkOuter)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->CreateClipper(dwFlags,lplpDDClipper,pUnkOuter);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::CreatePalette(DWORD dwFlags,
|
||||
__in LPPALETTEENTRY lpColorTable,
|
||||
__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette,
|
||||
__inout_opt IUnknown *pUnkOuter)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->CreatePalette(dwFlags,lpColorTable,lplpDDPalette,pUnkOuter);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::CreateSurface(__in LPDDSURFACEDESC lpDDSurfaceDesc,
|
||||
__deref_out LPDIRECTDRAWSURFACE *lplpDDSurface,
|
||||
__inout_opt IUnknown *pUnkOuter)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->CreateSurface(lpDDSurfaceDesc,lplpDDSurface,pUnkOuter);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::DuplicateSurface(__in LPDIRECTDRAWSURFACE lpDDSurface,
|
||||
__deref_out LPDIRECTDRAWSURFACE *lplpDupDDSurface)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->DuplicateSurface(lpDDSurface,lplpDupDDSurface);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::EnumDisplayModes(DWORD dwSurfaceDescCount,
|
||||
__in LPDDSURFACEDESC lplpDDSurfaceDescList,
|
||||
__in LPVOID lpContext,
|
||||
__in LPDDENUMMODESCALLBACK lpEnumCallback)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->EnumDisplayModes(dwSurfaceDescCount,lplpDDSurfaceDescList,lpContext,lpEnumCallback);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::EnumSurfaces(DWORD dwFlags,
|
||||
__in LPDDSURFACEDESC lpDDSD,
|
||||
__in LPVOID lpContext,
|
||||
__in LPDDENUMSURFACESCALLBACK lpEnumCallback)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->EnumSurfaces(dwFlags,lpDDSD,lpContext,lpEnumCallback);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::FlipToGDISurface()
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->FlipToGDISurface();
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::GetCaps(__out LPDDCAPS lpDDDriverCaps,__out LPDDCAPS lpDDHELCaps)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->GetCaps(lpDDDriverCaps,lpDDHELCaps);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::GetDisplayMode(__out LPDDSURFACEDESC lpDDSurfaceDesc)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->GetDisplayMode(lpDDSurfaceDesc);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::GetFourCCCodes(__inout LPDWORD lpNumCodes,__out_ecount(*lpNumCodes) LPDWORD lpCodes)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->GetFourCCCodes(lpNumCodes,lpCodes);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::GetGDISurface(__deref_out LPDIRECTDRAWSURFACE *lplpGDIDDSurface)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->GetGDISurface(lplpGDIDDSurface);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::GetMonitorFrequency(__out LPDWORD lpdwFrequency)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->GetMonitorFrequency(lpdwFrequency);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::GetScanLine(__out LPDWORD lpdwScanLine)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->GetScanLine(lpdwScanLine);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::GetVerticalBlankStatus(__out LPBOOL lpblsInVB)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->GetVerticalBlankStatus(lpblsInVB);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::Initialize(__in GUID *lpGUID)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->Initialize(lpGUID);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::RestoreDisplayMode()
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->RestoreDisplayMode();
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::SetCooperativeLevel(HWND hWnd,DWORD dwFlags)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->SetCooperativeLevel(hWnd,dwFlags);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->SetDisplayMode(dwWidth,dwHeight,dwBpp);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDirectDraw::WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent)
|
||||
{
|
||||
ASSERT(m_pDirectDraw);
|
||||
return m_pDirectDraw->WaitForVerticalBlank(dwFlags,hEvent);
|
||||
}
|
||||
|
||||
|
||||
// Class that aggregates an IDirectDrawSurface interface. Although DirectDraw
|
||||
// has the ability in its interfaces to be aggregated they're not currently
|
||||
// implemented. This makes it difficult for various parts of Quartz that want
|
||||
// to aggregate these interfaces. In particular the video renderer passes out
|
||||
// media samples that expose IDirectDraw and IDirectDrawSurface. The filter
|
||||
// graph manager also exposes IDirectDraw as a plug in distributor. For these
|
||||
// objects we provide these aggregation classes that republish the interfaces
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
|
||||
// Do we have this interface
|
||||
|
||||
if (riid == IID_IDirectDrawSurface) {
|
||||
return GetInterface((IDirectDrawSurface *)this,ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid,ppv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::AddAttachedSurface(__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->AddAttachedSurface(lpDDSAttachedSurface);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::AddOverlayDirtyRect(__in LPRECT lpRect)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->AddOverlayDirtyRect(lpRect);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::Blt(__in LPRECT lpDestRect,
|
||||
__in LPDIRECTDRAWSURFACE lpDDSrcSurface,
|
||||
__in LPRECT lpSrcRect,
|
||||
DWORD dwFlags,
|
||||
__in LPDDBLTFX lpDDBltFx)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->Blt(lpDestRect,lpDDSrcSurface,lpSrcRect,dwFlags,lpDDBltFx);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::BltBatch(__in_ecount(dwCount) LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->BltBatch(lpDDBltBatch,dwCount,dwFlags);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::BltFast(DWORD dwX,DWORD dwY,
|
||||
__in LPDIRECTDRAWSURFACE lpDDSrcSurface,
|
||||
__in LPRECT lpSrcRect,
|
||||
DWORD dwTrans)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->BltFast(dwX,dwY,lpDDSrcSurface,lpSrcRect,dwTrans);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::DeleteAttachedSurface(DWORD dwFlags,
|
||||
__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->DeleteAttachedSurface(dwFlags,lpDDSAttachedSurface);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::EnumAttachedSurfaces(__in LPVOID lpContext,
|
||||
__in LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->EnumAttachedSurfaces(lpContext,lpEnumSurfacesCallback);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::EnumOverlayZOrders(DWORD dwFlags,
|
||||
__in LPVOID lpContext,
|
||||
__in LPDDENUMSURFACESCALLBACK lpfnCallback)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->EnumOverlayZOrders(dwFlags,lpContext,lpfnCallback);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::Flip(__in LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->Flip(lpDDSurfaceTargetOverride,dwFlags);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetAttachedSurface(__in LPDDSCAPS lpDDSCaps,
|
||||
__deref_out LPDIRECTDRAWSURFACE *lplpDDAttachedSurface)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetAttachedSurface(lpDDSCaps,lplpDDAttachedSurface);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetBltStatus(DWORD dwFlags)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetBltStatus(dwFlags);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetCaps(__out LPDDSCAPS lpDDSCaps)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetCaps(lpDDSCaps);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetClipper(__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetClipper(lplpDDClipper);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetColorKey(DWORD dwFlags,__out LPDDCOLORKEY lpDDColorKey)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetColorKey(dwFlags,lpDDColorKey);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetDC(__out HDC *lphDC)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetDC(lphDC);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetFlipStatus(DWORD dwFlags)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetFlipStatus(dwFlags);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetOverlayPosition(__out LPLONG lpdwX,__out LPLONG lpdwY)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetOverlayPosition(lpdwX,lpdwY);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetPalette(__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetPalette(lplpDDPalette);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetPixelFormat(__out LPDDPIXELFORMAT lpDDPixelFormat)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->GetPixelFormat(lpDDPixelFormat);
|
||||
}
|
||||
|
||||
|
||||
// A bit of a warning here: Our media samples in DirectShow aggregate on
|
||||
// IDirectDraw and IDirectDrawSurface (ie are available through IMediaSample
|
||||
// by QueryInterface). Unfortunately the underlying DirectDraw code cannot
|
||||
// be aggregated so we have to use these classes. The snag is that when we
|
||||
// call a different surface and pass in this interface as perhaps the source
|
||||
// surface the call will fail because DirectDraw dereferences the pointer to
|
||||
// get at its private data structures. Therefore we supply this workaround to give
|
||||
// access to the real IDirectDraw surface. A filter can call GetSurfaceDesc
|
||||
// and we will fill in the lpSurface pointer with the real underlying surface
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::GetSurfaceDesc(__out LPDDSURFACEDESC lpDDSurfaceDesc)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
|
||||
// First call down to the underlying DirectDraw
|
||||
|
||||
HRESULT hr = m_pDirectDrawSurface->GetSurfaceDesc(lpDDSurfaceDesc);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Store the real DirectDrawSurface interface
|
||||
lpDDSurfaceDesc->lpSurface = m_pDirectDrawSurface;
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::Initialize(__in LPDIRECTDRAW lpDD,__in LPDDSURFACEDESC lpDDSurfaceDesc)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->Initialize(lpDD,lpDDSurfaceDesc);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::IsLost()
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->IsLost();
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::Lock(__in LPRECT lpDestRect,
|
||||
__inout LPDDSURFACEDESC lpDDSurfaceDesc,
|
||||
DWORD dwFlags,
|
||||
HANDLE hEvent)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->Lock(lpDestRect,lpDDSurfaceDesc,dwFlags,hEvent);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::ReleaseDC(HDC hDC)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->ReleaseDC(hDC);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::Restore()
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->Restore();
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::SetClipper(__in LPDIRECTDRAWCLIPPER lpDDClipper)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->SetClipper(lpDDClipper);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::SetColorKey(DWORD dwFlags,__in LPDDCOLORKEY lpDDColorKey)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->SetColorKey(dwFlags,lpDDColorKey);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::SetOverlayPosition(LONG dwX,LONG dwY)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->SetOverlayPosition(dwX,dwY);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::SetPalette(__in LPDIRECTDRAWPALETTE lpDDPalette)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->SetPalette(lpDDPalette);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::Unlock(__in LPVOID lpSurfaceData)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->Unlock(lpSurfaceData);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::UpdateOverlay(__in LPRECT lpSrcRect,
|
||||
__in LPDIRECTDRAWSURFACE lpDDDestSurface,
|
||||
__in LPRECT lpDestRect,
|
||||
DWORD dwFlags,
|
||||
__in LPDDOVERLAYFX lpDDOverlayFX)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->UpdateOverlay(lpSrcRect,lpDDDestSurface,lpDestRect,dwFlags,lpDDOverlayFX);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::UpdateOverlayDisplay(DWORD dwFlags)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->UpdateOverlayDisplay(dwFlags);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CAggDrawSurface::UpdateOverlayZOrder(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSReference)
|
||||
{
|
||||
ASSERT(m_pDirectDrawSurface);
|
||||
return m_pDirectDrawSurface->UpdateOverlayZOrder(dwFlags,lpDDSReference);
|
||||
}
|
||||
|
||||
|
||||
// DirectShow must work on multiple platforms. In particular, it also runs on
|
||||
// Windows NT 3.51 which does not have DirectDraw capabilities. The filters
|
||||
// cannot therefore link statically to the DirectDraw library. To make their
|
||||
// lives that little bit easier we provide this class that manages loading
|
||||
// and unloading the library and creating the initial IDirectDraw interface
|
||||
|
||||
CLoadDirectDraw::CLoadDirectDraw() :
|
||||
m_pDirectDraw(NULL),
|
||||
m_hDirectDraw(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Destructor forces unload
|
||||
|
||||
CLoadDirectDraw::~CLoadDirectDraw()
|
||||
{
|
||||
ReleaseDirectDraw();
|
||||
|
||||
if (m_hDirectDraw) {
|
||||
NOTE("Unloading library");
|
||||
FreeLibrary(m_hDirectDraw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We can't be sure that DirectDraw is always available so we can't statically
|
||||
// link to the library. Therefore we load the library, get the function entry
|
||||
// point addresses and call them to create the driver objects. We return S_OK
|
||||
// if we manage to load DirectDraw correctly otherwise we return E_NOINTERFACE
|
||||
// We initialise a DirectDraw instance by explicitely loading the library and
|
||||
// calling GetProcAddress on the DirectDrawCreate entry point that it exports
|
||||
|
||||
// On a multi monitor system, we can get the DirectDraw object for any
|
||||
// monitor (device) with the optional szDevice parameter
|
||||
|
||||
HRESULT CLoadDirectDraw::LoadDirectDraw(__in LPSTR szDevice)
|
||||
{
|
||||
PDRAWCREATE pDrawCreate;
|
||||
PDRAWENUM pDrawEnum;
|
||||
LPDIRECTDRAWENUMERATEEXA pDrawEnumEx;
|
||||
HRESULT hr = NOERROR;
|
||||
|
||||
NOTE("Entering DoLoadDirectDraw");
|
||||
|
||||
// Is DirectDraw already loaded
|
||||
|
||||
if (m_pDirectDraw) {
|
||||
NOTE("Already loaded");
|
||||
ASSERT(m_hDirectDraw);
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Make sure the library is available
|
||||
|
||||
if(!m_hDirectDraw)
|
||||
{
|
||||
UINT ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX);
|
||||
m_hDirectDraw = LoadLibrary(TEXT("DDRAW.DLL"));
|
||||
SetErrorMode(ErrorMode);
|
||||
|
||||
if (m_hDirectDraw == NULL) {
|
||||
DbgLog((LOG_ERROR,1,TEXT("Can't load DDRAW.DLL")));
|
||||
NOTE("No library");
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the DLL address for the creator function
|
||||
|
||||
pDrawCreate = (PDRAWCREATE)GetProcAddress(m_hDirectDraw,"DirectDrawCreate");
|
||||
// force ANSI, we assume it
|
||||
pDrawEnum = (PDRAWENUM)GetProcAddress(m_hDirectDraw,"DirectDrawEnumerateA");
|
||||
pDrawEnumEx = (LPDIRECTDRAWENUMERATEEXA)GetProcAddress(m_hDirectDraw,
|
||||
"DirectDrawEnumerateExA");
|
||||
|
||||
// We don't NEED DirectDrawEnumerateEx, that's just for multimon stuff
|
||||
if (pDrawCreate == NULL || pDrawEnum == NULL) {
|
||||
DbgLog((LOG_ERROR,1,TEXT("Can't get functions: Create=%x Enum=%x"),
|
||||
pDrawCreate, pDrawEnum));
|
||||
NOTE("No entry point");
|
||||
ReleaseDirectDraw();
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
DbgLog((LOG_TRACE,3,TEXT("Creating DDraw for device %s"),
|
||||
szDevice ? szDevice : "<NULL>"));
|
||||
|
||||
// Create a DirectDraw display provider for this device, using the fancy
|
||||
// multimon-aware version, if it exists
|
||||
if (pDrawEnumEx)
|
||||
m_pDirectDraw = DirectDrawCreateFromDeviceEx(szDevice, pDrawCreate,
|
||||
pDrawEnumEx);
|
||||
else
|
||||
m_pDirectDraw = DirectDrawCreateFromDevice(szDevice, pDrawCreate,
|
||||
pDrawEnum);
|
||||
|
||||
if (m_pDirectDraw == NULL) {
|
||||
DbgLog((LOG_ERROR,1,TEXT("Can't create DDraw")));
|
||||
NOTE("No instance");
|
||||
ReleaseDirectDraw();
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Called to release any DirectDraw provider we previously loaded. We may be
|
||||
// called at any time especially when something goes horribly wrong and when
|
||||
// we need to clean up before returning so we can't guarantee that all state
|
||||
// variables are consistent so free only those really allocated allocated
|
||||
// This should only be called once all reference counts have been released
|
||||
|
||||
void CLoadDirectDraw::ReleaseDirectDraw()
|
||||
{
|
||||
NOTE("Releasing DirectDraw driver");
|
||||
|
||||
// Release any DirectDraw provider interface
|
||||
|
||||
if (m_pDirectDraw) {
|
||||
NOTE("Releasing instance");
|
||||
m_pDirectDraw->Release();
|
||||
m_pDirectDraw = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Return NOERROR (S_OK) if DirectDraw has been loaded by this object
|
||||
|
||||
HRESULT CLoadDirectDraw::IsDirectDrawLoaded()
|
||||
{
|
||||
NOTE("Entering IsDirectDrawLoaded");
|
||||
|
||||
if (m_pDirectDraw == NULL) {
|
||||
NOTE("DirectDraw not loaded");
|
||||
return S_FALSE;
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Return the IDirectDraw interface we look after
|
||||
|
||||
LPDIRECTDRAW CLoadDirectDraw::GetDirectDraw()
|
||||
{
|
||||
NOTE("Entering GetDirectDraw");
|
||||
|
||||
if (m_pDirectDraw == NULL) {
|
||||
NOTE("No DirectDraw");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NOTE("Returning DirectDraw");
|
||||
m_pDirectDraw->AddRef();
|
||||
return m_pDirectDraw;
|
||||
}
|
||||
|
||||
|
||||
// Are we running on Direct Draw version 1? We need to find out as
|
||||
// we rely on specific bug fixes in DirectDraw 2 for fullscreen playback. To
|
||||
// find out, we simply see if it supports IDirectDraw2. Only version 2 and
|
||||
// higher support this.
|
||||
|
||||
BOOL CLoadDirectDraw::IsDirectDrawVersion1()
|
||||
{
|
||||
|
||||
if (m_pDirectDraw == NULL)
|
||||
return FALSE;
|
||||
|
||||
IDirectDraw2 *p = NULL;
|
||||
HRESULT hr = m_pDirectDraw->QueryInterface(IID_IDirectDraw2, (void **)&p);
|
||||
if (p)
|
||||
p->Release();
|
||||
if (hr == NOERROR) {
|
||||
DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 2 or greater")));
|
||||
return FALSE;
|
||||
} else {
|
||||
DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 1")));
|
||||
return TRUE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: VideoCtl.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __VIDEOCTL__
|
||||
#define __VIDEOCTL__
|
||||
|
||||
// These help with property page implementations. The first can be used to
|
||||
// load any string from a resource file. The buffer to load into is passed
|
||||
// as an input parameter. The same buffer is the return value if the string
|
||||
// was found otherwise it returns TEXT(""). The GetDialogSize is passed the
|
||||
// resource ID of a dialog box and returns the size of it in screen pixels
|
||||
|
||||
#define STR_MAX_LENGTH 256
|
||||
LPTSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPTSTR pBuffer, int iResourceID);
|
||||
|
||||
#ifdef UNICODE
|
||||
#define WideStringFromResource StringFromResource
|
||||
LPSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPSTR pBuffer, int iResourceID);
|
||||
#else
|
||||
LPWSTR WINAPI WideStringFromResource(__out_ecount(STR_MAX_LENGTH) LPWSTR pBuffer, int iResourceID);
|
||||
#endif
|
||||
|
||||
|
||||
BOOL WINAPI GetDialogSize(int iResourceID, // Dialog box resource identifier
|
||||
DLGPROC pDlgProc, // Pointer to dialog procedure
|
||||
LPARAM lParam, // Any user data wanted in pDlgProc
|
||||
__out SIZE *pResult);// Returns the size of dialog box
|
||||
|
||||
// Class that aggregates an IDirectDraw interface
|
||||
|
||||
class CAggDirectDraw : public IDirectDraw, public CUnknown
|
||||
{
|
||||
protected:
|
||||
|
||||
LPDIRECTDRAW m_pDirectDraw;
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);
|
||||
|
||||
// Constructor and destructor
|
||||
|
||||
CAggDirectDraw(__in_opt LPCTSTR pName,__inout_opt LPUNKNOWN pUnk) :
|
||||
CUnknown(pName,pUnk),
|
||||
m_pDirectDraw(NULL) { };
|
||||
|
||||
virtual CAggDirectDraw::~CAggDirectDraw() { };
|
||||
|
||||
// Set the object we should be aggregating
|
||||
void SetDirectDraw(__inout LPDIRECTDRAW pDirectDraw) {
|
||||
m_pDirectDraw = pDirectDraw;
|
||||
}
|
||||
|
||||
// IDirectDraw methods
|
||||
|
||||
STDMETHODIMP Compact();
|
||||
STDMETHODIMP CreateClipper(DWORD dwFlags,__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper,__inout_opt IUnknown *pUnkOuter);
|
||||
STDMETHODIMP CreatePalette(DWORD dwFlags,__in LPPALETTEENTRY lpColorTable,__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette,__inout_opt IUnknown *pUnkOuter);
|
||||
STDMETHODIMP CreateSurface(__in LPDDSURFACEDESC lpDDSurfaceDesc,__deref_out LPDIRECTDRAWSURFACE *lplpDDSurface,__inout_opt IUnknown *pUnkOuter);
|
||||
STDMETHODIMP DuplicateSurface(__in LPDIRECTDRAWSURFACE lpDDSurface,__deref_out LPDIRECTDRAWSURFACE *lplpDupDDSurface);
|
||||
STDMETHODIMP EnumDisplayModes(DWORD dwSurfaceDescCount,__in LPDDSURFACEDESC lplpDDSurfaceDescList,__in LPVOID lpContext,__in LPDDENUMMODESCALLBACK lpEnumCallback);
|
||||
STDMETHODIMP EnumSurfaces(DWORD dwFlags,__in LPDDSURFACEDESC lpDDSD,__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpEnumCallback);
|
||||
STDMETHODIMP FlipToGDISurface();
|
||||
STDMETHODIMP GetCaps(__out LPDDCAPS lpDDDriverCaps,__out LPDDCAPS lpDDHELCaps);
|
||||
STDMETHODIMP GetDisplayMode(__out LPDDSURFACEDESC lpDDSurfaceDesc);
|
||||
STDMETHODIMP GetFourCCCodes(__inout LPDWORD lpNumCodes,__out_ecount(*lpNumCodes) LPDWORD lpCodes);
|
||||
STDMETHODIMP GetGDISurface(__deref_out LPDIRECTDRAWSURFACE *lplpGDIDDSurface);
|
||||
STDMETHODIMP GetMonitorFrequency(__out LPDWORD lpdwFrequency);
|
||||
STDMETHODIMP GetScanLine(__out LPDWORD lpdwScanLine);
|
||||
STDMETHODIMP GetVerticalBlankStatus(__out LPBOOL lpblsInVB);
|
||||
STDMETHODIMP Initialize(__in GUID *lpGUID);
|
||||
STDMETHODIMP RestoreDisplayMode();
|
||||
STDMETHODIMP SetCooperativeLevel(HWND hWnd,DWORD dwFlags);
|
||||
STDMETHODIMP SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp);
|
||||
STDMETHODIMP WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent);
|
||||
};
|
||||
|
||||
|
||||
// Class that aggregates an IDirectDrawSurface interface
|
||||
|
||||
class CAggDrawSurface : public IDirectDrawSurface, public CUnknown
|
||||
{
|
||||
protected:
|
||||
|
||||
LPDIRECTDRAWSURFACE m_pDirectDrawSurface;
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);
|
||||
|
||||
// Constructor and destructor
|
||||
|
||||
CAggDrawSurface(__in_opt LPCTSTR pName,__inout_opt LPUNKNOWN pUnk) :
|
||||
CUnknown(pName,pUnk),
|
||||
m_pDirectDrawSurface(NULL) { };
|
||||
|
||||
virtual ~CAggDrawSurface() { };
|
||||
|
||||
// Set the object we should be aggregating
|
||||
void SetDirectDrawSurface(__inout LPDIRECTDRAWSURFACE pDirectDrawSurface) {
|
||||
m_pDirectDrawSurface = pDirectDrawSurface;
|
||||
}
|
||||
|
||||
// IDirectDrawSurface methods
|
||||
|
||||
STDMETHODIMP AddAttachedSurface(__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface);
|
||||
STDMETHODIMP AddOverlayDirtyRect(__in LPRECT lpRect);
|
||||
STDMETHODIMP Blt(__in LPRECT lpDestRect,__in LPDIRECTDRAWSURFACE lpDDSrcSurface,__in LPRECT lpSrcRect,DWORD dwFlags,__in LPDDBLTFX lpDDBltFx);
|
||||
STDMETHODIMP BltBatch(__in_ecount(dwCount) LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags);
|
||||
STDMETHODIMP BltFast(DWORD dwX,DWORD dwY,__in LPDIRECTDRAWSURFACE lpDDSrcSurface,__in LPRECT lpSrcRect,DWORD dwTrans);
|
||||
STDMETHODIMP DeleteAttachedSurface(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface);
|
||||
STDMETHODIMP EnumAttachedSurfaces(__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback);
|
||||
STDMETHODIMP EnumOverlayZOrders(DWORD dwFlags,__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpfnCallback);
|
||||
STDMETHODIMP Flip(__in LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags);
|
||||
STDMETHODIMP GetAttachedSurface(__in LPDDSCAPS lpDDSCaps,__deref_out LPDIRECTDRAWSURFACE *lplpDDAttachedSurface);
|
||||
STDMETHODIMP GetBltStatus(DWORD dwFlags);
|
||||
STDMETHODIMP GetCaps(__out LPDDSCAPS lpDDSCaps);
|
||||
STDMETHODIMP GetClipper(__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper);
|
||||
STDMETHODIMP GetColorKey(DWORD dwFlags,__out LPDDCOLORKEY lpDDColorKey);
|
||||
STDMETHODIMP GetDC(__out HDC *lphDC);
|
||||
STDMETHODIMP GetFlipStatus(DWORD dwFlags);
|
||||
STDMETHODIMP GetOverlayPosition(__out LPLONG lpdwX,__out LPLONG lpdwY);
|
||||
STDMETHODIMP GetPalette(__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette);
|
||||
STDMETHODIMP GetPixelFormat(__out LPDDPIXELFORMAT lpDDPixelFormat);
|
||||
STDMETHODIMP GetSurfaceDesc(__out LPDDSURFACEDESC lpDDSurfaceDesc);
|
||||
STDMETHODIMP Initialize(__in LPDIRECTDRAW lpDD,__in LPDDSURFACEDESC lpDDSurfaceDesc);
|
||||
STDMETHODIMP IsLost();
|
||||
STDMETHODIMP Lock(__in LPRECT lpDestRect,__inout LPDDSURFACEDESC lpDDSurfaceDesc,DWORD dwFlags,HANDLE hEvent);
|
||||
STDMETHODIMP ReleaseDC(HDC hDC);
|
||||
STDMETHODIMP Restore();
|
||||
STDMETHODIMP SetClipper(__in LPDIRECTDRAWCLIPPER lpDDClipper);
|
||||
STDMETHODIMP SetColorKey(DWORD dwFlags,__in LPDDCOLORKEY lpDDColorKey);
|
||||
STDMETHODIMP SetOverlayPosition(LONG dwX,LONG dwY);
|
||||
STDMETHODIMP SetPalette(__in LPDIRECTDRAWPALETTE lpDDPalette);
|
||||
STDMETHODIMP Unlock(__in LPVOID lpSurfaceData);
|
||||
STDMETHODIMP UpdateOverlay(__in LPRECT lpSrcRect,__in LPDIRECTDRAWSURFACE lpDDDestSurface,__in LPRECT lpDestRect,DWORD dwFlags,__in LPDDOVERLAYFX lpDDOverlayFX);
|
||||
STDMETHODIMP UpdateOverlayDisplay(DWORD dwFlags);
|
||||
STDMETHODIMP UpdateOverlayZOrder(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSReference);
|
||||
};
|
||||
|
||||
|
||||
class CLoadDirectDraw
|
||||
{
|
||||
LPDIRECTDRAW m_pDirectDraw; // The DirectDraw driver instance
|
||||
HINSTANCE m_hDirectDraw; // Handle to the loaded library
|
||||
|
||||
public:
|
||||
|
||||
CLoadDirectDraw();
|
||||
~CLoadDirectDraw();
|
||||
|
||||
HRESULT LoadDirectDraw(__in LPSTR szDevice);
|
||||
void ReleaseDirectDraw();
|
||||
HRESULT IsDirectDrawLoaded();
|
||||
LPDIRECTDRAW GetDirectDraw();
|
||||
BOOL IsDirectDrawVersion1();
|
||||
};
|
||||
|
||||
#endif // __VIDEOCTL__
|
||||
|
|
@ -0,0 +1,468 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: Vtrans.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#include <measure.h>
|
||||
// #include <vtransfr.h> // now in precomp file streams.h
|
||||
|
||||
CVideoTransformFilter::CVideoTransformFilter
|
||||
( __in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, REFCLSID clsid)
|
||||
: CTransformFilter(pName, pUnk, clsid)
|
||||
, m_itrLate(0)
|
||||
, m_nKeyFramePeriod(0) // No QM until we see at least 2 key frames
|
||||
, m_nFramesSinceKeyFrame(0)
|
||||
, m_bSkipping(FALSE)
|
||||
, m_tDecodeStart(0)
|
||||
, m_itrAvgDecode(300000) // 30mSec - probably allows skipping
|
||||
, m_bQualityChanged(FALSE)
|
||||
{
|
||||
#ifdef PERF
|
||||
RegisterPerfId();
|
||||
#endif // PERF
|
||||
}
|
||||
|
||||
|
||||
CVideoTransformFilter::~CVideoTransformFilter()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
||||
// Reset our quality management state
|
||||
|
||||
HRESULT CVideoTransformFilter::StartStreaming()
|
||||
{
|
||||
m_itrLate = 0;
|
||||
m_nKeyFramePeriod = 0; // No QM until we see at least 2 key frames
|
||||
m_nFramesSinceKeyFrame = 0;
|
||||
m_bSkipping = FALSE;
|
||||
m_tDecodeStart = 0;
|
||||
m_itrAvgDecode = 300000; // 30mSec - probably allows skipping
|
||||
m_bQualityChanged = FALSE;
|
||||
m_bSampleSkipped = FALSE;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Overriden to reset quality management information
|
||||
|
||||
HRESULT CVideoTransformFilter::EndFlush()
|
||||
{
|
||||
{
|
||||
// Synchronize
|
||||
CAutoLock lck(&m_csReceive);
|
||||
|
||||
// Reset our stats
|
||||
//
|
||||
// Note - we don't want to call derived classes here,
|
||||
// we only want to reset our internal variables and this
|
||||
// is a convenient way to do it
|
||||
CVideoTransformFilter::StartStreaming();
|
||||
}
|
||||
return CTransformFilter::EndFlush();
|
||||
}
|
||||
|
||||
|
||||
HRESULT CVideoTransformFilter::AbortPlayback(HRESULT hr)
|
||||
{
|
||||
NotifyEvent(EC_ERRORABORT, hr, 0);
|
||||
m_pOutput->DeliverEndOfStream();
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
// Receive()
|
||||
//
|
||||
// Accept a sample from upstream, decide whether to process it
|
||||
// or drop it. If we process it then get a buffer from the
|
||||
// allocator of the downstream connection, transform it into the
|
||||
// new buffer and deliver it to the downstream filter.
|
||||
// If we decide not to process it then we do not get a buffer.
|
||||
|
||||
// Remember that although this code will notice format changes coming into
|
||||
// the input pin, it will NOT change its output format if that results
|
||||
// in the filter needing to make a corresponding output format change. Your
|
||||
// derived filter will have to take care of that. (eg. a palette change if
|
||||
// the input and output is an 8 bit format). If the input sample is discarded
|
||||
// and nothing is sent out for this Receive, please remember to put the format
|
||||
// change on the first output sample that you actually do send.
|
||||
// If your filter will produce the same output type even when the input type
|
||||
// changes, then this base class code will do everything you need.
|
||||
|
||||
HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample)
|
||||
{
|
||||
// If the next filter downstream is the video renderer, then it may
|
||||
// be able to operate in DirectDraw mode which saves copying the data
|
||||
// and gives higher performance. In that case the buffer which we
|
||||
// get from GetDeliveryBuffer will be a DirectDraw buffer, and
|
||||
// drawing into this buffer draws directly onto the display surface.
|
||||
// This means that any waiting for the correct time to draw occurs
|
||||
// during GetDeliveryBuffer, and that once the buffer is given to us
|
||||
// the video renderer will count it in its statistics as a frame drawn.
|
||||
// This means that any decision to drop the frame must be taken before
|
||||
// calling GetDeliveryBuffer.
|
||||
|
||||
ASSERT(CritCheckIn(&m_csReceive));
|
||||
AM_MEDIA_TYPE *pmtOut, *pmt;
|
||||
#ifdef DEBUG
|
||||
FOURCCMap fccOut;
|
||||
#endif
|
||||
HRESULT hr;
|
||||
ASSERT(pSample);
|
||||
IMediaSample * pOutSample;
|
||||
|
||||
// If no output pin to deliver to then no point sending us data
|
||||
ASSERT (m_pOutput != NULL) ;
|
||||
|
||||
// The source filter may dynamically ask us to start transforming from a
|
||||
// different media type than the one we're using now. If we don't, we'll
|
||||
// draw garbage. (typically, this is a palette change in the movie,
|
||||
// but could be something more sinister like the compression type changing,
|
||||
// or even the video size changing)
|
||||
|
||||
#define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource
|
||||
#define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget
|
||||
|
||||
pSample->GetMediaType(&pmt);
|
||||
if (pmt != NULL && pmt->pbFormat != NULL) {
|
||||
|
||||
// spew some debug output
|
||||
ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL));
|
||||
#ifdef DEBUG
|
||||
fccOut.SetFOURCC(&pmt->subtype);
|
||||
LONG lCompression = HEADER(pmt->pbFormat)->biCompression;
|
||||
LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount;
|
||||
LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8;
|
||||
lStride = (lStride + 3) & ~3;
|
||||
DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to")));
|
||||
DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
|
||||
fccOut.GetFOURCC(), lCompression, lBitCount));
|
||||
DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
|
||||
HEADER(pmt->pbFormat)->biHeight,
|
||||
rcT1.left, rcT1.top, rcT1.right, rcT1.bottom));
|
||||
DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
|
||||
rcS1.left, rcS1.top, rcS1.right, rcS1.bottom,
|
||||
lStride));
|
||||
#endif
|
||||
|
||||
// now switch to using the new format. I am assuming that the
|
||||
// derived filter will do the right thing when its media type is
|
||||
// switched and streaming is restarted.
|
||||
|
||||
StopStreaming();
|
||||
m_pInput->CurrentMediaType() = *pmt;
|
||||
DeleteMediaType(pmt);
|
||||
// if this fails, playback will stop, so signal an error
|
||||
hr = StartStreaming();
|
||||
if (FAILED(hr)) {
|
||||
return AbortPlayback(hr);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have noticed any format changes on the input sample, it's
|
||||
// OK to discard it.
|
||||
|
||||
if (ShouldSkipFrame(pSample)) {
|
||||
MSR_NOTE(m_idSkip);
|
||||
m_bSampleSkipped = TRUE;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Set up the output sample
|
||||
hr = InitializeOutputSample(pSample, &pOutSample);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
m_bSampleSkipped = FALSE;
|
||||
|
||||
// The renderer may ask us to on-the-fly to start transforming to a
|
||||
// different format. If we don't obey it, we'll draw garbage
|
||||
|
||||
#define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource
|
||||
#define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget
|
||||
|
||||
pOutSample->GetMediaType(&pmtOut);
|
||||
if (pmtOut != NULL && pmtOut->pbFormat != NULL) {
|
||||
|
||||
// spew some debug output
|
||||
ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL));
|
||||
#ifdef DEBUG
|
||||
fccOut.SetFOURCC(&pmtOut->subtype);
|
||||
LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression;
|
||||
LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount;
|
||||
LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8;
|
||||
lStride = (lStride + 3) & ~3;
|
||||
DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to")));
|
||||
DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
|
||||
fccOut.GetFOURCC(), lCompression, lBitCount));
|
||||
DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
|
||||
HEADER(pmtOut->pbFormat)->biHeight,
|
||||
rcT.left, rcT.top, rcT.right, rcT.bottom));
|
||||
DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
|
||||
rcS.left, rcS.top, rcS.right, rcS.bottom,
|
||||
lStride));
|
||||
#endif
|
||||
|
||||
// now switch to using the new format. I am assuming that the
|
||||
// derived filter will do the right thing when its media type is
|
||||
// switched and streaming is restarted.
|
||||
|
||||
StopStreaming();
|
||||
m_pOutput->CurrentMediaType() = *pmtOut;
|
||||
DeleteMediaType(pmtOut);
|
||||
hr = StartStreaming();
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
// a new format, means a new empty buffer, so wait for a keyframe
|
||||
// before passing anything on to the renderer.
|
||||
// !!! a keyframe may never come, so give up after 30 frames
|
||||
DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe")));
|
||||
m_nWaitForKey = 30;
|
||||
|
||||
// if this fails, playback will stop, so signal an error
|
||||
} else {
|
||||
|
||||
// Must release the sample before calling AbortPlayback
|
||||
// because we might be holding the win16 lock or
|
||||
// ddraw lock
|
||||
pOutSample->Release();
|
||||
AbortPlayback(hr);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
// After a discontinuity, we need to wait for the next key frame
|
||||
if (pSample->IsDiscontinuity() == S_OK) {
|
||||
DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe")));
|
||||
m_nWaitForKey = 30;
|
||||
}
|
||||
|
||||
// Start timing the transform (and log it if PERF is defined)
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_tDecodeStart = timeGetTime();
|
||||
MSR_START(m_idTransform);
|
||||
|
||||
// have the derived class transform the data
|
||||
hr = Transform(pSample, pOutSample);
|
||||
|
||||
// Stop the clock (and log it if PERF is defined)
|
||||
MSR_STOP(m_idTransform);
|
||||
m_tDecodeStart = timeGetTime()-m_tDecodeStart;
|
||||
m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16);
|
||||
|
||||
// Maybe we're waiting for a keyframe still?
|
||||
if (m_nWaitForKey)
|
||||
m_nWaitForKey--;
|
||||
if (m_nWaitForKey && pSample->IsSyncPoint() == S_OK)
|
||||
m_nWaitForKey = FALSE;
|
||||
|
||||
// if so, then we don't want to pass this on to the renderer
|
||||
if (m_nWaitForKey && hr == NOERROR) {
|
||||
DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe")));
|
||||
hr = S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_TRACE,1,TEXT("Error from video transform")));
|
||||
} else {
|
||||
// the Transform() function can return S_FALSE to indicate that the
|
||||
// sample should not be delivered; we only deliver the sample if it's
|
||||
// really S_OK (same as NOERROR, of course.)
|
||||
// Try not to return S_FALSE to a direct draw buffer (it's wasteful)
|
||||
// Try to take the decision earlier - before you get it.
|
||||
|
||||
if (hr == NOERROR) {
|
||||
hr = m_pOutput->Deliver(pOutSample);
|
||||
} else {
|
||||
// S_FALSE returned from Transform is a PRIVATE agreement
|
||||
// We should return NOERROR from Receive() in this case because returning S_FALSE
|
||||
// from Receive() means that this is the end of the stream and no more data should
|
||||
// be sent.
|
||||
if (S_FALSE == hr) {
|
||||
|
||||
// We must Release() the sample before doing anything
|
||||
// like calling the filter graph because having the
|
||||
// sample means we may have the DirectDraw lock
|
||||
// (== win16 lock on some versions)
|
||||
pOutSample->Release();
|
||||
m_bSampleSkipped = TRUE;
|
||||
if (!m_bQualityChanged) {
|
||||
m_bQualityChanged = TRUE;
|
||||
NotifyEvent(EC_QUALITY_CHANGE,0,0);
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// release the output buffer. If the connected pin still needs it,
|
||||
// it will have addrefed it itself.
|
||||
pOutSample->Release();
|
||||
ASSERT(CritCheckIn(&m_csReceive));
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CVideoTransformFilter::ShouldSkipFrame( IMediaSample * pIn)
|
||||
{
|
||||
REFERENCE_TIME trStart, trStopAt;
|
||||
HRESULT hr = pIn->GetTime(&trStart, &trStopAt);
|
||||
|
||||
// Don't skip frames with no timestamps
|
||||
if (hr != S_OK)
|
||||
return FALSE;
|
||||
|
||||
int itrFrame = (int)(trStopAt - trStart); // frame duration
|
||||
|
||||
if(S_OK==pIn->IsSyncPoint()) {
|
||||
MSR_INTEGER(m_idFrameType, 1);
|
||||
if ( m_nKeyFramePeriod < m_nFramesSinceKeyFrame ) {
|
||||
// record the max
|
||||
m_nKeyFramePeriod = m_nFramesSinceKeyFrame;
|
||||
}
|
||||
m_nFramesSinceKeyFrame = 0;
|
||||
m_bSkipping = FALSE;
|
||||
} else {
|
||||
MSR_INTEGER(m_idFrameType, 2);
|
||||
if ( m_nFramesSinceKeyFrame>m_nKeyFramePeriod
|
||||
&& m_nKeyFramePeriod>0
|
||||
) {
|
||||
// We haven't seen the key frame yet, but we were clearly being
|
||||
// overoptimistic about how frequent they are.
|
||||
m_nKeyFramePeriod = m_nFramesSinceKeyFrame;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Whatever we might otherwise decide,
|
||||
// if we are taking only a small fraction of the required frame time to decode
|
||||
// then any quality problems are actually coming from somewhere else.
|
||||
// Could be a net problem at the source for instance. In this case there's
|
||||
// no point in us skipping frames here.
|
||||
if (m_itrAvgDecode*4>itrFrame) {
|
||||
|
||||
// Don't skip unless we are at least a whole frame late.
|
||||
// (We would skip B frames if more than 1/2 frame late, but they're safe).
|
||||
if ( m_itrLate > itrFrame ) {
|
||||
|
||||
// Don't skip unless the anticipated key frame would be no more than
|
||||
// 1 frame early. If the renderer has not been waiting (we *guess*
|
||||
// it hasn't because we're late) then it will allow frames to be
|
||||
// played early by up to a frame.
|
||||
|
||||
// Let T = Stream time from now to anticipated next key frame
|
||||
// = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame)
|
||||
// So we skip if T - Late < one frame i.e.
|
||||
// (duration) * (freq - FramesSince) - Late < duration
|
||||
// or (duration) * (freq - FramesSince - 1) < Late
|
||||
|
||||
// We don't dare skip until we have seen some key frames and have
|
||||
// some idea how often they occur and they are reasonably frequent.
|
||||
if (m_nKeyFramePeriod>0) {
|
||||
// It would be crazy - but we could have a stream with key frames
|
||||
// a very long way apart - and if they are further than about
|
||||
// 3.5 minutes apart then we could get arithmetic overflow in
|
||||
// reference time units. Therefore we switch to mSec at this point
|
||||
int it = (itrFrame/10000)
|
||||
* (m_nKeyFramePeriod-m_nFramesSinceKeyFrame - 1);
|
||||
MSR_INTEGER(m_idTimeTillKey, it);
|
||||
|
||||
// For debug - might want to see the details - dump them as scratch pad
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, itrFrame);
|
||||
MSR_INTEGER(0, m_nFramesSinceKeyFrame);
|
||||
MSR_INTEGER(0, m_nKeyFramePeriod);
|
||||
#endif
|
||||
if (m_itrLate/10000 > it) {
|
||||
m_bSkipping = TRUE;
|
||||
// Now we are committed. Once we start skipping, we
|
||||
// cannot stop until we hit a key frame.
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777770); // not near enough to next key
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777771); // Next key not predictable
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777772); // Less than one frame late
|
||||
MSR_INTEGER(0, m_itrLate);
|
||||
MSR_INTEGER(0, itrFrame);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777773); // Decode time short - not not worth skipping
|
||||
MSR_INTEGER(0, m_itrAvgDecode);
|
||||
MSR_INTEGER(0, itrFrame);
|
||||
#endif
|
||||
}
|
||||
|
||||
++m_nFramesSinceKeyFrame;
|
||||
|
||||
if (m_bSkipping) {
|
||||
// We will count down the lateness as we skip each frame.
|
||||
// We re-assess each frame. The key frame might not arrive when expected.
|
||||
// We reset m_itrLate if we get a new Quality message, but actually that's
|
||||
// not likely because we're not sending frames on to the Renderer. In
|
||||
// fact if we DID get another one it would mean that there's a long
|
||||
// pipe between us and the renderer and we might need an altogether
|
||||
// better strategy to avoid hunting!
|
||||
m_itrLate = m_itrLate - itrFrame;
|
||||
}
|
||||
|
||||
MSR_INTEGER(m_idLate, (int)m_itrLate/10000 ); // Note how late we think we are
|
||||
if (m_bSkipping) {
|
||||
if (!m_bQualityChanged) {
|
||||
m_bQualityChanged = TRUE;
|
||||
NotifyEvent(EC_QUALITY_CHANGE,0,0);
|
||||
}
|
||||
}
|
||||
return m_bSkipping;
|
||||
}
|
||||
|
||||
|
||||
HRESULT CVideoTransformFilter::AlterQuality(Quality q)
|
||||
{
|
||||
// to reduce the amount of 64 bit arithmetic, m_itrLate is an int.
|
||||
// +, -, >, == etc are not too bad, but * and / are painful.
|
||||
if (m_itrLate>300000000) {
|
||||
// Avoid overflow and silliness - more than 30 secs late is already silly
|
||||
m_itrLate = 300000000;
|
||||
} else {
|
||||
m_itrLate = (int)q.Late;
|
||||
}
|
||||
// We ignore the other fields
|
||||
|
||||
// We're actually not very good at handling this. In non-direct draw mode
|
||||
// most of the time can be spent in the renderer which can skip any frame.
|
||||
// In that case we'd rather the renderer handled things.
|
||||
// Nevertheless we will keep an eye on it and if we really start getting
|
||||
// a very long way behind then we will actually skip - but we'll still tell
|
||||
// the renderer (or whoever is downstream) that they should handle quality.
|
||||
|
||||
return E_FAIL; // Tell the renderer to do his thing.
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4
|
||||
#pragma warning(disable:4514)
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: VTrans.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a video transform class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// This class is derived from CTransformFilter, but is specialised to handle
|
||||
// the requirements of video quality control by frame dropping.
|
||||
// This is a non-in-place transform, (i.e. it copies the data) such as a decoder.
|
||||
|
||||
class CVideoTransformFilter : public CTransformFilter
|
||||
{
|
||||
public:
|
||||
|
||||
CVideoTransformFilter(__in_opt LPCTSTR, __inout_opt LPUNKNOWN, REFCLSID clsid);
|
||||
~CVideoTransformFilter();
|
||||
HRESULT EndFlush();
|
||||
|
||||
// =================================================================
|
||||
// ----- override these bits ---------------------------------------
|
||||
// =================================================================
|
||||
// The following methods are in CTransformFilter which is inherited.
|
||||
// They are mentioned here for completeness
|
||||
//
|
||||
// These MUST be supplied in a derived class
|
||||
//
|
||||
// NOTE:
|
||||
// virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
|
||||
// virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
|
||||
// virtual HRESULT CheckTransform
|
||||
// (const CMediaType* mtIn, const CMediaType* mtOut) PURE;
|
||||
// static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
|
||||
// virtual HRESULT DecideBufferSize
|
||||
// (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE;
|
||||
// virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
|
||||
//
|
||||
// These MAY also be overridden
|
||||
//
|
||||
// virtual HRESULT StopStreaming();
|
||||
// virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
|
||||
// virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
|
||||
// virtual HRESULT BreakConnect(PIN_DIRECTION dir);
|
||||
// virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
|
||||
// virtual HRESULT EndOfStream(void);
|
||||
// virtual HRESULT BeginFlush(void);
|
||||
// virtual HRESULT EndFlush(void);
|
||||
// virtual HRESULT NewSegment
|
||||
// (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate);
|
||||
#ifdef PERF
|
||||
|
||||
// If you override this - ensure that you register all these ids
|
||||
// as well as any of your own,
|
||||
virtual void RegisterPerfId() {
|
||||
m_idSkip = MSR_REGISTER(TEXT("Video Transform Skip frame"));
|
||||
m_idFrameType = MSR_REGISTER(TEXT("Video transform frame type"));
|
||||
m_idLate = MSR_REGISTER(TEXT("Video Transform Lateness"));
|
||||
m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key"));
|
||||
CTransformFilter::RegisterPerfId();
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
// =========== QUALITY MANAGEMENT IMPLEMENTATION ========================
|
||||
// Frames are assumed to come in three types:
|
||||
// Type 1: an AVI key frame or an MPEG I frame.
|
||||
// This frame can be decoded with no history.
|
||||
// Dropping this frame means that no further frame can be decoded
|
||||
// until the next type 1 frame.
|
||||
// Type 1 frames are sync points.
|
||||
// Type 2: an AVI non-key frame or an MPEG P frame.
|
||||
// This frame cannot be decoded unless the previous type 1 frame was
|
||||
// decoded and all type 2 frames since have been decoded.
|
||||
// Dropping this frame means that no further frame can be decoded
|
||||
// until the next type 1 frame.
|
||||
// Type 3: An MPEG B frame.
|
||||
// This frame cannot be decoded unless the previous type 1 or 2 frame
|
||||
// has been decoded AND the subsequent type 1 or 2 frame has also
|
||||
// been decoded. (This requires decoding the frames out of sequence).
|
||||
// Dropping this frame affects no other frames. This implementation
|
||||
// does not allow for these. All non-sync-point frames are treated
|
||||
// as being type 2.
|
||||
//
|
||||
// The spacing of frames of type 1 in a file is not guaranteed. There MUST
|
||||
// be a type 1 frame at (well, near) the start of the file in order to start
|
||||
// decoding at all. After that there could be one every half second or so,
|
||||
// there could be one at the start of each scene (aka "cut", "shot") or
|
||||
// there could be no more at all.
|
||||
// If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED
|
||||
// without losing all the rest of the movie. There is no way to tell whether
|
||||
// this is the case, so we find that we are in the gambling business.
|
||||
// To try to improve the odds, we record the greatest interval between type 1s
|
||||
// that we have seen and we bet on things being no worse than this in the
|
||||
// future.
|
||||
|
||||
// You can tell if it's a type 1 frame by calling IsSyncPoint().
|
||||
// there is no architected way to test for a type 3, so you should override
|
||||
// the quality management here if you have B-frames.
|
||||
|
||||
int m_nKeyFramePeriod; // the largest observed interval between type 1 frames
|
||||
// 1 means every frame is type 1, 2 means every other.
|
||||
|
||||
int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1.
|
||||
// becomes the new m_nKeyFramePeriod if greater.
|
||||
|
||||
BOOL m_bSkipping; // we are skipping to the next type 1 frame
|
||||
|
||||
#ifdef PERF
|
||||
int m_idFrameType; // MSR id Frame type. 1=Key, 2="non-key"
|
||||
int m_idSkip; // MSR id skipping
|
||||
int m_idLate; // MSR id lateness
|
||||
int m_idTimeTillKey; // MSR id for guessed time till next key frame.
|
||||
#endif
|
||||
|
||||
virtual HRESULT StartStreaming();
|
||||
|
||||
HRESULT AbortPlayback(HRESULT hr); // if something bad happens
|
||||
|
||||
HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
HRESULT AlterQuality(Quality q);
|
||||
|
||||
BOOL ShouldSkipFrame(IMediaSample * pIn);
|
||||
|
||||
int m_itrLate; // lateness from last Quality message
|
||||
// (this overflows at 214 secs late).
|
||||
int m_tDecodeStart; // timeGetTime when decode started.
|
||||
int m_itrAvgDecode; // Average decode time in reference units.
|
||||
|
||||
BOOL m_bNoSkip; // debug - no skipping.
|
||||
|
||||
// We send an EC_QUALITY_CHANGE notification to the app if we have to degrade.
|
||||
// We send one when we start degrading, not one for every frame, this means
|
||||
// we track whether we've sent one yet.
|
||||
BOOL m_bQualityChanged;
|
||||
|
||||
// When non-zero, don't pass anything to renderer until next keyframe
|
||||
// If there are few keys, give up and eventually draw something
|
||||
int m_nWaitForKey;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,224 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: WinCtrl.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines classes for video control
|
||||
// interfaces.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __WINCTRL__
|
||||
#define __WINCTRL__
|
||||
|
||||
#define ABSOL(x) (x < 0 ? -x : x)
|
||||
#define NEGAT(x) (x > 0 ? -x : x)
|
||||
|
||||
// Helper
|
||||
BOOL WINAPI PossiblyEatMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
class CBaseControlWindow : public CBaseVideoWindow, public CBaseWindow
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseFilter *m_pFilter; // Pointer to owning media filter
|
||||
CBasePin *m_pPin; // Controls media types for connection
|
||||
CCritSec *m_pInterfaceLock; // Externally defined critical section
|
||||
COLORREF m_BorderColour; // Current window border colour
|
||||
BOOL m_bAutoShow; // What happens when the state changes
|
||||
HWND m_hwndOwner; // Owner window that we optionally have
|
||||
HWND m_hwndDrain; // HWND to post any messages received
|
||||
BOOL m_bCursorHidden; // Should we hide the window cursor
|
||||
|
||||
public:
|
||||
|
||||
// Internal methods for other objects to get information out
|
||||
|
||||
HRESULT DoSetWindowStyle(long Style,long WindowLong);
|
||||
HRESULT DoGetWindowStyle(__out long *pStyle,long WindowLong);
|
||||
BOOL IsAutoShowEnabled() { return m_bAutoShow; };
|
||||
COLORREF GetBorderColour() { return m_BorderColour; };
|
||||
HWND GetOwnerWindow() { return m_hwndOwner; };
|
||||
BOOL IsCursorHidden() { return m_bCursorHidden; };
|
||||
|
||||
inline BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return ::PossiblyEatMessage(m_hwndDrain, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
// Derived classes must call this to set the pin the filter is using
|
||||
// We don't have the pin passed in to the constructor (as we do with
|
||||
// the CBaseFilter object) because filters typically create the
|
||||
// pins dynamically when requested in CBaseFilter::GetPin. This can
|
||||
// not be called from our constructor because is is a virtual method
|
||||
|
||||
void SetControlWindowPin(CBasePin *pPin) {
|
||||
m_pPin = pPin;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
CBaseControlWindow(__inout CBaseFilter *pFilter, // Owning media filter
|
||||
__in CCritSec *pInterfaceLock, // Locking object
|
||||
__in_opt LPCTSTR pName, // Object description
|
||||
__inout_opt LPUNKNOWN pUnk, // Normal COM ownership
|
||||
__inout HRESULT *phr); // OLE return code
|
||||
|
||||
// These are the properties we support
|
||||
|
||||
STDMETHODIMP put_Caption(__in BSTR strCaption);
|
||||
STDMETHODIMP get_Caption(__out BSTR *pstrCaption);
|
||||
STDMETHODIMP put_AutoShow(long AutoShow);
|
||||
STDMETHODIMP get_AutoShow(__out long *AutoShow);
|
||||
STDMETHODIMP put_WindowStyle(long WindowStyle);
|
||||
STDMETHODIMP get_WindowStyle(__out long *pWindowStyle);
|
||||
STDMETHODIMP put_WindowStyleEx(long WindowStyleEx);
|
||||
STDMETHODIMP get_WindowStyleEx(__out long *pWindowStyleEx);
|
||||
STDMETHODIMP put_WindowState(long WindowState);
|
||||
STDMETHODIMP get_WindowState(__out long *pWindowState);
|
||||
STDMETHODIMP put_BackgroundPalette(long BackgroundPalette);
|
||||
STDMETHODIMP get_BackgroundPalette(__out long *pBackgroundPalette);
|
||||
STDMETHODIMP put_Visible(long Visible);
|
||||
STDMETHODIMP get_Visible(__out long *pVisible);
|
||||
STDMETHODIMP put_Left(long Left);
|
||||
STDMETHODIMP get_Left(__out long *pLeft);
|
||||
STDMETHODIMP put_Width(long Width);
|
||||
STDMETHODIMP get_Width(__out long *pWidth);
|
||||
STDMETHODIMP put_Top(long Top);
|
||||
STDMETHODIMP get_Top(__out long *pTop);
|
||||
STDMETHODIMP put_Height(long Height);
|
||||
STDMETHODIMP get_Height(__out long *pHeight);
|
||||
STDMETHODIMP put_Owner(OAHWND Owner);
|
||||
STDMETHODIMP get_Owner(__out OAHWND *Owner);
|
||||
STDMETHODIMP put_MessageDrain(OAHWND Drain);
|
||||
STDMETHODIMP get_MessageDrain(__out OAHWND *Drain);
|
||||
STDMETHODIMP get_BorderColor(__out long *Color);
|
||||
STDMETHODIMP put_BorderColor(long Color);
|
||||
STDMETHODIMP get_FullScreenMode(__out long *FullScreenMode);
|
||||
STDMETHODIMP put_FullScreenMode(long FullScreenMode);
|
||||
|
||||
// And these are the methods
|
||||
|
||||
STDMETHODIMP SetWindowForeground(long Focus);
|
||||
STDMETHODIMP NotifyOwnerMessage(OAHWND hwnd,long uMsg,LONG_PTR wParam,LONG_PTR lParam);
|
||||
STDMETHODIMP GetMinIdealImageSize(__out long *pWidth,__out long *pHeight);
|
||||
STDMETHODIMP GetMaxIdealImageSize(__out long *pWidth,__out long *pHeight);
|
||||
STDMETHODIMP SetWindowPosition(long Left,long Top,long Width,long Height);
|
||||
STDMETHODIMP GetWindowPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);
|
||||
STDMETHODIMP GetRestorePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);
|
||||
STDMETHODIMP HideCursor(long HideCursor);
|
||||
STDMETHODIMP IsCursorHidden(__out long *CursorHidden);
|
||||
};
|
||||
|
||||
// This class implements the IBasicVideo interface
|
||||
|
||||
class CBaseControlVideo : public CBaseBasicVideo
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseFilter *m_pFilter; // Pointer to owning media filter
|
||||
CBasePin *m_pPin; // Controls media types for connection
|
||||
CCritSec *m_pInterfaceLock; // Externally defined critical section
|
||||
|
||||
public:
|
||||
|
||||
// Derived classes must provide these for the implementation
|
||||
|
||||
virtual HRESULT IsDefaultTargetRect() PURE;
|
||||
virtual HRESULT SetDefaultTargetRect() PURE;
|
||||
virtual HRESULT SetTargetRect(RECT *pTargetRect) PURE;
|
||||
virtual HRESULT GetTargetRect(RECT *pTargetRect) PURE;
|
||||
virtual HRESULT IsDefaultSourceRect() PURE;
|
||||
virtual HRESULT SetDefaultSourceRect() PURE;
|
||||
virtual HRESULT SetSourceRect(RECT *pSourceRect) PURE;
|
||||
virtual HRESULT GetSourceRect(RECT *pSourceRect) PURE;
|
||||
virtual HRESULT GetStaticImage(__inout long *pBufferSize,__out_bcount_part(*pBufferSize, *pBufferSize) long *pDIBImage) PURE;
|
||||
|
||||
// Derived classes must override this to return a VIDEOINFO representing
|
||||
// the video format. We cannot call IPin ConnectionMediaType to get this
|
||||
// format because various filters dynamically change the type when using
|
||||
// DirectDraw such that the format shows the position of the logical
|
||||
// bitmap in a frame buffer surface, so the size might be returned as
|
||||
// 1024x768 pixels instead of 320x240 which is the real video dimensions
|
||||
|
||||
__out virtual VIDEOINFOHEADER *GetVideoFormat() PURE;
|
||||
|
||||
// Helper functions for creating memory renderings of a DIB image
|
||||
|
||||
HRESULT GetImageSize(__in VIDEOINFOHEADER *pVideoInfo,
|
||||
__out LONG *pBufferSize,
|
||||
__in RECT *pSourceRect);
|
||||
|
||||
HRESULT CopyImage(IMediaSample *pMediaSample,
|
||||
__in VIDEOINFOHEADER *pVideoInfo,
|
||||
__inout LONG *pBufferSize,
|
||||
__out_bcount_part(*pBufferSize, *pBufferSize) BYTE *pVideoImage,
|
||||
__in RECT *pSourceRect);
|
||||
|
||||
// Override this if you want notifying when the rectangles change
|
||||
virtual HRESULT OnUpdateRectangles() { return NOERROR; };
|
||||
virtual HRESULT OnVideoSizeChange();
|
||||
|
||||
// Derived classes must call this to set the pin the filter is using
|
||||
// We don't have the pin passed in to the constructor (as we do with
|
||||
// the CBaseFilter object) because filters typically create the
|
||||
// pins dynamically when requested in CBaseFilter::GetPin. This can
|
||||
// not be called from our constructor because is is a virtual method
|
||||
|
||||
void SetControlVideoPin(__inout CBasePin *pPin) {
|
||||
m_pPin = pPin;
|
||||
}
|
||||
|
||||
// Helper methods for checking rectangles
|
||||
virtual HRESULT CheckSourceRect(__in RECT *pSourceRect);
|
||||
virtual HRESULT CheckTargetRect(__in RECT *pTargetRect);
|
||||
|
||||
public:
|
||||
|
||||
CBaseControlVideo(__inout CBaseFilter *pFilter, // Owning media filter
|
||||
__in CCritSec *pInterfaceLock, // Serialise interface
|
||||
__in_opt LPCTSTR pName, // Object description
|
||||
__inout_opt LPUNKNOWN pUnk, // Normal COM ownership
|
||||
__inout HRESULT *phr); // OLE return code
|
||||
|
||||
// These are the properties we support
|
||||
|
||||
STDMETHODIMP get_AvgTimePerFrame(__out REFTIME *pAvgTimePerFrame);
|
||||
STDMETHODIMP get_BitRate(__out long *pBitRate);
|
||||
STDMETHODIMP get_BitErrorRate(__out long *pBitErrorRate);
|
||||
STDMETHODIMP get_VideoWidth(__out long *pVideoWidth);
|
||||
STDMETHODIMP get_VideoHeight(__out long *pVideoHeight);
|
||||
STDMETHODIMP put_SourceLeft(long SourceLeft);
|
||||
STDMETHODIMP get_SourceLeft(__out long *pSourceLeft);
|
||||
STDMETHODIMP put_SourceWidth(long SourceWidth);
|
||||
STDMETHODIMP get_SourceWidth(__out long *pSourceWidth);
|
||||
STDMETHODIMP put_SourceTop(long SourceTop);
|
||||
STDMETHODIMP get_SourceTop(__out long *pSourceTop);
|
||||
STDMETHODIMP put_SourceHeight(long SourceHeight);
|
||||
STDMETHODIMP get_SourceHeight(__out long *pSourceHeight);
|
||||
STDMETHODIMP put_DestinationLeft(long DestinationLeft);
|
||||
STDMETHODIMP get_DestinationLeft(__out long *pDestinationLeft);
|
||||
STDMETHODIMP put_DestinationWidth(long DestinationWidth);
|
||||
STDMETHODIMP get_DestinationWidth(__out long *pDestinationWidth);
|
||||
STDMETHODIMP put_DestinationTop(long DestinationTop);
|
||||
STDMETHODIMP get_DestinationTop(__out long *pDestinationTop);
|
||||
STDMETHODIMP put_DestinationHeight(long DestinationHeight);
|
||||
STDMETHODIMP get_DestinationHeight(__out long *pDestinationHeight);
|
||||
|
||||
// And these are the methods
|
||||
|
||||
STDMETHODIMP GetVideoSize(__out long *pWidth,__out long *pHeight);
|
||||
STDMETHODIMP SetSourcePosition(long Left,long Top,long Width,long Height);
|
||||
STDMETHODIMP GetSourcePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);
|
||||
STDMETHODIMP GetVideoPaletteEntries(long StartIndex,long Entries,__out long *pRetrieved,__out_ecount_part(Entries, *pRetrieved) long *pPalette);
|
||||
STDMETHODIMP SetDefaultSourcePosition();
|
||||
STDMETHODIMP IsUsingDefaultSource();
|
||||
STDMETHODIMP SetDestinationPosition(long Left,long Top,long Width,long Height);
|
||||
STDMETHODIMP GetDestinationPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);
|
||||
STDMETHODIMP SetDefaultDestinationPosition();
|
||||
STDMETHODIMP IsUsingDefaultDestination();
|
||||
STDMETHODIMP GetCurrentImage(__inout long *pBufferSize,__out_bcount_part(*pBufferSize, *pBufferSize) long *pVideoImage);
|
||||
};
|
||||
|
||||
#endif // __WINCTRL__
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,419 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: WinUtil.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines generic handler classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Make sure that you call PrepareWindow to initialise the window after
|
||||
// the object has been constructed. It is a separate method so that
|
||||
// derived classes can override useful methods like MessageLoop. Also
|
||||
// any derived class must call DoneWithWindow in its destructor. If it
|
||||
// doesn't a message may be retrieved and call a derived class member
|
||||
// function while a thread is executing the base class destructor code
|
||||
|
||||
#ifndef __WINUTIL__
|
||||
#define __WINUTIL__
|
||||
|
||||
const int DEFWIDTH = 320; // Initial window width
|
||||
const int DEFHEIGHT = 240; // Initial window height
|
||||
const int CAPTION = 256; // Maximum length of caption
|
||||
const int TIMELENGTH = 50; // Maximum length of times
|
||||
const int PROFILESTR = 128; // Normal profile string
|
||||
const WORD PALVERSION = 0x300; // GDI palette version
|
||||
const LONG PALETTE_VERSION = (LONG) 1; // Initial palette version
|
||||
const COLORREF VIDEO_COLOUR = 0; // Defaults to black background
|
||||
const HANDLE hMEMORY = (HANDLE) (-1); // Says to open as memory file
|
||||
|
||||
#define WIDTH(x) ((*(x)).right - (*(x)).left)
|
||||
#define HEIGHT(x) ((*(x)).bottom - (*(x)).top)
|
||||
#define SHOWSTAGE TEXT("WM_SHOWSTAGE")
|
||||
#define SHOWSTAGETOP TEXT("WM_SHOWSTAGETOP")
|
||||
#define REALIZEPALETTE TEXT("WM_REALIZEPALETTE")
|
||||
|
||||
class AM_NOVTABLE CBaseWindow
|
||||
{
|
||||
protected:
|
||||
|
||||
HINSTANCE m_hInstance; // Global module instance handle
|
||||
HWND m_hwnd; // Handle for our window
|
||||
HDC m_hdc; // Device context for the window
|
||||
LONG m_Width; // Client window width
|
||||
LONG m_Height; // Client window height
|
||||
BOOL m_bActivated; // Has the window been activated
|
||||
LPTSTR m_pClassName; // Static string holding class name
|
||||
DWORD m_ClassStyles; // Passed in to our constructor
|
||||
DWORD m_WindowStyles; // Likewise the initial window styles
|
||||
DWORD m_WindowStylesEx; // And the extended window styles
|
||||
UINT m_ShowStageMessage; // Have the window shown with focus
|
||||
UINT m_ShowStageTop; // Makes the window WS_EX_TOPMOST
|
||||
UINT m_RealizePalette; // Makes us realize our new palette
|
||||
HDC m_MemoryDC; // Used for fast BitBlt operations
|
||||
HPALETTE m_hPalette; // Handle to any palette we may have
|
||||
BYTE m_bNoRealize; // Don't realize palette now
|
||||
BYTE m_bBackground; // Should we realise in background
|
||||
BYTE m_bRealizing; // already realizing the palette
|
||||
CCritSec m_WindowLock; // Serialise window object access
|
||||
BOOL m_bDoGetDC; // Should this window get a DC
|
||||
bool m_bDoPostToDestroy; // Use PostMessage to destroy
|
||||
CCritSec m_PaletteLock; // This lock protects m_hPalette.
|
||||
// It should be held anytime the
|
||||
// program use the value of m_hPalette.
|
||||
|
||||
// Maps windows message procedure into C++ methods
|
||||
friend LRESULT CALLBACK WndProc(HWND hwnd, // Window handle
|
||||
UINT uMsg, // Message ID
|
||||
WPARAM wParam, // First parameter
|
||||
LPARAM lParam); // Other parameter
|
||||
|
||||
virtual LRESULT OnPaletteChange(HWND hwnd, UINT Message);
|
||||
|
||||
public:
|
||||
|
||||
CBaseWindow(BOOL bDoGetDC = TRUE, bool bPostToDestroy = false);
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual ~CBaseWindow();
|
||||
#endif
|
||||
|
||||
virtual HRESULT DoneWithWindow();
|
||||
virtual HRESULT PrepareWindow();
|
||||
virtual HRESULT InactivateWindow();
|
||||
virtual HRESULT ActivateWindow();
|
||||
virtual BOOL OnSize(LONG Width, LONG Height);
|
||||
virtual BOOL OnClose();
|
||||
virtual RECT GetDefaultRect();
|
||||
virtual HRESULT UninitialiseWindow();
|
||||
virtual HRESULT InitialiseWindow(HWND hwnd);
|
||||
|
||||
HRESULT CompleteConnect();
|
||||
HRESULT DoCreateWindow();
|
||||
|
||||
HRESULT PerformanceAlignWindow();
|
||||
HRESULT DoShowWindow(LONG ShowCmd);
|
||||
void PaintWindow(BOOL bErase);
|
||||
void DoSetWindowForeground(BOOL bFocus);
|
||||
virtual HRESULT SetPalette(HPALETTE hPalette);
|
||||
void SetRealize(BOOL bRealize)
|
||||
{
|
||||
m_bNoRealize = !bRealize;
|
||||
}
|
||||
|
||||
// Jump over to the window thread to set the current palette
|
||||
HRESULT SetPalette();
|
||||
void UnsetPalette(void);
|
||||
virtual HRESULT DoRealisePalette(BOOL bForceBackground = FALSE);
|
||||
|
||||
void LockPaletteLock();
|
||||
void UnlockPaletteLock();
|
||||
|
||||
virtual BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{ return FALSE; };
|
||||
|
||||
// Access our window information
|
||||
|
||||
bool WindowExists();
|
||||
LONG GetWindowWidth();
|
||||
LONG GetWindowHeight();
|
||||
HWND GetWindowHWND();
|
||||
HDC GetMemoryHDC();
|
||||
HDC GetWindowHDC();
|
||||
|
||||
#ifdef DEBUG
|
||||
HPALETTE GetPalette();
|
||||
#endif // DEBUG
|
||||
|
||||
// This is the window procedure the derived object should override
|
||||
|
||||
virtual LRESULT OnReceiveMessage(HWND hwnd, // Window handle
|
||||
UINT uMsg, // Message ID
|
||||
WPARAM wParam, // First parameter
|
||||
LPARAM lParam); // Other parameter
|
||||
|
||||
// Must be overriden to return class and window styles
|
||||
|
||||
virtual LPTSTR GetClassWindowStyles(
|
||||
__out DWORD *pClassStyles, // Class styles
|
||||
__out DWORD *pWindowStyles, // Window styles
|
||||
__out DWORD *pWindowStylesEx) PURE; // Extended styles
|
||||
};
|
||||
|
||||
|
||||
// This helper class is entirely subservient to the owning CBaseWindow object
|
||||
// All this object does is to split out the actual drawing operation from the
|
||||
// main object (because it was becoming too large). We have a number of entry
|
||||
// points to set things like the draw device contexts, to implement the actual
|
||||
// drawing and to set the destination rectangle in the client window. We have
|
||||
// no critical section locking in this class because we are used exclusively
|
||||
// by the owning window object which looks after serialising calls into us
|
||||
|
||||
// If you want to use this class make sure you call NotifyAllocator once the
|
||||
// allocate has been agreed, also call NotifyMediaType with a pointer to a
|
||||
// NON stack based CMediaType once that has been set (we keep a pointer to
|
||||
// the original rather than taking a copy). When the palette changes call
|
||||
// IncrementPaletteVersion (easiest thing to do is to also call this method
|
||||
// in the SetMediaType method most filters implement). Finally before you
|
||||
// start rendering anything call SetDrawContext so that we can get the HDCs
|
||||
// for drawing from the CBaseWindow object we are given during construction
|
||||
|
||||
class CDrawImage
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseWindow *m_pBaseWindow; // Owning video window object
|
||||
CRefTime m_StartSample; // Start time for the current sample
|
||||
CRefTime m_EndSample; // And likewise it's end sample time
|
||||
HDC m_hdc; // Main window device context
|
||||
HDC m_MemoryDC; // Offscreen draw device context
|
||||
RECT m_TargetRect; // Target destination rectangle
|
||||
RECT m_SourceRect; // Source image rectangle
|
||||
BOOL m_bStretch; // Do we have to stretch the images
|
||||
BOOL m_bUsingImageAllocator; // Are the samples shared DIBSECTIONs
|
||||
CMediaType *m_pMediaType; // Pointer to the current format
|
||||
int m_perfidRenderTime; // Time taken to render an image
|
||||
LONG m_PaletteVersion; // Current palette version cookie
|
||||
|
||||
// Draw the video images in the window
|
||||
|
||||
void SlowRender(IMediaSample *pMediaSample);
|
||||
void FastRender(IMediaSample *pMediaSample);
|
||||
void DisplaySampleTimes(IMediaSample *pSample);
|
||||
void UpdateColourTable(HDC hdc,__in BITMAPINFOHEADER *pbmi);
|
||||
void SetStretchMode();
|
||||
|
||||
public:
|
||||
|
||||
// Used to control the image drawing
|
||||
|
||||
CDrawImage(__inout CBaseWindow *pBaseWindow);
|
||||
BOOL DrawImage(IMediaSample *pMediaSample);
|
||||
BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample,
|
||||
__in LPRECT lprcSrc, __in LPRECT lprcDst);
|
||||
void SetDrawContext();
|
||||
void SetTargetRect(__in RECT *pTargetRect);
|
||||
void SetSourceRect(__in RECT *pSourceRect);
|
||||
void GetTargetRect(__out RECT *pTargetRect);
|
||||
void GetSourceRect(__out RECT *pSourceRect);
|
||||
virtual RECT ScaleSourceRect(const RECT *pSource);
|
||||
|
||||
// Handle updating palettes as they change
|
||||
|
||||
LONG GetPaletteVersion();
|
||||
void ResetPaletteVersion();
|
||||
void IncrementPaletteVersion();
|
||||
|
||||
// Tell us media types and allocator assignments
|
||||
|
||||
void NotifyAllocator(BOOL bUsingImageAllocator);
|
||||
void NotifyMediaType(__in CMediaType *pMediaType);
|
||||
BOOL UsingImageAllocator();
|
||||
|
||||
// Called when we are about to draw an image
|
||||
|
||||
void NotifyStartDraw() {
|
||||
MSR_START(m_perfidRenderTime);
|
||||
};
|
||||
|
||||
// Called when we complete an image rendering
|
||||
|
||||
void NotifyEndDraw() {
|
||||
MSR_STOP(m_perfidRenderTime);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// This is the structure used to keep information about each GDI DIB. All the
|
||||
// samples we create from our allocator will have a DIBSECTION allocated to
|
||||
// them. When we receive the sample we know we can BitBlt straight to an HDC
|
||||
|
||||
typedef struct tagDIBDATA {
|
||||
|
||||
LONG PaletteVersion; // Current palette version in use
|
||||
DIBSECTION DibSection; // Details of DIB section allocated
|
||||
HBITMAP hBitmap; // Handle to bitmap for drawing
|
||||
HANDLE hMapping; // Handle to shared memory block
|
||||
BYTE *pBase; // Pointer to base memory address
|
||||
|
||||
} DIBDATA;
|
||||
|
||||
|
||||
// This class inherits from CMediaSample and uses all of it's methods but it
|
||||
// overrides the constructor to initialise itself with the DIBDATA structure
|
||||
// When we come to render an IMediaSample we will know if we are using our own
|
||||
// allocator, and if we are, we can cast the IMediaSample to a pointer to one
|
||||
// of these are retrieve the DIB section information and hence the HBITMAP
|
||||
|
||||
class CImageSample : public CMediaSample
|
||||
{
|
||||
protected:
|
||||
|
||||
DIBDATA m_DibData; // Information about the DIBSECTION
|
||||
BOOL m_bInit; // Is the DIB information setup
|
||||
|
||||
public:
|
||||
|
||||
// Constructor
|
||||
|
||||
CImageSample(__inout CBaseAllocator *pAllocator,
|
||||
__in_opt LPCTSTR pName,
|
||||
__inout HRESULT *phr,
|
||||
__in_bcount(length) LPBYTE pBuffer,
|
||||
LONG length);
|
||||
|
||||
// Maintain the DIB/DirectDraw state
|
||||
|
||||
void SetDIBData(__in DIBDATA *pDibData);
|
||||
__out DIBDATA *GetDIBData();
|
||||
};
|
||||
|
||||
|
||||
// This is an allocator based on the abstract CBaseAllocator base class that
|
||||
// allocates sample buffers in shared memory. The number and size of these
|
||||
// are determined when the output pin calls Prepare on us. The shared memory
|
||||
// blocks are used in subsequent calls to GDI CreateDIBSection, once that
|
||||
// has been done the output pin can fill the buffers with data which will
|
||||
// then be handed to GDI through BitBlt calls and thereby remove one copy
|
||||
|
||||
class CImageAllocator : public CBaseAllocator
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseFilter *m_pFilter; // Delegate reference counts to
|
||||
CMediaType *m_pMediaType; // Pointer to the current format
|
||||
|
||||
// Used to create and delete samples
|
||||
|
||||
HRESULT Alloc();
|
||||
void Free();
|
||||
|
||||
// Manage the shared DIBSECTION and DCI/DirectDraw buffers
|
||||
|
||||
HRESULT CreateDIB(LONG InSize,DIBDATA &DibData);
|
||||
STDMETHODIMP CheckSizes(__in ALLOCATOR_PROPERTIES *pRequest);
|
||||
virtual CImageSample *CreateImageSample(__in_bcount(Length) LPBYTE pData,LONG Length);
|
||||
|
||||
public:
|
||||
|
||||
// Constructor and destructor
|
||||
|
||||
CImageAllocator(__inout CBaseFilter *pFilter,__in_opt LPCTSTR pName,__inout HRESULT *phr);
|
||||
#ifdef DEBUG
|
||||
~CImageAllocator();
|
||||
#endif
|
||||
|
||||
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
|
||||
STDMETHODIMP_(ULONG) NonDelegatingRelease();
|
||||
void NotifyMediaType(__in CMediaType *pMediaType);
|
||||
|
||||
// Agree the number of buffers to be used and their size
|
||||
|
||||
STDMETHODIMP SetProperties(
|
||||
__in ALLOCATOR_PROPERTIES *pRequest,
|
||||
__out ALLOCATOR_PROPERTIES *pActual);
|
||||
};
|
||||
|
||||
|
||||
// This class is a fairly specialised helper class for image renderers that
|
||||
// have to create and manage palettes. The CBaseWindow class looks after
|
||||
// realising palettes once they have been installed. This class can be used
|
||||
// to create the palette handles from a media format (which must contain a
|
||||
// VIDEOINFO structure in the format block). We try to make the palette an
|
||||
// identity palette to maximise performance and also only change palettes
|
||||
// if actually required to (we compare palette colours before updating).
|
||||
// All the methods are virtual so that they can be overriden if so required
|
||||
|
||||
class CImagePalette
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseWindow *m_pBaseWindow; // Window to realise palette in
|
||||
CBaseFilter *m_pFilter; // Media filter to send events
|
||||
CDrawImage *m_pDrawImage; // Object who will be drawing
|
||||
HPALETTE m_hPalette; // The palette handle we own
|
||||
|
||||
public:
|
||||
|
||||
CImagePalette(__inout CBaseFilter *pBaseFilter,
|
||||
__inout CBaseWindow *pBaseWindow,
|
||||
__inout CDrawImage *pDrawImage);
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual ~CImagePalette();
|
||||
#endif
|
||||
|
||||
static HPALETTE MakePalette(const VIDEOINFOHEADER *pVideoInfo, __in LPSTR szDevice);
|
||||
HRESULT RemovePalette();
|
||||
static HRESULT MakeIdentityPalette(__inout_ecount_full(iColours) PALETTEENTRY *pEntry,INT iColours, __in LPSTR szDevice);
|
||||
HRESULT CopyPalette(const CMediaType *pSrc,__out CMediaType *pDest);
|
||||
BOOL ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,const VIDEOINFOHEADER *pOldInfo);
|
||||
HRESULT PreparePalette(const CMediaType *pmtNew,const CMediaType *pmtOld,__in LPSTR szDevice);
|
||||
|
||||
BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, __in LPRECT lprcSrc, __in LPRECT lprcDst)
|
||||
{
|
||||
return m_pDrawImage->DrawVideoImageHere(hdc, pMediaSample, lprcSrc,lprcDst);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Another helper class really for video based renderers. Most such renderers
|
||||
// need to know what the display format is to some degree or another. This
|
||||
// class initialises itself with the display format. The format can be asked
|
||||
// for through GetDisplayFormat and various other accessor functions. If a
|
||||
// filter detects a display format change (perhaps it gets a WM_DEVMODECHANGE
|
||||
// message then it can call RefreshDisplayType to reset that format). Also
|
||||
// many video renderers will want to check formats as they are proposed by
|
||||
// source filters. This class provides methods to check formats and only
|
||||
// accept those video formats that can be efficiently drawn using GDI calls
|
||||
|
||||
class CImageDisplay : public CCritSec
|
||||
{
|
||||
protected:
|
||||
|
||||
// This holds the display format; biSize should not be too big, so we can
|
||||
// safely use the VIDEOINFO structure
|
||||
VIDEOINFO m_Display;
|
||||
|
||||
static DWORD CountSetBits(const DWORD Field);
|
||||
static DWORD CountPrefixBits(const DWORD Field);
|
||||
static BOOL CheckBitFields(const VIDEOINFO *pInput);
|
||||
|
||||
public:
|
||||
|
||||
// Constructor and destructor
|
||||
|
||||
CImageDisplay();
|
||||
|
||||
// Used to manage BITMAPINFOHEADERs and the display format
|
||||
|
||||
const VIDEOINFO *GetDisplayFormat();
|
||||
HRESULT RefreshDisplayType(__in_opt LPSTR szDeviceName);
|
||||
static BOOL CheckHeaderValidity(const VIDEOINFO *pInput);
|
||||
static BOOL CheckPaletteHeader(const VIDEOINFO *pInput);
|
||||
BOOL IsPalettised();
|
||||
WORD GetDisplayDepth();
|
||||
|
||||
// Provide simple video format type checking
|
||||
|
||||
HRESULT CheckMediaType(const CMediaType *pmtIn);
|
||||
HRESULT CheckVideoType(const VIDEOINFO *pInput);
|
||||
HRESULT UpdateFormat(__inout VIDEOINFO *pVideoInfo);
|
||||
const DWORD *GetBitMasks(const VIDEOINFO *pVideoInfo);
|
||||
|
||||
BOOL GetColourMask(__out DWORD *pMaskRed,
|
||||
__out DWORD *pMaskGreen,
|
||||
__out DWORD *pMaskBlue);
|
||||
};
|
||||
|
||||
// Convert a FORMAT_VideoInfo to FORMAT_VideoInfo2
|
||||
STDAPI ConvertVideoInfoToVideoInfo2(__inout AM_MEDIA_TYPE *pmt);
|
||||
|
||||
// Check a media type containing VIDEOINFOHEADER
|
||||
STDAPI CheckVideoInfoType(const AM_MEDIA_TYPE *pmt);
|
||||
|
||||
// Check a media type containing VIDEOINFOHEADER
|
||||
STDAPI CheckVideoInfo2Type(const AM_MEDIA_TYPE *pmt);
|
||||
|
||||
#endif // __WINUTIL__
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,359 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: WXDebug.h
|
||||
//
|
||||
// Desc: DirectShow base classes - provides debugging facilities.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __WXDEBUG__
|
||||
#define __WXDEBUG__
|
||||
|
||||
// This library provides fairly straight forward debugging functionality, this
|
||||
// is split into two main sections. The first is assertion handling, there are
|
||||
// three types of assertions provided here. The most commonly used one is the
|
||||
// ASSERT(condition) macro which will pop up a message box including the file
|
||||
// and line number if the condition evaluates to FALSE. Then there is the
|
||||
// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will
|
||||
// still be executed in NON debug builds. The final type of assertion is the
|
||||
// KASSERT macro which is more suitable for pure (perhaps kernel) filters as
|
||||
// the condition is printed onto the debugger rather than in a message box.
|
||||
//
|
||||
// The other part of the debug module facilties is general purpose logging.
|
||||
// This is accessed by calling DbgLog(). The function takes a type and level
|
||||
// field which define the type of informational string you are presenting and
|
||||
// it's relative importance. The type field can be a combination (one or more)
|
||||
// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level
|
||||
// is a DWORD value where zero defines highest important. Use of zero as the
|
||||
// debug logging level is to be encouraged ONLY for major errors or events as
|
||||
// they will ALWAYS be displayed on the debugger. Other debug output has it's
|
||||
// level matched against the current debug output level stored in the registry
|
||||
// for this module and if less than the current setting it will be displayed.
|
||||
//
|
||||
// Each module or executable has it's own debug output level for each of the
|
||||
// five types. These are read in when the DbgInitialise function is called
|
||||
// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL
|
||||
// is loaded, executables must call it explicitely with the module instance
|
||||
// handle given to them through the WINMAIN entry point. An executable must
|
||||
// also call DbgTerminate when they have finished to clean up the resources
|
||||
// the debug library uses, once again this is done automatically for DLLs
|
||||
|
||||
// These are the five different categories of logging information
|
||||
|
||||
enum { LOG_TIMING = 0x01, // Timing and performance measurements
|
||||
LOG_TRACE = 0x02, // General step point call tracing
|
||||
LOG_MEMORY = 0x04, // Memory and object allocation/destruction
|
||||
LOG_LOCKING = 0x08, // Locking/unlocking of critical sections
|
||||
LOG_ERROR = 0x10, // Debug error notification
|
||||
LOG_CUSTOM1 = 0x20,
|
||||
LOG_CUSTOM2 = 0x40,
|
||||
LOG_CUSTOM3 = 0x80,
|
||||
LOG_CUSTOM4 = 0x100,
|
||||
LOG_CUSTOM5 = 0x200,
|
||||
};
|
||||
|
||||
#define LOG_FORCIBLY_SET 0x80000000
|
||||
|
||||
enum { CDISP_HEX = 0x01,
|
||||
CDISP_DEC = 0x02};
|
||||
|
||||
// For each object created derived from CBaseObject (in debug builds) we
|
||||
// create a descriptor that holds it's name (statically allocated memory)
|
||||
// and a cookie we assign it. We keep a list of all the active objects
|
||||
// we have registered so that we can dump a list of remaining objects
|
||||
|
||||
typedef struct tag_ObjectDesc {
|
||||
LPCSTR m_szName;
|
||||
LPCWSTR m_wszName;
|
||||
DWORD m_dwCookie;
|
||||
tag_ObjectDesc *m_pNext;
|
||||
} ObjectDesc;
|
||||
|
||||
#define DLLIMPORT __declspec(dllimport)
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define NAME(x) TEXT(x)
|
||||
|
||||
// These are used internally by the debug library (PRIVATE)
|
||||
|
||||
void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax);
|
||||
void WINAPI DbgInitGlobalSettings(bool fTakeMax);
|
||||
void WINAPI DbgInitModuleSettings(bool fTakeMax);
|
||||
void WINAPI DbgInitModuleName();
|
||||
DWORD WINAPI DbgRegisterObjectCreation(
|
||||
LPCSTR szObjectName, LPCWSTR wszObjectName);
|
||||
|
||||
BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie);
|
||||
|
||||
// These are the PUBLIC entry points
|
||||
|
||||
BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level);
|
||||
void WINAPI DbgSetModuleLevel(DWORD Type,DWORD Level);
|
||||
void WINAPI DbgSetAutoRefreshLevels(bool fAuto);
|
||||
|
||||
// Initialise the library with the module handle
|
||||
|
||||
void WINAPI DbgInitialise(HINSTANCE hInst);
|
||||
void WINAPI DbgTerminate();
|
||||
|
||||
void WINAPI DbgDumpObjectRegister();
|
||||
|
||||
// Display error and logging to the user
|
||||
|
||||
void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...);
|
||||
|
||||
void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);
|
||||
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCTSTR pFormat,...);
|
||||
#ifdef UNICODE
|
||||
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...);
|
||||
void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine);
|
||||
void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);
|
||||
#endif
|
||||
void WINAPI DbgOutString(LPCTSTR psz);
|
||||
|
||||
// Debug infinite wait stuff
|
||||
DWORD WINAPI DbgWaitForSingleObject(HANDLE h);
|
||||
DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,
|
||||
__in_ecount(nCount) CONST HANDLE *lpHandles,
|
||||
BOOL bWaitAll);
|
||||
void WINAPI DbgSetWaitTimeout(DWORD dwTimeout);
|
||||
|
||||
#ifdef __strmif_h__
|
||||
// Display a media type: Terse at level 2, verbose at level 5
|
||||
void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn);
|
||||
|
||||
// Dump lots of information about a filter graph
|
||||
void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel);
|
||||
#endif
|
||||
|
||||
#define KASSERT(_x_) if (!(_x_)) \
|
||||
DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
// Break on the debugger without putting up a message box
|
||||
// message goes to debugger instead
|
||||
|
||||
#define KDbgBreak(_x_) \
|
||||
DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
// We chose a common name for our ASSERT macro, MFC also uses this name
|
||||
// So long as the implementation evaluates the condition and handles it
|
||||
// then we will be ok. Rather than override the behaviour expected we
|
||||
// will leave whatever first defines ASSERT as the handler (i.e. MFC)
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(_x_) if (!(_x_)) \
|
||||
DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
#endif
|
||||
|
||||
#define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0)
|
||||
|
||||
// Put up a message box informing the user of a halt
|
||||
// condition in the program
|
||||
|
||||
#define DbgBreak(_x_) \
|
||||
DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
#define EXECUTE_ASSERT(_x_) ASSERT(_x_)
|
||||
#define DbgLog(_x_) DbgLogInfo _x_
|
||||
// MFC style trace macros
|
||||
|
||||
#define NOTE(_x_) DbgLog((LOG_TRACE,5,TEXT(_x_)))
|
||||
#define NOTE1(_x_,a) DbgLog((LOG_TRACE,5,TEXT(_x_),a))
|
||||
#define NOTE2(_x_,a,b) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b))
|
||||
#define NOTE3(_x_,a,b,c) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c))
|
||||
#define NOTE4(_x_,a,b,c,d) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d))
|
||||
#define NOTE5(_x_,a,b,c,d,e) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e))
|
||||
|
||||
#else
|
||||
|
||||
// Retail builds make public debug functions inert - WARNING the source
|
||||
// files do not define or build any of the entry points in debug builds
|
||||
// (public entry points compile to nothing) so if you go trying to call
|
||||
// any of the private entry points in your source they won't compile
|
||||
|
||||
#define NAME(_x_) ((LPTSTR) NULL)
|
||||
|
||||
#define DbgInitialise(hInst)
|
||||
#define DbgTerminate()
|
||||
#define DbgLog(_x_) 0
|
||||
#define DbgOutString(psz)
|
||||
#define DbgAssertAligned( _ptr_, _alignment_ ) 0
|
||||
|
||||
#define DbgRegisterObjectCreation(pObjectName)
|
||||
#define DbgRegisterObjectDestruction(dwCookie)
|
||||
#define DbgDumpObjectRegister()
|
||||
|
||||
#define DbgCheckModuleLevel(Type,Level)
|
||||
#define DbgSetModuleLevel(Type,Level)
|
||||
#define DbgSetAutoRefreshLevels(fAuto)
|
||||
|
||||
#define DbgWaitForSingleObject(h) WaitForSingleObject(h, INFINITE)
|
||||
#define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll) \
|
||||
WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE)
|
||||
#define DbgSetWaitTimeout(dwTimeout)
|
||||
|
||||
#define KDbgBreak(_x_)
|
||||
#define DbgBreak(_x_)
|
||||
|
||||
#define KASSERT(_x_) ((void)0)
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(_x_) ((void)0)
|
||||
#endif
|
||||
#define EXECUTE_ASSERT(_x_) ((void)(_x_))
|
||||
|
||||
// MFC style trace macros
|
||||
|
||||
#define NOTE(_x_) ((void)0)
|
||||
#define NOTE1(_x_,a) ((void)0)
|
||||
#define NOTE2(_x_,a,b) ((void)0)
|
||||
#define NOTE3(_x_,a,b,c) ((void)0)
|
||||
#define NOTE4(_x_,a,b,c,d) ((void)0)
|
||||
#define NOTE5(_x_,a,b,c,d,e) ((void)0)
|
||||
|
||||
#define DisplayType(label, pmtIn) ((void)0)
|
||||
#define DumpGraph(pGraph, label) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
// Checks a pointer which should be non NULL - can be used as follows.
|
||||
|
||||
#define CheckPointer(p,ret) {if((p)==NULL) return (ret);}
|
||||
|
||||
// HRESULT Foo(VOID *pBar)
|
||||
// {
|
||||
// CheckPointer(pBar,E_INVALIDARG)
|
||||
// }
|
||||
//
|
||||
// Or if the function returns a boolean
|
||||
//
|
||||
// BOOL Foo(VOID *pBar)
|
||||
// {
|
||||
// CheckPointer(pBar,FALSE)
|
||||
// }
|
||||
|
||||
#define ValidateReadPtr(p,cb) 0
|
||||
#define ValidateWritePtr(p,cb) 0
|
||||
#define ValidateReadWritePtr(p,cb) 0
|
||||
#define ValidateStringPtr(p) 0
|
||||
#define ValidateStringPtrA(p) 0
|
||||
#define ValidateStringPtrW(p) 0
|
||||
|
||||
|
||||
#ifdef _OBJBASE_H_
|
||||
|
||||
// Outputting GUID names. If you want to include the name
|
||||
// associated with a GUID (eg CLSID_...) then
|
||||
//
|
||||
// GuidNames[yourGUID]
|
||||
//
|
||||
// Returns the name defined in uuids.h as a string
|
||||
|
||||
typedef struct {
|
||||
CHAR *szName;
|
||||
GUID guid;
|
||||
} GUID_STRING_ENTRY;
|
||||
|
||||
class CGuidNameList {
|
||||
public:
|
||||
CHAR *operator [] (const GUID& guid);
|
||||
};
|
||||
|
||||
extern CGuidNameList GuidNames;
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef REMIND
|
||||
// REMIND macro - generates warning as reminder to complete coding
|
||||
// (eg) usage:
|
||||
//
|
||||
// #pragma message (REMIND("Add automation support"))
|
||||
|
||||
|
||||
#define QUOTE(x) #x
|
||||
#define QQUOTE(y) QUOTE(y)
|
||||
#define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") : " str
|
||||
#endif
|
||||
|
||||
// Method to display objects in a useful format
|
||||
//
|
||||
// eg If you want to display a LONGLONG ll in a debug string do (eg)
|
||||
//
|
||||
// DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX)));
|
||||
|
||||
|
||||
class CDispBasic
|
||||
{
|
||||
public:
|
||||
CDispBasic() { m_pString = m_String; };
|
||||
~CDispBasic();
|
||||
protected:
|
||||
PTCHAR m_pString; // normally points to m_String... unless too much data
|
||||
TCHAR m_String[50];
|
||||
};
|
||||
class CDisp : public CDispBasic
|
||||
{
|
||||
public:
|
||||
CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form
|
||||
CDisp(REFCLSID clsid); // Display a GUID
|
||||
CDisp(double d); // Display a floating point number
|
||||
#ifdef __strmif_h__
|
||||
#ifdef __STREAMS__
|
||||
CDisp(CRefTime t); // Display a Reference Time
|
||||
#endif
|
||||
CDisp(IPin *pPin); // Display a pin as {filter clsid}(pin name)
|
||||
CDisp(IUnknown *pUnk); // Display a filter or pin
|
||||
#endif // __strmif_h__
|
||||
~CDisp();
|
||||
|
||||
// Implement cast to (LPCTSTR) as parameter to logger
|
||||
operator LPCTSTR()
|
||||
{
|
||||
return (LPCTSTR)m_pString;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
class CAutoTrace
|
||||
{
|
||||
private:
|
||||
LPCTSTR _szBlkName;
|
||||
const int _level;
|
||||
static const TCHAR _szEntering[];
|
||||
static const TCHAR _szLeaving[];
|
||||
public:
|
||||
CAutoTrace(LPCTSTR szBlkName, const int level = 15)
|
||||
: _szBlkName(szBlkName), _level(level)
|
||||
{DbgLog((LOG_TRACE, _level, _szEntering, _szBlkName));}
|
||||
|
||||
~CAutoTrace()
|
||||
{DbgLog((LOG_TRACE, _level, _szLeaving, _szBlkName));}
|
||||
};
|
||||
|
||||
#if defined (__FUNCTION__)
|
||||
|
||||
#define AMTRACEFN() CAutoTrace __trace(TEXT(__FUNCTION__))
|
||||
#define AMTRACE(_x_) CAutoTrace __trace(TEXT(__FUNCTION__))
|
||||
|
||||
#else
|
||||
|
||||
#define AMTRACE(_x_) CAutoTrace __trace _x_
|
||||
#define AMTRACEFN()
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define AMTRACE(_x_)
|
||||
#define AMTRACEFN()
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __WXDEBUG__
|
||||
|
||||
|
|
@ -0,0 +1,891 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: WXList.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a non-MFC based generic list
|
||||
// template class.
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/* A generic list of pointers to objects.
|
||||
Objectives: avoid using MFC libraries in ndm kernel mode and
|
||||
provide a really useful list type.
|
||||
|
||||
The class is thread safe in that separate threads may add and
|
||||
delete items in the list concurrently although the application
|
||||
must ensure that constructor and destructor access is suitably
|
||||
synchronised.
|
||||
|
||||
The list name must not conflict with MFC classes as an
|
||||
application may use both
|
||||
|
||||
The nodes form a doubly linked, NULL terminated chain with an anchor
|
||||
block (the list object per se) holding pointers to the first and last
|
||||
nodes and a count of the nodes.
|
||||
There is a node cache to reduce the allocation and freeing overhead.
|
||||
It optionally (determined at construction time) has an Event which is
|
||||
set whenever the list becomes non-empty and reset whenever it becomes
|
||||
empty.
|
||||
It optionally (determined at construction time) has a Critical Section
|
||||
which is entered during the important part of each operation. (About
|
||||
all you can do outside it is some parameter checking).
|
||||
|
||||
The node cache is a repository of nodes that are NOT in the list to speed
|
||||
up storage allocation. Each list has its own cache to reduce locking and
|
||||
serialising. The list accesses are serialised anyway for a given list - a
|
||||
common cache would mean that we would have to separately serialise access
|
||||
of all lists within the cache. Because the cache only stores nodes that are
|
||||
not in the list, releasing the cache does not release any list nodes. This
|
||||
means that list nodes can be copied or rechained from one list to another
|
||||
without danger of creating a dangling reference if the original cache goes
|
||||
away.
|
||||
|
||||
Questionable design decisions:
|
||||
1. Retaining the warts for compatibility
|
||||
2. Keeping an element count -i.e. counting whenever we do anything
|
||||
instead of only when we want the count.
|
||||
3. Making the chain pointers NULL terminated. If the list object
|
||||
itself looks just like a node and the list is kept as a ring then
|
||||
it reduces the number of special cases. All inserts look the same.
|
||||
*/
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
|
||||
/* set cursor to the position of each element of list in turn */
|
||||
#define INTERNALTRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetHeadPositionI() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Next(cursor) \
|
||||
)
|
||||
|
||||
|
||||
/* set cursor to the position of each element of list in turn
|
||||
in reverse order
|
||||
*/
|
||||
#define INTERNALREVERSETRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetTailPositionI() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Prev(cursor) \
|
||||
)
|
||||
|
||||
/* Constructor calls a separate initialisation function that
|
||||
creates a node cache, optionally creates a lock object
|
||||
and optionally creates a signaling object.
|
||||
|
||||
By default we create a locking object, a DEFAULTCACHE sized
|
||||
cache but no event object so the list cannot be used in calls
|
||||
to WaitForSingleObject
|
||||
*/
|
||||
CBaseList::CBaseList(__in_opt LPCTSTR pName, // Descriptive list name
|
||||
INT iItems) : // Node cache size
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(iItems)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
CBaseList::CBaseList(__in_opt LPCTSTR pName) : // Descriptive list name
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(DEFAULTCACHE)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
#ifdef UNICODE
|
||||
CBaseList::CBaseList(__in_opt LPCSTR pName, // Descriptive list name
|
||||
INT iItems) : // Node cache size
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(iItems)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
CBaseList::CBaseList(__in_opt LPCSTR pName) : // Descriptive list name
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(DEFAULTCACHE)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
#endif
|
||||
|
||||
/* The destructor enumerates all the node objects in the list and
|
||||
in the cache deleting each in turn. We do not do any processing
|
||||
on the objects that the list holds (i.e. points to) so if they
|
||||
represent interfaces for example the creator of the list should
|
||||
ensure that each of them is released before deleting us
|
||||
*/
|
||||
CBaseList::~CBaseList()
|
||||
{
|
||||
/* Delete all our list nodes */
|
||||
|
||||
RemoveAll();
|
||||
|
||||
} // destructor
|
||||
|
||||
/* Remove all the nodes from the list but don't do anything
|
||||
with the objects that each node looks after (this is the
|
||||
responsibility of the creator).
|
||||
Aa a last act we reset the signalling event
|
||||
(if available) to indicate to clients that the list
|
||||
does not have any entries in it.
|
||||
*/
|
||||
void CBaseList::RemoveAll()
|
||||
{
|
||||
/* Free up all the CNode objects NOTE we don't bother putting the
|
||||
deleted nodes into the cache as this method is only really called
|
||||
in serious times of change such as when we are being deleted at
|
||||
which point the cache will be deleted anway */
|
||||
|
||||
CNode *pn = m_pFirst;
|
||||
while (pn) {
|
||||
CNode *op = pn;
|
||||
pn = pn->Next();
|
||||
delete op;
|
||||
}
|
||||
|
||||
/* Reset the object count and the list pointers */
|
||||
|
||||
m_Count = 0;
|
||||
m_pFirst = m_pLast = NULL;
|
||||
|
||||
} // RemoveAll
|
||||
|
||||
|
||||
|
||||
/* Return a position enumerator for the entire list.
|
||||
A position enumerator is a pointer to a node object cast to a
|
||||
transparent type so all we do is return the head/tail node
|
||||
pointer in the list.
|
||||
WARNING because the position is a pointer to a node there is
|
||||
an implicit assumption for users a the list class that after
|
||||
deleting an object from the list that any other position
|
||||
enumerators that you have may be invalid (since the node
|
||||
may be gone).
|
||||
*/
|
||||
__out_opt POSITION CBaseList::GetHeadPositionI() const
|
||||
{
|
||||
return (POSITION) m_pFirst;
|
||||
} // GetHeadPosition
|
||||
|
||||
|
||||
|
||||
__out_opt POSITION CBaseList::GetTailPositionI() const
|
||||
{
|
||||
return (POSITION) m_pLast;
|
||||
} // GetTailPosition
|
||||
|
||||
|
||||
|
||||
/* Get the number of objects in the list,
|
||||
Get the lock before accessing the count.
|
||||
Locking may not be entirely necessary but it has the side effect
|
||||
of making sure that all operations are complete before we get it.
|
||||
So for example if a list is being added to this list then that
|
||||
will have completed in full before we continue rather than seeing
|
||||
an intermediate albeit valid state
|
||||
*/
|
||||
int CBaseList::GetCountI() const
|
||||
{
|
||||
return m_Count;
|
||||
} // GetCount
|
||||
|
||||
|
||||
|
||||
/* Return the object at rp, update rp to the next object from
|
||||
the list or NULL if you have moved over the last object.
|
||||
You may still call this function once we return NULL but
|
||||
we will continue to return a NULL position value
|
||||
*/
|
||||
__out void *CBaseList::GetNextI(__inout POSITION& rp) const
|
||||
{
|
||||
/* have we reached the end of the list */
|
||||
|
||||
if (rp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lock the object before continuing */
|
||||
|
||||
void *pObject;
|
||||
|
||||
/* Copy the original position then step on */
|
||||
|
||||
CNode *pn = (CNode *) rp;
|
||||
ASSERT(pn != NULL);
|
||||
rp = (POSITION) pn->Next();
|
||||
|
||||
/* Get the object at the original position from the list */
|
||||
|
||||
pObject = pn->GetData();
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
return pObject;
|
||||
} //GetNext
|
||||
|
||||
|
||||
|
||||
/* Return the object at p.
|
||||
Asking for the object at NULL ASSERTs then returns NULL
|
||||
The object is NOT locked. The list is not being changed
|
||||
in any way. If another thread is busy deleting the object
|
||||
then locking would only result in a change from one bad
|
||||
behaviour to another.
|
||||
*/
|
||||
__out_opt void *CBaseList::GetI(__in_opt POSITION p) const
|
||||
{
|
||||
if (p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CNode * pn = (CNode *) p;
|
||||
void *pObject = pn->GetData();
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
return pObject;
|
||||
} //Get
|
||||
|
||||
__out void *CBaseList::GetValidI(__in POSITION p) const
|
||||
{
|
||||
CNode * pn = (CNode *) p;
|
||||
void *pObject = pn->GetData();
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
return pObject;
|
||||
} //Get
|
||||
|
||||
|
||||
/* Return the first position in the list which holds the given pointer.
|
||||
Return NULL if it's not found.
|
||||
*/
|
||||
__out_opt POSITION CBaseList::FindI( __in void * pObj) const
|
||||
{
|
||||
POSITION pn;
|
||||
INTERNALTRAVERSELIST(*this, pn){
|
||||
if (GetI(pn)==pObj) {
|
||||
return pn;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} // Find
|
||||
|
||||
|
||||
|
||||
/* Remove the first node in the list (deletes the pointer to its object
|
||||
from the list, does not free the object itself).
|
||||
Return the pointer to its object or NULL if empty
|
||||
*/
|
||||
__out_opt void *CBaseList::RemoveHeadI()
|
||||
{
|
||||
/* All we do is get the head position and ask for that to be deleted.
|
||||
We could special case this since some of the code path checking
|
||||
in Remove() is redundant as we know there is no previous
|
||||
node for example but it seems to gain little over the
|
||||
added complexity
|
||||
*/
|
||||
|
||||
return RemoveI((POSITION)m_pFirst);
|
||||
} // RemoveHead
|
||||
|
||||
|
||||
|
||||
/* Remove the last node in the list (deletes the pointer to its object
|
||||
from the list, does not free the object itself).
|
||||
Return the pointer to its object or NULL if empty
|
||||
*/
|
||||
__out_opt void *CBaseList::RemoveTailI()
|
||||
{
|
||||
/* All we do is get the tail position and ask for that to be deleted.
|
||||
We could special case this since some of the code path checking
|
||||
in Remove() is redundant as we know there is no previous
|
||||
node for example but it seems to gain little over the
|
||||
added complexity
|
||||
*/
|
||||
|
||||
return RemoveI((POSITION)m_pLast);
|
||||
} // RemoveTail
|
||||
|
||||
|
||||
|
||||
/* Remove the pointer to the object in this position from the list.
|
||||
Deal with all the chain pointers
|
||||
Return a pointer to the object removed from the list.
|
||||
The node object that is freed as a result
|
||||
of this operation is added to the node cache where
|
||||
it can be used again.
|
||||
Remove(NULL) is a harmless no-op - but probably is a wart.
|
||||
*/
|
||||
__out_opt void *CBaseList::RemoveI(__in_opt POSITION pos)
|
||||
{
|
||||
/* Lock the critical section before continuing */
|
||||
|
||||
// ASSERT (pos!=NULL); // Removing NULL is to be harmless!
|
||||
if (pos==NULL) return NULL;
|
||||
|
||||
|
||||
CNode *pCurrent = (CNode *) pos;
|
||||
ASSERT(pCurrent != NULL);
|
||||
|
||||
/* Update the previous node */
|
||||
|
||||
CNode *pNode = pCurrent->Prev();
|
||||
if (pNode == NULL) {
|
||||
m_pFirst = pCurrent->Next();
|
||||
} else {
|
||||
pNode->SetNext(pCurrent->Next());
|
||||
}
|
||||
|
||||
/* Update the following node */
|
||||
|
||||
pNode = pCurrent->Next();
|
||||
if (pNode == NULL) {
|
||||
m_pLast = pCurrent->Prev();
|
||||
} else {
|
||||
pNode->SetPrev(pCurrent->Prev());
|
||||
}
|
||||
|
||||
/* Get the object this node was looking after */
|
||||
|
||||
void *pObject = pCurrent->GetData();
|
||||
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
|
||||
/* Try and add the node object to the cache -
|
||||
a NULL return code from the cache means we ran out of room.
|
||||
The cache size is fixed by a constructor argument when the
|
||||
list is created and defaults to DEFAULTCACHE.
|
||||
This means that the cache will have room for this many
|
||||
node objects. So if you have a list of media samples
|
||||
and you know there will never be more than five active at
|
||||
any given time of them for example then override the default
|
||||
constructor
|
||||
*/
|
||||
|
||||
m_Cache.AddToCache(pCurrent);
|
||||
|
||||
/* If the list is empty then reset the list event */
|
||||
|
||||
--m_Count;
|
||||
ASSERT(m_Count >= 0);
|
||||
return pObject;
|
||||
} // Remove
|
||||
|
||||
|
||||
|
||||
/* Add this object to the tail end of our list
|
||||
Return the new tail position.
|
||||
*/
|
||||
|
||||
__out_opt POSITION CBaseList::AddTailI(__in void *pObject)
|
||||
{
|
||||
/* Lock the critical section before continuing */
|
||||
|
||||
CNode *pNode;
|
||||
// ASSERT(pObject); // NULL pointers in the list are allowed.
|
||||
|
||||
/* If there is a node objects in the cache then use
|
||||
that otherwise we will have to create a new one */
|
||||
|
||||
pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObject);
|
||||
pNode->SetNext(NULL);
|
||||
pNode->SetPrev(m_pLast);
|
||||
|
||||
if (m_pLast == NULL) {
|
||||
m_pFirst = pNode;
|
||||
} else {
|
||||
m_pLast->SetNext(pNode);
|
||||
}
|
||||
|
||||
/* Set the new last node pointer and also increment the number
|
||||
of list entries, the critical section is unlocked when we
|
||||
exit the function
|
||||
*/
|
||||
|
||||
m_pLast = pNode;
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
} // AddTail(object)
|
||||
|
||||
|
||||
|
||||
/* Add this object to the head end of our list
|
||||
Return the new head position.
|
||||
*/
|
||||
__out_opt POSITION CBaseList::AddHeadI(__in void *pObject)
|
||||
{
|
||||
CNode *pNode;
|
||||
// ASSERT(pObject); // NULL pointers in the list are allowed.
|
||||
|
||||
/* If there is a node objects in the cache then use
|
||||
that otherwise we will have to create a new one */
|
||||
|
||||
pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObject);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(NULL);
|
||||
pNode->SetNext(m_pFirst);
|
||||
|
||||
if (m_pFirst == NULL) {
|
||||
m_pLast = pNode;
|
||||
} else {
|
||||
m_pFirst->SetPrev(pNode);
|
||||
}
|
||||
m_pFirst = pNode;
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
} // AddHead(object)
|
||||
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the tail of this list.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
*/
|
||||
BOOL CBaseList::AddTail(__in CBaseList *pList)
|
||||
{
|
||||
/* lock the object before starting then enumerate
|
||||
each entry in the source list and add them one by one to
|
||||
our list (while still holding the object lock)
|
||||
Lock the other list too.
|
||||
*/
|
||||
POSITION pos = pList->GetHeadPositionI();
|
||||
|
||||
while (pos) {
|
||||
if (NULL == AddTailI(pList->GetNextI(pos))) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
} // AddTail(list)
|
||||
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the head of this list.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
*/
|
||||
BOOL CBaseList::AddHead(__in CBaseList *pList)
|
||||
{
|
||||
/* lock the object before starting then enumerate
|
||||
each entry in the source list and add them one by one to
|
||||
our list (while still holding the object lock)
|
||||
Lock the other list too.
|
||||
|
||||
To avoid reversing the list, traverse it backwards.
|
||||
*/
|
||||
|
||||
POSITION pos;
|
||||
|
||||
INTERNALREVERSETRAVERSELIST(*pList, pos) {
|
||||
if (NULL== AddHeadI(pList->GetValidI(pos))){
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
} // AddHead(list)
|
||||
|
||||
|
||||
|
||||
/* Add the object after position p
|
||||
p is still valid after the operation.
|
||||
AddAfter(NULL,x) adds x to the start - same as AddHead
|
||||
Return the position of the new object, NULL if it failed
|
||||
*/
|
||||
__out_opt POSITION CBaseList::AddAfterI(__in_opt POSITION pos, __in void * pObj)
|
||||
{
|
||||
if (pos==NULL)
|
||||
return AddHeadI(pObj);
|
||||
|
||||
/* As someone else might be furkling with the list -
|
||||
Lock the critical section before continuing
|
||||
*/
|
||||
CNode *pAfter = (CNode *) pos;
|
||||
ASSERT(pAfter != NULL);
|
||||
if (pAfter==m_pLast)
|
||||
return AddTailI(pObj);
|
||||
|
||||
/* set pnode to point to a new node, preferably from the cache */
|
||||
|
||||
CNode *pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObj);
|
||||
|
||||
/* It is to be added to the middle of the list - there is a before
|
||||
and after node. Chain it after pAfter, before pBefore.
|
||||
*/
|
||||
CNode * pBefore = pAfter->Next();
|
||||
ASSERT(pBefore != NULL);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(pAfter);
|
||||
pNode->SetNext(pBefore);
|
||||
pBefore->SetPrev(pNode);
|
||||
pAfter->SetNext(pNode);
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
|
||||
} // AddAfter(object)
|
||||
|
||||
|
||||
|
||||
BOOL CBaseList::AddAfter(__in_opt POSITION p, __in CBaseList *pList)
|
||||
{
|
||||
POSITION pos;
|
||||
INTERNALTRAVERSELIST(*pList, pos) {
|
||||
/* p follows along the elements being added */
|
||||
p = AddAfterI(p, pList->GetValidI(pos));
|
||||
if (p==NULL) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
} // AddAfter(list)
|
||||
|
||||
|
||||
|
||||
/* Mirror images:
|
||||
Add the element or list after position p.
|
||||
p is still valid after the operation.
|
||||
AddBefore(NULL,x) adds x to the end - same as AddTail
|
||||
*/
|
||||
__out_opt POSITION CBaseList::AddBeforeI(__in_opt POSITION pos, __in void * pObj)
|
||||
{
|
||||
if (pos==NULL)
|
||||
return AddTailI(pObj);
|
||||
|
||||
/* set pnode to point to a new node, preferably from the cache */
|
||||
|
||||
CNode *pBefore = (CNode *) pos;
|
||||
ASSERT(pBefore != NULL);
|
||||
if (pBefore==m_pFirst)
|
||||
return AddHeadI(pObj);
|
||||
|
||||
CNode * pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObj);
|
||||
|
||||
/* It is to be added to the middle of the list - there is a before
|
||||
and after node. Chain it after pAfter, before pBefore.
|
||||
*/
|
||||
|
||||
CNode * pAfter = pBefore->Prev();
|
||||
ASSERT(pAfter != NULL);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(pAfter);
|
||||
pNode->SetNext(pBefore);
|
||||
pBefore->SetPrev(pNode);
|
||||
pAfter->SetNext(pNode);
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
|
||||
} // Addbefore(object)
|
||||
|
||||
|
||||
|
||||
BOOL CBaseList::AddBefore(__in_opt POSITION p, __in CBaseList *pList)
|
||||
{
|
||||
POSITION pos;
|
||||
INTERNALREVERSETRAVERSELIST(*pList, pos) {
|
||||
/* p follows along the elements being added */
|
||||
p = AddBeforeI(p, pList->GetValidI(pos));
|
||||
if (p==NULL) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
} // AddBefore(list)
|
||||
|
||||
|
||||
|
||||
/* Split *this after position p in *this
|
||||
Retain as *this the tail portion of the original *this
|
||||
Add the head portion to the tail end of *pList
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
|
||||
e.g.
|
||||
foo->MoveToTail(foo->GetHeadPosition(), bar);
|
||||
moves one element from the head of foo to the tail of bar
|
||||
foo->MoveToTail(NULL, bar);
|
||||
is a no-op
|
||||
foo->MoveToTail(foo->GetTailPosition, bar);
|
||||
concatenates foo onto the end of bar and empties foo.
|
||||
|
||||
A better, except excessively long name might be
|
||||
MoveElementsFromHeadThroughPositionToOtherTail
|
||||
*/
|
||||
BOOL CBaseList::MoveToTail
|
||||
(__in_opt POSITION pos, __in CBaseList *pList)
|
||||
{
|
||||
/* Algorithm:
|
||||
Note that the elements (including their order) in the concatenation
|
||||
of *pList to the head of *this is invariant.
|
||||
1. Count elements to be moved
|
||||
2. Join *pList onto the head of this to make one long chain
|
||||
3. Set first/Last pointers in *this and *pList
|
||||
4. Break the chain at the new place
|
||||
5. Adjust counts
|
||||
6. Set/Reset any events
|
||||
*/
|
||||
|
||||
if (pos==NULL) return TRUE; // no-op. Eliminates special cases later.
|
||||
|
||||
|
||||
/* Make cMove the number of nodes to move */
|
||||
CNode * p = (CNode *)pos;
|
||||
int cMove = 0; // number of nodes to move
|
||||
while(p!=NULL) {
|
||||
p = p->Prev();
|
||||
++cMove;
|
||||
}
|
||||
|
||||
|
||||
/* Join the two chains together */
|
||||
if (pList->m_pLast!=NULL)
|
||||
pList->m_pLast->SetNext(m_pFirst);
|
||||
if (m_pFirst!=NULL)
|
||||
m_pFirst->SetPrev(pList->m_pLast);
|
||||
|
||||
|
||||
/* set first and last pointers */
|
||||
p = (CNode *)pos;
|
||||
|
||||
if (pList->m_pFirst==NULL)
|
||||
pList->m_pFirst = m_pFirst;
|
||||
m_pFirst = p->Next();
|
||||
if (m_pFirst==NULL)
|
||||
m_pLast = NULL;
|
||||
pList->m_pLast = p;
|
||||
|
||||
|
||||
/* Break the chain after p to create the new pieces */
|
||||
if (m_pFirst!=NULL)
|
||||
m_pFirst->SetPrev(NULL);
|
||||
p->SetNext(NULL);
|
||||
|
||||
|
||||
/* Adjust the counts */
|
||||
m_Count -= cMove;
|
||||
pList->m_Count += cMove;
|
||||
|
||||
return TRUE;
|
||||
|
||||
} // MoveToTail
|
||||
|
||||
|
||||
|
||||
/* Mirror image of MoveToTail:
|
||||
Split *this before position p in *this.
|
||||
Retain in *this the head portion of the original *this
|
||||
Add the tail portion to the start (i.e. head) of *pList
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
|
||||
e.g.
|
||||
foo->MoveToHead(foo->GetTailPosition(), bar);
|
||||
moves one element from the tail of foo to the head of bar
|
||||
foo->MoveToHead(NULL, bar);
|
||||
is a no-op
|
||||
foo->MoveToHead(foo->GetHeadPosition, bar);
|
||||
concatenates foo onto the start of bar and empties foo.
|
||||
*/
|
||||
BOOL CBaseList::MoveToHead
|
||||
(__in_opt POSITION pos, __in CBaseList *pList)
|
||||
{
|
||||
|
||||
/* See the comments on the algorithm in MoveToTail */
|
||||
|
||||
if (pos==NULL) return TRUE; // no-op. Eliminates special cases later.
|
||||
|
||||
/* Make cMove the number of nodes to move */
|
||||
CNode * p = (CNode *)pos;
|
||||
int cMove = 0; // number of nodes to move
|
||||
while(p!=NULL) {
|
||||
p = p->Next();
|
||||
++cMove;
|
||||
}
|
||||
|
||||
|
||||
/* Join the two chains together */
|
||||
if (pList->m_pFirst!=NULL)
|
||||
pList->m_pFirst->SetPrev(m_pLast);
|
||||
if (m_pLast!=NULL)
|
||||
m_pLast->SetNext(pList->m_pFirst);
|
||||
|
||||
|
||||
/* set first and last pointers */
|
||||
p = (CNode *)pos;
|
||||
|
||||
|
||||
if (pList->m_pLast==NULL)
|
||||
pList->m_pLast = m_pLast;
|
||||
|
||||
m_pLast = p->Prev();
|
||||
if (m_pLast==NULL)
|
||||
m_pFirst = NULL;
|
||||
pList->m_pFirst = p;
|
||||
|
||||
|
||||
/* Break the chain after p to create the new pieces */
|
||||
if (m_pLast!=NULL)
|
||||
m_pLast->SetNext(NULL);
|
||||
p->SetPrev(NULL);
|
||||
|
||||
|
||||
/* Adjust the counts */
|
||||
m_Count -= cMove;
|
||||
pList->m_Count += cMove;
|
||||
|
||||
return TRUE;
|
||||
|
||||
} // MoveToHead
|
||||
|
||||
|
||||
|
||||
/* Reverse the order of the [pointers to] objects in *this
|
||||
*/
|
||||
void CBaseList::Reverse()
|
||||
{
|
||||
/* algorithm:
|
||||
The obvious booby trap is that you flip pointers around and lose
|
||||
addressability to the node that you are going to process next.
|
||||
The easy way to avoid this is do do one chain at a time.
|
||||
|
||||
Run along the forward chain,
|
||||
For each node, set the reverse pointer to the one ahead of us.
|
||||
The reverse chain is now a copy of the old forward chain, including
|
||||
the NULL termination.
|
||||
|
||||
Run along the reverse chain (i.e. old forward chain again)
|
||||
For each node set the forward pointer of the node ahead to point back
|
||||
to the one we're standing on.
|
||||
The first node needs special treatment,
|
||||
it's new forward pointer is NULL.
|
||||
Finally set the First/Last pointers
|
||||
|
||||
*/
|
||||
CNode * p;
|
||||
|
||||
// Yes we COULD use a traverse, but it would look funny!
|
||||
p = m_pFirst;
|
||||
while (p!=NULL) {
|
||||
CNode * q;
|
||||
q = p->Next();
|
||||
p->SetNext(p->Prev());
|
||||
p->SetPrev(q);
|
||||
p = q;
|
||||
}
|
||||
|
||||
p = m_pFirst;
|
||||
m_pFirst = m_pLast;
|
||||
m_pLast = p;
|
||||
|
||||
|
||||
#if 0 // old version
|
||||
|
||||
if (m_pFirst==NULL) return; // empty list
|
||||
if (m_pFirst->Next()==NULL) return; // single node list
|
||||
|
||||
|
||||
/* run along forward chain */
|
||||
for ( p = m_pFirst
|
||||
; p!=NULL
|
||||
; p = p->Next()
|
||||
){
|
||||
p->SetPrev(p->Next());
|
||||
}
|
||||
|
||||
|
||||
/* special case first element */
|
||||
m_pFirst->SetNext(NULL); // fix the old first element
|
||||
|
||||
|
||||
/* run along new reverse chain i.e. old forward chain again */
|
||||
for ( p = m_pFirst // start at the old first element
|
||||
; p->Prev()!=NULL // while there's a node still to be set
|
||||
; p = p->Prev() // work in the same direction as before
|
||||
){
|
||||
p->Prev()->SetNext(p);
|
||||
}
|
||||
|
||||
|
||||
/* fix forward and reverse pointers
|
||||
- the triple XOR swap would work but all the casts look hideous */
|
||||
p = m_pFirst;
|
||||
m_pFirst = m_pLast;
|
||||
m_pLast = p;
|
||||
#endif
|
||||
|
||||
} // Reverse
|
|
@ -0,0 +1,553 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: WXList.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a non-MFC generic template list
|
||||
// class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/* A generic list of pointers to objects.
|
||||
No storage management or copying is done on the objects pointed to.
|
||||
Objectives: avoid using MFC libraries in ndm kernel mode and
|
||||
provide a really useful list type.
|
||||
|
||||
The class is thread safe in that separate threads may add and
|
||||
delete items in the list concurrently although the application
|
||||
must ensure that constructor and destructor access is suitably
|
||||
synchronised. An application can cause deadlock with operations
|
||||
which use two lists by simultaneously calling
|
||||
list1->Operation(list2) and list2->Operation(list1). So don't!
|
||||
|
||||
The names must not conflict with MFC classes as an application
|
||||
may use both.
|
||||
*/
|
||||
|
||||
#ifndef __WXLIST__
|
||||
#define __WXLIST__
|
||||
|
||||
/* A POSITION represents (in some fashion that's opaque) a cursor
|
||||
on the list that can be set to identify any element. NULL is
|
||||
a valid value and several operations regard NULL as the position
|
||||
"one step off the end of the list". (In an n element list there
|
||||
are n+1 places to insert and NULL is that "n+1-th" value).
|
||||
The POSITION of an element in the list is only invalidated if
|
||||
that element is deleted. Move operations may mean that what
|
||||
was a valid POSITION in one list is now a valid POSITION in
|
||||
a different list.
|
||||
|
||||
Some operations which at first sight are illegal are allowed as
|
||||
harmless no-ops. For instance RemoveHead is legal on an empty
|
||||
list and it returns NULL. This allows an atomic way to test if
|
||||
there is an element there, and if so, get it. The two operations
|
||||
AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper).
|
||||
|
||||
Single element operations return POSITIONs, non-NULL means it worked.
|
||||
whole list operations return a BOOL. TRUE means it all worked.
|
||||
|
||||
This definition is the same as the POSITION type for MFCs, so we must
|
||||
avoid defining it twice.
|
||||
*/
|
||||
#ifndef __AFX_H__
|
||||
struct __POSITION { int unused; };
|
||||
typedef __POSITION* POSITION;
|
||||
#endif
|
||||
|
||||
const int DEFAULTCACHE = 10; /* Default node object cache size */
|
||||
|
||||
/* A class representing one node in a list.
|
||||
Each node knows a pointer to it's adjacent nodes and also a pointer
|
||||
to the object that it looks after.
|
||||
All of these pointers can be retrieved or set through member functions.
|
||||
*/
|
||||
class CBaseList
|
||||
#ifdef DEBUG
|
||||
: public CBaseObject
|
||||
#endif
|
||||
{
|
||||
/* Making these classes inherit from CBaseObject does nothing
|
||||
functionally but it allows us to check there are no memory
|
||||
leaks in debug builds.
|
||||
*/
|
||||
|
||||
public:
|
||||
|
||||
#ifdef DEBUG
|
||||
class CNode : public CBaseObject {
|
||||
#else
|
||||
class CNode {
|
||||
#endif
|
||||
|
||||
CNode *m_pPrev; /* Previous node in the list */
|
||||
CNode *m_pNext; /* Next node in the list */
|
||||
void *m_pObject; /* Pointer to the object */
|
||||
|
||||
public:
|
||||
|
||||
/* Constructor - initialise the object's pointers */
|
||||
CNode()
|
||||
#ifdef DEBUG
|
||||
: CBaseObject(NAME("List node"))
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/* Return the previous node before this one */
|
||||
__out CNode *Prev() const { return m_pPrev; };
|
||||
|
||||
|
||||
/* Return the next node after this one */
|
||||
__out CNode *Next() const { return m_pNext; };
|
||||
|
||||
|
||||
/* Set the previous node before this one */
|
||||
void SetPrev(__in_opt CNode *p) { m_pPrev = p; };
|
||||
|
||||
|
||||
/* Set the next node after this one */
|
||||
void SetNext(__in_opt CNode *p) { m_pNext = p; };
|
||||
|
||||
|
||||
/* Get the pointer to the object for this node */
|
||||
__out void *GetData() const { return m_pObject; };
|
||||
|
||||
|
||||
/* Set the pointer to the object for this node */
|
||||
void SetData(__in void *p) { m_pObject = p; };
|
||||
};
|
||||
|
||||
class CNodeCache
|
||||
{
|
||||
public:
|
||||
CNodeCache(INT iCacheSize) : m_iCacheSize(iCacheSize),
|
||||
m_pHead(NULL),
|
||||
m_iUsed(0)
|
||||
{};
|
||||
~CNodeCache() {
|
||||
CNode *pNode = m_pHead;
|
||||
while (pNode) {
|
||||
CNode *pCurrent = pNode;
|
||||
pNode = pNode->Next();
|
||||
delete pCurrent;
|
||||
}
|
||||
};
|
||||
void AddToCache(__inout CNode *pNode)
|
||||
{
|
||||
if (m_iUsed < m_iCacheSize) {
|
||||
pNode->SetNext(m_pHead);
|
||||
m_pHead = pNode;
|
||||
m_iUsed++;
|
||||
} else {
|
||||
delete pNode;
|
||||
}
|
||||
};
|
||||
CNode *RemoveFromCache()
|
||||
{
|
||||
CNode *pNode = m_pHead;
|
||||
if (pNode != NULL) {
|
||||
m_pHead = pNode->Next();
|
||||
m_iUsed--;
|
||||
ASSERT(m_iUsed >= 0);
|
||||
} else {
|
||||
ASSERT(m_iUsed == 0);
|
||||
}
|
||||
return pNode;
|
||||
};
|
||||
private:
|
||||
INT m_iCacheSize;
|
||||
INT m_iUsed;
|
||||
CNode *m_pHead;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
CNode* m_pFirst; /* Pointer to first node in the list */
|
||||
CNode* m_pLast; /* Pointer to the last node in the list */
|
||||
LONG m_Count; /* Number of nodes currently in the list */
|
||||
|
||||
private:
|
||||
|
||||
CNodeCache m_Cache; /* Cache of unused node pointers */
|
||||
|
||||
private:
|
||||
|
||||
/* These override the default copy constructor and assignment
|
||||
operator for all list classes. They are in the private class
|
||||
declaration section so that anybody trying to pass a list
|
||||
object by value will generate a compile time error of
|
||||
"cannot access the private member function". If these were
|
||||
not here then the compiler will create default constructors
|
||||
and assignment operators which when executed first take a
|
||||
copy of all member variables and then during destruction
|
||||
delete them all. This must not be done for any heap
|
||||
allocated data.
|
||||
*/
|
||||
CBaseList(const CBaseList &refList);
|
||||
CBaseList &operator=(const CBaseList &refList);
|
||||
|
||||
public:
|
||||
|
||||
CBaseList(__in_opt LPCTSTR pName,
|
||||
INT iItems);
|
||||
|
||||
CBaseList(__in_opt LPCTSTR pName);
|
||||
#ifdef UNICODE
|
||||
CBaseList(__in_opt LPCSTR pName,
|
||||
INT iItems);
|
||||
|
||||
CBaseList(__in_opt LPCSTR pName);
|
||||
#endif
|
||||
~CBaseList();
|
||||
|
||||
/* Remove all the nodes from *this i.e. make the list empty */
|
||||
void RemoveAll();
|
||||
|
||||
|
||||
/* Return a cursor which identifies the first element of *this */
|
||||
__out_opt POSITION GetHeadPositionI() const;
|
||||
|
||||
|
||||
/* Return a cursor which identifies the last element of *this */
|
||||
__out_opt POSITION GetTailPositionI() const;
|
||||
|
||||
|
||||
/* Return the number of objects in *this */
|
||||
int GetCountI() const;
|
||||
|
||||
protected:
|
||||
/* Return the pointer to the object at rp,
|
||||
Update rp to the next node in *this
|
||||
but make it NULL if it was at the end of *this.
|
||||
This is a wart retained for backwards compatibility.
|
||||
GetPrev is not implemented.
|
||||
Use Next, Prev and Get separately.
|
||||
*/
|
||||
__out void *GetNextI(__inout POSITION& rp) const;
|
||||
|
||||
|
||||
/* Return a pointer to the object at p
|
||||
Asking for the object at NULL will return NULL harmlessly.
|
||||
*/
|
||||
__out_opt void *GetI(__in_opt POSITION p) const;
|
||||
__out void *GetValidI(__in POSITION p) const;
|
||||
|
||||
public:
|
||||
/* return the next / prev position in *this
|
||||
return NULL when going past the end/start.
|
||||
Next(NULL) is same as GetHeadPosition()
|
||||
Prev(NULL) is same as GetTailPosition()
|
||||
An n element list therefore behaves like a n+1 element
|
||||
cycle with NULL at the start/end.
|
||||
|
||||
!!WARNING!! - This handling of NULL is DIFFERENT from GetNext.
|
||||
|
||||
Some reasons are:
|
||||
1. For a list of n items there are n+1 positions to insert
|
||||
These are conveniently encoded as the n POSITIONs and NULL.
|
||||
2. If you are keeping a list sorted (fairly common) and you
|
||||
search forward for an element to insert before and don't
|
||||
find it you finish up with NULL as the element before which
|
||||
to insert. You then want that NULL to be a valid POSITION
|
||||
so that you can insert before it and you want that insertion
|
||||
point to mean the (n+1)-th one that doesn't have a POSITION.
|
||||
(symmetrically if you are working backwards through the list).
|
||||
3. It simplifies the algebra which the methods generate.
|
||||
e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x)
|
||||
in ALL cases. All the other arguments probably are reflections
|
||||
of the algebraic point.
|
||||
*/
|
||||
__out_opt POSITION Next(__in_opt POSITION pos) const
|
||||
{
|
||||
if (pos == NULL) {
|
||||
return (POSITION) m_pFirst;
|
||||
}
|
||||
CNode *pn = (CNode *) pos;
|
||||
return (POSITION) pn->Next();
|
||||
} //Next
|
||||
|
||||
// See Next
|
||||
__out_opt POSITION Prev(__in_opt POSITION pos) const
|
||||
{
|
||||
if (pos == NULL) {
|
||||
return (POSITION) m_pLast;
|
||||
}
|
||||
CNode *pn = (CNode *) pos;
|
||||
return (POSITION) pn->Prev();
|
||||
} //Prev
|
||||
|
||||
|
||||
/* Return the first position in *this which holds the given
|
||||
pointer. Return NULL if the pointer was not not found.
|
||||
*/
|
||||
protected:
|
||||
__out_opt POSITION FindI( __in void * pObj) const;
|
||||
|
||||
// ??? Should there be (or even should there be only)
|
||||
// ??? POSITION FindNextAfter(void * pObj, POSITION p)
|
||||
// ??? And of course FindPrevBefore too.
|
||||
// ??? List.Find(&Obj) then becomes List.FindNextAfter(&Obj, NULL)
|
||||
|
||||
|
||||
/* Remove the first node in *this (deletes the pointer to its
|
||||
object from the list, does not free the object itself).
|
||||
Return the pointer to its object.
|
||||
If *this was already empty it will harmlessly return NULL.
|
||||
*/
|
||||
__out_opt void *RemoveHeadI();
|
||||
|
||||
|
||||
/* Remove the last node in *this (deletes the pointer to its
|
||||
object from the list, does not free the object itself).
|
||||
Return the pointer to its object.
|
||||
If *this was already empty it will harmlessly return NULL.
|
||||
*/
|
||||
__out_opt void *RemoveTailI();
|
||||
|
||||
|
||||
/* Remove the node identified by p from the list (deletes the pointer
|
||||
to its object from the list, does not free the object itself).
|
||||
Asking to Remove the object at NULL will harmlessly return NULL.
|
||||
Return the pointer to the object removed.
|
||||
*/
|
||||
__out_opt void *RemoveI(__in_opt POSITION p);
|
||||
|
||||
/* Add single object *pObj to become a new last element of the list.
|
||||
Return the new tail position, NULL if it fails.
|
||||
If you are adding a COM objects, you might want AddRef it first.
|
||||
Other existing POSITIONs in *this are still valid
|
||||
*/
|
||||
__out_opt POSITION AddTailI(__in void * pObj);
|
||||
public:
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the tail of *this.
|
||||
This duplicates all the nodes in *pList (i.e. duplicates
|
||||
all its pointers to objects). It does not duplicate the objects.
|
||||
If you are adding a list of pointers to a COM object into the list
|
||||
it's a good idea to AddRef them all it when you AddTail it.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
Existing POSITIONs in *this are still valid
|
||||
|
||||
If you actually want to MOVE the elements, use MoveToTail instead.
|
||||
*/
|
||||
BOOL AddTail(__in CBaseList *pList);
|
||||
|
||||
|
||||
/* Mirror images of AddHead: */
|
||||
|
||||
/* Add single object to become a new first element of the list.
|
||||
Return the new head position, NULL if it fails.
|
||||
Existing POSITIONs in *this are still valid
|
||||
*/
|
||||
protected:
|
||||
__out_opt POSITION AddHeadI(__in void * pObj);
|
||||
public:
|
||||
|
||||
/* Add all the elements in *pList to the head of *this.
|
||||
Same warnings apply as for AddTail.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some of the objects may have been added.
|
||||
|
||||
If you actually want to MOVE the elements, use MoveToHead instead.
|
||||
*/
|
||||
BOOL AddHead(__in CBaseList *pList);
|
||||
|
||||
|
||||
/* Add the object *pObj to *this after position p in *this.
|
||||
AddAfter(NULL,x) adds x to the start - equivalent to AddHead
|
||||
Return the position of the object added, NULL if it failed.
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
protected:
|
||||
__out_opt POSITION AddAfterI(__in_opt POSITION p, __in void * pObj);
|
||||
public:
|
||||
|
||||
/* Add the list *pList to *this after position p in *this
|
||||
AddAfter(NULL,x) adds x to the start - equivalent to AddHead
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails, some of the objects may be added
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
BOOL AddAfter(__in_opt POSITION p, __in CBaseList *pList);
|
||||
|
||||
|
||||
/* Mirror images:
|
||||
Add the object *pObj to this-List after position p in *this.
|
||||
AddBefore(NULL,x) adds x to the end - equivalent to AddTail
|
||||
Return the position of the new object, NULL if it fails
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
protected:
|
||||
__out_opt POSITION AddBeforeI(__in_opt POSITION p, __in void * pObj);
|
||||
public:
|
||||
|
||||
/* Add the list *pList to *this before position p in *this
|
||||
AddAfter(NULL,x) adds x to the start - equivalent to AddHead
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails, some of the objects may be added
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
BOOL AddBefore(__in_opt POSITION p, __in CBaseList *pList);
|
||||
|
||||
|
||||
/* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x)
|
||||
even in cases where p is NULL or Next(p) is NULL.
|
||||
Similarly for mirror images etc.
|
||||
This may make it easier to argue about programs.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* The following operations do not copy any elements.
|
||||
They move existing blocks of elements around by switching pointers.
|
||||
They are fairly efficient for long lists as for short lists.
|
||||
(Alas, the Count slows things down).
|
||||
|
||||
They split the list into two parts.
|
||||
One part remains as the original list, the other part
|
||||
is appended to the second list. There are eight possible
|
||||
variations:
|
||||
Split the list {after/before} a given element
|
||||
keep the {head/tail} portion in the original list
|
||||
append the rest to the {head/tail} of the new list.
|
||||
|
||||
Since After is strictly equivalent to Before Next
|
||||
we are not in serious need of the Before/After variants.
|
||||
That leaves only four.
|
||||
|
||||
If you are processing a list left to right and dumping
|
||||
the bits that you have processed into another list as
|
||||
you go, the Tail/Tail variant gives the most natural result.
|
||||
If you are processing in reverse order, Head/Head is best.
|
||||
|
||||
By using NULL positions and empty lists judiciously either
|
||||
of the other two can be built up in two operations.
|
||||
|
||||
The definition of NULL (see Next/Prev etc) means that
|
||||
degenerate cases include
|
||||
"move all elements to new list"
|
||||
"Split a list into two lists"
|
||||
"Concatenate two lists"
|
||||
(and quite a few no-ops)
|
||||
|
||||
!!WARNING!! The type checking won't buy you much if you get list
|
||||
positions muddled up - e.g. use a POSITION that's in a different
|
||||
list and see what a mess you get!
|
||||
*/
|
||||
|
||||
/* Split *this after position p in *this
|
||||
Retain as *this the tail portion of the original *this
|
||||
Add the head portion to the tail end of *pList
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
|
||||
e.g.
|
||||
foo->MoveToTail(foo->GetHeadPosition(), bar);
|
||||
moves one element from the head of foo to the tail of bar
|
||||
foo->MoveToTail(NULL, bar);
|
||||
is a no-op, returns NULL
|
||||
foo->MoveToTail(foo->GetTailPosition, bar);
|
||||
concatenates foo onto the end of bar and empties foo.
|
||||
|
||||
A better, except excessively long name might be
|
||||
MoveElementsFromHeadThroughPositionToOtherTail
|
||||
*/
|
||||
BOOL MoveToTail(__in_opt POSITION pos, __in CBaseList *pList);
|
||||
|
||||
|
||||
/* Mirror image:
|
||||
Split *this before position p in *this.
|
||||
Retain in *this the head portion of the original *this
|
||||
Add the tail portion to the start (i.e. head) of *pList
|
||||
|
||||
e.g.
|
||||
foo->MoveToHead(foo->GetTailPosition(), bar);
|
||||
moves one element from the tail of foo to the head of bar
|
||||
foo->MoveToHead(NULL, bar);
|
||||
is a no-op, returns NULL
|
||||
foo->MoveToHead(foo->GetHeadPosition, bar);
|
||||
concatenates foo onto the start of bar and empties foo.
|
||||
*/
|
||||
BOOL MoveToHead(__in_opt POSITION pos, __in CBaseList *pList);
|
||||
|
||||
|
||||
/* Reverse the order of the [pointers to] objects in *this
|
||||
*/
|
||||
void Reverse();
|
||||
|
||||
|
||||
/* set cursor to the position of each element of list in turn */
|
||||
#define TRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetHeadPosition() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Next(cursor) \
|
||||
)
|
||||
|
||||
|
||||
/* set cursor to the position of each element of list in turn
|
||||
in reverse order
|
||||
*/
|
||||
#define REVERSETRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetTailPosition() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Prev(cursor) \
|
||||
)
|
||||
|
||||
}; // end of class declaration
|
||||
|
||||
template<class OBJECT> class CGenericList : public CBaseList
|
||||
{
|
||||
public:
|
||||
CGenericList(__in_opt LPCTSTR pName,
|
||||
INT iItems,
|
||||
BOOL bLock = TRUE,
|
||||
BOOL bAlert = FALSE) :
|
||||
CBaseList(pName, iItems) {
|
||||
UNREFERENCED_PARAMETER(bAlert);
|
||||
UNREFERENCED_PARAMETER(bLock);
|
||||
};
|
||||
CGenericList(__in_opt LPCTSTR pName) :
|
||||
CBaseList(pName) {
|
||||
};
|
||||
|
||||
__out_opt POSITION GetHeadPosition() const { return (POSITION)m_pFirst; }
|
||||
__out_opt POSITION GetTailPosition() const { return (POSITION)m_pLast; }
|
||||
int GetCount() const { return m_Count; }
|
||||
|
||||
__out OBJECT *GetNext(__inout POSITION& rp) const { return (OBJECT *) GetNextI(rp); }
|
||||
|
||||
__out_opt OBJECT *Get(__in_opt POSITION p) const { return (OBJECT *) GetI(p); }
|
||||
__out OBJECT *GetValid(__in POSITION p) const { return (OBJECT *) GetValidI(p); }
|
||||
__out_opt OBJECT *GetHead() const { return Get(GetHeadPosition()); }
|
||||
|
||||
__out_opt OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); }
|
||||
|
||||
__out_opt OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); }
|
||||
|
||||
__out_opt OBJECT *Remove(__in_opt POSITION p) { return (OBJECT *) RemoveI(p); }
|
||||
__out_opt POSITION AddBefore(__in_opt POSITION p, __in OBJECT * pObj) { return AddBeforeI(p, pObj); }
|
||||
__out_opt POSITION AddAfter(__in_opt POSITION p, __in OBJECT * pObj) { return AddAfterI(p, pObj); }
|
||||
__out_opt POSITION AddHead(__in OBJECT * pObj) { return AddHeadI(pObj); }
|
||||
__out_opt POSITION AddTail(__in OBJECT * pObj) { return AddTailI(pObj); }
|
||||
BOOL AddTail(__in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddTail((CBaseList *) pList); }
|
||||
BOOL AddHead(__in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddHead((CBaseList *) pList); }
|
||||
BOOL AddAfter(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddAfter(p, (CBaseList *) pList); };
|
||||
BOOL AddBefore(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddBefore(p, (CBaseList *) pList); };
|
||||
__out_opt POSITION Find( __in OBJECT * pObj) const { return FindI(pObj); }
|
||||
}; // end of class declaration
|
||||
|
||||
|
||||
|
||||
/* These define the standard list types */
|
||||
|
||||
typedef CGenericList<CBaseObject> CBaseObjectList;
|
||||
typedef CGenericList<IUnknown> CBaseInterfaceList;
|
||||
|
||||
#endif /* __WXLIST__ */
|
||||
|
|
@ -0,0 +1,769 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: WXUtil.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements helper classes for building
|
||||
// multimedia filters.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <streams.h>
|
||||
#define STRSAFE_NO_DEPRECATE
|
||||
#include <strsafe.h>
|
||||
|
||||
|
||||
// --- CAMEvent -----------------------
|
||||
CAMEvent::CAMEvent(BOOL fManualReset, __inout_opt HRESULT *phr)
|
||||
{
|
||||
m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL);
|
||||
if (NULL == m_hEvent) {
|
||||
if (NULL != phr && SUCCEEDED(*phr)) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAMEvent::CAMEvent(__inout_opt HRESULT *phr)
|
||||
{
|
||||
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (NULL == m_hEvent) {
|
||||
if (NULL != phr && SUCCEEDED(*phr)) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAMEvent::~CAMEvent()
|
||||
{
|
||||
if (m_hEvent) {
|
||||
EXECUTE_ASSERT(CloseHandle(m_hEvent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- CAMMsgEvent -----------------------
|
||||
// One routine. The rest is handled in CAMEvent
|
||||
|
||||
CAMMsgEvent::CAMMsgEvent(__inout_opt HRESULT *phr) : CAMEvent(FALSE, phr)
|
||||
{
|
||||
}
|
||||
|
||||
BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout)
|
||||
{
|
||||
// wait for the event to be signalled, or for the
|
||||
// timeout (in MS) to expire. allow SENT messages
|
||||
// to be processed while we wait
|
||||
DWORD dwWait;
|
||||
DWORD dwStartTime;
|
||||
|
||||
// set the waiting period.
|
||||
DWORD dwWaitTime = dwTimeout;
|
||||
|
||||
// the timeout will eventually run down as we iterate
|
||||
// processing messages. grab the start time so that
|
||||
// we can calculate elapsed times.
|
||||
if (dwWaitTime != INFINITE) {
|
||||
dwStartTime = timeGetTime();
|
||||
}
|
||||
|
||||
do {
|
||||
dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE);
|
||||
if (dwWait == WAIT_OBJECT_0 + 1) {
|
||||
MSG Message;
|
||||
PeekMessage(&Message,NULL,0,0,PM_NOREMOVE);
|
||||
|
||||
// If we have an explicit length of time to wait calculate
|
||||
// the next wake up point - which might be now.
|
||||
// If dwTimeout is INFINITE, it stays INFINITE
|
||||
if (dwWaitTime != INFINITE) {
|
||||
|
||||
DWORD dwElapsed = timeGetTime()-dwStartTime;
|
||||
|
||||
dwWaitTime =
|
||||
(dwElapsed >= dwTimeout)
|
||||
? 0 // wake up with WAIT_TIMEOUT
|
||||
: dwTimeout-dwElapsed;
|
||||
}
|
||||
}
|
||||
} while (dwWait == WAIT_OBJECT_0 + 1);
|
||||
|
||||
// return TRUE if we woke on the event handle,
|
||||
// FALSE if we timed out.
|
||||
return (dwWait == WAIT_OBJECT_0);
|
||||
}
|
||||
|
||||
// --- CAMThread ----------------------
|
||||
|
||||
|
||||
CAMThread::CAMThread(__inout_opt HRESULT *phr)
|
||||
: m_EventSend(TRUE, phr), // must be manual-reset for CheckRequest()
|
||||
m_EventComplete(FALSE, phr)
|
||||
{
|
||||
m_hThread = NULL;
|
||||
}
|
||||
|
||||
CAMThread::~CAMThread() {
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
// when the thread starts, it calls this function. We unwrap the 'this'
|
||||
//pointer and call ThreadProc.
|
||||
DWORD WINAPI
|
||||
CAMThread::InitialThreadProc(__inout LPVOID pv)
|
||||
{
|
||||
HRESULT hrCoInit = CAMThread::CoInitializeHelper();
|
||||
if(FAILED(hrCoInit)) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed.")));
|
||||
}
|
||||
|
||||
CAMThread * pThread = (CAMThread *) pv;
|
||||
|
||||
HRESULT hr = pThread->ThreadProc();
|
||||
|
||||
if(SUCCEEDED(hrCoInit)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CAMThread::Create()
|
||||
{
|
||||
DWORD threadid;
|
||||
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (ThreadExists()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_hThread = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
CAMThread::InitialThreadProc,
|
||||
this,
|
||||
0,
|
||||
&threadid);
|
||||
|
||||
if (!m_hThread) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD
|
||||
CAMThread::CallWorker(DWORD dwParam)
|
||||
{
|
||||
// lock access to the worker thread for scope of this object
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (!ThreadExists()) {
|
||||
return (DWORD) E_FAIL;
|
||||
}
|
||||
|
||||
// set the parameter
|
||||
m_dwParam = dwParam;
|
||||
|
||||
// signal the worker thread
|
||||
m_EventSend.Set();
|
||||
|
||||
// wait for the completion to be signalled
|
||||
m_EventComplete.Wait();
|
||||
|
||||
// done - this is the thread's return value
|
||||
return m_dwReturnVal;
|
||||
}
|
||||
|
||||
// Wait for a request from the client
|
||||
DWORD
|
||||
CAMThread::GetRequest()
|
||||
{
|
||||
m_EventSend.Wait();
|
||||
return m_dwParam;
|
||||
}
|
||||
|
||||
// is there a request?
|
||||
BOOL
|
||||
CAMThread::CheckRequest(__out_opt DWORD * pParam)
|
||||
{
|
||||
if (!m_EventSend.Check()) {
|
||||
return FALSE;
|
||||
} else {
|
||||
if (pParam) {
|
||||
*pParam = m_dwParam;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// reply to the request
|
||||
void
|
||||
CAMThread::Reply(DWORD dw)
|
||||
{
|
||||
m_dwReturnVal = dw;
|
||||
|
||||
// The request is now complete so CheckRequest should fail from
|
||||
// now on
|
||||
//
|
||||
// This event should be reset BEFORE we signal the client or
|
||||
// the client may Set it before we reset it and we'll then
|
||||
// reset it (!)
|
||||
|
||||
m_EventSend.Reset();
|
||||
|
||||
// Tell the client we're finished
|
||||
|
||||
m_EventComplete.Set();
|
||||
}
|
||||
|
||||
HRESULT CAMThread::CoInitializeHelper()
|
||||
{
|
||||
// call CoInitializeEx and tell OLE not to create a window (this
|
||||
// thread probably won't dispatch messages and will hang on
|
||||
// broadcast msgs o/w).
|
||||
//
|
||||
// If CoInitEx is not available, threads that don't call CoCreate
|
||||
// aren't affected. Threads that do will have to handle the
|
||||
// failure. Perhaps we should fall back to CoInitialize and risk
|
||||
// hanging?
|
||||
//
|
||||
|
||||
// older versions of ole32.dll don't have CoInitializeEx
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll"));
|
||||
if(hOle)
|
||||
{
|
||||
typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)(
|
||||
LPVOID pvReserved, DWORD dwCoInit);
|
||||
PCoInitializeEx pCoInitializeEx =
|
||||
(PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx"));
|
||||
if(pCoInitializeEx)
|
||||
{
|
||||
hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// caller must load ole32.dll
|
||||
DbgBreak("couldn't locate ole32.dll");
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
// destructor for CMsgThread - cleans up any messages left in the
|
||||
// queue when the thread exited
|
||||
CMsgThread::~CMsgThread()
|
||||
{
|
||||
if (m_hThread != NULL) {
|
||||
WaitForSingleObject(m_hThread, INFINITE);
|
||||
EXECUTE_ASSERT(CloseHandle(m_hThread));
|
||||
}
|
||||
|
||||
POSITION pos = m_ThreadQueue.GetHeadPosition();
|
||||
while (pos) {
|
||||
CMsg * pMsg = m_ThreadQueue.GetNext(pos);
|
||||
delete pMsg;
|
||||
}
|
||||
m_ThreadQueue.RemoveAll();
|
||||
|
||||
if (m_hSem != NULL) {
|
||||
EXECUTE_ASSERT(CloseHandle(m_hSem));
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMsgThread::CreateThread(
|
||||
)
|
||||
{
|
||||
m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
|
||||
if (m_hSem == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc,
|
||||
(LPVOID)this, 0, &m_ThreadId);
|
||||
return m_hThread != NULL;
|
||||
}
|
||||
|
||||
|
||||
// This is the threads message pump. Here we get and dispatch messages to
|
||||
// clients thread proc until the client refuses to process a message.
|
||||
// The client returns a non-zero value to stop the message pump, this
|
||||
// value becomes the threads exit code.
|
||||
|
||||
DWORD WINAPI
|
||||
CMsgThread::DefaultThreadProc(
|
||||
__inout LPVOID lpParam
|
||||
)
|
||||
{
|
||||
CMsgThread *lpThis = (CMsgThread *)lpParam;
|
||||
CMsg msg;
|
||||
LRESULT lResult;
|
||||
|
||||
// !!!
|
||||
CoInitialize(NULL);
|
||||
|
||||
// allow a derived class to handle thread startup
|
||||
lpThis->OnThreadInit();
|
||||
|
||||
do {
|
||||
lpThis->GetThreadMsg(&msg);
|
||||
lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags,
|
||||
msg.lpParam, msg.pEvent);
|
||||
} while (lResult == 0L);
|
||||
|
||||
// !!!
|
||||
CoUninitialize();
|
||||
|
||||
return (DWORD)lResult;
|
||||
}
|
||||
|
||||
|
||||
// Block until the next message is placed on the list m_ThreadQueue.
|
||||
// copies the message to the message pointed to by *pmsg
|
||||
void
|
||||
CMsgThread::GetThreadMsg(__out CMsg *msg)
|
||||
{
|
||||
CMsg * pmsg = NULL;
|
||||
|
||||
// keep trying until a message appears
|
||||
while (TRUE) {
|
||||
{
|
||||
CAutoLock lck(&m_Lock);
|
||||
pmsg = m_ThreadQueue.RemoveHead();
|
||||
if (pmsg == NULL) {
|
||||
m_lWaiting++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// the semaphore will be signalled when it is non-empty
|
||||
WaitForSingleObject(m_hSem, INFINITE);
|
||||
}
|
||||
// copy fields to caller's CMsg
|
||||
*msg = *pmsg;
|
||||
|
||||
// this CMsg was allocated by the 'new' in PutThreadMsg
|
||||
delete pmsg;
|
||||
|
||||
}
|
||||
|
||||
// Helper function - convert int to WSTR
|
||||
void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
if (FAILED(StringCchPrintf(wstr, 12, L"%d", i))) {
|
||||
wstr[0] = 0;
|
||||
}
|
||||
#else
|
||||
TCHAR temp[12];
|
||||
if (FAILED(StringCchPrintf(temp, NUMELMS(temp), "%d", i))) {
|
||||
wstr[0] = 0;
|
||||
} else {
|
||||
MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 12);
|
||||
}
|
||||
#endif
|
||||
} // IntToWstr
|
||||
|
||||
|
||||
#define MEMORY_ALIGNMENT 4
|
||||
#define MEMORY_ALIGNMENT_LOG2 2
|
||||
#define MEMORY_ALIGNMENT_MASK MEMORY_ALIGNMENT - 1
|
||||
|
||||
void * __stdcall memmoveInternal(void * dst, const void * src, size_t count)
|
||||
{
|
||||
void * ret = dst;
|
||||
|
||||
#ifdef _X86_
|
||||
if (dst <= src || (char *)dst >= ((char *)src + count)) {
|
||||
|
||||
/*
|
||||
* Non-Overlapping Buffers
|
||||
* copy from lower addresses to higher addresses
|
||||
*/
|
||||
_asm {
|
||||
mov esi,src
|
||||
mov edi,dst
|
||||
mov ecx,count
|
||||
cld
|
||||
mov edx,ecx
|
||||
and edx,MEMORY_ALIGNMENT_MASK
|
||||
shr ecx,MEMORY_ALIGNMENT_LOG2
|
||||
rep movsd
|
||||
or ecx,edx
|
||||
jz memmove_done
|
||||
rep movsb
|
||||
memmove_done:
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/*
|
||||
* Overlapping Buffers
|
||||
* copy from higher addresses to lower addresses
|
||||
*/
|
||||
_asm {
|
||||
mov esi,src
|
||||
mov edi,dst
|
||||
mov ecx,count
|
||||
std
|
||||
add esi,ecx
|
||||
add edi,ecx
|
||||
dec esi
|
||||
dec edi
|
||||
rep movsb
|
||||
cld
|
||||
}
|
||||
}
|
||||
#else
|
||||
MoveMemory(dst, src, count);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
HRESULT AMSafeMemMoveOffset(
|
||||
__in_bcount(dst_size) void * dst,
|
||||
__in size_t dst_size,
|
||||
__in DWORD cb_dst_offset,
|
||||
__in_bcount(src_size) const void * src,
|
||||
__in size_t src_size,
|
||||
__in DWORD cb_src_offset,
|
||||
__in size_t count)
|
||||
{
|
||||
// prevent read overruns
|
||||
if( count + cb_src_offset < count || // prevent integer overflow
|
||||
count + cb_src_offset > src_size) // prevent read overrun
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
// prevent write overruns
|
||||
if( count + cb_dst_offset < count || // prevent integer overflow
|
||||
count + cb_dst_offset > dst_size) // prevent write overrun
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
memmoveInternal( (BYTE *)dst+cb_dst_offset, (BYTE *)src+cb_src_offset, count);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
/******************************Public*Routine******************************\
|
||||
* Debug CCritSec helpers
|
||||
*
|
||||
* We provide debug versions of the Constructor, destructor, Lock and Unlock
|
||||
* routines. The debug code tracks who owns each critical section by
|
||||
* maintaining a depth count.
|
||||
*
|
||||
* History:
|
||||
*
|
||||
\**************************************************************************/
|
||||
|
||||
CCritSec::CCritSec()
|
||||
{
|
||||
InitializeCriticalSection(&m_CritSec);
|
||||
m_currentOwner = m_lockCount = 0;
|
||||
m_fTrace = FALSE;
|
||||
}
|
||||
|
||||
CCritSec::~CCritSec()
|
||||
{
|
||||
DeleteCriticalSection(&m_CritSec);
|
||||
}
|
||||
|
||||
void CCritSec::Lock()
|
||||
{
|
||||
UINT tracelevel=3;
|
||||
DWORD us = GetCurrentThreadId();
|
||||
DWORD currentOwner = m_currentOwner;
|
||||
if (currentOwner && (currentOwner != us)) {
|
||||
// already owned, but not by us
|
||||
if (m_fTrace) {
|
||||
DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"),
|
||||
GetCurrentThreadId(), &m_CritSec, currentOwner));
|
||||
tracelevel=2;
|
||||
// if we saw the message about waiting for the critical
|
||||
// section we ensure we see the message when we get the
|
||||
// critical section
|
||||
}
|
||||
}
|
||||
EnterCriticalSection(&m_CritSec);
|
||||
if (0 == m_lockCount++) {
|
||||
// we now own it for the first time. Set owner information
|
||||
m_currentOwner = us;
|
||||
|
||||
if (m_fTrace) {
|
||||
DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCritSec::Unlock() {
|
||||
if (0 == --m_lockCount) {
|
||||
// about to be unowned
|
||||
if (m_fTrace) {
|
||||
DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec));
|
||||
}
|
||||
|
||||
m_currentOwner = 0;
|
||||
}
|
||||
LeaveCriticalSection(&m_CritSec);
|
||||
}
|
||||
|
||||
void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace)
|
||||
{
|
||||
pcCrit->m_fTrace = fTrace;
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckIn(CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() == pcCrit->m_currentOwner);
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckIn(const CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() == pcCrit->m_currentOwner);
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckOut(CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() != pcCrit->m_currentOwner);
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckOut(const CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() != pcCrit->m_currentOwner);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
STDAPI WriteBSTR(__deref_out BSTR *pstrDest, LPCWSTR szSrc)
|
||||
{
|
||||
*pstrDest = SysAllocString( szSrc );
|
||||
if( !(*pstrDest) ) return E_OUTOFMEMORY;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
STDAPI FreeBSTR(__deref_in BSTR* pstr)
|
||||
{
|
||||
if( (PVOID)*pstr == NULL ) return S_FALSE;
|
||||
SysFreeString( *pstr );
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Return a wide string - allocating memory for it
|
||||
// Returns:
|
||||
// S_OK - no error
|
||||
// E_POINTER - ppszReturn == NULL
|
||||
// E_OUTOFMEMORY - can't allocate memory for returned string
|
||||
STDAPI AMGetWideString(LPCWSTR psz, __deref_out LPWSTR *ppszReturn)
|
||||
{
|
||||
CheckPointer(ppszReturn, E_POINTER);
|
||||
ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR));
|
||||
*ppszReturn = NULL;
|
||||
size_t nameLen;
|
||||
HRESULT hr = StringCbLengthW(psz, 100000, &nameLen);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
*ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen + sizeof(WCHAR));
|
||||
if (*ppszReturn == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
CopyMemory(*ppszReturn, psz, nameLen + sizeof(WCHAR));
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Waits for the HANDLE hObject. While waiting messages sent
|
||||
// to windows on our thread by SendMessage will be processed.
|
||||
// Using this function to do waits and mutual exclusion
|
||||
// avoids some deadlocks in objects with windows.
|
||||
// Return codes are the same as for WaitForSingleObject
|
||||
DWORD WINAPI WaitDispatchingMessages(
|
||||
HANDLE hObject,
|
||||
DWORD dwWait,
|
||||
HWND hwnd,
|
||||
UINT uMsg,
|
||||
HANDLE hEvent)
|
||||
{
|
||||
BOOL bPeeked = FALSE;
|
||||
DWORD dwResult;
|
||||
DWORD dwStart;
|
||||
DWORD dwThreadPriority;
|
||||
|
||||
static UINT uMsgId = 0;
|
||||
|
||||
HANDLE hObjects[2] = { hObject, hEvent };
|
||||
if (dwWait != INFINITE && dwWait != 0) {
|
||||
dwStart = GetTickCount();
|
||||
}
|
||||
for (; ; ) {
|
||||
DWORD nCount = NULL != hEvent ? 2 : 1;
|
||||
|
||||
// Minimize the chance of actually dispatching any messages
|
||||
// by seeing if we can lock immediately.
|
||||
dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0);
|
||||
if (dwResult < WAIT_OBJECT_0 + nCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD dwTimeOut = dwWait;
|
||||
if (dwTimeOut > 10) {
|
||||
dwTimeOut = 10;
|
||||
}
|
||||
dwResult = MsgWaitForMultipleObjects(
|
||||
nCount,
|
||||
hObjects,
|
||||
FALSE,
|
||||
dwTimeOut,
|
||||
hwnd == NULL ? QS_SENDMESSAGE :
|
||||
QS_SENDMESSAGE + QS_POSTMESSAGE);
|
||||
if (dwResult == WAIT_OBJECT_0 + nCount ||
|
||||
dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) {
|
||||
MSG msg;
|
||||
if (hwnd != NULL) {
|
||||
while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) {
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
// Do this anyway - the previous peek doesn't flush out the
|
||||
// messages
|
||||
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
|
||||
|
||||
if (dwWait != INFINITE && dwWait != 0) {
|
||||
DWORD dwNow = GetTickCount();
|
||||
|
||||
// Working with differences handles wrap-around
|
||||
DWORD dwDiff = dwNow - dwStart;
|
||||
if (dwDiff > dwWait) {
|
||||
dwWait = 0;
|
||||
} else {
|
||||
dwWait -= dwDiff;
|
||||
}
|
||||
dwStart = dwNow;
|
||||
}
|
||||
if (!bPeeked) {
|
||||
// Raise our priority to prevent our message queue
|
||||
// building up
|
||||
dwThreadPriority = GetThreadPriority(GetCurrentThread());
|
||||
if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) {
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||
}
|
||||
bPeeked = TRUE;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bPeeked) {
|
||||
SetThreadPriority(GetCurrentThread(), dwThreadPriority);
|
||||
if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
|
||||
if (uMsgId == 0) {
|
||||
uMsgId = RegisterWindowMessage(TEXT("AMUnblock"));
|
||||
}
|
||||
if (uMsgId != 0) {
|
||||
MSG msg;
|
||||
// Remove old ones
|
||||
while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) {
|
||||
}
|
||||
}
|
||||
PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0);
|
||||
}
|
||||
}
|
||||
return dwResult;
|
||||
}
|
||||
|
||||
HRESULT AmGetLastErrorToHResult()
|
||||
{
|
||||
DWORD dwLastError = GetLastError();
|
||||
if(dwLastError != 0)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(dwLastError);
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp)
|
||||
{
|
||||
if (lp != NULL)
|
||||
lp->AddRef();
|
||||
if (*pp)
|
||||
(*pp)->Release();
|
||||
*pp = lp;
|
||||
return lp;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
CompatibleTimeSetEvent
|
||||
|
||||
CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling
|
||||
timeSetEvent() if the current operating system supports it. TIME_KILL_SYNCHRONOUS
|
||||
is supported on Windows XP and later operating systems.
|
||||
|
||||
Parameters:
|
||||
- The same parameters as timeSetEvent(). See timeSetEvent()'s documentation in
|
||||
the Platform SDK for more information.
|
||||
|
||||
Return Value:
|
||||
- The same return value as timeSetEvent(). See timeSetEvent()'s documentation in
|
||||
the Platform SDK for more information.
|
||||
|
||||
******************************************************************************/
|
||||
MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent )
|
||||
{
|
||||
#if WINVER >= 0x0501
|
||||
{
|
||||
static bool fCheckedVersion = false;
|
||||
static bool fTimeKillSynchronousFlagAvailable = false;
|
||||
|
||||
if( !fCheckedVersion ) {
|
||||
fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable();
|
||||
fCheckedVersion = true;
|
||||
}
|
||||
|
||||
if( fTimeKillSynchronousFlagAvailable ) {
|
||||
fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS;
|
||||
}
|
||||
}
|
||||
#endif // WINVER >= 0x0501
|
||||
|
||||
return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent );
|
||||
}
|
||||
|
||||
bool TimeKillSynchronousFlagAvailable( void )
|
||||
{
|
||||
OSVERSIONINFO osverinfo;
|
||||
|
||||
osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);
|
||||
|
||||
if( GetVersionEx( &osverinfo ) ) {
|
||||
|
||||
// Windows XP's major version is 5 and its' minor version is 1.
|
||||
// timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag
|
||||
// in Windows XP.
|
||||
if( (osverinfo.dwMajorVersion > 5) ||
|
||||
( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,532 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// File: WXUtil.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines helper classes and functions for
|
||||
// building multimedia filters.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __WXUTIL__
|
||||
#define __WXUTIL__
|
||||
|
||||
// eliminate spurious "statement has no effect" warnings.
|
||||
#pragma warning(disable: 4705)
|
||||
|
||||
// wrapper for whatever critical section we have
|
||||
class CCritSec {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CCritSec(const CCritSec &refCritSec);
|
||||
CCritSec &operator=(const CCritSec &refCritSec);
|
||||
|
||||
CRITICAL_SECTION m_CritSec;
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
DWORD m_currentOwner;
|
||||
DWORD m_lockCount;
|
||||
BOOL m_fTrace; // Trace this one
|
||||
public:
|
||||
CCritSec();
|
||||
~CCritSec();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
#else
|
||||
|
||||
public:
|
||||
CCritSec() {
|
||||
InitializeCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
~CCritSec() {
|
||||
DeleteCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
void Lock() {
|
||||
EnterCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
void Unlock() {
|
||||
LeaveCriticalSection(&m_CritSec);
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
//
|
||||
// To make deadlocks easier to track it is useful to insert in the
|
||||
// code an assertion that says whether we own a critical section or
|
||||
// not. We make the routines that do the checking globals to avoid
|
||||
// having different numbers of member functions in the debug and
|
||||
// retail class implementations of CCritSec. In addition we provide
|
||||
// a routine that allows usage of specific critical sections to be
|
||||
// traced. This is NOT on by default - there are far too many.
|
||||
//
|
||||
|
||||
#ifdef DEBUG
|
||||
BOOL WINAPI CritCheckIn(CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckIn(const CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckOut(CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckOut(const CCritSec * pcCrit);
|
||||
void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace);
|
||||
#else
|
||||
#define CritCheckIn(x) TRUE
|
||||
#define CritCheckOut(x) TRUE
|
||||
#define DbgLockTrace(pc, fT)
|
||||
#endif
|
||||
|
||||
|
||||
// locks a critical section, and unlocks it automatically
|
||||
// when the lock goes out of scope
|
||||
class CAutoLock {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAutoLock(const CAutoLock &refAutoLock);
|
||||
CAutoLock &operator=(const CAutoLock &refAutoLock);
|
||||
|
||||
protected:
|
||||
CCritSec * m_pLock;
|
||||
|
||||
public:
|
||||
CAutoLock(CCritSec * plock)
|
||||
{
|
||||
m_pLock = plock;
|
||||
m_pLock->Lock();
|
||||
};
|
||||
|
||||
~CAutoLock() {
|
||||
m_pLock->Unlock();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
// wrapper for event objects
|
||||
class CAMEvent
|
||||
{
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAMEvent(const CAMEvent &refEvent);
|
||||
CAMEvent &operator=(const CAMEvent &refEvent);
|
||||
|
||||
protected:
|
||||
HANDLE m_hEvent;
|
||||
public:
|
||||
CAMEvent(BOOL fManualReset = FALSE, __inout_opt HRESULT *phr = NULL);
|
||||
CAMEvent(__inout_opt HRESULT *phr);
|
||||
~CAMEvent();
|
||||
|
||||
// Cast to HANDLE - we don't support this as an lvalue
|
||||
operator HANDLE () const { return m_hEvent; };
|
||||
|
||||
void Set() {EXECUTE_ASSERT(SetEvent(m_hEvent));};
|
||||
BOOL Wait(DWORD dwTimeout = INFINITE) {
|
||||
return (WaitForSingleObject(m_hEvent, dwTimeout) == WAIT_OBJECT_0);
|
||||
};
|
||||
void Reset() { ResetEvent(m_hEvent); };
|
||||
BOOL Check() { return Wait(0); };
|
||||
};
|
||||
|
||||
|
||||
// wrapper for event objects that do message processing
|
||||
// This adds ONE method to the CAMEvent object to allow sent
|
||||
// messages to be processed while waiting
|
||||
|
||||
class CAMMsgEvent : public CAMEvent
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
CAMMsgEvent(__inout_opt HRESULT *phr = NULL);
|
||||
|
||||
// Allow SEND messages to be processed while waiting
|
||||
BOOL WaitMsg(DWORD dwTimeout = INFINITE);
|
||||
};
|
||||
|
||||
// old name supported for the time being
|
||||
#define CTimeoutEvent CAMEvent
|
||||
|
||||
// support for a worker thread
|
||||
|
||||
#ifdef AM_NOVTABLE
|
||||
// simple thread class supports creation of worker thread, synchronization
|
||||
// and communication. Can be derived to simplify parameter passing
|
||||
class AM_NOVTABLE CAMThread {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAMThread(const CAMThread &refThread);
|
||||
CAMThread &operator=(const CAMThread &refThread);
|
||||
|
||||
CAMEvent m_EventSend;
|
||||
CAMEvent m_EventComplete;
|
||||
|
||||
DWORD m_dwParam;
|
||||
DWORD m_dwReturnVal;
|
||||
|
||||
protected:
|
||||
HANDLE m_hThread;
|
||||
|
||||
// thread will run this function on startup
|
||||
// must be supplied by derived class
|
||||
virtual DWORD ThreadProc() = 0;
|
||||
|
||||
public:
|
||||
CAMThread(__inout_opt HRESULT *phr = NULL);
|
||||
virtual ~CAMThread();
|
||||
|
||||
CCritSec m_AccessLock; // locks access by client threads
|
||||
CCritSec m_WorkerLock; // locks access to shared objects
|
||||
|
||||
// thread initially runs this. param is actually 'this'. function
|
||||
// just gets this and calls ThreadProc
|
||||
static DWORD WINAPI InitialThreadProc(__inout LPVOID pv);
|
||||
|
||||
// start thread running - error if already running
|
||||
BOOL Create();
|
||||
|
||||
// signal the thread, and block for a response
|
||||
//
|
||||
DWORD CallWorker(DWORD);
|
||||
|
||||
// accessor thread calls this when done with thread (having told thread
|
||||
// to exit)
|
||||
void Close() {
|
||||
|
||||
// Disable warning: Conversion from LONG to PVOID of greater size
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4312)
|
||||
HANDLE hThread = (HANDLE)InterlockedExchangePointer(&m_hThread, 0);
|
||||
#pragma warning(pop)
|
||||
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
};
|
||||
|
||||
// ThreadExists
|
||||
// Return TRUE if the thread exists. FALSE otherwise
|
||||
BOOL ThreadExists(void) const
|
||||
{
|
||||
if (m_hThread == 0) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for the next request
|
||||
DWORD GetRequest();
|
||||
|
||||
// is there a request?
|
||||
BOOL CheckRequest(__out_opt DWORD * pParam);
|
||||
|
||||
// reply to the request
|
||||
void Reply(DWORD);
|
||||
|
||||
// If you want to do WaitForMultipleObjects you'll need to include
|
||||
// this handle in your wait list or you won't be responsive
|
||||
HANDLE GetRequestHandle() const { return m_EventSend; };
|
||||
|
||||
// Find out what the request was
|
||||
DWORD GetRequestParam() const { return m_dwParam; };
|
||||
|
||||
// call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if
|
||||
// available. S_FALSE means it's not available.
|
||||
static HRESULT CoInitializeHelper();
|
||||
};
|
||||
#endif // AM_NOVTABLE
|
||||
|
||||
|
||||
// CQueue
|
||||
//
|
||||
// Implements a simple Queue ADT. The queue contains a finite number of
|
||||
// objects, access to which is controlled by a semaphore. The semaphore
|
||||
// is created with an initial count (N). Each time an object is added
|
||||
// a call to WaitForSingleObject is made on the semaphore's handle. When
|
||||
// this function returns a slot has been reserved in the queue for the new
|
||||
// object. If no slots are available the function blocks until one becomes
|
||||
// available. Each time an object is removed from the queue ReleaseSemaphore
|
||||
// is called on the semaphore's handle, thus freeing a slot in the queue.
|
||||
// If no objects are present in the queue the function blocks until an
|
||||
// object has been added.
|
||||
|
||||
#define DEFAULT_QUEUESIZE 2
|
||||
|
||||
template <class T> class CQueue {
|
||||
private:
|
||||
HANDLE hSemPut; // Semaphore controlling queue "putting"
|
||||
HANDLE hSemGet; // Semaphore controlling queue "getting"
|
||||
CRITICAL_SECTION CritSect; // Thread seriallization
|
||||
int nMax; // Max objects allowed in queue
|
||||
int iNextPut; // Array index of next "PutMsg"
|
||||
int iNextGet; // Array index of next "GetMsg"
|
||||
T *QueueObjects; // Array of objects (ptr's to void)
|
||||
|
||||
void Initialize(int n) {
|
||||
iNextPut = iNextGet = 0;
|
||||
nMax = n;
|
||||
InitializeCriticalSection(&CritSect);
|
||||
hSemPut = CreateSemaphore(NULL, n, n, NULL);
|
||||
hSemGet = CreateSemaphore(NULL, 0, n, NULL);
|
||||
QueueObjects = new T[n];
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
CQueue(int n) {
|
||||
Initialize(n);
|
||||
}
|
||||
|
||||
CQueue() {
|
||||
Initialize(DEFAULT_QUEUESIZE);
|
||||
}
|
||||
|
||||
~CQueue() {
|
||||
delete [] QueueObjects;
|
||||
DeleteCriticalSection(&CritSect);
|
||||
CloseHandle(hSemPut);
|
||||
CloseHandle(hSemGet);
|
||||
}
|
||||
|
||||
T GetQueueObject() {
|
||||
int iSlot;
|
||||
T Object;
|
||||
LONG lPrevious;
|
||||
|
||||
// Wait for someone to put something on our queue, returns straight
|
||||
// away is there is already an object on the queue.
|
||||
//
|
||||
WaitForSingleObject(hSemGet, INFINITE);
|
||||
|
||||
EnterCriticalSection(&CritSect);
|
||||
iSlot = iNextGet++ % nMax;
|
||||
Object = QueueObjects[iSlot];
|
||||
LeaveCriticalSection(&CritSect);
|
||||
|
||||
// Release anyone waiting to put an object onto our queue as there
|
||||
// is now space available in the queue.
|
||||
//
|
||||
ReleaseSemaphore(hSemPut, 1L, &lPrevious);
|
||||
return Object;
|
||||
}
|
||||
|
||||
void PutQueueObject(T Object) {
|
||||
int iSlot;
|
||||
LONG lPrevious;
|
||||
|
||||
// Wait for someone to get something from our queue, returns straight
|
||||
// away is there is already an empty slot on the queue.
|
||||
//
|
||||
WaitForSingleObject(hSemPut, INFINITE);
|
||||
|
||||
EnterCriticalSection(&CritSect);
|
||||
iSlot = iNextPut++ % nMax;
|
||||
QueueObjects[iSlot] = Object;
|
||||
LeaveCriticalSection(&CritSect);
|
||||
|
||||
// Release anyone waiting to remove an object from our queue as there
|
||||
// is now an object available to be removed.
|
||||
//
|
||||
ReleaseSemaphore(hSemGet, 1L, &lPrevious);
|
||||
}
|
||||
};
|
||||
|
||||
// Ensures that memory is not read past the length source buffer
|
||||
// and that memory is not written past the length of the dst buffer
|
||||
// dst - buffer to copy to
|
||||
// dst_size - total size of destination buffer
|
||||
// cb_dst_offset - offset, first byte copied to dst+cb_dst_offset
|
||||
// src - buffer to copy from
|
||||
// src_size - total size of source buffer
|
||||
// cb_src_offset - offset, first byte copied from src+cb_src_offset
|
||||
// count - number of bytes to copy
|
||||
//
|
||||
// Returns:
|
||||
// S_OK - no error
|
||||
// E_INVALIDARG - values passed would lead to overrun
|
||||
HRESULT AMSafeMemMoveOffset(
|
||||
__in_bcount(dst_size) void * dst,
|
||||
__in size_t dst_size,
|
||||
__in DWORD cb_dst_offset,
|
||||
__in_bcount(src_size) const void * src,
|
||||
__in size_t src_size,
|
||||
__in DWORD cb_src_offset,
|
||||
__in size_t count);
|
||||
|
||||
extern "C"
|
||||
void * __stdcall memmoveInternal(void *, const void *, size_t);
|
||||
|
||||
inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt)
|
||||
{
|
||||
#ifdef _X86_
|
||||
void *pRet = NULL;
|
||||
|
||||
_asm {
|
||||
cld // make sure we get the direction right
|
||||
mov ecx, cnt // num of bytes to scan
|
||||
mov edi, buf // pointer byte stream
|
||||
mov eax, chr // byte to scan for
|
||||
repne scasb // look for the byte in the byte stream
|
||||
jnz exit_memchr // Z flag set if byte found
|
||||
dec edi // scasb always increments edi even when it
|
||||
// finds the required byte
|
||||
mov pRet, edi
|
||||
exit_memchr:
|
||||
}
|
||||
return pRet;
|
||||
|
||||
#else
|
||||
while ( cnt && (*(unsigned char *)buf != (unsigned char)chr) ) {
|
||||
buf = (unsigned char *)buf + 1;
|
||||
cnt--;
|
||||
}
|
||||
|
||||
return(cnt ? (void *)buf : NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr);
|
||||
|
||||
#define WstrToInt(sz) _wtoi(sz)
|
||||
#define atoiW(sz) _wtoi(sz)
|
||||
#define atoiA(sz) atoi(sz)
|
||||
|
||||
// These are available to help managing bitmap VIDEOINFOHEADER media structures
|
||||
|
||||
extern const DWORD bits555[3];
|
||||
extern const DWORD bits565[3];
|
||||
extern const DWORD bits888[3];
|
||||
|
||||
// These help convert between VIDEOINFOHEADER and BITMAPINFO structures
|
||||
|
||||
STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader);
|
||||
STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader);
|
||||
STDAPI_(WORD) GetBitCount(const GUID *pSubtype);
|
||||
|
||||
// strmbase.lib implements this for compatibility with people who
|
||||
// managed to link to this directly. we don't want to advertise it.
|
||||
//
|
||||
// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype);
|
||||
|
||||
STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype);
|
||||
STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype);
|
||||
|
||||
#ifdef UNICODE
|
||||
#define GetSubtypeName GetSubtypeNameW
|
||||
#else
|
||||
#define GetSubtypeName GetSubtypeNameA
|
||||
#endif
|
||||
|
||||
STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader);
|
||||
STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader);
|
||||
|
||||
#ifdef __AMVIDEO__
|
||||
STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo);
|
||||
STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo);
|
||||
#endif // __AMVIDEO__
|
||||
|
||||
|
||||
// Compares two interfaces and returns TRUE if they are on the same object
|
||||
BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond);
|
||||
|
||||
// This is for comparing pins
|
||||
#define EqualPins(pPin1, pPin2) IsEqualObject(pPin1, pPin2)
|
||||
|
||||
|
||||
// Arithmetic helper functions
|
||||
|
||||
// Compute (a * b + rnd) / c
|
||||
LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG rnd);
|
||||
LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG rnd);
|
||||
|
||||
|
||||
// Avoids us dyna-linking to SysAllocString to copy BSTR strings
|
||||
STDAPI WriteBSTR(__deref_out BSTR * pstrDest, LPCWSTR szSrc);
|
||||
STDAPI FreeBSTR(__deref_in BSTR* pstr);
|
||||
|
||||
// Return a wide string - allocating memory for it
|
||||
// Returns:
|
||||
// S_OK - no error
|
||||
// E_POINTER - ppszReturn == NULL
|
||||
// E_OUTOFMEMORY - can't allocate memory for returned string
|
||||
STDAPI AMGetWideString(LPCWSTR pszString, __deref_out LPWSTR *ppszReturn);
|
||||
|
||||
// Special wait for objects owning windows
|
||||
DWORD WINAPI WaitDispatchingMessages(
|
||||
HANDLE hObject,
|
||||
DWORD dwWait,
|
||||
HWND hwnd = NULL,
|
||||
UINT uMsg = 0,
|
||||
HANDLE hEvent = NULL);
|
||||
|
||||
// HRESULT_FROM_WIN32 converts ERROR_SUCCESS to a success code, but in
|
||||
// our use of HRESULT_FROM_WIN32, it typically means a function failed
|
||||
// to call SetLastError(), and we still want a failure code.
|
||||
//
|
||||
#define AmHresultFromWin32(x) (MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, x))
|
||||
|
||||
// call GetLastError and return an HRESULT value that will fail the
|
||||
// SUCCEEDED() macro.
|
||||
HRESULT AmGetLastErrorToHResult(void);
|
||||
|
||||
// duplicate of ATL's CComPtr to avoid linker conflicts.
|
||||
|
||||
IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp);
|
||||
|
||||
template <class T>
|
||||
class QzCComPtr
|
||||
{
|
||||
public:
|
||||
typedef T _PtrClass;
|
||||
QzCComPtr() {p=NULL;}
|
||||
QzCComPtr(T* lp)
|
||||
{
|
||||
if ((p = lp) != NULL)
|
||||
p->AddRef();
|
||||
}
|
||||
QzCComPtr(const QzCComPtr<T>& lp)
|
||||
{
|
||||
if ((p = lp.p) != NULL)
|
||||
p->AddRef();
|
||||
}
|
||||
~QzCComPtr() {if (p) p->Release();}
|
||||
void Release() {if (p) p->Release(); p=NULL;}
|
||||
operator T*() {return (T*)p;}
|
||||
T& operator*() {ASSERT(p!=NULL); return *p; }
|
||||
//The assert on operator& usually indicates a bug. If this is really
|
||||
//what is needed, however, take the address of the p member explicitly.
|
||||
T** operator&() { ASSERT(p==NULL); return &p; }
|
||||
T* operator->() { ASSERT(p!=NULL); return p; }
|
||||
T* operator=(T* lp){return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp);}
|
||||
T* operator=(const QzCComPtr<T>& lp)
|
||||
{
|
||||
return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp.p);
|
||||
}
|
||||
#if _MSC_VER>1020
|
||||
bool operator!(){return (p == NULL);}
|
||||
#else
|
||||
BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}
|
||||
#endif
|
||||
T* p;
|
||||
};
|
||||
|
||||
MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent );
|
||||
bool TimeKillSynchronousFlagAvailable( void );
|
||||
|
||||
// Helper to replace lstrcpmi
|
||||
__inline int lstrcmpiLocaleIndependentW(LPCWSTR lpsz1, LPCWSTR lpsz2)
|
||||
{
|
||||
return CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;
|
||||
}
|
||||
__inline int lstrcmpiLocaleIndependentA(LPCSTR lpsz1, LPCSTR lpsz2)
|
||||
{
|
||||
return CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;
|
||||
}
|
||||
|
||||
#endif /* __WXUTIL__ */
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<OutDir>$(SolutionDir)bin_$(PlatformName)d\lib\</OutDir>
|
||||
<IntDir>$(SolutionDir)bin_$(PlatformName)d\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<OutDir>$(SolutionDir)bin_$(PlatformName)\lib\</OutDir>
|
||||
<IntDir>$(SolutionDir)bin_$(PlatformName)\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)includes;$(SolutionDir)baseclasses;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)bin_$(PlatformName)d\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)bin_$(PlatformName)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Hendrik Leppkes
|
||||
* http://www.1f0.de
|
||||
*
|
||||
* This Program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This Program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dvdmedia.h>
|
||||
|
||||
// H.264 UUIDs
|
||||
// 31435641-0000-0010-8000-00AA00389B71
|
||||
DEFINE_GUID(MEDIASUBTYPE_AVC1,
|
||||
0x31435641, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
|
||||
|
||||
// 31637661-0000-0010-8000-00AA00389B71
|
||||
DEFINE_GUID(MEDIASUBTYPE_avc1,
|
||||
0x31637661, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
|
||||
|
||||
// 31564343-0000-0010-8000-00AA00389B71 (custom H.264 FourCC used by Haali Media Splitter)
|
||||
DEFINE_GUID(MEDIASUBTYPE_CCV1,
|
||||
0x31564343, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Hendrik Leppkes
|
||||
* http://www.1f0.de
|
||||
*
|
||||
* This Program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This Program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "LAVCUDA.h"
|
||||
|
||||
#include "moreuuids.h"
|
||||
|
||||
// Define Input Media Types
|
||||
const AMOVIESETUP_MEDIATYPE CLAVCUDA::sudPinTypesIn[] = {
|
||||
// H264
|
||||
{ &MEDIATYPE_Video, &MEDIASUBTYPE_H264 },
|
||||
{ &MEDIATYPE_Video, &MEDIASUBTYPE_AVC1 },
|
||||
{ &MEDIATYPE_Video, &MEDIASUBTYPE_avc1 },
|
||||
{ &MEDIATYPE_Video, &MEDIASUBTYPE_CCV1 },
|
||||
};
|
||||
const int CLAVCUDA::sudPinTypesInCount = countof(CLAVCUDA::sudPinTypesIn);
|
||||
|
||||
// Define Output Media Types
|
||||
const AMOVIESETUP_MEDIATYPE CLAVCUDA::sudPinTypesOut[] = {
|
||||
{ &MEDIATYPE_Video, &MEDIASUBTYPE_NV12 },
|
||||
};
|
||||
const int CLAVCUDA::sudPinTypesOutCount = countof(CLAVCUDA::sudPinTypesOut);
|
||||
|
||||
// static constructor
|
||||
CUnknown* WINAPI CLAVCUDA::CreateInstance(LPUNKNOWN pUnk, HRESULT* phr)
|
||||
{
|
||||
return new CLAVCUDA(pUnk, phr);
|
||||
}
|
||||
|
||||
// Constructor
|
||||
CLAVCUDA::CLAVCUDA(LPUNKNOWN pUnk, HRESULT* phr)
|
||||
: CTransformFilter(NAME("LAV CUDA Video Decoder"), 0, __uuidof(CLAVCUDA))
|
||||
{
|
||||
|
||||
#ifdef DEBUG
|
||||
DbgSetModuleLevel (LOG_TRACE, DWORD_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
CLAVCUDA::~CLAVCUDA()
|
||||
{
|
||||
}
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP CLAVCUDA::NonDelegatingQueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
CheckPointer(ppv, E_POINTER);
|
||||
|
||||
*ppv = NULL;
|
||||
|
||||
return
|
||||
__super::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
HRESULT CLAVCUDA::CheckInputType(const CMediaType* mtIn)
|
||||
{
|
||||
for(int i = 0; i < sudPinTypesInCount; i++) {
|
||||
if(*sudPinTypesIn[i].clsMajorType == mtIn->majortype
|
||||
&& *sudPinTypesIn[i].clsMinorType == mtIn->subtype && (mtIn->formattype == FORMAT_VideoInfo || mtIn->formattype == FORMAT_VideoInfo2 || mtIn->formattype == FORMAT_MPEG2Video)) {
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return VFW_E_TYPE_NOT_ACCEPTED;
|
||||
}
|
||||
|
||||
HRESULT CLAVCUDA::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut)
|
||||
{
|
||||
return SUCCEEDED(CheckInputType(mtIn))
|
||||
&& mtOut->majortype == MEDIATYPE_Video
|
||||
&& (mtOut->subtype == MEDIASUBTYPE_NV12)
|
||||
? S_OK
|
||||
: VFW_E_TYPE_NOT_ACCEPTED;
|
||||
}
|
||||
|
||||
HRESULT CLAVCUDA::GetMediaType(int iPosition, CMediaType *pMediaType)
|
||||
{
|
||||
DbgLog((LOG_TRACE, 5, L"GetMediaType"));
|
||||
if(m_pInput->IsConnected() == FALSE) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
if(iPosition < 0) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if(iPosition > 0) {
|
||||
return VFW_S_NO_MORE_ITEMS;
|
||||
}
|
||||
|
||||
BITMAPINFOHEADER *bmi = NULL;
|
||||
REFERENCE_TIME rtAvgTimePerFrame = 0;
|
||||
|
||||
// Extract the information required for the media type out of the incoming media type
|
||||
CMediaType mtIn = m_pInput->CurrentMediaType();
|
||||
if (mtIn.formattype == FORMAT_VideoInfo) {
|
||||
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)mtIn.Format();
|
||||
rtAvgTimePerFrame = vih->AvgTimePerFrame;
|
||||
bmi = &vih->bmiHeader;
|
||||
} else if (mtIn.formattype == FORMAT_VideoInfo2) {
|
||||
VIDEOINFOHEADER2 *vih2 = (VIDEOINFOHEADER2 *)mtIn.Format();
|
||||
rtAvgTimePerFrame = vih2->AvgTimePerFrame;
|
||||
bmi = &vih2->bmiHeader;
|
||||
} else if (mtIn.formattype == FORMAT_MPEG2Video) {
|
||||
MPEG2VIDEOINFO *mp2vi = (MPEG2VIDEOINFO *)mtIn.Format();
|
||||
rtAvgTimePerFrame = mp2vi->hdr.AvgTimePerFrame;
|
||||
bmi = &mp2vi->hdr.bmiHeader;
|
||||
}
|
||||
|
||||
*pMediaType = CreateMediaType(bmi->biWidth, bmi->biHeight, rtAvgTimePerFrame);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
CMediaType CLAVCUDA::CreateMediaType(LONG biWidth, LONG biHeight, REFERENCE_TIME avgTime) const
|
||||
{
|
||||
CMediaType mt;
|
||||
|
||||
mt.majortype = MEDIATYPE_Video;
|
||||
mt.subtype = MEDIASUBTYPE_NV12;
|
||||
mt.formattype = FORMAT_VideoInfo;
|
||||
|
||||
VIDEOINFOHEADER vih;
|
||||
memset(&vih, 0, sizeof(vih));
|
||||
|
||||
vih.AvgTimePerFrame = avgTime;
|
||||
vih.bmiHeader.biSize = sizeof(vih.bmiHeader);
|
||||
vih.bmiHeader.biWidth = biWidth;
|
||||
vih.bmiHeader.biHeight = biHeight;
|
||||
vih.bmiHeader.biCompression = mt.subtype.Data1;
|
||||
vih.bmiHeader.biPlanes = 1;
|
||||
vih.bmiHeader.biBitCount = 12;
|
||||
vih.bmiHeader.biSizeImage = biWidth * biHeight * vih.bmiHeader.biBitCount / 8;
|
||||
|
||||
mt.SetSampleSize(vih.bmiHeader.biSizeImage);
|
||||
mt.SetFormat((BYTE *)&vih, sizeof(vih));
|
||||
|
||||
return mt;
|
||||
}
|
||||
|
||||
HRESULT CLAVCUDA::DecideBufferSize(IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop)
|
||||
{
|
||||
if(m_pInput->IsConnected() == FALSE) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
pprop->cBuffers = 4;
|
||||
pprop->cbBuffer = m_pOutput->CurrentMediaType().GetSampleSize();
|
||||
pprop->cbAlign = 1;
|
||||
pprop->cbPrefix = 0;
|
||||
|
||||
HRESULT hr;
|
||||
ALLOCATOR_PROPERTIES Actual;
|
||||
if(FAILED(hr = pAllocator->SetProperties(pprop, &Actual))) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return pprop->cBuffers > Actual.cBuffers || pprop->cbBuffer > Actual.cbBuffer
|
||||
? E_FAIL
|
||||
: NOERROR;
|
||||
}
|
||||
|
||||
HRESULT CLAVCUDA::Transform(IMediaSample *pIn, IMediaSample *pOut)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
; LAVCUDA.def ; declares the exports
|
||||
|
||||
LIBRARY "LAVCUDA.ax"
|
||||
|
||||
EXPORTS
|
||||
DllGetClassObject PRIVATE
|
||||
DllCanUnloadNow PRIVATE
|
||||
DllRegisterServer PRIVATE
|
||||
DllUnregisterServer PRIVATE
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Hendrik Leppkes
|
||||
* http://www.1f0.de
|
||||
*
|
||||
* This Program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This Program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
[uuid("62D767FE-4F1B-478B-B350-8ACE9E4DB00E")]
|
||||
class CLAVCUDA : public CTransformFilter
|
||||
{
|
||||
public:
|
||||
// constructor method used by class factory
|
||||
static CUnknown* WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT* phr);
|
||||
|
||||
// IUnknown
|
||||
DECLARE_IUNKNOWN;
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
|
||||
|
||||
// CTransformFilter
|
||||
HRESULT CheckInputType(const CMediaType* mtIn);
|
||||
HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
|
||||
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
|
||||
HRESULT DecideBufferSize(IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop);
|
||||
HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut );
|
||||
|
||||
public:
|
||||
// Pin Configuration
|
||||
const static AMOVIESETUP_MEDIATYPE sudPinTypesIn[];
|
||||
const static int sudPinTypesInCount;
|
||||
const static AMOVIESETUP_MEDIATYPE sudPinTypesOut[];
|
||||
const static int sudPinTypesOutCount;
|
||||
|
||||
private:
|
||||
CLAVCUDA(LPUNKNOWN pUnk, HRESULT* phr);
|
||||
~CLAVCUDA();
|
||||
|
||||
CMediaType CreateMediaType(LONG biWidth, LONG biHeight, REFERENCE_TIME avgTime) const;
|
||||
};
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Hendrik Leppkes
|
||||
* http://www.1f0.de
|
||||
*
|
||||
* This Program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This Program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
// Based on the SampleParser Template by GDCL
|
||||
// --------------------------------------------------------------------------------
|
||||
// Copyright (c) GDCL 2004. All Rights Reserved.
|
||||
// You are free to re-use this as the basis for your own filter development,
|
||||
// provided you retain this copyright notice in the source.
|
||||
// http://www.gdcl.co.uk
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// Initialize the GUIDs
|
||||
#include <InitGuid.h>
|
||||
|
||||
#include "LAVCUDA.h"
|
||||
#include "moreuuids.h"
|
||||
|
||||
// --- COM factory table and registration code --------------
|
||||
|
||||
// Workaround: graphedit crashes when a filter exposes more than 115 input MediaTypes!
|
||||
const AMOVIESETUP_PIN sudpPinsCUDAVideoDec[] = {
|
||||
{L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, CLAVCUDA::sudPinTypesInCount > 115 ? 115 : CLAVCUDA::sudPinTypesInCount, CLAVCUDA::sudPinTypesIn},
|
||||
{L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, CLAVCUDA::sudPinTypesOutCount, CLAVCUDA::sudPinTypesOut}
|
||||
};
|
||||
|
||||
const AMOVIESETUP_FILTER sudFilterReg =
|
||||
{
|
||||
&__uuidof(CLAVCUDA), // filter clsid
|
||||
L"LAV CUDA Video Decoder", // filter name
|
||||
MERIT_PREFERRED + 3, // merit
|
||||
countof(sudpPinsCUDAVideoDec), sudpPinsCUDAVideoDec
|
||||
};
|
||||
|
||||
// --- COM factory table and registration code --------------
|
||||
|
||||
// DirectShow base class COM factory requires this table,
|
||||
// declaring all the COM objects in this DLL
|
||||
CFactoryTemplate g_Templates[] = {
|
||||
// one entry for each CoCreate-able object
|
||||
{
|
||||
sudFilterReg.strName,
|
||||
sudFilterReg.clsID,
|
||||
CLAVCUDA::CreateInstance,
|
||||
NULL,
|
||||
&sudFilterReg
|
||||
}
|
||||
};
|
||||
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
|
||||
|
||||
// self-registration entrypoint
|
||||
STDAPI DllRegisterServer()
|
||||
{
|
||||
// base classes will handle registration using the factory template table
|
||||
return AMovieDllRegisterServer2(true);
|
||||
}
|
||||
|
||||
STDAPI DllUnregisterServer()
|
||||
{
|
||||
// base classes will handle de-registration using the factory template table
|
||||
return AMovieDllRegisterServer2(false);
|
||||
}
|
||||
|
||||
// if we declare the correct C runtime entrypoint and then forward it to the DShow base
|
||||
// classes we will be sure that both the C/C++ runtimes and the base classes are initialized
|
||||
// correctly
|
||||
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
|
||||
BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
return DllEntryPoint(reinterpret_cast<HINSTANCE>(hDllHandle), dwReason, lpReserved);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// LAVCUDA.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
|
@ -0,0 +1,15 @@
|
|||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define countof( array ) ( sizeof( array )/sizeof( array[0] ) )
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
|
||||
#include <DShow.h>
|
||||
#include <streams.h>
|
Loading…
Reference in New Issue