Compare commits

..

9 Commits

Author SHA1 Message Date
matthewjheaney
6623441ee8 changed version to 1.0.0.10
Change-Id: Ib5acbe084dce95b1bb520731015ff345390acad8
2010-12-16 15:13:41 -05:00
matthewjheaney
3ba8df9a64 return invisible flag setting
Change-Id: I1953b0a590813302054245b8ceb5430aa1d9d09b
2010-12-14 15:05:35 -05:00
matthewjheaney
bebe4accb8 changed version to 1.0.0.9
Change-Id: Iee4ba7605e191290e99a9424b76e57d6f0e965c2
2010-12-07 13:58:49 -05:00
matthewjheaney
93263f0b4a return error when no segment id found
Change-Id: I291335dc726171f7eb3820b9ade2d261a33500ff
2010-12-03 11:57:53 -05:00
matthewjheaney
e1e757f919 LoadCluster passes back posn and size
Change-Id: I91b3c2234f7621139a5cae2d71af7301a30c027e
2010-11-30 12:49:20 -05:00
matthewjheaney
6c45ab2d4c parse seek head only when source available
Change-Id: Iba3b9e46d0cb28fd91a2a7062c6aa4bac3f72c0c
2010-11-29 17:11:38 -05:00
matthewjheaney
cb7b24880f LoadCluster now checks for underflow
Change-Id: I5a1ad57753657f71075d0ad8ca39841e7aae9e57
2010-11-29 16:03:44 -05:00
matthewjheaney
157775ac74 fixed bug: was searching outside array bounds
Change-Id: If02466f240dc2a00b2a9a8f75b0ffa27a8984ec0
2010-11-22 16:29:40 -05:00
matthewjheaney
fd1d8006f0 moved seek function from segment to track
Change-Id: Ie34549cea4c5d961b6cc04ef229a3eadc1f0ee3b
2010-11-22 15:57:25 -05:00
2 changed files with 372 additions and 117 deletions

View File

