244 lines
5.6 KiB
C++
244 lines
5.6 KiB
C++
#include "cues.hpp"
|
|
#include "segment.hpp"
|
|
#include "ebmlreadhelpers.hpp"
|
|
#include "mkvprofiler.hpp"
|
|
|
|
#include <stddef.h>
|
|
#include <assert.h>
|
|
#include <algorithm>
|
|
|
|
namespace mkvparser
|
|
{
|
|
|
|
Cues::Cues(Segment* pSegment, const EBMLElement& elemInfo)
|
|
: m_pSegment(pSegment)
|
|
, m_elemInfo(elemInfo)
|
|
, m_pos(m_elemInfo.GetPayloadPos())
|
|
{
|
|
const size_t initialCuePointsSize = 2048;
|
|
m_cue_points.reserve(initialCuePointsSize);
|
|
}
|
|
|
|
|
|
Cues::~Cues()
|
|
{
|
|
CuePointVect_t::iterator first = m_cue_points.begin();
|
|
CuePointVect_t::iterator last = m_cue_points.end();
|
|
|
|
for( ; first != last; ++first)
|
|
{
|
|
CuePoint* const pCP = *first;
|
|
assert(pCP);
|
|
|
|
delete pCP;
|
|
}
|
|
}
|
|
|
|
|
|
long Cues::GetCount() const
|
|
{
|
|
return static_cast<long>(m_cue_points.size());
|
|
}
|
|
|
|
|
|
bool Cues::DoneParsing() const
|
|
{
|
|
return (m_pos >= m_elemInfo.GetEndPos());
|
|
}
|
|
|
|
|
|
bool Cues::LoadCuePoint()
|
|
{
|
|
PROFILER_SCOPED_EVENT("webm::Cues::LoadCuePoint");
|
|
|
|
//odbgstream os;
|
|
//os << "Cues::LoadCuePoint" << endl;
|
|
|
|
const long long stop = m_elemInfo.GetEndPos();
|
|
|
|
if (m_pos >= stop)
|
|
return false; //nothing else to do
|
|
|
|
IMkvReader* const pReader = m_pSegment->GetReader();
|
|
|
|
while (m_pos < stop)
|
|
{
|
|
EBMLElement elemInfo;
|
|
long long result = elemInfo.Parse(pReader, m_pos);
|
|
assert (result == 0);
|
|
|
|
if (elemInfo.GetID() != 0x3B) //CuePoint ID
|
|
{
|
|
m_pos += elemInfo.GetSize(); //consume payload
|
|
assert(m_pos <= stop);
|
|
|
|
continue;
|
|
}
|
|
|
|
const long idx = static_cast<long>(m_cue_points.size());
|
|
CuePoint* const pCP = new CuePoint(idx, m_pos);
|
|
assert(pCP);
|
|
assert((pCP->GetTimeCode() >= 0) || (-pCP->GetTimeCode() == m_pos));
|
|
|
|
pCP->Load(pReader);
|
|
|
|
m_cue_points.push_back(pCP);
|
|
|
|
m_pos += elemInfo.GetSize(); //consume payload
|
|
assert(m_pos <= stop);
|
|
|
|
return true; //yes, we loaded a cue point
|
|
}
|
|
|
|
//return (m_pos < stop);
|
|
return false; //no, we did not load a cue point
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
struct CuePointTimeLT
|
|
{
|
|
CuePointTimeLT(long long time_ns, const Segment* segment)
|
|
: m_time_ns(time_ns), m_segment(segment) {}
|
|
|
|
// one of the entries can be 0 (see usage in Cues::Find)
|
|
bool operator()(const CuePoint* left, const CuePoint* right) const
|
|
{
|
|
if(left)
|
|
{
|
|
const long long t = left->GetTime(m_segment);
|
|
return t < m_time_ns;
|
|
}
|
|
if (right)
|
|
{
|
|
const long long t = right->GetTime(m_segment);
|
|
return t < m_time_ns;
|
|
}
|
|
return false;
|
|
}
|
|
long long m_time_ns;
|
|
const Segment* m_segment;
|
|
};
|
|
}
|
|
|
|
|
|
bool Cues::Find(
|
|
long long time_ns,
|
|
const Track* pTrack,
|
|
const CuePoint*& pCP,
|
|
const CuePoint::TrackPosition*& pTP) const
|
|
{
|
|
assert(time_ns >= 0);
|
|
assert(pTrack);
|
|
|
|
if (m_cue_points.empty())
|
|
return false;
|
|
|
|
// in this lower_bound call, do not use the value (set to 0), the value to compare is set in CuePointTimeLT()
|
|
CuePointVect_t:: const_iterator it = std::lower_bound(m_cue_points.begin(),
|
|
m_cue_points.end(),
|
|
static_cast<const CuePoint*>(0),
|
|
CuePointTimeLT(time_ns, m_pSegment));
|
|
if (it == m_cue_points.end())
|
|
{
|
|
--it; // result is one before end
|
|
}
|
|
else
|
|
{
|
|
const CuePoint* cp = *it;
|
|
assert(cp);
|
|
const long long t = cp->GetTime(m_pSegment);
|
|
|
|
// lower_bound returns the first element which is not less than time_ns
|
|
if (t > time_ns)
|
|
{
|
|
if(it != m_cue_points.begin())
|
|
--it; // the result is one before
|
|
else
|
|
return false; // no cue point before time_ns
|
|
}
|
|
}
|
|
|
|
pCP = *it;
|
|
assert(pCP);
|
|
assert(pCP->GetTime(m_pSegment) <= time_ns);
|
|
|
|
//TODO: here and elsewhere, it's probably not correct to search
|
|
//for the cue point with this time, and then search for a matching
|
|
//track. In principle, the matching track could be on some earlier
|
|
//cue point, and with our current algorithm, we'd miss it. To make
|
|
//this bullet-proof, we'd need to create a secondary structure,
|
|
//with a list of cue points that apply to a track, and then search
|
|
//that track-based structure for a matching cue point.
|
|
|
|
pTP = pCP->Find(pTrack);
|
|
return (pTP != NULL);
|
|
}
|
|
|
|
|
|
const CuePoint* Cues::GetFirst() const
|
|
{
|
|
if (m_cue_points.empty())
|
|
return NULL;
|
|
|
|
CuePoint* const pCP = m_cue_points.front();
|
|
assert(pCP);
|
|
assert(pCP->GetTimeCode() >= 0);
|
|
|
|
return pCP;
|
|
}
|
|
|
|
|
|
const CuePoint* Cues::GetLast() const
|
|
{
|
|
if (m_cue_points.empty())
|
|
return NULL;
|
|
|
|
CuePoint* const pCP = m_cue_points.back();
|
|
assert(pCP);
|
|
assert(pCP->GetTimeCode() >= 0);
|
|
|
|
return pCP;
|
|
}
|
|
|
|
|
|
const CuePoint* Cues::GetNext(const CuePoint* pCurr) const
|
|
{
|
|
if (pCurr == NULL)
|
|
return NULL;
|
|
|
|
assert(pCurr->GetTimeCode() >= 0);
|
|
assert(m_cue_points.size() >= 1);
|
|
|
|
long index = pCurr->m_index;
|
|
assert(index < static_cast<long>(m_cue_points.size()));
|
|
assert(m_cue_points[index] == pCurr);
|
|
|
|
++index;
|
|
|
|
if (index >= static_cast<long>(m_cue_points.size()))
|
|
return NULL;
|
|
|
|
CuePoint* const pNext = m_cue_points[index];
|
|
assert(pNext);
|
|
assert(pNext->GetTimeCode() >= 0);
|
|
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const BlockEntry* Cues::GetBlock(
|
|
const CuePoint* pCP,
|
|
const CuePoint::TrackPosition* pTP) const
|
|
{
|
|
if (pCP == NULL)
|
|
return NULL;
|
|
|
|
if (pTP == NULL)
|
|
return NULL;
|
|
|
|
return m_pSegment->GetBlock(*pCP, *pTP);
|
|
}
|
|
|
|
} // namespace mkvparser
|