Merge "Add VPx Decoder filter."
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