@@ -21,7 +21,7 @@ void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision)
major = 1; major = 1;
minor = 0; minor = 0;
build = 0; build = 0;
revision = 8; revision = 10;
} }
long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len) long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
@@ -29,75 +29,56 @@ long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
assert(pReader); assert(pReader);
assert(pos >= 0); assert(pos >= 0);
long long total, available; int status;
long hr = pReader->Length(&total, &available); #ifdef _DEBUG
assert(hr >= 0); long long total, available;
status = pReader->Length(&total, &available);
assert(status >= 0);
assert(total > 0);
assert(available <= total);
assert(pos < available); assert(pos < available);
assert((available - pos) >= 1); //assume here max u-int len is 8 assert((available - pos) >= 1); //assume here max u-int len is 8
#endif
unsigned char b; unsigned char b;
hr = pReader->Read(pos, 1, &b); status = pReader->Read(pos, 1, &b);
if (hr < 0)
return hr;
assert(hr == 0L); if (status < 0) //error
return status;
if (b & 0x80) //1000 0000 if (status > 0) //interpreted as "underflow"
return E_BUFFER_NOT_FULL;
if (b == 0) //we can't handle u-int values larger than 8 bytes
return E_FILE_FORMAT_INVALID;
unsigned char m = 0x80;
len = 1;
while (!(b & m))
{ {
len = 1; m >>= 1;
b &= 0x7F; //0111 1111 ++len;
}
else if (b & 0x40) //0100 0000
{
len = 2;
b &= 0x3F; //0011 1111
}
else if (b & 0x20) //0010 0000
{
len = 3;
b &= 0x1F; //0001 1111
}
else if (b & 0x10) //0001 0000
{
len = 4;
b &= 0x0F; //0000 1111
}
else if (b & 0x08) //0000 1000
{
len = 5;
b &= 0x07; //0000 0111
}
else if (b & 0x04) //0000 0100
{
len = 6;
b &= 0x03; //0000 0011
}
else if (b & 0x02) //0000 0010
{
len = 7;
b &= 0x01; //0000 0001
}
else
{
assert(b & 0x01); //0000 0001
len = 8;
b = 0; //0000 0000
} }
#ifdef _DEBUG
assert((available - pos) >= len); assert((available - pos) >= len);
#endif
long long result = b; long long result = b & (~m);
++pos; ++pos;
for (long i = 1; i < len; ++i)
for (int i = 1; i < len; ++i)
{ {
hr = pReader->Read(pos, 1, &b); status = pReader->Read(pos, 1, &b);
if (hr < 0) if (status < 0)
return hr; return status;
assert(hr == 0L); if (status > 0)
return E_BUFFER_NOT_FULL;
result <<= 8; result <<= 8;
result |= b; result |= b;
@@ -118,8 +99,9 @@ long long mkvparser::GetUIntLength(
long long total, available; long long total, available;
long hr = pReader->Length(&total, &available); int status = pReader->Length(&total, &available);
assert(hr >= 0); assert(status >= 0);
assert(total >= 0);
assert(available <= total); assert(available <= total);
if (pos >= available) if (pos >= available)
@@ -127,12 +109,12 @@ long long mkvparser::GetUIntLength(
unsigned char b; unsigned char b;
hr = pReader->Read(pos, 1, &b); status = pReader->Read(pos, 1, &b);
if (hr < 0) if (status < 0)
return hr; return status;
assert(hr == 0L); assert(status == 0);
if (b == 0) //we can't handle u-int values larger than 8 bytes if (b == 0) //we can't handle u-int values larger than 8 bytes
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
@@ -892,8 +874,10 @@ long long Segment::CreateInstance(
else if ((pos + size) > total) else if ((pos + size) > total)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
pSegment = new Segment(pReader, pos, size); pSegment = new (std::nothrow) Segment(pReader, pos, size);
assert(pSegment); //TODO
if (pSegment == 0)
return -1; //generic error
return 0; //success return 0; //success
} }
@@ -904,12 +888,7 @@ long long Segment::CreateInstance(
pos += size; //consume payload pos += size; //consume payload
} }
assert(pos == total); return E_FILE_FORMAT_INVALID; //there is no segment
pSegment = new Segment(pReader, pos, 0);
assert(pSegment); //TODO
return 0; //success (sort of)
} }
@@ -920,17 +899,16 @@ long long Segment::ParseHeaders()
//inner (level 1) elements. //inner (level 1) elements.
long long total, available; long long total, available;
long hr = m_pReader->Length(&total, &available); const int status = m_pReader->Length(&total, &available);
assert(hr >= 0); assert(status == 0);
assert(total >= 0);
assert(available <= total); assert(available <= total);
const long long stop = m_start + m_size; const long long stop = m_start + m_size;
assert(stop <= total); assert(stop <= total);
assert(m_pos <= stop); assert(m_pos <= stop);
bool bQuit = false; while (m_pos < stop)
while ((m_pos < stop) && !bQuit)
{ {
long long pos = m_pos; long long pos = m_pos;
@@ -952,6 +930,9 @@ long long Segment::ParseHeaders()
if (id < 0) //error if (id < 0) //error
return id; return id;
if (id == 0x0F43B675) //Cluster ID
break;
pos += len; //consume ID pos += len; //consume ID
//Read Size //Read Size
@@ -1007,15 +988,11 @@ long long Segment::ParseHeaders()
} }
else if (id == 0x014D9B74) //SeekHead ID else if (id == 0x014D9B74) //SeekHead ID
{ {
ParseSeekHead(pos, size); if (available >= total)
} ParseSeekHead(pos, size);
else if (id == 0x0F43B675) //Cluster ID
{
bQuit = true;
} }
if (!bQuit) m_pos = pos + size; //consume payload
m_pos = pos + size; //consume payload
} }
assert(m_pos <= stop); assert(m_pos <= stop);
@@ -1030,6 +1007,93 @@ long long Segment::ParseHeaders()
} }
#if 0
long Segment::FindNextCluster(long long& pos, size& len) const
{
//Outermost (level 0) segment object has been constructed,
//and pos designates start of payload. We need to find the
//inner (level 1) elements.
long long total, available;
const int status = m_pReader->Length(&total, &available);
assert(status == 0);
assert(total >= 0);
assert(available <= total);
const long long stop = m_start + m_size;
assert(stop <= total);
assert(m_pos <= stop);
pos = m_pos;
while (pos < stop)
{
long long result = GetUIntLength(m_pReader, pos, len);
if (result < 0)
return static_cast<long>(result);
if (result > 0)
return E_BUFFER_NOT_FULL;
if ((pos + len) > stop)
return E_FILE_FORMAT_INVALID;
if ((pos + len) > available)
return E_BUFFER_NOT_FULL;
const long long idpos = pos;
const long long id = ReadUInt(m_pReader, idpos, len);
if (id < 0) //error
return static_cast<long>(id);
pos += len; //consume ID
//Read Size
result = GetUIntLength(m_pReader, pos, len);
if (result < 0) //error
return static_cast<long>(result);
if (result > 0)
return E_BUFFER_NOT_FULL;
if ((pos + len) > stop)
return E_FILE_FORMAT_INVALID;
if ((pos + len) > available)
return E_BUFFER_NOT_FULL;
const long long size = ReadUInt(m_pReader, pos, len);
if (size < 0) //error
return static_cast<long>(size);
pos += len; //consume length of size of element
//Pos now points to start of payload
if ((pos + size) > stop)
return E_FILE_FORMAT_INVALID;
if ((pos + size) > available)
return E_BUFFER_NOT_FULL;
if (id == 0x0F43B675) //Cluster ID
{
len = static_cast<long>(size);
return 0; //success
}
pos += size; //consume payload
}
return E_FILE_FORMAT_INVALID;
}
#endif
long Segment::ParseCluster(long long& off, long long& new_pos) const long Segment::ParseCluster(long long& off, long long& new_pos) const
{ {
off = -1; off = -1;
@@ -1179,21 +1243,26 @@ bool Segment::AddCluster(long long off, long long pos)
} }
long Segment::LoadCluster() long Segment::LoadCluster(
long long& pos,
long& len)
{ {
const long long stop = m_start + m_size; const long long stop = m_start + m_size;
while (m_pos < stop) while (m_pos < stop)
{ {
long long pos = m_pos; pos = m_pos;
long len; //Read ID
long long result = GetUIntLength(m_pReader, pos, len); long long result = GetUIntLength(m_pReader, pos, len);
if (result < 0) //error if (result < 0) //error
return static_cast<long>(result); return static_cast<long>(result);
if (result > 0)
return E_BUFFER_NOT_FULL;
if ((pos + len) > stop) if ((pos + len) > stop)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
@@ -1206,11 +1275,15 @@ long Segment::LoadCluster()
pos += len; //consume ID pos += len; //consume ID
//Read Size //Read Size
result = GetUIntLength(m_pReader, pos, len); result = GetUIntLength(m_pReader, pos, len);
if (result < 0) //error if (result < 0) //error
return static_cast<long>(result); return static_cast<long>(result);
if (result > 0)
return E_BUFFER_NOT_FULL;
if ((pos + len) > stop) if ((pos + len) > stop)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
@@ -1232,6 +1305,19 @@ long Segment::LoadCluster()
if ((pos + size) > stop) if ((pos + size) > stop)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
long long total, avail;
const int status = m_pReader->Length(&total, &avail);
assert(status == 0);
assert(total >= 0);
assert(avail <= total);
if ((pos + size) > avail)
{
len = static_cast<long>(size);
return E_BUFFER_NOT_FULL;
}
if (id == 0x0C53BB6B) //Cues ID if (id == 0x0C53BB6B) //Cues ID
{ {
if (m_pCues == NULL) if (m_pCues == NULL)
@@ -2666,6 +2752,7 @@ const Cluster* Segment::FindCluster(long long time_ns) const
} }
#if 0
const BlockEntry* Segment::Seek( const BlockEntry* Segment::Seek(
long long time_ns, long long time_ns,
const Track* pTrack) const const Track* pTrack) const
@@ -2808,6 +2895,7 @@ const BlockEntry* Segment::Seek(
return pTrack->GetEOS(); return pTrack->GetEOS();
} }
#endif
#if 0 #if 0
@@ -3107,7 +3195,9 @@ long Track::GetFirst(const BlockEntry*& pBlockEntry) const
const Block* const pBlock = pBlockEntry->GetBlock(); const Block* const pBlock = pBlockEntry->GetBlock();
assert(pBlock); assert(pBlock);
if (pBlock->GetTrackNumber() == m_info.number) const long long tn = pBlock->GetTrackNumber();
if ((tn == m_info.number) && VetEntry(pBlockEntry))
return 0; return 0;
pBlockEntry = pCluster->GetNext(pBlockEntry); pBlockEntry = pCluster->GetNext(pBlockEntry);
@@ -3322,6 +3412,99 @@ bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const
} }
long VideoTrack::Seek(
long long time_ns,
const BlockEntry*& pResult) const
{
const long status = GetFirst(pResult);
if (status < 0) //buffer underflow, etc
return status;
assert(pResult);
if (pResult->EOS())
return 0;
const Cluster* pCluster = pResult->GetCluster();
assert(pCluster);
if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
return 0;
Cluster** const clusters = m_pSegment->m_clusters;
assert(clusters);
const long count = m_pSegment->GetCount();
assert(count > 0);
Cluster** const i = clusters + pCluster->m_index;
assert(i);
assert(*i == pCluster);
assert(pCluster->GetTime() <= time_ns);
Cluster** const j = clusters + count;
Cluster** lo = i;
Cluster** hi = j;
while (lo < hi)
{
//INVARIANT:
//[i, lo) <= time_ns
//[lo, hi) ?
//[hi, j) > time_ns
Cluster** const mid = lo + (hi - lo) / 2;
assert(mid < hi);
pCluster = *mid;
assert(pCluster);
assert(pCluster->m_index == long(mid - m_pSegment->m_clusters));
const long long t = pCluster->GetTime();
if (t <= time_ns)
lo = mid + 1;
else
hi = mid;
assert(lo <= hi);
}
assert(lo == hi);
assert(lo > i);
assert(lo <= j);
pCluster = *--lo;
assert(pCluster);
assert(pCluster->GetTime() <= time_ns);
pResult = pCluster->GetEntry(this, time_ns);
if ((pResult != 0) && !pResult->EOS()) //found a keyframe
return 0;
while (lo != i)
{
pCluster = *--lo;
assert(pCluster);
assert(pCluster->GetTime() <= time_ns);
pResult = pCluster->GetMaxKey(this);
if ((pResult != 0) && !pResult->EOS())
return 0;
}
//weird: we're on the first cluster, but no keyframe found
//should never happen but we must return something anyway
pResult = GetEOS();
return 0;
}
long long VideoTrack::GetWidth() const long long VideoTrack::GetWidth() const
{ {
return m_width; return m_width;
@@ -3413,6 +3596,89 @@ bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const
} }
long AudioTrack::Seek(
long long time_ns,
const BlockEntry*& pResult) const
{
const long status = GetFirst(pResult);
if (status < 0) //buffer underflow, etc
return status;
assert(pResult);
if (pResult->EOS())
return 0;
const Cluster* pCluster = pResult->GetCluster();
assert(pCluster);
if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
return 0;
Cluster** const clusters = m_pSegment->m_clusters;
assert(clusters);
const long count = m_pSegment->GetCount();
assert(count > 0);
Cluster** const i = clusters + pCluster->m_index;
assert(i);
assert(*i == pCluster);
assert(pCluster->GetTime() <= time_ns);
Cluster** const j = clusters + count;
Cluster** lo = i;
Cluster** hi = j;
while (lo < hi)
{
//INVARIANT:
//[i, lo) <= time_ns
//[lo, hi) ?
//[hi, j) > time_ns
Cluster** const mid = lo + (hi - lo) / 2;
assert(mid < hi);
pCluster = *mid;
assert(pCluster);
assert(pCluster->m_index == long(mid - m_pSegment->m_clusters));
const long long t = pCluster->GetTime();
if (t <= time_ns)
lo = mid + 1;
else
hi = mid;
assert(lo <= hi);
}
assert(lo == hi);
assert(lo > i);
assert(lo <= j);
while (lo > i)
{
pCluster = *--lo;
assert(pCluster);
assert(pCluster->GetTime() <= time_ns);
pResult = pCluster->GetEntry(this);
if ((pResult != 0) && !pResult->EOS())
return 0;
//landed on empty cluster (no entries)
}
pResult = GetEOS(); //weird
return 0;
}
double AudioTrack::GetSamplingRate() const double AudioTrack::GetSamplingRate() const
{ {
return m_rate; return m_rate;
@@ -4587,9 +4853,11 @@ Block::Block(long long start, long long size_, IMkvReader* pReader) :
long status = pReader->Read(pos, 1, &m_flags); long status = pReader->Read(pos, 1, &m_flags);
assert(status == 0); assert(status == 0);
#if 0
const int invisible = int(m_flags & 0x08) >> 3; const int invisible = int(m_flags & 0x08) >> 3;
invisible; invisible;
assert(!invisible); //TODO assert(!invisible); //TODO
#endif
const int lacing = int(m_flags & 0x06) >> 1; const int lacing = int(m_flags & 0x06) >> 1;
@@ -4869,28 +5137,12 @@ void Block::SetKey(bool bKey)
} }
#if 0 bool Block::IsInvisible() const
long long Block::GetOffset() const
{ {
return m_frameOff; return bool(int(m_flags & 0x08) != 0);
} }
long Block::GetSize() const
{
return m_frameSize;
}
long Block::Read(IMkvReader* pReader, unsigned char* buf) const
{
assert(pReader);
assert(buf);
const long hr = pReader->Read(m_frameOff, m_frameSize, buf);
return hr;
}
#else
int Block::GetFrameCount() const int Block::GetFrameCount() const
{ {
return m_frame_count; return m_frame_count;
@@ -4918,7 +5170,6 @@ long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const
const long status = pReader->Read(pos, len, buf); const long status = pReader->Read(pos, len, buf);
return status; return status;
} }
#endif
} //end namespace mkvparser } //end namespace mkvparser

