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