webmdshow clean-up: Mass EoL convert (CRLF -> LF) in libmkvparser. Change-Id: I8e8f1036f76e6eedbc4a09eddeaeadf2f7965072
diff --git a/libmkvparser/mkvparserstream.cc b/libmkvparser/mkvparserstream.cc index 139d220..05e7bc7 100644 --- a/libmkvparser/mkvparserstream.cc +++ b/libmkvparser/mkvparserstream.cc
@@ -1,670 +1,670 @@ -// 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 "mkvparserstream.h" -#include "mkvparser.hpp" -#include "mkvparserstreamreader.h" -#include <cassert> -#include <sstream> -#include <iomanip> -#include <vfwmsgs.h> -#ifdef _DEBUG -#include "odbgstream.h" -using std::endl; -#endif - -namespace mkvparser -{ - - -Stream::Stream(const Track* pTrack) : - m_pTrack(pTrack), - m_pLocked(0) -{ - Init(); -} - - -Stream::~Stream() -{ - SetCurr(0); -} - - -void Stream::Init() -{ - m_base_time_ns = -1; - //m_pBase = 0; - SetCurr(0); //lazy init this later - m_pStop = m_pTrack->GetEOS(); //means play entire stream - m_bDiscontinuity = true; -} - - -void Stream::Stop() -{ - IMkvReader* const pReader_ = m_pTrack->m_pSegment->m_pReader; - - using mkvparser::IStreamReader; - IStreamReader* const pReader = static_cast<IStreamReader*>(pReader_); - - pReader->UnlockPages(m_pLocked); - m_pLocked = 0; -} - - -HRESULT Stream::SetCurr(const mkvparser::BlockEntry* pNext) -{ - IMkvReader* const pReader_ = m_pTrack->m_pSegment->m_pReader; - - using mkvparser::IStreamReader; - IStreamReader* const pReader = static_cast<IStreamReader*>(pReader_); - - m_pCurr = pNext; - - pReader->UnlockPages(m_pLocked); - m_pLocked = m_pCurr; - - const HRESULT hr = pReader->LockPages(m_pLocked); - assert(SUCCEEDED(hr)); - return hr; -} - - -std::wstring Stream::GetId() const -{ - std::wostringstream os; - - GetKind(os); //"Video" or "Audio" - - os << std::setw(3) << std::setfill(L'0') << m_pTrack->GetNumber(); - - return os.str(); -} - - -std::wstring Stream::GetName() const -{ - const Track* const t = m_pTrack; - assert(t); - - if (const char* codecName = t->GetCodecNameAsUTF8()) - return ConvertFromUTF8(codecName); - - if (const char* name = t->GetNameAsUTF8()) - return ConvertFromUTF8(name); - - if (LONGLONG tn = t->GetNumber()) - { - std::wostringstream os; - os << L"Track" << tn; - return os.str(); - } - - if (const char* codecId = t->GetCodecId()) - { - std::wstring result; - - const char* p = codecId; - - while (*p) - result += wchar_t(*p++); //TODO: is this cast meaningful? - - return result; - } - - return GetId(); -} - - -#if 0 -__int64 Stream::GetDuration() const -{ - Segment* const pSegment = m_pTrack->m_pSegment; - assert(pSegment); - - const __int64 ns = pSegment->GetDuration(); //scaled (nanoseconds) - assert(ns >= 0); - - const __int64 d = ns / 100; //100-ns ticks - - return d; -} -#endif - - -#if 0 -HRESULT Stream::GetAvailable(LONGLONG* pLatest) const -{ - if (pLatest == 0) - return E_POINTER; - - LONGLONG& pos = *pLatest; //units are current time format - - Segment* const pSegment = m_pTrack->m_pSegment; - - if (pSegment->Unparsed() <= 0) - pos = GetDuration(); - else - { - const Cluster* const pCluster = pSegment->GetLast(); - - if ((pCluster == 0) || pCluster->EOS()) - pos = 0; - else - { - const __int64 ns = pCluster->GetTime(); - pos = ns / 100; //TODO: reftime units are assumed here - } - } - - return S_OK; -} -#endif - - -//__int64 Stream::GetCurrPosition() const -//{ -// return GetCurrTime(); //TODO: for now only support reftime units -//} - - -__int64 Stream::GetCurrTime() const -{ - if (m_pCurr == 0) //NULL means lazy init hasn't happened yet - return 0; //TODO: assumes track starts with t=0 - - if (m_pCurr->EOS()) - return -1; - - const Block* const pBlock = m_pCurr->GetBlock(); - assert(pBlock); - - const __int64 ns = pBlock->GetTime(m_pCurr->GetCluster()); - assert(ns >= 0); - - const __int64 reftime = ns / 100; //100-ns ticks - - return reftime; -} - - -//__int64 Stream::GetStopPosition() const -//{ -// return GetStopTime(); //TODO: for now we only support reftime units -//} - - -__int64 Stream::GetStopTime() const -{ - if (m_pStop == 0) //interpreted to mean "play to end of stream" - return -1; - - if (m_pStop->EOS()) - return -1; - - const Block* const pBlock = m_pStop->GetBlock(); - assert(pBlock); - - const __int64 ns = pBlock->GetTime(m_pStop->GetCluster()); - assert(ns >= 0); - - const __int64 reftime = ns / 100; //100-ns ticks - - return reftime; -} - - -LONGLONG Stream::GetSeekTime( - LONGLONG currpos_reftime, - DWORD dwCurr_) const -{ - const DWORD dwCurrPos = dwCurr_ & AM_SEEKING_PositioningBitsMask; - assert(dwCurrPos != AM_SEEKING_NoPositioning); //handled by caller - - Segment* const pSegment = m_pTrack->m_pSegment; - - const __int64 currpos_ns = currpos_reftime * 100; - //__int64 tCurr_ns; - - switch (dwCurrPos) - { - case AM_SEEKING_IncrementalPositioning: //applies only to stop pos - default: - assert(false); - return 0; - - case AM_SEEKING_AbsolutePositioning: - return currpos_ns; - - case AM_SEEKING_RelativePositioning: - { - if (m_pCurr == 0) //lazy init - return currpos_ns; //t=0 is assumed here - - else if (m_pCurr->EOS()) - { - const __int64 duration_ns = pSegment->GetDuration(); - - if (duration_ns >= 0) //actually have a duration - return duration_ns + currpos_ns; - - return 0; //TODO: is there a better value we can return here? - } - else - { - const Block* const pBlock = m_pCurr->GetBlock(); - assert(pBlock); - - return pBlock->GetTime(m_pCurr->GetCluster()) + currpos_ns; - } - } - } -} - - -void Stream::SetCurrPosition( - //const Cluster* pBase, - LONGLONG base_time_ns, - const BlockEntry* pCurr) -{ - //m_pBase = pBase; - SetCurr(pCurr); - m_base_time_ns = base_time_ns; - m_bDiscontinuity = true; -} - - -void Stream::SetStopPosition( - LONGLONG stoppos_reftime, - DWORD dwStop_) -{ - const DWORD dwStopPos = dwStop_ & AM_SEEKING_PositioningBitsMask; - assert(dwStopPos != AM_SEEKING_NoPositioning); //handled by caller - - Segment* const pSegment = m_pTrack->m_pSegment; - - if (pSegment->GetCount() == 0) - { - m_pStop = m_pTrack->GetEOS(); //means "play to end" - return; - } - - if ((m_pCurr != 0) && m_pCurr->EOS()) - { - m_pStop = m_pTrack->GetEOS(); - return; - } - - __int64 tCurr_ns; - - if (m_pCurr == 0) //lazy init - tCurr_ns = 0; //nanoseconds - else - { - const Block* const pBlock = m_pCurr->GetBlock(); - - tCurr_ns = pBlock->GetTime(m_pCurr->GetCluster()); - assert(tCurr_ns >= 0); - } - - //const Cluster* const pFirst = pSegment->GetFirst(); - //const Cluster* const pCurrCluster = m_pBase ? m_pBase : pFirst; - //pCurrCluster; - //assert(pCurrCluster); - //assert(!pCurrCluster->EOS()); - //assert(tCurr_ns >= pCurrCluster->GetTime()); - - //const __int64 duration_ns = pSegment->GetDuration(); - //assert(duration_ns >= 0); - - const __int64 stoppos_ns = stoppos_reftime * 100; - __int64 tStop_ns; - - switch (dwStopPos) - { - default: - assert(false); - return; - - case AM_SEEKING_AbsolutePositioning: - { - tStop_ns = stoppos_reftime; - break; - } - case AM_SEEKING_RelativePositioning: - { - if ((m_pStop == 0) || m_pStop->EOS()) - { - const __int64 duration_ns = pSegment->GetDuration(); - - if (duration_ns <= 0) //don't have a duration - { - m_pStop = m_pTrack->GetEOS(); //means "play to end" - return; - } - - tStop_ns = duration_ns + stoppos_ns; - } - else - { - const Block* const pBlock = m_pStop->GetBlock(); - assert(pBlock); - - tStop_ns = pBlock->GetTime(m_pStop->GetCluster()) + stoppos_ns; - } - - break; - } - case AM_SEEKING_IncrementalPositioning: - { - if (stoppos_reftime <= 0) - { - m_pStop = m_pCurr; - return; - } - - tStop_ns = tCurr_ns + stoppos_ns; - break; - } - } - - if (tStop_ns <= tCurr_ns) - { - m_pStop = m_pCurr; - return; - } - - //if (tStop_ns >= duration_ns) - //{ - // m_pStop = m_pTrack->GetEOS(); - // return; - //} - - //TODO: here we find a stop block whose time is aligned with - //a cluster time. We should really do better here, and find - //the exact block that corresponds to the requested time. - - const Cluster* pStopCluster = pSegment->FindCluster(tStop_ns); - assert(pStopCluster); - - if (pStopCluster == m_pCurr->GetCluster()) - pStopCluster = pSegment->GetNext(pStopCluster); - - m_pStop = pStopCluster->GetEntry(m_pTrack); - assert((m_pStop == 0) || - m_pStop->EOS() || - (m_pStop->GetBlock()->GetTime(m_pStop->GetCluster()) >= tCurr_ns)); -} - - -void Stream::SetStopPositionEOS() -{ - m_pStop = m_pTrack->GetEOS(); -} - - -#if 0 -HRESULT Stream::Preload() -{ - Segment* const pSegment = m_pTrack->m_pSegment; - - const long status = pSegment->LoadCluster(); - - if (status < 0) //error - return E_FAIL; - - return S_OK; -} -#endif - - -HRESULT Stream::InitCurr() -{ - if (m_pCurr) - return S_OK; - - //lazy-init of first block - - Segment* const pSegment = m_pTrack->m_pSegment; - - if (pSegment->GetCount() <= 0) - return VFW_E_BUFFER_UNDERFLOW; - - const mkvparser::BlockEntry* pCurr; - - const long status = m_pTrack->GetFirst(pCurr); - - if (status == E_BUFFER_NOT_FULL) - return VFW_E_BUFFER_UNDERFLOW; - - assert(status >= 0); //success - assert(pCurr); - assert(pCurr->EOS() || - (m_pTrack->GetType() == 2) || - pCurr->GetBlock()->IsKey()); - - SetCurr(pCurr); - - const Cluster* const pBase = pSegment->GetFirst(); - assert(pBase); - assert(!pBase->EOS()); - - m_base_time_ns = pBase->GetFirstTime(); - //assert(m_base_time_ns >= 0); - -#ifdef _DEBUG - if (!m_pCurr->EOS()) - { - const Block* const pBlock = m_pCurr->GetBlock(); - - const LONGLONG time_ns = pBlock->GetTime(m_pCurr->GetCluster()); - - const LONGLONG dt_ns = time_ns - m_base_time_ns; - assert(dt_ns >= 0); - - const double dt_sec = double(dt_ns) / 1000000000; - assert(dt_sec >= 0); - } -#endif - - return S_OK; -} - - -HRESULT Stream::UpdateAllocatorProperties( - ALLOCATOR_PROPERTIES& props) const -{ - const long cBuffers = GetBufferCount(); - - if (props.cBuffers <= cBuffers) //to handle laced frames - props.cBuffers = cBuffers; - - const long cbBuffer = GetBufferSize(); - - if (props.cbBuffer < cbBuffer) - props.cbBuffer = cbBuffer; - - if (props.cbAlign <= 0) - props.cbAlign = 1; - - if (props.cbPrefix < 0) - props.cbPrefix = 0; - - return S_OK; -} - - -void Stream::Clear(samples_t& samples) -{ - while (!samples.empty()) - { - IMediaSample* const p = samples.back(); - assert(p); - - samples.pop_back(); - - p->Release(); - } -} - - -HRESULT Stream::GetSampleCount(long& count) -{ - count = 0; - - HRESULT hr = InitCurr(); - - if (FAILED(hr)) - return hr; - - if (m_pStop == 0) //TODO: this test might not be req'd - { - if (m_pCurr->EOS()) - return S_FALSE; //send EOS downstream - } - else if (m_pCurr == m_pStop) - { - return S_FALSE; //EOS - } - - const Block* const pCurrBlock = m_pCurr->GetBlock(); - assert(pCurrBlock); - assert(pCurrBlock->GetTrackNumber() == m_pTrack->GetNumber()); - - count = pCurrBlock->GetFrameCount(); - assert(count <= GetBufferCount()); - - return S_OK; -} - - -HRESULT Stream::PopulateSamples(const samples_t& samples) -{ - //if (SendPreroll(pSample)) - // return S_OK; - - HRESULT hr = InitCurr(); - - if (FAILED(hr)) - return hr; - - if (m_pStop == 0) //TODO: this test might not be req'd - { - if (m_pCurr->EOS()) - return S_FALSE; //send EOS downstream - } - else if (m_pCurr == m_pStop) - { - return S_FALSE; //EOS - } - - assert(!m_pCurr->EOS()); - - const BlockEntry* pNext; - const long status = m_pTrack->GetNext(m_pCurr, pNext); - - if (status == E_BUFFER_NOT_FULL) - return VFW_E_BUFFER_UNDERFLOW; - - assert(status >= 0); //success - assert(pNext); - - const Block* const pCurrBlock = m_pCurr->GetBlock(); - - const Cluster* const pCurrCluster = m_pCurr->GetCluster(); - assert(pCurrCluster); - - const __int64 start_ns = pCurrBlock->GetTime(pCurrCluster); - - if (start_ns < 0) - { - SetCurr(pNext); //throw curr block away - return 2; //no samples, but not EOS either - } - - const LONGLONG base_ns = m_base_time_ns; - //assert(base_ns >= 0); - - if (start_ns < base_ns) - { - SetCurr(pNext); //throw curr block away - return 2; //no samples, but not EOS either - } - - const int nFrames = pCurrBlock->GetFrameCount(); - - if (nFrames <= 0) //should never happen - { - SetCurr(pNext); //throw curr block away - return 2; //no samples, but not EOS either - } - - if (samples.size() != samples_t::size_type(nFrames)) - return 2; //try again - - OnPopulateSample(pNext, samples); - - hr = SetCurr(pNext); - m_bDiscontinuity = false; - - return hr; -} - - -//bool Stream::SendPreroll(IMediaSample*) -//{ -// return false; -//} - - -ULONG Stream::GetClusterCount() const -{ - return m_pTrack->m_pSegment->GetCount(); -} - - -HRESULT Stream::SetConnectionMediaType(const AM_MEDIA_TYPE&) -{ - return S_OK; -} - - -std::wstring Stream::ConvertFromUTF8(const char* str) -{ - const int cch = MultiByteToWideChar( - CP_UTF8, - 0, //TODO: MB_ERR_INVALID_CHARS - str, - -1, //include NUL terminator in result - 0, - 0); //request length - - assert(cch > 0); - - const size_t cb = cch * sizeof(wchar_t); - wchar_t* const wstr = (wchar_t*)_malloca(cb); - - const int cch2 = MultiByteToWideChar( - CP_UTF8, - 0, //TODO: MB_ERR_INVALID_CHARS - str, - -1, - wstr, - cch); - - cch2; - assert(cch2 > 0); - assert(cch2 == cch); - - return wstr; -} - - -} //end namespace mkvparser +// 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 "mkvparserstream.h" +#include "mkvparser.hpp" +#include "mkvparserstreamreader.h" +#include <cassert> +#include <sstream> +#include <iomanip> +#include <vfwmsgs.h> +#ifdef _DEBUG +#include "odbgstream.h" +using std::endl; +#endif + +namespace mkvparser +{ + + +Stream::Stream(const Track* pTrack) : + m_pTrack(pTrack), + m_pLocked(0) +{ + Init(); +} + + +Stream::~Stream() +{ + SetCurr(0); +} + + +void Stream::Init() +{ + m_base_time_ns = -1; + //m_pBase = 0; + SetCurr(0); //lazy init this later + m_pStop = m_pTrack->GetEOS(); //means play entire stream + m_bDiscontinuity = true; +} + + +void Stream::Stop() +{ + IMkvReader* const pReader_ = m_pTrack->m_pSegment->m_pReader; + + using mkvparser::IStreamReader; + IStreamReader* const pReader = static_cast<IStreamReader*>(pReader_); + + pReader->UnlockPages(m_pLocked); + m_pLocked = 0; +} + + +HRESULT Stream::SetCurr(const mkvparser::BlockEntry* pNext) +{ + IMkvReader* const pReader_ = m_pTrack->m_pSegment->m_pReader; + + using mkvparser::IStreamReader; + IStreamReader* const pReader = static_cast<IStreamReader*>(pReader_); + + m_pCurr = pNext; + + pReader->UnlockPages(m_pLocked); + m_pLocked = m_pCurr; + + const HRESULT hr = pReader->LockPages(m_pLocked); + assert(SUCCEEDED(hr)); + return hr; +} + + +std::wstring Stream::GetId() const +{ + std::wostringstream os; + + GetKind(os); //"Video" or "Audio" + + os << std::setw(3) << std::setfill(L'0') << m_pTrack->GetNumber(); + + return os.str(); +} + + +std::wstring Stream::GetName() const +{ + const Track* const t = m_pTrack; + assert(t); + + if (const char* codecName = t->GetCodecNameAsUTF8()) + return ConvertFromUTF8(codecName); + + if (const char* name = t->GetNameAsUTF8()) + return ConvertFromUTF8(name); + + if (LONGLONG tn = t->GetNumber()) + { + std::wostringstream os; + os << L"Track" << tn; + return os.str(); + } + + if (const char* codecId = t->GetCodecId()) + { + std::wstring result; + + const char* p = codecId; + + while (*p) + result += wchar_t(*p++); //TODO: is this cast meaningful? + + return result; + } + + return GetId(); +} + + +#if 0 +__int64 Stream::GetDuration() const +{ + Segment* const pSegment = m_pTrack->m_pSegment; + assert(pSegment); + + const __int64 ns = pSegment->GetDuration(); //scaled (nanoseconds) + assert(ns >= 0); + + const __int64 d = ns / 100; //100-ns ticks + + return d; +} +#endif + + +#if 0 +HRESULT Stream::GetAvailable(LONGLONG* pLatest) const +{ + if (pLatest == 0) + return E_POINTER; + + LONGLONG& pos = *pLatest; //units are current time format + + Segment* const pSegment = m_pTrack->m_pSegment; + + if (pSegment->Unparsed() <= 0) + pos = GetDuration(); + else + { + const Cluster* const pCluster = pSegment->GetLast(); + + if ((pCluster == 0) || pCluster->EOS()) + pos = 0; + else + { + const __int64 ns = pCluster->GetTime(); + pos = ns / 100; //TODO: reftime units are assumed here + } + } + + return S_OK; +} +#endif + + +//__int64 Stream::GetCurrPosition() const +//{ +// return GetCurrTime(); //TODO: for now only support reftime units +//} + + +__int64 Stream::GetCurrTime() const +{ + if (m_pCurr == 0) //NULL means lazy init hasn't happened yet + return 0; //TODO: assumes track starts with t=0 + + if (m_pCurr->EOS()) + return -1; + + const Block* const pBlock = m_pCurr->GetBlock(); + assert(pBlock); + + const __int64 ns = pBlock->GetTime(m_pCurr->GetCluster()); + assert(ns >= 0); + + const __int64 reftime = ns / 100; //100-ns ticks + + return reftime; +} + + +//__int64 Stream::GetStopPosition() const +//{ +// return GetStopTime(); //TODO: for now we only support reftime units +//} + + +__int64 Stream::GetStopTime() const +{ + if (m_pStop == 0) //interpreted to mean "play to end of stream" + return -1; + + if (m_pStop->EOS()) + return -1; + + const Block* const pBlock = m_pStop->GetBlock(); + assert(pBlock); + + const __int64 ns = pBlock->GetTime(m_pStop->GetCluster()); + assert(ns >= 0); + + const __int64 reftime = ns / 100; //100-ns ticks + + return reftime; +} + + +LONGLONG Stream::GetSeekTime( + LONGLONG currpos_reftime, + DWORD dwCurr_) const +{ + const DWORD dwCurrPos = dwCurr_ & AM_SEEKING_PositioningBitsMask; + assert(dwCurrPos != AM_SEEKING_NoPositioning); //handled by caller + + Segment* const pSegment = m_pTrack->m_pSegment; + + const __int64 currpos_ns = currpos_reftime * 100; + //__int64 tCurr_ns; + + switch (dwCurrPos) + { + case AM_SEEKING_IncrementalPositioning: //applies only to stop pos + default: + assert(false); + return 0; + + case AM_SEEKING_AbsolutePositioning: + return currpos_ns; + + case AM_SEEKING_RelativePositioning: + { + if (m_pCurr == 0) //lazy init + return currpos_ns; //t=0 is assumed here + + else if (m_pCurr->EOS()) + { + const __int64 duration_ns = pSegment->GetDuration(); + + if (duration_ns >= 0) //actually have a duration + return duration_ns + currpos_ns; + + return 0; //TODO: is there a better value we can return here? + } + else + { + const Block* const pBlock = m_pCurr->GetBlock(); + assert(pBlock); + + return pBlock->GetTime(m_pCurr->GetCluster()) + currpos_ns; + } + } + } +} + + +void Stream::SetCurrPosition( + //const Cluster* pBase, + LONGLONG base_time_ns, + const BlockEntry* pCurr) +{ + //m_pBase = pBase; + SetCurr(pCurr); + m_base_time_ns = base_time_ns; + m_bDiscontinuity = true; +} + + +void Stream::SetStopPosition( + LONGLONG stoppos_reftime, + DWORD dwStop_) +{ + const DWORD dwStopPos = dwStop_ & AM_SEEKING_PositioningBitsMask; + assert(dwStopPos != AM_SEEKING_NoPositioning); //handled by caller + + Segment* const pSegment = m_pTrack->m_pSegment; + + if (pSegment->GetCount() == 0) + { + m_pStop = m_pTrack->GetEOS(); //means "play to end" + return; + } + + if ((m_pCurr != 0) && m_pCurr->EOS()) + { + m_pStop = m_pTrack->GetEOS(); + return; + } + + __int64 tCurr_ns; + + if (m_pCurr == 0) //lazy init + tCurr_ns = 0; //nanoseconds + else + { + const Block* const pBlock = m_pCurr->GetBlock(); + + tCurr_ns = pBlock->GetTime(m_pCurr->GetCluster()); + assert(tCurr_ns >= 0); + } + + //const Cluster* const pFirst = pSegment->GetFirst(); + //const Cluster* const pCurrCluster = m_pBase ? m_pBase : pFirst; + //pCurrCluster; + //assert(pCurrCluster); + //assert(!pCurrCluster->EOS()); + //assert(tCurr_ns >= pCurrCluster->GetTime()); + + //const __int64 duration_ns = pSegment->GetDuration(); + //assert(duration_ns >= 0); + + const __int64 stoppos_ns = stoppos_reftime * 100; + __int64 tStop_ns; + + switch (dwStopPos) + { + default: + assert(false); + return; + + case AM_SEEKING_AbsolutePositioning: + { + tStop_ns = stoppos_reftime; + break; + } + case AM_SEEKING_RelativePositioning: + { + if ((m_pStop == 0) || m_pStop->EOS()) + { + const __int64 duration_ns = pSegment->GetDuration(); + + if (duration_ns <= 0) //don't have a duration + { + m_pStop = m_pTrack->GetEOS(); //means "play to end" + return; + } + + tStop_ns = duration_ns + stoppos_ns; + } + else + { + const Block* const pBlock = m_pStop->GetBlock(); + assert(pBlock); + + tStop_ns = pBlock->GetTime(m_pStop->GetCluster()) + stoppos_ns; + } + + break; + } + case AM_SEEKING_IncrementalPositioning: + { + if (stoppos_reftime <= 0) + { + m_pStop = m_pCurr; + return; + } + + tStop_ns = tCurr_ns + stoppos_ns; + break; + } + } + + if (tStop_ns <= tCurr_ns) + { + m_pStop = m_pCurr; + return; + } + + //if (tStop_ns >= duration_ns) + //{ + // m_pStop = m_pTrack->GetEOS(); + // return; + //} + + //TODO: here we find a stop block whose time is aligned with + //a cluster time. We should really do better here, and find + //the exact block that corresponds to the requested time. + + const Cluster* pStopCluster = pSegment->FindCluster(tStop_ns); + assert(pStopCluster); + + if (pStopCluster == m_pCurr->GetCluster()) + pStopCluster = pSegment->GetNext(pStopCluster); + + m_pStop = pStopCluster->GetEntry(m_pTrack); + assert((m_pStop == 0) || + m_pStop->EOS() || + (m_pStop->GetBlock()->GetTime(m_pStop->GetCluster()) >= tCurr_ns)); +} + + +void Stream::SetStopPositionEOS() +{ + m_pStop = m_pTrack->GetEOS(); +} + + +#if 0 +HRESULT Stream::Preload() +{ + Segment* const pSegment = m_pTrack->m_pSegment; + + const long status = pSegment->LoadCluster(); + + if (status < 0) //error + return E_FAIL; + + return S_OK; +} +#endif + + +HRESULT Stream::InitCurr() +{ + if (m_pCurr) + return S_OK; + + //lazy-init of first block + + Segment* const pSegment = m_pTrack->m_pSegment; + + if (pSegment->GetCount() <= 0) + return VFW_E_BUFFER_UNDERFLOW; + + const mkvparser::BlockEntry* pCurr; + + const long status = m_pTrack->GetFirst(pCurr); + + if (status == E_BUFFER_NOT_FULL) + return VFW_E_BUFFER_UNDERFLOW; + + assert(status >= 0); //success + assert(pCurr); + assert(pCurr->EOS() || + (m_pTrack->GetType() == 2) || + pCurr->GetBlock()->IsKey()); + + SetCurr(pCurr); + + const Cluster* const pBase = pSegment->GetFirst(); + assert(pBase); + assert(!pBase->EOS()); + + m_base_time_ns = pBase->GetFirstTime(); + //assert(m_base_time_ns >= 0); + +#ifdef _DEBUG + if (!m_pCurr->EOS()) + { + const Block* const pBlock = m_pCurr->GetBlock(); + + const LONGLONG time_ns = pBlock->GetTime(m_pCurr->GetCluster()); + + const LONGLONG dt_ns = time_ns - m_base_time_ns; + assert(dt_ns >= 0); + + const double dt_sec = double(dt_ns) / 1000000000; + assert(dt_sec >= 0); + } +#endif + + return S_OK; +} + + +HRESULT Stream::UpdateAllocatorProperties( + ALLOCATOR_PROPERTIES& props) const +{ + const long cBuffers = GetBufferCount(); + + if (props.cBuffers <= cBuffers) //to handle laced frames + props.cBuffers = cBuffers; + + const long cbBuffer = GetBufferSize(); + + if (props.cbBuffer < cbBuffer) + props.cbBuffer = cbBuffer; + + if (props.cbAlign <= 0) + props.cbAlign = 1; + + if (props.cbPrefix < 0) + props.cbPrefix = 0; + + return S_OK; +} + + +void Stream::Clear(samples_t& samples) +{ + while (!samples.empty()) + { + IMediaSample* const p = samples.back(); + assert(p); + + samples.pop_back(); + + p->Release(); + } +} + + +HRESULT Stream::GetSampleCount(long& count) +{ + count = 0; + + HRESULT hr = InitCurr(); + + if (FAILED(hr)) + return hr; + + if (m_pStop == 0) //TODO: this test might not be req'd + { + if (m_pCurr->EOS()) + return S_FALSE; //send EOS downstream + } + else if (m_pCurr == m_pStop) + { + return S_FALSE; //EOS + } + + const Block* const pCurrBlock = m_pCurr->GetBlock(); + assert(pCurrBlock); + assert(pCurrBlock->GetTrackNumber() == m_pTrack->GetNumber()); + + count = pCurrBlock->GetFrameCount(); + assert(count <= GetBufferCount()); + + return S_OK; +} + + +HRESULT Stream::PopulateSamples(const samples_t& samples) +{ + //if (SendPreroll(pSample)) + // return S_OK; + + HRESULT hr = InitCurr(); + + if (FAILED(hr)) + return hr; + + if (m_pStop == 0) //TODO: this test might not be req'd + { + if (m_pCurr->EOS()) + return S_FALSE; //send EOS downstream + } + else if (m_pCurr == m_pStop) + { + return S_FALSE; //EOS + } + + assert(!m_pCurr->EOS()); + + const BlockEntry* pNext; + const long status = m_pTrack->GetNext(m_pCurr, pNext); + + if (status == E_BUFFER_NOT_FULL) + return VFW_E_BUFFER_UNDERFLOW; + + assert(status >= 0); //success + assert(pNext); + + const Block* const pCurrBlock = m_pCurr->GetBlock(); + + const Cluster* const pCurrCluster = m_pCurr->GetCluster(); + assert(pCurrCluster); + + const __int64 start_ns = pCurrBlock->GetTime(pCurrCluster); + + if (start_ns < 0) + { + SetCurr(pNext); //throw curr block away + return 2; //no samples, but not EOS either + } + + const LONGLONG base_ns = m_base_time_ns; + //assert(base_ns >= 0); + + if (start_ns < base_ns) + { + SetCurr(pNext); //throw curr block away + return 2; //no samples, but not EOS either + } + + const int nFrames = pCurrBlock->GetFrameCount(); + + if (nFrames <= 0) //should never happen + { + SetCurr(pNext); //throw curr block away + return 2; //no samples, but not EOS either + } + + if (samples.size() != samples_t::size_type(nFrames)) + return 2; //try again + + OnPopulateSample(pNext, samples); + + hr = SetCurr(pNext); + m_bDiscontinuity = false; + + return hr; +} + + +//bool Stream::SendPreroll(IMediaSample*) +//{ +// return false; +//} + + +ULONG Stream::GetClusterCount() const +{ + return m_pTrack->m_pSegment->GetCount(); +} + + +HRESULT Stream::SetConnectionMediaType(const AM_MEDIA_TYPE&) +{ + return S_OK; +} + + +std::wstring Stream::ConvertFromUTF8(const char* str) +{ + const int cch = MultiByteToWideChar( + CP_UTF8, + 0, //TODO: MB_ERR_INVALID_CHARS + str, + -1, //include NUL terminator in result + 0, + 0); //request length + + assert(cch > 0); + + const size_t cb = cch * sizeof(wchar_t); + wchar_t* const wstr = (wchar_t*)_malloca(cb); + + const int cch2 = MultiByteToWideChar( + CP_UTF8, + 0, //TODO: MB_ERR_INVALID_CHARS + str, + -1, + wstr, + cch); + + cch2; + assert(cch2 > 0); + assert(cch2 == cch); + + return wstr; +} + + +} //end namespace mkvparser
diff --git a/libmkvparser/mkvparserstream.h b/libmkvparser/mkvparserstream.h index a38fcce..99052b8 100644 --- a/libmkvparser/mkvparserstream.h +++ b/libmkvparser/mkvparserstream.h
@@ -1,100 +1,100 @@ -// 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 once -#include <string> -#include <iosfwd> -#include <vector> - -class CMediaTypes; - -namespace mkvparser -{ - -class Track; -class BlockEntry; -class Cluster; - -class Stream -{ - Stream(const Stream&); - Stream& operator=(const Stream&); - -public: - virtual ~Stream(); - void Init(); - void Stop(); - - std::wstring GetId() const; //IPin::QueryId - std::wstring GetName() const; //IPin::QueryPinInfo - virtual void GetMediaTypes(CMediaTypes&) const = 0; - virtual HRESULT QueryAccept(const AM_MEDIA_TYPE*) const = 0; - - virtual HRESULT SetConnectionMediaType(const AM_MEDIA_TYPE&); - HRESULT UpdateAllocatorProperties(ALLOCATOR_PROPERTIES&) const; - - //HRESULT Preload(); //exactly one cluster - - HRESULT GetSampleCount(long&); - - typedef std::vector<IMediaSample*> samples_t; - - HRESULT PopulateSamples(const samples_t&); - static void Clear(samples_t&); - - //__int64 GetDuration() const; - //__int64 GetCurrPosition() const; - //__int64 GetStopPosition() const; - __int64 GetCurrTime() const; - __int64 GetStopTime() const; - - //HRESULT GetAvailable(LONGLONG*) const; - - LONGLONG GetSeekTime(LONGLONG currTime, DWORD dwCurr) const; - //convert from reftime to ns - - void SetCurrPosition( - //const Cluster*, - LONGLONG base_time_ns, - const BlockEntry*); - - void SetStopPosition(LONGLONG, DWORD); - void SetStopPositionEOS(); - - ULONG GetClusterCount() const; - - const Track* const m_pTrack; - static std::wstring ConvertFromUTF8(const char*); - -protected: - explicit Stream(const Track*); - bool m_bDiscontinuity; - const BlockEntry* m_pCurr; - const BlockEntry* m_pStop; - //const Cluster* m_pBase; - LONGLONG m_base_time_ns; - - virtual std::wostream& GetKind(std::wostream&) const = 0; - - HRESULT InitCurr(); - - virtual long GetBufferSize() const = 0; - virtual long GetBufferCount() const = 0; - - virtual void OnPopulateSample( - const BlockEntry*, - const samples_t&) const = 0; - -private: - - const BlockEntry* m_pLocked; - HRESULT SetCurr(const mkvparser::BlockEntry*); - -}; - -} //end namespace mkvparser +// 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 once +#include <string> +#include <iosfwd> +#include <vector> + +class CMediaTypes; + +namespace mkvparser +{ + +class Track; +class BlockEntry; +class Cluster; + +class Stream +{ + Stream(const Stream&); + Stream& operator=(const Stream&); + +public: + virtual ~Stream(); + void Init(); + void Stop(); + + std::wstring GetId() const; //IPin::QueryId + std::wstring GetName() const; //IPin::QueryPinInfo + virtual void GetMediaTypes(CMediaTypes&) const = 0; + virtual HRESULT QueryAccept(const AM_MEDIA_TYPE*) const = 0; + + virtual HRESULT SetConnectionMediaType(const AM_MEDIA_TYPE&); + HRESULT UpdateAllocatorProperties(ALLOCATOR_PROPERTIES&) const; + + //HRESULT Preload(); //exactly one cluster + + HRESULT GetSampleCount(long&); + + typedef std::vector<IMediaSample*> samples_t; + + HRESULT PopulateSamples(const samples_t&); + static void Clear(samples_t&); + + //__int64 GetDuration() const; + //__int64 GetCurrPosition() const; + //__int64 GetStopPosition() const; + __int64 GetCurrTime() const; + __int64 GetStopTime() const; + + //HRESULT GetAvailable(LONGLONG*) const; + + LONGLONG GetSeekTime(LONGLONG currTime, DWORD dwCurr) const; + //convert from reftime to ns + + void SetCurrPosition( + //const Cluster*, + LONGLONG base_time_ns, + const BlockEntry*); + + void SetStopPosition(LONGLONG, DWORD); + void SetStopPositionEOS(); + + ULONG GetClusterCount() const; + + const Track* const m_pTrack; + static std::wstring ConvertFromUTF8(const char*); + +protected: + explicit Stream(const Track*); + bool m_bDiscontinuity; + const BlockEntry* m_pCurr; + const BlockEntry* m_pStop; + //const Cluster* m_pBase; + LONGLONG m_base_time_ns; + + virtual std::wostream& GetKind(std::wostream&) const = 0; + + HRESULT InitCurr(); + + virtual long GetBufferSize() const = 0; + virtual long GetBufferCount() const = 0; + + virtual void OnPopulateSample( + const BlockEntry*, + const samples_t&) const = 0; + +private: + + const BlockEntry* m_pLocked; + HRESULT SetCurr(const mkvparser::BlockEntry*); + +}; + +} //end namespace mkvparser
diff --git a/libmkvparser/mkvparserstreamaudio.cc b/libmkvparser/mkvparserstreamaudio.cc index 4d47515..f00d42a 100644 --- a/libmkvparser/mkvparserstreamaudio.cc +++ b/libmkvparser/mkvparserstreamaudio.cc
@@ -1,904 +1,904 @@ -// 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 "mkvparserstreamaudio.h" -#include "mkvparser.hpp" -#include "vorbistypes.h" -#include "cmediatypes.h" -#include <cassert> -#include <limits> -#include <uuids.h> -#include <mmreg.h> -#include <malloc.h> -#ifdef _DEBUG -#include "odbgstream.h" -using std::endl; -#endif - - -namespace mkvparser -{ - -AudioStream* AudioStream::CreateInstance(const AudioTrack* pTrack) -{ - assert(pTrack); - - const char* const id = pTrack->GetCodecId(); - assert(id); //TODO - - if (_stricmp(id, "A_VORBIS") == 0) - __noop; - else - return 0; //can't create a stream from this track - - const __int64 channels = pTrack->GetChannels(); - - if (channels <= 0) - return 0; //bad track header - - //TODO: - //In principle, Vorbis can have multiple audio channels. - //We do handle this case in the Media Foundation components, - //so we should handle it here in the DirectShow filters too. - //END TODO. - - if (channels > 2) //TODO: handle this case - return 0; - - const double rate = pTrack->GetSamplingRate(); - - if (rate <= 0) - return 0; //bad track header - - double intrate; - const double fracrate = modf(rate, &intrate); - - if (fracrate != 0) - return 0; //bad track header - - //For Vorbis audio, the CodecPrivate part of the WebM track - //header contains the 3 Vorbis headers: the identification header, - //the comments header, and the setup header. They are all packed - //together as the payload of the CodecPrivate element, using - //Vorbis-style lacing. - - size_t cp_size; - - const BYTE* const cp = pTrack->GetCodecPrivate(cp_size); - - if ((cp == 0) || (cp_size == 0)) - return 0; //bad track header - - const BYTE* const begin = &cp[0]; - const BYTE* const end = begin + cp_size; - - const BYTE* p = begin; //to walk the payload buffer - - if (p >= end) - return 0; - - //The first byte in the stream is the number of headers - //present. For Vorbis there must be 3 headers. This is - //Vorbis-style lacing so the count is biased (it can never - //have 0 as the value), so 2 is the (biased) count we must - //have in order for this track header to be considered valid. - - const BYTE n = *p++; - - if (n != 2) //biased count value - return 0; - - if (p >= end) - return 0; - - //Immediately following the (biased) header count value is - //the lengths of the individual headers that follow. The - //physical number of lengths present is exactly equal to the - //biased count value (here, 2). The length of the last header - //is inferred from the difference between the total length - //of the buffer and the sum of the lengths of the other headers. - //(So the logical number of lengths present does equal the unbiased - //count value; here, 3 headers total.) - - //The header lengths are represented in the stream as the - //sum of a sequence of bytes; the sequence terminates for - //a particular length value when the byte has a value less - //then 255. If the byte value is 255, then this implies that - //the header length is at least 255 bytes long and so more bytes - //of the sequence follow. Note that it is valid for a byte in - //the sequence to have 0 as its value; this just means that - //the length happened to be an exact multiple of 255. - - //The length of the identification header is defined to be - //exactly 30 bytes, so its length is represented in the stream - //using 1 byte only. - - const ULONG id_len = *p++; - - if (id_len != 30) - return 0; - - if (p >= end) - return 0; - - //The comment header holds the Ogg metadata for an audio track, - //and so in principle it can be any length. Here that means that - //the length can be represented in the stream using a sequence - //comprising multiple bytes, so to determine the length we must - //loop until we find a byte whose value is less than 255. - - ULONG comment_len = 0; - - for (;;) - { - const BYTE b = *p++; - - if (p >= end) - return 0; - - comment_len += b; - - if (b < 255) - break; - } - - //Each vorbis header begins with a byte having a distinguished - //value that specifies what kind of header this is, followed - //by the string "vorbis". Therefore each well-formed header - //must be at least 7 bytes long. - - if (comment_len < 7) - return 0; - - //We have consumed the sequence of bytes used to represent - //the lengths of the individual headers. What remains in - //the stream are the actual headers. Here we don't particularly - //care much about the actual header payload (we defer such - //matters to the Vorbis decoder), but we do interrogate the - //first 7 bytes of each header to confirm that the headers - //have their correct Vorbis header-kind indicators. - - //p points the first header (the ident header) - - const BYTE* const id_hdr = p; - - //The Vorbis ident header has 1 as its kind indicator. - - if (memcmp(id_hdr, "\x01vorbis", 7) != 0) - return 0; - - const BYTE* const comment_hdr = id_hdr + id_len; - - //The Vorbis comment header has 3 as its kind indicator. - - if (memcmp(comment_hdr, "\x03vorbis", 7) != 0) - return 0; - - const BYTE* const setup_hdr = comment_hdr + comment_len; - - if (setup_hdr >= end) - return 0; - - const ptrdiff_t setup_len_ = end - setup_hdr; - - if (setup_len_ <= 0) - return 0; - - const DWORD setup_len = static_cast<DWORD>(setup_len_); - - if (setup_len < 7) - return 0; - - //The Vorbis setup header has 5 as its kind indicator. - - if (memcmp(setup_hdr, "\x05vorbis", 7) != 0) - return 0; - - //We are satisfied that the CodecPrivate value is well-formed, - //and so we now create the audio stream for this pin. - - AudioStream* const s = new (std::nothrow) AudioStream(pTrack); - assert(s); //TODO - - return s; -} - - -AudioStream::AudioStream(const AudioTrack* pTrack) : - Stream(pTrack) - //m_preroll(0) -{ -} - - -std::wostream& AudioStream::GetKind(std::wostream& os) const -{ - return os << L"Audio"; -} - - -BYTE AudioStream::GetChannels() const -{ - const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); - - const __int64 channels = pTrack->GetChannels(); - assert(channels > 0); - assert(channels <= 255); - - const BYTE result = static_cast<BYTE>(channels); - return result; -} - - -ULONG AudioStream::GetSamplesPerSec() const -{ - const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); - - const double rate = pTrack->GetSamplingRate(); - assert(rate > 0); - - double intrate_; - const double fracrate = modf(rate, &intrate_); - fracrate; - assert(fracrate == 0); - - const ULONG result = static_cast<ULONG>(intrate_); - return result; -} - - -BYTE AudioStream::GetBitsPerSample() const -{ - const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); - - const __int64 val = pTrack->GetBitDepth(); - - if (val <= 0) - return 0; - - assert(val < 256); - assert((val % 8) == 0); - - const BYTE result = static_cast<BYTE>(val); - return result; -} - - -void AudioStream::GetMediaTypes(CMediaTypes& mtv) const -{ - mtv.Clear(); - - const char* const id = m_pTrack->GetCodecId(); - assert(id); - - if (_stricmp(id, "A_VORBIS") == 0) - GetVorbisMediaTypes(mtv); - else - assert(false); -} - - -void AudioStream::GetVorbisMediaTypes(CMediaTypes& mtv) const -{ - size_t cp_size; - - const BYTE* const cp = m_pTrack->GetCodecPrivate(cp_size); - assert(cp); - assert(cp_size > 0); - - const BYTE* const begin = &cp[0]; - const BYTE* const end = begin + cp_size; - - const BYTE* p = begin; - assert(p < end); - - const BYTE n = *p++; - n; - assert(n == 2); - assert(p < end); - - const ULONG id_len = *p++; - assert(id_len == 30); - assert(p < end); - - ULONG comment_len = 0; - - for (;;) - { - const BYTE b = *p++; - assert(p < end); - - comment_len += b; - - if (b < 255) - break; - } - - assert(comment_len >= 7); - - //p points to first header - - const BYTE* const id_hdr = p; - id_hdr; - assert(memcmp(id_hdr, "\x01vorbis", 7) == 0); - - const BYTE* const comment_hdr = id_hdr + id_len; - comment_hdr; - assert(memcmp(comment_hdr, "\x03vorbis", 7) == 0); - - const BYTE* const setup_hdr = comment_hdr + comment_len; - setup_hdr; - assert(setup_hdr < end); - - const ptrdiff_t setup_len_ = end - setup_hdr; - assert(setup_len_ > 0); - - const DWORD setup_len = static_cast<DWORD>(setup_len_); - assert(setup_len >= 7); - assert(memcmp(setup_hdr, "\x05vorbis", 7) == 0); - - const size_t hdr_len = id_len + comment_len + setup_len; - - using VorbisTypes::VORBISFORMAT2; - - const size_t cb = sizeof(VORBISFORMAT2) + hdr_len; - BYTE* const pb = (BYTE*)_malloca(cb); - - VORBISFORMAT2& fmt = (VORBISFORMAT2&)(*pb); - - fmt.channels = GetChannels(); - fmt.samplesPerSec = GetSamplesPerSec(); - fmt.bitsPerSample = GetBitsPerSample(); - fmt.headerSize[0] = id_len; - fmt.headerSize[1] = comment_len; - fmt.headerSize[2] = setup_len; - - assert(p < end); - assert(size_t(end - p) == hdr_len); - - BYTE* const dst = pb + sizeof(VORBISFORMAT2); - memcpy(dst, p, hdr_len); - - 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 = static_cast<ULONG>(cb); - mt.pbFormat = pb; - - mtv.Add(mt); - - //TODO: if we decide source filter should attempt to also - //connect to Xiph Ogg Vorbis decoder filter: - //mt.majortype = VorbisTypes::MEDIATYPE_OggPacketStream; - //mt.subtype = MEDIASUBTYPE_None; - //mt.bFixedSizeSamples = FALSE; - //mt.bTemporalCompression = FALSE; - //mt.lSampleSize = 0; - //mt.formattype = VorbisTypes::FORMAT_OggIdentHeader; - //mt.pUnk = 0; - //mt.cbFormat = id_len; - //mt.pbFormat = const_cast<BYTE*>(id_hdr); - // - //mtv.Add(mt); -} - - -HRESULT AudioStream::QueryAccept(const AM_MEDIA_TYPE* pmt) const -{ - if (pmt == 0) - return E_INVALIDARG; - - const AM_MEDIA_TYPE& mt = *pmt; - - if (mt.majortype != MEDIATYPE_Audio) - return S_FALSE; - - const char* const id = m_pTrack->GetCodecId(); - assert(id); - - if (_stricmp(id, "A_VORBIS") == 0) - { - if (mt.subtype != VorbisTypes::MEDIASUBTYPE_Vorbis2) - return S_FALSE; - - return S_OK; - } - - return S_FALSE; -} - - -#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter: -HRESULT AudioStream::SetConnectionMediaType(const AM_MEDIA_TYPE&) -{ - if (mt.majortype == VorbisTypes::MEDIATYPE_OggPacketStream) - m_preroll = &AudioStream::SendOggIdentPacket; - else - m_preroll = &AudioStream::DoNothing; - - return S_OK; -} -#endif - - -#if 0 -HRESULT AudioStream::UpdateAllocatorProperties( - ALLOCATOR_PROPERTIES& props) const -{ - if (props.cBuffers < 50) //to handle laced audio - props.cBuffers = 50; - - const long size = GetBufferSize(); - - if (props.cbBuffer < size) - props.cbBuffer = size; - - if (props.cbAlign <= 0) - props.cbAlign = 1; - - if (props.cbPrefix < 0) - props.cbPrefix = 0; - - return S_OK; -} -#endif - - -long AudioStream::GetBufferSize() const -{ - const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); - - const double rr = pTrack->GetSamplingRate(); - const long nSamplesPerSec = static_cast<long>(rr); - - const __int64 cc = pTrack->GetChannels(); - const long nChannels = static_cast<long>(cc); - - const long nBlockAlign = nChannels * 2; //16-bit PCM - const long nAvgBytesPerSec = nBlockAlign * nSamplesPerSec; - - const long size = nAvgBytesPerSec; //TODO: make a better estimate - - return size; -} - - -long AudioStream::GetBufferCount() const -{ - return 256; -} - - -void AudioStream::OnPopulateSample( - const BlockEntry* pNextEntry, - const samples_t& samples) const -{ - assert(!samples.empty()); - //assert(m_pBase); - //assert(!m_pBase->EOS()); - assert(m_pCurr); - assert(m_pCurr != m_pStop); - assert(!m_pCurr->EOS()); - - const Block* const pCurrBlock = m_pCurr->GetBlock(); - assert(pCurrBlock); - assert(pCurrBlock->GetTrackNumber() == m_pTrack->GetNumber()); - - const int nFrames = pCurrBlock->GetFrameCount(); - assert(nFrames > 0); //checked by caller - assert(samples.size() == samples_t::size_type(nFrames)); - - const Cluster* const pCurrCluster = m_pCurr->GetCluster(); - assert(pCurrCluster); - - const __int64 start_ns = pCurrBlock->GetTime(pCurrCluster); - assert(start_ns >= 0); - //assert((start_ns % 100) == 0); - - const LONGLONG base_ns = m_base_time_ns; - //assert(base_ns >= 0); - assert(start_ns >= base_ns); - - Segment* const pSegment = m_pTrack->m_pSegment; - IMkvReader* const pFile = pSegment->m_pReader; - - __int64 stop_ns; - - if ((pNextEntry == 0) || pNextEntry->EOS()) - { - const LONGLONG duration_ns = pSegment->GetDuration(); - - if ((duration_ns >= 0) && (duration_ns > start_ns)) - stop_ns = duration_ns; - else - { - const LONGLONG ns_per_frame = 10000000; //10ms - stop_ns = start_ns + LONGLONG(nFrames) * ns_per_frame; - } - } - else - { - const Block* const pNextBlock = pNextEntry->GetBlock(); - assert(pNextBlock); - - const Cluster* const pNextCluster = pNextEntry->GetCluster(); - - stop_ns = pNextBlock->GetTime(pNextCluster); - //assert(stop_ns > start_ns); - - if (stop_ns <= start_ns) - { - const LONGLONG ns_per_frame = 10000000; //10ms - stop_ns = start_ns + LONGLONG(nFrames) * ns_per_frame; - } - } - - __int64 start_reftime = (start_ns - base_ns) / 100; - assert(start_ns >= 0); - - const __int64 block_stop_reftime = (stop_ns - base_ns) / 100; - assert(block_stop_reftime > start_reftime); - - const __int64 block_duration = block_stop_reftime - start_reftime; - assert(block_duration > 0); - - __int64 frame_duration = block_duration / nFrames; //reftime units - - if (frame_duration <= 0) //weird: block duration is very small - frame_duration = 1; - - BOOL bDiscontinuity = m_bDiscontinuity ? TRUE : FALSE; - - for (int idx = 0; idx < nFrames; ++idx) - { - IMediaSample* const pSample = samples[idx]; - - const Block::Frame& f = pCurrBlock->GetFrame(idx); - - const LONG srcsize = f.len; - assert(srcsize >= 0); - - const long tgtsize = pSample->GetSize(); - tgtsize; - assert(tgtsize >= 0); - assert(tgtsize >= srcsize); - - BYTE* ptr; - - HRESULT hr = pSample->GetPointer(&ptr); - assert(SUCCEEDED(hr)); - assert(ptr); - - const long status = f.Read(pFile, ptr); - assert(status == 0); //all bytes were read - - hr = pSample->SetActualDataLength(srcsize); - - hr = pSample->SetPreroll(FALSE); - assert(SUCCEEDED(hr)); - - hr = pSample->SetMediaType(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetDiscontinuity(bDiscontinuity); - assert(SUCCEEDED(hr)); - - bDiscontinuity = FALSE; - - hr = pSample->SetMediaTime(0, 0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetSyncPoint(TRUE); - assert(SUCCEEDED(hr)); - - LONGLONG stop_reftime = start_reftime + frame_duration; - - hr = pSample->SetTime(&start_reftime, &stop_reftime); - assert(SUCCEEDED(hr)); - - start_reftime = stop_reftime; - } -} - - -#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter -bool AudioStream::SendPreroll(IMediaSample* pSample) -{ - assert(m_preroll); - return (this->*m_preroll)(pSample); -} - - -bool AudioStream::DoNothing(IMediaSample*) -{ - return false; -} - - -bool AudioStream::SendOggIdentPacket(IMediaSample* pSample) -{ - assert(pSample); - - const char* const id = m_pTrack->GetCodecId(); - id; - assert(id); - assert(_stricmp(id, "A_VORBIS") == 0); - - const bytes_t& cp = m_pTrack->GetCodecPrivate(); - assert(!cp.empty()); - - const ULONG cp_size = static_cast<ULONG>(cp.size()); - assert(cp_size > 0); - - const BYTE* const begin = &cp[0]; - const BYTE* const end = begin + cp_size; - end; - - const BYTE* p = begin; - assert(p < end); - - const BYTE n = *p++; - n; - assert(n == 2); - assert(p < end); - - const ULONG id_len = *p++; //TODO: don't assume < 255 - id_len; - assert(id_len < 255); - assert(id_len > 0); - assert(p < end); - - const ULONG comment_len = *p++; //TODO: don't assume < 255 - comment_len; - assert(comment_len < 255); - assert(comment_len > 0); - assert(p < end); - - //p points to first header - - const BYTE* const id_hdr = p; - - const long size = pSample->GetSize(); - size; - assert(size >= 0); - assert(ULONG(size) >= id_len); - - BYTE* buf; - - HRESULT hr = pSample->GetPointer(&buf); - assert(SUCCEEDED(hr)); - assert(buf); - - memcpy(buf, id_hdr, id_len); - - hr = pSample->SetActualDataLength(id_len); - assert(SUCCEEDED(hr)); - - hr = pSample->SetPreroll(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetMediaType(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetDiscontinuity(TRUE /* m_bDiscontinuity */ ); //TODO - assert(SUCCEEDED(hr)); - - //TODO - //set by caller: - //m_bDiscontinuity = false; - - hr = pSample->SetMediaTime(0, 0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetSyncPoint(FALSE); - assert(SUCCEEDED(hr)); - - hr = pSample->SetTime(0, 0); - assert(SUCCEEDED(hr)); - - m_preroll = &AudioStream::SendOggCommentPacket; - - return true; //preroll - don't send payload -} - - -bool AudioStream::SendOggCommentPacket(IMediaSample* pSample) -{ - assert(pSample); - - const char* const id = m_pTrack->GetCodecId(); - id; - assert(id); - assert(_stricmp(id, "A_VORBIS") == 0); - - const bytes_t& cp = m_pTrack->GetCodecPrivate(); - assert(!cp.empty()); - - const ULONG cp_size = static_cast<ULONG>(cp.size()); - assert(cp_size > 0); - - const BYTE* const begin = &cp[0]; - const BYTE* const end = begin + cp_size; - end; - - const BYTE* p = begin; - assert(p < end); - - const BYTE n = *p++; - n; - assert(n == 2); - assert(p < end); - - const ULONG id_len = *p++; //TODO: don't assume < 255 - assert(id_len < 255); - assert(id_len > 0); - assert(p < end); - - const ULONG comment_len = *p++; //TODO: don't assume < 255 - assert(comment_len < 255); - assert(comment_len > 0); - assert(p < end); - - //p points to first header - - const BYTE* const id_hdr = p; - id_hdr; - - const BYTE* const comment_hdr = id_hdr + id_len; - comment_hdr; - - const long size = pSample->GetSize(); - size; - assert(size >= 0); - assert(ULONG(size) >= comment_len); - - BYTE* buf; - - HRESULT hr = pSample->GetPointer(&buf); - assert(SUCCEEDED(hr)); - assert(buf); - - memcpy(buf, comment_hdr, comment_len); - - hr = pSample->SetActualDataLength(comment_len); - assert(SUCCEEDED(hr)); - - hr = pSample->SetPreroll(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetMediaType(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetDiscontinuity(TRUE /* m_bDiscontinuity */ ); //TODO - assert(SUCCEEDED(hr)); - - //TODO - //set by caller: - //m_bDiscontinuity = false; - - hr = pSample->SetMediaTime(0, 0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetSyncPoint(FALSE); - assert(SUCCEEDED(hr)); - - hr = pSample->SetTime(0, 0); - assert(SUCCEEDED(hr)); - - m_preroll = &AudioStream::SendOggSetupPacket; - - return true; //preroll - don't send payload -} - - -bool AudioStream::SendOggSetupPacket(IMediaSample* pSample) -{ - assert(pSample); - - const char* const id = m_pTrack->GetCodecId(); - id; - assert(id); - assert(_stricmp(id, "A_VORBIS") == 0); - - const bytes_t& cp = m_pTrack->GetCodecPrivate(); - assert(!cp.empty()); - - const ULONG cp_size = static_cast<ULONG>(cp.size()); - assert(cp_size > 0); - - const BYTE* const begin = &cp[0]; - const BYTE* const end = begin + cp_size; - - const BYTE* p = begin; - assert(p < end); - - const BYTE n = *p++; - n; - assert(n == 2); - assert(p < end); - - const ULONG id_len = *p++; //TODO: don't assume < 255 - assert(id_len < 255); - assert(id_len > 0); - assert(p < end); - - const ULONG comment_len = *p++; //TODO: don't assume < 255 - assert(comment_len < 255); - assert(comment_len > 0); - assert(p < end); - - //p points to first header - - const BYTE* const id_hdr = p; - id_hdr; - - const BYTE* const comment_hdr = id_hdr + id_len; - comment_hdr; - - const BYTE* const setup_hdr = comment_hdr + comment_len; - setup_hdr; - assert(setup_hdr < end); - - const ptrdiff_t setup_len_ = end - setup_hdr; - assert(setup_len_ > 0); - - const ULONG setup_len = static_cast<ULONG>(setup_len_); - - const long size = pSample->GetSize(); - size; - assert(size >= 0); - assert(ULONG(size) >= setup_len); - - BYTE* buf; - - HRESULT hr = pSample->GetPointer(&buf); - assert(SUCCEEDED(hr)); - assert(buf); - - memcpy(buf, setup_hdr, setup_len); - - hr = pSample->SetActualDataLength(setup_len); - assert(SUCCEEDED(hr)); - - hr = pSample->SetPreroll(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetMediaType(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetDiscontinuity(TRUE /* m_bDiscontinuity */ ); //TODO - assert(SUCCEEDED(hr)); - - //TODO - //set by caller: - //m_bDiscontinuity = false; - - hr = pSample->SetMediaTime(0, 0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetSyncPoint(FALSE); - assert(SUCCEEDED(hr)); - - hr = pSample->SetTime(0, 0); - assert(SUCCEEDED(hr)); - - m_preroll = &AudioStream::DoNothing; - - return true; //don't send payload -} -#endif - - -} //end namespace mkvparser +// 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 "mkvparserstreamaudio.h" +#include "mkvparser.hpp" +#include "vorbistypes.h" +#include "cmediatypes.h" +#include <cassert> +#include <limits> +#include <uuids.h> +#include <mmreg.h> +#include <malloc.h> +#ifdef _DEBUG +#include "odbgstream.h" +using std::endl; +#endif + + +namespace mkvparser +{ + +AudioStream* AudioStream::CreateInstance(const AudioTrack* pTrack) +{ + assert(pTrack); + + const char* const id = pTrack->GetCodecId(); + assert(id); //TODO + + if (_stricmp(id, "A_VORBIS") == 0) + __noop; + else + return 0; //can't create a stream from this track + + const __int64 channels = pTrack->GetChannels(); + + if (channels <= 0) + return 0; //bad track header + + //TODO: + //In principle, Vorbis can have multiple audio channels. + //We do handle this case in the Media Foundation components, + //so we should handle it here in the DirectShow filters too. + //END TODO. + + if (channels > 2) //TODO: handle this case + return 0; + + const double rate = pTrack->GetSamplingRate(); + + if (rate <= 0) + return 0; //bad track header + + double intrate; + const double fracrate = modf(rate, &intrate); + + if (fracrate != 0) + return 0; //bad track header + + //For Vorbis audio, the CodecPrivate part of the WebM track + //header contains the 3 Vorbis headers: the identification header, + //the comments header, and the setup header. They are all packed + //together as the payload of the CodecPrivate element, using + //Vorbis-style lacing. + + size_t cp_size; + + const BYTE* const cp = pTrack->GetCodecPrivate(cp_size); + + if ((cp == 0) || (cp_size == 0)) + return 0; //bad track header + + const BYTE* const begin = &cp[0]; + const BYTE* const end = begin + cp_size; + + const BYTE* p = begin; //to walk the payload buffer + + if (p >= end) + return 0; + + //The first byte in the stream is the number of headers + //present. For Vorbis there must be 3 headers. This is + //Vorbis-style lacing so the count is biased (it can never + //have 0 as the value), so 2 is the (biased) count we must + //have in order for this track header to be considered valid. + + const BYTE n = *p++; + + if (n != 2) //biased count value + return 0; + + if (p >= end) + return 0; + + //Immediately following the (biased) header count value is + //the lengths of the individual headers that follow. The + //physical number of lengths present is exactly equal to the + //biased count value (here, 2). The length of the last header + //is inferred from the difference between the total length + //of the buffer and the sum of the lengths of the other headers. + //(So the logical number of lengths present does equal the unbiased + //count value; here, 3 headers total.) + + //The header lengths are represented in the stream as the + //sum of a sequence of bytes; the sequence terminates for + //a particular length value when the byte has a value less + //then 255. If the byte value is 255, then this implies that + //the header length is at least 255 bytes long and so more bytes + //of the sequence follow. Note that it is valid for a byte in + //the sequence to have 0 as its value; this just means that + //the length happened to be an exact multiple of 255. + + //The length of the identification header is defined to be + //exactly 30 bytes, so its length is represented in the stream + //using 1 byte only. + + const ULONG id_len = *p++; + + if (id_len != 30) + return 0; + + if (p >= end) + return 0; + + //The comment header holds the Ogg metadata for an audio track, + //and so in principle it can be any length. Here that means that + //the length can be represented in the stream using a sequence + //comprising multiple bytes, so to determine the length we must + //loop until we find a byte whose value is less than 255. + + ULONG comment_len = 0; + + for (;;) + { + const BYTE b = *p++; + + if (p >= end) + return 0; + + comment_len += b; + + if (b < 255) + break; + } + + //Each vorbis header begins with a byte having a distinguished + //value that specifies what kind of header this is, followed + //by the string "vorbis". Therefore each well-formed header + //must be at least 7 bytes long. + + if (comment_len < 7) + return 0; + + //We have consumed the sequence of bytes used to represent + //the lengths of the individual headers. What remains in + //the stream are the actual headers. Here we don't particularly + //care much about the actual header payload (we defer such + //matters to the Vorbis decoder), but we do interrogate the + //first 7 bytes of each header to confirm that the headers + //have their correct Vorbis header-kind indicators. + + //p points the first header (the ident header) + + const BYTE* const id_hdr = p; + + //The Vorbis ident header has 1 as its kind indicator. + + if (memcmp(id_hdr, "\x01vorbis", 7) != 0) + return 0; + + const BYTE* const comment_hdr = id_hdr + id_len; + + //The Vorbis comment header has 3 as its kind indicator. + + if (memcmp(comment_hdr, "\x03vorbis", 7) != 0) + return 0; + + const BYTE* const setup_hdr = comment_hdr + comment_len; + + if (setup_hdr >= end) + return 0; + + const ptrdiff_t setup_len_ = end - setup_hdr; + + if (setup_len_ <= 0) + return 0; + + const DWORD setup_len = static_cast<DWORD>(setup_len_); + + if (setup_len < 7) + return 0; + + //The Vorbis setup header has 5 as its kind indicator. + + if (memcmp(setup_hdr, "\x05vorbis", 7) != 0) + return 0; + + //We are satisfied that the CodecPrivate value is well-formed, + //and so we now create the audio stream for this pin. + + AudioStream* const s = new (std::nothrow) AudioStream(pTrack); + assert(s); //TODO + + return s; +} + + +AudioStream::AudioStream(const AudioTrack* pTrack) : + Stream(pTrack) + //m_preroll(0) +{ +} + + +std::wostream& AudioStream::GetKind(std::wostream& os) const +{ + return os << L"Audio"; +} + + +BYTE AudioStream::GetChannels() const +{ + const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); + + const __int64 channels = pTrack->GetChannels(); + assert(channels > 0); + assert(channels <= 255); + + const BYTE result = static_cast<BYTE>(channels); + return result; +} + + +ULONG AudioStream::GetSamplesPerSec() const +{ + const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); + + const double rate = pTrack->GetSamplingRate(); + assert(rate > 0); + + double intrate_; + const double fracrate = modf(rate, &intrate_); + fracrate; + assert(fracrate == 0); + + const ULONG result = static_cast<ULONG>(intrate_); + return result; +} + + +BYTE AudioStream::GetBitsPerSample() const +{ + const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); + + const __int64 val = pTrack->GetBitDepth(); + + if (val <= 0) + return 0; + + assert(val < 256); + assert((val % 8) == 0); + + const BYTE result = static_cast<BYTE>(val); + return result; +} + + +void AudioStream::GetMediaTypes(CMediaTypes& mtv) const +{ + mtv.Clear(); + + const char* const id = m_pTrack->GetCodecId(); + assert(id); + + if (_stricmp(id, "A_VORBIS") == 0) + GetVorbisMediaTypes(mtv); + else + assert(false); +} + + +void AudioStream::GetVorbisMediaTypes(CMediaTypes& mtv) const +{ + size_t cp_size; + + const BYTE* const cp = m_pTrack->GetCodecPrivate(cp_size); + assert(cp); + assert(cp_size > 0); + + const BYTE* const begin = &cp[0]; + const BYTE* const end = begin + cp_size; + + const BYTE* p = begin; + assert(p < end); + + const BYTE n = *p++; + n; + assert(n == 2); + assert(p < end); + + const ULONG id_len = *p++; + assert(id_len == 30); + assert(p < end); + + ULONG comment_len = 0; + + for (;;) + { + const BYTE b = *p++; + assert(p < end); + + comment_len += b; + + if (b < 255) + break; + } + + assert(comment_len >= 7); + + //p points to first header + + const BYTE* const id_hdr = p; + id_hdr; + assert(memcmp(id_hdr, "\x01vorbis", 7) == 0); + + const BYTE* const comment_hdr = id_hdr + id_len; + comment_hdr; + assert(memcmp(comment_hdr, "\x03vorbis", 7) == 0); + + const BYTE* const setup_hdr = comment_hdr + comment_len; + setup_hdr; + assert(setup_hdr < end); + + const ptrdiff_t setup_len_ = end - setup_hdr; + assert(setup_len_ > 0); + + const DWORD setup_len = static_cast<DWORD>(setup_len_); + assert(setup_len >= 7); + assert(memcmp(setup_hdr, "\x05vorbis", 7) == 0); + + const size_t hdr_len = id_len + comment_len + setup_len; + + using VorbisTypes::VORBISFORMAT2; + + const size_t cb = sizeof(VORBISFORMAT2) + hdr_len; + BYTE* const pb = (BYTE*)_malloca(cb); + + VORBISFORMAT2& fmt = (VORBISFORMAT2&)(*pb); + + fmt.channels = GetChannels(); + fmt.samplesPerSec = GetSamplesPerSec(); + fmt.bitsPerSample = GetBitsPerSample(); + fmt.headerSize[0] = id_len; + fmt.headerSize[1] = comment_len; + fmt.headerSize[2] = setup_len; + + assert(p < end); + assert(size_t(end - p) == hdr_len); + + BYTE* const dst = pb + sizeof(VORBISFORMAT2); + memcpy(dst, p, hdr_len); + + 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 = static_cast<ULONG>(cb); + mt.pbFormat = pb; + + mtv.Add(mt); + + //TODO: if we decide source filter should attempt to also + //connect to Xiph Ogg Vorbis decoder filter: + //mt.majortype = VorbisTypes::MEDIATYPE_OggPacketStream; + //mt.subtype = MEDIASUBTYPE_None; + //mt.bFixedSizeSamples = FALSE; + //mt.bTemporalCompression = FALSE; + //mt.lSampleSize = 0; + //mt.formattype = VorbisTypes::FORMAT_OggIdentHeader; + //mt.pUnk = 0; + //mt.cbFormat = id_len; + //mt.pbFormat = const_cast<BYTE*>(id_hdr); + // + //mtv.Add(mt); +} + + +HRESULT AudioStream::QueryAccept(const AM_MEDIA_TYPE* pmt) const +{ + if (pmt == 0) + return E_INVALIDARG; + + const AM_MEDIA_TYPE& mt = *pmt; + + if (mt.majortype != MEDIATYPE_Audio) + return S_FALSE; + + const char* const id = m_pTrack->GetCodecId(); + assert(id); + + if (_stricmp(id, "A_VORBIS") == 0) + { + if (mt.subtype != VorbisTypes::MEDIASUBTYPE_Vorbis2) + return S_FALSE; + + return S_OK; + } + + return S_FALSE; +} + + +#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter: +HRESULT AudioStream::SetConnectionMediaType(const AM_MEDIA_TYPE&) +{ + if (mt.majortype == VorbisTypes::MEDIATYPE_OggPacketStream) + m_preroll = &AudioStream::SendOggIdentPacket; + else + m_preroll = &AudioStream::DoNothing; + + return S_OK; +} +#endif + + +#if 0 +HRESULT AudioStream::UpdateAllocatorProperties( + ALLOCATOR_PROPERTIES& props) const +{ + if (props.cBuffers < 50) //to handle laced audio + props.cBuffers = 50; + + const long size = GetBufferSize(); + + if (props.cbBuffer < size) + props.cbBuffer = size; + + if (props.cbAlign <= 0) + props.cbAlign = 1; + + if (props.cbPrefix < 0) + props.cbPrefix = 0; + + return S_OK; +} +#endif + + +long AudioStream::GetBufferSize() const +{ + const AudioTrack* const pTrack = static_cast<const AudioTrack*>(m_pTrack); + + const double rr = pTrack->GetSamplingRate(); + const long nSamplesPerSec = static_cast<long>(rr); + + const __int64 cc = pTrack->GetChannels(); + const long nChannels = static_cast<long>(cc); + + const long nBlockAlign = nChannels * 2; //16-bit PCM + const long nAvgBytesPerSec = nBlockAlign * nSamplesPerSec; + + const long size = nAvgBytesPerSec; //TODO: make a better estimate + + return size; +} + + +long AudioStream::GetBufferCount() const +{ + return 256; +} + + +void AudioStream::OnPopulateSample( + const BlockEntry* pNextEntry, + const samples_t& samples) const +{ + assert(!samples.empty()); + //assert(m_pBase); + //assert(!m_pBase->EOS()); + assert(m_pCurr); + assert(m_pCurr != m_pStop); + assert(!m_pCurr->EOS()); + + const Block* const pCurrBlock = m_pCurr->GetBlock(); + assert(pCurrBlock); + assert(pCurrBlock->GetTrackNumber() == m_pTrack->GetNumber()); + + const int nFrames = pCurrBlock->GetFrameCount(); + assert(nFrames > 0); //checked by caller + assert(samples.size() == samples_t::size_type(nFrames)); + + const Cluster* const pCurrCluster = m_pCurr->GetCluster(); + assert(pCurrCluster); + + const __int64 start_ns = pCurrBlock->GetTime(pCurrCluster); + assert(start_ns >= 0); + //assert((start_ns % 100) == 0); + + const LONGLONG base_ns = m_base_time_ns; + //assert(base_ns >= 0); + assert(start_ns >= base_ns); + + Segment* const pSegment = m_pTrack->m_pSegment; + IMkvReader* const pFile = pSegment->m_pReader; + + __int64 stop_ns; + + if ((pNextEntry == 0) || pNextEntry->EOS()) + { + const LONGLONG duration_ns = pSegment->GetDuration(); + + if ((duration_ns >= 0) && (duration_ns > start_ns)) + stop_ns = duration_ns; + else + { + const LONGLONG ns_per_frame = 10000000; //10ms + stop_ns = start_ns + LONGLONG(nFrames) * ns_per_frame; + } + } + else + { + const Block* const pNextBlock = pNextEntry->GetBlock(); + assert(pNextBlock); + + const Cluster* const pNextCluster = pNextEntry->GetCluster(); + + stop_ns = pNextBlock->GetTime(pNextCluster); + //assert(stop_ns > start_ns); + + if (stop_ns <= start_ns) + { + const LONGLONG ns_per_frame = 10000000; //10ms + stop_ns = start_ns + LONGLONG(nFrames) * ns_per_frame; + } + } + + __int64 start_reftime = (start_ns - base_ns) / 100; + assert(start_ns >= 0); + + const __int64 block_stop_reftime = (stop_ns - base_ns) / 100; + assert(block_stop_reftime > start_reftime); + + const __int64 block_duration = block_stop_reftime - start_reftime; + assert(block_duration > 0); + + __int64 frame_duration = block_duration / nFrames; //reftime units + + if (frame_duration <= 0) //weird: block duration is very small + frame_duration = 1; + + BOOL bDiscontinuity = m_bDiscontinuity ? TRUE : FALSE; + + for (int idx = 0; idx < nFrames; ++idx) + { + IMediaSample* const pSample = samples[idx]; + + const Block::Frame& f = pCurrBlock->GetFrame(idx); + + const LONG srcsize = f.len; + assert(srcsize >= 0); + + const long tgtsize = pSample->GetSize(); + tgtsize; + assert(tgtsize >= 0); + assert(tgtsize >= srcsize); + + BYTE* ptr; + + HRESULT hr = pSample->GetPointer(&ptr); + assert(SUCCEEDED(hr)); + assert(ptr); + + const long status = f.Read(pFile, ptr); + assert(status == 0); //all bytes were read + + hr = pSample->SetActualDataLength(srcsize); + + hr = pSample->SetPreroll(FALSE); + assert(SUCCEEDED(hr)); + + hr = pSample->SetMediaType(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetDiscontinuity(bDiscontinuity); + assert(SUCCEEDED(hr)); + + bDiscontinuity = FALSE; + + hr = pSample->SetMediaTime(0, 0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetSyncPoint(TRUE); + assert(SUCCEEDED(hr)); + + LONGLONG stop_reftime = start_reftime + frame_duration; + + hr = pSample->SetTime(&start_reftime, &stop_reftime); + assert(SUCCEEDED(hr)); + + start_reftime = stop_reftime; + } +} + + +#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter +bool AudioStream::SendPreroll(IMediaSample* pSample) +{ + assert(m_preroll); + return (this->*m_preroll)(pSample); +} + + +bool AudioStream::DoNothing(IMediaSample*) +{ + return false; +} + + +bool AudioStream::SendOggIdentPacket(IMediaSample* pSample) +{ + assert(pSample); + + const char* const id = m_pTrack->GetCodecId(); + id; + assert(id); + assert(_stricmp(id, "A_VORBIS") == 0); + + const bytes_t& cp = m_pTrack->GetCodecPrivate(); + assert(!cp.empty()); + + const ULONG cp_size = static_cast<ULONG>(cp.size()); + assert(cp_size > 0); + + const BYTE* const begin = &cp[0]; + const BYTE* const end = begin + cp_size; + end; + + const BYTE* p = begin; + assert(p < end); + + const BYTE n = *p++; + n; + assert(n == 2); + assert(p < end); + + const ULONG id_len = *p++; //TODO: don't assume < 255 + id_len; + assert(id_len < 255); + assert(id_len > 0); + assert(p < end); + + const ULONG comment_len = *p++; //TODO: don't assume < 255 + comment_len; + assert(comment_len < 255); + assert(comment_len > 0); + assert(p < end); + + //p points to first header + + const BYTE* const id_hdr = p; + + const long size = pSample->GetSize(); + size; + assert(size >= 0); + assert(ULONG(size) >= id_len); + + BYTE* buf; + + HRESULT hr = pSample->GetPointer(&buf); + assert(SUCCEEDED(hr)); + assert(buf); + + memcpy(buf, id_hdr, id_len); + + hr = pSample->SetActualDataLength(id_len); + assert(SUCCEEDED(hr)); + + hr = pSample->SetPreroll(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetMediaType(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetDiscontinuity(TRUE /* m_bDiscontinuity */ ); //TODO + assert(SUCCEEDED(hr)); + + //TODO + //set by caller: + //m_bDiscontinuity = false; + + hr = pSample->SetMediaTime(0, 0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetSyncPoint(FALSE); + assert(SUCCEEDED(hr)); + + hr = pSample->SetTime(0, 0); + assert(SUCCEEDED(hr)); + + m_preroll = &AudioStream::SendOggCommentPacket; + + return true; //preroll - don't send payload +} + + +bool AudioStream::SendOggCommentPacket(IMediaSample* pSample) +{ + assert(pSample); + + const char* const id = m_pTrack->GetCodecId(); + id; + assert(id); + assert(_stricmp(id, "A_VORBIS") == 0); + + const bytes_t& cp = m_pTrack->GetCodecPrivate(); + assert(!cp.empty()); + + const ULONG cp_size = static_cast<ULONG>(cp.size()); + assert(cp_size > 0); + + const BYTE* const begin = &cp[0]; + const BYTE* const end = begin + cp_size; + end; + + const BYTE* p = begin; + assert(p < end); + + const BYTE n = *p++; + n; + assert(n == 2); + assert(p < end); + + const ULONG id_len = *p++; //TODO: don't assume < 255 + assert(id_len < 255); + assert(id_len > 0); + assert(p < end); + + const ULONG comment_len = *p++; //TODO: don't assume < 255 + assert(comment_len < 255); + assert(comment_len > 0); + assert(p < end); + + //p points to first header + + const BYTE* const id_hdr = p; + id_hdr; + + const BYTE* const comment_hdr = id_hdr + id_len; + comment_hdr; + + const long size = pSample->GetSize(); + size; + assert(size >= 0); + assert(ULONG(size) >= comment_len); + + BYTE* buf; + + HRESULT hr = pSample->GetPointer(&buf); + assert(SUCCEEDED(hr)); + assert(buf); + + memcpy(buf, comment_hdr, comment_len); + + hr = pSample->SetActualDataLength(comment_len); + assert(SUCCEEDED(hr)); + + hr = pSample->SetPreroll(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetMediaType(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetDiscontinuity(TRUE /* m_bDiscontinuity */ ); //TODO + assert(SUCCEEDED(hr)); + + //TODO + //set by caller: + //m_bDiscontinuity = false; + + hr = pSample->SetMediaTime(0, 0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetSyncPoint(FALSE); + assert(SUCCEEDED(hr)); + + hr = pSample->SetTime(0, 0); + assert(SUCCEEDED(hr)); + + m_preroll = &AudioStream::SendOggSetupPacket; + + return true; //preroll - don't send payload +} + + +bool AudioStream::SendOggSetupPacket(IMediaSample* pSample) +{ + assert(pSample); + + const char* const id = m_pTrack->GetCodecId(); + id; + assert(id); + assert(_stricmp(id, "A_VORBIS") == 0); + + const bytes_t& cp = m_pTrack->GetCodecPrivate(); + assert(!cp.empty()); + + const ULONG cp_size = static_cast<ULONG>(cp.size()); + assert(cp_size > 0); + + const BYTE* const begin = &cp[0]; + const BYTE* const end = begin + cp_size; + + const BYTE* p = begin; + assert(p < end); + + const BYTE n = *p++; + n; + assert(n == 2); + assert(p < end); + + const ULONG id_len = *p++; //TODO: don't assume < 255 + assert(id_len < 255); + assert(id_len > 0); + assert(p < end); + + const ULONG comment_len = *p++; //TODO: don't assume < 255 + assert(comment_len < 255); + assert(comment_len > 0); + assert(p < end); + + //p points to first header + + const BYTE* const id_hdr = p; + id_hdr; + + const BYTE* const comment_hdr = id_hdr + id_len; + comment_hdr; + + const BYTE* const setup_hdr = comment_hdr + comment_len; + setup_hdr; + assert(setup_hdr < end); + + const ptrdiff_t setup_len_ = end - setup_hdr; + assert(setup_len_ > 0); + + const ULONG setup_len = static_cast<ULONG>(setup_len_); + + const long size = pSample->GetSize(); + size; + assert(size >= 0); + assert(ULONG(size) >= setup_len); + + BYTE* buf; + + HRESULT hr = pSample->GetPointer(&buf); + assert(SUCCEEDED(hr)); + assert(buf); + + memcpy(buf, setup_hdr, setup_len); + + hr = pSample->SetActualDataLength(setup_len); + assert(SUCCEEDED(hr)); + + hr = pSample->SetPreroll(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetMediaType(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetDiscontinuity(TRUE /* m_bDiscontinuity */ ); //TODO + assert(SUCCEEDED(hr)); + + //TODO + //set by caller: + //m_bDiscontinuity = false; + + hr = pSample->SetMediaTime(0, 0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetSyncPoint(FALSE); + assert(SUCCEEDED(hr)); + + hr = pSample->SetTime(0, 0); + assert(SUCCEEDED(hr)); + + m_preroll = &AudioStream::DoNothing; + + return true; //don't send payload +} +#endif + + +} //end namespace mkvparser
diff --git a/libmkvparser/mkvparserstreamaudio.h b/libmkvparser/mkvparserstreamaudio.h index 01d04f8..8ce4724 100644 --- a/libmkvparser/mkvparserstreamaudio.h +++ b/libmkvparser/mkvparserstreamaudio.h
@@ -1,60 +1,60 @@ -// 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 once -#include "mkvparserstream.h" - -namespace mkvparser -{ - -class AudioTrack; -class AudioStream : public Stream -{ - explicit AudioStream(const AudioTrack*); - AudioStream(const AudioStream&); - AudioStream& operator=(const AudioStream&); - -public: - static AudioStream* CreateInstance(const AudioTrack*); - - void GetMediaTypes(CMediaTypes&) const; - HRESULT QueryAccept(const AM_MEDIA_TYPE*) const; - -#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter: - HRESULT SetConnectionMediaType(const AM_MEDIA_TYPE&); -#endif - - //HRESULT UpdateAllocatorProperties(ALLOCATOR_PROPERTIES&) const; - -protected: - std::wostream& GetKind(std::wostream&) const; - - long GetBufferSize() const; - long GetBufferCount() const; - - void OnPopulateSample(const BlockEntry*, const samples_t&) const; - - void GetVorbisMediaTypes(CMediaTypes&) const; - -#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter: - bool SendPreroll(IMediaSample*); - bool (AudioStream::*m_preroll)(IMediaSample*); - bool DoNothing(IMediaSample*); - bool SendOggIdentPacket(IMediaSample*); - bool SendOggCommentPacket(IMediaSample*); - bool SendOggSetupPacket(IMediaSample*); -#endif - - BYTE GetChannels() const; - ULONG GetSamplesPerSec() const; - BYTE GetBitsPerSample() const; - -}; - - -} //end namespace mkvparser +// 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 once +#include "mkvparserstream.h" + +namespace mkvparser +{ + +class AudioTrack; +class AudioStream : public Stream +{ + explicit AudioStream(const AudioTrack*); + AudioStream(const AudioStream&); + AudioStream& operator=(const AudioStream&); + +public: + static AudioStream* CreateInstance(const AudioTrack*); + + void GetMediaTypes(CMediaTypes&) const; + HRESULT QueryAccept(const AM_MEDIA_TYPE*) const; + +#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter: + HRESULT SetConnectionMediaType(const AM_MEDIA_TYPE&); +#endif + + //HRESULT UpdateAllocatorProperties(ALLOCATOR_PROPERTIES&) const; + +protected: + std::wostream& GetKind(std::wostream&) const; + + long GetBufferSize() const; + long GetBufferCount() const; + + void OnPopulateSample(const BlockEntry*, const samples_t&) const; + + void GetVorbisMediaTypes(CMediaTypes&) const; + +#if 0 //if we decide to support Xiph Ogg Vorbis decoder filter: + bool SendPreroll(IMediaSample*); + bool (AudioStream::*m_preroll)(IMediaSample*); + bool DoNothing(IMediaSample*); + bool SendOggIdentPacket(IMediaSample*); + bool SendOggCommentPacket(IMediaSample*); + bool SendOggSetupPacket(IMediaSample*); +#endif + + BYTE GetChannels() const; + ULONG GetSamplesPerSec() const; + BYTE GetBitsPerSample() const; + +}; + + +} //end namespace mkvparser
diff --git a/libmkvparser/mkvparserstreamreader.cc b/libmkvparser/mkvparserstreamreader.cc index 38a2f84..48bace5 100644 --- a/libmkvparser/mkvparserstreamreader.cc +++ b/libmkvparser/mkvparserstreamreader.cc
@@ -1,24 +1,24 @@ -#include <objbase.h> -#include "mkvparserstreamreader.h" - -namespace mkvparser -{ - -IStreamReader::IStreamReader() -{ -} - -IStreamReader::~IStreamReader() -{ -} - -HRESULT IStreamReader::LockPages(const BlockEntry*) -{ - return S_OK; -} - -void IStreamReader::UnlockPages(const BlockEntry*) -{ -} - -} //end namespace mkvparser +#include <objbase.h> +#include "mkvparserstreamreader.h" + +namespace mkvparser +{ + +IStreamReader::IStreamReader() +{ +} + +IStreamReader::~IStreamReader() +{ +} + +HRESULT IStreamReader::LockPages(const BlockEntry*) +{ + return S_OK; +} + +void IStreamReader::UnlockPages(const BlockEntry*) +{ +} + +} //end namespace mkvparser
diff --git a/libmkvparser/mkvparserstreamreader.h b/libmkvparser/mkvparserstreamreader.h index 7eaa1d4..e1f43eb 100644 --- a/libmkvparser/mkvparserstreamreader.h +++ b/libmkvparser/mkvparserstreamreader.h
@@ -1,22 +1,22 @@ -#pragma once -#include "mkvparser.hpp" - -namespace mkvparser -{ - class IStreamReader : public IMkvReader - { - private: - IStreamReader(const IStreamReader&); - IStreamReader& operator=(const IStreamReader&); - - protected: - IStreamReader(); - virtual ~IStreamReader(); - - public: - virtual HRESULT LockPages(const BlockEntry*); - virtual void UnlockPages(const BlockEntry*); - - }; - -} //end namespace mkvparser +#pragma once +#include "mkvparser.hpp" + +namespace mkvparser +{ + class IStreamReader : public IMkvReader + { + private: + IStreamReader(const IStreamReader&); + IStreamReader& operator=(const IStreamReader&); + + protected: + IStreamReader(); + virtual ~IStreamReader(); + + public: + virtual HRESULT LockPages(const BlockEntry*); + virtual void UnlockPages(const BlockEntry*); + + }; + +} //end namespace mkvparser
diff --git a/libmkvparser/mkvparserstreamvideo.cc b/libmkvparser/mkvparserstreamvideo.cc index 15cd51d..6427762 100644 --- a/libmkvparser/mkvparserstreamvideo.cc +++ b/libmkvparser/mkvparserstreamvideo.cc
@@ -1,476 +1,476 @@ -// 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 "mkvparserstreamvideo.h" -#include "mkvparser.hpp" -#include "webmtypes.h" -#include "graphutil.h" -#include "cmediatypes.h" -#include <cassert> -#include <amvideo.h> -#include <dvdmedia.h> -#include <uuids.h> -#include <limits> -#ifdef _DEBUG -#include "odbgstream.h" -using std::endl; -#endif - -static const char* const s_CodecId_VP8 = "V_VP8"; -static const char* const s_CodecId_VP9 = "V_VP9"; -static const char* const s_CodecId_ON2VP8 = "V_ON2VP8"; -static const char* const s_CodecId_VFW = "V_MS/VFW/FOURCC"; - -namespace mkvparser -{ - - -VideoStream* VideoStream::CreateInstance(const VideoTrack* pTrack) -{ - assert(pTrack); - - const char* const id = pTrack->GetCodecId(); - assert(id); //TODO - - if (_stricmp(id, s_CodecId_VP8) == 0) - __noop; - else if (_stricmp(id, s_CodecId_VP9) == 0) - __noop; - else if (_stricmp(id, s_CodecId_ON2VP8) == 0) - __noop; - else if (_stricmp(id, s_CodecId_VFW) == 0) - __noop; - else - return 0; //can't create a stream from this track - - //TODO: vet settings, etc - - //At least one cluster should have been loaded when we opened - //the file. We search the first cluster for a frame having - //this track number, and use that to start from. If no frame - //with this track number is present in first cluster, then - //we assume (correctly or incorrectly) that this file doesn't - //have any frames from that track at all. - - VideoStream* const s = new (std::nothrow) VideoStream(pTrack); - assert(s); //TODO - - return s; -} - - -VideoStream::VideoStream(const VideoTrack* pTrack) : Stream(pTrack) -{ -} - - -std::wostream& VideoStream::GetKind(std::wostream& os) const -{ - return os << L"Video"; -} - - -void VideoStream::GetMediaTypes(CMediaTypes& mtv) const -{ - mtv.Clear(); - - const char* const id = m_pTrack->GetCodecId(); - assert(id); - - if ((_stricmp(id, s_CodecId_VP8) == 0) || - (_stricmp(id, s_CodecId_ON2VP8) == 0)) - { - GetVpxMediaTypes(WebmTypes::MEDIASUBTYPE_VP80, mtv); - } - else if (_stricmp(id, s_CodecId_VP9) == 0) - { - GetVpxMediaTypes(WebmTypes::MEDIASUBTYPE_VP90, mtv); - } - else if (_stricmp(id, s_CodecId_VFW) == 0) - { - GetVfwMediaTypes(mtv); - } -} - - -void VideoStream::GetVpxMediaTypes(const GUID& subtype, CMediaTypes& mtv) const -{ - AM_MEDIA_TYPE mt; - - VIDEOINFOHEADER vih; - BITMAPINFOHEADER& bmih = vih.bmiHeader; - - mt.majortype = MEDIATYPE_Video; - mt.subtype = subtype; - mt.bFixedSizeSamples = FALSE; - mt.bTemporalCompression = TRUE; - mt.lSampleSize = 0; - mt.formattype = FORMAT_VideoInfo; - mt.pUnk = 0; - mt.cbFormat = sizeof vih; - mt.pbFormat = (BYTE*)&vih; - - SetRectEmpty(&vih.rcSource); //TODO - SetRectEmpty(&vih.rcTarget); - vih.dwBitRate = 0; - vih.dwBitErrorRate = 0; - - const VideoTrack* const pTrack = static_cast<const VideoTrack*>(m_pTrack); - - const double r = pTrack->GetFrameRate(); - - if (r <= 0) - vih.AvgTimePerFrame = 0; - else - { - const double tt = 10000000 / r; //[ticks/sec] / [frames/sec] - vih.AvgTimePerFrame = static_cast<__int64>(tt); - } - - const __int64 w = pTrack->GetWidth(); - assert(w > 0); - assert(w <= LONG_MAX); - - const __int64 h = pTrack->GetHeight(); - assert(h > 0); - assert(h <= LONG_MAX); - - bmih.biSize = sizeof bmih; - bmih.biWidth = static_cast<LONG>(w); - bmih.biHeight = static_cast<LONG>(h); - bmih.biPlanes = 1; - bmih.biBitCount = 0; - bmih.biCompression = mt.subtype.Data1; - bmih.biSizeImage = 0; - bmih.biXPelsPerMeter = 0; - bmih.biYPelsPerMeter = 0; - bmih.biClrUsed = 0; - bmih.biClrImportant = 0; - - mtv.Add(mt); -} - - -void VideoStream::GetVfwMediaTypes(CMediaTypes& mtv) const -{ - size_t cp_size; - - const BYTE* const cp = m_pTrack->GetCodecPrivate(cp_size); - assert(cp); - assert(cp_size >= sizeof(BITMAPINFOHEADER)); - - AM_MEDIA_TYPE mt; - - VIDEOINFOHEADER vih; - BITMAPINFOHEADER& bmih = vih.bmiHeader; - - memcpy(&bmih, &cp[0], sizeof bmih); - assert(bmih.biSize >= sizeof(BITMAPINFOHEADER)); - - mt.majortype = MEDIATYPE_Video; - mt.subtype = GraphUtil::FourCCGUID(bmih.biCompression); - mt.bFixedSizeSamples = FALSE; - mt.bTemporalCompression = TRUE; - mt.lSampleSize = 0; - mt.formattype = FORMAT_VideoInfo; - mt.pUnk = 0; - mt.cbFormat = sizeof vih; - mt.pbFormat = (BYTE*)&vih; - - SetRectEmpty(&vih.rcSource); //TODO - SetRectEmpty(&vih.rcTarget); - vih.dwBitRate = 0; - vih.dwBitErrorRate = 0; - - const VideoTrack* const pTrack = static_cast<const VideoTrack*>(m_pTrack); - - const double r = pTrack->GetFrameRate(); - - if (r <= 0) - vih.AvgTimePerFrame = 0; - else - { - const double tt = 10000000 / r; //[ticks/sec] / [frames/sec] - vih.AvgTimePerFrame = static_cast<__int64>(tt); - } - - const __int64 w = pTrack->GetWidth(); - w; - assert(w > 0); - assert(w <= LONG_MAX); - assert(w == bmih.biWidth); - - const __int64 h = pTrack->GetHeight(); - h; - assert(h > 0); - assert(h <= LONG_MAX); - assert(h == bmih.biHeight); - - mtv.Add(mt); -} - - -HRESULT VideoStream::QueryAccept(const AM_MEDIA_TYPE* pmt) const -{ - if (pmt == 0) - return E_INVALIDARG; - - const AM_MEDIA_TYPE& mt = *pmt; - - if (mt.majortype != MEDIATYPE_Video) - return S_FALSE; - - const char* const id = m_pTrack->GetCodecId(); - assert(id); - - if ((_stricmp(id, s_CodecId_VP8) == 0) || - (_stricmp(id, s_CodecId_ON2VP8) == 0)) - { - if (mt.subtype != WebmTypes::MEDIASUBTYPE_VP80) - return S_FALSE; - - //TODO: more vetting here - - return S_OK; - } - - if (_stricmp(id, s_CodecId_VP9) == 0) - { - if (mt.subtype != WebmTypes::MEDIASUBTYPE_VP90) - return S_FALSE; - - return S_OK; - } - - if (_stricmp(id, s_CodecId_VFW) == 0) - { - if (!GraphUtil::FourCCGUID::IsFourCC(mt.subtype)) - return S_FALSE; - - size_t cp_size; - const BYTE* const cp = m_pTrack->GetCodecPrivate(cp_size); - - if (cp == 0) - return S_FALSE; - - if (cp_size < sizeof(BITMAPINFOHEADER)) - return S_FALSE; - - BITMAPINFOHEADER bmih; - - memcpy(&bmih, &cp[0], sizeof bmih); - - if (bmih.biSize < sizeof bmih) - return S_FALSE; - - //TODO: more vetting here - - const DWORD fcc = mt.subtype.Data1; //"VP80" or "VP90" - - if (fcc != bmih.biCompression) - return S_FALSE; - - if (mt.subtype == WebmTypes::MEDIASUBTYPE_VP80) - __noop; - else if (mt.subtype == WebmTypes::MEDIASUBTYPE_VP90) - __noop; - else - return S_FALSE; - - return S_OK; - } - - return S_FALSE; -} - - -#if 0 -HRESULT VideoStream::UpdateAllocatorProperties( - ALLOCATOR_PROPERTIES& props) const -{ - if (props.cBuffers <= cBuffers) //to handle laced video - props.cBuffers = cBuffers; - - const long size = GetBufferSize(); - - if (props.cbBuffer < size) - props.cbBuffer = size; - - if (props.cbAlign <= 0) - props.cbAlign = 1; - - if (props.cbPrefix < 0) - props.cbPrefix = 0; - - return S_OK; -} -#endif - - -long VideoStream::GetBufferSize() const -{ - const VideoTrack* const pTrack = static_cast<const VideoTrack*>(m_pTrack); - - const __int64 w = pTrack->GetWidth(); - const __int64 h = pTrack->GetHeight(); - - //TODO: we can do better here. VPx is based on YV12, which would waste - //less memory than assuming RGB32. - const __int64 size_ = w * h * 4; //RGB32 (worst case) - assert(size_ <= LONG_MAX); - - const long size = static_cast<LONG>(size_); - - return size; -} - - -long VideoStream::GetBufferCount() const -{ - return 10; //? -} - - -void VideoStream::OnPopulateSample( - const BlockEntry* pNextEntry, - const samples_t& samples) const -{ - assert(!samples.empty()); - //assert(m_pBase); - //assert(!m_pBase->EOS()); - assert(m_pCurr); - assert(m_pCurr != m_pStop); - assert(!m_pCurr->EOS()); - - const Block* const pCurrBlock = m_pCurr->GetBlock(); - assert(pCurrBlock); - assert(pCurrBlock->GetTrackNumber() == m_pTrack->GetNumber()); - - const Cluster* const pCurrCluster = m_pCurr->GetCluster(); - assert(pCurrCluster); - - assert((m_pStop == 0) || - m_pStop->EOS() || - (m_pStop->GetBlock()->GetTimeCode(m_pStop->GetCluster()) > - pCurrBlock->GetTimeCode(pCurrCluster))); - - const int nFrames = pCurrBlock->GetFrameCount(); - assert(nFrames > 0); //checked by caller - assert(samples.size() == samples_t::size_type(nFrames)); - - const LONGLONG base_ns = m_base_time_ns; - //assert(base_ns >= 0); - - Segment* const pSegment = m_pTrack->m_pSegment; - IMkvReader* const pFile = pSegment->m_pReader; - - const bool bKey = pCurrBlock->IsKey(); - assert(!m_bDiscontinuity || bKey); - - const bool bInvisible = pCurrBlock->IsInvisible(); - - const __int64 start_ns = pCurrBlock->GetTime(pCurrCluster); - assert(start_ns >= base_ns); - //assert((start_ns % 100) == 0); - - __int64 stop_ns; - - if ((pNextEntry == 0) || pNextEntry->EOS()) - { - //TODO: read duration from block group, if present - - const LONGLONG duration_ns = pSegment->GetDuration(); - - if ((duration_ns >= 0) && (duration_ns > start_ns)) - stop_ns = duration_ns; - else - stop_ns = start_ns + 1000000; //add 1ms - } - else - { - const Block* const pNextBlock = pNextEntry->GetBlock(); - assert(pNextBlock); - - const Cluster* const pNextCluster = pNextEntry->GetCluster(); - - stop_ns = pNextBlock->GetTime(pNextCluster); - assert(stop_ns >= start_ns); - //assert((stop_ns % 100) == 0); - } - - __int64 start_reftime = (start_ns - base_ns) / 100; - - const __int64 block_stop_reftime = (stop_ns - base_ns) / 100; - assert(block_stop_reftime >= start_reftime); - - const __int64 block_duration = block_stop_reftime - start_reftime; - assert(block_duration >= 0); - - __int64 frame_duration = block_duration / nFrames; //reftime units - - if ((nFrames > 1) && (frame_duration <= 0)) //weird: small block duration - frame_duration = 1; - - BOOL bDiscontinuity = m_bDiscontinuity ? TRUE : FALSE; - - for (int idx = 0; idx < nFrames; ++idx) - { - IMediaSample* const pSample = samples[idx]; - - const Block::Frame& f = pCurrBlock->GetFrame(idx); - - const LONG srcsize = f.len; - assert(srcsize >= 0); - - const long tgtsize = pSample->GetSize(); - tgtsize; - assert(tgtsize >= 0); - assert(tgtsize >= srcsize); - - BYTE* ptr; - - HRESULT hr = pSample->GetPointer(&ptr); //read srcsize bytes - assert(SUCCEEDED(hr)); - assert(ptr); - - const long status = f.Read(pFile, ptr); - assert(status == 0); //all bytes were read - - hr = pSample->SetActualDataLength(srcsize); - - hr = pSample->SetPreroll(bInvisible ? TRUE : FALSE); - assert(SUCCEEDED(hr)); - - hr = pSample->SetMediaType(0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetDiscontinuity(bDiscontinuity); - assert(SUCCEEDED(hr)); - - bDiscontinuity = FALSE; - - hr = pSample->SetMediaTime(0, 0); - assert(SUCCEEDED(hr)); - - hr = pSample->SetSyncPoint(bKey ? TRUE : FALSE); - assert(SUCCEEDED(hr)); - - LONGLONG stop_reftime = start_reftime + frame_duration; - - hr = pSample->SetTime(&start_reftime, &stop_reftime); - assert(SUCCEEDED(hr)); - - start_reftime = stop_reftime; - } -} - - - - -} //end namespace mkvparser +// 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 "mkvparserstreamvideo.h" +#include "mkvparser.hpp" +#include "webmtypes.h" +#include "graphutil.h" +#include "cmediatypes.h" +#include <cassert> +#include <amvideo.h> +#include <dvdmedia.h> +#include <uuids.h> +#include <limits> +#ifdef _DEBUG +#include "odbgstream.h" +using std::endl; +#endif + +static const char* const s_CodecId_VP8 = "V_VP8"; +static const char* const s_CodecId_VP9 = "V_VP9"; +static const char* const s_CodecId_ON2VP8 = "V_ON2VP8"; +static const char* const s_CodecId_VFW = "V_MS/VFW/FOURCC"; + +namespace mkvparser +{ + + +VideoStream* VideoStream::CreateInstance(const VideoTrack* pTrack) +{ + assert(pTrack); + + const char* const id = pTrack->GetCodecId(); + assert(id); //TODO + + if (_stricmp(id, s_CodecId_VP8) == 0) + __noop; + else if (_stricmp(id, s_CodecId_VP9) == 0) + __noop; + else if (_stricmp(id, s_CodecId_ON2VP8) == 0) + __noop; + else if (_stricmp(id, s_CodecId_VFW) == 0) + __noop; + else + return 0; //can't create a stream from this track + + //TODO: vet settings, etc + + //At least one cluster should have been loaded when we opened + //the file. We search the first cluster for a frame having + //this track number, and use that to start from. If no frame + //with this track number is present in first cluster, then + //we assume (correctly or incorrectly) that this file doesn't + //have any frames from that track at all. + + VideoStream* const s = new (std::nothrow) VideoStream(pTrack); + assert(s); //TODO + + return s; +} + + +VideoStream::VideoStream(const VideoTrack* pTrack) : Stream(pTrack) +{ +} + + +std::wostream& VideoStream::GetKind(std::wostream& os) const +{ + return os << L"Video"; +} + + +void VideoStream::GetMediaTypes(CMediaTypes& mtv) const +{ + mtv.Clear(); + + const char* const id = m_pTrack->GetCodecId(); + assert(id); + + if ((_stricmp(id, s_CodecId_VP8) == 0) || + (_stricmp(id, s_CodecId_ON2VP8) == 0)) + { + GetVpxMediaTypes(WebmTypes::MEDIASUBTYPE_VP80, mtv); + } + else if (_stricmp(id, s_CodecId_VP9) == 0) + { + GetVpxMediaTypes(WebmTypes::MEDIASUBTYPE_VP90, mtv); + } + else if (_stricmp(id, s_CodecId_VFW) == 0) + { + GetVfwMediaTypes(mtv); + } +} + + +void VideoStream::GetVpxMediaTypes(const GUID& subtype, CMediaTypes& mtv) const +{ + AM_MEDIA_TYPE mt; + + VIDEOINFOHEADER vih; + BITMAPINFOHEADER& bmih = vih.bmiHeader; + + mt.majortype = MEDIATYPE_Video; + mt.subtype = subtype; + mt.bFixedSizeSamples = FALSE; + mt.bTemporalCompression = TRUE; + mt.lSampleSize = 0; + mt.formattype = FORMAT_VideoInfo; + mt.pUnk = 0; + mt.cbFormat = sizeof vih; + mt.pbFormat = (BYTE*)&vih; + + SetRectEmpty(&vih.rcSource); //TODO + SetRectEmpty(&vih.rcTarget); + vih.dwBitRate = 0; + vih.dwBitErrorRate = 0; + + const VideoTrack* const pTrack = static_cast<const VideoTrack*>(m_pTrack); + + const double r = pTrack->GetFrameRate(); + + if (r <= 0) + vih.AvgTimePerFrame = 0; + else + { + const double tt = 10000000 / r; //[ticks/sec] / [frames/sec] + vih.AvgTimePerFrame = static_cast<__int64>(tt); + } + + const __int64 w = pTrack->GetWidth(); + assert(w > 0); + assert(w <= LONG_MAX); + + const __int64 h = pTrack->GetHeight(); + assert(h > 0); + assert(h <= LONG_MAX); + + bmih.biSize = sizeof bmih; + bmih.biWidth = static_cast<LONG>(w); + bmih.biHeight = static_cast<LONG>(h); + bmih.biPlanes = 1; + bmih.biBitCount = 0; + bmih.biCompression = mt.subtype.Data1; + bmih.biSizeImage = 0; + bmih.biXPelsPerMeter = 0; + bmih.biYPelsPerMeter = 0; + bmih.biClrUsed = 0; + bmih.biClrImportant = 0; + + mtv.Add(mt); +} + + +void VideoStream::GetVfwMediaTypes(CMediaTypes& mtv) const +{ + size_t cp_size; + + const BYTE* const cp = m_pTrack->GetCodecPrivate(cp_size); + assert(cp); + assert(cp_size >= sizeof(BITMAPINFOHEADER)); + + AM_MEDIA_TYPE mt; + + VIDEOINFOHEADER vih; + BITMAPINFOHEADER& bmih = vih.bmiHeader; + + memcpy(&bmih, &cp[0], sizeof bmih); + assert(bmih.biSize >= sizeof(BITMAPINFOHEADER)); + + mt.majortype = MEDIATYPE_Video; + mt.subtype = GraphUtil::FourCCGUID(bmih.biCompression); + mt.bFixedSizeSamples = FALSE; + mt.bTemporalCompression = TRUE; + mt.lSampleSize = 0; + mt.formattype = FORMAT_VideoInfo; + mt.pUnk = 0; + mt.cbFormat = sizeof vih; + mt.pbFormat = (BYTE*)&vih; + + SetRectEmpty(&vih.rcSource); //TODO + SetRectEmpty(&vih.rcTarget); + vih.dwBitRate = 0; + vih.dwBitErrorRate = 0; + + const VideoTrack* const pTrack = static_cast<const VideoTrack*>(m_pTrack); + + const double r = pTrack->GetFrameRate(); + + if (r <= 0) + vih.AvgTimePerFrame = 0; + else + { + const double tt = 10000000 / r; //[ticks/sec] / [frames/sec] + vih.AvgTimePerFrame = static_cast<__int64>(tt); + } + + const __int64 w = pTrack->GetWidth(); + w; + assert(w > 0); + assert(w <= LONG_MAX); + assert(w == bmih.biWidth); + + const __int64 h = pTrack->GetHeight(); + h; + assert(h > 0); + assert(h <= LONG_MAX); + assert(h == bmih.biHeight); + + mtv.Add(mt); +} + + +HRESULT VideoStream::QueryAccept(const AM_MEDIA_TYPE* pmt) const +{ + if (pmt == 0) + return E_INVALIDARG; + + const AM_MEDIA_TYPE& mt = *pmt; + + if (mt.majortype != MEDIATYPE_Video) + return S_FALSE; + + const char* const id = m_pTrack->GetCodecId(); + assert(id); + + if ((_stricmp(id, s_CodecId_VP8) == 0) || + (_stricmp(id, s_CodecId_ON2VP8) == 0)) + { + if (mt.subtype != WebmTypes::MEDIASUBTYPE_VP80) + return S_FALSE; + + //TODO: more vetting here + + return S_OK; + } + + if (_stricmp(id, s_CodecId_VP9) == 0) + { + if (mt.subtype != WebmTypes::MEDIASUBTYPE_VP90) + return S_FALSE; + + return S_OK; + } + + if (_stricmp(id, s_CodecId_VFW) == 0) + { + if (!GraphUtil::FourCCGUID::IsFourCC(mt.subtype)) + return S_FALSE; + + size_t cp_size; + const BYTE* const cp = m_pTrack->GetCodecPrivate(cp_size); + + if (cp == 0) + return S_FALSE; + + if (cp_size < sizeof(BITMAPINFOHEADER)) + return S_FALSE; + + BITMAPINFOHEADER bmih; + + memcpy(&bmih, &cp[0], sizeof bmih); + + if (bmih.biSize < sizeof bmih) + return S_FALSE; + + //TODO: more vetting here + + const DWORD fcc = mt.subtype.Data1; //"VP80" or "VP90" + + if (fcc != bmih.biCompression) + return S_FALSE; + + if (mt.subtype == WebmTypes::MEDIASUBTYPE_VP80) + __noop; + else if (mt.subtype == WebmTypes::MEDIASUBTYPE_VP90) + __noop; + else + return S_FALSE; + + return S_OK; + } + + return S_FALSE; +} + + +#if 0 +HRESULT VideoStream::UpdateAllocatorProperties( + ALLOCATOR_PROPERTIES& props) const +{ + if (props.cBuffers <= cBuffers) //to handle laced video + props.cBuffers = cBuffers; + + const long size = GetBufferSize(); + + if (props.cbBuffer < size) + props.cbBuffer = size; + + if (props.cbAlign <= 0) + props.cbAlign = 1; + + if (props.cbPrefix < 0) + props.cbPrefix = 0; + + return S_OK; +} +#endif + + +long VideoStream::GetBufferSize() const +{ + const VideoTrack* const pTrack = static_cast<const VideoTrack*>(m_pTrack); + + const __int64 w = pTrack->GetWidth(); + const __int64 h = pTrack->GetHeight(); + + //TODO: we can do better here. VPx is based on YV12, which would waste + //less memory than assuming RGB32. + const __int64 size_ = w * h * 4; //RGB32 (worst case) + assert(size_ <= LONG_MAX); + + const long size = static_cast<LONG>(size_); + + return size; +} + + +long VideoStream::GetBufferCount() const +{ + return 10; //? +} + + +void VideoStream::OnPopulateSample( + const BlockEntry* pNextEntry, + const samples_t& samples) const +{ + assert(!samples.empty()); + //assert(m_pBase); + //assert(!m_pBase->EOS()); + assert(m_pCurr); + assert(m_pCurr != m_pStop); + assert(!m_pCurr->EOS()); + + const Block* const pCurrBlock = m_pCurr->GetBlock(); + assert(pCurrBlock); + assert(pCurrBlock->GetTrackNumber() == m_pTrack->GetNumber()); + + const Cluster* const pCurrCluster = m_pCurr->GetCluster(); + assert(pCurrCluster); + + assert((m_pStop == 0) || + m_pStop->EOS() || + (m_pStop->GetBlock()->GetTimeCode(m_pStop->GetCluster()) > + pCurrBlock->GetTimeCode(pCurrCluster))); + + const int nFrames = pCurrBlock->GetFrameCount(); + assert(nFrames > 0); //checked by caller + assert(samples.size() == samples_t::size_type(nFrames)); + + const LONGLONG base_ns = m_base_time_ns; + //assert(base_ns >= 0); + + Segment* const pSegment = m_pTrack->m_pSegment; + IMkvReader* const pFile = pSegment->m_pReader; + + const bool bKey = pCurrBlock->IsKey(); + assert(!m_bDiscontinuity || bKey); + + const bool bInvisible = pCurrBlock->IsInvisible(); + + const __int64 start_ns = pCurrBlock->GetTime(pCurrCluster); + assert(start_ns >= base_ns); + //assert((start_ns % 100) == 0); + + __int64 stop_ns; + + if ((pNextEntry == 0) || pNextEntry->EOS()) + { + //TODO: read duration from block group, if present + + const LONGLONG duration_ns = pSegment->GetDuration(); + + if ((duration_ns >= 0) && (duration_ns > start_ns)) + stop_ns = duration_ns; + else + stop_ns = start_ns + 1000000; //add 1ms + } + else + { + const Block* const pNextBlock = pNextEntry->GetBlock(); + assert(pNextBlock); + + const Cluster* const pNextCluster = pNextEntry->GetCluster(); + + stop_ns = pNextBlock->GetTime(pNextCluster); + assert(stop_ns >= start_ns); + //assert((stop_ns % 100) == 0); + } + + __int64 start_reftime = (start_ns - base_ns) / 100; + + const __int64 block_stop_reftime = (stop_ns - base_ns) / 100; + assert(block_stop_reftime >= start_reftime); + + const __int64 block_duration = block_stop_reftime - start_reftime; + assert(block_duration >= 0); + + __int64 frame_duration = block_duration / nFrames; //reftime units + + if ((nFrames > 1) && (frame_duration <= 0)) //weird: small block duration + frame_duration = 1; + + BOOL bDiscontinuity = m_bDiscontinuity ? TRUE : FALSE; + + for (int idx = 0; idx < nFrames; ++idx) + { + IMediaSample* const pSample = samples[idx]; + + const Block::Frame& f = pCurrBlock->GetFrame(idx); + + const LONG srcsize = f.len; + assert(srcsize >= 0); + + const long tgtsize = pSample->GetSize(); + tgtsize; + assert(tgtsize >= 0); + assert(tgtsize >= srcsize); + + BYTE* ptr; + + HRESULT hr = pSample->GetPointer(&ptr); //read srcsize bytes + assert(SUCCEEDED(hr)); + assert(ptr); + + const long status = f.Read(pFile, ptr); + assert(status == 0); //all bytes were read + + hr = pSample->SetActualDataLength(srcsize); + + hr = pSample->SetPreroll(bInvisible ? TRUE : FALSE); + assert(SUCCEEDED(hr)); + + hr = pSample->SetMediaType(0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetDiscontinuity(bDiscontinuity); + assert(SUCCEEDED(hr)); + + bDiscontinuity = FALSE; + + hr = pSample->SetMediaTime(0, 0); + assert(SUCCEEDED(hr)); + + hr = pSample->SetSyncPoint(bKey ? TRUE : FALSE); + assert(SUCCEEDED(hr)); + + LONGLONG stop_reftime = start_reftime + frame_duration; + + hr = pSample->SetTime(&start_reftime, &stop_reftime); + assert(SUCCEEDED(hr)); + + start_reftime = stop_reftime; + } +} + + + + +} //end namespace mkvparser
diff --git a/libmkvparser/mkvparserstreamvideo.h b/libmkvparser/mkvparserstreamvideo.h index 2492364..3bc3702 100644 --- a/libmkvparser/mkvparserstreamvideo.h +++ b/libmkvparser/mkvparserstreamvideo.h
@@ -1,44 +1,44 @@ -// 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 once -#include "mkvparserstream.h" - -namespace mkvparser -{ - -class VideoTrack; - -class VideoStream : public Stream -{ - explicit VideoStream(const VideoTrack*); - VideoStream(const VideoStream&); - VideoStream& operator=(const VideoStream&); - -public: - static VideoStream* CreateInstance(const VideoTrack*); - - void GetMediaTypes(CMediaTypes&) const; - HRESULT QueryAccept(const AM_MEDIA_TYPE*) const; - //HRESULT UpdateAllocatorProperties(ALLOCATOR_PROPERTIES&) const; - -protected: - std::wostream& GetKind(std::wostream&) const; - - long GetBufferSize() const; - long GetBufferCount() const; - - void OnPopulateSample(const BlockEntry*, const samples_t&) const; - - void GetVpxMediaTypes(const GUID& subtype, CMediaTypes&) const; - void GetVfwMediaTypes(CMediaTypes&) const; - -}; - - -} //end namespace mkvparser +// 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 once +#include "mkvparserstream.h" + +namespace mkvparser +{ + +class VideoTrack; + +class VideoStream : public Stream +{ + explicit VideoStream(const VideoTrack*); + VideoStream(const VideoStream&); + VideoStream& operator=(const VideoStream&); + +public: + static VideoStream* CreateInstance(const VideoTrack*); + + void GetMediaTypes(CMediaTypes&) const; + HRESULT QueryAccept(const AM_MEDIA_TYPE*) const; + //HRESULT UpdateAllocatorProperties(ALLOCATOR_PROPERTIES&) const; + +protected: + std::wostream& GetKind(std::wostream&) const; + + long GetBufferSize() const; + long GetBufferCount() const; + + void OnPopulateSample(const BlockEntry*, const samples_t&) const; + + void GetVpxMediaTypes(const GUID& subtype, CMediaTypes&) const; + void GetVfwMediaTypes(CMediaTypes&) const; + +}; + + +} //end namespace mkvparser