blob: db2ff7bae3ed406b14113d309585127b38882824 [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.
#pragma warning(disable:4505) //unreferenced local function has been removed
#include "webmvorbisdecoderfilter.hpp"
#include "webmvorbisdecoderoutpin.hpp"
#include "mediatypeutil.hpp"
#include "graphutil.hpp"
#include "webmtypes.hpp"
#include "vorbistypes.hpp"
#include <vfwmsgs.h>
#include <uuids.h>
#include <cassert>
#include <amvideo.h>
#include <evcode.h>
#ifdef _DEBUG
#include <iomanip>
#include "odbgstream.hpp"
using std::endl;
using std::hex;
using std::dec;
#endif
namespace WebmVorbisDecoderLib
{
Inpin::Inpin(Filter* p) :
Pin(p, PINDIR_INPUT, L"input"),
m_bEndOfStream(false),
m_bFlush(false),
m_bDone(false)
{
AM_MEDIA_TYPE mt;
mt.majortype = MEDIATYPE_Audio;
mt.subtype = VorbisTypes::MEDIASUBTYPE_Vorbis2;
mt.bFixedSizeSamples = FALSE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0;
mt.formattype = VorbisTypes::FORMAT_Vorbis2;
mt.pUnk = 0;
mt.cbFormat = 0;
mt.pbFormat = 0;
m_preferred_mtv.Add(mt);
m_packet.packetno = -1;
m_hSamples = CreateEvent(0, 0, 0, 0);
assert(m_hSamples);
}
Inpin::~Inpin()
{
const BOOL b = CloseHandle(m_hSamples);
assert(b);
}
HRESULT Inpin::QueryInterface(const IID& iid, void** ppv)
{
if (ppv == 0)
return E_POINTER;
IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv);
if (iid == __uuidof(IUnknown))
pUnk = static_cast<IPin*>(this);
else if (iid == __uuidof(IPin))
pUnk = static_cast<IPin*>(this);
else if (iid == __uuidof(IMemInputPin))
pUnk = static_cast<IMemInputPin*>(this);
else
{
pUnk = 0;
return E_NOINTERFACE;
}
pUnk->AddRef();
return S_OK;
}
ULONG Inpin::AddRef()
{
return m_pFilter->AddRef();
}
ULONG Inpin::Release()
{
return m_pFilter->Release();
}
HRESULT Inpin::Connect(IPin*, const AM_MEDIA_TYPE*)
{
return E_UNEXPECTED; //for output pins only
}
HRESULT Inpin::QueryInternalConnections(
IPin** pa,
ULONG* pn)
{
if (pn == 0)
return E_POINTER;
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
const ULONG m = 1; //number of output pins
ULONG& n = *pn;
if (n == 0)
{
if (pa == 0) //query for required number
{
n = m;
return S_OK;
}
return S_FALSE; //means "insufficient number of array elements"
}
if (n < m)
{
n = 0;
return S_FALSE; //means "insufficient number of array elements"
}
if (pa == 0)
{
n = 0;
return E_POINTER;
}
IPin*& pin = pa[0];
pin = &m_pFilter->m_outpin;
pin->AddRef();
n = m;
return S_OK;
}
HRESULT Inpin::ReceiveConnection(
IPin* pin,
const AM_MEDIA_TYPE* pmt)
{
if (pin == 0)
return E_INVALIDARG;
if (pmt == 0)
return E_INVALIDARG;
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
if (m_pFilter->m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
if (bool(m_pPinConnection))
return VFW_E_ALREADY_CONNECTED;
m_connection_mtv.Clear();
hr = QueryAccept(pmt);
if (hr != S_OK)
return VFW_E_TYPE_NOT_ACCEPTED;
const AM_MEDIA_TYPE& mt = *pmt;
hr = m_connection_mtv.Add(mt);
if (FAILED(hr))
return hr;
m_pPinConnection = pin;
m_pFilter->m_outpin.OnInpinConnect(mt);
return S_OK;
}
HRESULT Inpin::EndOfStream()
{
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
if (!bool(m_pPinConnection))
return VFW_E_NOT_CONNECTED;
#ifdef _DEBUG
odbgstream os;
os << "webmvorbisdecoder::inpin::EOS" << endl;
#endif
if (m_bFlush)
return S_FALSE; //?
m_bEndOfStream = true;
m_buffers.push_back(0);
const BOOL b = SetEvent(m_hSamples);
assert(b);
return S_OK;
}
HRESULT Inpin::BeginFlush()
{
#ifdef _DEBUG
odbgstream os;
os << "webmvorbisdecoder::inpin::beginflush(begin)" << endl;
#endif
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
if (!bool(m_pPinConnection))
return VFW_E_NOT_CONNECTED;
//IPin::BeginFlush
//In a flush operation, a filter discards whatever data it was processing.
//It rejects new data until the flush is completed. The flush is
//completed when the upstream pin calls the IPin::EndFlush method.
//Flushing enables the filter graph to be more responsive when events
//alter the normal data flow. For example, flushing occurs during a seek.
//
//When BeginFlush is called, the filter performs the following steps:
//
//(1) Passes the IPin::BeginFlush call downstream.
//
//(2) Sets an internal flag that causes all data-streaming methods to
// fail, such as IMemInputPin::Receive.
//
//(3) Returns from any blocked calls to the Receive method.
//
//
//When the BeginFlush notification reaches a renderer filter,
//the renderer frees any samples that it holds.
//
//After BeginFlush is called, the pin rejects all samples from upstream,
//with a return value of S_FALSE, until the IPin::EndFlush method is
//called.
//IPin::EndOfStream
//The IPin::BeginFlush method flushes any queued end-of-stream
//notifications. This is intended for input pins only.
m_bFlush = true;
Outpin& outpin = m_pFilter->m_outpin;
if (IPin* const pPin = outpin.m_pPinConnection)
{
#ifdef _DEBUG
os << "webmvorbisdecoder::inpin::beginflush:"
<< " flushing downstream filter"
<< endl;
#endif
hr = pPin->BeginFlush(); //safe to do this here, while holding lock?
#ifdef _DEBUG
os << "webmvorbisdecoder::inpin::beginflush:"
<< " called BeginFlush: hr=0x" << hex << hr << dec
<< "\nwebmvorbisdecoder::inpin::beginflush: waiting for thread"
<< "to terminate"
<< endl;
#endif
const BOOL b = SetEvent(m_hSamples); //to terminate thread
assert(b);
hr = lock.Release();
assert(SUCCEEDED(hr));
//The thread might not exist yet, if we have been connected but not
//started. In which case, attempting to stop the thread is benign.
outpin.StopThread();
}
#ifdef _DEBUG
os << "webmvorbisdecoder::inpin::beginflush(end #2): thread terminated"
<< endl;
#endif
return S_OK;
}
HRESULT Inpin::EndFlush()
{
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
if (!bool(m_pPinConnection))
return VFW_E_NOT_CONNECTED;
#ifdef _DEBUG
odbgstream os;
os << "webmvorbisdecoder::inpin::endflush(begin)" << endl;
#endif
//IPin::EndFlush
//When this method is called, the filter performs the following actions:
//
//(1) Waits for all queued samples to be discarded.
//
//(2) Frees any buffered data, including any pending end-of-stream
//notifications.
//
//(3) Clears any pending EC_COMPLETE notifications.
//
//(4) Calls EndFlush downstream.
//
//When the method returns, the pin can accept new samples.
m_bFlush = false;
m_bEndOfStream = false;
m_first_reftime = -1;
m_bDone = false;
while (!m_buffers.empty())
{
IMediaSample* const pSample = m_buffers.front();
m_buffers.pop_front();
if (pSample)
pSample->Release();
}
typedef channels_t::iterator iter_t;
iter_t i = m_channels.begin();
const iter_t j = m_channels.end();
while (i != j)
{
samples_t& ss = *i++;
ss.clear();
}
Outpin& outpin = m_pFilter->m_outpin;
if (IPin* const pPin = outpin.m_pPinConnection)
{
#ifdef _DEBUG
os << "webmvorbisdecoder::inpin::endflush: calling endflush on"
<< " downstream filter"
<< endl;
#endif
hr = pPin->EndFlush(); //safe to call this without releasing lock?
if (m_pFilter->m_state != State_Stopped)
{
#ifdef _DEBUG
os << "webmvorbisdecoder::inpin::endflush: state != stopped,"
<< " so starting thread"
<< endl;
#endif
outpin.StartThread();
#ifdef _DEBUG
os << "webmvorbisdecoder::inpin::endflush: started thread"
<< endl;
#endif
}
}
#ifdef _DEBUG
os << "webmvorbisdecoder::inpin::endflush(end)" << endl;
#endif
return S_OK;
}
void Inpin::OnCompletion()
{
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
assert(SUCCEEDED(hr));
while (!m_buffers.empty())
{
IMediaSample* const pSample = m_buffers.front();
if (pSample == 0) //EOS notification
break; //don't throw EOS notification away
m_buffers.pop_front();
pSample->Release();
}
typedef channels_t::iterator iter_t;
iter_t i = m_channels.begin();
const iter_t j = m_channels.end();
while (i != j)
{
samples_t& ss = *i++;
ss.clear();
}
m_bDone = true;
}
HRESULT Inpin::NewSegment(
REFERENCE_TIME st,
REFERENCE_TIME sp,
double r)
{
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
if (!bool(m_pPinConnection))
return VFW_E_NOT_CONNECTED;
if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection)
{
lock.Release();
const HRESULT hr = pPin->NewSegment(st, sp, r);
return hr;
}
return S_OK;
}
HRESULT Inpin::QueryAccept(const AM_MEDIA_TYPE* pmt)
{
if (pmt == 0)
return E_INVALIDARG;
const AM_MEDIA_TYPE& mt = *pmt;
if (mt.majortype != MEDIATYPE_Audio)
return S_FALSE;
if (mt.subtype != VorbisTypes::MEDIASUBTYPE_Vorbis2)
return S_FALSE;
if (mt.formattype != VorbisTypes::FORMAT_Vorbis2)
return S_FALSE;
if (mt.pbFormat == 0)
return S_FALSE;
typedef VorbisTypes::VORBISFORMAT2 FMT;
if (mt.cbFormat < sizeof(FMT))
return S_FALSE;
const FMT& fmt = (FMT&)(*mt.pbFormat);
if (fmt.channels == 0)
return S_FALSE;
if (fmt.channels > 2) //TODO: handle up to 8
return S_FALSE;
if (fmt.samplesPerSec == 0)
return S_FALSE;
//check bitsPerSample?
const ULONG id_len = fmt.headerSize[0];
if (id_len == 0)
return S_FALSE;
const ULONG comment_len = fmt.headerSize[1];
if (comment_len == 0)
return S_FALSE;
const ULONG setup_len = fmt.headerSize[2];
if (setup_len == 0)
return S_FALSE;
const ULONG hdr_len = id_len + comment_len + setup_len;
if (mt.cbFormat < (sizeof(FMT) + hdr_len))
return S_FALSE;
//TODO: vet headers
return S_OK;
}
HRESULT Inpin::GetAllocator(IMemAllocator** p)
{
if (p)
*p = 0;
return VFW_E_NO_ALLOCATOR;
}
HRESULT Inpin::NotifyAllocator(
IMemAllocator* pAllocator,
BOOL)
{
if (pAllocator == 0)
return E_INVALIDARG;
ALLOCATOR_PROPERTIES props;
const HRESULT hr = pAllocator->GetProperties(&props);
hr;
assert(SUCCEEDED(hr));
#ifdef _DEBUG
wodbgstream os;
os << "webmvorbisdecoder::inpin::NotifyAllocator: props.cBuffers="
<< props.cBuffers
<< " cbBuffer="
<< props.cbBuffer
<< " cbAlign="
<< props.cbAlign
<< " cbPrefix="
<< props.cbPrefix
<< endl;
#endif
return S_OK;
}
HRESULT Inpin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES* pp)
{
if (pp == 0)
return E_POINTER;
ALLOCATOR_PROPERTIES& p = *pp;
p;
return S_OK;
}
HRESULT Inpin::Receive(IMediaSample* pInSample)
{
if (pInSample == 0)
return E_INVALIDARG;
HRESULT hr;
//#define DEBUG_RECEIVE
#undef DEBUG_RECEIVE
#ifdef DEBUG_RECEIVE
__int64 start_reftime_, stop_reftime_;
hr = pInSample->GetTime(&start_reftime_, &stop_reftime_);
#endif
Filter::Lock lock;
hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
//#ifdef DEBUG_RECEIVE
// wodbgstream os;
// os << L"vp8dec::inpin::Receive: THREAD=0x"
// << std::hex << GetCurrentThreadId() << std::dec
// << endl;
//#endif
if (!bool(m_pPinConnection))
return VFW_E_NOT_CONNECTED;
Outpin& outpin = m_pFilter->m_outpin;
if (!bool(outpin.m_pPinConnection))
return S_FALSE;
if (!bool(outpin.m_pAllocator)) //should never happen
return VFW_E_NO_ALLOCATOR;
if (m_pFilter->m_state == State_Stopped)
return VFW_E_NOT_RUNNING;
if (m_bEndOfStream)
return VFW_E_SAMPLE_REJECTED_EOS;
if (m_bFlush)
return S_FALSE;
if (m_bDone)
return S_FALSE;
if (m_first_reftime < 0)
{
LONGLONG sp;
hr = pInSample->GetTime(&m_first_reftime, &sp);
if (FAILED(hr))
return S_OK;
if (m_first_reftime < 0)
return S_OK;
m_start_reftime = m_first_reftime;
m_samples = 0;
#ifdef DEBUG_RECEIVE
odbgstream os;
os << std::fixed << std::setprecision(3);
os << "\nwebmvorbisdec::Inpin::Receive: RESET FIRST REFTIME;"
<< " st=" << m_start_reftime
<< " st[sec]=" << (double(m_start_reftime) / 10000000)
<< endl;
#endif
const int status = vorbis_synthesis_restart(&m_dsp_state);
status;
assert(status == 0); //success
m_bDiscontinuity = true;
}
#ifdef DEBUG_RECEIVE
{
odbgstream os;
os << "webmvorbisdec::inpin::receive: ";
os << std::fixed << std::setprecision(3);
if (hr == S_OK)
os << "start[sec]="
<< double(start_reftime_) / 10000000
<< "; stop[sec]="
<< double(stop_reftime_) / 10000000
<< "; dt[ms]="
<< double(stop_reftime_ - start_reftime_) / 10000;
else if (hr == VFW_S_NO_STOP_TIME)
os << "start[sec]=" << double(start_reftime_) / 10000000;
os << endl;
}
#endif
Decode(pInSample);
hr = lock.Release();
assert(SUCCEEDED(hr));
if (FAILED(hr))
return hr;
return PopulateSamples();
}
void Inpin::Decode(IMediaSample* pInSample)
{
BYTE* buf_in;
HRESULT hr = pInSample->GetPointer(&buf_in);
assert(SUCCEEDED(hr));
assert(buf_in);
const long len_in = pInSample->GetActualDataLength();
assert(len_in >= 0);
ogg_packet& pkt = m_packet;
pkt.packet = buf_in;
pkt.bytes = len_in;
++pkt.packetno;
int status = vorbis_synthesis(&m_block, &pkt);
assert(status == 0); //TODO
status = vorbis_synthesis_blockin(&m_dsp_state, &m_block);
assert(status == 0); //TODO
typedef VorbisTypes::VORBISFORMAT2 FMT;
const AM_MEDIA_TYPE& mt = m_connection_mtv[0];
assert(mt.cbFormat > sizeof(FMT));
assert(mt.pbFormat);
const FMT& fmt = (const FMT&)(*mt.pbFormat);
assert(fmt.channels > 0);
assert(fmt.samplesPerSec > 0);
float** sv;
const int pcmout_count = vorbis_synthesis_pcmout(&m_dsp_state, &sv);
if (pcmout_count <= 0)
return;
assert(sv);
for (DWORD i = 0; i < fmt.channels; ++i)
{
const float* const first = sv[i];
const float* const last = first + pcmout_count;
samples_t& ss = m_channels[i];
ss.insert(ss.end(), first, last);
}
sv = 0;
status = vorbis_synthesis_read(&m_dsp_state, pcmout_count);
assert(status == 0);
}
void Inpin::PopulateSample(
IMediaSample* pOutSample,
long target_count,
const WAVEFORMATEX& wfx)
{
assert(pOutSample);
assert(target_count >= 1);
const DWORD channels = wfx.nChannels;
assert(channels > 0);
assert(channels <= 2); //TODO
assert(channels == m_channels.size());
const long block_align = wfx.nBlockAlign;
assert(size_t(block_align) == (channels * sizeof(float)));
//ALLOCATOR_PROPERTIES props;
//hr = outpin.m_pAllocator->GetProperties(&props);
//assert(SUCCEEDED(hr));
//if (FAILED(hr))
// return hr;
//const long cbBuffer = props.cbBuffer;
//assert(cbBuffer >= block_align);
const long size = pOutSample->GetSize();
assert(size >= 0);
assert(size >= block_align);
const long samples_ = size / block_align; //max samples in buffer
assert(samples_ >= 1);
const long samples = (samples_ < target_count) ? samples_ : target_count;
const long len_out = samples * block_align;
assert(len_out <= size);
BYTE* dst;
HRESULT hr = pOutSample->GetPointer(&dst);
assert(SUCCEEDED(hr));
assert(dst);
BYTE* const dst_end = dst + len_out;
dst_end;
for (long i = 0; i < samples; ++i)
{
for (DWORD j = 0; j < channels; ++j)
{
samples_t& ss = m_channels[j];
assert(!ss.empty());
const float& s = ss.front();
memcpy(dst, &s, sizeof(float)); //TODO: proper channel mapping
dst += sizeof(float);
assert(dst <= dst_end);
ss.pop_front(); //OK for deque (horrible for vector)
}
}
assert(dst == dst_end);
hr = pOutSample->SetActualDataLength(len_out);
assert(SUCCEEDED(hr));
m_samples += samples;
const double secs = m_samples / double(wfx.nSamplesPerSec);
const double ticks = secs * 10000000;
LONGLONG stop_reftime = m_first_reftime + static_cast<LONGLONG>(ticks);
hr = pOutSample->SetTime(&m_start_reftime, &stop_reftime);
assert(SUCCEEDED(hr));
m_start_reftime = stop_reftime;
hr = pOutSample->SetSyncPoint(TRUE); //?
assert(SUCCEEDED(hr));
hr = pOutSample->SetPreroll(FALSE);
assert(SUCCEEDED(hr));
#if 0
hr = pInSample->IsDiscontinuity();
assert(SUCCEEDED(hr));
hr = pOutSample->SetDiscontinuity(BOOL(hr == S_OK));
assert(SUCCEEDED(hr));
#else
hr = pOutSample->SetDiscontinuity(m_bDiscontinuity ? TRUE : FALSE);
assert(SUCCEEDED(hr));
m_bDiscontinuity = false;
#endif
hr = pOutSample->SetMediaTime(0, 0);
assert(SUCCEEDED(hr));
}
HRESULT Inpin::PopulateSamples()
{
//Filter is NOT locked
const Outpin& outpin = m_pFilter->m_outpin;
for (;;)
{
GraphUtil::IMediaSamplePtr pOutSample;
HRESULT hr = outpin.m_pAllocator->GetBuffer(&pOutSample, 0, 0, 0);
if (FAILED(hr))
return hr;
Filter::Lock lock;
hr = lock.Seize(m_pFilter);
assert(SUCCEEDED(hr));
if (FAILED(hr))
return hr;
if (m_pFilter->m_state == State_Stopped)
return VFW_E_NOT_RUNNING;
if (m_bEndOfStream)
return VFW_E_SAMPLE_REJECTED_EOS;
if (m_bFlush)
return S_FALSE;
if (m_bDone)
return S_FALSE;
if (!bool(outpin.m_pPinConnection)) //weird
return S_FALSE;
if (!bool(outpin.m_pInputPin)) //weird
return S_FALSE;
//if (!bool(outpin.m_pAllocator)) //weird
// return S_FALSE;
const WAVEFORMATEX* const pwfx = outpin.GetFormat();
assert(pwfx);
assert(pwfx->nChannels > 0);
assert(pwfx->nChannels == m_channels.size());
const long actual = m_channels[0].size();
const long target = pwfx->nSamplesPerSec / Pin::kSampleRateDivisor;
if (actual < target)
return S_OK;
PopulateSample(pOutSample, target, *pwfx);
m_buffers.push_back(pOutSample.Detach());
const BOOL b = SetEvent(m_hSamples);
assert(b);
}
}
int Inpin::GetSample(IMediaSample** ppSample)
{
assert(ppSample);
IMediaSample*& pSample = *ppSample;
if (m_bFlush)
{
pSample = 0;
return -2; //terminate
}
if (m_buffers.empty())
{
pSample = 0;
if (m_packet.packetno < 0) //stopped
return -2; //terminate
assert(m_pFilter->m_state != State_Stopped);
return -1; //wait
}
pSample = m_buffers.front();
m_buffers.pop_front();
if (pSample)
return 1;
assert(m_buffers.empty());
return 0; //EOS
}
HRESULT Inpin::ReceiveMultiple(
IMediaSample** pSamples,
long n, //in
long* pm) //out
{
if (pm == 0)
return E_POINTER;
long& m = *pm; //out
m = 0;
if (n <= 0)
return S_OK; //weird
if (pSamples == 0)
return E_INVALIDARG;
for (long i = 0; i < n; ++i)
{
IMediaSample* const pSample = pSamples[i];
assert(pSample);
const HRESULT hr = Receive(pSample);
if (hr != S_OK)
return hr;
++m;
}
return S_OK;
}
HRESULT Inpin::ReceiveCanBlock()
{
return S_OK; //because we wait for output sample
}
HRESULT Inpin::OnDisconnect()
{
return m_pFilter->m_outpin.OnInpinDisconnect();
}
HRESULT Inpin::GetName(PIN_INFO& info) const
{
const wchar_t name[] = L"Vorbis";
#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;
}
HRESULT Inpin::Start()
{
m_bEndOfStream = false;
m_bFlush = false;
m_bDone = false;
ogg_packet& pkt = m_packet;
assert(pkt.packetno < 0);
if (!bool(m_pPinConnection))
return S_FALSE;
const Outpin& outpin = m_pFilter->m_outpin;
if (!bool(outpin.m_pPinConnection))
return S_FALSE;
typedef VorbisTypes::VORBISFORMAT2 FMT;
const AM_MEDIA_TYPE& mt = m_connection_mtv[0];
assert(mt.cbFormat > sizeof(FMT));
assert(mt.pbFormat);
BYTE* pb = mt.pbFormat;
BYTE* const pb_end = pb + mt.cbFormat;
const FMT& fmt = (const FMT&)(*pb);
pb += sizeof(FMT);
assert(pb < pb_end);
const ULONG id_len = fmt.headerSize[0];
assert(id_len > 0);
const ULONG comment_len = fmt.headerSize[1];
assert(comment_len > 0);
const ULONG setup_len = fmt.headerSize[2];
assert(setup_len > 0);
BYTE* const id_buf = pb;
assert(id_buf < pb_end);
BYTE* const comment_buf = pb += id_len;
assert(comment_buf < pb_end);
BYTE* const setup_buf = pb += comment_len;
assert(setup_buf < pb_end);
pb += setup_len;
assert(pb == pb_end);
pkt.packet = id_buf;
pkt.bytes = id_len;
pkt.b_o_s = 1;
pkt.e_o_s = 0;
pkt.granulepos = 0;
pkt.packetno = 0;
int status = vorbis_synthesis_idheader(&pkt);
assert(status == 1); //TODO
vorbis_info& info = m_info;
vorbis_info_init(&info);
vorbis_comment& comment = m_comment;
vorbis_comment_init(&comment);
status = vorbis_synthesis_headerin(&info, &comment, &pkt);
assert(status == 0);
assert((info.channels >= 0) && (DWORD(info.channels) == fmt.channels));
assert((info.rate >= 0) && (DWORD(info.rate) == fmt.samplesPerSec));
pkt.packet = comment_buf;
pkt.bytes = comment_len;
pkt.b_o_s = 0;
++pkt.packetno;
status = vorbis_synthesis_headerin(&info, &comment, &pkt);
assert(status == 0);
pkt.packet = setup_buf;
pkt.bytes = setup_len;
++pkt.packetno;
status = vorbis_synthesis_headerin(&info, &comment, &pkt);
assert(status == 0);
status = vorbis_synthesis_init(&m_dsp_state, &info);
assert(status == 0);
status = vorbis_block_init(&m_dsp_state, &m_block);
assert(status == 0);
m_first_reftime = -1;
//m_start_reftime
//m_samples
m_channels.clear();
m_channels.resize(fmt.channels);
assert(m_buffers.empty());
return S_OK;
}
void Inpin::Stop()
{
while (!m_buffers.empty())
{
IMediaSample* const pSample = m_buffers.front();
m_buffers.pop_front();
if (pSample)
pSample->Release();
}
const BOOL b = SetEvent(m_hSamples); //tell thread to terminate
assert(b);
m_channels.clear();
m_first_reftime = -1;
if (m_packet.packetno < 0)
return;
vorbis_block_clear(&m_block);
vorbis_dsp_clear(&m_dsp_state);
vorbis_comment_clear(&m_comment);
vorbis_info_clear(&m_info);
m_packet.packetno = -1;
}
} //end namespace WebmVorbisDecoderLib