blob: 1cc4dee3ca507e373f970fe7dae12373a59f4b03 [file] [log] [blame]
// 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 <strmif.h>
//#include <comdef.h>
#include "webmvorbisdecoderfilter.hpp"
#include "webmvorbisdecoderoutpin.hpp"
#include "cmediasample.hpp"
#include "mediatypeutil.hpp"
#include "webmtypes.hpp"
#include "vorbistypes.hpp"
#include <vfwmsgs.h>
#include <mmreg.h>
#include <uuids.h>
#include <cassert>
#include <process.h>
#ifdef _DEBUG
#include "odbgstream.hpp"
#include <iomanip>
#include "iidstr.hpp"
using std::endl;
using std::dec;
using std::hex;
using std::fixed;
using std::setprecision;
#endif
using std::wstring;
namespace WebmVorbisDecoderLib
{
Outpin::Outpin(Filter* pFilter) :
Pin(pFilter, PINDIR_OUTPUT, L"output"),
m_hThread(0)
{
SetDefaultMediaTypes();
//m_hQuit = CreateEvent(0, 0, 0, 0);
//assert(m_hQuit);
}
Outpin::~Outpin()
{
assert(m_hThread == 0);
assert(!bool(m_pAllocator));
assert(!bool(m_pInputPin));
//const BOOL b = CloseHandle(m_hQuit);
//assert(b);
}
HRESULT Outpin::Start() //transition from stopped
{
if (m_pPinConnection == 0)
return S_FALSE; //nothing we need to do
assert(bool(m_pAllocator));
assert(bool(m_pInputPin));
const HRESULT hr = m_pAllocator->Commit();
hr;
assert(SUCCEEDED(hr)); //TODO
StartThread();
return S_OK;
}
void Outpin::Stop() //transition to stopped
{
if (m_pPinConnection == 0)
return; //nothing was done
assert(bool(m_pAllocator));
assert(bool(m_pInputPin));
const HRESULT hr = m_pAllocator->Decommit();
hr;
assert(SUCCEEDED(hr));
StopThread();
//m_bDone = true;
}
HRESULT Outpin::QueryInterface(const IID& iid, void** ppv)
{
if (ppv == 0)
return E_POINTER;
IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv);
if (iid == __uuidof(IUnknown))
pUnk = static_cast<IPin*>(this);
else if (iid == __uuidof(IPin))
pUnk = static_cast<IPin*>(this);
else if (iid == __uuidof(IMediaSeeking))
pUnk = static_cast<IMediaSeeking*>(this);
else
{
#if 0
wodbgstream os;
os << "vp8dec::outpin::QI: iid=" << IIDStr(iid) << std::endl;
#endif
pUnk = 0;
return E_NOINTERFACE;
}
pUnk->AddRef();
return S_OK;
}
ULONG Outpin::AddRef()
{
return m_pFilter->AddRef();
}
ULONG Outpin::Release()
{
return m_pFilter->Release();
}
HRESULT Outpin::Connect(
IPin* pin,
const AM_MEDIA_TYPE* pmt)
{
if (pin == 0)
return E_POINTER;
GraphUtil::IMemInputPinPtr pInputPin;
HRESULT hr = pin->QueryInterface(&pInputPin);
if (hr != S_OK)
return hr;
Filter::Lock lock;
hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
if (m_pFilter->m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
if (bool(m_pPinConnection))
return VFW_E_ALREADY_CONNECTED;
if (!bool(m_pFilter->m_inpin.m_pPinConnection))
return VFW_E_NO_TYPES; //VFW_E_NOT_CONNECTED?
m_connection_mtv.Clear();
if (pmt)
{
hr = QueryAccept(pmt);
if (hr != S_OK)
return VFW_E_TYPE_NOT_ACCEPTED;
hr = pin->ReceiveConnection(this, pmt);
if (FAILED(hr))
return hr;
const AM_MEDIA_TYPE& mt = *pmt;
m_connection_mtv.Add(mt);
}
else
{
ULONG i = 0;
const ULONG j = m_preferred_mtv.Size();
while (i < j)
{
const AM_MEDIA_TYPE& mt = m_preferred_mtv[i];
hr = pin->ReceiveConnection(this, &mt);
if (SUCCEEDED(hr))
break;
++i;
}
if (i >= j)
return VFW_E_NO_ACCEPTABLE_TYPES;
const AM_MEDIA_TYPE& mt = m_preferred_mtv[i];
m_connection_mtv.Add(mt);
}
GraphUtil::IMemAllocatorPtr pAllocator;
hr = pInputPin->GetAllocator(&pAllocator);
if (FAILED(hr))
{
hr = CMediaSample::CreateAllocator(&pAllocator);
if (FAILED(hr))
return VFW_E_NO_ALLOCATOR;
}
assert(bool(pAllocator));
ALLOCATOR_PROPERTIES props, actual;
props.cBuffers = -1; //number of buffers
props.cbBuffer = -1; //size of each buffer, excluding prefix
props.cbAlign = -1; //applies to prefix, too
props.cbPrefix = -1; //imediasample::getbuffer does NOT include prefix
hr = pInputPin->GetAllocatorRequirements(&props);
const long cBuffers = 3;
if (props.cBuffers < cBuffers)
props.cBuffers = cBuffers;
const AM_MEDIA_TYPE& mt = m_connection_mtv[0];
assert(mt.formattype == FORMAT_WaveFormatEx);
assert(mt.cbFormat >= 18);
assert(mt.pbFormat);
const WAVEFORMATEX& wfx = (WAVEFORMATEX&)(*mt.pbFormat);
const DWORD target_count = wfx.nSamplesPerSec / Pin::kSampleRateDivisor;
const long cbBuffer = target_count * wfx.nBlockAlign;
if (props.cbBuffer < cbBuffer)
props.cbBuffer = cbBuffer;
if (props.cbAlign <= 0)
props.cbAlign = 1;
if (props.cbPrefix < 0)
props.cbPrefix = 0;
hr = pAllocator->SetProperties(&props, &actual);
if (FAILED(hr))
return hr;
hr = pInputPin->NotifyAllocator(pAllocator, 0); //allow writes
if (FAILED(hr) && (hr != E_NOTIMPL))
return hr;
m_pPinConnection = pin;
m_pAllocator = pAllocator;
m_pInputPin = pInputPin;
return S_OK;
}
HRESULT Outpin::OnDisconnect()
{
m_pInputPin = 0;
m_pAllocator = 0;
return S_OK;
}
HRESULT Outpin::ReceiveConnection(
IPin*,
const AM_MEDIA_TYPE*)
{
return E_UNEXPECTED; //for input pins only
}
HRESULT Outpin::QueryAccept(const AM_MEDIA_TYPE* pmt)
{
if (pmt == 0)
return E_INVALIDARG;
Filter::Lock lock;
const HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
const Inpin& inpin = m_pFilter->m_inpin;
if (!bool(inpin.m_pPinConnection))
return VFW_E_NO_TYPES;
const AM_MEDIA_TYPE& mtOut = *pmt;
if (mtOut.majortype != MEDIATYPE_Audio)
return S_FALSE;
if (mtOut.subtype != MEDIASUBTYPE_IEEE_FLOAT)
return S_FALSE;
if (mtOut.formattype != FORMAT_WaveFormatEx)
return S_FALSE;
if (mtOut.pbFormat == 0)
return S_FALSE;
if (mtOut.cbFormat < 18)
return S_FALSE;
const WAVEFORMATEX& wfxOut = (WAVEFORMATEX&)(*mtOut.pbFormat);
if (wfxOut.wFormatTag != WAVE_FORMAT_IEEE_FLOAT)
return S_FALSE;
if (wfxOut.cbSize > 0)
return S_FALSE;
const AM_MEDIA_TYPE& mtIn = inpin.m_connection_mtv[0];
const WAVEFORMATEX& wfxIn = (WAVEFORMATEX&)(*mtIn.pbFormat);
if (wfxOut.nChannels != wfxIn.nChannels)
return S_FALSE;
if (wfxOut.nSamplesPerSec != wfxIn.nSamplesPerSec)
return S_FALSE;
if (wfxOut.nBlockAlign != wfxIn.nBlockAlign)
return S_FALSE;
//TODO: check bits/sample
return S_OK;
}
HRESULT Outpin::QueryInternalConnections(IPin** pa, ULONG* pn)
{
if (pn == 0)
return E_POINTER;
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
ULONG& n = *pn;
if (n == 0)
{
if (pa == 0) //query for required number
{
n = 1;
return S_OK;
}
return S_FALSE; //means "insufficient number of array elements"
}
if (n < 1)
{
n = 0;
return S_FALSE; //means "insufficient number of array elements"
}
if (pa == 0)
{
n = 0;
return E_POINTER;
}
IPin*& pPin = pa[0];
pPin = &m_pFilter->m_inpin;
pPin->AddRef();
n = 1;
return S_OK;
}
HRESULT Outpin::EndOfStream()
{
return E_UNEXPECTED; //for inpins only
}
HRESULT Outpin::NewSegment(
REFERENCE_TIME,
REFERENCE_TIME,
double)
{
return E_UNEXPECTED;
}
HRESULT Outpin::BeginFlush()
{
return E_UNEXPECTED;
}
HRESULT Outpin::EndFlush()
{
return E_UNEXPECTED;
}
HRESULT Outpin::GetCapabilities(DWORD* pdw)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetCapabilities(pdw);
if (pdw == 0)
return E_POINTER;
DWORD& dw = *pdw;
dw = 0;
return S_OK; //?
}
HRESULT Outpin::CheckCapabilities(DWORD* pdw)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->CheckCapabilities(pdw);
if (pdw == 0)
return E_POINTER;
DWORD& dw = *pdw;
const DWORD dwRequested = dw;
if (dwRequested == 0)
return E_INVALIDARG;
return E_FAIL;
}
HRESULT Outpin::IsFormatSupported(const GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->IsFormatSupported(p);
if (p == 0)
return E_POINTER;
const GUID& g = *p;
if (g == TIME_FORMAT_MEDIA_TIME)
return S_OK;
return S_FALSE;
}
HRESULT Outpin::QueryPreferredFormat(GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->QueryPreferredFormat(p);
if (p == 0)
return E_POINTER;
*p = TIME_FORMAT_MEDIA_TIME;
return S_OK;
}
HRESULT Outpin::GetTimeFormat(GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetTimeFormat(p);
if (p == 0)
return E_POINTER;
*p = TIME_FORMAT_MEDIA_TIME;
return S_OK;
}
HRESULT Outpin::IsUsingTimeFormat(const GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->IsUsingTimeFormat(p);
if (p == 0)
return E_INVALIDARG;
return (*p == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE;
}
HRESULT Outpin::SetTimeFormat(const GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->SetTimeFormat(p);
if (p == 0)
return E_INVALIDARG;
if (*p == TIME_FORMAT_MEDIA_TIME)
return S_OK;
return E_INVALIDARG;
}
HRESULT Outpin::GetDuration(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetDuration(p);
if (p == 0)
return E_POINTER;
return E_FAIL;
}
HRESULT Outpin::GetStopPosition(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetStopPosition(p);
if (p == 0)
return E_POINTER;
return E_FAIL;
}
HRESULT Outpin::GetCurrentPosition(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetCurrentPosition(p);
if (p == 0)
return E_POINTER;
return E_FAIL;
}
HRESULT Outpin::ConvertTimeFormat(
LONGLONG* ptgt,
const GUID* ptgtfmt,
LONGLONG src,
const GUID* psrcfmt)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->ConvertTimeFormat(ptgt, ptgtfmt, src, psrcfmt);
if (ptgt == 0)
return E_POINTER;
LONGLONG& tgt = *ptgt;
const GUID& tgtfmt = ptgtfmt ? *ptgtfmt : TIME_FORMAT_MEDIA_TIME;
const GUID& srcfmt = psrcfmt ? *psrcfmt : TIME_FORMAT_MEDIA_TIME;
if (tgtfmt != TIME_FORMAT_MEDIA_TIME)
return E_INVALIDARG;
if (srcfmt != TIME_FORMAT_MEDIA_TIME)
return E_INVALIDARG;
tgt = src;
return S_OK;
}
HRESULT Outpin::SetPositions(
LONGLONG* pCurr,
DWORD dwCurr,
LONGLONG* pStop,
DWORD dwStop)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->SetPositions(pCurr, dwCurr, pStop, dwStop);
return E_FAIL;
}
HRESULT Outpin::GetPositions(
LONGLONG* pCurrPos,
LONGLONG* pStopPos)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetPositions(pCurrPos, pStopPos);
return E_FAIL;
}
HRESULT Outpin::GetAvailable(
LONGLONG* pEarliest,
LONGLONG* pLatest)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetAvailable(pEarliest, pLatest);
return E_FAIL;
}
HRESULT Outpin::SetRate(double r)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->SetRate(r);
return E_FAIL;
}
HRESULT Outpin::GetRate(double* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetRate(p);
return E_FAIL;
}
HRESULT Outpin::GetPreroll(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetPreroll(p);
return E_FAIL;
}
HRESULT Outpin::GetName(PIN_INFO& info) const
{
const wchar_t* const name_ = L"PCM (IEEE Float)";
#if _MSC_VER >= 1400
enum { namelen = sizeof(info.achName) / sizeof(WCHAR) };
const errno_t e = wcscpy_s(info.achName, namelen, name_);
e;
assert(e == 0);
#else
wcscpy(info.achName, name_);
#endif
return S_OK;
}
void Outpin::OnInpinConnect(const AM_MEDIA_TYPE& mtIn)
{
typedef VorbisTypes::VORBISFORMAT2 FMT;
const FMT& fmt = (FMT&)(*mtIn.pbFormat);
m_preferred_mtv.Clear();
AM_MEDIA_TYPE mt;
WAVEFORMATEX wfx;
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_IEEE_FLOAT;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
//mt.lSampleSize
mt.formattype = FORMAT_WaveFormatEx;
mt.pUnk = 0;
mt.cbFormat = 18;
mt.pbFormat = (BYTE*)&wfx;
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
wfx.nChannels = static_cast<WORD>(fmt.channels);
wfx.nSamplesPerSec = fmt.samplesPerSec;
const size_t bytesPerSample = sizeof(float);
const size_t bitsPerSample = 8 * bytesPerSample;
wfx.wBitsPerSample = static_cast<WORD>(bitsPerSample);
wfx.nBlockAlign = bytesPerSample * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
wfx.cbSize = 0;
mt.lSampleSize = wfx.nBlockAlign;
m_preferred_mtv.Add(mt);
}
HRESULT Outpin::OnInpinDisconnect()
{
if (bool(m_pPinConnection))
{
IFilterGraph* const pGraph = m_pFilter->m_info.pGraph;
assert(pGraph);
HRESULT hr = pGraph->Disconnect(m_pPinConnection);
assert(SUCCEEDED(hr));
hr = pGraph->Disconnect(this);
assert(SUCCEEDED(hr));
assert(!bool(m_pPinConnection));
}
SetDefaultMediaTypes();
return S_OK;
}
void Outpin::SetDefaultMediaTypes()
{
m_preferred_mtv.Clear();
AM_MEDIA_TYPE mt;
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_IEEE_FLOAT;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0;
mt.formattype = GUID_NULL;
mt.pUnk = 0;
mt.cbFormat = 0;
mt.pbFormat = 0;
m_preferred_mtv.Add(mt);
}
const WAVEFORMATEX* Outpin::GetFormat() const
{
if (!bool(m_pPinConnection))
return 0;
if (m_connection_mtv.Empty())
return 0;
const AM_MEDIA_TYPE& mt = m_connection_mtv[0];
assert(mt.formattype == FORMAT_WaveFormatEx);
assert(mt.cbFormat >= sizeof(WAVEFORMATEX));
assert(mt.pbFormat);
const WAVEFORMATEX& wfx = (WAVEFORMATEX&)(*mt.pbFormat);
return &wfx;
}
void Outpin::StartThread()
{
assert(m_hThread == 0);
const uintptr_t h = _beginthreadex(
0, //security
0, //stack size
&Outpin::ThreadProc,
this,
0, //run immediately
0); //thread id
m_hThread = reinterpret_cast<HANDLE>(h);
assert(m_hThread);
#ifdef _DEBUG
wodbgstream os;
os << "webmvorbisdec::Outpin[" << m_id << "]::StartThread: hThread=0x"
<< hex << h << dec
<< endl;
#endif
}
void Outpin::StopThread()
{
if (m_hThread == 0)
return;
#ifdef _DEBUG
wodbgstream os;
os << "webmvorbisdec::Outpin[" << m_id << "]::StopThread: hThread=0x"
<< hex << uintptr_t(m_hThread) << dec
<< endl;
#endif
//BOOL b = SetEvent(m_hQuit);
//assert(b);
const DWORD dw = WaitForSingleObject(m_hThread, 5000);
assert(dw == WAIT_OBJECT_0);
const BOOL b = CloseHandle(m_hThread);
assert(b);
m_hThread = 0;
}
//bool Outpin::Done() const
//{
// if (m_hThread == 0)
// return true;
//
// const DWORD dw = WaitForSingleObject(m_hThread, 0);
//
// if (dw == WAIT_TIMEOUT)
// return false;
//
// assert(dw == WAIT_OBJECT_0);
//
// return true;
//}
unsigned Outpin::ThreadProc(void* pv)
{
Outpin* const pPin = static_cast<Outpin*>(pv);
assert(pPin);
#ifdef _DEBUG
wodbgstream os;
os << "webmvorbisdec::Outpin["
<< pPin->m_id
<< "]::ThreadProc(begin): hThread=0x"
<< hex << uintptr_t(pPin->m_hThread) << dec
<< endl;
#endif
pPin->Main();
#ifdef _DEBUG
os << "webmvorbisdec::Outpin["
<< pPin->m_id << "]::ThreadProc(end): hThread=0x"
<< hex << uintptr_t(pPin->m_hThread) << dec
<< endl;
#endif
return 0;
}
unsigned Outpin::Main()
{
assert(bool(m_pPinConnection));
assert(bool(m_pInputPin));
Inpin& inpin = m_pFilter->m_inpin;
const HANDLE hSamples = inpin.m_hSamples;
for (;;)
{
GraphUtil::IMediaSamplePtr pSample;
Filter::Lock lock;
HRESULT hrLock = lock.Seize(m_pFilter);
assert(SUCCEEDED(hrLock));
if (FAILED(hrLock))
return 0; //TODO: signal error
const int status = inpin.GetSample(&pSample);
hrLock = lock.Release();
assert(SUCCEEDED(hrLock));
if (FAILED(hrLock))
return 0;
if (status < -1) //terminate thread
return 0;
if (status == 0) //EOS (no payload)
{
#ifdef _DEBUG
odbgstream os;
os << "webmvorbisdec::outpin::EOS: calling pin->EOS" << endl;
#endif
const HRESULT hrEOS = m_pPinConnection->EndOfStream();
hrEOS;
#ifdef _DEBUG
os << "webmvorbisdec::outpin::EOS: called pin->EOS; hr=0x"
<< hex << hrEOS << dec
<< endl;
#endif
}
else if (status > 0) //have payload to send downstream
{
#if 0
LONGLONG st, sp;
const HRESULT hr = pSample->GetTime(&st, &sp);
assert(SUCCEEDED(hr));
odbgstream os;
os << "A: "
<< fixed
<< setprecision(3)
<< (double(st)/10000000.0)
<< endl;
#endif
const HRESULT hrReceive = m_pInputPin->Receive(pSample);
if (hrReceive == S_OK)
continue;
inpin.OnCompletion();
}
const DWORD dw = WaitForSingleObject(hSamples, INFINITE);
if (dw == WAIT_FAILED)
return 0; //signal error to FGM
assert(dw == WAIT_OBJECT_0);
}
}
} //end namespace WebmVorbisDecoderLib