Add VPx Decoder filter. Merges the features of the VP8 and VP9 decoder filters into a single filter. Change-Id: I98cf86350aa1e5d4741c755c6c88b7cd2f5a9d37
diff --git a/IDL/vpxdecoder.idl b/IDL/vpxdecoder.idl new file mode 100644 index 0000000..669890b --- /dev/null +++ b/IDL/vpxdecoder.idl
@@ -0,0 +1,50 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + uuid(2C8C46D4-2E83-476A-ADE9-43CFDB830E48), + helpstring("VPX Decoder Filter Type Library"), + version(1.0) +] + +library VPXDecoderLib { + +enum VP8PostProcessingFlags { + VP8None = 0x00, + VP8Deblock = 0x01, + VP8Demacroblock = 0x02, + VP8AddNoise = 0x04 +}; + +[ + object, + uuid(EB6EBF79-29BC-4377-93FB-B2AD76952B0A), + helpstring("VP8 Decoder Post-Processing Interface") +] +interface IVP8PostProcessing : IUnknown { + HRESULT SetFlags([in] int PostProcessingFlags); + HRESULT GetFlags([out] int* pPostProcessingFlags); + HRESULT SetDeblockingLevel([in] int DeblockingLevel); + HRESULT GetDeblockingLevel([out] int* pDeblockingLevel); + HRESULT SetNoiseLevel([in] int NoiseLevel); + HRESULT GetNoiseLevel([out] int* pNoiseLevel); + HRESULT ApplyPostProcessing(); +} + +[ + uuid(BDDB6A11-9D65-46D8-824E-F376D64E4A8A), + helpstring("VPX Decoder Filter Class") +] +coclass VPXDecoder { + [default] interface IVP8PostProcessing; +} + +} // library VPXDecoderLib
diff --git a/vpxdecoder/.gitignore b/vpxdecoder/.gitignore new file mode 100644 index 0000000..e6ee719 --- /dev/null +++ b/vpxdecoder/.gitignore
@@ -0,0 +1,3 @@ +*.aps +*.TMP +*.user
diff --git a/vpxdecoder/dllentry.cc b/vpxdecoder/dllentry.cc new file mode 100644 index 0000000..2e7ff38 --- /dev/null +++ b/vpxdecoder/dllentry.cc
@@ -0,0 +1,207 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include <comdef.h> +#include <strmif.h> +#include <uuids.h> + +#include <cassert> + +#include "cfactory.h" +#include "comreg.h" +#include "graphutil.h" +#include "vpxdecoderidl.h" +#include "webmtypes.h" + +HMODULE g_hModule; +static ULONG s_cLock; + +namespace VPXDecoderLib { + +HRESULT CreateInstance(IClassFactory*, IUnknown*, const IID&, void**); + +} // namespace VPXDecoderLib + +namespace { + +CFactory factory(&s_cLock, &VPXDecoderLib::CreateInstance); + +} // namespace + +BOOL APIENTRY DllMain(HINSTANCE hModule, + DWORD dwReason, + LPVOID /*lpReserved*/) { + switch (dwReason) { + case DLL_PROCESS_ATTACH: + g_hModule = hModule; + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + default: + break; + } + + return TRUE; +} + +STDAPI DllCanUnloadNow() { return s_cLock ? S_FALSE : S_OK; } + +STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { + if (clsid == CLSID_VPXDecoder) + return factory.QueryInterface(iid, ppv); + + return CLASS_E_CLASSNOTAVAILABLE; +} + +STDAPI DllUnregisterServer() { + const GraphUtil::IFilterMapper2Ptr pMapper(CLSID_FilterMapper2); + if (pMapper == NULL) { + assert(pMapper != NULL); + return E_FAIL; + } + + HRESULT hr = pMapper->UnregisterFilter(&CLSID_LegacyAmFilterCategory, 0, + CLSID_VPXDecoder); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + + hr = ComReg::UnRegisterCoclass(CLSID_VPXDecoder); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + + std::wstring filename_; + hr = ComReg::ComRegGetModuleFileName(g_hModule, filename_); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + if (filename_.empty()) { + assert(!filename_.empty()); + return E_FAIL; + } + + hr = ComReg::UnRegisterTypeLibResource(filename_.c_str()); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + } + + return hr; +} + +STDAPI DllRegisterServer() { + std::wstring filename_; + HRESULT hr = ComReg::ComRegGetModuleFileName(g_hModule, filename_); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + if (filename_.empty()) { + assert(!filename_.empty()); + return E_FAIL; + } + +#if _DEBUG + const wchar_t friendlyname[] = L"WebM VPx Decoder Filter (Debug)"; +#else + const wchar_t friendlyname[] = L"WebM VPx Decoder Filter"; +#endif + + hr = DllUnregisterServer(); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + + hr = ComReg::RegisterTypeLibResource(filename_.c_str(), 0); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + + hr = ComReg::RegisterCoclass( + CLSID_VPXDecoder, friendlyname, filename_.c_str(), L"Webm.VPXDecoder", + L"Webm.VPXDecoder.1", + false, // not insertable + false, // not a control + ComReg::kBoth, // DShow filters must support "both" + LIBID_VPXDecoderLib, // typelib + 0, // no version specified + 0); // no toolbox bitmap + + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + + const GraphUtil::IFilterMapper2Ptr pMapper(CLSID_FilterMapper2); + if (pMapper == NULL) { + assert(pMapper != NULL); + return E_FAIL; + } + + enum { cPins = 2 }; + REGFILTERPINS pins[cPins]; + + REGFILTERPINS& inpin = pins[0]; + + enum { nInpinMediaTypes = 2 }; + const REGPINTYPES inpinMediaTypes[nInpinMediaTypes] = { + {&MEDIATYPE_Video, &WebmTypes::MEDIASUBTYPE_VP80}, + {&MEDIATYPE_Video, &WebmTypes::MEDIASUBTYPE_VP90}}; + + inpin.strName = 0; + inpin.bRendered = FALSE; + inpin.bOutput = FALSE; + inpin.bZero = FALSE; + inpin.bMany = FALSE; + inpin.clsConnectsToFilter = 0; + inpin.strConnectsToPin = 0; + inpin.nMediaTypes = nInpinMediaTypes; + inpin.lpMediaType = inpinMediaTypes; + + REGFILTERPINS& outpin = pins[1]; + + enum { nOutpinMediaTypes = 7 }; + const REGPINTYPES outpinMediaTypes[nOutpinMediaTypes] = { + {&MEDIATYPE_Video, &MEDIASUBTYPE_NV12}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_YV12}, + {&MEDIATYPE_Video, &WebmTypes::MEDIASUBTYPE_I420}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_UYVY}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_YUY2}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_YUYV}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_YVYU}}; + + outpin.strName = 0; + outpin.bRendered = FALSE; + outpin.bOutput = TRUE; + outpin.bZero = FALSE; + outpin.bMany = FALSE; + outpin.clsConnectsToFilter = 0; + outpin.strConnectsToPin = 0; + outpin.nMediaTypes = nOutpinMediaTypes; + outpin.lpMediaType = outpinMediaTypes; + + REGFILTER2 filter; + filter.dwVersion = 1; + filter.dwMerit = MERIT_NORMAL; + filter.cPins = cPins; + filter.rgPins = pins; + + hr = pMapper->RegisterFilter(CLSID_VPXDecoder, friendlyname, 0, + &CLSID_LegacyAmFilterCategory, 0, &filter); + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + } + + return hr; +}
diff --git a/vpxdecoder/resource.h b/vpxdecoder/resource.h new file mode 100644 index 0000000..b67bdcf --- /dev/null +++ b/vpxdecoder/resource.h
@@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by vpxdecoder.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif
diff --git a/vpxdecoder/vpxdecoder.def b/vpxdecoder/vpxdecoder.def new file mode 100644 index 0000000..f620ba2 --- /dev/null +++ b/vpxdecoder/vpxdecoder.def
@@ -0,0 +1,5 @@ +EXPORTS + DllGetClassObject private + DllRegisterServer private + DllUnregisterServer private + DllCanUnloadNow private
diff --git a/vpxdecoder/vpxdecoder.rc b/vpxdecoder/vpxdecoder.rc new file mode 100644 index 0000000..1901487 --- /dev/null +++ b/vpxdecoder/vpxdecoder.rc
@@ -0,0 +1,98 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "1 TYPELIB ""vpxdecoder.tlb\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,3,1 + PRODUCTVERSION 1,0,3,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Google" + VALUE "FileDescription", "WebM VPX Decoder Filter" + VALUE "FileVersion", "1, 0, 3, 1" + VALUE "InternalName", "vpxdecoder" + VALUE "LegalCopyright", "Copyright (C) 2014" + VALUE "OriginalFilename", "vpxdecoder.dll" + VALUE "ProductName", "WebM VPX Decoder Filter" + VALUE "ProductVersion", "1, 0, 3, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + +1 TYPELIB "vpxdecoder.tlb" +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED +
diff --git a/vpxdecoder/vpxdecoder.vcxproj b/vpxdecoder/vpxdecoder.vcxproj new file mode 100644 index 0000000..0b22904 --- /dev/null +++ b/vpxdecoder/vpxdecoder.vcxproj
@@ -0,0 +1,171 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{C4A3A16F-C46B-41BA-A031-94391A535C00}</ProjectGuid> + <RootNamespace>vpxdecoder</RootNamespace> + <Keyword>Win32Proj</Keyword> + <ProjectName>vpxdecoder</ProjectName> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <WholeProgramOptimization>true</WholeProgramOptimization> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v120</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> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)..\dll\webmdshow\$(Configuration)\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)..\obj\$(SolutionName)\$(ProjectName)\$(Configuration)\</IntDir> + <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> + <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)..\dll\webmdshow\$(Configuration)\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)..\obj\$(SolutionName)\$(ProjectName)\$(Configuration)\</IntDir> + <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> + <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> + <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(RootNamespace)</TargetName> + <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(RootNamespace)</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Midl> + <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <TypeLibraryName>$(IntDir)%(Filename).tlb</TypeLibraryName> + <OutputDirectory>%(RootDir)%(Directory)</OutputDirectory> + <HeaderFileName>%(Filename)idl.h</HeaderFileName> + <InterfaceIdentifierFileName>%(Filename)idl.c</InterfaceIdentifierFileName> + </Midl> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>$(SolutionDir)common;$(SolutionDir)IDL;$(SolutionDir)third_party\libvpx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;VPXDECODER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <DebugInformationFormat>OldStyle</DebugInformationFormat> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>strmiids.lib;vpxmtd.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(TargetPath)</OutputFile> + <AdditionalLibraryDirectories>$(SolutionDir)third_party\libvpx\x86\debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ModuleDefinitionFile>vpxdecoder.def</ModuleDefinitionFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <SubSystem>NotSet</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <DataExecutionPrevention> + </DataExecutionPrevention> + <TargetMachine>MachineX86</TargetMachine> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Midl> + <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <TypeLibraryName>$(IntDir)%(Filename).tlb</TypeLibraryName> + <OutputDirectory>%(RootDir)%(Directory)</OutputDirectory> + <HeaderFileName>%(Filename)idl.h</HeaderFileName> + <InterfaceIdentifierFileName>%(Filename)idl.c</InterfaceIdentifierFileName> + </Midl> + <ClCompile> + <AdditionalIncludeDirectories>$(SolutionDir)common;$(SolutionDir)IDL;$(SolutionDir)third_party\libvpx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;VPXDECODER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <DebugInformationFormat> + </DebugInformationFormat> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>strmiids.lib;vpxmt.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(TargetPath)</OutputFile> + <AdditionalLibraryDirectories>$(SolutionDir)third_party\libvpx\x86\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ModuleDefinitionFile>vpxdecoder.def</ModuleDefinitionFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <DataExecutionPrevention> + </DataExecutionPrevention> + <TargetMachine>MachineX86</TargetMachine> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\common\cenumpins.cc" /> + <ClCompile Include="..\common\cfactory.cc" /> + <ClCompile Include="..\common\clockable.cc" /> + <ClCompile Include="..\common\cmediasample.cc" /> + <ClCompile Include="..\common\cmediatypes.cc" /> + <ClCompile Include="..\common\cmemallocator.cc" /> + <ClCompile Include="..\common\comreg.cc" /> + <ClCompile Include="..\common\iidstr.cc" /> + <ClCompile Include="..\common\mediatypeutil.cc" /> + <ClCompile Include="..\common\webmtypes.cc" /> + <ClCompile Include="..\IDL\vpxdecoderidl.c" /> + <ClCompile Include="dllentry.cc" /> + <ClCompile Include="vpxdecoderfilter.cc" /> + <ClCompile Include="vpxdecoderinpin.cc" /> + <ClCompile Include="vpxdecoderoutpin.cc" /> + <ClCompile Include="vpxdecoderpin.cc" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\common\cenumpins.h" /> + <ClInclude Include="..\common\cfactory.h" /> + <ClInclude Include="..\common\clockable.h" /> + <ClInclude Include="..\common\cmediasample.h" /> + <ClInclude Include="..\common\cmediatypes.h" /> + <ClInclude Include="..\common\cmemallocator.h" /> + <ClInclude Include="..\common\comreg.h" /> + <ClInclude Include="..\common\iidstr.h" /> + <ClInclude Include="..\common\mediatypeutil.h" /> + <ClInclude Include="..\common\webmtypes.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="..\IDL\vpxdecoderidl.h" /> + <ClInclude Include="vpxdecoderfilter.h" /> + <ClInclude Include="vpxdecoderinpin.h" /> + <ClInclude Include="vpxdecoderoutpin.h" /> + <ClInclude Include="vpxdecoderpin.h" /> + </ItemGroup> + <ItemGroup> + <Midl Include="..\IDL\vpxdecoder.idl" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="vpxdecoder.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file
diff --git a/vpxdecoder/vpxdecoder.vcxproj.filters b/vpxdecoder/vpxdecoder.vcxproj.filters new file mode 100644 index 0000000..f9e0721 --- /dev/null +++ b/vpxdecoder/vpxdecoder.vcxproj.filters
@@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Common Files"> + <UniqueIdentifier>{b4b7c47c-e6d2-4246-b839-07cc0b21fe32}</UniqueIdentifier> + </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="..\common\cenumpins.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\cfactory.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\clockable.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\cmediasample.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\cmediatypes.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\cmemallocator.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\comreg.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\mediatypeutil.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\common\webmtypes.cc"> + <Filter>Common Files</Filter> + </ClCompile> + <ClCompile Include="..\IDL\vpxdecoderidl.c"> + <Filter>Resource Files</Filter> + </ClCompile> + <ClCompile Include="dllentry.cc" /> + <ClCompile Include="vpxdecoderfilter.cc" /> + <ClCompile Include="vpxdecoderinpin.cc" /> + <ClCompile Include="vpxdecoderoutpin.cc" /> + <ClCompile Include="vpxdecoderpin.cc" /> + <ClCompile Include="..\common\iidstr.cc"> + <Filter>Common Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\common\cenumpins.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\cfactory.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\clockable.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\cmediasample.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\cmediatypes.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\cmemallocator.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\comreg.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\mediatypeutil.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="..\common\webmtypes.h"> + <Filter>Common Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Resource Files</Filter> + </ClInclude> + <ClInclude Include="..\IDL\vpxdecoderidl.h"> + <Filter>Resource Files</Filter> + </ClInclude> + <ClInclude Include="vpxdecoderfilter.h" /> + <ClInclude Include="vpxdecoderinpin.h" /> + <ClInclude Include="vpxdecoderoutpin.h" /> + <ClInclude Include="vpxdecoderpin.h" /> + <ClInclude Include="..\common\iidstr.h"> + <Filter>Common Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Midl Include="..\IDL\vpxdecoder.idl"> + <Filter>Resource Files</Filter> + </Midl> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="vpxdecoder.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project> \ No newline at end of file
diff --git a/vpxdecoder/vpxdecoderfilter.cc b/vpxdecoder/vpxdecoderfilter.cc new file mode 100644 index 0000000..f16d368 --- /dev/null +++ b/vpxdecoder/vpxdecoderfilter.cc
@@ -0,0 +1,636 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "vpxdecoderfilter.h" + +#include <strmif.h> +#include <uuids.h> +#include <vfwmsgs.h> + +#include <cassert> +#include <new> + +#include "cenumpins.h" +#include "vpxdecoderidl.h" +#include "webmtypes.h" + +#ifdef _DEBUG +#include "iidstr.h" +#include "odbgstream.h" +using std::endl; +using std::hex; +using std::dec; +#endif + +using std::wstring; + +namespace VPXDecoderLib { + +HRESULT CreateInstance(IClassFactory* pClassFactory, IUnknown* pOuter, + const IID& iid, void** ppv) { + if (ppv == 0) + return E_POINTER; + + *ppv = 0; + + if ((pOuter != 0) && (iid != __uuidof(IUnknown))) + return E_INVALIDARG; + + Filter* p = new (std::nothrow) Filter(pClassFactory, pOuter); + + if (p == 0) + return E_OUTOFMEMORY; + + assert(p->m_nondelegating.m_cRef == 0); + + const HRESULT hr = p->m_nondelegating.QueryInterface(iid, ppv); + + if (SUCCEEDED(hr)) { + assert(*ppv); + assert(p->m_nondelegating.m_cRef == 1); + + return S_OK; + } + + assert(*ppv == 0); + assert(p->m_nondelegating.m_cRef == 0); + + delete p; + p = 0; + + return hr; +} + +// +// Filter::CNondelegating +// +HRESULT Filter::CNondelegating::QueryInterface(const IID& iid, void** ppv) { + if (ppv == 0) + return E_POINTER; + + IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv); + + if (iid == __uuidof(IUnknown)) { + pUnk = this; // must be nondelegating + } else if (iid == __uuidof(IBaseFilter) || + iid == __uuidof(IMediaFilter) || + iid == __uuidof(IPersist)) { + pUnk = static_cast<IBaseFilter*>(m_pFilter); + } else if (iid == __uuidof(IVP8PostProcessing)) { + pUnk = static_cast<IVP8PostProcessing*>(m_pFilter); + } else { +#if _DEBUG + wodbgstream os; + os << "vpxdec::filter::QI: iid=" << IIDStr(iid) << std::endl; +#endif + + pUnk = 0; + return E_NOINTERFACE; + } + + pUnk->AddRef(); + return S_OK; +} + +ULONG Filter::CNondelegating::AddRef() { + return InterlockedIncrement(&m_cRef); +} + +ULONG Filter::CNondelegating::Release() { + const LONG n = InterlockedDecrement(&m_cRef); + +#if _DEBUG + odbgstream os; + os << "vpxdec::filter::Release: n=" << n << endl; +#endif + + if (n > 0) + return n; + + delete m_pFilter; + return 0; +} + +// +// Filter +// +#pragma warning(disable : 4355) // 'this' ptr in member init list +Filter::Filter(IClassFactory* pClassFactory, IUnknown* pOuter) + : m_pClassFactory(pClassFactory), + m_nondelegating(this), + m_pOuter(pOuter ? pOuter : &m_nondelegating), + m_clock(0), + m_state(kStateStopped), + m_inpin(this), + m_outpin(this) { + m_pClassFactory->LockServer(TRUE); + + const HRESULT hr = CLockable::Init(); + hr; + assert(SUCCEEDED(hr)); + + m_info.pGraph = 0; + m_info.achName[0] = L'\0'; + + // TODO: these need to be read from and written to the .grf stream. + m_cfg.flags = 0; + m_cfg.deblock = 0; + m_cfg.noise = 0; + +#ifdef _DEBUG + odbgstream os; + os << "vpxdec::filter::ctor" << endl; +#endif +} +#pragma warning(default : 4355) + +Filter::~Filter() { +#ifdef _DEBUG + odbgstream os; + os << "vpxdec::filter::dtor" << endl; +#endif + + m_pClassFactory->LockServer(FALSE); +} + +HRESULT Filter::QueryInterface(const IID& iid, void** ppv) { + return m_pOuter->QueryInterface(iid, ppv); +} + +ULONG Filter::AddRef() { + return m_pOuter->AddRef(); +} + +ULONG Filter::Release() { + return m_pOuter->Release(); +} + +HRESULT Filter::GetClassID(CLSID* p) { + if (p == 0) + return E_POINTER; + + *p = CLSID_VPXDecoder; + return S_OK; +} + +HRESULT Filter::Stop() { + // Stop is a synchronous operation: when it completes, + // the filter is stopped. + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + // odbgstream os; + // os << "mkvsplit::Filter::Stop" << endl; + + switch (m_state) { + case kStatePaused: + case kStatePausedWaitingForKeyframe: + case kStateRunning: + case kStateRunningWaitingForKeyframe: + m_state = kStateStopped; + OnStop(); // decommit outpin's allocator + break; + + case kStateStopped: + break; + + default: + assert(false); + break; + } + + return S_OK; +} + +HRESULT Filter::Pause() { + // Unlike Stop(), Pause() can be asynchronous (that's why you have + // GetState()). + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + // odbgstream os; + // os << "mkvsplit::Filter::Pause" << endl; + + switch (m_state) { + case kStateStopped: + OnStart(); // commit outpin's allocator + m_state = kStatePausedWaitingForKeyframe; + break; + + case kStateRunning: + m_state = kStatePaused; + break; + + case kStateRunningWaitingForKeyframe: + m_state = kStatePausedWaitingForKeyframe; + break; + + case kStatePausedWaitingForKeyframe: + case kStatePaused: + break; + + default: + assert(false); + break; + } + + return S_OK; +} + +HRESULT Filter::Run(REFERENCE_TIME start) { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + // odbgstream os; + // os << "mkvsplit::Filter::Run" << endl; + + switch (m_state) { + case kStateStopped: + OnStart(); + m_state = kStateRunningWaitingForKeyframe; + break; + + case kStatePausedWaitingForKeyframe: + m_state = kStateRunningWaitingForKeyframe; + break; + + case kStatePaused: + m_state = kStateRunning; + break; + + case kStateRunningWaitingForKeyframe: + case kStateRunning: + break; + + default: + assert(false); + break; + } + + m_start = start; + return S_OK; +} + +HRESULT Filter::GetState(DWORD, FILTER_STATE* p) { + if (p == 0) + return E_POINTER; + + Lock lock; + + const HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + *p = GetStateLocked(); + return S_OK; +} + +HRESULT Filter::SetSyncSource(IReferenceClock* clock) { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_clock) + m_clock->Release(); + + m_clock = clock; + + if (m_clock) + m_clock->AddRef(); + + return S_OK; +} + +HRESULT Filter::GetSyncSource(IReferenceClock** pclock) { + if (pclock == 0) + return E_POINTER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + IReferenceClock*& clock = *pclock; + + clock = m_clock; + + if (clock) + clock->AddRef(); + + return S_OK; +} + +HRESULT Filter::EnumPins(IEnumPins** pp) { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + IPin* pins[2]; + + pins[0] = &m_inpin; + pins[1] = &m_outpin; + + return CEnumPins::CreateInstance(pins, 2, pp); +} + +HRESULT Filter::FindPin(LPCWSTR id1, IPin** pp) { + if (pp == 0) + return E_POINTER; + + IPin*& p = *pp; + p = 0; + + if (id1 == 0) + return E_INVALIDARG; + + Pin* pPin = &m_inpin; + + if (wcscmp(id1, pPin->m_id.c_str()) == 0) { + p = pPin; + p->AddRef(); + + return S_OK; + } + + pPin = &m_outpin; + + if (wcscmp(id1, pPin->m_id.c_str()) == 0) { + p = pPin; + p->AddRef(); + + return S_OK; + } + + return VFW_E_NOT_FOUND; +} + +HRESULT Filter::QueryFilterInfo(FILTER_INFO* p) { + if (p == 0) + return E_POINTER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + enum { size = sizeof(p->achName) / sizeof(WCHAR) }; + const errno_t e = wcscpy_s(p->achName, size, m_info.achName); + assert(e == 0); + if (e != 0) + return E_FAIL; + + p->pGraph = m_info.pGraph; + + if (p->pGraph) + p->pGraph->AddRef(); + + return S_OK; +} + +HRESULT Filter::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR name) { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + // NOTE: + // No, do not adjust reference counts here! + // Read the docs for the reasons why. + // ENDNOTE. + + m_info.pGraph = pGraph; + + if (name == 0) { + m_info.achName[0] = L'\0'; + } else { + enum { size = sizeof(m_info.achName) / sizeof(WCHAR) }; + const errno_t e = wcscpy_s(m_info.achName, size, name); + e; + assert(e == 0); // TODO + } + + return S_OK; +} + +HRESULT Filter::QueryVendorInfo(LPWSTR* pstr) { + if (pstr == 0) + return E_POINTER; + + wchar_t*& str = *pstr; + + str = 0; + return E_NOTIMPL; +} + +HRESULT Filter::SetFlags(int Flags) { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (Flags & ~0x07) + return E_INVALIDARG; + + m_cfg.flags = Flags; + + return S_OK; +} + +HRESULT Filter::SetDeblockingLevel(int level) { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (level < 0) + return E_INVALIDARG; + + if (level > 16) + return E_INVALIDARG; + + m_cfg.deblock = level; + + return S_OK; +} + +HRESULT Filter::SetNoiseLevel(int level) { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (level < 0) + return E_INVALIDARG; + + if (level > 16) + return E_INVALIDARG; + + m_cfg.noise = level; + + return S_OK; +} + +HRESULT Filter::GetFlags(int* pFlags) { + if (pFlags == 0) + return E_POINTER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + *pFlags = m_cfg.flags; + + return S_OK; +} + +HRESULT Filter::GetDeblockingLevel(int* pLevel) { + if (pLevel == 0) + return E_POINTER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + *pLevel = m_cfg.deblock; + + return S_OK; +} + +HRESULT Filter::GetNoiseLevel(int* pLevel) { + if (pLevel == 0) + return E_POINTER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + *pLevel = m_cfg.noise; + + return S_OK; +} + +HRESULT Filter::ApplyPostProcessing() { + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_state != State_Paused) + return VFW_E_NOT_PAUSED; + + return m_inpin.OnApplyPostProcessing(); +} + +void Filter::OnStart() { + HRESULT hr = m_inpin.Start(); + assert(SUCCEEDED(hr)); // TODO + + hr = m_outpin.Start(); + assert(SUCCEEDED(hr)); // TODO +} + +void Filter::OnStop() { + m_outpin.Stop(); + m_inpin.Stop(); +} + +FILTER_STATE Filter::GetStateLocked() const { + switch (m_state) { + case kStateStopped: + return State_Stopped; + + case kStateRunning: + case kStateRunningWaitingForKeyframe: + return State_Running; + + case kStatePaused: + case kStatePausedWaitingForKeyframe: + return State_Paused; + + default: + assert(false); + return State_Stopped; + } +} + +HRESULT Filter::OnDecodeFailureLocked() { + switch (m_state) { + case kStateRunning: + m_state = kStateRunningWaitingForKeyframe; + break; + + case kStatePaused: + m_state = kStatePausedWaitingForKeyframe; + break; + + default: + break; + } + + return S_OK; // continue accepting frames +} + +void Filter::OnDecodeSuccessLocked(bool is_key) { + switch (m_state) { + case kStateRunningWaitingForKeyframe: + if (is_key) + m_state = kStateRunning; + break; + + case kStatePausedWaitingForKeyframe: + if (is_key) + m_state = kStatePaused; + break; + + default: + break; + } +} + +} // namespace VPXDecoderLib
diff --git a/vpxdecoder/vpxdecoderfilter.h b/vpxdecoder/vpxdecoderfilter.h new file mode 100644 index 0000000..7d65821 --- /dev/null +++ b/vpxdecoder/vpxdecoderfilter.h
@@ -0,0 +1,115 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef WEBMDSHOW_VPXDECODER_VPXDECODERFILTER_H_ +#define WEBMDSHOW_VPXDECODER_VPXDECODERFILTER_H_ + +#include <strmif.h> + +#include <string> + +#include "clockable.h" +#include "vpxdecoderidl.h" +#include "vpxdecoderinpin.h" +#include "vpxdecoderoutpin.h" + +namespace VPXDecoderLib { + +class Filter : public IBaseFilter, public IVP8PostProcessing, public CLockable { + public: + struct Config { + int flags; + int deblock; + int noise; + }; + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IBaseFilter + HRESULT STDMETHODCALLTYPE GetClassID(CLSID*); + HRESULT STDMETHODCALLTYPE Stop(); + HRESULT STDMETHODCALLTYPE Pause(); + HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME); + HRESULT STDMETHODCALLTYPE GetState(DWORD, FILTER_STATE*); + HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock*); + HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock**); + HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins**); + HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR, IPin**); + HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO*); + HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph*, LPCWSTR); + HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR*); + + // IVP8PostProcessing + HRESULT STDMETHODCALLTYPE SetFlags(int); + HRESULT STDMETHODCALLTYPE SetDeblockingLevel(int); + HRESULT STDMETHODCALLTYPE SetNoiseLevel(int); + HRESULT STDMETHODCALLTYPE GetFlags(int*); + HRESULT STDMETHODCALLTYPE GetDeblockingLevel(int*); + HRESULT STDMETHODCALLTYPE GetNoiseLevel(int*); + HRESULT STDMETHODCALLTYPE ApplyPostProcessing(); + + // local classes and methods + FILTER_STATE GetStateLocked() const; + HRESULT OnDecodeFailureLocked(); + void OnDecodeSuccessLocked(bool is_key); + + // public members + FILTER_INFO m_info; + Inpin m_inpin; + Outpin m_outpin; + Config m_cfg; + + private: + class CNondelegating : public IUnknown { + public: + explicit CNondelegating(Filter* f) : m_pFilter(f), m_cRef(0) {} + virtual ~CNondelegating() {} + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + Filter* const m_pFilter; + LONG m_cRef; + + private: + CNondelegating(const CNondelegating&); + CNondelegating& operator=(const CNondelegating&); + }; + + enum State { + kStateStopped, + kStatePausedWaitingForKeyframe, + kStatePaused, + kStateRunning, + kStateRunningWaitingForKeyframe + }; + + friend HRESULT CreateInstance(IClassFactory*, IUnknown*, const IID&, void**); + + void OnStop(); + void OnStart(); + + Filter(IClassFactory*, IUnknown*); + virtual ~Filter(); + Filter(const Filter&); + Filter& operator=(const Filter&); + + IClassFactory* const m_pClassFactory; + CNondelegating m_nondelegating; + IUnknown* const m_pOuter; + REFERENCE_TIME m_start; + IReferenceClock* m_clock; + State m_state; +}; + +} // namespace VPXDecoderLib + +#endif // WEBMDSHOW_VPXDECODER_VPXDECODERFILTER_H_ \ No newline at end of file
diff --git a/vpxdecoder/vpxdecoderinpin.cc b/vpxdecoder/vpxdecoderinpin.cc new file mode 100644 index 0000000..5f2026e --- /dev/null +++ b/vpxdecoder/vpxdecoderinpin.cc
@@ -0,0 +1,985 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +// Disable MSVC warning advising use of _s variants of library functions. +#pragma warning(disable:4996) + +// TODO(tomfinegan): Get rid of this warning. +#pragma warning( once : 4505 ) // unreferenced local function has been removed + +#include "vpxdecoderinpin.h" + +#include <amvideo.h> +#include <dvdmedia.h> +#include <evcode.h> +#include <uuids.h> +#include <vfwmsgs.h> + +#include <cassert> + +#include "vpx/vp8dx.h" + +#include "graphutil.h" +#include "mediatypeutil.h" +#include "vpxdecoderfilter.h" +#include "vpxdecoderoutpin.h" +#include "webmtypes.h" + +#ifdef _DEBUG +#include <iomanip> +#include "odbgstream.h" +using std::endl; +using std::hex; +using std::dec; +using std::fixed; +using std::setprecision; +#endif + +namespace { + +const wchar_t kVP8PinName[] = L"VP80"; +const wchar_t kVP9PinName[] = L"VP90"; + +} // namespace + +namespace VPXDecoderLib { + +Inpin::Inpin(Filter* p) + : Pin(p, PINDIR_INPUT, L"input"), m_bEndOfStream(false), m_bFlush(false) { + AM_MEDIA_TYPE mt; + + mt.majortype = MEDIATYPE_Video; + mt.subtype = WebmTypes::MEDIASUBTYPE_VP80; + mt.bFixedSizeSamples = FALSE; + mt.bTemporalCompression = TRUE; + mt.lSampleSize = 0; + mt.formattype = GUID_NULL; + mt.pUnk = 0; + mt.cbFormat = 0; + mt.pbFormat = 0; + + m_preferred_mtv.Add(mt); +} + +HRESULT Inpin::QueryInterface(const IID& iid, void** ppv) { + if (ppv == 0) + return E_POINTER; + + IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv); + + if (iid == __uuidof(IUnknown)) { + pUnk = static_cast<IPin*>(this); + } else if (iid == __uuidof(IPin)) { + pUnk = static_cast<IPin*>(this); + } else if (iid == __uuidof(IMemInputPin)) { + pUnk = static_cast<IMemInputPin*>(this); + } else { + pUnk = 0; + return E_NOINTERFACE; + } + + pUnk->AddRef(); + return S_OK; +} + +ULONG Inpin::AddRef() { + return m_pFilter->AddRef(); +} + +ULONG Inpin::Release() { + return m_pFilter->Release(); +} + +HRESULT Inpin::Connect(IPin*, const AM_MEDIA_TYPE*) { + return E_UNEXPECTED; // for output pins only +} + +HRESULT Inpin::QueryInternalConnections(IPin** pa, ULONG* pn) { + if (pn == 0) + return E_POINTER; + + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + const ULONG m = 1; // number of output pins + + ULONG& n = *pn; + + if (n == 0) { + if (pa == 0) { + // query for required number + n = m; + return S_OK; + } + + return S_FALSE; // means "insufficient number of array elements" + } + + if (n < m) { + n = 0; + return S_FALSE; // means "insufficient number of array elements" + } + + if (pa == 0) { + n = 0; + return E_POINTER; + } + + IPin*& pin = pa[0]; + + pin = &m_pFilter->m_outpin; + pin->AddRef(); + + n = m; + return S_OK; +} + +HRESULT Inpin::ReceiveConnection(IPin* pin, const AM_MEDIA_TYPE* pmt) { + if (pin == 0) + return E_INVALIDARG; + + if (pmt == 0) + return E_INVALIDARG; + + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (m_pFilter->GetStateLocked() != State_Stopped) + return VFW_E_NOT_STOPPED; + + if (bool(m_pPinConnection)) + return VFW_E_ALREADY_CONNECTED; + + m_connection_mtv.Clear(); + + hr = QueryAccept(pmt); + + if (hr != S_OK) + return VFW_E_TYPE_NOT_ACCEPTED; + + const AM_MEDIA_TYPE& mt = *pmt; + + hr = m_connection_mtv.Add(mt); + + if (FAILED(hr)) + return hr; + + m_pPinConnection = pin; + + // TODO: init decompressor here? + + m_pFilter->m_outpin.OnInpinConnect(mt); + + return S_OK; +} + +HRESULT Inpin::EndOfStream() { + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (!bool(m_pPinConnection)) + return VFW_E_NOT_CONNECTED; + + m_bEndOfStream = true; + + if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { + lock.Release(); + +#ifdef _DEBUG + odbgstream os; + os << "vpxdecoder::inpin::EOS: calling pin->EOS" << endl; +#endif + + const HRESULT hr = pPin->EndOfStream(); + +#ifdef _DEBUG + os << "vpxdecoder::inpin::EOS: called pin->EOS; hr=0x" << hex << hr << dec + << endl; +#endif + + return hr; + } + + return S_OK; +} + +HRESULT Inpin::BeginFlush() { + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (!bool(m_pPinConnection)) + return VFW_E_NOT_CONNECTED; + +#if _DEBUG + odbgstream os; + os << "vpxdecoder::inpin::beginflush" << endl; +#endif + + m_bFlush = true; + + if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { + lock.Release(); + + const HRESULT hr = pPin->BeginFlush(); + return hr; + } + + return S_OK; +} + +HRESULT Inpin::EndFlush() { + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (!bool(m_pPinConnection)) + return VFW_E_NOT_CONNECTED; + +#if _DEBUG + odbgstream os; + os << "vpxdecoder::inpin::endflush" << endl; +#endif + + m_bFlush = false; + m_bEndOfStream = false; + + if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { + lock.Release(); + + const HRESULT hr = pPin->EndFlush(); + return hr; + } + + return S_OK; +} + +HRESULT Inpin::NewSegment(REFERENCE_TIME start_time, + REFERENCE_TIME stop_time, + double rate) { + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (!bool(m_pPinConnection)) + return VFW_E_NOT_CONNECTED; + + if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { + lock.Release(); + + const HRESULT hr = pPin->NewSegment(start_time, stop_time, rate); + return hr; + } + + return S_OK; +} + +HRESULT Inpin::QueryAccept(const AM_MEDIA_TYPE* pmt) { + if (pmt == 0) + return E_INVALIDARG; + + const AM_MEDIA_TYPE& mt = *pmt; + + if (mt.majortype != MEDIATYPE_Video) + return S_FALSE; + + if (mt.subtype != WebmTypes::MEDIASUBTYPE_VP80 && + mt.subtype != WebmTypes::MEDIASUBTYPE_VP90) + return S_FALSE; + + if (mt.formattype != FORMAT_VideoInfo) // TODO: liberalize + return S_FALSE; + + if (mt.pbFormat == 0) + return S_FALSE; + + if (mt.cbFormat < sizeof(VIDEOINFOHEADER)) + return S_FALSE; + + const VIDEOINFOHEADER& vih = (VIDEOINFOHEADER&)(*mt.pbFormat); + const BITMAPINFOHEADER& bmih = vih.bmiHeader; + + if (bmih.biSize != sizeof(BITMAPINFOHEADER)) // TODO: liberalize + return S_FALSE; + + if (bmih.biWidth <= 0) + return S_FALSE; + + if (bmih.biHeight <= 0) + return S_FALSE; + + // Confirm that four CCs match. + if (bmih.biCompression != WebmTypes::MEDIASUBTYPE_VP80.Data1 && + bmih.biCompression != WebmTypes::MEDIASUBTYPE_VP90.Data1) { + return S_FALSE; + } + + return S_OK; +} + +HRESULT Inpin::GetAllocator(IMemAllocator** p) { + if (p) + *p = 0; + + return VFW_E_NO_ALLOCATOR; +} + +HRESULT Inpin::NotifyAllocator(IMemAllocator* pAllocator, BOOL) { + if (pAllocator == 0) + return E_INVALIDARG; + + ALLOCATOR_PROPERTIES props; + const HRESULT hr = pAllocator->GetProperties(&props); + + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + +#ifdef _DEBUG + wodbgstream os; + os << "vpxdec::inpin::NotifyAllocator allocator props: \n" + << " cBuffers=" << props.cBuffers << "\n" + << " cbBuffer=" << props.cbBuffer << "\n" + << " cbAlign=" << props.cbAlign << "\n" + << " cbPrefix=" << props.cbPrefix << "\n"; +#endif + + return S_OK; +} + +HRESULT Inpin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES* pp) { + if (pp == NULL) + return E_POINTER; + + return S_OK; +} + +HRESULT Inpin::Receive(IMediaSample* pInSample) { + if (pInSample == 0) + return E_INVALIDARG; + +//#define DEBUG_RECEIVE + +#ifdef DEBUG_RECEIVE + __int64 start_reftime, stop_reftime; + const HRESULT hr_debug = pInSample->GetTime(&start_reftime, &stop_reftime); + + odbgstream os; + os << "vpxdec::inpin::receive: "; + + os << std::fixed << std::setprecision(3); + + if (hr_debug == S_OK) { + os << "start[ms]=" << double(start_reftime) / 10000 + << "; stop[ms]=" << double(stop_reftime) / 10000 + << "; dt[ms]=" << double(stop_reftime - start_reftime) / 10000; + } else if (hr_debug == VFW_S_NO_STOP_TIME) { + os << "start[ms]=" << double(start_reftime) / 10000; + } + + os << endl; +#endif // DEBUG_RECEIVE + + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + +#ifdef DEBUG_RECEIVE + wodbgstream os; + os << L"vpxdec::inpin::Receive: THREAD=0x" + << std::hex << GetCurrentThreadId() << std::dec + << endl; +#endif // DEBUG_RECEIVE + + if (!bool(m_pPinConnection)) + return VFW_E_NOT_CONNECTED; + + Outpin& outpin = m_pFilter->m_outpin; + + if (!bool(outpin.m_pPinConnection)) + return S_FALSE; + + if (!bool(outpin.m_pAllocator)) + return VFW_E_NO_ALLOCATOR; + + if (m_pFilter->GetStateLocked() == State_Stopped) + return VFW_E_NOT_RUNNING; + + if (m_bEndOfStream) + return VFW_E_SAMPLE_REJECTED_EOS; + + if (m_bFlush) + return S_FALSE; + + BYTE* buf; + + hr = pInSample->GetPointer(&buf); + assert(SUCCEEDED(hr)); + assert(buf); + + const long len = pInSample->GetActualDataLength(); + assert(len >= 0); + + const vpx_codec_err_t err = vpx_codec_decode(&m_ctx, buf, len, 0, 0); + + if (err != VPX_CODEC_OK) + return m_pFilter->OnDecodeFailureLocked(); + + hr = pInSample->IsSyncPoint(); + + m_pFilter->OnDecodeSuccessLocked(hr == S_OK); + + if (pInSample->IsPreroll() == S_OK) + return S_OK; + + lock.Release(); + + GraphUtil::IMediaSamplePtr pOutSample; + + hr = outpin.m_pAllocator->GetBuffer(&pOutSample, 0, 0, 0); + + if (FAILED(hr)) + return S_FALSE; + + assert(bool(pOutSample)); + + hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (m_pFilter->GetStateLocked() == State_Stopped) + return VFW_E_NOT_RUNNING; + + if (!bool(outpin.m_pPinConnection)) // should never happen + return S_FALSE; + + if (!bool(outpin.m_pInputPin)) // should never happen + return S_FALSE; + + vpx_codec_iter_t iter = 0; + + vpx_image_t* const f = vpx_codec_get_frame(&m_ctx, &iter); + + if (f == 0) + return S_OK; + + AM_MEDIA_TYPE* pmt; + + hr = pOutSample->GetMediaType(&pmt); + + if (SUCCEEDED(hr) && (pmt != 0)) { + hr = outpin.QueryAccept(pmt); + + if (hr != S_OK) { + assert(hr == S_OK); + return hr; + } + + outpin.m_connection_mtv.Clear(); + outpin.m_connection_mtv.Add(*pmt); + + MediaTypeUtil::Free(pmt); + pmt = 0; + } + + const AM_MEDIA_TYPE& mt = outpin.m_connection_mtv[0]; + + const BITMAPINFOHEADER* bmih_ptr; + const RECT* rc_ptr; + + if (mt.formattype == FORMAT_VideoInfo) { + assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER)); + assert(mt.pbFormat); + + const VIDEOINFOHEADER& vih_out = (VIDEOINFOHEADER&)(*mt.pbFormat); + const RECT& rc_out = vih_out.rcSource; + const BITMAPINFOHEADER& bmih_out = vih_out.bmiHeader; + + bmih_ptr = &bmih_out; + rc_ptr = &rc_out; + } else if (mt.formattype == FORMAT_VideoInfo2) { + assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER2)); + assert(mt.pbFormat); + + const VIDEOINFOHEADER2& vih2_out = (VIDEOINFOHEADER2&)(*mt.pbFormat); + const RECT& rc_out = vih2_out.rcSource; + const BITMAPINFOHEADER& bmih_out = vih2_out.bmiHeader; + + bmih_ptr = &bmih_out; + rc_ptr = &rc_out; + } else { + return E_FAIL; + } + + if (mt.subtype == MEDIASUBTYPE_NV12) + CopyToPlanar(f, pOutSample, mt.subtype, *bmih_ptr); + + else if (mt.subtype == MEDIASUBTYPE_YV12) + CopyToPlanar(f, pOutSample, mt.subtype, *bmih_ptr); + + else if (mt.subtype == WebmTypes::MEDIASUBTYPE_I420) + CopyToPlanar(f, pOutSample, mt.subtype, *bmih_ptr); + + else if (mt.subtype == MEDIASUBTYPE_UYVY) + CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); + + else if (mt.subtype == MEDIASUBTYPE_YUY2) + CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); + + else if (mt.subtype == MEDIASUBTYPE_YUYV) + CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); + + else if (mt.subtype == MEDIASUBTYPE_YVYU) + CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); + + else + return E_FAIL; + + __int64 st, sp; + + hr = pInSample->GetTime(&st, &sp); + + if (FAILED(hr)) { + hr = pOutSample->SetTime(0, 0); + assert(SUCCEEDED(hr)); + } else if (hr == S_OK) { + hr = pOutSample->SetTime(&st, &sp); + assert(SUCCEEDED(hr)); + } else { + hr = pOutSample->SetTime(&st, 0); + assert(SUCCEEDED(hr)); + } + + hr = pOutSample->SetSyncPoint(TRUE); + assert(SUCCEEDED(hr)); + + hr = pOutSample->SetPreroll(FALSE); + assert(SUCCEEDED(hr)); + + hr = pInSample->IsDiscontinuity(); + hr = pOutSample->SetDiscontinuity(hr == S_OK); + + hr = pOutSample->SetMediaTime(0, 0); + +#if 0 + __int64 st, sp; + hr = pOutSample->GetTime(&st, &sp); + assert(SUCCEEDED(hr)); + + odbgstream os; + os << "V: " << fixed << setprecision(3) << (double(st)/10000000.0) << endl; +#endif + + lock.Release(); + + return outpin.m_pInputPin->Receive(pOutSample); +} + +HRESULT Inpin::ReceiveMultiple(IMediaSample** pSamples, + long n, // in + long* pm) { // out + if (pm == 0) + return E_POINTER; + + long& m = *pm; // out + m = 0; + + if (n <= 0) + return S_OK; // weird + + if (pSamples == 0) + return E_INVALIDARG; + + for (long i = 0; i < n; ++i) { + IMediaSample* const pSample = pSamples[i]; + assert(pSample); + + const HRESULT hr = Receive(pSample); + + if (hr != S_OK) + return hr; + + ++m; + } + + return S_OK; +} + +void Inpin::CopyToPlanar(const vpx_image_t* f, IMediaSample* pOutSample, + const GUID& subtype_out, + const BITMAPINFOHEADER& bmih_out) { + // Y + + const BYTE* pInY = f->planes[VPX_PLANE_Y]; + assert(pInY); + + unsigned int width_in = f->d_w; + unsigned int height_in = f->d_h; + + BYTE* pOutBuf; + + HRESULT hr = pOutSample->GetPointer(&pOutBuf); + assert(SUCCEEDED(hr)); + assert(pOutBuf); + + BYTE* pOut = pOutBuf; + + const int strideInY = f->stride[VPX_PLANE_Y]; + + LONG strideOut = bmih_out.biWidth; + assert(strideOut); + assert((strideOut % 2) == 0); + + for (unsigned int y = 0; y < height_in; ++y) { + memcpy(pOut, pInY, width_in); + pInY += strideInY; + pOut += strideOut; + } + + width_in = (width_in + 1) / 2; + height_in = (height_in + 1) / 2; + + const BYTE* pInV = f->planes[VPX_PLANE_V]; + assert(pInV); + + const int strideInV = f->stride[VPX_PLANE_V]; + + const BYTE* pInU = f->planes[VPX_PLANE_U]; + assert(pInU); + + const int strideInU = f->stride[VPX_PLANE_U]; + + if (subtype_out == MEDIASUBTYPE_NV12) { + // Note that while NV12 is considered a planar format, + // the chroma plane packs the UV samples. + + // UV + + for (unsigned int y = 0; y < height_in; ++y) { + const BYTE* u = pInU; + const BYTE* v = pInV; + BYTE* uv = pOut; + + for (unsigned int idx = 0; idx < width_in; ++idx) { + *uv++ = *u++; + *uv++ = *v++; + } + + pInU += strideInU; + pInV += strideInV; + pOut += strideOut; + } + } else if (subtype_out == MEDIASUBTYPE_YV12) { + strideOut /= 2; + + // V + + for (unsigned int y = 0; y < height_in; ++y) { + memcpy(pOut, pInV, width_in); + pInV += strideInV; + pOut += strideOut; + } + + // U + + for (unsigned int y = 0; y < height_in; ++y) { + memcpy(pOut, pInU, width_in); + pInU += strideInU; + pOut += strideOut; + } + } else { + assert(subtype_out == WebmTypes::MEDIASUBTYPE_I420); + strideOut /= 2; + + // U + + for (unsigned int y = 0; y < height_in; ++y) { + memcpy(pOut, pInU, width_in); + pInU += strideInU; + pOut += strideOut; + } + + // V + + for (unsigned int y = 0; y < height_in; ++y) { + memcpy(pOut, pInV, width_in); + pInV += strideInV; + pOut += strideOut; + } + } + + const ptrdiff_t lenOut_ = pOut - pOutBuf; + const long lenOut = static_cast<long>(lenOut_); + + hr = pOutSample->SetActualDataLength(lenOut); + assert(SUCCEEDED(hr)); +} + +void Inpin::CopyToPacked(const vpx_image_t* f, IMediaSample* pOutSample, + const GUID& subtype_out, const RECT& rc_out, + const BITMAPINFOHEADER& bmih_out) { + const LONG rect_width_out = rc_out.right - rc_out.left; + assert(rect_width_out >= 0); + + const LONG width_out = + (rect_width_out > 0) ? rect_width_out : bmih_out.biWidth; + assert(width_out > 0); + + const LONG rect_height_out = rc_out.bottom - rc_out.top; + assert(rect_height_out >= 0); + + const LONG height_out = + (rect_height_out > 0) ? rect_height_out : labs(bmih_out.biHeight); + + const BYTE* pInY_base = f->planes[VPX_PLANE_Y]; + assert(pInY_base); + + const int strideInY = f->stride[VPX_PLANE_Y]; + + const BYTE* pInV_base = f->planes[VPX_PLANE_V]; + assert(pInV_base); + + const int strideInV = f->stride[VPX_PLANE_V]; + + const BYTE* pInU_base = f->planes[VPX_PLANE_U]; + assert(pInU_base); + + const int strideInU = f->stride[VPX_PLANE_U]; + + const unsigned int width_in = f->d_w; + assert(LONG(width_in) == width_out); + + const unsigned int height_in = f->d_h; + assert(LONG(height_in) == height_out); + + BYTE* pOutBuf; + + HRESULT hr = pOutSample->GetPointer(&pOutBuf); + assert(SUCCEEDED(hr)); + assert(pOutBuf); + + const LONG strideOut_ = 2 * width_in; + LONG strideOut; + + if (bmih_out.biWidth < strideOut_) + strideOut = strideOut_; + else + strideOut = bmih_out.biWidth; + + const LONG uv_width = width_in / 2; + const LONG uv_height = height_in / 2; + + int u_off, v_off, y_off; + + if (subtype_out == MEDIASUBTYPE_UYVY) { + u_off = 0; + v_off = 2; + y_off = 1; + } else if ((subtype_out == MEDIASUBTYPE_YUY2) || + (subtype_out == MEDIASUBTYPE_YUYV)) { + u_off = 1; + v_off = 3; + y_off = 0; + } else { + assert(subtype_out == MEDIASUBTYPE_YVYU); + + u_off = 3; + v_off = 1; + y_off = 0; + } + + BYTE* pOut = pOutBuf; + + for (LONG hdx = 0; hdx < uv_height; ++hdx) { + BYTE* const pOut0 = pOut; + pOut += strideOut; + + BYTE* const pOut1 = pOut; + pOut += strideOut; + + BYTE* pOutU0 = pOut0 + u_off; + BYTE* pOutU1 = pOut1 + u_off; + + BYTE* pOutV0 = pOut0 + v_off; + BYTE* pOutV1 = pOut1 + v_off; + + BYTE* pOutY0 = pOut0 + y_off; + BYTE* pOutY1 = pOut1 + y_off; + + const BYTE* pInU = pInU_base; + pInU_base += strideInU; + + const BYTE* pInV = pInV_base; + pInV_base += strideInV; + + const BYTE* pInY0 = pInY_base; + pInY_base += strideInY; + + const BYTE* pInY1 = pInY_base; + pInY_base += strideInY; + + for (LONG wdx = 0; wdx < uv_width; ++wdx) { + *pOutU0 = *pInU; + *pOutU1 = *pInU; + + pOutU0 += 4; + pOutU1 += 4; + + ++pInU; + + *pOutV0 = *pInV; + *pOutV1 = *pInV; + + pOutV0 += 4; + pOutV1 += 4; + + ++pInV; + + *pOutY0 = *pInY0; + *pOutY1 = *pInY1; + + pOutY0 += 2; + pOutY1 += 2; + + ++pInY0; + ++pInY1; + + *pOutY0 = *pInY0; + *pOutY1 = *pInY1; + + pOutY0 += 2; + pOutY1 += 2; + + ++pInY0; + ++pInY1; + } + } + + const ptrdiff_t lenOut_ = pOut - pOutBuf; + const long lenOut = static_cast<long>(lenOut_); + + hr = pOutSample->SetActualDataLength(lenOut); + assert(SUCCEEDED(hr)); +} + +HRESULT Inpin::ReceiveCanBlock() { + Filter::Lock lock; + + const HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return S_OK; //? + + if (IMemInputPin* pPin = m_pFilter->m_outpin.m_pInputPin) { + lock.Release(); + return pPin->ReceiveCanBlock(); + } + + return S_FALSE; +} + +HRESULT Inpin::OnDisconnect() { + return m_pFilter->m_outpin.OnInpinDisconnect(); +} + +HRESULT Inpin::GetName(PIN_INFO& info) const { + const wchar_t* name = L"No connection"; + if (m_connection_mtv.Empty() == false) { + if (m_connection_mtv[0].subtype == WebmTypes::MEDIASUBTYPE_VP80) + name = kVP8PinName; + else // (m_connection_mtv[0].subtype == WebmTypes::MEDIASUBTYPE_VP90) + name = kVP9PinName; + } + + wcscpy(info.achName, name); + return S_OK; +} + +HRESULT Inpin::Start() { + m_bEndOfStream = false; + m_bFlush = false; + + vpx_codec_iface_t* vpx = NULL; + int flags = 0; + + if (m_connection_mtv[0].subtype == WebmTypes::MEDIASUBTYPE_VP80) { + // TODO(tomfinegan): Do we really want post proc? VP8Decoder always enabled + // it, so here it remains. + flags = VPX_CODEC_USE_POSTPROC; + vpx = &vpx_codec_vp8_dx_algo; + } else if (m_connection_mtv[0].subtype == WebmTypes::MEDIASUBTYPE_VP90) { + vpx = &vpx_codec_vp9_dx_algo; + } else { + return E_FAIL; + } + + const vpx_codec_err_t err = vpx_codec_dec_init(&m_ctx, vpx, 0, flags); + if (err == VPX_CODEC_MEM_ERROR) + return E_OUTOFMEMORY; + + if (err != VPX_CODEC_OK) + return E_FAIL; + + if (m_connection_mtv[0].subtype == WebmTypes::MEDIASUBTYPE_VP80) { + const HRESULT hr = OnApplyPostProcessing(); + + if (FAILED(hr)) { + Stop(); + return hr; + } + } + + return S_OK; +} + +void Inpin::Stop() { + const vpx_codec_err_t err = vpx_codec_destroy(&m_ctx); + err; + assert(err == VPX_CODEC_OK); +} + +HRESULT Inpin::OnApplyPostProcessing() { + const Filter::Config& src = m_pFilter->m_cfg; + vp8_postproc_cfg_t tgt; + + tgt.post_proc_flag = src.flags; + tgt.deblocking_level = src.deblock; + tgt.noise_level = src.noise; + + const vpx_codec_err_t err = vpx_codec_control(&m_ctx, VP8_SET_POSTPROC, &tgt); + + return (err == VPX_CODEC_OK) ? S_OK : E_FAIL; +} + +} // namespace VPXDecoderLib
diff --git a/vpxdecoder/vpxdecoderinpin.h b/vpxdecoder/vpxdecoderinpin.h new file mode 100644 index 0000000..5873364 --- /dev/null +++ b/vpxdecoder/vpxdecoderinpin.h
@@ -0,0 +1,80 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef WEBMDSHOW_VPXDECODER_VPXDECODERINPIN_H_ +#define WEBMDSHOW_VPXDECODER_VPXDECODERINPIN_H_ + +#include <amvideo.h> + +#include "vpx/vpx_decoder.h" + +#include "graphutil.h" +#include "vpxdecoderpin.h" + +namespace VPXDecoderLib { + +class Inpin : public Pin, public IMemInputPin { + public: + explicit Inpin(Filter*); + + // IUnknown interface: + HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IPin interface: + HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE*); + HRESULT STDMETHODCALLTYPE Connect(IPin*, const AM_MEDIA_TYPE*); + + // HRESULT STDMETHODCALLTYPE Disconnect(); + HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin*, const AM_MEDIA_TYPE*); + HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin**, ULONG*); + HRESULT STDMETHODCALLTYPE EndOfStream(); + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME, REFERENCE_TIME, double); + + // IMemInputPin + HRESULT STDMETHODCALLTYPE GetAllocator(IMemAllocator**); + HRESULT STDMETHODCALLTYPE NotifyAllocator(IMemAllocator*, BOOL); + HRESULT STDMETHODCALLTYPE GetAllocatorRequirements(ALLOCATOR_PROPERTIES*); + HRESULT STDMETHODCALLTYPE Receive(IMediaSample*); + HRESULT STDMETHODCALLTYPE ReceiveMultiple(IMediaSample**, long, long*); + HRESULT STDMETHODCALLTYPE ReceiveCanBlock(); + + // local functions + HRESULT Start(); // from stopped to running/paused + void Stop(); // from running/paused to stopped + HRESULT OnApplyPostProcessing(); + + protected: + HRESULT GetName(PIN_INFO&) const; + HRESULT OnDisconnect(); + + private: + HRESULT PopulateSample(IMediaSample*, const vpx_image_t*); + + static void CopyToPlanar(const vpx_image_t* image, IMediaSample* sample, + const GUID& subtype_out, + const BITMAPINFOHEADER& bmih_out); + + static void CopyToPacked(const vpx_image_t* image, IMediaSample* sample, + const GUID& subtype_out, const RECT& rc_out, + const BITMAPINFOHEADER& bmih_out); + + // Manual DISALLOW_COPY_AND_ASSIGN. + Inpin(const Inpin&); + Inpin& operator=(const Inpin&); + + bool m_bEndOfStream; + bool m_bFlush; + vpx_codec_ctx_t m_ctx; +}; + +} // namespace VPXDecoderLib + +#endif // WEBMDSHOW_VPXDECODER_VPXDECODERINPIN_H_
diff --git a/vpxdecoder/vpxdecoderoutpin.cc b/vpxdecoder/vpxdecoderoutpin.cc new file mode 100644 index 0000000..629893d --- /dev/null +++ b/vpxdecoder/vpxdecoderoutpin.cc
@@ -0,0 +1,944 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include "vpxdecoderoutpin.h" + +#include <amvideo.h> +#include <dvdmedia.h> +#include <strmif.h> +#include <uuids.h> +#include <vfwmsgs.h> + +#include <cassert> + +#include "cmediasample.h" +#include "mediatypeutil.h" +#include "vpxdecoderfilter.h" +#include "webmtypes.h" + +#ifdef _DEBUG +#include "odbgstream.h" +#include "iidstr.h" +#endif + +using std::wstring; + +namespace VPXDecoderLib { + +Outpin::Outpin(Filter* pFilter) : Pin(pFilter, PINDIR_OUTPUT, L"output") { + SetDefaultMediaTypes(); +} + +Outpin::~Outpin() { + assert(!bool(m_pAllocator)); + assert(!bool(m_pInputPin)); +} + +HRESULT Outpin::Start() // transition from stopped +{ + if (m_pPinConnection == 0) + return S_FALSE; // nothing we need to do + + assert(bool(m_pAllocator)); + assert(bool(m_pInputPin)); + + const HRESULT hr = m_pAllocator->Commit(); + hr; + assert(SUCCEEDED(hr)); // TODO + + return S_OK; +} + +void Outpin::Stop() // transition to stopped +{ + if (m_pPinConnection == 0) + return; // nothing was done + + assert(bool(m_pAllocator)); + assert(bool(m_pInputPin)); + + const HRESULT hr = m_pAllocator->Decommit(); + hr; + assert(SUCCEEDED(hr)); +} + +HRESULT Outpin::QueryInterface(const IID& iid, void** ppv) { + if (ppv == 0) + return E_POINTER; + + IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv); + + if (iid == __uuidof(IUnknown)) { + pUnk = static_cast<IPin*>(this); + } else if (iid == __uuidof(IPin)) { + pUnk = static_cast<IPin*>(this); + } else if (iid == __uuidof(IMediaSeeking)) { + pUnk = static_cast<IMediaSeeking*>(this); + } else { +#if _DEBUG + wodbgstream os; + os << "vpxdec::outpin::QI: iid=" << IIDStr(iid) << std::endl; +#endif + + pUnk = 0; + return E_NOINTERFACE; + } + + pUnk->AddRef(); + return S_OK; +} + +ULONG Outpin::AddRef() { + return m_pFilter->AddRef(); +} + +ULONG Outpin::Release() { + return m_pFilter->Release(); +} + +HRESULT Outpin::Connect(IPin* pin, const AM_MEDIA_TYPE* pmt) { + if (pin == 0) + return E_POINTER; + + GraphUtil::IMemInputPinPtr pInputPin; + + HRESULT hr = pin->QueryInterface(&pInputPin); + + if (hr != S_OK) + return hr; + + Filter::Lock lock; + + hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (m_pFilter->GetStateLocked() != State_Stopped) + return VFW_E_NOT_STOPPED; + + if (bool(m_pPinConnection)) + return VFW_E_ALREADY_CONNECTED; + + if (!bool(m_pFilter->m_inpin.m_pPinConnection)) + return VFW_E_NO_TYPES; // VFW_E_NOT_CONNECTED? + + m_connection_mtv.Clear(); + + if (pmt) { + hr = QueryAccept(pmt); + + if (hr != S_OK) + return VFW_E_TYPE_NOT_ACCEPTED; + + if ((pmt->formattype == FORMAT_VideoInfo) || + (pmt->formattype == FORMAT_VideoInfo2)) { + hr = pin->ReceiveConnection(this, pmt); + + if (FAILED(hr)) + return hr; + + m_connection_mtv.Add(*pmt); + } else { // partial media type + const ULONG n = m_preferred_mtv.Size(); + LONG idx = -1; + + for (ULONG i = 0; i < n; ++i) { + const AM_MEDIA_TYPE& mt = m_preferred_mtv[i]; + + if (pmt->subtype == mt.subtype) { + idx = i; + break; + } + } + + if (idx < 0) // weird + return VFW_E_TYPE_NOT_ACCEPTED; + + const AM_MEDIA_TYPE& mt = m_preferred_mtv[idx]; + + hr = pin->ReceiveConnection(this, &mt); + + if (FAILED(hr)) + return hr; + + m_connection_mtv.Add(mt); + } + } else { + ULONG i = 0; + const ULONG j = m_preferred_mtv.Size(); + + while (i < j) { + const AM_MEDIA_TYPE& mt = m_preferred_mtv[i]; + + hr = pin->ReceiveConnection(this, &mt); + + if (SUCCEEDED(hr)) + break; + + ++i; + } + + if (i >= j) + return VFW_E_NO_ACCEPTABLE_TYPES; + + const AM_MEDIA_TYPE& mt = m_preferred_mtv[i]; + + m_connection_mtv.Add(mt); + } + + GraphUtil::IMemAllocatorPtr pAllocator; + hr = pInputPin->GetAllocator(&pAllocator); + if (FAILED(hr)) + hr = CMediaSample::CreateAllocator(&pAllocator); + + if (FAILED(hr) || pAllocator == NULL) { + assert(SUCCEEDED(hr)); + assert(pAllocator != NULL); + return VFW_E_NO_ALLOCATOR; + } + + ALLOCATOR_PROPERTIES props = {0}; + hr = pInputPin->GetAllocatorRequirements(&props); + + if (props.cBuffers <= 0) + props.cBuffers = 1; + + LONG w, h; + GetConnectionDimensions(w, h); + + const long cbBuffer = 2 * w * h; + + if (props.cbBuffer < cbBuffer) + props.cbBuffer = cbBuffer; + + if (props.cbAlign <= 0) + props.cbAlign = 1; + + if (props.cbPrefix < 0) + props.cbPrefix = 0; + + ALLOCATOR_PROPERTIES actual = {0}; + hr = pAllocator->SetProperties(&props, &actual); + +#if _DEBUG + wodbgstream os; + os << "vpxdec::outpin::Connect alloc props, requested first:\n" + << " cBuffers=" << props.cBuffers << " " << actual.cBuffers << "\n" + << " cbBuffer=" << props.cbBuffer << " " << actual.cbBuffer << "\n" + << " cbAlign=" << props.cbAlign << " " << actual.cbAlign << "\n" + << " cbPrefix=" << props.cbPrefix << " " << actual.cbPrefix << "\n"; +#endif + + if (FAILED(hr)) { + assert(SUCCEEDED(hr)); + return hr; + } + + hr = pInputPin->NotifyAllocator(pAllocator, 0); // allow writes + if (FAILED(hr) && hr != E_NOTIMPL) + return hr; + + m_pPinConnection = pin; + m_pAllocator = pAllocator; + m_pInputPin = pInputPin; + + return S_OK; +} + +HRESULT Outpin::OnDisconnect() { + m_pInputPin = 0; + m_pAllocator = 0; + + return S_OK; +} + +HRESULT Outpin::ReceiveConnection(IPin*, const AM_MEDIA_TYPE*) { + return E_UNEXPECTED; // for input pins only +} + +HRESULT Outpin::QueryAccept(const AM_MEDIA_TYPE* pmt_query) { + if (pmt_query == 0) + return E_INVALIDARG; + + const AM_MEDIA_TYPE& mt_query = *pmt_query; + + if (mt_query.majortype != MEDIATYPE_Video) + return S_FALSE; + + + if (mt_query.subtype != MEDIASUBTYPE_NV12 && + mt_query.subtype != MEDIASUBTYPE_YV12 && + mt_query.subtype != WebmTypes::MEDIASUBTYPE_I420 && + mt_query.subtype != MEDIASUBTYPE_UYVY && + mt_query.subtype != MEDIASUBTYPE_YVYU && + mt_query.subtype != MEDIASUBTYPE_YUY2 && + mt_query.subtype != MEDIASUBTYPE_YUYV) { + return S_FALSE; + } + + Filter::Lock lock; + const HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return S_FALSE; + + const Inpin& inpin = m_pFilter->m_inpin; + + if (!bool(inpin.m_pPinConnection)) + return S_FALSE; + + if (mt_query.formattype == FORMAT_None) + return S_OK; + + if (mt_query.formattype == GUID_NULL) + return S_OK; + + const AM_MEDIA_TYPE& mt_in = inpin.m_connection_mtv[0]; + + if (mt_query.formattype == FORMAT_VideoInfo) + return QueryAcceptVideoInfo(mt_in, mt_query); + + if (mt_query.formattype == FORMAT_VideoInfo2) + return QueryAcceptVideoInfo2(mt_in, mt_query); + + return S_FALSE; +} + +HRESULT Outpin::QueryAcceptVideoInfo(const AM_MEDIA_TYPE& mt_in, + const AM_MEDIA_TYPE& mt_out) { + assert(mt_in.formattype == FORMAT_VideoInfo); + assert(mt_in.pbFormat); + assert(mt_in.cbFormat >= sizeof(VIDEOINFOHEADER)); + assert(mt_out.formattype == FORMAT_VideoInfo); + + if (mt_out.pbFormat == 0) + return S_FALSE; + + if (mt_out.cbFormat < sizeof(VIDEOINFOHEADER)) + return S_FALSE; + + const VIDEOINFOHEADER& vih_in = + reinterpret_cast<VIDEOINFOHEADER&>(*mt_in.pbFormat); + + const VIDEOINFOHEADER& vih_out = + reinterpret_cast<VIDEOINFOHEADER&>(*mt_out.pbFormat); + + const BITMAPINFOHEADER& bmih_out = vih_out.bmiHeader; + + if (!VetBMIH(vih_in, mt_out.subtype, vih_out.rcSource, bmih_out)) + return S_FALSE; + + return S_OK; +} + +HRESULT Outpin::QueryAcceptVideoInfo2(const AM_MEDIA_TYPE& mt_in, + const AM_MEDIA_TYPE& mt_out) { + assert(mt_in.formattype == FORMAT_VideoInfo); + assert(mt_in.pbFormat); + assert(mt_in.cbFormat >= sizeof(VIDEOINFOHEADER)); + assert(mt_out.formattype == FORMAT_VideoInfo2); + + if (mt_out.pbFormat == 0) + return S_FALSE; + + if (mt_out.cbFormat < sizeof(VIDEOINFOHEADER2)) + return S_FALSE; + + const VIDEOINFOHEADER& vih_in = + reinterpret_cast<VIDEOINFOHEADER&>(*mt_in.pbFormat); + + const VIDEOINFOHEADER2& vih2_out = + reinterpret_cast<VIDEOINFOHEADER2&>(*mt_out.pbFormat); + + const BITMAPINFOHEADER& bmih_out = vih2_out.bmiHeader; + + if (vih2_out.dwInterlaceFlags & AMINTERLACE_UNUSED) + return S_FALSE; + + if (vih2_out.dwCopyProtectFlags & ~AMCOPYPROTECT_RestrictDuplication) + return S_FALSE; + + if (vih2_out.dwReserved2) + return S_FALSE; + + if (!VetBMIH(vih_in, mt_out.subtype, vih2_out.rcSource, bmih_out)) + return S_FALSE; + + return S_OK; +} + +bool Outpin::VetBMIH(const VIDEOINFOHEADER& vih_in, const GUID& subtype_out, + const RECT& rc_out, const BITMAPINFOHEADER& bmih_out) { + if (bmih_out.biSize != sizeof(BITMAPINFOHEADER)) // TODO: liberalize + return false; + + if (bmih_out.biCompression != subtype_out.Data1) + return false; + + const LONG stride_out = bmih_out.biWidth; + + if (stride_out <= 0) + return false; + + if (stride_out % 2) + return false; + + const LONG height_out = labs(bmih_out.biHeight); // yes, negative OK + + const BITMAPINFOHEADER& bmih_in = vih_in.bmiHeader; + + const LONG width_in = bmih_in.biWidth; + assert(width_in >= 0); + + const LONG height_in = bmih_in.biHeight; + assert(height_in >= 0); + + if (stride_out < width_in) + return false; + + if (height_out != height_in) + return false; + + const LONG width_out = rc_out.right - rc_out.left; + + if (width_out == 0) { + if (stride_out != width_in) + return false; + } else if (width_out != width_in) { + return false; + } + + return true; +} + +HRESULT Outpin::QueryInternalConnections(IPin** pa, ULONG* pn) { + if (pn == 0) + return E_POINTER; + + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + ULONG& n = *pn; + + if (n == 0) { + if (pa == 0) { // query for required number + n = 1; + return S_OK; + } + + return S_FALSE; // means "insufficient number of array elements" + } + + if (n < 1) { + n = 0; + return S_FALSE; // means "insufficient number of array elements" + } + + if (pa == 0) { + n = 0; + return E_POINTER; + } + + IPin*& pPin = pa[0]; + + pPin = &m_pFilter->m_inpin; + pPin->AddRef(); + + n = 1; + return S_OK; +} + +HRESULT Outpin::EndOfStream() { + return E_UNEXPECTED; // for inpins only +} + +HRESULT Outpin::NewSegment(REFERENCE_TIME, REFERENCE_TIME, double) { + return E_UNEXPECTED; +} + +HRESULT Outpin::BeginFlush() { + return E_UNEXPECTED; +} + +HRESULT Outpin::EndFlush() { + return E_UNEXPECTED; +} + +HRESULT Outpin::GetCapabilities(DWORD* pdw) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetCapabilities(pdw); + + if (pdw == 0) + return E_POINTER; + + DWORD& dw = *pdw; + dw = 0; + + return S_OK; +} + +HRESULT Outpin::CheckCapabilities(DWORD* pdw) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->CheckCapabilities(pdw); + + if (pdw == 0) + return E_POINTER; + + DWORD& dw = *pdw; + + const DWORD dwRequested = dw; + + if (dwRequested == 0) + return E_INVALIDARG; + + return E_FAIL; +} + +HRESULT Outpin::IsFormatSupported(const GUID* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->IsFormatSupported(p); + + if (p == 0) + return E_POINTER; + + const GUID& g = *p; + + if (g == TIME_FORMAT_MEDIA_TIME) + return S_OK; + + return S_FALSE; +} + +HRESULT Outpin::QueryPreferredFormat(GUID* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->QueryPreferredFormat(p); + + if (p == 0) + return E_POINTER; + + *p = TIME_FORMAT_MEDIA_TIME; + return S_OK; +} + +HRESULT Outpin::GetTimeFormat(GUID* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetTimeFormat(p); + + if (p == 0) + return E_POINTER; + + *p = TIME_FORMAT_MEDIA_TIME; + return S_OK; +} + +HRESULT Outpin::IsUsingTimeFormat(const GUID* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->IsUsingTimeFormat(p); + + if (p == 0) + return E_INVALIDARG; + + return (*p == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE; +} + +HRESULT Outpin::SetTimeFormat(const GUID* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->SetTimeFormat(p); + + if (p == 0) + return E_INVALIDARG; + + if (*p == TIME_FORMAT_MEDIA_TIME) + return S_OK; + + return E_INVALIDARG; +} + +HRESULT Outpin::GetDuration(LONGLONG* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetDuration(p); + + if (p == 0) + return E_POINTER; + + return E_FAIL; +} + +HRESULT Outpin::GetStopPosition(LONGLONG* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetStopPosition(p); + + if (p == 0) + return E_POINTER; + + return E_FAIL; +} + +HRESULT Outpin::GetCurrentPosition(LONGLONG* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetCurrentPosition(p); + + if (p == 0) + return E_POINTER; + + return E_FAIL; +} + +HRESULT Outpin::ConvertTimeFormat(LONGLONG* ptgt, const GUID* ptgtfmt, + LONGLONG src, const GUID* psrcfmt) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->ConvertTimeFormat(ptgt, ptgtfmt, src, psrcfmt); + + if (ptgt == 0) + return E_POINTER; + + LONGLONG& tgt = *ptgt; + + const GUID& tgtfmt = ptgtfmt ? *ptgtfmt : TIME_FORMAT_MEDIA_TIME; + const GUID& srcfmt = psrcfmt ? *psrcfmt : TIME_FORMAT_MEDIA_TIME; + + if (tgtfmt != TIME_FORMAT_MEDIA_TIME) + return E_INVALIDARG; + + if (srcfmt != TIME_FORMAT_MEDIA_TIME) + return E_INVALIDARG; + + tgt = src; + return S_OK; +} + +HRESULT Outpin::SetPositions(LONGLONG* pCurr, DWORD dwCurr, LONGLONG* pStop, + DWORD dwStop) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->SetPositions(pCurr, dwCurr, pStop, dwStop); + + return E_FAIL; +} + +HRESULT Outpin::GetPositions(LONGLONG* pCurrPos, LONGLONG* pStopPos) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetPositions(pCurrPos, pStopPos); + + return E_FAIL; +} + +HRESULT Outpin::GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetAvailable(pEarliest, pLatest); + + return E_FAIL; +} + +HRESULT Outpin::SetRate(double r) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->SetRate(r); + + return E_FAIL; +} + +HRESULT Outpin::GetRate(double* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetRate(p); + + return E_FAIL; +} + +HRESULT Outpin::GetPreroll(LONGLONG* p) { + const Inpin& inpin = m_pFilter->m_inpin; + const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); + + if (bool(pSeek)) + return pSeek->GetPreroll(p); + + return E_FAIL; +} + +HRESULT Outpin::GetName(PIN_INFO& info) const { + wstring name; + + if (!bool(m_pPinConnection)) { + name = L"YUV"; + } else { + const AM_MEDIA_TYPE& mt = m_connection_mtv[0]; + const char* p = (const char*)&mt.subtype.Data1; + const char* const q = p + 4; + + while (p != q) { + const char c = *p++; + name += wchar_t(c); //? + } + } + + const wchar_t* const name_ = name.c_str(); + +#if _MSC_VER >= 1400 + enum { namelen = sizeof(info.achName) / sizeof(WCHAR) }; + const errno_t e = wcscpy_s(info.achName, namelen, name_); + e; + assert(e == 0); +#else + wcscpy(info.achName, name_); +#endif + + return S_OK; +} + +void Outpin::OnInpinConnect(const AM_MEDIA_TYPE& mtIn) { + assert(mtIn.cbFormat >= sizeof(VIDEOINFOHEADER)); + assert(mtIn.pbFormat); + + const VIDEOINFOHEADER& vihIn = (VIDEOINFOHEADER&)(*mtIn.pbFormat); + const BITMAPINFOHEADER& bmihIn = vihIn.bmiHeader; + + const LONG w = bmihIn.biWidth; + assert(w > 0); + + const LONG h = bmihIn.biHeight; + assert(h > 0); + + m_preferred_mtv.Clear(); + + // planar + + DWORD dwBitCount = 12; + DWORD dwSizeImage = w * h + 2 * ((w + 1) / 2 * (h + 1) / 2); + + AddPreferred(MEDIASUBTYPE_NV12, vihIn.AvgTimePerFrame, w, h, dwBitCount, + dwSizeImage); + + AddPreferred(MEDIASUBTYPE_YV12, vihIn.AvgTimePerFrame, w, h, dwBitCount, + dwSizeImage); + + AddPreferred(WebmTypes::MEDIASUBTYPE_I420, vihIn.AvgTimePerFrame, w, h, + dwBitCount, dwSizeImage); + + // packed + + dwBitCount = 16; + dwSizeImage = 2 * w * h; + + AddPreferred(MEDIASUBTYPE_UYVY, vihIn.AvgTimePerFrame, w, h, dwBitCount, + dwSizeImage); + + AddPreferred(MEDIASUBTYPE_YUY2, vihIn.AvgTimePerFrame, w, h, dwBitCount, + dwSizeImage); + + AddPreferred(MEDIASUBTYPE_YUYV, vihIn.AvgTimePerFrame, w, h, dwBitCount, + dwSizeImage); + + AddPreferred(MEDIASUBTYPE_YVYU, vihIn.AvgTimePerFrame, w, h, dwBitCount, + dwSizeImage); +} + +HRESULT Outpin::OnInpinDisconnect() { + if (bool(m_pPinConnection)) { + IFilterGraph* const pGraph = m_pFilter->m_info.pGraph; + assert(pGraph); + + HRESULT hr = pGraph->Disconnect(m_pPinConnection); + assert(SUCCEEDED(hr)); + + hr = pGraph->Disconnect(this); + assert(SUCCEEDED(hr)); + + assert(!bool(m_pPinConnection)); + } + + SetDefaultMediaTypes(); + + return S_OK; +} + +void Outpin::SetDefaultMediaTypes() { m_preferred_mtv.Clear(); } + +void Outpin::GetConnectionDimensions(LONG& w, LONG& h) const { + assert(!m_connection_mtv.Empty()); + const AM_MEDIA_TYPE& mt = m_connection_mtv[0]; + + if (mt.formattype == FORMAT_VideoInfo) { + assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER)); + assert(mt.pbFormat); + + const VIDEOINFOHEADER& vih = (VIDEOINFOHEADER&)(*mt.pbFormat); + const BITMAPINFOHEADER& bmih = vih.bmiHeader; + + w = bmih.biWidth; + assert(w > 0); + + h = labs(bmih.biHeight); + assert(h > 0); + } else { + assert(mt.formattype == FORMAT_VideoInfo2); + assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER2)); + assert(mt.pbFormat); + + const VIDEOINFOHEADER2& vih2 = (VIDEOINFOHEADER2&)(*mt.pbFormat); + const BITMAPINFOHEADER& bmih = vih2.bmiHeader; + + w = bmih.biWidth; + assert(w > 0); + + h = labs(bmih.biHeight); + assert(h > 0); + } +} + +void Outpin::AddPreferred(const GUID& subtype, REFERENCE_TIME AvgTimePerFrame, + LONG width, LONG height, DWORD dwBitCount, + DWORD dwSizeImage) { + AddVIH2(m_preferred_mtv, subtype, AvgTimePerFrame, width, height, dwBitCount, + dwSizeImage); + + AddVIH(m_preferred_mtv, subtype, AvgTimePerFrame, width, height, dwBitCount, + dwSizeImage); +} + +void Outpin::AddVIH(CMediaTypes& mtv, const GUID& subtype, + REFERENCE_TIME AvgTimePerFrame, LONG width, LONG height, + DWORD dwBitCount, DWORD dwSizeImage) { + AM_MEDIA_TYPE mt; + + VIDEOINFOHEADER vih; + BITMAPINFOHEADER& bmih = vih.bmiHeader; + + mt.majortype = MEDIATYPE_Video; + mt.subtype = subtype; + mt.bFixedSizeSamples = TRUE; + mt.bTemporalCompression = FALSE; + mt.lSampleSize = 0; + mt.formattype = FORMAT_VideoInfo; + mt.pUnk = 0; + mt.cbFormat = sizeof vih; + mt.pbFormat = (BYTE*)&vih; + + SetRectEmpty(&vih.rcSource); + SetRectEmpty(&vih.rcTarget); + vih.dwBitRate = 0; + vih.dwBitErrorRate = 0; + vih.AvgTimePerFrame = AvgTimePerFrame; + + bmih.biSize = sizeof bmih; + bmih.biWidth = width; + bmih.biHeight = height; + bmih.biPlanes = 1; // because Microsoft says so + bmih.biBitCount = static_cast<WORD>(dwBitCount); + bmih.biCompression = subtype.Data1; + bmih.biSizeImage = dwSizeImage; + bmih.biXPelsPerMeter = 0; + bmih.biYPelsPerMeter = 0; + bmih.biClrUsed = 0; + bmih.biClrImportant = 0; + + mtv.Add(mt); +} + +void Outpin::AddVIH2(CMediaTypes& mtv, const GUID& subtype, + REFERENCE_TIME AvgTimePerFrame, LONG width, LONG height, + DWORD dwBitCount, DWORD dwSizeImage) { + AM_MEDIA_TYPE mt; + + VIDEOINFOHEADER2 vih2; + BITMAPINFOHEADER& bmih = vih2.bmiHeader; + + mt.majortype = MEDIATYPE_Video; + mt.subtype = subtype; + mt.bFixedSizeSamples = TRUE; + mt.bTemporalCompression = FALSE; + mt.lSampleSize = 0; + mt.formattype = FORMAT_VideoInfo2; + mt.pUnk = 0; + mt.cbFormat = sizeof vih2; + mt.pbFormat = (BYTE*)&vih2; + + RECT& rc = vih2.rcSource; + rc.left = 0; + rc.top = 0; + rc.right = width; + rc.bottom = height; + + vih2.rcTarget = rc; + + vih2.dwBitRate = 0; + vih2.dwBitErrorRate = 0; + vih2.AvgTimePerFrame = AvgTimePerFrame; + vih2.dwInterlaceFlags = 0; + vih2.dwCopyProtectFlags = 0; + vih2.dwPictAspectRatioX = width; + vih2.dwPictAspectRatioY = height; + vih2.dwControlFlags = 0; + vih2.dwReserved2 = 0; + + bmih.biSize = sizeof bmih; + bmih.biWidth = width; + bmih.biHeight = height; + bmih.biPlanes = 1; // because Microsoft says so + bmih.biBitCount = static_cast<WORD>(dwBitCount); + bmih.biCompression = subtype.Data1; + bmih.biSizeImage = dwSizeImage; + bmih.biXPelsPerMeter = 0; + bmih.biYPelsPerMeter = 0; + bmih.biClrUsed = 0; + bmih.biClrImportant = 0; + + mtv.Add(mt); +} + +} // namespace VPXDecoderLib
diff --git a/vpxdecoder/vpxdecoderoutpin.h b/vpxdecoder/vpxdecoderoutpin.h new file mode 100644 index 0000000..d7edc74 --- /dev/null +++ b/vpxdecoder/vpxdecoderoutpin.h
@@ -0,0 +1,112 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef WEBMDSHOW_VPXDECODER_VPXDECODEROUTPIN_H_ +#define WEBMDSHOW_VPXDECODER_VPXDECODEROUTPIN_H_ + +#include <comdef.h> + +#include "graphutil.h" +#include "vpxdecoderpin.h" + +namespace VPXDecoderLib { +class Filter; + +class Outpin : public Pin, public IMediaSeeking { + public: + explicit Outpin(Filter*); + virtual ~Outpin(); + + // IUnknown interface: + HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IPin interface: + HRESULT STDMETHODCALLTYPE Connect(IPin* pin, const AM_MEDIA_TYPE* media_type); + HRESULT STDMETHODCALLTYPE + ReceiveConnection(IPin* pin, const AM_MEDIA_TYPE* media_type); + + HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE*); + HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin**, ULONG*); + HRESULT STDMETHODCALLTYPE EndOfStream(); + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME, REFERENCE_TIME, double); + + // IMediaSeeking + HRESULT STDMETHODCALLTYPE GetCapabilities(DWORD*); + HRESULT STDMETHODCALLTYPE CheckCapabilities(DWORD*); + HRESULT STDMETHODCALLTYPE IsFormatSupported(const GUID*); + HRESULT STDMETHODCALLTYPE QueryPreferredFormat(GUID*); + HRESULT STDMETHODCALLTYPE GetTimeFormat(GUID*); + HRESULT STDMETHODCALLTYPE IsUsingTimeFormat(const GUID*); + HRESULT STDMETHODCALLTYPE SetTimeFormat(const GUID*); + HRESULT STDMETHODCALLTYPE GetDuration(LONGLONG*); + HRESULT STDMETHODCALLTYPE GetStopPosition(LONGLONG*); + HRESULT STDMETHODCALLTYPE GetCurrentPosition(LONGLONG*); + + HRESULT STDMETHODCALLTYPE + ConvertTimeFormat(LONGLONG*, const GUID*, LONGLONG, const GUID*); + + HRESULT STDMETHODCALLTYPE SetPositions(LONGLONG*, DWORD, LONGLONG*, DWORD); + + HRESULT STDMETHODCALLTYPE GetPositions(LONGLONG*, LONGLONG*); + + HRESULT STDMETHODCALLTYPE GetAvailable(LONGLONG*, LONGLONG*); + + HRESULT STDMETHODCALLTYPE SetRate(double); + HRESULT STDMETHODCALLTYPE GetRate(double*); + HRESULT STDMETHODCALLTYPE GetPreroll(LONGLONG*); + + // local functions + GraphUtil::IMemInputPinPtr m_pInputPin; + GraphUtil::IMemAllocatorPtr m_pAllocator; + + void OnInpinConnect(const AM_MEDIA_TYPE&); + HRESULT OnInpinDisconnect(); + + HRESULT Start(); // from stopped to running/paused + void Stop(); // from running/paused to stopped + + protected: + HRESULT OnDisconnect(); + HRESULT GetName(PIN_INFO&) const; + + private: + Outpin(const Outpin&); + Outpin& operator=(const Outpin&); + + void SetDefaultMediaTypes(); + + static HRESULT QueryAcceptVideoInfo(const AM_MEDIA_TYPE& mt_in, + const AM_MEDIA_TYPE& mt_out); + + static HRESULT QueryAcceptVideoInfo2(const AM_MEDIA_TYPE& mt_in, + const AM_MEDIA_TYPE& mt_out); + + static bool VetBMIH(const VIDEOINFOHEADER& vih_in, const GUID& subtype_out, + const RECT& rc_out, const BITMAPINFOHEADER& bmih_out); + + void GetConnectionDimensions(LONG& w, LONG& h) const; + + void AddPreferred(const GUID& subtype, REFERENCE_TIME AvgTimePerFrame, + LONG width, LONG height, DWORD dwBitCount, + DWORD dwSizeImage); + + static void AddVIH(CMediaTypes&, const GUID& subtype, + REFERENCE_TIME AvgTimePerFrame, LONG width, LONG height, + DWORD dwBitCount, DWORD dwSizeImage); + + static void AddVIH2(CMediaTypes&, const GUID& subtype, + REFERENCE_TIME AvgTimePerFrame, LONG width, LONG height, + DWORD dwBitCount, DWORD dwSizeImage); +}; + +} // namespace VPXDecoderLib + +#endif // WEBMDSHOW_VPXDECODER_VPXDECODEROUTPIN_H_
diff --git a/vpxdecoder/vpxdecoderpin.cc b/vpxdecoder/vpxdecoderpin.cc new file mode 100644 index 0000000..505340e --- /dev/null +++ b/vpxdecoder/vpxdecoderpin.cc
@@ -0,0 +1,167 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "vpxdecoderpin.h" + +#include <cassert> + +#include <vfwmsgs.h> + +#include "vpxdecoderfilter.h" + +#ifdef _DEBUG +#include "odbgstream.h" +using std::endl; +#endif + +namespace VPXDecoderLib { + +Pin::Pin(Filter* pFilter, PIN_DIRECTION dir, const wchar_t* id) + : m_pFilter(pFilter), m_dir(dir), m_id(id) {} + +Pin::~Pin() { assert(!bool(m_pPinConnection)); } + +HRESULT Pin::EnumMediaTypes(IEnumMediaTypes** pp) { + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + +#if _DEBUG + wodbgstream os; + os << "vpxdec::pin[" << m_id << "]::EnumMediaTypes" << endl; +#endif + + return m_preferred_mtv.CreateEnum(this, pp); +} + +HRESULT Pin::Disconnect() { + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (m_pFilter->GetStateLocked() != State_Stopped) + return VFW_E_NOT_STOPPED; + + if (!bool(m_pPinConnection)) + return S_FALSE; + + hr = OnDisconnect(); + assert(SUCCEEDED(hr)); + + m_pPinConnection = 0; + m_connection_mtv.Clear(); + + return S_OK; +} + +HRESULT Pin::OnDisconnect() { + return S_OK; +} + +HRESULT Pin::ConnectedTo(IPin** pp) { + if (pp == 0) + return E_POINTER; + + IPin*& p = *pp; + + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + p = m_pPinConnection; + + if (p == 0) + return VFW_E_NOT_CONNECTED; + + p->AddRef(); + return S_OK; +} + +HRESULT Pin::ConnectionMediaType(AM_MEDIA_TYPE* p) { + if (p == 0) + return E_POINTER; + + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + if (!bool(m_pPinConnection)) + return VFW_E_NOT_CONNECTED; + + const CMediaTypes& mtv = m_connection_mtv; + assert(mtv.Size() == 1); + + return mtv.Copy(0, *p); +} + +HRESULT Pin::QueryPinInfo(PIN_INFO* p) { + if (p == 0) + return E_POINTER; + + Filter::Lock lock; + + HRESULT hr = lock.Seize(m_pFilter); + + if (FAILED(hr)) + return hr; + + PIN_INFO& i = *p; + + i.pFilter = static_cast<IBaseFilter*>(m_pFilter); + i.pFilter->AddRef(); + + i.dir = m_dir; + + hr = GetName(i); + assert(SUCCEEDED(hr)); + + return S_OK; +} + +HRESULT Pin::QueryDirection(PIN_DIRECTION* p) { + if (p == 0) + return E_POINTER; + + *p = m_dir; + return S_OK; +} + +HRESULT Pin::QueryId(LPWSTR* p) { + if (p == 0) + return E_POINTER; + + wchar_t*& id = *p; + + const size_t len = m_id.length(); // wchar strlen + const size_t buflen = len + 1; // wchar strlen + wchar null + const size_t cb = buflen * sizeof(wchar_t); // total bytes + + id = (wchar_t*)CoTaskMemAlloc(cb); + + if (id == 0) + return E_OUTOFMEMORY; + + const errno_t e = wcscpy_s(id, buflen, m_id.c_str()); + e; + assert(e == 0); + + return S_OK; +} + +} // namespace VPXDecoderLib
diff --git a/vpxdecoder/vpxdecoderpin.h b/vpxdecoder/vpxdecoderpin.h new file mode 100644 index 0000000..c9b0b55 --- /dev/null +++ b/vpxdecoder/vpxdecoderpin.h
@@ -0,0 +1,53 @@ +// Copyright (c) 2014 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef WEBMDSHOW_VPXDECODER_VPXDECODERPIN_H_ +#define WEBMDSHOW_VPXDECODER_VPXDECODERPIN_H_ + +#include <amvideo.h> +#include <strmif.h> + +#include <string> + +#include "cmediatypes.h" +#include "graphutil.h" + +namespace VPXDecoderLib { +class Filter; + +class Pin : public IPin { + public: + Filter* const m_pFilter; + const PIN_DIRECTION m_dir; + const std::wstring m_id; + CMediaTypes m_preferred_mtv; + CMediaTypes m_connection_mtv; // only one of these + GraphUtil::IPinPtr m_pPinConnection; + + // IPin interface: + HRESULT STDMETHODCALLTYPE Disconnect(); + HRESULT STDMETHODCALLTYPE ConnectedTo(IPin** pPin); + HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE*); + HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO*); + HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION*); + HRESULT STDMETHODCALLTYPE QueryId(LPWSTR*); + HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes**); + + protected: + Pin(Filter*, PIN_DIRECTION, const wchar_t*); + virtual ~Pin(); + + virtual HRESULT GetName(PIN_INFO&) const = 0; + virtual HRESULT OnDisconnect(); + + private: + Pin& operator=(const Pin&); +}; + +} // namespace VPXDecoderLib + +#endif // WEBMDSHOW_VPXDECODER_VPXDECODERPIN_H_
diff --git a/webmdshow.sln b/webmdshow.sln index 0628ed6..e2d8528 100644 --- a/webmdshow.sln +++ b/webmdshow.sln
@@ -35,6 +35,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vp9decoder", "vp9decoder\vp9decoder.vcxproj", "{BC8E93B9-9062-4BC4-A351-3408604B8409}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vpxdecoder", "vpxdecoder\vpxdecoder.vcxproj", "{C4A3A16F-C46B-41BA-A031-94391A535C00}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -185,6 +187,16 @@ {BC8E93B9-9062-4BC4-A351-3408604B8409}.Release|Mixed Platforms.Build.0 = Release|Win32 {BC8E93B9-9062-4BC4-A351-3408604B8409}.Release|Win32.ActiveCfg = Release|Win32 {BC8E93B9-9062-4BC4-A351-3408604B8409}.Release|Win32.Build.0 = Release|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Debug|Win32.ActiveCfg = Debug|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Debug|Win32.Build.0 = Debug|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Release|Any CPU.ActiveCfg = Release|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Release|Mixed Platforms.Build.0 = Release|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Release|Win32.ActiveCfg = Release|Win32 + {C4A3A16F-C46B-41BA-A031-94391A535C00}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE