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