fixed bug in AddCluster

Change-Id: Iab96e08de77e8ccef2f5ff717ab2bae888ca4abf
This commit is contained in:
matthewjheaney 2010-09-21 16:37:19 -04:00
parent c22b286ee0
commit fe56cd5929
2 changed files with 267 additions and 50 deletions

View File

@ -21,7 +21,6 @@ void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision)
minor = 0;
build = 0;
revision = 1;
return;
}
long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
@ -808,7 +807,9 @@ Segment::Segment(
m_pos(start),
m_pInfo(NULL),
m_pTracks(NULL),
m_clusterCount(0)
m_pCues(NULL),
m_clusterCount(0),
m_clusterSize(0)
{
}
@ -829,6 +830,9 @@ Segment::~Segment()
delete m_pTracks;
delete m_pInfo;
//TODO:
//delete m_pCues;
}
@ -944,6 +948,7 @@ long long Segment::ParseHeaders()
assert(m_pos <= stop);
bool bQuit = false;
while ((m_pos < stop) && !bQuit)
{
long long pos = m_pos;
@ -1000,32 +1005,28 @@ long long Segment::ParseHeaders()
if (id == 0x0549A966) //Segment Info ID
{
assert(m_pInfo == NULL);
m_pInfo = new SegmentInfo(this, pos, size);
assert(m_pInfo); //TODO
if (m_pTracks)
bQuit = true;
m_pInfo = new SegmentInfo(this, pos, size);
assert(m_pInfo); //TODO
}
else if (id == 0x0654AE6B) //Tracks ID
{
assert(m_pTracks == NULL);
m_pTracks = new Tracks(this, pos, size);
assert(m_pTracks); //TODO
if (m_pInfo)
bQuit = true;
m_pTracks = new Tracks(this, pos, size);
assert(m_pTracks); //TODO
}
else if (id == 0x0C53BB6B) //Cues ID
{
assert(m_pCues == NULL);
#if 0 //TODO
m_pCues = new Cues(this, pos, size);
assert(m_pCues); //TODO
#endif
}
else if (id == 0x0F43B675) //Cluster ID
{
#if 0
if (m_pInfo == NULL) //TODO: liberalize
;
else if (m_pTracks == NULL)
;
else
//ParseCluster(idpos, pos, size);
Cluster::Parse(this, m_clusters, pos, size);
#endif
bQuit = true;
}
@ -1049,7 +1050,6 @@ long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const
long long pos = m_pos;
long long off = -1;
while (pos < stop)
{
long len;
@ -1082,16 +1082,14 @@ long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const
pos += size; //consume payload
assert(pos <= stop);
if (off >= 0)
if (id == 0x0F43B675) //Cluster ID
{
pos_ = idpos;
off = idpos - m_start; // >= 0 means we found a cluster
break;
}
if (id == 0x0F43B675) //Cluster ID
off = idpos - m_start;
}
#if 0
Segment* const this_ = const_cast<Segment*>(this);
const size_t idx = m_clusterCount;
@ -1130,6 +1128,88 @@ long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const
pCluster = Cluster::Parse(this_, idx, off);
return 0L;
#else
assert(pos <= stop);
//Indicate to caller how much of file has been consumed. This is
//used later in AddCluster to adjust the current parse position
//(the value cached in the segment object itself) to the
//file position value just past the cluster we parsed.
if (off < 0) //we did not found any more clusters
{
pos_ = stop; //pos_ >= 0 here means EOF (cluster is NULL)
return 0; //TODO: confirm this return value
}
//We found a cluster. Now read something, to ensure that it is
//fully loaded in the network cache.
if (pos >= stop) //we parsed the entire segment
{
//We did find a cluster, but it was very last element in the segment.
//Our preference is that the loop above runs 1 1/2 times:
//the first pass finds the cluster, and the second pass
//finds the element the follows the cluster. In this case, however,
//we reached the end of the file without finding another element,
//so we didn't actually read anything yet associated with "end of the
//cluster". And we must perform an actual read, in order
//to guarantee that all of the data that belongs to this
//cluster has been loaded into the network cache. So instead
//of reading the next element that follows the cluster, we
//read the last byte of the cluster (which is also the last
//byte in the file).
//Read the last byte of the file. (Reading 0 bytes at pos
//might work too -- it would depend on how the reader is
//implemented. Here we take the more conservative approach,
//since this makes fewer assumptions about the network
//reader abstraction.)
unsigned char b;
const int result = m_pReader->Read(pos - 1, 1, &b);
assert(result == 0);
pos_ = stop;
}
else
{
long len;
const __int64 idpos = pos;
const __int64 id = SyncReadUInt(m_pReader, pos, stop, len);
if (id < 0) //error
return static_cast<long>(id);
if (id == 0)
return E_BUFFER_NOT_FULL;
pos += len; //consume id
assert(pos < stop);
const __int64 size = SyncReadUInt(m_pReader, pos, stop, len);
if (size < 0) //error
return static_cast<long>(size);
pos_ = idpos;
}
//We found a cluster, and it has been completely loaded into the
//network cache. (We can guarantee this because we actually read
//the EBML tag that follows the cluster, or, if we reached EOF,
//because we actually read the last byte of the cluster).
Segment* const this_ = const_cast<Segment*>(this);
pCluster = Cluster::Parse(this_, m_clusterCount, off);
assert(pCluster);
assert(pCluster->m_index == m_clusterCount);
return 0;
#endif
}
@ -1141,7 +1221,35 @@ bool Segment::AddCluster(Cluster* pCluster, long long pos)
assert(pos <= stop);
if (pCluster)
m_clusters[pos] = pCluster;
{
size_t& size = m_clusterSize;
assert(size >= m_clusterCount);
const size_t idx = pCluster->m_index;
assert(idx == m_clusterCount);
if (idx >= size)
{
const size_t n = (size <= 0) ? 2048 : 2 * size;
Cluster** const qq = new Cluster*[n];
Cluster** q = qq;
Cluster** p = m_clusters;
Cluster** const pp = p + m_clusterCount;
while (p != pp)
*p++ = *q++;
delete[] m_clusters;
m_clusters = qq;
size = n;
}
m_clusters[idx] = pCluster;
++m_clusterCount;
}
m_pos = pos; //m_pos >= stop is now we know we have all clusters
@ -1155,7 +1263,8 @@ long Segment::Load()
//and pos designates start of payload. We need to find the
//inner (level 1) elements.
const long long stop = m_start + m_size;
#ifdef _DEBUG
#ifdef _DEBUG //TODO: this is really Microsoft-specific
{
long long total, available;
@ -1167,9 +1276,6 @@ long Segment::Load()
#endif
long long index = m_pos;
//TODO: we don't want to count clusters here.
//Just do a lazy init.
m_clusterCount = 0;
long long* fileposition_of_clusters = NULL;
@ -1229,6 +1335,7 @@ long Segment::Load()
delete[] fileposition_of_clusters;
fileposition_of_clusters = temp;
}
fileposition_of_clusters[m_clusterCount] = idpos;
++m_clusterCount;
}
@ -1248,18 +1355,29 @@ long Segment::Load()
fileposition_of_clusters = new long long[size_of_cluster_pos];
memset(fileposition_of_clusters, 0, size_of_cluster_pos);
}
index += size;
}
if (m_clusterCount == 0)
return -1L;
//TODO: we shouldn't need the local filepos_of_clusters array;
//just write into the m_clusters array directly.
m_clusters = new Cluster*[m_clusterCount];
for (size_t i = 0; i < m_clusterCount; ++i)
m_clusters[i] = Cluster::Parse(this, i, fileposition_of_clusters[i]);
delete[] fileposition_of_clusters;
m_clusterSize = m_clusterCount;
delete[] fileposition_of_clusters; //TODO: optimize this away
//TODO: why do we have this separate loop? It appears that all
//it does it find the Tracks element -- but we could have done
//that in the loop above (the same as we do for the SegmentInfo
//element).
while (m_pos < stop)
{
@ -1319,22 +1437,8 @@ long Segment::Load()
assert(m_clusters);
//TODO: see notes above. This check is here (temporarily) to ensure
//that the first seekhead has entries for the clusters (because that's
//when they're loaded). In case we are given a file that lists the
//clusters in a second seekhead, the worst thing that happens is that
//we treat this as an invalid file (which is better then simply
//asserting somewhere). But that's only a work-around. What we need
//to do is be able to handle having multiple seekheads, and having
//clusters listed somewhere besides the first seekhead.
//
//if (m_clusters == NULL)
// return E_FILE_FORMAT_INVALID;
//TODO: we need to parse the Cues element
//NOTE: we stop parsing when we reach the first cluster, under the
//assumption all clusters are named in some SeekHead. Clusters
//will have been (pre)loaded, so we indicate that we have all clusters
//by adjusting the parse position:
m_pos = stop; //means "we have all clusters"
return 0L;
@ -1345,6 +1449,7 @@ void Segment::ParseSeekHead(long long start, long long size_, size_t* pIndex)
{
long long pos = start;
const long long stop = start + size_;
while (pos < stop)
{
long len;
@ -2088,7 +2193,9 @@ long Track::GetFirst(const BlockEntry*& pBlockEntry) const
const Block* const pBlock = pBlockEntry->GetBlock();
assert(pBlock);
if (pBlock->GetTrackNumber() == static_cast<unsigned long>(m_info.number))
const long long tn = pBlock->GetTrackNumber();
if (tn == m_info.number)
return 0;
pBlockEntry = pCluster->GetNext(pBlockEntry);
@ -2113,7 +2220,11 @@ long Track::GetNext(
{
assert(pCurrEntry);
assert(!pCurrEntry->EOS()); //?
assert(pCurrEntry->GetBlock()->GetTrackNumber() == static_cast<unsigned long>(m_info.number));
const Block* const pCurrBlock = pCurrEntry->GetBlock();
const long long tn = pCurrBlock->GetTrackNumber();
tn;
assert(tn == m_info.number);
#if 0
const Cluster* const pCurrCluster = pCurrEntry->GetCluster();
@ -2228,7 +2339,9 @@ long Track::GetNext(
const Block* const pNextBlock = pNextEntry->GetBlock();
assert(pNextBlock);
if (pNextBlock->GetTrackNumber() == static_cast<unsigned long>(m_info.number))
const long long tn = pNextBlock->GetTrackNumber();
if (tn == m_info.number)
return 0;
pNextEntry = pCluster->GetNext(pNextEntry);

View File

@ -335,6 +335,98 @@ private:
};
class CuePoint
{
public:
void Parse(IMkvReader*, long long start, long long size);
long long m_timecode; //absolute but unscaled
long long GetTime(Segment*) const; //absolute and scaled (ns units)
struct TrackPosition
{
long long m_track;
long long m_pos; //of cluster
long long m_block;
//codec_state //defaults to 0
//reference = clusters containing req'd referenced blocks
// reftime = timecode of the referenced block
};
#if 0 //TODO
typedef std::list<TrackPosition> track_positions_t;
track_positions_t m_track_positions;
const TrackPosition* Find(const Track*) const;
class CompareTime : std::binary_function<long long, CuePoint, bool>
{
CompareTime& operator=(const CompareTime&);
public:
Segment* const m_pSegment;
explicit CompareTime(Segment* p) : m_pSegment(p) {}
CompareTime(const CompareTime& rhs) : m_pSegment(rhs.m_pSegment) {}
long long GetTime(const CuePoint& cp) const
{
return cp.GetTime(m_pSegment);
}
bool operator()(long long left_ns, const CuePoint& cp) const
{
return (left_ns < GetTime(cp));
}
bool operator()(const CuePoint& cp, long long right_ns) const
{
return (GetTime(cp) < right_ns);
}
bool operator()(const CuePoint& lhs, const CuePoint& rhs) const
{
return (lhs.m_timecode < rhs.m_timecode);
}
};
#endif
private:
void ParseTrackPosition(IMkvReader*, long long, long long);
};
class Cues
{
Cues(const Cues&);
Cues& operator=(const Cues&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
Cues(Segment*, long long start, long long size);
bool Find( //lower bound of time_ns
long long time_ns,
const Track*,
const CuePoint*&,
const CuePoint::TrackPosition*&) const;
bool FindNext( //upper_bound of time_ns
long long time_ns,
const Track*,
const CuePoint*&,
const CuePoint::TrackPosition*&) const;
private:
CuePoint* m_cue_points;
size_t m_cue_counts_count;
};
class Cluster
{
Cluster(const Cluster&);
@ -426,16 +518,28 @@ public:
Cluster*&,
const BlockEntry*&);
const Cues* GetCues() const;
private:
long long m_pos; //absolute file posn; what has been consumed so far
SegmentInfo* m_pInfo;
Tracks* m_pTracks;
Cues* m_pCues;
Cluster** m_clusters;
size_t m_clusterCount;
size_t m_clusterCount; //number of entries
size_t m_clusterSize; //array size
void ParseSeekHead(long long pos, long long size, size_t*);
void ParseSeekEntry(long long pos, long long size, size_t*);
void ParseSecondarySeekHead(long long off, size_t*);
void ParseCues(long long off);
bool SearchCues(
long long time_ns,
Track*,
Cluster*&,
const BlockEntry*&);
};