View File

@@ -80,6 +80,7 @@ public:
long long GetTime(const Cluster*) const; //absolute, and scaled (ns) long long GetTime(const Cluster*) const; //absolute, and scaled (ns)
bool IsKey() const; bool IsKey() const;
void SetKey(bool); void SetKey(bool);
bool IsInvisible() const;
int GetFrameCount() const; //to index frames: [0, count) int GetFrameCount() const; //to index frames: [0, count)
@@ -91,26 +92,15 @@ public:
long Read(IMkvReader*, unsigned char*) const; long Read(IMkvReader*, unsigned char*) const;
}; };
#if 0
long long GetOffset() const;
long GetSize() const;
long Read(IMkvReader*, unsigned char*) const;
#else
const Frame& GetFrame(int frame_index) const; const Frame& GetFrame(int frame_index) const;
#endif
private: private:
long long m_track; //Track::Number() long long m_track; //Track::Number()
short m_timecode; //relative to cluster short m_timecode; //relative to cluster
unsigned char m_flags; unsigned char m_flags;
#if 0
long long m_frameOff;
long m_frameSize;
#else
Frame* m_frames; Frame* m_frames;
int m_frame_count; int m_frame_count;
#endif
}; };
@@ -244,6 +234,7 @@ public:
long GetFirst(const BlockEntry*&) const; long GetFirst(const BlockEntry*&) const;
long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
virtual bool VetEntry(const BlockEntry*) const = 0; virtual bool VetEntry(const BlockEntry*) const = 0;
virtual long Seek(long long time_ns, const BlockEntry*&) const = 0;
protected: protected:
Track(Segment*, const Info&); Track(Segment*, const Info&);
@@ -278,6 +269,7 @@ public:
double GetFrameRate() const; double GetFrameRate() const;
bool VetEntry(const BlockEntry*) const; bool VetEntry(const BlockEntry*) const;
long Seek(long long time_ns, const BlockEntry*&) const;
private: private:
long long m_width; long long m_width;
@@ -298,6 +290,7 @@ public:
long long GetChannels() const; long long GetChannels() const;
long long GetBitDepth() const; long long GetBitDepth() const;
bool VetEntry(const BlockEntry*) const; bool VetEntry(const BlockEntry*) const;
long Seek(long long time_ns, const BlockEntry*&) const;
private: private:
double m_rate; double m_rate;
@@ -506,6 +499,8 @@ private:
class Segment class Segment
{ {
friend class Cues; friend class Cues;
friend class VideoTrack;
friend class AudioTrack;
Segment(const Segment&); Segment(const Segment&);
Segment& operator=(const Segment&); Segment& operator=(const Segment&);
@@ -524,10 +519,12 @@ public:
long Load(); //loads headers and all clusters long Load(); //loads headers and all clusters
//for incremental loading (splitter) //for incremental loading
long long Unparsed() const; long long Unparsed() const;
long long ParseHeaders(); //stops when first cluster is found long long ParseHeaders(); //stops when first cluster is found
long LoadCluster(); //loads one cluster //long FindNextCluster(long long& pos, long& size) const;
long LoadCluster(long long& pos, long& size); //load one cluster
long LoadCluster();
//This pair parses one cluster, but only changes the state of the //This pair parses one cluster, but only changes the state of the
//segment object when the cluster is actually added to the index. //segment object when the cluster is actually added to the index.
@@ -546,7 +543,7 @@ public:
const Cluster* GetNext(const Cluster*); const Cluster* GetNext(const Cluster*);
const Cluster* FindCluster(long long time_nanoseconds) const; const Cluster* FindCluster(long long time_nanoseconds) const;
const BlockEntry* Seek(long long time_nanoseconds, const Track*) const; //const BlockEntry* Seek(long long time_nanoseconds, const Track*) const;
private: private:
@@ -572,7 +569,14 @@ private:
}; };
} //end namespace mkvparser } //end namespace mkvparser
inline long mkvparser::Segment::LoadCluster()
{
long long pos;
long size;
return LoadCluster(pos, size);
}
#endif //MKVPARSER_HPP #endif //MKVPARSER_HPP