lazy load cue points as they're searched

Change-Id: Ifa767e579624f4da06670bf791a85cbee4336682
This commit is contained in:
matthewjheaney
2010-10-08 18:26:44 -04:00
parent 1d0c804fb9
commit 485b6e8125
2 changed files with 315 additions and 62 deletions

View File

@@ -1711,22 +1711,25 @@ Cues::Cues(Segment* pSegment, long long start_, long long size_) :
m_pSegment(pSegment), m_pSegment(pSegment),
m_start(start_), m_start(start_),
m_size(size_), m_size(size_),
m_cue_points_size(0), m_cue_points(NULL),
m_cue_points_count(0), m_count(0),
m_preload_count(0),
m_pos(start_) m_pos(start_)
{ {
//odbgstream os;
//os << "Cues: ctor (begin)" << endl;
IMkvReader* const pReader = m_pSegment->m_pReader; IMkvReader* const pReader = m_pSegment->m_pReader;
const long long stop = m_start + m_size; const long long stop = m_start + m_size;
long long pos = m_start; long long pos = m_start;
//odbgstream os; size_t cue_points_size = 0;
//os << "Cues: ctor (begin)" << endl;
//First count the cue points
while (pos < stop) while (pos < stop)
{ {
const long long idpos = pos;
long len; long len;
const long long id = ReadUInt(pReader, pos, len); const long long id = ReadUInt(pReader, pos, len);
@@ -1743,64 +1746,137 @@ Cues::Cues(Segment* pSegment, long long start_, long long size_) :
assert((pos + size) <= stop); assert((pos + size) <= stop);
if (id == 0x3B) //CuePoint ID if (id == 0x3B) //CuePoint ID
++m_cue_points_size; PreloadCuePoint(cue_points_size, idpos);
pos += size; //consume payload pos += size; //consume payload
assert(pos <= stop); assert(pos <= stop);
} }
assert(m_cue_points_size > 0); //TODO //os << "Cues: ctor (end): preload_count="
// << m_preload_count
// << " cue_points_size="
// << cue_points_size
// << endl;
//os << "Cues::ctor: count=" << m_cue_points_size << endl; LoadCuePoint();
m_cue_points = new CuePoint[m_cue_points_size];
//os << "Cues: ctor (end)" << endl;
} }
Cues::~Cues() Cues::~Cues()
{ {
const size_t n = m_count + m_preload_count;
CuePoint** p = m_cue_points;
CuePoint** const q = p + n;
while (p != q)
{
CuePoint* const pCP = *p++;
assert(pCP);
delete pCP;
}
delete[] m_cue_points; delete[] m_cue_points;
} }
void Cues::PreloadCuePoint(
size_t& cue_points_size,
long long pos)
{
assert(m_count == 0);
if (m_preload_count >= cue_points_size)
{
size_t n;
if (cue_points_size > 0)
n = static_cast<size_t>(2 * cue_points_size);
else
{
const SegmentInfo* const pInfo = m_pSegment->GetInfo();
if (pInfo == NULL)
n = 2048;
else
{
const long long ns = pInfo->GetDuration();
if (ns <= 0)
n = 2048;
else
{
const long long sec = (ns + 999999999LL) / 1000000000LL;
n = static_cast<size_t>(sec);
}
}
}
CuePoint** const qq = new CuePoint*[n];
CuePoint** q = qq; //beginning of target
CuePoint** p = m_cue_points; //beginning of source
CuePoint** const pp = p + m_preload_count; //end of source
while (p != pp)
*q++ = *p++;
delete[] m_cue_points;
m_cue_points = qq;
cue_points_size = n;
}
CuePoint* const pCP = new CuePoint(pos);
m_cue_points[m_preload_count++] = pCP;
}
const CuePoint* Cues::GetFirst() const const CuePoint* Cues::GetFirst() const
{ {
if ((m_cue_points == NULL) || (m_cue_points_count < 1)) if (m_count < 1)
return NULL; return NULL;
CuePoint& cp = m_cue_points[0]; CuePoint* const pCP = m_cue_points[0];
assert(pCP);
assert(pCP->GetTimeCode() >= 0);
return &cp; return pCP;
} }
const CuePoint* Cues::GetLast() const const CuePoint* Cues::GetLast() const
{ {
if ((m_cue_points == NULL) || (m_cue_points_count < 1)) if (m_count < 1)
return NULL; return NULL;
const size_t idx = m_cue_points_count - 1; const size_t idx = m_count - 1;
CuePoint& cp = m_cue_points[idx]; CuePoint* const pCP = m_cue_points[idx];
assert(pCP);
assert(pCP->GetTimeCode() >= 0);
return &cp; return pCP;
} }
size_t Cues::LoadCuePoint() bool Cues::LoadCuePoint()
{ {
if (m_cue_points_count >= m_cue_points_size) //odbgstream os;
return 0; //os << "Cues::LoadCuePoint" << endl;
const long long stop = m_start + m_size;
if (m_pos >= stop)
return false; //nothing else to do
IMkvReader* const pReader = m_pSegment->m_pReader; IMkvReader* const pReader = m_pSegment->m_pReader;
const long long stop = m_start + m_size; while (m_pos < stop)
bool bDone = false;
while (!bDone && (m_pos < stop))
{ {
const long long idpos = m_pos;
long len; long len;
const long long id = ReadUInt(pReader, m_pos, len); const long long id = ReadUInt(pReader, m_pos, len);
@@ -1816,20 +1892,33 @@ size_t Cues::LoadCuePoint()
m_pos += len; //consume Size field m_pos += len; //consume Size field
assert((m_pos + size) <= stop); assert((m_pos + size) <= stop);
if (id == 0x3B) //CuePoint ID if (id != 0x3B) //CuePoint ID
{ {
CuePoint& cp = m_cue_points[m_cue_points_count++]; m_pos += size; //consume payload
cp.Parse(pReader, m_pos, size); assert(m_pos <= stop);
bDone = true; continue;
} }
assert(m_preload_count > 0);
const size_t idx = m_count;
assert(idx < m_size);
CuePoint* const pCP = m_cue_points[idx];
assert(pCP);
pCP->Load(pReader, idpos);
++m_count;
--m_preload_count;
m_pos += size; //consume payload m_pos += size; //consume payload
assert(m_pos <= stop); assert(m_pos <= stop);
break;
} }
const size_t result = m_cue_points_size - m_cue_points_count; return (m_pos < stop);
return result;
} }
@@ -1842,16 +1931,19 @@ bool Cues::Find(
assert(time_ns >= 0); assert(time_ns >= 0);
assert(pTrack); assert(pTrack);
if ((m_cue_points == NULL) || (m_cue_points_count == 0)) if (m_count == 0)
return false; return false;
const CuePoint* const ii = m_cue_points; assert(m_cue_points);
const CuePoint* i = ii;
const CuePoint* const jj = ii + m_cue_points_count; const CuePoint* const* const ii = m_cue_points;
const CuePoint* j = jj; const CuePoint* const* i = ii;
pCP = i; const CuePoint* const* const jj = ii + m_count;
const CuePoint* const* j = jj;
pCP = *i;
assert(pCP);
if (time_ns <= pCP->GetTime(m_pSegment)) if (time_ns <= pCP->GetTime(m_pSegment))
{ {
@@ -1866,10 +1958,13 @@ bool Cues::Find(
//[i, j) ? //[i, j) ?
//[j, jj) > time_ns //[j, jj) > time_ns
const CuePoint* const k = i + (j - i) / 2; const CuePoint* const* const k = i + (j - i) / 2;
assert(k < jj); assert(k < jj);
const long long t = k->GetTime(m_pSegment); pCP = *k;
assert(pCP);
const long long t = pCP->GetTime(m_pSegment);
if (t <= time_ns) if (t <= time_ns)
i = k + 1; i = k + 1;
@@ -1883,11 +1978,20 @@ bool Cues::Find(
assert(i > ii); assert(i > ii);
assert(i <= jj); assert(i <= jj);
pCP = i - 1; pCP = *--i;
assert(pCP);
assert(pCP->GetTime(m_pSegment) <= time_ns); assert(pCP->GetTime(m_pSegment) <= time_ns);
pTP = pCP->Find(pTrack); pTP = pCP->Find(pTrack);
return (pTP != NULL); return (pTP != NULL);
//TODO: here and elsewhere, it's probably not correct to search
//for the cue point with this time, and the 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.
} }
@@ -1900,14 +2004,16 @@ bool Cues::FindNext(
pCP = 0; pCP = 0;
pTP = 0; pTP = 0;
if ((m_cue_points == NULL) || (m_cue_points_count == 0)) //weird if (m_count == 0)
return false; return false;
const CuePoint* const ii = m_cue_points; assert(m_cue_points);
const CuePoint* i = ii;
const CuePoint* const jj = ii + m_cue_points_count; const CuePoint* const* const ii = m_cue_points;
const CuePoint* j = jj; const CuePoint* const* i = ii;
const CuePoint* const* const jj = ii + m_count;
const CuePoint* const* j = jj;
while (i < j) while (i < j)
{ {
@@ -1916,10 +2022,13 @@ bool Cues::FindNext(
//[i, j) ? //[i, j) ?
//[j, jj) > time_ns //[j, jj) > time_ns
const CuePoint* const k = i + (j - i) / 2; const CuePoint* const* const k = i + (j - i) / 2;
assert(k < jj); assert(k < jj);
const long long t = k->GetTime(m_pSegment); pCP = *k;
assert(pCP);
const long long t = pCP->GetTime(m_pSegment);
if (t <= time_ns) if (t <= time_ns)
i = k + 1; i = k + 1;
@@ -1935,7 +2044,8 @@ bool Cues::FindNext(
if (i >= jj) //time_ns is greater than max cue point if (i >= jj) //time_ns is greater than max cue point
return false; return false;
pCP = i; pCP = *i;
assert(pCP);
assert(pCP->GetTime(m_pSegment) > time_ns); assert(pCP->GetTime(m_pSegment) > time_ns);
pTP = pCP->Find(pTrack); pTP = pCP->Find(pTrack);
@@ -1943,10 +2053,96 @@ bool Cues::FindNext(
} }
CuePoint::CuePoint() : const CuePoint* Cues::LoadCuePoint(
long long time_ns,
const Track* pTrack,
const CuePoint::TrackPosition*& pTP) const
{
assert(time_ns >= 0);
assert(pTrack);
//odbgstream os;
//os << "Cues::LoadCuePoint: time[sec]="
// << (double(time_ns) / 1000000000)
// << " count=" << m_count
// << " preload_count=" << m_preload_count
// << endl;
if (m_count > 0)
{
const CuePoint* pCP = GetLast();
assert(pCP);
const long long ns = pCP->GetTime(m_pSegment);
if ((ns >= time_ns) || (m_preload_count == 0))
{
const bool bFound = Find(time_ns, pTrack, pCP, pTP);
assert(bFound); //TODO
assert(pCP);
assert(pTP);
return pCP;
}
}
assert(m_preload_count > 0);
assert(m_cue_points);
IMkvReader* const pReader = m_pSegment->m_pReader;
CuePoint** const ii = m_cue_points + m_count;
CuePoint** i = ii;
CuePoint** const jj = ii + m_preload_count;
CuePoint** j = jj;
while (i < j)
{
//INVARIANT:
//[ii, i) <= time_ns
//[i, j) ?
//[j, jj) > time_ns
CuePoint** const k = i + (j - i) / 2;
assert(k < jj);
CuePoint* const pCP = *k;
assert(pCP);
pCP->Load(pReader, 0);
const long long t = pCP->GetTime(m_pSegment);
if (t <= time_ns)
i = k + 1;
else
j = k;
assert(i <= j);
}
assert(i == j);
assert(i <= jj);
assert(i > m_cue_points);
const CuePoint* const pCP = *--i;
assert(pCP);
assert(pCP->GetTime(m_pSegment) <= time_ns);
pTP = pCP->Find(pTrack);
assert(pTP); //TODO
return pCP;
}
CuePoint::CuePoint(long long pos) :
m_timecode(-1 * pos),
m_track_positions(NULL), m_track_positions(NULL),
m_track_positions_count(0) m_track_positions_count(0)
{ {
assert(pos > 0);
} }
@@ -1956,15 +2152,44 @@ CuePoint::~CuePoint()
} }
void CuePoint::Parse(IMkvReader* pReader, long long start_, long long size_) void CuePoint::Load(IMkvReader* pReader, long long idpos)
{ {
//odbgstream os;
//os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
if (m_timecode >= 0) //already loaded
return;
assert(m_track_positions == NULL); assert(m_track_positions == NULL);
assert(m_track_positions_count == 0); assert(m_track_positions_count == 0);
const long long stop = start_ + size_; long long pos_ = -1 * m_timecode;
long long pos = start_; assert((idpos <= 0) || (idpos == pos_));
m_timecode = -1; long long stop;
{
long len;
const long long id = ReadUInt(pReader, pos_, len);
assert(id == 0x3B); //CuePoint ID
//assert((pos + len) <= stop);
pos_ += len; //consume ID
const long long size = ReadUInt(pReader, pos_, len);
assert(size >= 0);
//assert((pos + len) <= stop);
pos_ += len; //consume Size field
//assert((pos + size) <= stop);
//pos_ now points to start of payload
stop = pos_ + size;
}
long long pos = pos_;
//First count number of track positions //First count number of track positions
@@ -1998,12 +2223,16 @@ void CuePoint::Parse(IMkvReader* pReader, long long start_, long long size_)
assert(m_timecode >= 0); assert(m_timecode >= 0);
assert(m_track_positions_count > 0); assert(m_track_positions_count > 0);
//os << "CuePoint::Load(cont'd): idpos=" << idpos
// << " timecode=" << m_timecode
// << endl;
m_track_positions = new TrackPosition[m_track_positions_count]; m_track_positions = new TrackPosition[m_track_positions_count];
//Now parse track positions //Now parse track positions
TrackPosition* p = m_track_positions; TrackPosition* p = m_track_positions;
pos = start_; pos = pos_;
while (pos < stop) while (pos < stop)
{ {
@@ -2036,6 +2265,7 @@ void CuePoint::Parse(IMkvReader* pReader, long long start_, long long size_)
} }
void CuePoint::TrackPosition::Parse( void CuePoint::TrackPosition::Parse(
IMkvReader* pReader, IMkvReader* pReader,
long long start_, long long start_,
@@ -2113,6 +2343,7 @@ long long CuePoint::GetTimeCode() const
long long CuePoint::GetTime(Segment* pSegment) const long long CuePoint::GetTime(Segment* pSegment) const
{ {
assert(pSegment); assert(pSegment);
assert(m_timecode >= 0);
const SegmentInfo* const pInfo = pSegment->GetInfo(); const SegmentInfo* const pInfo = pSegment->GetInfo();
assert(pInfo); assert(pInfo);
@@ -2563,6 +2794,7 @@ bool Segment::SearchCues(
// << (double(time_ns) / 1000000000) // << (double(time_ns) / 1000000000)
// << endl; // << endl;
#if 0
while (m_pCues->LoadCuePoint()) while (m_pCues->LoadCuePoint())
{ {
const CuePoint* const pCP = m_pCues->GetLast(); const CuePoint* const pCP = m_pCues->GetLast();
@@ -2585,6 +2817,13 @@ bool Segment::SearchCues(
assert(pCP); assert(pCP);
assert(pTP); assert(pTP);
assert(pTP->m_track == pTrack->GetNumber()); assert(pTP->m_track == pTrack->GetNumber());
#else
const CuePoint::TrackPosition* pTP;
const CuePoint* const pCP = m_pCues->LoadCuePoint(time_ns, pTrack, pTP);
assert(pCP);
assert(pTP);
assert(pTP->m_track == pTrack->GetNumber());
#endif
//We have the cue point and track position we want, //We have the cue point and track position we want,
//so we now need to search for the cluster having //so we now need to search for the cluster having

View File

@@ -337,11 +337,15 @@ private:
class CuePoint class CuePoint
{ {
CuePoint(const CuePoint&);
CuePoint& operator=(const CuePoint&);
public: public:
CuePoint(); explicit CuePoint(long long);
~CuePoint(); ~CuePoint();
void Parse(IMkvReader*, long long start, long long size); //void Parse(IMkvReader*, long long start, long long size);
void Load(IMkvReader*, long long);
long long GetTimeCode() const; //absolute but unscaled long long GetTimeCode() const; //absolute but unscaled
long long GetTime(Segment*) const; //absolute and scaled (ns units) long long GetTime(Segment*) const; //absolute and scaled (ns units)
@@ -361,6 +365,7 @@ public:
const TrackPosition* Find(const Track*) const; const TrackPosition* Find(const Track*) const;
private: private:
//long long m_pos;
long long m_timecode; long long m_timecode;
TrackPosition* m_track_positions; TrackPosition* m_track_positions;
size_t m_track_positions_count; size_t m_track_positions_count;
@@ -393,14 +398,23 @@ public:
const CuePoint*&, const CuePoint*&,
const CuePoint::TrackPosition*&) const; const CuePoint::TrackPosition*&) const;
size_t LoadCuePoint();
const CuePoint* GetFirst() const; const CuePoint* GetFirst() const;
const CuePoint* GetLast() const; const CuePoint* GetLast() const;
bool LoadCuePoint();
const CuePoint* LoadCuePoint(
long long time_ns,
const Track*,
const CuePoint::TrackPosition*&) const;
private: private:
CuePoint* m_cue_points; void PreloadCuePoint(size_t&, long long);
size_t m_cue_points_count;
size_t m_cue_points_size; CuePoint** m_cue_points;
//size_t m_cue_points_size;
size_t m_count;
size_t m_preload_count;
long long m_pos; long long m_pos;
}; };