webmdshow clean up: Fix EoLs (CRLF -> LF) in MF VP8 decoder. Change-Id: Ifaebca2db98df7b5aa75cf85542696faa892dd6e
diff --git a/mediafoundation/webmmfvp8dec/dllentry.cc b/mediafoundation/webmmfvp8dec/dllentry.cc index d5cf8b6..d78fd58 100644 --- a/mediafoundation/webmmfvp8dec/dllentry.cc +++ b/mediafoundation/webmmfvp8dec/dllentry.cc
@@ -1,150 +1,150 @@ -// Copyright (c) 2010 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 "cfactory.h" -#include "comreg.h" -#include "webmtypes.h" -#include <mfapi.h> -#include <cassert> -#include <comdef.h> -#include <uuids.h> - -HMODULE g_hModule; -static ULONG s_cLock; - -namespace WebmMfVp8DecLib -{ - - HRESULT CreateDecoder( - IClassFactory*, - IUnknown*, - const IID&, - void**); - -} //end namespace WebmMfVp8DecLib - -static CFactory s_handler_factory(&s_cLock, &WebmMfVp8DecLib::CreateDecoder); - - -BOOL APIENTRY DllMain( - HINSTANCE hModule, - DWORD dwReason, - LPVOID) -{ - 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 == WebmTypes::CLSID_WebmMfVp8Dec) - return s_handler_factory.QueryInterface(iid, ppv); - - return CLASS_E_CLASSNOTAVAILABLE; -} - - -STDAPI DllUnregisterServer() -{ - HRESULT hr = MFTUnregister(WebmTypes::CLSID_WebmMfVp8Dec); - //assert(SUCCEEDED(hr)); //TODO: dump this it fails - - hr = ComReg::UnRegisterCoclass(WebmTypes::CLSID_WebmMfVp8Dec); - - return hr; -} - - -STDAPI DllRegisterServer() -{ - HRESULT hr = DllUnregisterServer(); - assert(SUCCEEDED(hr)); - - std::wstring filename_; - - hr = ComReg::ComRegGetModuleFileName(g_hModule, filename_); - assert(SUCCEEDED(hr)); - assert(!filename_.empty()); - - const wchar_t* const filename = filename_.c_str(); - -#if _DEBUG - const wchar_t friendly_name[] = L"WebM MF VP8 Decoder Transform (Debug)"; -#else - const wchar_t friendly_name[] = L"WebM MF VP8 Decoder Transform"; -#endif - - hr = ComReg::RegisterCoclass( - WebmTypes::CLSID_WebmMfVp8Dec, - friendly_name, - filename, - L"Webm.MfVp8Dec", //TODO: do we really need ProgIDs? - L"Webm.MfVp8Dec.1", - false, //not insertable - false, //not a control - ComReg::kBoth, //DShow filters must support "both" - GUID_NULL, //typelib - 0, //no version specified - 0); //no toolbox bitmap - - if (FAILED(hr)) - return hr; - - enum { cInputTypes = 1 }; - MFT_REGISTER_TYPE_INFO pInputTypes[cInputTypes] = - { - { MFMediaType_Video, WebmTypes::MEDIASUBTYPE_VP80 } - }; - - enum { cOutputTypes = 3 }; - MFT_REGISTER_TYPE_INFO pOutputTypes[cOutputTypes] = - { - { MFMediaType_Video, MFVideoFormat_NV12 }, - { MFMediaType_Video, MFVideoFormat_YV12 }, - { MFMediaType_Video, MFVideoFormat_IYUV } - }; - - wchar_t* const friendly_name_ = const_cast<wchar_t*>(friendly_name); - - hr = MFTRegister( - WebmTypes::CLSID_WebmMfVp8Dec, - MFT_CATEGORY_VIDEO_DECODER, - friendly_name_, - MFT_ENUM_FLAG_SYNCMFT, //TODO: for now, just support sync - cInputTypes, - pInputTypes, - cOutputTypes, - pOutputTypes, - 0); //no attributes - - return hr; -} +// Copyright (c) 2010 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 "cfactory.h" +#include "comreg.h" +#include "webmtypes.h" +#include <mfapi.h> +#include <cassert> +#include <comdef.h> +#include <uuids.h> + +HMODULE g_hModule; +static ULONG s_cLock; + +namespace WebmMfVp8DecLib +{ + + HRESULT CreateDecoder( + IClassFactory*, + IUnknown*, + const IID&, + void**); + +} //end namespace WebmMfVp8DecLib + +static CFactory s_handler_factory(&s_cLock, &WebmMfVp8DecLib::CreateDecoder); + + +BOOL APIENTRY DllMain( + HINSTANCE hModule, + DWORD dwReason, + LPVOID) +{ + 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 == WebmTypes::CLSID_WebmMfVp8Dec) + return s_handler_factory.QueryInterface(iid, ppv); + + return CLASS_E_CLASSNOTAVAILABLE; +} + + +STDAPI DllUnregisterServer() +{ + HRESULT hr = MFTUnregister(WebmTypes::CLSID_WebmMfVp8Dec); + //assert(SUCCEEDED(hr)); //TODO: dump this it fails + + hr = ComReg::UnRegisterCoclass(WebmTypes::CLSID_WebmMfVp8Dec); + + return hr; +} + + +STDAPI DllRegisterServer() +{ + HRESULT hr = DllUnregisterServer(); + assert(SUCCEEDED(hr)); + + std::wstring filename_; + + hr = ComReg::ComRegGetModuleFileName(g_hModule, filename_); + assert(SUCCEEDED(hr)); + assert(!filename_.empty()); + + const wchar_t* const filename = filename_.c_str(); + +#if _DEBUG + const wchar_t friendly_name[] = L"WebM MF VP8 Decoder Transform (Debug)"; +#else + const wchar_t friendly_name[] = L"WebM MF VP8 Decoder Transform"; +#endif + + hr = ComReg::RegisterCoclass( + WebmTypes::CLSID_WebmMfVp8Dec, + friendly_name, + filename, + L"Webm.MfVp8Dec", //TODO: do we really need ProgIDs? + L"Webm.MfVp8Dec.1", + false, //not insertable + false, //not a control + ComReg::kBoth, //DShow filters must support "both" + GUID_NULL, //typelib + 0, //no version specified + 0); //no toolbox bitmap + + if (FAILED(hr)) + return hr; + + enum { cInputTypes = 1 }; + MFT_REGISTER_TYPE_INFO pInputTypes[cInputTypes] = + { + { MFMediaType_Video, WebmTypes::MEDIASUBTYPE_VP80 } + }; + + enum { cOutputTypes = 3 }; + MFT_REGISTER_TYPE_INFO pOutputTypes[cOutputTypes] = + { + { MFMediaType_Video, MFVideoFormat_NV12 }, + { MFMediaType_Video, MFVideoFormat_YV12 }, + { MFMediaType_Video, MFVideoFormat_IYUV } + }; + + wchar_t* const friendly_name_ = const_cast<wchar_t*>(friendly_name); + + hr = MFTRegister( + WebmTypes::CLSID_WebmMfVp8Dec, + MFT_CATEGORY_VIDEO_DECODER, + friendly_name_, + MFT_ENUM_FLAG_SYNCMFT, //TODO: for now, just support sync + cInputTypes, + pInputTypes, + cOutputTypes, + pOutputTypes, + 0); //no attributes + + return hr; +}
diff --git a/mediafoundation/webmmfvp8dec/resource.h b/mediafoundation/webmmfvp8dec/resource.h index aeb74fa..5b870fd 100644 --- a/mediafoundation/webmmfvp8dec/resource.h +++ b/mediafoundation/webmmfvp8dec/resource.h
@@ -1,15 +1,15 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by webmmfvp8dec.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 +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by webmmfvp8dec.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/mediafoundation/webmmfvp8dec/webmmfvp8dec.cc b/mediafoundation/webmmfvp8dec/webmmfvp8dec.cc index 9db1253..a06ebb4 100644 --- a/mediafoundation/webmmfvp8dec/webmmfvp8dec.cc +++ b/mediafoundation/webmmfvp8dec/webmmfvp8dec.cc
@@ -1,2397 +1,2397 @@ -#pragma warning(disable:4505) //unreferenced local function removed -#include "clockable.h" -#include <mfidl.h> -#include "vpx/vpx_decoder.h" -#include "vpx/vp8dx.h" -#include "webmmfvp8dec.h" -#include "webmtypes.h" -#include <mfapi.h> -#include <mferror.h> -#include <comdef.h> -#include <cassert> -#include <new> -#ifdef _DEBUG -#include "odbgstream.h" -#include "iidstr.h" -using std::endl; -using std::boolalpha; -#endif - -_COM_SMARTPTR_TYPEDEF(IMFMediaBuffer, __uuidof(IMFMediaBuffer)); -_COM_SMARTPTR_TYPEDEF(IMF2DBuffer, __uuidof(IMF2DBuffer)); - - -namespace WebmMfVp8DecLib -{ - - -HRESULT CreateDecoder( - IClassFactory* pClassFactory, - IUnknown* pOuter, - const IID& iid, - void** ppv) -{ - if (ppv == 0) - return E_POINTER; - - *ppv = 0; - - if (pOuter) - return CLASS_E_NOAGGREGATION; - - WebmMfVp8Dec* const p = new (std::nothrow) WebmMfVp8Dec(pClassFactory); - - if (p == 0) - return E_OUTOFMEMORY; - - IMFTransform* const pUnk = p; - - const HRESULT hr = pUnk->QueryInterface(iid, ppv); - - const ULONG cRef = pUnk->Release(); - cRef; - - return hr; -} - - -WebmMfVp8Dec::WebmMfVp8Dec(IClassFactory* pClassFactory) : - m_pClassFactory(pClassFactory), - m_cRef(1), - m_pInputMediaType(0), - m_pOutputMediaType(0), - m_rate(1), - m_bThin(FALSE), - m_drop_mode(MF_DROP_MODE_NONE), - m_drop_budget(0), - m_lag_sum(0), - m_lag_count(0), - m_lag_frames(0), - m_frame_rate(-1) -{ - HRESULT hr = m_pClassFactory->LockServer(TRUE); - assert(SUCCEEDED(hr)); - - hr = CLockable::Init(); - assert(SUCCEEDED(hr)); - - //m_frame_rate.Init(); -} - - -WebmMfVp8Dec::~WebmMfVp8Dec() -{ - if (m_pInputMediaType) - { - const ULONG n = m_pInputMediaType->Release(); - n; - assert(n == 0); - - m_pInputMediaType = 0; - - const vpx_codec_err_t e = vpx_codec_destroy(&m_ctx); - e; - assert(e == VPX_CODEC_OK); - } - - if (m_pOutputMediaType) - { - const ULONG n = m_pOutputMediaType->Release(); - n; - assert(n == 0); - - m_pOutputMediaType = 0; - } - - Flush(); - - HRESULT hr = m_pClassFactory->LockServer(FALSE); - assert(SUCCEEDED(hr)); -} - - -HRESULT WebmMfVp8Dec::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<IMFTransform*>(this); //must be nondelegating - } - else if (iid == __uuidof(IMFTransform)) - { - pUnk = static_cast<IMFTransform*>(this); - } - else if (iid == __uuidof(IMFQualityAdvise2)) - { - pUnk = static_cast<IMFQualityAdvise2*>(this); - } - else if (iid == __uuidof(IMFQualityAdvise)) - { - pUnk = static_cast<IMFQualityAdvise*>(this); - } - else if (iid == __uuidof(IMFRateControl)) - { - pUnk = static_cast<IMFRateControl*>(this); - } - else if (iid == __uuidof(IMFRateSupport)) - { - pUnk = static_cast<IMFRateSupport*>(this); - } - else if (iid == __uuidof(IMFGetService)) - { - pUnk = static_cast<IMFGetService*>(this); - } - else - { -//#ifdef _DEBUG -// wodbgstream os; -// os << "WebmMfVp8Dec::QI: iid=" << IIDStr(iid) << std::endl; -//#endif - - pUnk = 0; - return E_NOINTERFACE; - } - - pUnk->AddRef(); - return S_OK; -} - - -ULONG WebmMfVp8Dec::AddRef() -{ - return InterlockedIncrement(&m_cRef); -} - - -ULONG WebmMfVp8Dec::Release() -{ - if (LONG n = InterlockedDecrement(&m_cRef)) - return n; - - delete this; - return 0; -} - - -HRESULT WebmMfVp8Dec::GetStreamLimits( - DWORD* pdwInputMin, - DWORD* pdwInputMax, - DWORD* pdwOutputMin, - DWORD* pdwOutputMax) -{ - if (pdwInputMin == 0) - return E_POINTER; - - if (pdwInputMax == 0) - return E_POINTER; - - if (pdwOutputMin == 0) - return E_POINTER; - - if (pdwOutputMax == 0) - return E_POINTER; - - DWORD& dwInputMin = *pdwInputMin; - dwInputMin = 1; - - DWORD& dwInputMax = *pdwInputMax; - dwInputMax = 1; - - DWORD& dwOutputMin = *pdwOutputMin; - dwOutputMin = 1; - - DWORD& dwOutputMax = *pdwOutputMax; - dwOutputMax = 1; - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetStreamCount( - DWORD* pcInputStreams, - DWORD* pcOutputStreams) -{ - if (pcInputStreams == 0) - return E_POINTER; - - if (pcOutputStreams == 0) - return E_POINTER; - - DWORD& cInputStreams = *pcInputStreams; - cInputStreams = 1; - - DWORD& cOutputStreams = *pcOutputStreams; - cOutputStreams = 1; - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetStreamIDs( - DWORD, - DWORD* pdwInputIDs, - DWORD, - DWORD* pdwOutputIDs) -{ - if (pdwInputIDs) - *pdwInputIDs = 0; - - if (pdwOutputIDs) - *pdwOutputIDs = 0; - - return E_NOTIMPL; -} - - -HRESULT WebmMfVp8Dec::GetInputStreamInfo( - DWORD dwInputStreamID, - MFT_INPUT_STREAM_INFO* pStreamInfo) -{ - if (pStreamInfo == 0) - return E_POINTER; - - if (dwInputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - MFT_INPUT_STREAM_INFO& info = *pStreamInfo; - - //LONGLONG hnsMaxLatency; - //DWORD dwFlags; - //DWORD cbSize; - //DWORD cbMaxLookahead; - //DWORD cbAlignment; - - info.cbMaxLookahead = 0; - info.hnsMaxLatency = 0; //TODO: Is 0 correct? - //TODO: does lag-in-frames matter here? - //See "_MFT_INPUT_STREAM_INFO_FLAGS Enumeration" for more info: - //http://msdn.microsoft.com/en-us/library/ms703975%28v=VS.85%29.aspx - - //enum _MFT_INPUT_STREAM_INFO_FLAGS - // { MFT_INPUT_STREAM_WHOLE_SAMPLES = 0x1, - // MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER = 0x2, - // MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE = 0x4, - // MFT_INPUT_STREAM_HOLDS_BUFFERS = 0x8, - // MFT_INPUT_STREAM_DOES_NOT_ADDREF = 0x100, - // MFT_INPUT_STREAM_REMOVABLE= 0x200, - // MFT_INPUT_STREAM_OPTIONAL = 0x400, - // MFT_INPUT_STREAM_PROCESSES_IN_PLACE = 0x800 - // }; - - info.dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES; - //MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; - //MFT_INPUT_STREAM_DOES_NOT_ADDREF; - - info.cbSize = 0; //input size is variable - info.cbAlignment = 0; //no specific alignment requirements - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetOutputStreamInfo( - DWORD dwOutputStreamID, - MFT_OUTPUT_STREAM_INFO* pStreamInfo) -{ - if (pStreamInfo == 0) - return E_POINTER; - - if (dwOutputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - Lock lock; - - const HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - MFT_OUTPUT_STREAM_INFO& info = *pStreamInfo; - - //enum _MFT_OUTPUT_STREAM_INFO_FLAGS - // { MFT_OUTPUT_STREAM_WHOLE_SAMPLES = 0x1, - // MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER = 0x2, - // MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE = 0x4, - // MFT_OUTPUT_STREAM_DISCARDABLE = 0x8, - // MFT_OUTPUT_STREAM_OPTIONAL = 0x10, - // MFT_OUTPUT_STREAM_PROVIDES_SAMPLES = 0x100, - // MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES = 0x200, - // MFT_OUTPUT_STREAM_LAZY_READ = 0x400, - // MFT_OUTPUT_STREAM_REMOVABLE = 0x800 - // }; - - //see Decoder sample in the SDK - //decoder.cc - - //The API says that the only flag that is meaningful prior to SetOutputType - //is the OPTIONAL flag. We need the frame dimensions, and the stride, - //in order to calculte the cbSize value. - - info.dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | - MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | - MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; - - FrameSize size; - - info.cbSize = GetOutputBufferSize(size); - info.cbAlignment = 0; - - return S_OK; -} - - -DWORD WebmMfVp8Dec::GetOutputBufferSize(FrameSize& s) const -{ - //MFT was already locked by caller - - //TODO: for now, assume width and height are specified - //via the input media type. - - if (m_pInputMediaType == 0) - return 0; - - HRESULT hr = MFGetAttributeSize( - m_pInputMediaType, - MF_MT_FRAME_SIZE, - &s.width, - &s.height); - - assert(SUCCEEDED(hr)); - - const DWORD w = s.width; - assert(w); - - const DWORD h = s.height; - assert(h); - - const DWORD cb = w*h + 2*(((w+1)/2)*((h+1)/2)); - - //TODO: this result does not account for stride - - return cb; -} - - -HRESULT WebmMfVp8Dec::GetAttributes(IMFAttributes** pp) -{ - if (pp) - *pp = 0; - - return E_NOTIMPL; -} - - -HRESULT WebmMfVp8Dec::GetInputStreamAttributes( - DWORD, - IMFAttributes** pp) -{ - if (pp) - *pp = 0; - - return E_NOTIMPL; -} - - -HRESULT WebmMfVp8Dec::GetOutputStreamAttributes( - DWORD, - IMFAttributes** pp) -{ - if (pp) - *pp = 0; - - return E_NOTIMPL; -} - - -HRESULT WebmMfVp8Dec::DeleteInputStream(DWORD) -{ - return E_NOTIMPL; -} - - -HRESULT WebmMfVp8Dec::AddInputStreams( - DWORD, - DWORD*) -{ - return E_NOTIMPL; -} - - -HRESULT WebmMfVp8Dec::GetInputAvailableType( - DWORD dwInputStreamID, - DWORD dwTypeIndex, - IMFMediaType** pp) -{ - if (pp) - *pp = 0; - - if (dwInputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (dwTypeIndex > 0) - return MF_E_NO_MORE_TYPES; - - if (pp == 0) - return E_POINTER; - - IMFMediaType*& pmt = *pp; - - HRESULT hr = MFCreateMediaType(&pmt); - assert(SUCCEEDED(hr)); - assert(pmt); - - hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); - assert(SUCCEEDED(hr)); - - hr = pmt->SetGUID(MF_MT_SUBTYPE, WebmTypes::MEDIASUBTYPE_VP80); - assert(SUCCEEDED(hr)); - - hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, FALSE); - assert(SUCCEEDED(hr)); - - hr = pmt->SetUINT32(MF_MT_COMPRESSED, TRUE); - assert(SUCCEEDED(hr)); - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetOutputAvailableType( - DWORD dwOutputStreamID, - DWORD dwTypeIndex, - IMFMediaType** pp) -{ - if (pp) - *pp = 0; - - if (dwOutputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - enum { subtype_count = 3 }; - - if (dwTypeIndex >= subtype_count) - return MF_E_NO_MORE_TYPES; - - if (pp == 0) - return E_POINTER; - - IMFMediaType*& pmt = *pp; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - hr = MFCreateMediaType(&pmt); - assert(SUCCEEDED(hr)); - assert(pmt); - - hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); - assert(SUCCEEDED(hr)); - - const GUID subtypes[subtype_count] = - { - MFVideoFormat_NV12, - MFVideoFormat_YV12, - MFVideoFormat_IYUV - }; - - const GUID& subtype = subtypes[dwTypeIndex]; - - hr = pmt->SetGUID(MF_MT_SUBTYPE, subtype); - assert(SUCCEEDED(hr)); - - hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); - assert(SUCCEEDED(hr)); - - hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); - assert(SUCCEEDED(hr)); - - if (m_pInputMediaType == 0) //nothing else we can say - return S_OK; - - FrameRate r; - - hr = r.AssignFrom(m_pInputMediaType); - - if (hr == S_OK) //means attribute was set, and its value is valid - { - //odbgstream os; - //os << "\nvp8dec: num=" << r.m_numerator - // << " den=" << r.m_denominator; - // - //const float rate = float(r.m_numerator) / float(r.m_denominator); - //os << rate << '\n' << endl; - - //TODO: a simpler way to handle this is to copy - //an item directly from media type to media type (assuming - //the MF API provides such a function), instead of going - //through this intermediate FrameRate value. - - hr = r.CopyTo(pmt); - assert(SUCCEEDED(hr)); - } - - FrameSize s; - - hr = MFGetAttributeSize( - m_pInputMediaType, - MF_MT_FRAME_SIZE, - &s.width, - &s.height); - - assert(SUCCEEDED(hr)); - assert(s.width); - assert(s.height); - - hr = MFSetAttributeSize( - pmt, - MF_MT_FRAME_SIZE, - s.width, - s.height); - - assert(SUCCEEDED(hr)); - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::SetInputType( - DWORD dwInputStreamID, - IMFMediaType* pmt, - DWORD dwFlags) -{ - if (dwInputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (pmt == 0) - { - //TODO: disallow this case while we're playing? - - if (m_pInputMediaType) - { - const ULONG n = m_pInputMediaType->Release(); - n; - assert(n == 0); - - m_pInputMediaType = 0; - - const vpx_codec_err_t e = vpx_codec_destroy(&m_ctx); - e; - assert(e == VPX_CODEC_OK); - } - - if (m_pOutputMediaType) - { - const ULONG n = m_pOutputMediaType->Release(); - n; - assert(n == 0); - - m_pOutputMediaType = 0; - } - - return S_OK; - } - - //TODO: handle the case when already have an input media type - //or output media type, or are already playing. I don't - //think we can change media types while we're playing. - - GUID g; - - hr = pmt->GetMajorType(&g); - - if (FAILED(hr)) - return MF_E_INVALIDMEDIATYPE; - - if (g != MFMediaType_Video) - return MF_E_INVALIDMEDIATYPE; - - hr = pmt->GetGUID(MF_MT_SUBTYPE, &g); - - if (FAILED(hr)) - return MF_E_INVALIDMEDIATYPE; - - if (g != WebmTypes::MEDIASUBTYPE_VP80) - return MF_E_INVALIDMEDIATYPE; - - //hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); - //assert(SUCCEEDED(hr)); - - FrameRate r; - - hr = r.AssignFrom(pmt); - - if (FAILED(hr)) //input mt has framerate, but it's bad - return hr; - - r.CopyTo(m_frame_rate); - - //odbgstream os; - //os << "\nvp8dec: framerate=" << m_frame_rate << '\n' << endl; - - FrameSize s; - - hr = MFGetAttributeSize( - pmt, - MF_MT_FRAME_SIZE, - &s.width, - &s.height); - - if (FAILED(hr)) - return MF_E_INVALIDMEDIATYPE; - - if (s.width == 0) - return MF_E_INVALIDMEDIATYPE; - - if (s.width % 2) //TODO - return MF_E_INVALIDMEDIATYPE; - - if (s.height == 0) - return MF_E_INVALIDMEDIATYPE; - - //TODO: do we need to check for odd height too? - - if (dwFlags & MFT_SET_TYPE_TEST_ONLY) - return S_OK; - - if (m_pInputMediaType) - { - hr = m_pInputMediaType->DeleteAllItems(); - assert(SUCCEEDED(hr)); - - const vpx_codec_err_t e = vpx_codec_destroy(&m_ctx); - e; - assert(e == VPX_CODEC_OK); - } - else - { - hr = MFCreateMediaType(&m_pInputMediaType); - - if (FAILED(hr)) - return hr; - } - - hr = pmt->CopyAllItems(m_pInputMediaType); - - if (FAILED(hr)) - return hr; - - //m_frame_rate = r; - - //TODO: should this really be done here? - - vpx_codec_iface_t& vp8 = vpx_codec_vp8_dx_algo; - - const int flags = 0; //TODO: VPX_CODEC_USE_POSTPROC; - - const vpx_codec_err_t err = vpx_codec_dec_init( - &m_ctx, - &vp8, - 0, - flags); - - if (err == VPX_CODEC_MEM_ERROR) - return E_OUTOFMEMORY; - - if (err != VPX_CODEC_OK) - return E_FAIL; - - //const HRESULT hr = OnApplyPostProcessing(); - - if (m_pOutputMediaType) - { - //TODO: Is this the correct behavior? - - const ULONG n = m_pOutputMediaType->Release(); - n; - assert(n == 0); - - m_pOutputMediaType = 0; - } - -#if 0 - //TODO: - //We could update the preferred ("available") output media types, - //now that we know the frame rate and frame size, etc. - - hr = MFCreateMediaType(&m_pOutputMediaType); - assert(SUCCEEDED(hr)); //TODO - - hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); - assert(SUCCEEDED(hr)); //TODO - - hr = pmt->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12); - assert(SUCCEEDED(hr)); //TODO - - hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); - assert(SUCCEEDED(hr)); //TODO - - hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); - assert(SUCCEEDED(hr)); //TODO - - if (r.numerator) //means "has been set" - { - hr = MFSetAttributeRatio( - m_pOutputMediaType, - MF_MT_FRAME_RATE, - r.numerator, - r.denominator); - - assert(SUCCEEDED(hr)); //TODO - } - - hr = MFSetAttributeSize( - m_pOutputMediaType, - MF_MT_FRAME_SIZE, - s.width, - s.height); - - assert(SUCCEEDED(hr)); //TODO -#endif - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::SetOutputType( - DWORD dwOutputStreamID, - IMFMediaType* pmt, - DWORD dwFlags) -{ - if (dwOutputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (pmt == 0) - { - //TODO: disallow this case while we're playing? - - if (m_pOutputMediaType) - { - const ULONG n = m_pOutputMediaType->Release(); - n; - assert(n == 0); - - m_pOutputMediaType = 0; - } - - return S_OK; - } - - if (m_pInputMediaType == 0) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - GUID g; - - hr = pmt->GetMajorType(&g); - - if (FAILED(hr)) - return MF_E_INVALIDMEDIATYPE; - - if (g != MFMediaType_Video) - return MF_E_INVALIDMEDIATYPE; - - hr = pmt->GetGUID(MF_MT_SUBTYPE, &g); - - if (FAILED(hr)) - return MF_E_INVALIDMEDIATYPE; - - if (g == MFVideoFormat_NV12) - __noop; - else if (g == MFVideoFormat_YV12) - __noop; - else if (g == MFVideoFormat_IYUV) - __noop; - else //TODO: add I420 support - return MF_E_INVALIDMEDIATYPE; - - //hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); - //assert(SUCCEEDED(hr)); - - //hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); - //assert(SUCCEEDED(hr)); - - FrameRate r_out; - - hr = r_out.AssignFrom(pmt); - - if (FAILED(hr)) //output type has value, but it's not valid - return hr; - - if (hr == S_OK) //output type has value, and it's valid - { - FrameRate r_in; - - hr = r_in.AssignFrom(m_pInputMediaType); - - if ((hr == S_OK) && !r_in.Match(r_out)) - return MF_E_INVALIDMEDIATYPE; - } - - FrameSize s_out; - - hr = MFGetAttributeSize( - pmt, - MF_MT_FRAME_SIZE, - &s_out.width, - &s_out.height); - - if (SUCCEEDED(hr)) - { - if (s_out.width == 0) - return MF_E_INVALIDMEDIATYPE; - - //TODO: check whether width is odd - - if (s_out.height == 0) - return MF_E_INVALIDMEDIATYPE; - - FrameSize s_in; - - hr = MFGetAttributeSize( - m_pInputMediaType, - MF_MT_FRAME_SIZE, - &s_in.width, - &s_in.height); - - assert(SUCCEEDED(hr)); - - if (s_out.width != s_in.width) - return MF_E_INVALIDMEDIATYPE; - - if (s_out.height != s_in.height) - return MF_E_INVALIDMEDIATYPE; - } - - if (dwFlags & MFT_SET_TYPE_TEST_ONLY) - return S_OK; - - if (m_pOutputMediaType) - { - hr = m_pOutputMediaType->DeleteAllItems(); - assert(SUCCEEDED(hr)); - } - else - { - hr = MFCreateMediaType(&m_pOutputMediaType); - - if (FAILED(hr)) - return hr; - } - - hr = pmt->CopyAllItems(m_pOutputMediaType); - - if (FAILED(hr)) - return hr; - - //TODO: do something - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetInputCurrentType( - DWORD dwInputStreamID, - IMFMediaType** pp) -{ - if (dwInputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (pp) - *pp = 0; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (m_pInputMediaType == 0) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - IMFMediaType*& p = *pp; - - hr = MFCreateMediaType(&p); - - if (FAILED(hr)) - return hr; - - return m_pInputMediaType->CopyAllItems(p); -} - - -HRESULT WebmMfVp8Dec::GetOutputCurrentType( - DWORD dwOutputStreamID, - IMFMediaType** pp) -{ - if (dwOutputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (pp) - *pp = 0; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - //TODO: synthesize from input media type? - - if (m_pOutputMediaType == 0) //TODO: liberalize? - return MF_E_TRANSFORM_TYPE_NOT_SET; - - IMFMediaType*& p = *pp; - - hr = MFCreateMediaType(&p); - - if (FAILED(hr)) - return hr; - - return m_pOutputMediaType->CopyAllItems(p); -} - - -HRESULT WebmMfVp8Dec::GetInputStatus( - DWORD dwInputStreamID, - DWORD* pdwFlags) -{ - if (dwInputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (m_pInputMediaType == 0) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - //TODO: check output media type too? - - if (pdwFlags == 0) - return E_POINTER; - - DWORD& dwFlags = *pdwFlags; - - dwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; //because we always queue - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetOutputStatus(DWORD*) -{ -#if 1 - return E_NOTIMPL; -#else - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (m_pInputMediaType == 0) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - //TODO: check output media type too? - - if (pdwFlags == 0) - return E_POINTER; - - DWORD& dwFlags = *pdwFlags; - - const vpx_image_t* const f = vpx_codec_get_frame(&m_ctx, &m_iter); - - dwFlags = f ? MFT_OUTPUT_STATUS_SAMPLE_READY : 0; - - //TODO: alternatively, we could return E_NOTIMPL, which - //forces client to call ProcessOutput to determine whether - //a sample is ready. - - return S_OK; -#endif -} - - -HRESULT WebmMfVp8Dec::SetOutputBounds( - LONGLONG /* hnsLowerBound */ , - LONGLONG /* hnsUpperBound */ ) -{ - return E_NOTIMPL; //TODO -} - - -HRESULT WebmMfVp8Dec::ProcessEvent( - DWORD dwInputStreamID, - IMFMediaEvent* /* pEvent */ ) -{ - if (dwInputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - -#if 0 //def _DEBUG - if (pEvent) - { - MediaEventType t; - - HRESULT hr = pEvent->GetType(&t); - assert(SUCCEEDED(hr)); - - odbgstream os; - os << "WebmMfVp8Dec::ProcessEvent: type=" << t << endl; - } -#endif - - return E_NOTIMPL; //TODO -} - - -HRESULT WebmMfVp8Dec::ProcessMessage( - MFT_MESSAGE_TYPE m, - ULONG_PTR) -{ -#if 0 //def _DEBUG - odbgstream os; - os << "WebmMfVp8Dec::ProcessMessage(samples.size=" - << m_samples.size() << "): "; -#endif - - switch (m) - { - case MFT_MESSAGE_COMMAND_FLUSH: -#if 0 //def _DEBUG - os << "COMMAND_FLUSH" << endl; -#endif - - //http://msdn.microsoft.com/en-us/library/dd940419%28v=VS.85%29.aspx - - Flush(); - return S_OK; - - case MFT_MESSAGE_COMMAND_DRAIN: -#if 0 //def _DEBUG - os << "COMMAND_DRAIN" << endl; -#endif - - //http://msdn.microsoft.com/en-us/library/dd940418%28v=VS.85%29.aspx - - //TODO: input stream does not accept input in the MFT processes all - //data from previous calls to ProcessInput. - - return S_OK; - - case MFT_MESSAGE_SET_D3D_MANAGER: -#if 0 //def _DEBUG - os << "SET_D3D" << endl; -#endif - - return S_OK; - - case MFT_MESSAGE_DROP_SAMPLES: -#if 0 //def _DEBUG - os << "DROP_SAMPLES" << endl; -#endif - - return S_OK; - - case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: -#if 0 //def _DEBUG - os << "NOTIFY_BEGIN_STREAMING" << endl; -#endif - - //http://msdn.microsoft.com/en-us/library/dd940421%28v=VS.85%29.aspx - - //TODO: init decoder library here, instead of during SetInputType - - return S_OK; - - case MFT_MESSAGE_NOTIFY_END_STREAMING: -#if 0 //def _DEBUG - os << "NOTIFY_END_STREAMING" << endl; -#endif - - //http://msdn.microsoft.com/en-us/library/dd940423%28v=VS.85%29.aspx - - //NOTE: flush is not performed here - - return S_OK; - - case MFT_MESSAGE_NOTIFY_END_OF_STREAM: -#if 0 //def _DEBUG - os << "NOTIFY_EOS" << endl; -#endif - - //http://msdn.microsoft.com/en-us/library/dd940422%28v=VS.85%29.aspx - - //TODO: set discontinuity flag on first sample after this - - return S_OK; - - case MFT_MESSAGE_NOTIFY_START_OF_STREAM: -#if 0 //def _DEBUG - os << "NOTIFY_START_OF_STREAM" << endl; -#endif - - return S_OK; - - case MFT_MESSAGE_COMMAND_MARKER: -#if 0 //def _DEBUG - os << "COMMAND_MARKER" << endl; -#endif - - return S_OK; - - default: - return S_OK; - } -} - - -HRESULT WebmMfVp8Dec::ProcessInput( - DWORD dwInputStreamID, - IMFSample* pSample, - DWORD) -{ - if (dwInputStreamID != 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (pSample == 0) - return E_INVALIDARG; - - DWORD count; - - HRESULT hr = pSample->GetBufferCount(&count); - assert(SUCCEEDED(hr)); - - if (count == 0) //weird - return S_OK; - - //if (count > 1) - // return E_INVALIDARG; - - //TODO: check duration - //TODO: check timestamp - - Lock lock; - - hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (m_pInputMediaType == 0) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (m_pOutputMediaType == 0) //TODO: need this check here? - return MF_E_TRANSFORM_TYPE_NOT_SET; - - pSample->AddRef(); - - m_samples.push_back(SampleInfo()); - - SampleInfo& i = m_samples.back(); - - i.pSample = pSample; - i.dwBuffer = 0; - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::ProcessOutput( - DWORD dwFlags, - DWORD cOutputBufferCount, - MFT_OUTPUT_DATA_BUFFER* pOutputSamples, - DWORD* pdwStatus) -{ - if (pdwStatus) - *pdwStatus = 0; - - if (dwFlags) - return E_INVALIDARG; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (m_pInputMediaType == 0) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (m_pOutputMediaType == 0) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (cOutputBufferCount == 0) - return E_INVALIDARG; - - if (pOutputSamples == 0) - return E_INVALIDARG; - - const MFT_OUTPUT_DATA_BUFFER& data = pOutputSamples[0]; - //data.dwStreamID should equal 0, but we ignore it - - for (;;) - { - hr = Decode(data.pSample); - - if (FAILED(hr) || (hr == S_OK)) - return hr; - } -} - - -HRESULT WebmMfVp8Dec::Decode(IMFSample* pSample_out) -{ - if (pSample_out == 0) - return E_INVALIDARG; - - if (m_samples.empty()) - return MF_E_TRANSFORM_NEED_MORE_INPUT; - - HRESULT hr; - LONGLONG time, duration; - - { - SampleInfo& i = m_samples.front(); - assert(i.pSample); - - UINT32 iKey; - - hr = i.pSample->GetUINT32(MFSampleExtension_CleanPoint, &iKey); - - const bool bKey = SUCCEEDED(hr) && (iKey != FALSE); - - //TODO: this is an optimization oppurtunity, since it would - //optimize-away the expense of decoding too (not the expense - //of mere rendering, which is what other modes optimize away). - //It's tricky to implement since if we - //transition OUT of DROP_5 mode, then it's possible that we - //we're in the middle of a GOP and so we cannot decode - //again until we see the next keyframe. In other modes, we - //decode every frame (we are suppressing the rendering, not - //decode), so a state transition between modes is not an issue. - //For now, just decompress every frame. - // - //if (!bKey && (m_drop_mode == MF_DROP_MODE_5)) - //{ - // i.pSample->Release(); - // i.pSample = 0; - // - // m_samples.pop_front(); - // - // return S_FALSE; - //} - //END TODO. - - UINT32 bPreroll; - - hr = i.pSample->GetUINT32(WebmTypes::WebMSample_Preroll, &bPreroll); - - if (SUCCEEDED(hr) && (bPreroll != FALSE)) //preroll frame - { - hr = i.DecodeAll(m_ctx); - - i.pSample->Release(); - i.pSample = 0; - - m_samples.pop_front(); - - if (FAILED(hr)) //bad decode - return hr; - - ReplenishDropBudget(); - return S_FALSE; //throw away this sample - } - - hr = i.DecodeOne(m_ctx, time, duration); - - if (FAILED(hr) || (hr != S_OK)) - { - i.pSample->Release(); - i.pSample = 0; - - m_samples.pop_front(); - - if (FAILED(hr)) //bad decode - return hr; - } - - if (m_drop_mode == MF_DROP_MODE_NONE) - __noop; //we're not throwing away frames - - else if (m_drop_mode >= MF_DROP_MODE_5) - { - if (!bKey) - return S_FALSE; //throw away this sample - } - else if (bKey || (m_drop_budget > 1)) - --m_drop_budget; //spend out budget to render this frame - - else - { - ReplenishDropBudget(); - return S_FALSE; //throw away this sample - } - } - - DWORD count_out; - - hr = pSample_out->GetBufferCount(&count_out); - - if (SUCCEEDED(hr) && (count_out != 1)) //TODO: handle this? - return E_INVALIDARG; - - IMFMediaBufferPtr buf_out; - - hr = pSample_out->GetBufferByIndex(0, &buf_out); - - if (FAILED(hr) || !bool(buf_out)) - return E_INVALIDARG; - - GUID subtype; - - hr = m_pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &subtype); - assert(SUCCEEDED(hr)); - - //TODO: - //MFVideoFormat_I420 - - FrameSize frame_size; - - const DWORD cbFrameLen = GetOutputBufferSize(frame_size); - assert(cbFrameLen > 0); - - //The sequence of querying for the output buffer is described on - //the page "Uncompressed Video Buffers". - //http://msdn.microsoft.com/en-us/library/aa473821%28v=VS.85%29.aspx - - IMF2DBuffer* buf2d_out; - - hr = buf_out->QueryInterface(&buf2d_out); - - if (SUCCEEDED(hr)) - { - assert(buf2d_out); - - BYTE* ptr_out; - LONG stride_out; - - hr = buf2d_out->Lock2D(&ptr_out, &stride_out); - assert(SUCCEEDED(hr)); - assert(ptr_out); - assert(stride_out > 0); //top-down DIBs are positive, right? - - const HRESULT hrGetFrame = GetFrame(ptr_out, stride_out, subtype); - - //TODO: set output buffer length? - - hr = buf2d_out->Unlock2D(); - assert(SUCCEEDED(hr)); - - buf2d_out->Release(); - buf2d_out = 0; - - if (FAILED(hrGetFrame) || (hrGetFrame != S_OK)) - return hrGetFrame; - } - else - { - BYTE* ptr_out; - DWORD cbMaxLen; - - hr = buf_out->Lock(&ptr_out, &cbMaxLen, 0); - assert(SUCCEEDED(hr)); - assert(ptr_out); - assert(cbMaxLen >= cbFrameLen); - - //TODO: verify stride of output buffer - //The page "Uncompressed Video Buffers" here: - //http://msdn.microsoft.com/en-us/library/aa473821%28v=VS.85%29.aspx - //explains how to calculate the "minimum stride": - // MF_MT_DEFAULT_STRIDE - // or, MFGetStrideForBitmapInfoHeader - // or, calculate it yourself - - INT32 stride_out; - - hr = pSample_out->GetUINT32( - MF_MT_DEFAULT_STRIDE, - (UINT32*)&stride_out); - - if (SUCCEEDED(hr) && (stride_out != 0)) - assert(stride_out > 0); - else - { - const DWORD fcc = subtype.Data1; - - const DWORD w = frame_size.width; - LONG stride_out_; - - hr = MFGetStrideForBitmapInfoHeader(fcc, w, &stride_out_); - - if (SUCCEEDED(hr) && (stride_out_ != 0)) - { - assert(stride_out_ > 0); - stride_out = stride_out_; - } - else - { - assert((w % 2) == 0); //TODO - stride_out = w; //TODO: is this correct??? - } - } - - const HRESULT hrGetFrame = GetFrame(ptr_out, stride_out, subtype); - - hr = buf_out->Unlock(); - assert(SUCCEEDED(hr)); - - if (FAILED(hrGetFrame) || (hrGetFrame != S_OK)) - return hrGetFrame; - - hr = buf_out->SetCurrentLength(cbFrameLen); - assert(SUCCEEDED(hr)); - } - - if (time >= 0) - { - hr = pSample_out->SetSampleTime(time); - assert(SUCCEEDED(hr)); - } - - if (duration >= 0) - { - hr = pSample_out->SetSampleDuration(duration); - assert(SUCCEEDED(hr)); - } - - return S_OK; //done -} - - -HRESULT WebmMfVp8Dec::GetFrame( - BYTE* pOutBuf, - ULONG strideOut, - const GUID& subtype) -{ - assert(pOutBuf); - assert(strideOut); - assert((strideOut % 2) == 0); //TODO: resolve this issue - - vpx_codec_iter_t iter = 0; - - const vpx_image_t* const f = vpx_codec_get_frame(&m_ctx, &iter); - - if (f == 0) //alt-ref - return S_FALSE; //tell caller to pop this buffer and call me back - - //Y - - const BYTE* pInY = f->planes[VPX_PLANE_Y]; - assert(pInY); - - unsigned int wIn = f->d_w; - unsigned int hIn = f->d_h; - - BYTE* pOut = pOutBuf; - - const int strideInY = f->stride[VPX_PLANE_Y]; - - for (unsigned int y = 0; y < hIn; ++y) - { - memcpy(pOut, pInY, wIn); - pInY += strideInY; - pOut += strideOut; - } - - 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]; - - wIn = (wIn + 1) / 2; - hIn = (hIn + 1) / 2; - - if (subtype == MFVideoFormat_NV12) - { - for (unsigned int y = 0; y < hIn; ++y) - { - const BYTE* u = pInU; //src - const BYTE* v = pInV; //src - BYTE* uv = pOut; //dst - - for (unsigned int idx = 0; idx < wIn; ++idx) //uv - { - *uv++ = *u++; //Cb - *uv++ = *v++; //Cr - } - - pInU += strideInU; - pInV += strideInV; - pOut += strideOut; - } - } - else if (subtype == MFVideoFormat_YV12) - { - strideOut /= 2; - - //V - - for (unsigned int y = 0; y < hIn; ++y) - { - memcpy(pOut, pInV, wIn); - pInV += strideInV; - pOut += strideOut; - } - - //U - - for (unsigned int y = 0; y < hIn; ++y) - { - memcpy(pOut, pInU, wIn); - pInU += strideInU; - pOut += strideOut; - } - } - else - { - assert(subtype == MFVideoFormat_IYUV); - - strideOut /= 2; - - //U - - for (unsigned int y = 0; y < hIn; ++y) - { - memcpy(pOut, pInU, wIn); - pInU += strideInU; - pOut += strideOut; - } - - //V - - for (unsigned int y = 0; y < hIn; ++y) - { - memcpy(pOut, pInV, wIn); - pInV += strideInV; - pOut += strideOut; - } - } - - const vpx_image_t* const f2 = vpx_codec_get_frame(&m_ctx, &iter); - f2; - assert(f2 == 0); - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::SetRate(BOOL bThin, float rate) -{ - //odbgstream os; - //os << "WebmMfVp8Dec::SetRate: bThin=" - // << boolalpha << (bThin ? true : false) - // << " rate=" - // << rate - // << endl; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - //if (m_pEvents == 0) - // return MF_E_SHUTDOWN; - - //if (bThin) - // return MF_E_THINNING_UNSUPPORTED; //TODO - - if (rate < 0) - return MF_E_REVERSE_UNSUPPORTED; //TODO - - m_rate = rate; - m_bThin = bThin; - - //reset drop mode when rate change - - m_lag_sum = 0; - m_lag_count = 0; - m_lag_frames = 0; - - hr = WebmMfVp8Dec::SetDropMode(MF_DROP_MODE_NONE); - assert(SUCCEEDED(hr)); - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetRate(BOOL* pbThin, float* pRate) -{ - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - //if (m_pEvents == 0) - // return MF_E_SHUTDOWN; - - if (pbThin) - *pbThin = m_bThin; - - if (pRate) //return error when pRate ptr is NULL? - *pRate = m_rate; - - //odbgstream os; - //os << "WebmMfVp8Dec::GetRate: bThin=" - // << boolalpha << m_bThin - // << " rate=" - // << m_rate - // << endl; - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetSlowestRate( - MFRATE_DIRECTION d, - BOOL /* bThin */ , - float* pRate) -{ - //odbgstream os; - //os << "WebmMfVp8Dec::GetSlowestRate" << endl; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - //if (m_pEvents == 0) - // return MF_E_SHUTDOWN; - - if (d == MFRATE_REVERSE) - return MF_E_REVERSE_UNSUPPORTED; //TODO - - //if (bThin) - // return MF_E_THINNING_UNSUPPORTED; //TODO - - if (pRate == 0) - return E_POINTER; - - float& r = *pRate; - r = 0; //? - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetFastestRate( - MFRATE_DIRECTION d, - BOOL /* bThin */ , - float* pRate) -{ - //odbgstream os; - //os << "WebmMfSource::GetFastestRate" << endl; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - //if (m_pEvents == 0) - // return MF_E_SHUTDOWN; - - if (d == MFRATE_REVERSE) - return MF_E_REVERSE_UNSUPPORTED; //TODO - - //if (bThin) - // return MF_E_THINNING_UNSUPPORTED; //TODO - - if (pRate == 0) - return E_POINTER; - - float& r = *pRate; - r = 64; //? - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::IsRateSupported( - BOOL /* bThin */ , - float rate, - float* pNearestRate) -{ - //odbgstream os; - //os << "WebmMfVp8Dec::IsRateSupported: rate=" << rate << endl; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - //if (m_pEvents == 0) - // return MF_E_SHUTDOWN; - - //if (bThin) - // return MF_E_THINNING_UNSUPPORTED; //TODO - - if (rate < 0) - return MF_E_REVERSE_UNSUPPORTED; //TODO - - //float int_part; - //const float frac_part = modf(rate, &int_part); - - if (pNearestRate) - *pNearestRate = rate; - - return S_OK; //TODO -} - - -HRESULT WebmMfVp8Dec::GetService( - REFGUID sid, - REFIID iid, - LPVOID* ppv) -{ - if (sid == MF_RATE_CONTROL_SERVICE) - return WebmMfVp8Dec::QueryInterface(iid, ppv); - - if (ppv) - *ppv = 0; - - return MF_E_UNSUPPORTED_SERVICE; -} - - -void WebmMfVp8Dec::Flush() -{ - while (!m_samples.empty()) - { - SampleInfo& i = m_samples.front(); - assert(i.pSample); - - i.pSample->Release(); - i.pSample = 0; - - m_samples.pop_front(); - } -} - - -HRESULT WebmMfVp8Dec::SampleInfo::DecodeAll(vpx_codec_ctx_t& ctx) -{ - assert(pSample); - - DWORD count; - - HRESULT hr = pSample->GetBufferCount(&count); - assert(SUCCEEDED(hr)); - assert(count > 0); - - while (dwBuffer < count) - { - IMFMediaBufferPtr buf; - - hr = pSample->GetBufferByIndex(dwBuffer, &buf); - assert(SUCCEEDED(hr)); - assert(buf); - - BYTE* ptr; - DWORD len; - - hr = buf->Lock(&ptr, 0, &len); - assert(SUCCEEDED(hr)); - assert(ptr); - assert(len); - - const vpx_codec_err_t e = vpx_codec_decode(&ctx, ptr, len, 0, 0); - - hr = buf->Unlock(); - assert(SUCCEEDED(hr)); - - if (e != VPX_CODEC_OK) - return MF_E_INVALID_STREAM_DATA; - - ++dwBuffer; //consume this buffer - } - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::SampleInfo::DecodeOne( - vpx_codec_ctx_t& ctx, - LONGLONG& time, - LONGLONG& duration) -{ - assert(pSample); - - DWORD count; - - HRESULT hr = pSample->GetBufferCount(&count); - assert(SUCCEEDED(hr)); - assert(count > 0); - assert(count > dwBuffer); - - IMFMediaBufferPtr buf; - - hr = pSample->GetBufferByIndex(dwBuffer, &buf); - assert(SUCCEEDED(hr)); - assert(buf); - - BYTE* ptr; - DWORD len; - - hr = buf->Lock(&ptr, 0, &len); - assert(SUCCEEDED(hr)); - assert(ptr); - assert(len); - - const vpx_codec_err_t e = vpx_codec_decode(&ctx, ptr, len, 0, 0); - - hr = buf->Unlock(); - assert(SUCCEEDED(hr)); - - if (e != VPX_CODEC_OK) - return MF_E_INVALID_STREAM_DATA; - - LONGLONG t; - - hr = pSample->GetSampleTime(&t); - - if (FAILED(hr) || (t < 0)) - { - time = -1; - duration = -1; - } - else - { - LONGLONG d; - - hr = pSample->GetSampleDuration(&d); - - if (FAILED(hr) || (d <= 0)) - { - if (dwBuffer == 0) //first buffer - time = t; - else - time = -1; - - duration = -1; - } - else - { - duration = d / count; - time = t + dwBuffer * duration; - } - } - - ++dwBuffer; //consume this buffer - return (dwBuffer >= count) ? S_FALSE : S_OK; -} - - -HRESULT WebmMfVp8Dec::SetDropMode(MF_QUALITY_DROP_MODE d) -{ - if (d > MF_DROP_MODE_5) - return MF_E_NO_MORE_DROP_MODES; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - m_drop_mode = d; - m_drop_budget = 0; - - //odbgstream os; - //os << "SetDropMode=" << m_drop_mode << endl; - - return S_OK; - - //we always keep keyframes ("cleanpoint") - //1: drop 1 out of every 2 frames - //2: drop 2 out of every 3 frames - //3: drop 3 out of every 4 frames - //4: drop 4 out of every 5 frames - //5: drop all delta frames - // - //alternatively: - //1: drop 1 out of every 16 = 2^4 = 2^(5-1) - //2: drop 1 out of every 8 = 2^3 = 2^(5-2) = 2 out of every 16 - //3: drop 1 out of every 4 = 2^2 = 2^(5-3) = 4 out of every 16 - //4: drop 1 out of every 2 = 2^1 = 2^(5-4) = 8 out of every 16 - //5: drop all delta frames - - //when we detect a keyframe, this resets the counter - // - //level 1: - //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 - // x - // - //level 2: - //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 - // x x - // - //level 3: - //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 - // x x x x x - // - //level 4: - //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 - // x x x x x x x x x x - // - //we could also tie this to framerate, e.g. - //1: drop 1 frame per second - //2: drop 1 frame per 1/2 sec - //3: drop 1 frame per 1/4 sec - //4: drop 1 frame per 1/8 sec - //5: drop all delta frames - // - //Give outselves a budget: the smaller the drop mode, - //the larger the budget. Drop the first eligrable - //frame when our budget drops to 0. -} - - -void WebmMfVp8Dec::ReplenishDropBudget() -{ - if (m_drop_mode == MF_DROP_MODE_NONE) - { - m_drop_budget = 0; //doesn't really matter - return; - } - - if (m_drop_mode >= MF_DROP_MODE_5) - { - m_drop_budget = 0; //doesn't really matter - return; - } - - const int d = m_drop_mode; - assert(d >= 1); - assert(d <= 4); - - m_drop_budget = (1 << (5 - d)); -} - - -HRESULT WebmMfVp8Dec::SetQualityLevel(MF_QUALITY_LEVEL q) -{ - if (q == MF_QUALITY_NORMAL) - return S_OK; - - return MF_E_NO_MORE_QUALITY_LEVELS; -} - - -HRESULT WebmMfVp8Dec::GetDropMode(MF_QUALITY_DROP_MODE* p) -{ - if (p == 0) - return E_POINTER; - - Lock lock; - - HRESULT hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - MF_QUALITY_DROP_MODE& d = *p; - d = m_drop_mode; - - //odbgstream os; - //os << "GetDropMode=" << m_drop_mode << endl; - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::GetQualityLevel(MF_QUALITY_LEVEL* p) -{ - if (p == 0) - return E_POINTER; - - MF_QUALITY_LEVEL& q = *p; - q = MF_QUALITY_NORMAL; //no post-processing for us to shut off - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::DropTime(LONGLONG t) -{ - t; - return MF_E_DROPTIME_NOT_SUPPORTED; -} - - -HRESULT WebmMfVp8Dec::NotifyQualityEvent(IMFMediaEvent* pe, DWORD* pdw) -{ - if (pe == 0) - return E_INVALIDARG; - - if (pdw == 0) - return E_POINTER; - - DWORD& dw = *pdw; - dw = 0; //TODO: possibly set this to MF_QUALITY_CANNOT_KEEP_UP - - MediaEventType type; - - HRESULT hr = pe->GetType(&type); - - if (FAILED(hr)) - return hr; - - if (type != MEQualityNotify) - return E_INVALIDARG; - - GUID ext; - - hr = pe->GetExtendedType(&ext); - - if (FAILED(hr)) - return hr; - - if (ext != MF_QUALITY_NOTIFY_SAMPLE_LAG) - return S_OK; - - PROPVARIANT v; - - hr = pe->GetValue(&v); - - if (FAILED(hr)) - return E_INVALIDARG; - - if (v.vt != VT_I8) - return E_INVALIDARG; - - const LONGLONG reftime = v.hVal.QuadPart; - - //MEQualityNotify Event - //http://msdn.microsoft.com/en-us/library/ms694893(VS.85).aspx - - //negative is good - frames are early - //positive is bad - frames are late - //value is a lateness - time in 100ns units - //we're seeing extreme lateness: 2 or 3s - - //if (ext == MF_QUALITY_NOTIFY_PROCESSING_LATENCY) - // return S_OK; - - Lock lock; - - hr = lock.Seize(this); - - if (FAILED(hr)) - return hr; - - if (m_frame_rate <= 0) //no framerate available - return S_OK; //TODO: for now, don't bother trying to drop - - if (m_bThin || (m_rate != 1)) //bThin = true implies drop level 5 already - return S_OK; - - if (reftime <= 0) //assume we're all caught up, even if value is spurious - { - m_lag_sum = 0; - m_lag_count = 0; - m_lag_frames = 0; - - hr = WebmMfVp8Dec::SetDropMode(MF_DROP_MODE_NONE); - assert(SUCCEEDED(hr)); - - return S_OK; - } - - m_lag_sum += reftime; - ++m_lag_count; - - if (m_lag_count < 16) - return S_OK; - - const float lag_reftime = float(m_lag_sum) / float(m_lag_count); - - const float frames_ = lag_reftime * m_frame_rate / 10000000.0F; - const int frames = static_cast<int>(frames_); - - if (frames <= 1) //treat small values as noise - { - m_lag_sum = 0; - m_lag_count = 0; - m_lag_frames = 0; - - hr = WebmMfVp8Dec::SetDropMode(MF_DROP_MODE_NONE); - assert(SUCCEEDED(hr)); - } - - MF_QUALITY_DROP_MODE m; - - hr = WebmMfVp8Dec::GetDropMode(&m); - assert(SUCCEEDED(hr)); - - if ((m_lag_frames > 0) && //have previous value - (frames >= m_lag_frames) && //we're not getting better - (m < MF_DROP_MODE_5)) //TODO: use MF_QUALITY_CANNOT_KEEP_UP? - { -#ifdef _DEBUG - odbgstream os; - os << "WebmMfVp8Dec::NotifyQualityEvent: old drop mode=" << m; -#endif - - m = static_cast<MF_QUALITY_DROP_MODE>(int(m) + 1); - -#ifdef _DEBUG - os << " new drop mode=" << m << endl; -#endif - - hr = WebmMfVp8Dec::SetDropMode(m); - assert(SUCCEEDED(hr)); - } - - m_lag_sum = 0; - m_lag_count = 0; - m_lag_frames = frames; - - return S_OK; - - //Here's a case when there's been a long sequence of frames - //having positive lag. That means we're slightly behind, so - //we should compare how much we're behind now to how much - //we were behind in the recent past. The reason we do that - //is to discover whether we have a trend. - // - //How many data points to we use to compute the trend? We used - //the most recent 16 points to compute our latest lag value, so - //we must compare the most recent lag value to previous lag values. - // - //If we have a previous lag value, then we can compute a trend, even - //if we only have two points. - // - //If the avg lag computed here is same or (slightly?) larger, then it - //means we really are behind. We would choose a drop level. Our - //current algorithm even for dropping doesn't work that fast: at - //drop level 1, we drop only 1 frame for every 16 frames, so once - //we set a non-zero drop level we do have to wait at least 16 frames - //before reacting (which is what we do here). - // - //We don't necessarily have to compare lag times: we convert this to - //a frame, so we could compare frames instead. - // - //The issue remain: how often do we adjust the drop level? - // - //I suppose that when the reported lag time is less than 0, then this - //will reset everything anyway, so we can set the drop level back to - //0 when that happens. - // - //We probalby only have to increase the drop level, until we're caught - //up, and it gets reset back to 0. The question is how often we adjust - //the drop level up. - // - //1st time: 1 out of 16 - //2nd time: 2 out of 16 - //3rd time: 4 out of 16 - //4th time: 8 out of 16 - //5th time: keyframes only - // - //if lag has increased since last time, go ahead and increase the drop - //level. - // - //if lag has decreased since last time, the we can probably either decrease - //the drop level, or leave it as is. - - //These dampened out by themselves, so there's nothing special we needed - //to do. We don't want to react right away. The problem we need to - //handle - //is when the lag is positive and large, and stays constant or is - //increasing. - //It the number is trending down then the problem solves itself. The hard - //cases are when the lag is growing, or staying constant and large. - // - //How to define "growing"? If the slope is positive, that's growing. If - //we remain at any positive slope, the lag will definitely grow. - // - //We could use a one-shot flag: if the flag is true, throw away the next - //non-key frame, then clear the flag. - // - //Our goal is to pull the slope down to 0 (or negative). This should drive - //the lag down, which should drive the slope towards 0. - // - //A running average isn't necessarily what we want, although this - //will give us an estimate of the avg lag. If the avg is large and - //constant then we must correct. - - //How to compute avg lag: - // (sum of lag times) / (number of lag times) - //or could weigh curr avg and curr lag - //e.g. new avg lag = (15/16) * (old avg lag) + (1/16) * (new lag) - //if lag <= 0 then reset avg lag - - //10000000 = 1 sec - // 5000000 = 1/2 sec - // 2500000 = 1/4 sec - // 1250000 = 1/8 sec - // 625000 = 1/16 sec - // 312500 = 1/32 sec - // 156250 = 1/64 sec - // 78125 = 1/128 sec - - // 1 sec late - // if we see 2 frames the set drop mode #5 (keyframe only) - // - // 1/2 sec late - // if we see 4 frames then set drop mode #4 - // - // 1/4 sec late - // if we see - - //drop mode 1 = 1/16 frames are dropped - // 2 = 1/8 frames - // 3 = 1/4 frames - // 4 = 1/2 frames - // - //If we do set the drop mode, then the decoder doesn't - //react that fast. - // - //for drop mode 1, we gain 1/16th sec every 16 frames - //if we were behind by 1/16th sec, then we'd catch up after 1 cycle - //if we were behind by 1/8th sec, we'd catch up after 2 cycles, etc - // - //for drop mode 2, we gain 1/8th sec every 8 frames - //if we were behind by 1/8th sec, we'd catch up after 1 cycle - //if we were behind by 1/4 sec, then we'd catch up after 2 cycles, etc - // - //for drop mode 3, we gain 1/4 sec every 4 frames - //if we were behind by 1/4 sec, we'd catch up after 1 cycle - //if we were behind by 1/2 sec, then we'd catch up after 2 cycles, etc - // - //for drop mode 4, we gain 1/2 sec every 2 frames - // - //THAT'S ALL WRONG - //the drop modes work in terms of frames, not times - //IQualityAdvise2 works in terms of time, not frames - // - //To convert between the two advise interfaces we need the frame rate -} - - -void WebmMfVp8Dec::FrameRate::Init() -{ - m_numerator = 0; - m_denominator = 0; -} - - -HRESULT WebmMfVp8Dec::FrameRate::AssignFrom(IMFMediaType* pmt) -{ - UINT32& num = m_numerator; - UINT32& den = m_denominator; - - const HRESULT hr = MFGetAttributeRatio(pmt, MF_MT_FRAME_RATE, &num, &den); - - //TODO: check explicitly for the result that means "attribute not set". - //MF_E_ATTRIBUTENOTFOUND - //MF_E_INVALIDTYPE - - if (FAILED(hr)) //we assume here this means value wasn't found - { - Init(); - return S_FALSE; - } - - if (den == 0) - return MF_E_INVALIDMEDIATYPE; - - if (num == 0) - return MF_E_INVALIDMEDIATYPE; - - return S_OK; -} - - -HRESULT WebmMfVp8Dec::FrameRate::CopyTo(IMFMediaType* pmt) const -{ - const UINT32 num = m_numerator; - const UINT32 den = m_denominator; - - return MFSetAttributeRatio(pmt, MF_MT_FRAME_RATE, num, den); -} - - -HRESULT WebmMfVp8Dec::FrameRate::CopyTo(float& value) const -{ - const UINT32 num = m_numerator; - const UINT32 den = m_denominator; - - if (den == 0) - value = -1; //serves as our "undefined framerate" nonce value - else - value = float(num) / float(den); - - return S_OK; -} - - -bool WebmMfVp8Dec::FrameRate::Match(const FrameRate& rhs) const -{ - const UINT64 n_in = m_numerator; - const UINT64 d_in = m_denominator; - - const UINT64 n_out = rhs.m_numerator; - const UINT64 d_out = rhs.m_denominator; - - // n_in / d_in = n_out / d_out - // n_in * d_out = n_out * d_in - - return ((n_in * d_out) == (n_out * d_in)); -} - - -} //end namespace WebmMfVp8DecLib +#pragma warning(disable:4505) //unreferenced local function removed +#include "clockable.h" +#include <mfidl.h> +#include "vpx/vpx_decoder.h" +#include "vpx/vp8dx.h" +#include "webmmfvp8dec.h" +#include "webmtypes.h" +#include <mfapi.h> +#include <mferror.h> +#include <comdef.h> +#include <cassert> +#include <new> +#ifdef _DEBUG +#include "odbgstream.h" +#include "iidstr.h" +using std::endl; +using std::boolalpha; +#endif + +_COM_SMARTPTR_TYPEDEF(IMFMediaBuffer, __uuidof(IMFMediaBuffer)); +_COM_SMARTPTR_TYPEDEF(IMF2DBuffer, __uuidof(IMF2DBuffer)); + + +namespace WebmMfVp8DecLib +{ + + +HRESULT CreateDecoder( + IClassFactory* pClassFactory, + IUnknown* pOuter, + const IID& iid, + void** ppv) +{ + if (ppv == 0) + return E_POINTER; + + *ppv = 0; + + if (pOuter) + return CLASS_E_NOAGGREGATION; + + WebmMfVp8Dec* const p = new (std::nothrow) WebmMfVp8Dec(pClassFactory); + + if (p == 0) + return E_OUTOFMEMORY; + + IMFTransform* const pUnk = p; + + const HRESULT hr = pUnk->QueryInterface(iid, ppv); + + const ULONG cRef = pUnk->Release(); + cRef; + + return hr; +} + + +WebmMfVp8Dec::WebmMfVp8Dec(IClassFactory* pClassFactory) : + m_pClassFactory(pClassFactory), + m_cRef(1), + m_pInputMediaType(0), + m_pOutputMediaType(0), + m_rate(1), + m_bThin(FALSE), + m_drop_mode(MF_DROP_MODE_NONE), + m_drop_budget(0), + m_lag_sum(0), + m_lag_count(0), + m_lag_frames(0), + m_frame_rate(-1) +{ + HRESULT hr = m_pClassFactory->LockServer(TRUE); + assert(SUCCEEDED(hr)); + + hr = CLockable::Init(); + assert(SUCCEEDED(hr)); + + //m_frame_rate.Init(); +} + + +WebmMfVp8Dec::~WebmMfVp8Dec() +{ + if (m_pInputMediaType) + { + const ULONG n = m_pInputMediaType->Release(); + n; + assert(n == 0); + + m_pInputMediaType = 0; + + const vpx_codec_err_t e = vpx_codec_destroy(&m_ctx); + e; + assert(e == VPX_CODEC_OK); + } + + if (m_pOutputMediaType) + { + const ULONG n = m_pOutputMediaType->Release(); + n; + assert(n == 0); + + m_pOutputMediaType = 0; + } + + Flush(); + + HRESULT hr = m_pClassFactory->LockServer(FALSE); + assert(SUCCEEDED(hr)); +} + + +HRESULT WebmMfVp8Dec::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<IMFTransform*>(this); //must be nondelegating + } + else if (iid == __uuidof(IMFTransform)) + { + pUnk = static_cast<IMFTransform*>(this); + } + else if (iid == __uuidof(IMFQualityAdvise2)) + { + pUnk = static_cast<IMFQualityAdvise2*>(this); + } + else if (iid == __uuidof(IMFQualityAdvise)) + { + pUnk = static_cast<IMFQualityAdvise*>(this); + } + else if (iid == __uuidof(IMFRateControl)) + { + pUnk = static_cast<IMFRateControl*>(this); + } + else if (iid == __uuidof(IMFRateSupport)) + { + pUnk = static_cast<IMFRateSupport*>(this); + } + else if (iid == __uuidof(IMFGetService)) + { + pUnk = static_cast<IMFGetService*>(this); + } + else + { +//#ifdef _DEBUG +// wodbgstream os; +// os << "WebmMfVp8Dec::QI: iid=" << IIDStr(iid) << std::endl; +//#endif + + pUnk = 0; + return E_NOINTERFACE; + } + + pUnk->AddRef(); + return S_OK; +} + + +ULONG WebmMfVp8Dec::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + + +ULONG WebmMfVp8Dec::Release() +{ + if (LONG n = InterlockedDecrement(&m_cRef)) + return n; + + delete this; + return 0; +} + + +HRESULT WebmMfVp8Dec::GetStreamLimits( + DWORD* pdwInputMin, + DWORD* pdwInputMax, + DWORD* pdwOutputMin, + DWORD* pdwOutputMax) +{ + if (pdwInputMin == 0) + return E_POINTER; + + if (pdwInputMax == 0) + return E_POINTER; + + if (pdwOutputMin == 0) + return E_POINTER; + + if (pdwOutputMax == 0) + return E_POINTER; + + DWORD& dwInputMin = *pdwInputMin; + dwInputMin = 1; + + DWORD& dwInputMax = *pdwInputMax; + dwInputMax = 1; + + DWORD& dwOutputMin = *pdwOutputMin; + dwOutputMin = 1; + + DWORD& dwOutputMax = *pdwOutputMax; + dwOutputMax = 1; + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetStreamCount( + DWORD* pcInputStreams, + DWORD* pcOutputStreams) +{ + if (pcInputStreams == 0) + return E_POINTER; + + if (pcOutputStreams == 0) + return E_POINTER; + + DWORD& cInputStreams = *pcInputStreams; + cInputStreams = 1; + + DWORD& cOutputStreams = *pcOutputStreams; + cOutputStreams = 1; + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetStreamIDs( + DWORD, + DWORD* pdwInputIDs, + DWORD, + DWORD* pdwOutputIDs) +{ + if (pdwInputIDs) + *pdwInputIDs = 0; + + if (pdwOutputIDs) + *pdwOutputIDs = 0; + + return E_NOTIMPL; +} + + +HRESULT WebmMfVp8Dec::GetInputStreamInfo( + DWORD dwInputStreamID, + MFT_INPUT_STREAM_INFO* pStreamInfo) +{ + if (pStreamInfo == 0) + return E_POINTER; + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + MFT_INPUT_STREAM_INFO& info = *pStreamInfo; + + //LONGLONG hnsMaxLatency; + //DWORD dwFlags; + //DWORD cbSize; + //DWORD cbMaxLookahead; + //DWORD cbAlignment; + + info.cbMaxLookahead = 0; + info.hnsMaxLatency = 0; //TODO: Is 0 correct? + //TODO: does lag-in-frames matter here? + //See "_MFT_INPUT_STREAM_INFO_FLAGS Enumeration" for more info: + //http://msdn.microsoft.com/en-us/library/ms703975%28v=VS.85%29.aspx + + //enum _MFT_INPUT_STREAM_INFO_FLAGS + // { MFT_INPUT_STREAM_WHOLE_SAMPLES = 0x1, + // MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER = 0x2, + // MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE = 0x4, + // MFT_INPUT_STREAM_HOLDS_BUFFERS = 0x8, + // MFT_INPUT_STREAM_DOES_NOT_ADDREF = 0x100, + // MFT_INPUT_STREAM_REMOVABLE= 0x200, + // MFT_INPUT_STREAM_OPTIONAL = 0x400, + // MFT_INPUT_STREAM_PROCESSES_IN_PLACE = 0x800 + // }; + + info.dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES; + //MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; + //MFT_INPUT_STREAM_DOES_NOT_ADDREF; + + info.cbSize = 0; //input size is variable + info.cbAlignment = 0; //no specific alignment requirements + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetOutputStreamInfo( + DWORD dwOutputStreamID, + MFT_OUTPUT_STREAM_INFO* pStreamInfo) +{ + if (pStreamInfo == 0) + return E_POINTER; + + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + Lock lock; + + const HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + MFT_OUTPUT_STREAM_INFO& info = *pStreamInfo; + + //enum _MFT_OUTPUT_STREAM_INFO_FLAGS + // { MFT_OUTPUT_STREAM_WHOLE_SAMPLES = 0x1, + // MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER = 0x2, + // MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE = 0x4, + // MFT_OUTPUT_STREAM_DISCARDABLE = 0x8, + // MFT_OUTPUT_STREAM_OPTIONAL = 0x10, + // MFT_OUTPUT_STREAM_PROVIDES_SAMPLES = 0x100, + // MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES = 0x200, + // MFT_OUTPUT_STREAM_LAZY_READ = 0x400, + // MFT_OUTPUT_STREAM_REMOVABLE = 0x800 + // }; + + //see Decoder sample in the SDK + //decoder.cc + + //The API says that the only flag that is meaningful prior to SetOutputType + //is the OPTIONAL flag. We need the frame dimensions, and the stride, + //in order to calculte the cbSize value. + + info.dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | + MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | + MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; + + FrameSize size; + + info.cbSize = GetOutputBufferSize(size); + info.cbAlignment = 0; + + return S_OK; +} + + +DWORD WebmMfVp8Dec::GetOutputBufferSize(FrameSize& s) const +{ + //MFT was already locked by caller + + //TODO: for now, assume width and height are specified + //via the input media type. + + if (m_pInputMediaType == 0) + return 0; + + HRESULT hr = MFGetAttributeSize( + m_pInputMediaType, + MF_MT_FRAME_SIZE, + &s.width, + &s.height); + + assert(SUCCEEDED(hr)); + + const DWORD w = s.width; + assert(w); + + const DWORD h = s.height; + assert(h); + + const DWORD cb = w*h + 2*(((w+1)/2)*((h+1)/2)); + + //TODO: this result does not account for stride + + return cb; +} + + +HRESULT WebmMfVp8Dec::GetAttributes(IMFAttributes** pp) +{ + if (pp) + *pp = 0; + + return E_NOTIMPL; +} + + +HRESULT WebmMfVp8Dec::GetInputStreamAttributes( + DWORD, + IMFAttributes** pp) +{ + if (pp) + *pp = 0; + + return E_NOTIMPL; +} + + +HRESULT WebmMfVp8Dec::GetOutputStreamAttributes( + DWORD, + IMFAttributes** pp) +{ + if (pp) + *pp = 0; + + return E_NOTIMPL; +} + + +HRESULT WebmMfVp8Dec::DeleteInputStream(DWORD) +{ + return E_NOTIMPL; +} + + +HRESULT WebmMfVp8Dec::AddInputStreams( + DWORD, + DWORD*) +{ + return E_NOTIMPL; +} + + +HRESULT WebmMfVp8Dec::GetInputAvailableType( + DWORD dwInputStreamID, + DWORD dwTypeIndex, + IMFMediaType** pp) +{ + if (pp) + *pp = 0; + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (dwTypeIndex > 0) + return MF_E_NO_MORE_TYPES; + + if (pp == 0) + return E_POINTER; + + IMFMediaType*& pmt = *pp; + + HRESULT hr = MFCreateMediaType(&pmt); + assert(SUCCEEDED(hr)); + assert(pmt); + + hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + assert(SUCCEEDED(hr)); + + hr = pmt->SetGUID(MF_MT_SUBTYPE, WebmTypes::MEDIASUBTYPE_VP80); + assert(SUCCEEDED(hr)); + + hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, FALSE); + assert(SUCCEEDED(hr)); + + hr = pmt->SetUINT32(MF_MT_COMPRESSED, TRUE); + assert(SUCCEEDED(hr)); + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetOutputAvailableType( + DWORD dwOutputStreamID, + DWORD dwTypeIndex, + IMFMediaType** pp) +{ + if (pp) + *pp = 0; + + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + enum { subtype_count = 3 }; + + if (dwTypeIndex >= subtype_count) + return MF_E_NO_MORE_TYPES; + + if (pp == 0) + return E_POINTER; + + IMFMediaType*& pmt = *pp; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + hr = MFCreateMediaType(&pmt); + assert(SUCCEEDED(hr)); + assert(pmt); + + hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + assert(SUCCEEDED(hr)); + + const GUID subtypes[subtype_count] = + { + MFVideoFormat_NV12, + MFVideoFormat_YV12, + MFVideoFormat_IYUV + }; + + const GUID& subtype = subtypes[dwTypeIndex]; + + hr = pmt->SetGUID(MF_MT_SUBTYPE, subtype); + assert(SUCCEEDED(hr)); + + hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + assert(SUCCEEDED(hr)); + + hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); + assert(SUCCEEDED(hr)); + + if (m_pInputMediaType == 0) //nothing else we can say + return S_OK; + + FrameRate r; + + hr = r.AssignFrom(m_pInputMediaType); + + if (hr == S_OK) //means attribute was set, and its value is valid + { + //odbgstream os; + //os << "\nvp8dec: num=" << r.m_numerator + // << " den=" << r.m_denominator; + // + //const float rate = float(r.m_numerator) / float(r.m_denominator); + //os << rate << '\n' << endl; + + //TODO: a simpler way to handle this is to copy + //an item directly from media type to media type (assuming + //the MF API provides such a function), instead of going + //through this intermediate FrameRate value. + + hr = r.CopyTo(pmt); + assert(SUCCEEDED(hr)); + } + + FrameSize s; + + hr = MFGetAttributeSize( + m_pInputMediaType, + MF_MT_FRAME_SIZE, + &s.width, + &s.height); + + assert(SUCCEEDED(hr)); + assert(s.width); + assert(s.height); + + hr = MFSetAttributeSize( + pmt, + MF_MT_FRAME_SIZE, + s.width, + s.height); + + assert(SUCCEEDED(hr)); + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::SetInputType( + DWORD dwInputStreamID, + IMFMediaType* pmt, + DWORD dwFlags) +{ + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (pmt == 0) + { + //TODO: disallow this case while we're playing? + + if (m_pInputMediaType) + { + const ULONG n = m_pInputMediaType->Release(); + n; + assert(n == 0); + + m_pInputMediaType = 0; + + const vpx_codec_err_t e = vpx_codec_destroy(&m_ctx); + e; + assert(e == VPX_CODEC_OK); + } + + if (m_pOutputMediaType) + { + const ULONG n = m_pOutputMediaType->Release(); + n; + assert(n == 0); + + m_pOutputMediaType = 0; + } + + return S_OK; + } + + //TODO: handle the case when already have an input media type + //or output media type, or are already playing. I don't + //think we can change media types while we're playing. + + GUID g; + + hr = pmt->GetMajorType(&g); + + if (FAILED(hr)) + return MF_E_INVALIDMEDIATYPE; + + if (g != MFMediaType_Video) + return MF_E_INVALIDMEDIATYPE; + + hr = pmt->GetGUID(MF_MT_SUBTYPE, &g); + + if (FAILED(hr)) + return MF_E_INVALIDMEDIATYPE; + + if (g != WebmTypes::MEDIASUBTYPE_VP80) + return MF_E_INVALIDMEDIATYPE; + + //hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); + //assert(SUCCEEDED(hr)); + + FrameRate r; + + hr = r.AssignFrom(pmt); + + if (FAILED(hr)) //input mt has framerate, but it's bad + return hr; + + r.CopyTo(m_frame_rate); + + //odbgstream os; + //os << "\nvp8dec: framerate=" << m_frame_rate << '\n' << endl; + + FrameSize s; + + hr = MFGetAttributeSize( + pmt, + MF_MT_FRAME_SIZE, + &s.width, + &s.height); + + if (FAILED(hr)) + return MF_E_INVALIDMEDIATYPE; + + if (s.width == 0) + return MF_E_INVALIDMEDIATYPE; + + if (s.width % 2) //TODO + return MF_E_INVALIDMEDIATYPE; + + if (s.height == 0) + return MF_E_INVALIDMEDIATYPE; + + //TODO: do we need to check for odd height too? + + if (dwFlags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + if (m_pInputMediaType) + { + hr = m_pInputMediaType->DeleteAllItems(); + assert(SUCCEEDED(hr)); + + const vpx_codec_err_t e = vpx_codec_destroy(&m_ctx); + e; + assert(e == VPX_CODEC_OK); + } + else + { + hr = MFCreateMediaType(&m_pInputMediaType); + + if (FAILED(hr)) + return hr; + } + + hr = pmt->CopyAllItems(m_pInputMediaType); + + if (FAILED(hr)) + return hr; + + //m_frame_rate = r; + + //TODO: should this really be done here? + + vpx_codec_iface_t& vp8 = vpx_codec_vp8_dx_algo; + + const int flags = 0; //TODO: VPX_CODEC_USE_POSTPROC; + + const vpx_codec_err_t err = vpx_codec_dec_init( + &m_ctx, + &vp8, + 0, + flags); + + if (err == VPX_CODEC_MEM_ERROR) + return E_OUTOFMEMORY; + + if (err != VPX_CODEC_OK) + return E_FAIL; + + //const HRESULT hr = OnApplyPostProcessing(); + + if (m_pOutputMediaType) + { + //TODO: Is this the correct behavior? + + const ULONG n = m_pOutputMediaType->Release(); + n; + assert(n == 0); + + m_pOutputMediaType = 0; + } + +#if 0 + //TODO: + //We could update the preferred ("available") output media types, + //now that we know the frame rate and frame size, etc. + + hr = MFCreateMediaType(&m_pOutputMediaType); + assert(SUCCEEDED(hr)); //TODO + + hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + assert(SUCCEEDED(hr)); //TODO + + hr = pmt->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12); + assert(SUCCEEDED(hr)); //TODO + + hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + assert(SUCCEEDED(hr)); //TODO + + hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); + assert(SUCCEEDED(hr)); //TODO + + if (r.numerator) //means "has been set" + { + hr = MFSetAttributeRatio( + m_pOutputMediaType, + MF_MT_FRAME_RATE, + r.numerator, + r.denominator); + + assert(SUCCEEDED(hr)); //TODO + } + + hr = MFSetAttributeSize( + m_pOutputMediaType, + MF_MT_FRAME_SIZE, + s.width, + s.height); + + assert(SUCCEEDED(hr)); //TODO +#endif + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::SetOutputType( + DWORD dwOutputStreamID, + IMFMediaType* pmt, + DWORD dwFlags) +{ + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (pmt == 0) + { + //TODO: disallow this case while we're playing? + + if (m_pOutputMediaType) + { + const ULONG n = m_pOutputMediaType->Release(); + n; + assert(n == 0); + + m_pOutputMediaType = 0; + } + + return S_OK; + } + + if (m_pInputMediaType == 0) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + GUID g; + + hr = pmt->GetMajorType(&g); + + if (FAILED(hr)) + return MF_E_INVALIDMEDIATYPE; + + if (g != MFMediaType_Video) + return MF_E_INVALIDMEDIATYPE; + + hr = pmt->GetGUID(MF_MT_SUBTYPE, &g); + + if (FAILED(hr)) + return MF_E_INVALIDMEDIATYPE; + + if (g == MFVideoFormat_NV12) + __noop; + else if (g == MFVideoFormat_YV12) + __noop; + else if (g == MFVideoFormat_IYUV) + __noop; + else //TODO: add I420 support + return MF_E_INVALIDMEDIATYPE; + + //hr = pmt->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + //assert(SUCCEEDED(hr)); + + //hr = pmt->SetUINT32(MF_MT_COMPRESSED, FALSE); + //assert(SUCCEEDED(hr)); + + FrameRate r_out; + + hr = r_out.AssignFrom(pmt); + + if (FAILED(hr)) //output type has value, but it's not valid + return hr; + + if (hr == S_OK) //output type has value, and it's valid + { + FrameRate r_in; + + hr = r_in.AssignFrom(m_pInputMediaType); + + if ((hr == S_OK) && !r_in.Match(r_out)) + return MF_E_INVALIDMEDIATYPE; + } + + FrameSize s_out; + + hr = MFGetAttributeSize( + pmt, + MF_MT_FRAME_SIZE, + &s_out.width, + &s_out.height); + + if (SUCCEEDED(hr)) + { + if (s_out.width == 0) + return MF_E_INVALIDMEDIATYPE; + + //TODO: check whether width is odd + + if (s_out.height == 0) + return MF_E_INVALIDMEDIATYPE; + + FrameSize s_in; + + hr = MFGetAttributeSize( + m_pInputMediaType, + MF_MT_FRAME_SIZE, + &s_in.width, + &s_in.height); + + assert(SUCCEEDED(hr)); + + if (s_out.width != s_in.width) + return MF_E_INVALIDMEDIATYPE; + + if (s_out.height != s_in.height) + return MF_E_INVALIDMEDIATYPE; + } + + if (dwFlags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + if (m_pOutputMediaType) + { + hr = m_pOutputMediaType->DeleteAllItems(); + assert(SUCCEEDED(hr)); + } + else + { + hr = MFCreateMediaType(&m_pOutputMediaType); + + if (FAILED(hr)) + return hr; + } + + hr = pmt->CopyAllItems(m_pOutputMediaType); + + if (FAILED(hr)) + return hr; + + //TODO: do something + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetInputCurrentType( + DWORD dwInputStreamID, + IMFMediaType** pp) +{ + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (pp) + *pp = 0; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_pInputMediaType == 0) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + IMFMediaType*& p = *pp; + + hr = MFCreateMediaType(&p); + + if (FAILED(hr)) + return hr; + + return m_pInputMediaType->CopyAllItems(p); +} + + +HRESULT WebmMfVp8Dec::GetOutputCurrentType( + DWORD dwOutputStreamID, + IMFMediaType** pp) +{ + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (pp) + *pp = 0; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + //TODO: synthesize from input media type? + + if (m_pOutputMediaType == 0) //TODO: liberalize? + return MF_E_TRANSFORM_TYPE_NOT_SET; + + IMFMediaType*& p = *pp; + + hr = MFCreateMediaType(&p); + + if (FAILED(hr)) + return hr; + + return m_pOutputMediaType->CopyAllItems(p); +} + + +HRESULT WebmMfVp8Dec::GetInputStatus( + DWORD dwInputStreamID, + DWORD* pdwFlags) +{ + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_pInputMediaType == 0) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + //TODO: check output media type too? + + if (pdwFlags == 0) + return E_POINTER; + + DWORD& dwFlags = *pdwFlags; + + dwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; //because we always queue + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetOutputStatus(DWORD*) +{ +#if 1 + return E_NOTIMPL; +#else + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_pInputMediaType == 0) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + //TODO: check output media type too? + + if (pdwFlags == 0) + return E_POINTER; + + DWORD& dwFlags = *pdwFlags; + + const vpx_image_t* const f = vpx_codec_get_frame(&m_ctx, &m_iter); + + dwFlags = f ? MFT_OUTPUT_STATUS_SAMPLE_READY : 0; + + //TODO: alternatively, we could return E_NOTIMPL, which + //forces client to call ProcessOutput to determine whether + //a sample is ready. + + return S_OK; +#endif +} + + +HRESULT WebmMfVp8Dec::SetOutputBounds( + LONGLONG /* hnsLowerBound */ , + LONGLONG /* hnsUpperBound */ ) +{ + return E_NOTIMPL; //TODO +} + + +HRESULT WebmMfVp8Dec::ProcessEvent( + DWORD dwInputStreamID, + IMFMediaEvent* /* pEvent */ ) +{ + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + +#if 0 //def _DEBUG + if (pEvent) + { + MediaEventType t; + + HRESULT hr = pEvent->GetType(&t); + assert(SUCCEEDED(hr)); + + odbgstream os; + os << "WebmMfVp8Dec::ProcessEvent: type=" << t << endl; + } +#endif + + return E_NOTIMPL; //TODO +} + + +HRESULT WebmMfVp8Dec::ProcessMessage( + MFT_MESSAGE_TYPE m, + ULONG_PTR) +{ +#if 0 //def _DEBUG + odbgstream os; + os << "WebmMfVp8Dec::ProcessMessage(samples.size=" + << m_samples.size() << "): "; +#endif + + switch (m) + { + case MFT_MESSAGE_COMMAND_FLUSH: +#if 0 //def _DEBUG + os << "COMMAND_FLUSH" << endl; +#endif + + //http://msdn.microsoft.com/en-us/library/dd940419%28v=VS.85%29.aspx + + Flush(); + return S_OK; + + case MFT_MESSAGE_COMMAND_DRAIN: +#if 0 //def _DEBUG + os << "COMMAND_DRAIN" << endl; +#endif + + //http://msdn.microsoft.com/en-us/library/dd940418%28v=VS.85%29.aspx + + //TODO: input stream does not accept input in the MFT processes all + //data from previous calls to ProcessInput. + + return S_OK; + + case MFT_MESSAGE_SET_D3D_MANAGER: +#if 0 //def _DEBUG + os << "SET_D3D" << endl; +#endif + + return S_OK; + + case MFT_MESSAGE_DROP_SAMPLES: +#if 0 //def _DEBUG + os << "DROP_SAMPLES" << endl; +#endif + + return S_OK; + + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: +#if 0 //def _DEBUG + os << "NOTIFY_BEGIN_STREAMING" << endl; +#endif + + //http://msdn.microsoft.com/en-us/library/dd940421%28v=VS.85%29.aspx + + //TODO: init decoder library here, instead of during SetInputType + + return S_OK; + + case MFT_MESSAGE_NOTIFY_END_STREAMING: +#if 0 //def _DEBUG + os << "NOTIFY_END_STREAMING" << endl; +#endif + + //http://msdn.microsoft.com/en-us/library/dd940423%28v=VS.85%29.aspx + + //NOTE: flush is not performed here + + return S_OK; + + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: +#if 0 //def _DEBUG + os << "NOTIFY_EOS" << endl; +#endif + + //http://msdn.microsoft.com/en-us/library/dd940422%28v=VS.85%29.aspx + + //TODO: set discontinuity flag on first sample after this + + return S_OK; + + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: +#if 0 //def _DEBUG + os << "NOTIFY_START_OF_STREAM" << endl; +#endif + + return S_OK; + + case MFT_MESSAGE_COMMAND_MARKER: +#if 0 //def _DEBUG + os << "COMMAND_MARKER" << endl; +#endif + + return S_OK; + + default: + return S_OK; + } +} + + +HRESULT WebmMfVp8Dec::ProcessInput( + DWORD dwInputStreamID, + IMFSample* pSample, + DWORD) +{ + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (pSample == 0) + return E_INVALIDARG; + + DWORD count; + + HRESULT hr = pSample->GetBufferCount(&count); + assert(SUCCEEDED(hr)); + + if (count == 0) //weird + return S_OK; + + //if (count > 1) + // return E_INVALIDARG; + + //TODO: check duration + //TODO: check timestamp + + Lock lock; + + hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_pInputMediaType == 0) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (m_pOutputMediaType == 0) //TODO: need this check here? + return MF_E_TRANSFORM_TYPE_NOT_SET; + + pSample->AddRef(); + + m_samples.push_back(SampleInfo()); + + SampleInfo& i = m_samples.back(); + + i.pSample = pSample; + i.dwBuffer = 0; + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::ProcessOutput( + DWORD dwFlags, + DWORD cOutputBufferCount, + MFT_OUTPUT_DATA_BUFFER* pOutputSamples, + DWORD* pdwStatus) +{ + if (pdwStatus) + *pdwStatus = 0; + + if (dwFlags) + return E_INVALIDARG; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_pInputMediaType == 0) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (m_pOutputMediaType == 0) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (cOutputBufferCount == 0) + return E_INVALIDARG; + + if (pOutputSamples == 0) + return E_INVALIDARG; + + const MFT_OUTPUT_DATA_BUFFER& data = pOutputSamples[0]; + //data.dwStreamID should equal 0, but we ignore it + + for (;;) + { + hr = Decode(data.pSample); + + if (FAILED(hr) || (hr == S_OK)) + return hr; + } +} + + +HRESULT WebmMfVp8Dec::Decode(IMFSample* pSample_out) +{ + if (pSample_out == 0) + return E_INVALIDARG; + + if (m_samples.empty()) + return MF_E_TRANSFORM_NEED_MORE_INPUT; + + HRESULT hr; + LONGLONG time, duration; + + { + SampleInfo& i = m_samples.front(); + assert(i.pSample); + + UINT32 iKey; + + hr = i.pSample->GetUINT32(MFSampleExtension_CleanPoint, &iKey); + + const bool bKey = SUCCEEDED(hr) && (iKey != FALSE); + + //TODO: this is an optimization oppurtunity, since it would + //optimize-away the expense of decoding too (not the expense + //of mere rendering, which is what other modes optimize away). + //It's tricky to implement since if we + //transition OUT of DROP_5 mode, then it's possible that we + //we're in the middle of a GOP and so we cannot decode + //again until we see the next keyframe. In other modes, we + //decode every frame (we are suppressing the rendering, not + //decode), so a state transition between modes is not an issue. + //For now, just decompress every frame. + // + //if (!bKey && (m_drop_mode == MF_DROP_MODE_5)) + //{ + // i.pSample->Release(); + // i.pSample = 0; + // + // m_samples.pop_front(); + // + // return S_FALSE; + //} + //END TODO. + + UINT32 bPreroll; + + hr = i.pSample->GetUINT32(WebmTypes::WebMSample_Preroll, &bPreroll); + + if (SUCCEEDED(hr) && (bPreroll != FALSE)) //preroll frame + { + hr = i.DecodeAll(m_ctx); + + i.pSample->Release(); + i.pSample = 0; + + m_samples.pop_front(); + + if (FAILED(hr)) //bad decode + return hr; + + ReplenishDropBudget(); + return S_FALSE; //throw away this sample + } + + hr = i.DecodeOne(m_ctx, time, duration); + + if (FAILED(hr) || (hr != S_OK)) + { + i.pSample->Release(); + i.pSample = 0; + + m_samples.pop_front(); + + if (FAILED(hr)) //bad decode + return hr; + } + + if (m_drop_mode == MF_DROP_MODE_NONE) + __noop; //we're not throwing away frames + + else if (m_drop_mode >= MF_DROP_MODE_5) + { + if (!bKey) + return S_FALSE; //throw away this sample + } + else if (bKey || (m_drop_budget > 1)) + --m_drop_budget; //spend out budget to render this frame + + else + { + ReplenishDropBudget(); + return S_FALSE; //throw away this sample + } + } + + DWORD count_out; + + hr = pSample_out->GetBufferCount(&count_out); + + if (SUCCEEDED(hr) && (count_out != 1)) //TODO: handle this? + return E_INVALIDARG; + + IMFMediaBufferPtr buf_out; + + hr = pSample_out->GetBufferByIndex(0, &buf_out); + + if (FAILED(hr) || !bool(buf_out)) + return E_INVALIDARG; + + GUID subtype; + + hr = m_pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &subtype); + assert(SUCCEEDED(hr)); + + //TODO: + //MFVideoFormat_I420 + + FrameSize frame_size; + + const DWORD cbFrameLen = GetOutputBufferSize(frame_size); + assert(cbFrameLen > 0); + + //The sequence of querying for the output buffer is described on + //the page "Uncompressed Video Buffers". + //http://msdn.microsoft.com/en-us/library/aa473821%28v=VS.85%29.aspx + + IMF2DBuffer* buf2d_out; + + hr = buf_out->QueryInterface(&buf2d_out); + + if (SUCCEEDED(hr)) + { + assert(buf2d_out); + + BYTE* ptr_out; + LONG stride_out; + + hr = buf2d_out->Lock2D(&ptr_out, &stride_out); + assert(SUCCEEDED(hr)); + assert(ptr_out); + assert(stride_out > 0); //top-down DIBs are positive, right? + + const HRESULT hrGetFrame = GetFrame(ptr_out, stride_out, subtype); + + //TODO: set output buffer length? + + hr = buf2d_out->Unlock2D(); + assert(SUCCEEDED(hr)); + + buf2d_out->Release(); + buf2d_out = 0; + + if (FAILED(hrGetFrame) || (hrGetFrame != S_OK)) + return hrGetFrame; + } + else + { + BYTE* ptr_out; + DWORD cbMaxLen; + + hr = buf_out->Lock(&ptr_out, &cbMaxLen, 0); + assert(SUCCEEDED(hr)); + assert(ptr_out); + assert(cbMaxLen >= cbFrameLen); + + //TODO: verify stride of output buffer + //The page "Uncompressed Video Buffers" here: + //http://msdn.microsoft.com/en-us/library/aa473821%28v=VS.85%29.aspx + //explains how to calculate the "minimum stride": + // MF_MT_DEFAULT_STRIDE + // or, MFGetStrideForBitmapInfoHeader + // or, calculate it yourself + + INT32 stride_out; + + hr = pSample_out->GetUINT32( + MF_MT_DEFAULT_STRIDE, + (UINT32*)&stride_out); + + if (SUCCEEDED(hr) && (stride_out != 0)) + assert(stride_out > 0); + else + { + const DWORD fcc = subtype.Data1; + + const DWORD w = frame_size.width; + LONG stride_out_; + + hr = MFGetStrideForBitmapInfoHeader(fcc, w, &stride_out_); + + if (SUCCEEDED(hr) && (stride_out_ != 0)) + { + assert(stride_out_ > 0); + stride_out = stride_out_; + } + else + { + assert((w % 2) == 0); //TODO + stride_out = w; //TODO: is this correct??? + } + } + + const HRESULT hrGetFrame = GetFrame(ptr_out, stride_out, subtype); + + hr = buf_out->Unlock(); + assert(SUCCEEDED(hr)); + + if (FAILED(hrGetFrame) || (hrGetFrame != S_OK)) + return hrGetFrame; + + hr = buf_out->SetCurrentLength(cbFrameLen); + assert(SUCCEEDED(hr)); + } + + if (time >= 0) + { + hr = pSample_out->SetSampleTime(time); + assert(SUCCEEDED(hr)); + } + + if (duration >= 0) + { + hr = pSample_out->SetSampleDuration(duration); + assert(SUCCEEDED(hr)); + } + + return S_OK; //done +} + + +HRESULT WebmMfVp8Dec::GetFrame( + BYTE* pOutBuf, + ULONG strideOut, + const GUID& subtype) +{ + assert(pOutBuf); + assert(strideOut); + assert((strideOut % 2) == 0); //TODO: resolve this issue + + vpx_codec_iter_t iter = 0; + + const vpx_image_t* const f = vpx_codec_get_frame(&m_ctx, &iter); + + if (f == 0) //alt-ref + return S_FALSE; //tell caller to pop this buffer and call me back + + //Y + + const BYTE* pInY = f->planes[VPX_PLANE_Y]; + assert(pInY); + + unsigned int wIn = f->d_w; + unsigned int hIn = f->d_h; + + BYTE* pOut = pOutBuf; + + const int strideInY = f->stride[VPX_PLANE_Y]; + + for (unsigned int y = 0; y < hIn; ++y) + { + memcpy(pOut, pInY, wIn); + pInY += strideInY; + pOut += strideOut; + } + + 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]; + + wIn = (wIn + 1) / 2; + hIn = (hIn + 1) / 2; + + if (subtype == MFVideoFormat_NV12) + { + for (unsigned int y = 0; y < hIn; ++y) + { + const BYTE* u = pInU; //src + const BYTE* v = pInV; //src + BYTE* uv = pOut; //dst + + for (unsigned int idx = 0; idx < wIn; ++idx) //uv + { + *uv++ = *u++; //Cb + *uv++ = *v++; //Cr + } + + pInU += strideInU; + pInV += strideInV; + pOut += strideOut; + } + } + else if (subtype == MFVideoFormat_YV12) + { + strideOut /= 2; + + //V + + for (unsigned int y = 0; y < hIn; ++y) + { + memcpy(pOut, pInV, wIn); + pInV += strideInV; + pOut += strideOut; + } + + //U + + for (unsigned int y = 0; y < hIn; ++y) + { + memcpy(pOut, pInU, wIn); + pInU += strideInU; + pOut += strideOut; + } + } + else + { + assert(subtype == MFVideoFormat_IYUV); + + strideOut /= 2; + + //U + + for (unsigned int y = 0; y < hIn; ++y) + { + memcpy(pOut, pInU, wIn); + pInU += strideInU; + pOut += strideOut; + } + + //V + + for (unsigned int y = 0; y < hIn; ++y) + { + memcpy(pOut, pInV, wIn); + pInV += strideInV; + pOut += strideOut; + } + } + + const vpx_image_t* const f2 = vpx_codec_get_frame(&m_ctx, &iter); + f2; + assert(f2 == 0); + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::SetRate(BOOL bThin, float rate) +{ + //odbgstream os; + //os << "WebmMfVp8Dec::SetRate: bThin=" + // << boolalpha << (bThin ? true : false) + // << " rate=" + // << rate + // << endl; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + //if (m_pEvents == 0) + // return MF_E_SHUTDOWN; + + //if (bThin) + // return MF_E_THINNING_UNSUPPORTED; //TODO + + if (rate < 0) + return MF_E_REVERSE_UNSUPPORTED; //TODO + + m_rate = rate; + m_bThin = bThin; + + //reset drop mode when rate change + + m_lag_sum = 0; + m_lag_count = 0; + m_lag_frames = 0; + + hr = WebmMfVp8Dec::SetDropMode(MF_DROP_MODE_NONE); + assert(SUCCEEDED(hr)); + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetRate(BOOL* pbThin, float* pRate) +{ + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + //if (m_pEvents == 0) + // return MF_E_SHUTDOWN; + + if (pbThin) + *pbThin = m_bThin; + + if (pRate) //return error when pRate ptr is NULL? + *pRate = m_rate; + + //odbgstream os; + //os << "WebmMfVp8Dec::GetRate: bThin=" + // << boolalpha << m_bThin + // << " rate=" + // << m_rate + // << endl; + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetSlowestRate( + MFRATE_DIRECTION d, + BOOL /* bThin */ , + float* pRate) +{ + //odbgstream os; + //os << "WebmMfVp8Dec::GetSlowestRate" << endl; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + //if (m_pEvents == 0) + // return MF_E_SHUTDOWN; + + if (d == MFRATE_REVERSE) + return MF_E_REVERSE_UNSUPPORTED; //TODO + + //if (bThin) + // return MF_E_THINNING_UNSUPPORTED; //TODO + + if (pRate == 0) + return E_POINTER; + + float& r = *pRate; + r = 0; //? + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetFastestRate( + MFRATE_DIRECTION d, + BOOL /* bThin */ , + float* pRate) +{ + //odbgstream os; + //os << "WebmMfSource::GetFastestRate" << endl; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + //if (m_pEvents == 0) + // return MF_E_SHUTDOWN; + + if (d == MFRATE_REVERSE) + return MF_E_REVERSE_UNSUPPORTED; //TODO + + //if (bThin) + // return MF_E_THINNING_UNSUPPORTED; //TODO + + if (pRate == 0) + return E_POINTER; + + float& r = *pRate; + r = 64; //? + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::IsRateSupported( + BOOL /* bThin */ , + float rate, + float* pNearestRate) +{ + //odbgstream os; + //os << "WebmMfVp8Dec::IsRateSupported: rate=" << rate << endl; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + //if (m_pEvents == 0) + // return MF_E_SHUTDOWN; + + //if (bThin) + // return MF_E_THINNING_UNSUPPORTED; //TODO + + if (rate < 0) + return MF_E_REVERSE_UNSUPPORTED; //TODO + + //float int_part; + //const float frac_part = modf(rate, &int_part); + + if (pNearestRate) + *pNearestRate = rate; + + return S_OK; //TODO +} + + +HRESULT WebmMfVp8Dec::GetService( + REFGUID sid, + REFIID iid, + LPVOID* ppv) +{ + if (sid == MF_RATE_CONTROL_SERVICE) + return WebmMfVp8Dec::QueryInterface(iid, ppv); + + if (ppv) + *ppv = 0; + + return MF_E_UNSUPPORTED_SERVICE; +} + + +void WebmMfVp8Dec::Flush() +{ + while (!m_samples.empty()) + { + SampleInfo& i = m_samples.front(); + assert(i.pSample); + + i.pSample->Release(); + i.pSample = 0; + + m_samples.pop_front(); + } +} + + +HRESULT WebmMfVp8Dec::SampleInfo::DecodeAll(vpx_codec_ctx_t& ctx) +{ + assert(pSample); + + DWORD count; + + HRESULT hr = pSample->GetBufferCount(&count); + assert(SUCCEEDED(hr)); + assert(count > 0); + + while (dwBuffer < count) + { + IMFMediaBufferPtr buf; + + hr = pSample->GetBufferByIndex(dwBuffer, &buf); + assert(SUCCEEDED(hr)); + assert(buf); + + BYTE* ptr; + DWORD len; + + hr = buf->Lock(&ptr, 0, &len); + assert(SUCCEEDED(hr)); + assert(ptr); + assert(len); + + const vpx_codec_err_t e = vpx_codec_decode(&ctx, ptr, len, 0, 0); + + hr = buf->Unlock(); + assert(SUCCEEDED(hr)); + + if (e != VPX_CODEC_OK) + return MF_E_INVALID_STREAM_DATA; + + ++dwBuffer; //consume this buffer + } + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::SampleInfo::DecodeOne( + vpx_codec_ctx_t& ctx, + LONGLONG& time, + LONGLONG& duration) +{ + assert(pSample); + + DWORD count; + + HRESULT hr = pSample->GetBufferCount(&count); + assert(SUCCEEDED(hr)); + assert(count > 0); + assert(count > dwBuffer); + + IMFMediaBufferPtr buf; + + hr = pSample->GetBufferByIndex(dwBuffer, &buf); + assert(SUCCEEDED(hr)); + assert(buf); + + BYTE* ptr; + DWORD len; + + hr = buf->Lock(&ptr, 0, &len); + assert(SUCCEEDED(hr)); + assert(ptr); + assert(len); + + const vpx_codec_err_t e = vpx_codec_decode(&ctx, ptr, len, 0, 0); + + hr = buf->Unlock(); + assert(SUCCEEDED(hr)); + + if (e != VPX_CODEC_OK) + return MF_E_INVALID_STREAM_DATA; + + LONGLONG t; + + hr = pSample->GetSampleTime(&t); + + if (FAILED(hr) || (t < 0)) + { + time = -1; + duration = -1; + } + else + { + LONGLONG d; + + hr = pSample->GetSampleDuration(&d); + + if (FAILED(hr) || (d <= 0)) + { + if (dwBuffer == 0) //first buffer + time = t; + else + time = -1; + + duration = -1; + } + else + { + duration = d / count; + time = t + dwBuffer * duration; + } + } + + ++dwBuffer; //consume this buffer + return (dwBuffer >= count) ? S_FALSE : S_OK; +} + + +HRESULT WebmMfVp8Dec::SetDropMode(MF_QUALITY_DROP_MODE d) +{ + if (d > MF_DROP_MODE_5) + return MF_E_NO_MORE_DROP_MODES; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + m_drop_mode = d; + m_drop_budget = 0; + + //odbgstream os; + //os << "SetDropMode=" << m_drop_mode << endl; + + return S_OK; + + //we always keep keyframes ("cleanpoint") + //1: drop 1 out of every 2 frames + //2: drop 2 out of every 3 frames + //3: drop 3 out of every 4 frames + //4: drop 4 out of every 5 frames + //5: drop all delta frames + // + //alternatively: + //1: drop 1 out of every 16 = 2^4 = 2^(5-1) + //2: drop 1 out of every 8 = 2^3 = 2^(5-2) = 2 out of every 16 + //3: drop 1 out of every 4 = 2^2 = 2^(5-3) = 4 out of every 16 + //4: drop 1 out of every 2 = 2^1 = 2^(5-4) = 8 out of every 16 + //5: drop all delta frames + + //when we detect a keyframe, this resets the counter + // + //level 1: + //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 + // x + // + //level 2: + //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 + // x x + // + //level 3: + //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 + // x x x x x + // + //level 4: + //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 + // x x x x x x x x x x + // + //we could also tie this to framerate, e.g. + //1: drop 1 frame per second + //2: drop 1 frame per 1/2 sec + //3: drop 1 frame per 1/4 sec + //4: drop 1 frame per 1/8 sec + //5: drop all delta frames + // + //Give outselves a budget: the smaller the drop mode, + //the larger the budget. Drop the first eligrable + //frame when our budget drops to 0. +} + + +void WebmMfVp8Dec::ReplenishDropBudget() +{ + if (m_drop_mode == MF_DROP_MODE_NONE) + { + m_drop_budget = 0; //doesn't really matter + return; + } + + if (m_drop_mode >= MF_DROP_MODE_5) + { + m_drop_budget = 0; //doesn't really matter + return; + } + + const int d = m_drop_mode; + assert(d >= 1); + assert(d <= 4); + + m_drop_budget = (1 << (5 - d)); +} + + +HRESULT WebmMfVp8Dec::SetQualityLevel(MF_QUALITY_LEVEL q) +{ + if (q == MF_QUALITY_NORMAL) + return S_OK; + + return MF_E_NO_MORE_QUALITY_LEVELS; +} + + +HRESULT WebmMfVp8Dec::GetDropMode(MF_QUALITY_DROP_MODE* p) +{ + if (p == 0) + return E_POINTER; + + Lock lock; + + HRESULT hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + MF_QUALITY_DROP_MODE& d = *p; + d = m_drop_mode; + + //odbgstream os; + //os << "GetDropMode=" << m_drop_mode << endl; + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::GetQualityLevel(MF_QUALITY_LEVEL* p) +{ + if (p == 0) + return E_POINTER; + + MF_QUALITY_LEVEL& q = *p; + q = MF_QUALITY_NORMAL; //no post-processing for us to shut off + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::DropTime(LONGLONG t) +{ + t; + return MF_E_DROPTIME_NOT_SUPPORTED; +} + + +HRESULT WebmMfVp8Dec::NotifyQualityEvent(IMFMediaEvent* pe, DWORD* pdw) +{ + if (pe == 0) + return E_INVALIDARG; + + if (pdw == 0) + return E_POINTER; + + DWORD& dw = *pdw; + dw = 0; //TODO: possibly set this to MF_QUALITY_CANNOT_KEEP_UP + + MediaEventType type; + + HRESULT hr = pe->GetType(&type); + + if (FAILED(hr)) + return hr; + + if (type != MEQualityNotify) + return E_INVALIDARG; + + GUID ext; + + hr = pe->GetExtendedType(&ext); + + if (FAILED(hr)) + return hr; + + if (ext != MF_QUALITY_NOTIFY_SAMPLE_LAG) + return S_OK; + + PROPVARIANT v; + + hr = pe->GetValue(&v); + + if (FAILED(hr)) + return E_INVALIDARG; + + if (v.vt != VT_I8) + return E_INVALIDARG; + + const LONGLONG reftime = v.hVal.QuadPart; + + //MEQualityNotify Event + //http://msdn.microsoft.com/en-us/library/ms694893(VS.85).aspx + + //negative is good - frames are early + //positive is bad - frames are late + //value is a lateness - time in 100ns units + //we're seeing extreme lateness: 2 or 3s + + //if (ext == MF_QUALITY_NOTIFY_PROCESSING_LATENCY) + // return S_OK; + + Lock lock; + + hr = lock.Seize(this); + + if (FAILED(hr)) + return hr; + + if (m_frame_rate <= 0) //no framerate available + return S_OK; //TODO: for now, don't bother trying to drop + + if (m_bThin || (m_rate != 1)) //bThin = true implies drop level 5 already + return S_OK; + + if (reftime <= 0) //assume we're all caught up, even if value is spurious + { + m_lag_sum = 0; + m_lag_count = 0; + m_lag_frames = 0; + + hr = WebmMfVp8Dec::SetDropMode(MF_DROP_MODE_NONE); + assert(SUCCEEDED(hr)); + + return S_OK; + } + + m_lag_sum += reftime; + ++m_lag_count; + + if (m_lag_count < 16) + return S_OK; + + const float lag_reftime = float(m_lag_sum) / float(m_lag_count); + + const float frames_ = lag_reftime * m_frame_rate / 10000000.0F; + const int frames = static_cast<int>(frames_); + + if (frames <= 1) //treat small values as noise + { + m_lag_sum = 0; + m_lag_count = 0; + m_lag_frames = 0; + + hr = WebmMfVp8Dec::SetDropMode(MF_DROP_MODE_NONE); + assert(SUCCEEDED(hr)); + } + + MF_QUALITY_DROP_MODE m; + + hr = WebmMfVp8Dec::GetDropMode(&m); + assert(SUCCEEDED(hr)); + + if ((m_lag_frames > 0) && //have previous value + (frames >= m_lag_frames) && //we're not getting better + (m < MF_DROP_MODE_5)) //TODO: use MF_QUALITY_CANNOT_KEEP_UP? + { +#ifdef _DEBUG + odbgstream os; + os << "WebmMfVp8Dec::NotifyQualityEvent: old drop mode=" << m; +#endif + + m = static_cast<MF_QUALITY_DROP_MODE>(int(m) + 1); + +#ifdef _DEBUG + os << " new drop mode=" << m << endl; +#endif + + hr = WebmMfVp8Dec::SetDropMode(m); + assert(SUCCEEDED(hr)); + } + + m_lag_sum = 0; + m_lag_count = 0; + m_lag_frames = frames; + + return S_OK; + + //Here's a case when there's been a long sequence of frames + //having positive lag. That means we're slightly behind, so + //we should compare how much we're behind now to how much + //we were behind in the recent past. The reason we do that + //is to discover whether we have a trend. + // + //How many data points to we use to compute the trend? We used + //the most recent 16 points to compute our latest lag value, so + //we must compare the most recent lag value to previous lag values. + // + //If we have a previous lag value, then we can compute a trend, even + //if we only have two points. + // + //If the avg lag computed here is same or (slightly?) larger, then it + //means we really are behind. We would choose a drop level. Our + //current algorithm even for dropping doesn't work that fast: at + //drop level 1, we drop only 1 frame for every 16 frames, so once + //we set a non-zero drop level we do have to wait at least 16 frames + //before reacting (which is what we do here). + // + //We don't necessarily have to compare lag times: we convert this to + //a frame, so we could compare frames instead. + // + //The issue remain: how often do we adjust the drop level? + // + //I suppose that when the reported lag time is less than 0, then this + //will reset everything anyway, so we can set the drop level back to + //0 when that happens. + // + //We probalby only have to increase the drop level, until we're caught + //up, and it gets reset back to 0. The question is how often we adjust + //the drop level up. + // + //1st time: 1 out of 16 + //2nd time: 2 out of 16 + //3rd time: 4 out of 16 + //4th time: 8 out of 16 + //5th time: keyframes only + // + //if lag has increased since last time, go ahead and increase the drop + //level. + // + //if lag has decreased since last time, the we can probably either decrease + //the drop level, or leave it as is. + + //These dampened out by themselves, so there's nothing special we needed + //to do. We don't want to react right away. The problem we need to + //handle + //is when the lag is positive and large, and stays constant or is + //increasing. + //It the number is trending down then the problem solves itself. The hard + //cases are when the lag is growing, or staying constant and large. + // + //How to define "growing"? If the slope is positive, that's growing. If + //we remain at any positive slope, the lag will definitely grow. + // + //We could use a one-shot flag: if the flag is true, throw away the next + //non-key frame, then clear the flag. + // + //Our goal is to pull the slope down to 0 (or negative). This should drive + //the lag down, which should drive the slope towards 0. + // + //A running average isn't necessarily what we want, although this + //will give us an estimate of the avg lag. If the avg is large and + //constant then we must correct. + + //How to compute avg lag: + // (sum of lag times) / (number of lag times) + //or could weigh curr avg and curr lag + //e.g. new avg lag = (15/16) * (old avg lag) + (1/16) * (new lag) + //if lag <= 0 then reset avg lag + + //10000000 = 1 sec + // 5000000 = 1/2 sec + // 2500000 = 1/4 sec + // 1250000 = 1/8 sec + // 625000 = 1/16 sec + // 312500 = 1/32 sec + // 156250 = 1/64 sec + // 78125 = 1/128 sec + + // 1 sec late + // if we see 2 frames the set drop mode #5 (keyframe only) + // + // 1/2 sec late + // if we see 4 frames then set drop mode #4 + // + // 1/4 sec late + // if we see + + //drop mode 1 = 1/16 frames are dropped + // 2 = 1/8 frames + // 3 = 1/4 frames + // 4 = 1/2 frames + // + //If we do set the drop mode, then the decoder doesn't + //react that fast. + // + //for drop mode 1, we gain 1/16th sec every 16 frames + //if we were behind by 1/16th sec, then we'd catch up after 1 cycle + //if we were behind by 1/8th sec, we'd catch up after 2 cycles, etc + // + //for drop mode 2, we gain 1/8th sec every 8 frames + //if we were behind by 1/8th sec, we'd catch up after 1 cycle + //if we were behind by 1/4 sec, then we'd catch up after 2 cycles, etc + // + //for drop mode 3, we gain 1/4 sec every 4 frames + //if we were behind by 1/4 sec, we'd catch up after 1 cycle + //if we were behind by 1/2 sec, then we'd catch up after 2 cycles, etc + // + //for drop mode 4, we gain 1/2 sec every 2 frames + // + //THAT'S ALL WRONG + //the drop modes work in terms of frames, not times + //IQualityAdvise2 works in terms of time, not frames + // + //To convert between the two advise interfaces we need the frame rate +} + + +void WebmMfVp8Dec::FrameRate::Init() +{ + m_numerator = 0; + m_denominator = 0; +} + + +HRESULT WebmMfVp8Dec::FrameRate::AssignFrom(IMFMediaType* pmt) +{ + UINT32& num = m_numerator; + UINT32& den = m_denominator; + + const HRESULT hr = MFGetAttributeRatio(pmt, MF_MT_FRAME_RATE, &num, &den); + + //TODO: check explicitly for the result that means "attribute not set". + //MF_E_ATTRIBUTENOTFOUND + //MF_E_INVALIDTYPE + + if (FAILED(hr)) //we assume here this means value wasn't found + { + Init(); + return S_FALSE; + } + + if (den == 0) + return MF_E_INVALIDMEDIATYPE; + + if (num == 0) + return MF_E_INVALIDMEDIATYPE; + + return S_OK; +} + + +HRESULT WebmMfVp8Dec::FrameRate::CopyTo(IMFMediaType* pmt) const +{ + const UINT32 num = m_numerator; + const UINT32 den = m_denominator; + + return MFSetAttributeRatio(pmt, MF_MT_FRAME_RATE, num, den); +} + + +HRESULT WebmMfVp8Dec::FrameRate::CopyTo(float& value) const +{ + const UINT32 num = m_numerator; + const UINT32 den = m_denominator; + + if (den == 0) + value = -1; //serves as our "undefined framerate" nonce value + else + value = float(num) / float(den); + + return S_OK; +} + + +bool WebmMfVp8Dec::FrameRate::Match(const FrameRate& rhs) const +{ + const UINT64 n_in = m_numerator; + const UINT64 d_in = m_denominator; + + const UINT64 n_out = rhs.m_numerator; + const UINT64 d_out = rhs.m_denominator; + + // n_in / d_in = n_out / d_out + // n_in * d_out = n_out * d_in + + return ((n_in * d_out) == (n_out * d_in)); +} + + +} //end namespace WebmMfVp8DecLib
diff --git a/mediafoundation/webmmfvp8dec/webmmfvp8dec.h b/mediafoundation/webmmfvp8dec/webmmfvp8dec.h index 19348db..18ee506 100644 --- a/mediafoundation/webmmfvp8dec/webmmfvp8dec.h +++ b/mediafoundation/webmmfvp8dec/webmmfvp8dec.h
@@ -1,239 +1,239 @@ -#pragma once -#include <list> - -namespace WebmMfVp8DecLib -{ - -class WebmMfVp8Dec : public IMFTransform, - //public IVP8PostProcessing, //TODO - //public IMFQualityAdvise, - public IMFQualityAdvise2, - public IMFRateControl, - public IMFRateSupport, - public IMFGetService, - public CLockable -{ - friend HRESULT CreateDecoder( - IClassFactory*, - IUnknown*, - const IID&, - void**); - - WebmMfVp8Dec(const WebmMfVp8Dec&); - WebmMfVp8Dec& operator=(const WebmMfVp8Dec&); - -public: - - //IUnknown - - HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**); - ULONG STDMETHODCALLTYPE AddRef(); - ULONG STDMETHODCALLTYPE Release(); - - //IMFTransform - - HRESULT STDMETHODCALLTYPE GetStreamLimits( - DWORD* pdwInputMinimum, - DWORD* pdwInputMaximum, - DWORD* pdwOutputMinimum, - DWORD* pdwOutputMaximum); - - HRESULT STDMETHODCALLTYPE GetStreamCount( - DWORD* pcInputStreams, - DWORD* pcOutputStreams); - - HRESULT STDMETHODCALLTYPE GetStreamIDs( - DWORD dwInputIDArraySize, - DWORD* pdwInputIDs, - DWORD dwOutputIDArraySize, - DWORD* pdwOutputIDs); - - HRESULT STDMETHODCALLTYPE GetInputStreamInfo( - DWORD dwInputStreamID, - MFT_INPUT_STREAM_INFO* pStreamInfo); - - HRESULT STDMETHODCALLTYPE GetOutputStreamInfo( - DWORD dwOutputStreamID, - MFT_OUTPUT_STREAM_INFO* pStreamInfo); - - HRESULT STDMETHODCALLTYPE GetAttributes(IMFAttributes**); - - HRESULT STDMETHODCALLTYPE GetInputStreamAttributes( - DWORD dwInputStreamID, - IMFAttributes**); - - HRESULT STDMETHODCALLTYPE GetOutputStreamAttributes( - DWORD dwOutputStreamID, - IMFAttributes**); - - HRESULT STDMETHODCALLTYPE DeleteInputStream(DWORD dwStreamID); - - HRESULT STDMETHODCALLTYPE AddInputStreams( - DWORD cStreams, - DWORD* adwStreamIDs); - - HRESULT STDMETHODCALLTYPE GetInputAvailableType( - DWORD dwInputStreamID, - DWORD dwTypeIndex, - IMFMediaType**); - - HRESULT STDMETHODCALLTYPE GetOutputAvailableType( - DWORD dwOutputStreamID, - DWORD dwTypeIndex, - IMFMediaType**); - - HRESULT STDMETHODCALLTYPE SetInputType( - DWORD dwInputStreamID, - IMFMediaType*, - DWORD dwFlags); - - HRESULT STDMETHODCALLTYPE SetOutputType( - DWORD dwOutputStreamID, - IMFMediaType*, - DWORD dwFlags); - - HRESULT STDMETHODCALLTYPE GetInputCurrentType( - DWORD dwInputStreamID, - IMFMediaType**); - - HRESULT STDMETHODCALLTYPE GetOutputCurrentType( - DWORD dwOutputStreamID, - IMFMediaType**); - - HRESULT STDMETHODCALLTYPE GetInputStatus( - DWORD dwInputStreamID, - DWORD* pdwFlags); - - HRESULT STDMETHODCALLTYPE GetOutputStatus( - DWORD* pdwFlags); - - HRESULT STDMETHODCALLTYPE SetOutputBounds( - LONGLONG hnsLowerBound, - LONGLONG hnsUpperBound); - - HRESULT STDMETHODCALLTYPE ProcessEvent( - DWORD dwInputStreamID, - IMFMediaEvent*); - - HRESULT STDMETHODCALLTYPE ProcessMessage( - MFT_MESSAGE_TYPE, - ULONG_PTR); - - HRESULT STDMETHODCALLTYPE ProcessInput( - DWORD dwInputStreamID, - IMFSample*, - DWORD dwFlags); - - HRESULT STDMETHODCALLTYPE ProcessOutput( - DWORD dwFlags, - DWORD cOutputBufferCount, - MFT_OUTPUT_DATA_BUFFER* pOutputSamples, - DWORD* pdwStatus); - - //IMFQualityAdvise - - HRESULT STDMETHODCALLTYPE SetDropMode(MF_QUALITY_DROP_MODE); - - HRESULT STDMETHODCALLTYPE SetQualityLevel(MF_QUALITY_LEVEL); - - HRESULT STDMETHODCALLTYPE GetDropMode(MF_QUALITY_DROP_MODE*); - - HRESULT STDMETHODCALLTYPE GetQualityLevel(MF_QUALITY_LEVEL*); - - HRESULT STDMETHODCALLTYPE DropTime(LONGLONG); - - //IMFQualityAdvise2 - - HRESULT STDMETHODCALLTYPE NotifyQualityEvent(IMFMediaEvent*, DWORD*); - - //IMFRateControl - - HRESULT STDMETHODCALLTYPE SetRate(BOOL, float); - - HRESULT STDMETHODCALLTYPE GetRate(BOOL*, float*); - - //IMFRateSupport - - HRESULT STDMETHODCALLTYPE GetSlowestRate(MFRATE_DIRECTION, BOOL, float*); - - HRESULT STDMETHODCALLTYPE GetFastestRate(MFRATE_DIRECTION, BOOL, float*); - - HRESULT STDMETHODCALLTYPE IsRateSupported(BOOL, float, float*); - - //IMFGetService - - HRESULT STDMETHODCALLTYPE GetService(REFGUID, REFIID, LPVOID*); - -private: - - explicit WebmMfVp8Dec(IClassFactory*); - virtual ~WebmMfVp8Dec(); - - IClassFactory* const m_pClassFactory; - LONG m_cRef; - - struct FrameSize { UINT32 width; UINT32 height; }; - - struct FrameRate - { - UINT32 m_numerator; - UINT32 m_denominator; - - void Init(); - bool Match(const FrameRate& rhs) const; - - HRESULT AssignFrom(IMFMediaType*); - HRESULT CopyTo(IMFMediaType*) const; - HRESULT CopyTo(float&) const; - }; - - //FrameRate m_frame_rate; - float m_frame_rate; - IMFMediaType* m_pInputMediaType; - IMFMediaType* m_pOutputMediaType; - - struct SampleInfo - { - IMFSample* pSample; - DWORD dwBuffer; - - HRESULT DecodeAll(vpx_codec_ctx_t&); - HRESULT DecodeOne(vpx_codec_ctx_t&, LONGLONG&, LONGLONG&); - }; - - typedef std::list<SampleInfo> samples_t; - samples_t m_samples; - - vpx_codec_ctx_t m_ctx; - - float m_rate; //trick-play mode - BOOL m_bThin; - - MF_QUALITY_DROP_MODE m_drop_mode; - int m_drop_budget; - int m_drop_late; - void ReplenishDropBudget(); - - //struct LagInfo - //{ - // LONGLONG lag; - //}; - - LONGLONG m_lag_sum; //sum of lag values in list - int m_lag_count; - - //typedef std::list<LagInfo> lag_info_list_t; - //lag_info_list_t m_lag_info_list; - - //float m_lag_reftime; - int m_lag_frames; - - DWORD GetOutputBufferSize(FrameSize&) const; - HRESULT Decode(IMFSample*); - HRESULT GetFrame(BYTE*, ULONG, const GUID&); - - void Flush(); - -}; - -} //end namespace WebmMfVp8DecLib +#pragma once +#include <list> + +namespace WebmMfVp8DecLib +{ + +class WebmMfVp8Dec : public IMFTransform, + //public IVP8PostProcessing, //TODO + //public IMFQualityAdvise, + public IMFQualityAdvise2, + public IMFRateControl, + public IMFRateSupport, + public IMFGetService, + public CLockable +{ + friend HRESULT CreateDecoder( + IClassFactory*, + IUnknown*, + const IID&, + void**); + + WebmMfVp8Dec(const WebmMfVp8Dec&); + WebmMfVp8Dec& operator=(const WebmMfVp8Dec&); + +public: + + //IUnknown + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + //IMFTransform + + HRESULT STDMETHODCALLTYPE GetStreamLimits( + DWORD* pdwInputMinimum, + DWORD* pdwInputMaximum, + DWORD* pdwOutputMinimum, + DWORD* pdwOutputMaximum); + + HRESULT STDMETHODCALLTYPE GetStreamCount( + DWORD* pcInputStreams, + DWORD* pcOutputStreams); + + HRESULT STDMETHODCALLTYPE GetStreamIDs( + DWORD dwInputIDArraySize, + DWORD* pdwInputIDs, + DWORD dwOutputIDArraySize, + DWORD* pdwOutputIDs); + + HRESULT STDMETHODCALLTYPE GetInputStreamInfo( + DWORD dwInputStreamID, + MFT_INPUT_STREAM_INFO* pStreamInfo); + + HRESULT STDMETHODCALLTYPE GetOutputStreamInfo( + DWORD dwOutputStreamID, + MFT_OUTPUT_STREAM_INFO* pStreamInfo); + + HRESULT STDMETHODCALLTYPE GetAttributes(IMFAttributes**); + + HRESULT STDMETHODCALLTYPE GetInputStreamAttributes( + DWORD dwInputStreamID, + IMFAttributes**); + + HRESULT STDMETHODCALLTYPE GetOutputStreamAttributes( + DWORD dwOutputStreamID, + IMFAttributes**); + + HRESULT STDMETHODCALLTYPE DeleteInputStream(DWORD dwStreamID); + + HRESULT STDMETHODCALLTYPE AddInputStreams( + DWORD cStreams, + DWORD* adwStreamIDs); + + HRESULT STDMETHODCALLTYPE GetInputAvailableType( + DWORD dwInputStreamID, + DWORD dwTypeIndex, + IMFMediaType**); + + HRESULT STDMETHODCALLTYPE GetOutputAvailableType( + DWORD dwOutputStreamID, + DWORD dwTypeIndex, + IMFMediaType**); + + HRESULT STDMETHODCALLTYPE SetInputType( + DWORD dwInputStreamID, + IMFMediaType*, + DWORD dwFlags); + + HRESULT STDMETHODCALLTYPE SetOutputType( + DWORD dwOutputStreamID, + IMFMediaType*, + DWORD dwFlags); + + HRESULT STDMETHODCALLTYPE GetInputCurrentType( + DWORD dwInputStreamID, + IMFMediaType**); + + HRESULT STDMETHODCALLTYPE GetOutputCurrentType( + DWORD dwOutputStreamID, + IMFMediaType**); + + HRESULT STDMETHODCALLTYPE GetInputStatus( + DWORD dwInputStreamID, + DWORD* pdwFlags); + + HRESULT STDMETHODCALLTYPE GetOutputStatus( + DWORD* pdwFlags); + + HRESULT STDMETHODCALLTYPE SetOutputBounds( + LONGLONG hnsLowerBound, + LONGLONG hnsUpperBound); + + HRESULT STDMETHODCALLTYPE ProcessEvent( + DWORD dwInputStreamID, + IMFMediaEvent*); + + HRESULT STDMETHODCALLTYPE ProcessMessage( + MFT_MESSAGE_TYPE, + ULONG_PTR); + + HRESULT STDMETHODCALLTYPE ProcessInput( + DWORD dwInputStreamID, + IMFSample*, + DWORD dwFlags); + + HRESULT STDMETHODCALLTYPE ProcessOutput( + DWORD dwFlags, + DWORD cOutputBufferCount, + MFT_OUTPUT_DATA_BUFFER* pOutputSamples, + DWORD* pdwStatus); + + //IMFQualityAdvise + + HRESULT STDMETHODCALLTYPE SetDropMode(MF_QUALITY_DROP_MODE); + + HRESULT STDMETHODCALLTYPE SetQualityLevel(MF_QUALITY_LEVEL); + + HRESULT STDMETHODCALLTYPE GetDropMode(MF_QUALITY_DROP_MODE*); + + HRESULT STDMETHODCALLTYPE GetQualityLevel(MF_QUALITY_LEVEL*); + + HRESULT STDMETHODCALLTYPE DropTime(LONGLONG); + + //IMFQualityAdvise2 + + HRESULT STDMETHODCALLTYPE NotifyQualityEvent(IMFMediaEvent*, DWORD*); + + //IMFRateControl + + HRESULT STDMETHODCALLTYPE SetRate(BOOL, float); + + HRESULT STDMETHODCALLTYPE GetRate(BOOL*, float*); + + //IMFRateSupport + + HRESULT STDMETHODCALLTYPE GetSlowestRate(MFRATE_DIRECTION, BOOL, float*); + + HRESULT STDMETHODCALLTYPE GetFastestRate(MFRATE_DIRECTION, BOOL, float*); + + HRESULT STDMETHODCALLTYPE IsRateSupported(BOOL, float, float*); + + //IMFGetService + + HRESULT STDMETHODCALLTYPE GetService(REFGUID, REFIID, LPVOID*); + +private: + + explicit WebmMfVp8Dec(IClassFactory*); + virtual ~WebmMfVp8Dec(); + + IClassFactory* const m_pClassFactory; + LONG m_cRef; + + struct FrameSize { UINT32 width; UINT32 height; }; + + struct FrameRate + { + UINT32 m_numerator; + UINT32 m_denominator; + + void Init(); + bool Match(const FrameRate& rhs) const; + + HRESULT AssignFrom(IMFMediaType*); + HRESULT CopyTo(IMFMediaType*) const; + HRESULT CopyTo(float&) const; + }; + + //FrameRate m_frame_rate; + float m_frame_rate; + IMFMediaType* m_pInputMediaType; + IMFMediaType* m_pOutputMediaType; + + struct SampleInfo + { + IMFSample* pSample; + DWORD dwBuffer; + + HRESULT DecodeAll(vpx_codec_ctx_t&); + HRESULT DecodeOne(vpx_codec_ctx_t&, LONGLONG&, LONGLONG&); + }; + + typedef std::list<SampleInfo> samples_t; + samples_t m_samples; + + vpx_codec_ctx_t m_ctx; + + float m_rate; //trick-play mode + BOOL m_bThin; + + MF_QUALITY_DROP_MODE m_drop_mode; + int m_drop_budget; + int m_drop_late; + void ReplenishDropBudget(); + + //struct LagInfo + //{ + // LONGLONG lag; + //}; + + LONGLONG m_lag_sum; //sum of lag values in list + int m_lag_count; + + //typedef std::list<LagInfo> lag_info_list_t; + //lag_info_list_t m_lag_info_list; + + //float m_lag_reftime; + int m_lag_frames; + + DWORD GetOutputBufferSize(FrameSize&) const; + HRESULT Decode(IMFSample*); + HRESULT GetFrame(BYTE*, ULONG, const GUID&); + + void Flush(); + +}; + +} //end namespace WebmMfVp8DecLib