Update libwebm
Pull the latest mkvparser/mkvparser.[ch]pp from
http://git.chromium.org/webm/libwebm.git
56488f73ef32e55ebde533f317575d66d6eead44
Fixes several crashes on malformed streams including:
http://code.google.com/p/webm/issues/detail?id=398
http://code.google.com/p/webm/issues/detail?id=399
http://code.google.com/p/webm/issues/detail?id=400
Change-Id: If5f72215f88ea5932807845af7bbef0f2dd0c736
diff --git a/mkvparser/mkvparser.cpp b/mkvparser/mkvparser.cpp
index be71bfb..e48343f 100644
--- a/mkvparser/mkvparser.cpp
+++ b/mkvparser/mkvparser.cpp
@@ -21,7 +21,7 @@
major = 1;
minor = 0;
build = 0;
- revision = 20;
+ revision = 24;
}
long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
@@ -137,64 +137,6 @@
return 0; //success
}
-long long mkvparser::SyncReadUInt(
- IMkvReader* pReader,
- long long pos,
- long long stop,
- long& len)
-{
- assert(pReader);
-
- if (pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- unsigned char b;
-
- long hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
-
- if (hr != 0L)
- 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))
- {
- m >>= 1;
- ++len;
- }
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- long long result = b & (~m);
- ++pos;
-
- for (int i = 1; i < len; ++i)
- {
- hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
-
- if (hr != 0L)
- return E_BUFFER_NOT_FULL;
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
-}
-
long long mkvparser::UnserializeUInt(
IMkvReader* pReader,
@@ -203,8 +145,9 @@
{
assert(pReader);
assert(pos >= 0);
- assert(size > 0);
- assert(size <= 8);
+
+ if ((size <= 0) || (size > 8))
+ return E_FILE_FORMAT_INVALID;
long long result = 0;
@@ -227,205 +170,193 @@
}
-float mkvparser::Unserialize4Float(
+long mkvparser::UnserializeFloat(
IMkvReader* pReader,
- long long pos)
+ long long pos,
+ long long size_,
+ double& result)
{
assert(pReader);
assert(pos >= 0);
-#ifdef _DEBUG
- {
- long long total, available;
+ if ((size_ != 4) && (size_ != 8))
+ return E_FILE_FORMAT_INVALID;
- const long status = pReader->Length(&total, &available);
- assert(status >= 0);
- assert((total < 0) || (available <= total));
- assert((pos + 4) <= available);
+ const long size = static_cast<long>(size_);
+
+ unsigned char buf[8];
+
+ const int status = pReader->Read(pos, size, buf);
+
+ if (status < 0) //error
+ return status;
+
+ if (size == 4)
+ {
+ union
+ {
+ float f;
+ unsigned long ff;
+ };
+
+ ff = 0;
+
+ for (int i = 0;;)
+ {
+ ff |= buf[i];
+
+ if (++i >= 4)
+ break;
+
+ ff <<= 8;
+ }
+
+ result = f;
}
-#endif
-
-#if 0
- float result;
-
- unsigned char* const p = (unsigned char*)&result;
- unsigned char* q = p + 4;
-
- for (;;)
+ else
{
- hr = pReader->Read(pos, 1, --q);
- assert(hr == 0L);
+ assert(size == 8);
- if (q == p)
- break;
+ union
+ {
+ double d;
+ unsigned long long dd;
+ };
- ++pos;
+ dd = 0;
+
+ for (int i = 0;;)
+ {
+ dd |= buf[i];
+
+ if (++i >= 8)
+ break;
+
+ dd <<= 8;
+ }
+
+ result = d;
}
-#else
- union
- {
- float result;
- unsigned long buf;
- };
- buf = 0;
-
- for (int i = 0;;)
- {
- unsigned char b;
-
- const int status = pReader->Read(pos++, 1, &b);
-
- if (status < 0) //error
- return static_cast<float>(status);
-
- buf |= b;
-
- if (++i >= 4)
- break;
-
- buf <<= 8;
- }
-#endif
-
- return result;
+ return 0;
}
-double mkvparser::Unserialize8Double(
+long mkvparser::UnserializeInt(
IMkvReader* pReader,
- long long pos)
+ long long pos,
+ long size,
+ long long& result)
{
assert(pReader);
assert(pos >= 0);
+ assert(size > 0);
+ assert(size <= 8);
-#if 0
- double result;
-
- unsigned char* const p = (unsigned char*)&result;
- unsigned char* q = p + 8;
-
- for (;;)
{
- const long hr = pReader->Read(pos, 1, --q);
- assert(hr == 0L);
+ signed char b;
- if (q == p)
- break;
+ const long status = pReader->Read(pos, 1, (unsigned char*)&b);
+
+ if (status < 0)
+ return status;
+
+ result = b;
++pos;
}
-#else
- union
- {
- double result;
- long long buf;
- };
- buf = 0;
-
- for (int i = 0;;)
+ for (long i = 1; i < size; ++i)
{
unsigned char b;
- const int status = pReader->Read(pos++, 1, &b);
+ const long status = pReader->Read(pos, 1, &b);
- if (status < 0) //error
- return static_cast<double>(status);
-
- buf |= b;
-
- if (++i >= 8)
- break;
-
- buf <<= 8;
- }
-#endif
-
- return result;
-}
-
-signed char mkvparser::Unserialize1SInt(
- IMkvReader* pReader,
- long long pos)
-{
- assert(pReader);
- assert(pos >= 0);
-
-#ifdef _DEBUG
- {
- long long total, available;
-
- const long status = pReader->Length(&total, &available);
- assert(status == 0);
- assert((total < 0) || (available <= total));
- assert(pos < available);
- }
-#endif
-
- signed char result;
- unsigned char& b = reinterpret_cast<unsigned char&>(result);
-
- const int status = pReader->Read(pos, 1, &b);
- assert(status == 0); //TODO: must be handled somehow
-
- return result;
-}
-
-short mkvparser::Unserialize2SInt(
- IMkvReader* pReader,
- long long pos)
-{
- assert(pReader);
- assert(pos >= 0);
-
-#ifdef _DEBUG
- {
- long long total, available;
-
- const long status = pReader->Length(&total, &available);
- assert(status >= 0);
- assert((total < 0) || (available <= total));
- assert((pos + 2) <= available);
- }
-#endif
-
-#if 0
- short result;
-
- unsigned char* const p = (unsigned char*)&result;
- unsigned char* q = p + 2;
-
- for (;;)
- {
- hr = pReader->Read(pos, 1, --q);
- assert(hr == 0L);
-
- if (q == p)
- break;
-
- ++pos;
- }
-#else
- short result = 0;
-
- for (int i = 0;;)
- {
- unsigned char b;
-
- const int status = pReader->Read(pos++, 1, &b);
- assert(status == 0); //TODO: must be handled somehow
-
- result |= b;
-
- if (++i >= 2)
- break;
+ if (status < 0)
+ return status;
result <<= 8;
- }
-#endif
+ result |= b;
- return result;
+ ++pos;
+ }
+
+ return 0; //success
+}
+
+
+long mkvparser::UnserializeString(
+ IMkvReader* pReader,
+ long long pos,
+ long long size_,
+ char*& str)
+{
+ delete[] str;
+ str = NULL;
+
+ if (size_ >= LONG_MAX) //we need (size+1) chars
+ return E_FILE_FORMAT_INVALID;
+
+ const long size = static_cast<long>(size_);
+
+ str = new (std::nothrow) char[size+1];
+
+ if (str == NULL)
+ return -1;
+
+ unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
+
+ const long status = pReader->Read(pos, size, buf);
+
+ if (status)
+ {
+ delete[] str;
+ str = NULL;
+
+ return status;
+ }
+
+ str[size] = '\0';
+
+ return 0; //success
+}
+
+
+long mkvparser::ParseElementHeader(
+ IMkvReader* pReader,
+ long long& pos,
+ long long stop,
+ long long& id,
+ long long& size)
+{
+ if ((stop >= 0) && (pos >= stop))
+ return E_FILE_FORMAT_INVALID;
+
+ long len;
+
+ id = ReadUInt(pReader, pos, len);
+
+ if (id <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume id
+
+ if ((stop >= 0) && (pos >= stop))
+ return E_FILE_FORMAT_INVALID;
+
+ size = ReadUInt(pReader, pos, len);
+
+ if (size < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume length of size
+
+ //pos now designates payload
+
+ if ((stop >= 0) && ((pos + size) > stop))
+ return E_FILE_FORMAT_INVALID;
+
+ return 0; //success
}
@@ -478,65 +409,6 @@
IMkvReader* pReader,
long long& pos,
unsigned long id_,
- char*& val)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long status = pReader->Length(&total, &available);
- assert(status >= 0);
- assert((total < 0) || (available <= total));
-
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- if ((unsigned long)id != id_)
- return false;
-
- pos += len; //consume id
-
- const long long size_ = ReadUInt(pReader, pos, len);
- assert(size_ >= 0);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- pos += len; //consume length of size of payload
- assert((pos + size_) <= available);
-
- const size_t size = static_cast<size_t>(size_);
- val = new char[size+1];
-
- for (size_t i = 0; i < size; ++i)
- {
- char c;
-
- status = pReader->Read(pos + i, 1, (unsigned char*)&c);
- assert(status == 0); //TODO
-
- val[i] = c;
-
- if (c == '\0')
- break;
- }
-
- val[size] = '\0';
- pos += size_; //consume size of payload
-
- return true;
-}
-
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
unsigned char*& buf,
size_t& buflen)
{
@@ -585,103 +457,6 @@
}
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- double& val)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- const long status = pReader->Length(&total, &available);
- assert(status >= 0);
- assert((total < 0) || (available <= total));
-
- long idlen;
-
- const long long id = ReadUInt(pReader, pos, idlen);
- assert(id >= 0); //TODO
-
- if ((unsigned long)id != id_)
- return false;
-
- long sizelen;
- const long long size = ReadUInt(pReader, pos + idlen, sizelen);
-
- switch (size)
- {
- case 4:
- case 8:
- break;
- default:
- return false;
- }
-
- pos += idlen + sizelen; //consume id and size fields
- assert((pos + size) <= available);
-
- if (size == 4)
- val = Unserialize4Float(pReader, pos);
- else
- {
- assert(size == 8);
- val = Unserialize8Double(pReader, pos);
- }
-
- pos += size; //consume size of payload
-
- return true;
-}
-
-
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- short& val)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- const long status = pReader->Length(&total, &available);
- assert(status >= 0);
- assert((total < 0) || (available <= total));
-
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert((pos + len) <= available);
-
- if ((unsigned long)id != id_)
- return false;
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size <= 2);
- assert((pos + len) <= available);
-
- pos += len; //consume length of size of payload
- assert((pos + size) <= available);
-
- //TODO:
- // Generalize this to work for any size signed int
- if (size == 1)
- val = Unserialize1SInt(pReader, pos);
- else
- val = Unserialize2SInt(pReader, pos);
-
- pos += size; //consume size of payload
-
- return true;
-}
-
-
namespace mkvparser
{
@@ -824,38 +599,75 @@
while (pos < end)
{
- if (Match(pReader, pos, 0x0286, m_version))
- ;
- else if (Match(pReader, pos, 0x02F7, m_readVersion))
- ;
- else if (Match(pReader, pos, 0x02F2, m_maxIdLength))
- ;
- else if (Match(pReader, pos, 0x02F3, m_maxSizeLength))
- ;
- else if (Match(pReader, pos, 0x0282, m_docType))
- ;
- else if (Match(pReader, pos, 0x0287, m_docTypeVersion))
- ;
- else if (Match(pReader, pos, 0x0285, m_docTypeReadVersion))
- ;
- else
+ long long id, size;
+
+ status = ParseElementHeader(
+ pReader,
+ pos,
+ end,
+ id,
+ size);
+
+ if (status < 0) //error
+ return status;
+
+ if (size == 0) //weird
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == 0x0286) //version
{
- result = ReadUInt(pReader, pos, len);
- assert(result > 0);
- assert(len > 0);
- assert(len <= 8);
+ m_version = UnserializeUInt(pReader, pos, size);
- pos += len;
- assert(pos < end);
-
- result = ReadUInt(pReader, pos, len);
- assert(result >= 0);
- assert(len > 0);
- assert(len <= 8);
-
- pos += len + result;
- assert(pos <= end);
+ if (m_version <= 0)
+ return E_FILE_FORMAT_INVALID;
}
+ else if (id == 0x02F7) //read version
+ {
+ m_readVersion = UnserializeUInt(pReader, pos, size);
+
+ if (m_readVersion <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x02F2) //max id length
+ {
+ m_maxIdLength = UnserializeUInt(pReader, pos, size);
+
+ if (m_maxIdLength <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x02F3) //max size length
+ {
+ m_maxSizeLength = UnserializeUInt(pReader, pos, size);
+
+ if (m_maxSizeLength <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x0282) //doctype
+ {
+ if (m_docType)
+ return E_FILE_FORMAT_INVALID;
+
+ status = UnserializeString(pReader, pos, size, m_docType);
+
+ if (status) //error
+ return status;
+ }
+ else if (id == 0x0287) //doctype version
+ {
+ m_docTypeVersion = UnserializeUInt(pReader, pos, size);
+
+ if (m_docTypeVersion <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x0285) //doctype read version
+ {
+ m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
+
+ if (m_docTypeReadVersion <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos += size;
}
assert(pos == end);
@@ -865,9 +677,13 @@
Segment::Segment(
IMkvReader* pReader,
+ long long elem_start,
+ //long long elem_size,
long long start,
long long size) :
m_pReader(pReader),
+ m_element_start(elem_start),
+ //m_element_size(elem_size),
m_start(start),
m_size(size),
m_pos(start),
@@ -967,6 +783,7 @@
if ((pos + len) > available)
return pos + len;
+ const long long idpos = pos;
const long long id = ReadUInt(pReader, pos, len);
if (id < 0) //error
@@ -1007,15 +824,15 @@
else if (total < 0)
size = -1;
-#if 0 //this turned out to be too conservative:
- else if ((pos + size) > end)
- return E_FILE_FORMAT_INVALID;
-#else //so do this instead
else if ((pos + size) > total)
size = -1;
-#endif
- pSegment = new (std::nothrow) Segment(pReader, pos, size);
+ pSegment = new (std::nothrow) Segment(
+ pReader,
+ idpos,
+ //elem_size
+ pos,
+ size);
if (pSegment == 0)
return -1; //generic error
@@ -1045,7 +862,10 @@
long long total, available;
const int status = m_pReader->Length(&total, &available);
- assert(status == 0);
+
+ if (status < 0) //error
+ return status;
+
assert((total < 0) || (available <= total));
const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
@@ -1131,55 +951,77 @@
if (id == 0x0549A966) //Segment Info ID
{
- assert(m_pInfo == NULL);
+ if (m_pInfo)
+ return E_FILE_FORMAT_INVALID;
- m_pInfo = new SegmentInfo(this,
- pos,
- size,
- element_start,
- element_size);
- assert(m_pInfo); //TODO
+ m_pInfo = new (std::nothrow) SegmentInfo(
+ this,
+ pos,
+ size,
+ element_start,
+ element_size);
+
+ if (m_pInfo == NULL)
+ return -1;
+
+ const long status = m_pInfo->Parse();
+
+ if (status)
+ return status;
}
else if (id == 0x0654AE6B) //Tracks ID
{
- assert(m_pTracks == NULL);
+ if (m_pTracks)
+ return E_FILE_FORMAT_INVALID;
- m_pTracks = new Tracks(this,
- pos,
- size,
- element_start,
- element_size);
- assert(m_pTracks); //TODO
+ m_pTracks = new (std::nothrow) Tracks(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+
+ if (m_pTracks == NULL)
+ return -1;
+
+ const long status = m_pTracks->Parse();
+
+ if (status)
+ return status;
}
else if (id == 0x0C53BB6B) //Cues ID
{
if (m_pCues == NULL)
{
- m_pCues = new Cues(this,
- pos,
- size,
- element_start,
- element_size);
- assert(m_pCues); //TODO
+ m_pCues = new (std::nothrow) Cues(
+ this,
+ pos,
+ size,
+ element_start,
+ element_size);
+
+ if (m_pCues == NULL)
+ return -1;
}
}
else if (id == 0x014D9B74) //SeekHead ID
{
-#if 0
- if (available >= total)
- ParseSeekHead(pos, size);
-#else
if (m_pSeekHead == NULL)
{
- m_pSeekHead = new SeekHead(this,
- pos,
- size,
- element_start,
- element_size);
+ m_pSeekHead = new (std::nothrow) SeekHead(
+ this,
+ pos,
+ size,
+ element_start,
+ element_size);
- assert(m_pSeekHead); //TODO
+ if (m_pSeekHead == NULL)
+ return -1;
+
+ const long status = m_pSeekHead->Parse();
+
+ if (status)
+ return status;
}
-#endif
}
m_pos = pos + size; //consume payload
@@ -1197,248 +1039,6 @@
}
-#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
-
-
-#if 0
-long Segment::ParseCluster(long long& off, long long& new_pos) const
-{
- off = -1;
- new_pos = -1;
-
- const long long stop = m_start + m_size;
- assert(m_pos <= stop);
-
- long long pos = m_pos;
-
- while (pos < stop)
- {
- long len;
- const long long idpos = pos;
-
- const long long id = SyncReadUInt(m_pReader, pos, stop, len);
-
- if (id < 0) //error
- return static_cast<long>(id);
-
- if (id == 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; //consume id
- assert(pos < stop);
-
- const long long size = SyncReadUInt(m_pReader, pos, stop, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- pos += len; //consume size
- assert(pos <= stop);
-
- if (size == 0) //weird
- continue;
-
- //pos now points to start of payload
-
- pos += size; //consume payload
- assert(pos <= stop);
-
- if (id == 0x0F43B675) //Cluster ID
- {
- const long long off_ = idpos - m_start;
-
- if (Cluster::HasBlockEntries(this, off_))
- {
- off = off_; // >= 0 means we found a cluster
- break;
- }
- }
- }
-
- 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
- {
- new_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);
-
- new_pos = stop;
- }
- else
- {
- long len;
- const long long idpos = pos;
-
- const long long 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 long long size = SyncReadUInt(m_pReader, pos, stop, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- new_pos = idpos;
- }
-
- return 0;
-}
-
-
-bool Segment::AddCluster(long long off, long long pos)
-{
- assert(pos >= m_start);
-
- const long long stop = m_start + m_size;
- assert(pos <= stop);
-
- if (off >= 0)
- {
- Cluster* const pCluster = Cluster::Parse(this,
- m_clusterCount,
- off,
- 0,
- 0);
- assert(pCluster);
-
- AppendCluster(pCluster);
- assert(m_clusters);
- assert(m_clusterSize > pCluster->m_index);
- assert(m_clusters[pCluster->m_index] == pCluster);
- }
-
- m_pos = pos; //m_pos >= stop is now we know we have all clusters
- return (pos >= stop);
-}
-#endif
-
-
long Segment::LoadCluster(
long long& pos,
long& len)
@@ -2097,24 +1697,7 @@
if (count >= size)
{
- long n;
-
- if (size > 0)
- n = 2 * size;
- else if (m_pInfo == 0)
- n = 2048;
- else
- {
- const long long ns = m_pInfo->GetDuration();
-
- if (ns <= 0)
- n = 2048;
- else
- {
- const long long sec = (ns + 999999999LL) / 1000000000LL;
- n = static_cast<long>(sec);
- }
- }
+ const long n = (size <= 0) ? 2048 : 2*size;
Cluster** const qq = new Cluster*[n];
Cluster** q = qq;
@@ -2173,24 +1756,7 @@
if (count >= size)
{
- long n;
-
- if (size > 0)
- n = 2 * size;
- else if (m_pInfo == 0)
- n = 2048;
- else
- {
- const long long ns = m_pInfo->GetDuration();
-
- if (ns <= 0)
- n = 2048;
- else
- {
- const long long sec = (ns + 999999999LL) / 1000000000LL;
- n = static_cast<long>(sec);
- }
- }
+ const long n = (size <= 0) ? 2048 : 2*size;
Cluster** const qq = new Cluster*[n];
Cluster** q = qq;
@@ -2240,181 +1806,30 @@
//and pos designates start of payload. We need to find the
//inner (level 1) elements.
- long long total, avail;
+ const long long header_status = ParseHeaders();
- long status = m_pReader->Length(&total, &avail);
+ if (header_status < 0) //error
+ return static_cast<long>(header_status);
- if (status < 0) //error
- return status;
+ if (header_status > 0) //underflow
+ return E_BUFFER_NOT_FULL;
- assert((total < 0) || (avail <= total));
-
- const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+ assert(m_pInfo);
+ assert(m_pTracks);
for (;;)
{
- long long pos = m_pos;
+ const int status = LoadCluster();
- if ((total >= 0) && (pos >= total))
- break;
+ if (status < 0) //error
+ return status;
- if ((segment_stop >= 0) && (pos >= segment_stop))
- break;
-
- const long long element_start = pos;
-
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- 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 ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; //consume length of size of element
-
- //Pos now points to start of payload
-
- const long long element_size = (pos - element_start) + size;
-
- if ((segment_stop >= 0) && ((pos + size) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if (id == 0x0F43B675) //Cluster ID
- {
- const long idx = m_clusterCount;
- const long long off = idpos - m_start;
-
- long long pos_;
- long len_;
-
- status = Cluster::HasBlockEntries(this, off, pos_, len_);
-
- if (status < 0) //weird: error or underflow
- return status;
-
- if (status > 0) //have block entries
- {
- Cluster* const pCluster = Cluster::Create(this,
- idx,
- off);
- //element_size);
- assert(pCluster);
-
- AppendCluster(pCluster);
- assert(m_clusters);
- assert(m_clusterSize > idx);
- assert(m_clusters[idx] == pCluster);
- }
- }
- else if (id == 0x0C53BB6B) //Cues ID
- {
- assert(m_pCues == NULL);
-
- m_pCues = new Cues(this, pos, size, element_start, element_size);
- assert(m_pCues); //TODO
- }
- else if (id == 0x0549A966) //SegmentInfo ID
- {
- assert(m_pInfo == NULL);
-
- m_pInfo = new SegmentInfo(this,
- pos,
- size,
- element_start,
- element_size);
- assert(m_pInfo);
- }
- else if (id == 0x0654AE6B) //Tracks ID
- {
- assert(m_pTracks == NULL);
-
- m_pTracks = new Tracks(this,
- pos,
- size,
- element_start,
- element_size);
- assert(m_pTracks); //TODO
- }
-
- m_pos = pos + size; //consume payload
+ if (status >= 1) //no more clusters
+ return 0;
}
-
- if (m_pInfo == NULL)
- return E_FILE_FORMAT_INVALID; //TODO: ignore this case?
-
- if (m_pTracks == NULL)
- return E_FILE_FORMAT_INVALID;
-
- if (m_clusters == NULL) //TODO: ignore this case?
- return E_FILE_FORMAT_INVALID;
-
- return 0;
}
-#if 0
-void Segment::ParseSeekHead(long long start, long long size_)
-{
- long long pos = start;
- const long long stop = start + size_;
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(m_pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume Size field
- assert((pos + size) <= stop);
-
- if (id == 0x0DBB) //SeekEntry ID
- ParseSeekEntry(pos, size);
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- assert(pos == stop);
-}
-#else
SeekHead::SeekHead(
Segment* pSegment,
long long start,
@@ -2427,95 +1842,133 @@
m_element_start(element_start),
m_element_size(element_size),
m_entries(0),
- m_count(0)
+ m_entry_count(0),
+ m_void_elements(0),
+ m_void_element_count(0)
{
- long long pos = start;
- const long long stop = start + size_;
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- //first count the seek head entries
-
- int count = 0;
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- 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);
-
- if (id == 0x0DBB) //SeekEntry ID
- ++count;
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- assert(pos == stop);
-
- if (count <= 0)
- return; //nothing else for us to do
-
- m_entries = new (std::nothrow) Entry[count];
- assert(m_entries); //TODO
-
- //now parse the entries
-
- Entry* pEntry = m_entries;
- pos = start;
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- 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);
-
- if (id == 0x0DBB) //SeekEntry ID
- ParseEntry(pReader, pos, size, pEntry);
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- assert(pos == stop);
-
- const ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
- assert(count_ >= 0);
- assert(count_ <= count);
-
- m_count = static_cast<int>(count_);
}
+
SeekHead::~SeekHead()
{
delete[] m_entries;
+ delete[] m_void_elements;
}
+
+long SeekHead::Parse()
+{
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = m_start;
+ const long long stop = m_start + m_size;
+
+ //first count the seek head entries
+
+ int entry_count = 0;
+ int void_element_count = 0;
+
+ while (pos < stop)
+ {
+ long long id, size;
+
+ const long status = ParseElementHeader(
+ pReader,
+ pos,
+ stop,
+ id,
+ size);
+
+ if (status < 0) //error
+ return status;
+
+ if (id == 0x0DBB) //SeekEntry ID
+ ++entry_count;
+ else if (id == 0x6C) //Void ID
+ ++void_element_count;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+
+ m_entries = new (std::nothrow) Entry[entry_count];
+
+ if (m_entries == NULL)
+ return -1;
+
+ m_void_elements = new (std::nothrow) VoidElement[void_element_count];
+
+ if (m_void_elements == NULL)
+ return -1;
+
+ //now parse the entries and void elements
+
+ Entry* pEntry = m_entries;
+ VoidElement* pVoidElement = m_void_elements;
+
+ pos = m_start;
+
+ while (pos < stop)
+ {
+ const long long idpos = pos;
+
+ long long id, size;
+
+ const long status = ParseElementHeader(
+ pReader,
+ pos,
+ stop,
+ id,
+ size);
+
+ if (status < 0) //error
+ return status;
+
+ if (id == 0x0DBB) //SeekEntry ID
+ {
+ if (ParseEntry(pReader, pos, size, pEntry))
+ {
+ Entry& e = *pEntry++;
+
+ e.element_start = idpos;
+ e.element_size = (pos + size) - idpos;
+ }
+ }
+ else if (id == 0x6C) //Void ID
+ {
+ VoidElement& e = *pVoidElement++;
+
+ e.element_start = idpos;
+ e.element_size = (pos + size) - idpos;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+
+ ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
+ assert(count_ >= 0);
+ assert(count_ <= entry_count);
+
+ m_entry_count = static_cast<int>(count_);
+
+ count_ = ptrdiff_t(pVoidElement - m_void_elements);
+ assert(count_ >= 0);
+ assert(count_ <= void_element_count);
+
+ m_void_element_count = static_cast<int>(count_);
+
+ return 0;
+}
+
+
int SeekHead::GetCount() const
{
- return m_count;
+ return m_entry_count;
}
const SeekHead::Entry* SeekHead::GetEntry(int idx) const
@@ -2523,12 +1976,27 @@
if (idx < 0)
return 0;
- if (idx >= m_count)
+ if (idx >= m_entry_count)
return 0;
return m_entries + idx;
}
-#endif
+
+int SeekHead::GetVoidElementCount() const
+{
+ return m_void_element_count;
+}
+
+const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const
+{
+ if (idx < 0)
+ return 0;
+
+ if (idx >= m_void_element_count)
+ return 0;
+
+ return m_void_elements + idx;
+}
#if 0
@@ -2766,12 +2234,15 @@
ParseCues(seekOff);
}
#else
-void SeekHead::ParseEntry(
+bool SeekHead::ParseEntry(
IMkvReader* pReader,
long long start,
long long size_,
- Entry*& pEntry)
+ Entry* pEntry)
{
+ if (size_ <= 0)
+ return false;
+
long long pos = start;
const long long stop = start + size_;
@@ -2783,75 +2254,81 @@
//seekIdId;
if (seekIdId != 0x13AB) //SeekID ID
- return;
+ return false;
if ((pos + len) > stop)
- return;
+ return false;
pos += len; //consume SeekID id
const long long seekIdSize = ReadUInt(pReader, pos, len);
if (seekIdSize <= 0)
- return;
+ return false;
if ((pos + len) > stop)
- return;
+ return false;
pos += len; //consume size of field
if ((pos + seekIdSize) > stop)
- return;
+ return false;
- //TODO: it's not clear whether this is correct
- //It seems as if the payload here is "binary" which
- //means the value of the ID should be unserialized,
- //not parsed as an uint.
+ //Note that the SeekId payload really is serialized
+ //as a "Matroska integer", not as a plain binary value.
+ //In fact, Matroska requires that ID values in the
+ //stream exactly match the binary representation as listed
+ //in the Matroska specification.
//
+ //This parser is more liberal, and permits IDs to have
+ //any width. (This could make the representation in the stream
+ //different from what's in the spec, but it doesn't matter here,
+ //since we always normalize "Matroska integer" values.)
+
pEntry->id = ReadUInt(pReader, pos, len); //payload
if (pEntry->id <= 0)
- return;
+ return false;
if (len != seekIdSize)
- return;
+ return false;
pos += seekIdSize; //consume SeekID payload
const long long seekPosId = ReadUInt(pReader, pos, len);
if (seekPosId != 0x13AC) //SeekPos ID
- return;
+ return false;
if ((pos + len) > stop)
- return;
+ return false;
pos += len; //consume id
const long long seekPosSize = ReadUInt(pReader, pos, len);
if (seekPosSize <= 0)
- return;
+ return false;
if ((pos + len) > stop)
- return;
+ return false;
pos += len; //consume size
if ((pos + seekPosSize) > stop)
- return;
+ return false;
pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
if (pEntry->pos < 0)
- return;
+ return false;
pos += seekPosSize; //consume payload
if (pos != stop)
- return;
+ return false;
- ++pEntry; //success
+ return true;
}
#endif
@@ -2865,12 +2342,12 @@
m_pSegment(pSegment),
m_start(start_),
m_size(size_),
+ m_element_start(element_start),
+ m_element_size(element_size),
m_cue_points(NULL),
m_count(0),
m_preload_count(0),
- m_pos(start_),
- m_element_start(element_start),
- m_element_size(element_size)
+ m_pos(start_)
{
}
@@ -2961,29 +2438,7 @@
if (m_preload_count >= cue_points_size)
{
- long n;
-
- if (cue_points_size > 0)
- n = 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<long>(sec);
- }
- }
- }
+ const long n = (cue_points_size <= 0) ? 2048 : 2*cue_points_size;
CuePoint** const qq = new CuePoint*[n];
CuePoint** q = qq; //beginning of target
@@ -3059,10 +2514,11 @@
m_pos += size; //consume payload
assert(m_pos <= stop);
- break;
+ return true; //yes, we loaded a cue point
}
- return (m_pos < stop);
+ //return (m_pos < stop);
+ return false; //no, we did not load a cue point
}
@@ -3737,6 +3193,7 @@
}
+#if 0
long long Segment::Unparsed() const
{
if (m_size < 0)
@@ -3749,6 +3206,29 @@
return result;
}
+#else
+bool Segment::DoneParsing() const
+{
+ if (m_size < 0)
+ {
+ long long total, avail;
+
+ const int status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return true; //must assume done
+
+ if (total < 0)
+ return false; //assume live stream
+
+ return (m_pos >= total);
+ }
+
+ const long long stop = m_start + m_size;
+
+ return (m_pos >= stop);
+}
+#endif
const Cluster* Segment::GetFirst() const
@@ -4751,81 +4231,119 @@
m_pSegment(pSegment),
m_start(start),
m_size(size_),
+ m_element_start(element_start),
+ m_element_size(element_size),
m_pMuxingAppAsUTF8(NULL),
m_pWritingAppAsUTF8(NULL),
- m_pTitleAsUTF8(NULL),
- m_element_start(element_start),
- m_element_size(element_size)
+ m_pTitleAsUTF8(NULL)
{
+}
+
+SegmentInfo::~SegmentInfo()
+{
+ delete[] m_pMuxingAppAsUTF8;
+ m_pMuxingAppAsUTF8 = NULL;
+
+ delete[] m_pWritingAppAsUTF8;
+ m_pWritingAppAsUTF8 = NULL;
+
+ delete[] m_pTitleAsUTF8;
+ m_pTitleAsUTF8 = NULL;
+}
+
+
+long SegmentInfo::Parse()
+{
+ assert(m_pMuxingAppAsUTF8 == NULL);
+ assert(m_pWritingAppAsUTF8 == NULL);
+ assert(m_pTitleAsUTF8 == NULL);
+
IMkvReader* const pReader = m_pSegment->m_pReader;
- long long pos = start;
- const long long stop = start + size_;
+ long long pos = m_start;
+ const long long stop = m_start + m_size;
m_timecodeScale = 1000000;
m_duration = -1;
while (pos < stop)
{
- if (Match(pReader, pos, 0x0AD7B1, m_timecodeScale))
- assert(m_timecodeScale > 0);
+ long long id, size;
- else if (Match(pReader, pos, 0x0489, m_duration))
- assert(m_duration >= 0);
+ const long status = ParseElementHeader(
+ pReader,
+ pos,
+ stop,
+ id,
+ size);
- else if (Match(pReader, pos, 0x0D80, m_pMuxingAppAsUTF8)) //[4D][80]
- assert(m_pMuxingAppAsUTF8);
+ if (status < 0) //error
+ return status;
- else if (Match(pReader, pos, 0x1741, m_pWritingAppAsUTF8)) //[57][41]
- assert(m_pWritingAppAsUTF8);
-
- else if (Match(pReader, pos, 0x3BA9, m_pTitleAsUTF8)) //[7B][A9]
- assert(m_pTitleAsUTF8);
-
- else
+ if (id == 0x0AD7B1) //Timecode Scale
{
- long len;
+ m_timecodeScale = UnserializeUInt(pReader, pos, size);
- const long long id = ReadUInt(pReader, pos, len);
- //id;
- assert(id >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume id
- assert((stop - pos) > 0);
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len + size; //consume size and payload
- assert(pos <= stop);
+ if (m_timecodeScale <= 0)
+ return E_FILE_FORMAT_INVALID;
}
+ else if (id == 0x0489) //Segment duration
+ {
+ const long status = UnserializeFloat(
+ pReader,
+ pos,
+ size,
+ m_duration);
+
+ if (status < 0)
+ return status;
+
+ if (m_duration < 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x0D80) //MuxingApp
+ {
+ const long status = UnserializeString(
+ pReader,
+ pos,
+ size,
+ m_pMuxingAppAsUTF8);
+
+ if (status)
+ return status;
+ }
+ else if (id == 0x1741) //WritingApp
+ {
+ const long status = UnserializeString(
+ pReader,
+ pos,
+ size,
+ m_pWritingAppAsUTF8);
+
+ if (status)
+ return status;
+ }
+ else if (id == 0x3BA9) //Title
+ {
+ const long status = UnserializeString(
+ pReader,
+ pos,
+ size,
+ m_pTitleAsUTF8);
+
+ if (status)
+ return status;
+ }
+
+ pos += size;
+ assert(pos <= stop);
}
assert(pos == stop);
+
+ return 0;
}
-SegmentInfo::~SegmentInfo()
-{
- if (m_pMuxingAppAsUTF8)
- {
- delete[] m_pMuxingAppAsUTF8;
- m_pMuxingAppAsUTF8 = NULL;
- }
-
- if (m_pWritingAppAsUTF8)
- {
- delete[] m_pWritingAppAsUTF8;
- m_pWritingAppAsUTF8 = NULL;
- }
-
- if (m_pTitleAsUTF8)
- {
- delete[] m_pTitleAsUTF8;
- m_pTitleAsUTF8 = NULL;
- }
-}
long long SegmentInfo::GetTimeCodeScale() const
{
@@ -4862,15 +4380,287 @@
return m_pTitleAsUTF8;
}
+///////////////////////////////////////////////////////////////
+// ContentEncoding element
+ContentEncoding::ContentCompression::ContentCompression()
+ : algo(0),
+ settings(NULL) {
+}
+
+ContentEncoding::ContentCompression::~ContentCompression() {
+ delete [] settings;
+}
+
+ContentEncoding::ContentEncryption::ContentEncryption()
+ : algo(0),
+ key_id(NULL),
+ key_id_len(0),
+ signature(NULL),
+ signature_len(0),
+ sig_key_id(NULL),
+ sig_key_id_len(0),
+ sig_algo(0),
+ sig_hash_algo(0) {
+}
+
+ContentEncoding::ContentEncryption::~ContentEncryption() {
+ delete [] key_id;
+ delete [] signature;
+ delete [] sig_key_id;
+}
+
+ContentEncoding::ContentEncoding()
+ : compression_entries_(NULL),
+ compression_entries_end_(NULL),
+ encryption_entries_(NULL),
+ encryption_entries_end_(NULL),
+ encoding_order_(0),
+ encoding_scope_(1),
+ encoding_type_(0) {
+}
+
+ContentEncoding::~ContentEncoding() {
+ ContentCompression** comp_i = compression_entries_;
+ ContentCompression** const comp_j = compression_entries_end_;
+
+ while (comp_i != comp_j) {
+ ContentCompression* const comp = *comp_i++;
+ delete comp;
+ }
+
+ delete [] compression_entries_;
+
+ ContentEncryption** enc_i = encryption_entries_;
+ ContentEncryption** const enc_j = encryption_entries_end_;
+
+ while (enc_i != enc_j) {
+ ContentEncryption* const enc = *enc_i++;
+ delete enc;
+ }
+
+ delete [] encryption_entries_;
+}
+
+
+const ContentEncoding::ContentCompression*
+ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
+ const ptrdiff_t count = compression_entries_end_ - compression_entries_;
+ assert(count >= 0);
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return compression_entries_[idx];
+}
+
+unsigned long ContentEncoding::GetCompressionCount() const {
+ const ptrdiff_t count = compression_entries_end_ - compression_entries_;
+ assert(count >= 0);
+
+ return static_cast<unsigned long>(count);
+}
+
+const ContentEncoding::ContentEncryption*
+ContentEncoding::GetEncryptionByIndex(unsigned long idx) const {
+ const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
+ assert(count >= 0);
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return encryption_entries_[idx];
+}
+
+unsigned long ContentEncoding::GetEncryptionCount() const {
+ const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
+ assert(count >= 0);
+
+ return static_cast<unsigned long>(count);
+}
+
+void ContentEncoding::ParseEncryptionEntry(
+ long long start,
+ long long size,
+ IMkvReader* const pReader,
+ ContentEncryption* const encryption) {
+ assert(pReader);
+ assert(encryption);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ while (pos < stop) {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+#endif
+
+ long long value;
+ unsigned char* buf;
+ size_t buf_len;
+
+ if (Match(pReader, pos, 0x7E1, value)) {
+ // ContentEncAlgo
+ encryption->algo = value;
+ } else if (Match(pReader, pos, 0x7E2, buf, buf_len)) {
+ // ContentEncKeyID
+ encryption->key_id = buf;
+ encryption->key_id_len = buf_len;
+ } else if (Match(pReader, pos, 0x7E3, buf, buf_len)) {
+ // ContentSignature
+ encryption->signature = buf;
+ encryption->signature_len = buf_len;
+ } else if (Match(pReader, pos, 0x7E4, buf, buf_len)) {
+ // ContentSigKeyID
+ encryption->sig_key_id = buf;
+ encryption->sig_key_id_len = buf_len;
+ } else if (Match(pReader, pos, 0x7E5, value)) {
+ // ContentSigAlgo
+ encoding_type_ = value;
+ } else if (Match(pReader, pos, 0x7E6, value)) {
+ // ContentSigHashAlgo
+ encoding_type_ = value;
+ } else {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ assert((pos + size) <= stop);
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+}
+
+bool ContentEncoding::ParseContentEncodingEntry(long long start,
+ long long size,
+ IMkvReader* const pReader) {
+ assert(pReader);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ // Count ContentCompression and ContentEncryption elements.
+ long long pos1 = start;
+ int compression_count = 0;
+ int encryption_count = 0;
+
+ while (pos1 < stop) {
+ long len;
+ const long long id = ReadUInt(pReader, pos1, len);
+ assert(id >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos1, len);
+ assert(size >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume length of size
+
+ //pos now designates start of element
+ if (id == 0x1034) // ContentCompression ID
+ ++compression_count;
+
+ if (id == 0x1035) // ContentEncryption ID
+ ++encryption_count;
+
+ pos1 += size; //consume payload
+ assert(pos1 <= stop);
+ }
+
+ if (compression_count <= 0 && encryption_count <= 0)
+ return false;
+
+ if (compression_count > 0) {
+ compression_entries_ = new ContentCompression*[compression_count];
+ compression_entries_end_ = compression_entries_;
+ }
+
+ if (encryption_count > 0) {
+ encryption_entries_ = new ContentEncryption*[encryption_count];
+ encryption_entries_end_ = encryption_entries_;
+ }
+
+ while (pos < stop) {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+#endif
+
+ long long value;
+ if (Match(pReader, pos, 0x1031, value)) {
+ // ContentEncodingOrder
+ encoding_order_ = value;
+ } else if (Match(pReader, pos, 0x1032, value)) {
+ // ContentEncodingScope
+ encoding_scope_ = value;
+ assert(encoding_scope_ > 0);
+ } else if (Match(pReader, pos, 0x1033, value)) {
+ // ContentEncodingType
+ encoding_type_ = value;
+ } else {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ assert((pos + size) <= stop);
+
+ //pos now designates start of payload
+
+ if (id == 0x1034) {
+ // ContentCompression ID
+ // TODO(fgaligan): Add code to parse ContentCompression elements.
+ } else if (id == 0x1035) {
+ // ContentEncryption ID
+ ContentEncryption* const encryption = new ContentEncryption();
+
+ ParseEncryptionEntry(pos, size, pReader, encryption);
+ *encryption_entries_end_ = encryption;
+ ++encryption_entries_end_;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ assert(pos == stop);
+
+ return true;
+}
+
Track::Track(
Segment* pSegment,
- const Info& i,
long long element_start,
long long element_size) :
m_pSegment(pSegment),
- m_info(i),
m_element_start(element_start),
- m_element_size(element_size)
+ m_element_size(element_size),
+ content_encoding_entries_(NULL),
+ content_encoding_entries_end_(NULL)
{
}
@@ -4878,20 +4668,32 @@
{
Info& info = const_cast<Info&>(m_info);
info.Clear();
+
+ ContentEncoding** i = content_encoding_entries_;
+ ContentEncoding** const j = content_encoding_entries_end_;
+
+ while (i != j) {
+ ContentEncoding* const encoding = *i++;
+ delete encoding;
+ }
+
+ delete [] content_encoding_entries_;
}
Track::Info::Info():
- type(-1),
- number(-1),
- uid(ULLONG_MAX),
nameAsUTF8(NULL),
codecId(NULL),
+ codecNameAsUTF8(NULL),
codecPrivate(NULL),
- codecPrivateSize(0),
- codecNameAsUTF8(NULL)
+ codecPrivateSize(0)
{
}
+Track::Info::~Info()
+{
+ Clear();
+}
+
void Track::Info::Clear()
{
delete[] nameAsUTF8;
@@ -4902,24 +4704,100 @@
delete[] codecPrivate;
codecPrivate = NULL;
-
codecPrivateSize = 0;
delete[] codecNameAsUTF8;
codecNameAsUTF8 = NULL;
}
+int Track::Info::CopyStr(char* Info::*str, Info& dst_) const
+{
+ if (str == static_cast<char* Info::*>(NULL))
+ return -1;
+
+ char*& dst = dst_.*str;
+
+ if (dst) //should be NULL already
+ return -1;
+
+ const char* const src = this->*str;
+
+ if (src == NULL)
+ return 0;
+
+ const size_t len = strlen(src);
+
+ dst = new (std::nothrow) char[len+1];
+
+ if (dst == NULL)
+ return -1;
+
+ strcpy(dst, src);
+
+ return 0;
+}
+
+
+int Track::Info::Copy(Info& dst) const
+{
+ if (&dst == this)
+ return 0;
+
+ dst.type = type;
+ dst.number = number;
+ dst.uid = uid;
+ dst.lacing = lacing;
+ dst.settings = settings;
+
+ //We now copy the string member variables from src to dst.
+ //This involves memory allocation so in principle the operation
+ //can fail (indeed, that's why we have Info::Copy), so we must
+ //report this to the caller. An error return from this function
+ //therefore implies that the copy was only partially successful.
+
+ if (int status = CopyStr(&Info::nameAsUTF8, dst))
+ return status;
+
+ if (int status = CopyStr(&Info::codecId, dst))
+ return status;
+
+ if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
+ return status;
+
+ if (codecPrivateSize > 0)
+ {
+ if (codecPrivate == NULL)
+ return -1;
+
+ if (dst.codecPrivate)
+ return -1;
+
+ if (dst.codecPrivateSize != 0)
+ return -1;
+
+ dst.codecPrivate = new (std::nothrow) unsigned char[codecPrivateSize];
+
+ if (dst.codecPrivate == NULL)
+ return -1;
+
+ memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
+ dst.codecPrivateSize = codecPrivateSize;
+ }
+
+ return 0;
+}
+
const BlockEntry* Track::GetEOS() const
{
return &m_eos;
}
-long long Track::GetType() const
+long Track::GetType() const
{
return m_info.type;
}
-long long Track::GetNumber() const
+long Track::GetNumber() const
{
return m_info.number;
}
@@ -4972,17 +4850,28 @@
if (pCluster->EOS())
{
+#if 0
if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded
{
pBlockEntry = GetEOS();
return 1;
}
+#else
+ if (m_pSegment->DoneParsing())
+ {
+ pBlockEntry = GetEOS();
+ return 1;
+ }
+#endif
pBlockEntry = 0;
return E_BUFFER_NOT_FULL;
}
- pBlockEntry = pCluster->GetFirst();
+ long status = pCluster->GetFirst(pBlockEntry);
+
+ if (status < 0) //error
+ return status;
if (pBlockEntry == 0) //empty cluster
{
@@ -5000,10 +4889,17 @@
if ((tn == m_info.number) && VetEntry(pBlockEntry))
return 0;
- pBlockEntry = pCluster->GetNext(pBlockEntry);
+ const BlockEntry* pNextEntry;
- if (pBlockEntry == 0)
+ status = pCluster->GetNext(pBlockEntry, pNextEntry);
+
+ if (status < 0) //error
+ return status;
+
+ if (pNextEntry == 0)
break;
+
+ pBlockEntry = pNextEntry;
}
++i;
@@ -5037,7 +4933,10 @@
assert(pCluster);
assert(!pCluster->EOS());
- pNextEntry = pCluster->GetNext(pCurrEntry);
+ long status = pCluster->GetNext(pCurrEntry, pNextEntry);
+
+ if (status < 0) //error
+ return status;
for (int i = 0; ; )
{
@@ -5049,7 +4948,12 @@
if (pNextBlock->GetTrackNumber() == m_info.number)
return 0;
- pNextEntry = pCluster->GetNext(pNextEntry);
+ pCurrEntry = pNextEntry;
+
+ status = pCluster->GetNext(pCurrEntry, pNextEntry);
+
+ if (status < 0) //error
+ return status;
}
pCluster = m_pSegment->GetNext(pCluster);
@@ -5062,11 +4966,19 @@
if (pCluster->EOS())
{
+#if 0
if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded
{
pNextEntry = GetEOS();
return 1;
}
+#else
+ if (m_pSegment->DoneParsing())
+ {
+ pNextEntry = GetEOS();
+ return 1;
+ }
+#endif
//TODO: there is a potential O(n^2) problem here: we tell the
//caller to (pre)load another cluster, which he does, but then he
@@ -5084,7 +4996,10 @@
return E_BUFFER_NOT_FULL;
}
- pNextEntry = pCluster->GetFirst();
+ status = pCluster->GetFirst(pNextEntry);
+
+ if (status < 0) //error
+ return status;
if (pNextEntry == NULL) //empty cluster
continue;
@@ -5103,27 +5018,108 @@
return 1;
}
+const ContentEncoding*
+Track::GetContentEncodingByIndex(unsigned long idx) const {
+ const ptrdiff_t count =
+ content_encoding_entries_end_ - content_encoding_entries_;
+ assert(count >= 0);
-Track::EOSBlock::EOSBlock()
-{
-}
-
-
-bool Track::EOSBlock::EOS() const
-{
- return true;
-}
-
-
-const Cluster* Track::EOSBlock::GetCluster() const
-{
+ if (idx >= static_cast<unsigned long>(count))
return NULL;
+
+ return content_encoding_entries_[idx];
}
+unsigned long Track::GetContentEncodingCount() const {
+ const ptrdiff_t count =
+ content_encoding_entries_end_ - content_encoding_entries_;
+ assert(count >= 0);
-size_t Track::EOSBlock::GetIndex() const
+ return static_cast<unsigned long>(count);
+}
+
+void Track::ParseContentEncodingsEntry(long long start, long long size) {
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+ assert(pReader);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ // Count ContentEncoding elements.
+ long long pos1 = start;
+ int count = 0;
+
+ while (pos1 < stop) {
+ long len;
+ const long long id = ReadUInt(pReader, pos1, len);
+ assert(id >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos1, len);
+ assert(size >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume length of size
+
+ //pos now designates start of element
+ if (id == 0x2240) // ContentEncoding ID
+ ++count;
+
+ pos1 += size; //consume payload
+ assert(pos1 <= stop);
+ }
+
+ if (count <= 0)
+ return;
+
+ content_encoding_entries_ = new ContentEncoding*[count];
+ content_encoding_entries_end_ = content_encoding_entries_;
+
+ while (pos < stop) {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size1 = ReadUInt(pReader, pos, len);
+ assert(size1 >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+
+ //pos now designates start of element
+ if (id == 0x2240) { // ContentEncoding ID
+ ContentEncoding* const content_encoding = new ContentEncoding();
+
+ if (!content_encoding->ParseContentEncodingEntry(pos, size1, pReader)) {
+ delete content_encoding;
+ } else {
+ *content_encoding_entries_end_ = content_encoding;
+ ++content_encoding_entries_end_;
+ }
+ }
+
+ pos += size1; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+
+ return;
+}
+
+Track::EOSBlock::EOSBlock() :
+ BlockEntry(NULL, LONG_MIN)
{
- return 0;
+}
+
+BlockEntry::Kind Track::EOSBlock::GetKind() const
+{
+ return kBlockEOS;
}
@@ -5133,24 +5129,31 @@
}
-bool Track::EOSBlock::IsBFrame() const
+VideoTrack::VideoTrack(
+ Segment* pSegment,
+ long long element_start,
+ long long element_size) :
+ Track(pSegment, element_start, element_size)
{
- return false;
}
-VideoTrack::VideoTrack(
+long VideoTrack::Parse(
Segment* pSegment,
const Info& i,
- long long element_start,
- long long element_size) :
- Track(pSegment, i, element_start, element_size),
- m_width(-1),
- m_height(-1),
- m_rate(-1)
+ long long elem_st,
+ long long elem_sz,
+ VideoTrack*& pTrack)
{
- assert(i.type == 1);
- assert(i.number > 0);
+ if (pTrack)
+ return -1;
+
+ if (i.type != Track::kVideo)
+ return -1;
+
+ long long width = 0;
+ long long height = 0;
+ double rate = 0.0;
IMkvReader* const pReader = pSegment->m_pReader;
@@ -5165,42 +5168,68 @@
while (pos < stop)
{
-#ifdef _DEBUG
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-#endif
- if (Match(pReader, pos, 0x30, m_width))
- ;
- else if (Match(pReader, pos, 0x3A, m_height))
- ;
- else if (Match(pReader, pos, 0x0383E3, m_rate))
- ;
- else
+ long long id, size;
+
+ const long status = ParseElementHeader(
+ pReader,
+ pos,
+ stop,
+ id,
+ size);
+
+ if (status < 0) //error
+ return status;
+
+ if (id == 0x30) //pixel width
{
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
+ width = UnserializeUInt(pReader, pos, size);
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume length of size
- assert((pos + size) <= stop);
-
- //pos now designates start of payload
-
- pos += size; //consume payload
- assert(pos <= stop);
+ if (width <= 0)
+ return E_FILE_FORMAT_INVALID;
}
+ else if (id == 0x3A) //pixel height
+ {
+ height = UnserializeUInt(pReader, pos, size);
+
+ if (height <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x0383E3) //frame rate
+ {
+ const long status = UnserializeFloat(
+ pReader,
+ pos,
+ size,
+ rate);
+
+ if (status < 0)
+ return status;
+
+ if (rate <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
}
- return;
+ assert(pos == stop);
+
+ pTrack = new (std::nothrow) VideoTrack(pSegment, elem_st, elem_sz);
+
+ if (pTrack == NULL)
+ return -1; //generic error
+
+ const int status = i.Copy(pTrack->m_info);
+
+ if (status)
+ return status;
+
+ pTrack->m_width = width;
+ pTrack->m_height = height;
+ pTrack->m_rate = rate;
+
+ return 0; //success
}
@@ -5297,12 +5326,16 @@
assert(pCluster);
assert(pCluster->GetTime() <= time_ns);
+#if 0
//TODO:
//We need to handle the case when a cluster
//contains multiple keyframes. Simply returning
//the largest keyframe on the cluster isn't
//good enough.
pResult = pCluster->GetMaxKey(this);
+#else
+ pResult = pCluster->GetEntry(this, time_ns);
+#endif
if ((pResult != 0) && !pResult->EOS())
return 0;
@@ -5336,16 +5369,25 @@
AudioTrack::AudioTrack(
Segment* pSegment,
- const Info& i,
long long element_start,
long long element_size) :
- Track(pSegment, i, element_start, element_size),
- m_rate(0.0),
- m_channels(0),
- m_bitDepth(-1)
+ Track(pSegment, element_start, element_size)
{
- assert(i.type == 2);
- assert(i.number > 0);
+}
+
+
+long AudioTrack::Parse(
+ Segment* pSegment,
+ const Info& i,
+ long long elem_st,
+ long long elem_sz,
+ AudioTrack*& pTrack)
+{
+ if (pTrack)
+ return -1;
+
+ if (i.type != Track::kAudio)
+ return -1;
IMkvReader* const pReader = pSegment->m_pReader;
@@ -5358,47 +5400,70 @@
const long long stop = pos + s.size;
+ double rate = 8000.0;
+ long long channels = 1;
+ long long bit_depth = 0;
+
while (pos < stop)
{
-#ifdef _DEBUG
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-#endif
- if (Match(pReader, pos, 0x35, m_rate))
- ;
- else if (Match(pReader, pos, 0x1F, m_channels))
- ;
- else if (Match(pReader, pos, 0x2264, m_bitDepth))
- ;
- else
+ long long id, size;
+
+ long status = ParseElementHeader(
+ pReader,
+ pos,
+ stop,
+ id,
+ size);
+
+ if (status < 0) //error
+ return status;
+
+ if (id == 0x35) //Sample Rate
{
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
+ status = UnserializeFloat(pReader, pos, size, rate);
- pos += len; //consume id
+ if (status < 0)
+ return status;
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume length of size
- assert((pos + size) <= stop);
-
- //pos now designates start of payload
-
- pos += size; //consume payload
- assert(pos <= stop);
+ if (rate <= 0)
+ return E_FILE_FORMAT_INVALID;
}
+ else if (id == 0x1F) //Channel Count
+ {
+ channels = UnserializeUInt(pReader, pos, size);
+
+ if (channels <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x2264) //Bit Depth
+ {
+ bit_depth = UnserializeUInt(pReader, pos, size);
+
+ if (bit_depth <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
}
- if (m_channels <= 0)
- m_channels = 1; //Matroska spec says this is the default
+ assert(pos == stop);
- return;
+ pTrack = new (std::nothrow) AudioTrack(pSegment, elem_st, elem_sz);
+
+ if (pTrack == NULL)
+ return -1; //generic error
+
+ const int status = i.Copy(pTrack->m_info);
+
+ if (status)
+ return status;
+
+ pTrack->m_rate = rate;
+ pTrack->m_channels = channels;
+ pTrack->m_bitDepth = bit_depth;
+
+ return 0; //success
}
@@ -5524,81 +5589,113 @@
m_pSegment(pSegment),
m_start(start),
m_size(size_),
- m_trackEntries(NULL),
- m_trackEntriesEnd(NULL),
m_element_start(element_start),
- m_element_size(element_size)
+ m_element_size(element_size),
+ m_trackEntries(NULL),
+ m_trackEntriesEnd(NULL)
{
- long long stop = m_start + m_size;
+}
+
+
+long Tracks::Parse()
+{
+ assert(m_trackEntries == NULL);
+ assert(m_trackEntriesEnd == NULL);
+
+ const long long stop = m_start + m_size;
IMkvReader* const pReader = m_pSegment->m_pReader;
- long long pos1 = m_start;
int count = 0;
-
- while (pos1 < stop)
- {
- long len;
- const long long id = ReadUInt(pReader, pos1, len);
- assert(id >= 0);
- assert((pos1 + len) <= stop);
-
- pos1 += len; //consume id
-
- const long long size = ReadUInt(pReader, pos1, len);
- assert(size >= 0);
- assert((pos1 + len) <= stop);
-
- pos1 += len; //consume length of size
-
- //pos now desinates start of element
- if (id == 0x2E) //TrackEntry ID
- ++count;
-
- pos1 += size; //consume payload
- assert(pos1 <= stop);
- }
-
- if (count <= 0)
- return;
-
- m_trackEntries = new Track*[count];
- m_trackEntriesEnd = m_trackEntries;
-
long long pos = m_start;
while (pos < stop)
{
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert((pos + len) <= stop);
+ long long id, size;
+ const long status = ParseElementHeader(
+ pReader,
+ pos,
+ stop,
+ id,
+ size);
+
+ if (status < 0) //error
+ return status;
+
+ if (size == 0) //weird
+ continue;
+
+ if (id == 0x2E) //TrackEntry ID
+ ++count;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+
+ if (count <= 0)
+ return 0; //success
+
+ m_trackEntries = new (std::nothrow) Track*[count];
+
+ if (m_trackEntries == NULL)
+ return -1;
+
+ m_trackEntriesEnd = m_trackEntries;
+
+ pos = m_start;
+
+ while (pos < stop)
+ {
const long long element_start = pos;
- pos += len; //consume id
+ long long id, payload_size;
- const long long size1 = ReadUInt(pReader, pos, len);
- assert(size1 >= 0);
- assert((pos + len) <= stop);
+ const long status = ParseElementHeader(
+ pReader,
+ pos,
+ stop,
+ id,
+ payload_size);
- pos += len; //consume length of size
+ if (status < 0) //error
+ return status;
- //pos now desinates start of element
+ if (payload_size == 0) //weird
+ continue;
- const long long element_size = size1 + pos - element_start;
+ const long long payload_stop = pos + payload_size;
+ assert(payload_stop <= stop); //checked in ParseElement
+
+ const long long element_size = payload_stop - element_start;
if (id == 0x2E) //TrackEntry ID
{
Track*& pTrack = *m_trackEntriesEnd;
- ParseTrackEntry(pos, size1, pTrack, element_start, element_size);
+ pTrack = NULL;
+
+ const long status = ParseTrackEntry(
+ pos,
+ payload_size,
+ element_start,
+ element_size,
+ pTrack);
+
+ if (status)
+ return status;
if (pTrack)
++m_trackEntriesEnd;
}
- pos += size1; //consume payload
+ pos = payload_stop;
assert(pos <= stop);
}
+
+ assert(pos == stop);
+
+ return 0; //success
}
@@ -5610,164 +5707,264 @@
return static_cast<unsigned long>(result);
}
-
-void Tracks::ParseTrackEntry(
- long long start,
- long long size,
- Track*& pTrack,
- long long element_start,
- long long element_size)
+long Tracks::ParseTrackEntry(
+ long long track_start,
+ long long track_size,
+ long long elem_st,
+ long long elem_sz,
+ Track*& pTrack) const
{
+ if (pTrack)
+ return -1;
+
IMkvReader* const pReader = m_pSegment->m_pReader;
- long long pos = start;
- const long long stop = start + size;
+ long long pos = track_start;
+ const long long track_stop = track_start + track_size;
Track::Info i;
- Track::Settings videoSettings;
- videoSettings.start = -1;
+ i.type = 0;
+ i.number = 0;
+ i.uid = 0;
- Track::Settings audioSettings;
- audioSettings.start = -1;
+ Track::Settings v;
+ v.start = -1;
+ v.size = -1;
+
+ Track::Settings a;
+ a.start = -1;
+ a.size = -1;
+
+ Track::Settings e; //content_encodings_settings;
+ e.start = -1;
+ e.size = -1;
long long lacing = 1; //default is true
- while (pos < stop)
+ while (pos < track_stop)
{
-#ifdef _DEBUG
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- len;
- id;
-#endif
- if (Match(pReader, pos, 0x57, i.number))
- assert(i.number > 0);
- //else if (Match(pReader, pos, 0x33C5, i.uid))
- // ;
- else if (Match(pReader, pos, 0x03, i.type))
- ;
- else if (Match(pReader, pos, 0x136E, i.nameAsUTF8))
- assert(i.nameAsUTF8);
- else if (Match(pReader, pos, 0x06, i.codecId))
- ;
- else if (Match(pReader, pos, 0x1C, lacing))
- assert(lacing <= 1);
- else if (Match(pReader,
- pos,
- 0x23A2,
- i.codecPrivate,
- i.codecPrivateSize))
- ;
- else if (Match(pReader, pos, 0x058688, i.codecNameAsUTF8))
- assert(i.codecNameAsUTF8);
- else
+ long long id, size;
+
+ const long status = ParseElementHeader(
+ pReader,
+ pos,
+ track_stop,
+ id,
+ size);
+
+ if (status < 0) //error
+ return status;
+
+ const long long start = pos;
+
+ if (id == 0x60) // VideoSettings ID
{
- long len;
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
- const long long idpos = pos;
- idpos;
+ v.start = start;
+ v.size = size;
+ }
+ else if (id == 0x61) // AudioSettings ID
+ {
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
+ a.start = start;
+ a.size = size;
+ }
+ else if (id == 0x2D80) // ContentEncodings ID
+ {
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
- pos += len; //consume id
+ e.start = start;
+ e.size = size;
+ }
+ else if (id == 0x33C5) //Track UID
+ {
+ if ((size <= 0) || (size > 8))
+ return E_FILE_FORMAT_INVALID;
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
+ i.uid = 0;
- pos += len; //consume length of size
- const long long start = pos;
+ long long pos_ = start;
+ const long long pos_end = start + size;
- pos += size; //consume payload
- assert(pos <= stop);
-
- if (id == 0x60)
+ while (pos_ != pos_end)
{
- videoSettings.start = start;
- videoSettings.size = size;
- }
- else if (id == 0x61)
- {
- audioSettings.start = start;
- audioSettings.size = size;
- }
- else if (id == 0x33C5) //Track UID
- {
- assert(size <= 8);
+ unsigned char b;
- i.uid = 0;
- long long pos_ = start;
- const long long pos_end = start + size;
+ const int status = pReader->Read(pos_, 1, &b);
- while (pos_ != pos_end)
- {
- unsigned char b;
+ if (status)
+ return status;
- const long status = pReader->Read(pos_, 1, &b);
- assert(status == 0);
+ i.uid <<= 8;
+ i.uid |= b;
- i.uid <<= 8;
- i.uid |= b;
-
- ++pos_;
- }
+ ++pos_;
}
}
+ else if (id == 0x57) //Track Number
+ {
+ const long long num = UnserializeUInt(pReader, pos, size);
+
+ if ((num <= 0) || (num > 127))
+ return E_FILE_FORMAT_INVALID;
+
+ i.number = static_cast<long>(num);
+ }
+ else if (id == 0x03) //Track Type
+ {
+ const long long type = UnserializeUInt(pReader, pos, size);
+
+ if ((type <= 0) || (type > 254))
+ return E_FILE_FORMAT_INVALID;
+
+ i.type = static_cast<long>(type);
+ }
+ else if (id == 0x136E) //Track Name
+ {
+ const long status = UnserializeString(
+ pReader,
+ pos,
+ size,
+ i.nameAsUTF8);
+
+ if (status)
+ return status;
+ }
+ else if (id == 0x06) //CodecID
+ {
+ const long status = UnserializeString(
+ pReader,
+ pos,
+ size,
+ i.codecId);
+
+ if (status)
+ return status;
+ }
+ else if (id == 0x1C) //lacing
+ {
+ lacing = UnserializeUInt(pReader, pos, size);
+
+ if ((lacing < 0) || (lacing > 1))
+ return E_FILE_FORMAT_INVALID;
+ }
+ else if (id == 0x23A2) //Codec Private
+ {
+ delete[] i.codecPrivate;
+ i.codecPrivate = NULL;
+ i.codecPrivateSize = 0;
+
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ const size_t buflen = static_cast<size_t>(size);
+
+ typedef unsigned char* buf_t;
+
+ const buf_t buf = new (std::nothrow) unsigned char[buflen];
+
+ if (buf == NULL)
+ return -1;
+
+ const int status = pReader->Read(pos, buflen, buf);
+
+ if (status)
+ {
+ delete[] buf;
+ return status;
+ }
+
+ i.codecPrivate = buf;
+ i.codecPrivateSize = buflen;
+ }
+ else if (id == 0x058688) //Codec Name
+ {
+ const long status = UnserializeString(
+ pReader,
+ pos,
+ size,
+ i.codecNameAsUTF8);
+
+ if (status)
+ return status;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= track_stop);
}
- assert(pos == stop);
- //TODO: propertly vet info.number, to ensure both its existence,
- //and that it is unique among all tracks.
- assert(i.number > 0);
+ assert(pos == track_stop);
+
+ if (i.number <= 0) //not specified
+ return E_FILE_FORMAT_INVALID;
+
+ if (GetTrackByNumber(i.number))
+ return E_FILE_FORMAT_INVALID;
+
+ if (i.type <= 0) //not specified
+ return E_FILE_FORMAT_INVALID;
+
+ if ((i.type != Track::kVideo) && (i.type != Track::kAudio))
+ {
+ //TODO(matthewjheaney): go ahead and create a "generic" track
+ //object, so that GetTrackByXXX always returns something, even
+ //if the object it returns has a type that is not kVideo or kAudio.
+
+ return 0; //no error
+ }
i.lacing = (lacing > 0) ? true : false;
- //TODO: vet settings, to ensure that video settings (0x60)
- //were specified when type = 1, and that audio settings (0x61)
- //were specified when type = 2.
- if (i.type == 1) //video
+ long status;
+
+ if (i.type == Track::kVideo)
{
- assert(audioSettings.start < 0);
- assert(videoSettings.start >= 0);
+ if (v.start < 0)
+ return E_FILE_FORMAT_INVALID;
- i.settings = videoSettings;
+ if (a.start >= 0)
+ return E_FILE_FORMAT_INVALID;
- VideoTrack* const t = new VideoTrack(
- m_pSegment,
- i,
- element_start,
- element_size);
- assert(t); //TODO
- pTrack = t;
- }
- else if (i.type == 2) //audio
- {
- assert(videoSettings.start < 0);
- assert(audioSettings.start >= 0);
+ i.settings = v;
- i.settings = audioSettings;
+ VideoTrack* p = NULL;
- AudioTrack* const t = new AudioTrack(
- m_pSegment,
- i,
- element_start,
- element_size);
- assert(t); //TODO
- pTrack = t;
+ status = VideoTrack::Parse(m_pSegment, i, elem_st, elem_sz, p);
+ pTrack = p;
}
else
{
- // for now we do not support other track types yet.
- // TODO: support other track types
- i.Clear();
+ assert(i.type == Track::kAudio);
- pTrack = NULL;
+ if (a.start < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (v.start >= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ i.settings = a;
+
+ AudioTrack* p = NULL;
+
+ status = AudioTrack::Parse(m_pSegment, i, elem_st, elem_sz, p);
+ pTrack = p;
}
- return;
+ if (status)
+ return status;
+
+ assert(pTrack);
+
+ if (e.start >= 0)
+ pTrack->ParseContentEncodingsEntry(e.start, e.size);
+
+ return 0; //success
}
@@ -5785,9 +5982,10 @@
delete[] m_trackEntries;
}
-const Track* Tracks::GetTrackByNumber(unsigned long tn_) const
+const Track* Tracks::GetTrackByNumber(long tn) const
{
- const long long tn = tn_;
+ if (tn < 0)
+ return NULL;
Track** i = m_trackEntries;
Track** const j = m_trackEntriesEnd;
@@ -5837,82 +6035,6 @@
#endif
-void Cluster::Load() const
-{
- assert(m_pSegment);
- assert(m_pos >= m_element_start);
- //assert(m_size);
-
- if (m_timecode >= 0) //loaded
- return;
-
- assert(m_pos == m_element_start);
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long len;
-
- const long long id_ = ReadUInt(pReader, m_pos, len);
- assert(id_ >= 0);
- assert(id_ == 0x0F43B675); //Cluster ID
-
- m_pos += len; //consume id
-
- const long long cluster_size = ReadUInt(pReader, m_pos, len);
- assert(cluster_size >= 0); //TODO
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
- unknown_size;
- assert(cluster_size != unknown_size); //TODO
-
- m_pos += len; //consume size field
-
- const long long stop = m_pos + cluster_size;
-
- const long long element_size = stop - m_element_start;
- assert((m_element_size <= 0) || (m_element_size == element_size));
-
- if (m_element_size <= 0)
- m_element_size = element_size;
-
- long long timecode = -1;
-
- while (m_pos < stop)
- {
- if (Match(pReader, m_pos, 0x67, timecode))
- break;
- else
- {
- const long long id = ReadUInt(pReader, m_pos, len);
- assert(id >= 0); //TODO
- assert((m_pos + len) <= stop);
-
- m_pos += len; //consume id
-
- const long long size = ReadUInt(pReader, m_pos, len);
- assert(size >= 0); //TODO
- assert((m_pos + len) <= stop);
-
- m_pos += len; //consume size
-
- if (id == 0x20) //BlockGroup ID
- break;
-
- if (id == 0x23) //SimpleBlock ID
- break;
-
- m_pos += size; //consume payload
- assert(m_pos <= stop);
- }
- }
-
- assert(m_pos <= stop);
- assert(timecode >= 0);
-
- m_timecode = timecode;
-}
-
-
long Cluster::Load(long long& pos, long& len) const
{
assert(m_pSegment);
@@ -6316,11 +6438,13 @@
return E_BUFFER_NOT_FULL;
}
+ Cluster* const this_ = const_cast<Cluster*>(this);
+
if (id == 0x20) //BlockGroup
- return ParseBlockGroup(size, pos, len);
+ return this_->ParseBlockGroup(size, pos, len);
if (id == 0x23) //SimpleBlock
- return ParseSimpleBlock(size, pos, len);
+ return this_->ParseSimpleBlock(size, pos, len);
pos += size; //consume payload
assert((cluster_stop < 0) || (pos <= cluster_stop));
@@ -6362,7 +6486,7 @@
long Cluster::ParseSimpleBlock(
long long block_size,
long long& pos,
- long& len) const
+ long& len)
{
const long long block_start = pos;
const long long block_stop = pos + block_size;
@@ -6408,6 +6532,25 @@
if (track == 0)
return E_FILE_FORMAT_INVALID;
+#if 0
+ //TODO(matthewjheaney)
+ //This turned out to be too conservative. The problem is that
+ //if we see a track header in the tracks element with an unsupported
+ //track type, we throw that track header away, so it is not present
+ //in the track map. But even though we don't understand the track
+ //header, there are still blocks in the cluster with that track
+ //number. It was our decision to ignore that track header, so it's
+ //up to us to deal with blocks associated with that track -- we
+ //cannot simply report an error since technically there's nothing
+ //wrong with the file.
+ //
+ //For now we go ahead and finish the parse, creating a block entry
+ //for this block. This is somewhat wasteful, because without a
+ //track header there's nothing you can do with the block. What
+ //we really need here is a special return value that indicates to
+ //the caller that he should ignore this particular block, and
+ //continue parsing.
+
const Tracks* const pTracks = m_pSegment->GetTracks();
assert(pTracks);
@@ -6417,6 +6560,7 @@
if (pTrack == NULL)
return E_FILE_FORMAT_INVALID;
+#endif
pos += len; //consume track number
@@ -6464,7 +6608,11 @@
return E_BUFFER_NOT_FULL;
}
- CreateBlock(0x23, block_start, block_size); //simple block id
+ status = CreateBlock(0x23, block_start, block_size); //simple block id
+
+ if (status != 0)
+ return status;
+
m_pos = block_stop;
return 0; //success
@@ -6474,7 +6622,7 @@
long Cluster::ParseBlockGroup(
long long payload_size,
long long& pos,
- long& len) const
+ long& len)
{
const long long payload_start = pos;
const long long payload_stop = pos + payload_size;
@@ -6620,10 +6768,28 @@
if (track == 0)
return E_FILE_FORMAT_INVALID;
+#if 0
+ //TODO(matthewjheaney)
+ //This turned out to be too conservative. The problem is that
+ //if we see a track header in the tracks element with an unsupported
+ //track type, we throw that track header away, so it is not present
+ //in the track map. But even though we don't understand the track
+ //header, there are still blocks in the cluster with that track
+ //number. It was our decision to ignore that track header, so it's
+ //up to us to deal with blocks associated with that track -- we
+ //cannot simply report an error since technically there's nothing
+ //wrong with the file.
+ //
+ //For now we go ahead and finish the parse, creating a block entry
+ //for this block. This is somewhat wasteful, because without a
+ //track header there's nothing you can do with the block. What
+ //we really need here is a special return value that indicates to
+ //the caller that he should ignore this particular block, and
+ //continue parsing.
+
const Tracks* const pTracks = m_pSegment->GetTracks();
assert(pTracks);
-#if 0
const long tn = static_cast<long>(track);
const Track* const pTrack = pTracks->GetTrackByNumber(tn);
@@ -6684,7 +6850,11 @@
assert(pos == payload_stop);
- CreateBlock(0x20, payload_start, payload_size); //BlockGroup ID
+ status = CreateBlock(0x20, payload_start, payload_size); //BlockGroup ID
+
+ if (status != 0)
+ return status;
+
m_pos = payload_stop;
return 0; //success
@@ -6695,7 +6865,7 @@
{
assert(m_pos >= m_element_start);
- pEntry = 0;
+ pEntry = NULL;
if (index < 0)
return -1; //generic error
@@ -6750,10 +6920,9 @@
Cluster::Cluster() :
m_pSegment(NULL),
+ m_element_start(0),
m_index(0),
m_pos(0),
- //m_size(0),
- m_element_start(0),
m_element_size(0),
m_timecode(0),
m_entries(NULL),
@@ -6769,11 +6938,10 @@
long long element_start
/* long long element_size */ ) :
m_pSegment(pSegment),
+ m_element_start(element_start),
m_index(idx),
m_pos(element_start),
- m_element_start(element_start),
m_element_size(-1 /* element_size */ ),
- //m_size(-1),
m_timecode(-1),
m_entries(NULL),
m_entries_size(0),
@@ -6846,7 +7014,7 @@
long len;
const long long id = ReadUInt(pReader, pos, len);
- id;
+ (void)id;
assert(id >= 0);
assert(id == 0x0F43B675); //Cluster ID
@@ -7112,202 +7280,16 @@
}
-void Cluster::LoadBlockEntries() const
-{
- //LoadBlockEntries loads all of the entries on the cluster.
-
- //if (m_entries)
- // return;
-
- //if (m_entries_count == 0) //already parsed, and no entries found
- // return;
-
- if (m_pSegment == 0) //EOS cluster
- return;
-
- assert(m_pos >= m_element_start);
- //assert(m_size); //preloaded only, or (partially) loaded
- //assert(m_entries_count < 0);
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- //if (m_pos < 0)
- // m_pos *= -1; //relative to segment
- //long long pos = m_pSegment->m_start + m_pos; //absolute
-
- if (m_element_size < 0)
- {
- assert(m_pos == m_element_start);
-
- long len;
-
- const long long id = ReadUInt(pReader, m_pos, len);
- id;
- assert(id >= 0);
- assert(id == 0x0F43B675); //Cluster ID
-
- m_pos += len; //consume id
-
- const long long cluster_size = ReadUInt(pReader, m_pos, len);
- assert(cluster_size > 0);
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
- unknown_size;
- assert(cluster_size != unknown_size);
-
- m_pos += len; //consume size field
-
- //m_pos now points to start of cluster payload
-
- const long long cluster_stop = m_pos + cluster_size;
- const long long element_size = cluster_stop - m_element_start;
- assert((m_element_size <= 0) || (m_element_size == element_size));
-
- if (m_element_size <= 0)
- m_element_size = element_size;
- }
-
- //assert(m_size > 0);
- //assert(m_element_size > m_size);
-
- const long long cluster_stop = m_element_start + m_element_size;
-
- if (m_pos >= cluster_stop)
- return;
-
- long long timecode = -1; //of cluster itself
-
- //First count the number of entries (that remain)
-
- long long pos = m_pos;
- int entries_count = 0; //that remain
-
- while (pos < cluster_stop)
- {
- if (Match(pReader, pos, 0x67, timecode))
- {
- if (m_timecode >= 0)
- assert(timecode == m_timecode);
- else
- m_timecode = timecode;
- }
- else
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= cluster_stop);
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO
- assert((pos + len) <= cluster_stop);
-
- pos += len; //consume size
-
- if (id == 0x20) //BlockGroup ID
- ++entries_count;
- else if (id == 0x23) //SimpleBlock ID
- ++entries_count;
-
- pos += size; //consume payload
- assert(pos <= cluster_stop);
- }
- }
-
- assert(pos == cluster_stop);
- assert(m_timecode >= 0);
-
- if (entries_count == 0) //nothing remains to be done
- {
- m_pos = pos;
-
- if (m_entries_count < 0)
- m_entries_count = 0;
-
- return;
- }
-
- BlockEntry** ppEntry;
-
- if (m_entries_count < 0) //haven't parsed anything yet
- {
- assert(m_entries == NULL);
- assert(m_entries_size == 0);
-
- m_entries_size = entries_count;
- m_entries = new BlockEntry*[m_entries_size];
-
- ppEntry = m_entries;
- m_entries_count = entries_count;
- }
- else
- {
- assert(m_entries);
- assert(m_entries_size > 0);
- assert(m_entries_count > 0);
- assert(m_entries_count <= m_entries_size);
-
- const long entries_size = m_entries_count + entries_count;
-
- if (m_entries_size < entries_size)
- {
- BlockEntry** const entries = new BlockEntry*[entries_size];
- assert(entries);
-
- BlockEntry** src = m_entries;
- BlockEntry** const src_end = src + m_entries_count;
-
- BlockEntry** dst = entries;
-
- while (src != src_end)
- *dst++ = *src++;
-
- delete[] m_entries;
-
- m_entries = entries;
- m_entries_size = entries_size;
- }
-
- ppEntry = m_entries + m_entries_count;
- m_entries_count = entries_size;
- }
-
- while (m_pos < cluster_stop)
- {
- long len;
- const long long id = ReadUInt(pReader, m_pos, len);
- assert(id >= 0); //TODO
- assert((m_pos + len) <= cluster_stop);
-
- m_pos += len; //consume id
-
- const long long size = ReadUInt(pReader, m_pos, len);
- assert(size >= 0); //TODO
- assert((m_pos + len) <= cluster_stop);
-
- m_pos += len; //consume size
-
- if (id == 0x20) //BlockGroup ID
- CreateBlockGroup(m_pos, size, ppEntry);
- else if (id == 0x23) //SimpleBlock ID
- CreateSimpleBlock(m_pos, size, ppEntry);
-
- m_pos += size; //consume payload
- assert(m_pos <= cluster_stop);
- }
-
- assert(m_pos == cluster_stop);
- assert((ppEntry - m_entries) == m_entries_count);
-}
-
-
-
long long Cluster::GetTimeCode() const
{
- Load();
+ long long pos;
+ long len;
+
+ const long status = Load(pos, len);
+
+ if (status < 0) //error
+ return status;
+
return m_timecode;
}
@@ -7315,7 +7297,9 @@
long long Cluster::GetTime() const
{
const long long tc = GetTimeCode();
- assert(tc >= 0);
+
+ if (tc < 0)
+ return tc;
const SegmentInfo* const pInfo = m_pSegment->GetInfo();
assert(pInfo);
@@ -7331,7 +7315,12 @@
long long Cluster::GetFirstTime() const
{
- const BlockEntry* const pEntry = GetFirst();
+ const BlockEntry* pEntry;
+
+ const long status = GetFirst(pEntry);
+
+ if (status < 0) //error
+ return status;
if (pEntry == NULL) //empty cluster
return GetTime();
@@ -7345,7 +7334,12 @@
long long Cluster::GetLastTime() const
{
- const BlockEntry* const pEntry = GetLast();
+ const BlockEntry* pEntry;
+
+ const long status = GetLast(pEntry);
+
+ if (status < 0) //error
+ return status;
if (pEntry == NULL) //empty cluster
return GetTime();
@@ -7357,12 +7351,12 @@
}
-void Cluster::CreateBlock(
+long Cluster::CreateBlock(
long long id,
long long pos, //absolute pos of payload
- long long size) const
+ long long size)
{
- BlockEntry** ppEntry;
+ assert((id == 0x20) || (id == 0x23)); //BlockGroup or SimpleBlock
if (m_entries_count < 0) //haven't parsed anything yet
{
@@ -7372,14 +7366,12 @@
m_entries_size = 1024;
m_entries = new BlockEntry*[m_entries_size];
- ppEntry = m_entries;
- m_entries_count = 1;
+ m_entries_count = 0;
}
else
{
assert(m_entries);
assert(m_entries_size > 0);
- assert(m_entries_count > 0);
assert(m_entries_count <= m_entries_size);
if (m_entries_count >= m_entries_size)
@@ -7402,106 +7394,275 @@
m_entries = entries;
m_entries_size = entries_size;
}
-
- ppEntry = m_entries + m_entries_count;
- ++m_entries_count;
}
if (id == 0x20) //BlockGroup ID
- CreateBlockGroup(pos, size, ppEntry);
- else
+ return CreateBlockGroup(pos, size);
+ else //SimpleBlock ID
+ return CreateSimpleBlock(pos, size);
+}
+
+
+long Cluster::CreateBlockGroup(
+ long long st,
+ long long sz)
+{
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count >= 0);
+ assert(m_entries_count < m_entries_size);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = st;
+ const long long stop = st + sz;
+
+ //For WebM files, there is a bias towards previous reference times
+ //(in order to support alt-ref frames, which refer back to the previous
+ //keyframe). Normally a 0 value is not possible, but here we tenatively
+ //allow 0 as the value of a reference frame, with the interpretation
+ //that this is a "previous" reference time.
+
+ long long prev = 1; //nonce
+ long long next = 0; //nonce
+ long long duration = -1; //really, this is unsigned
+
+ long long bpos = -1;
+ long long bsize = -1;
+
+ while (pos < stop)
{
- assert(id == 0x23); //SimpleBlock ID
- CreateSimpleBlock(pos, size, ppEntry);
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ if (id == 0x21) //Block ID
+ {
+ if (bpos < 0) //Block ID
+ {
+ bpos = pos;
+ bsize = size;
+ }
+ }
+ else if (id == 0x1B) //Duration ID
+ {
+ assert(size <= 8);
+
+ duration = UnserializeUInt(pReader, pos, size);
+ assert(duration >= 0); //TODO
+ }
+ else if (id == 0x7B) //ReferenceBlock
+ {
+ assert(size <= 8);
+ const long size_ = static_cast<long>(size);
+
+ long long time;
+
+ long status = UnserializeInt(pReader, pos, size_, time);
+ assert(status == 0); //TODO
+
+ if (time <= 0) //see note above
+ prev = time;
+ else //weird
+ next = time;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
}
+
+ assert(pos == stop);
+ assert(bpos >= 0);
+ assert(bsize >= 0);
+
+ const long idx = m_entries_count;
+
+ BlockEntry** const ppEntry = m_entries + idx;
+ BlockEntry*& pEntry = *ppEntry;
+
+ pEntry = new (std::nothrow) BlockGroup(
+ this,
+ idx,
+ bpos,
+ bsize,
+ prev,
+ next,
+ duration);
+
+ if (pEntry == NULL)
+ return -1; //generic error
+
+ BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
+
+ const long status = p->Parse();
+
+ if (status == 0) //success
+ {
+ ++m_entries_count;
+ return 0;
+ }
+
+ delete pEntry;
+ pEntry = 0;
+
+ return status;
}
-void Cluster::CreateBlockGroup(
+
+long Cluster::CreateSimpleBlock(
long long st,
- long long sz,
- BlockEntry**& ppEntry) const
+ long long sz)
{
assert(m_entries);
assert(m_entries_size > 0);
- assert(ppEntry);
- assert(ppEntry >= m_entries);
+ assert(m_entries_count >= 0);
+ assert(m_entries_count < m_entries_size);
- const ptrdiff_t idx = ppEntry - m_entries;
- assert(idx >= 0);
- assert(idx < m_entries_size);
+ const long idx = m_entries_count;
- Cluster* const this_ = const_cast<Cluster*>(this);
- *ppEntry++ = new BlockGroup(this_, idx, st, sz);
+ BlockEntry** const ppEntry = m_entries + idx;
+ BlockEntry*& pEntry = *ppEntry;
+
+ pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
+
+ if (pEntry == NULL)
+ return -1; //generic error
+
+ SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
+
+ const long status = p->Parse();
+
+ if (status == 0)
+ {
+ ++m_entries_count;
+ return 0;
+ }
+
+ delete pEntry;
+ pEntry = 0;
+
+ return status;
}
-
-void Cluster::CreateSimpleBlock(
- long long st,
- long long sz,
- BlockEntry**& ppEntry) const
+long Cluster::GetFirst(const BlockEntry*& pFirst) const
{
+ if (m_entries_count <= 0)
+ {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) //error
+ {
+ pFirst = NULL;
+ return status;
+ }
+
+ if (m_entries_count <= 0) //empty cluster
+ {
+ pFirst = NULL;
+ return 0;
+ }
+ }
+
assert(m_entries);
- assert(m_entries_size > 0);
- assert(ppEntry);
- assert(ppEntry >= m_entries);
- const ptrdiff_t idx = ppEntry - m_entries;
- assert(idx >= 0);
- assert(idx < m_entries_size);
-
- Cluster* const this_ = const_cast<Cluster*>(this);
- *ppEntry++ = new SimpleBlock(this_, idx, st, sz);
-}
-
-
-const BlockEntry* Cluster::GetFirst() const
-{
- LoadBlockEntries();
-
- if ((m_entries == NULL) || (m_entries_count <= 0))
- return NULL;
-
- const BlockEntry* const pFirst = m_entries[0];
+ pFirst = m_entries[0];
assert(pFirst);
- return pFirst;
+ return 0; //success
}
-
-const BlockEntry* Cluster::GetLast() const
+long Cluster::GetLast(const BlockEntry*& pLast) const
{
- LoadBlockEntries();
+ for (;;)
+ {
+ long long pos;
+ long len;
- if ((m_entries == NULL) || (m_entries_count <= 0))
- return NULL;
+ const long status = Parse(pos, len);
+
+ if (status < 0) //error
+ {
+ pLast = NULL;
+ return status;
+ }
+
+ if (status > 0) //no new block
+ break;
+ }
+
+ if (m_entries_count <= 0)
+ {
+ pLast = NULL;
+ return 0;
+ }
+
+ assert(m_entries);
const long idx = m_entries_count - 1;
- const BlockEntry* const pLast = m_entries[idx];
+ pLast = m_entries[idx];
assert(pLast);
- return pLast;
+ return 0;
}
-const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const
+long Cluster::GetNext(
+ const BlockEntry* pCurr,
+ const BlockEntry*& pNext) const
{
- assert(pEntry);
- assert(m_entries != NULL);
+ assert(pCurr);
+ assert(m_entries);
assert(m_entries_count > 0);
- size_t idx = pEntry->GetIndex();
+ size_t idx = pCurr->GetIndex();
assert(idx < size_t(m_entries_count));
- assert(m_entries[idx] == pEntry);
+ assert(m_entries[idx] == pCurr);
++idx;
if (idx >= size_t(m_entries_count))
- return NULL;
+ {
+ long long pos;
+ long len;
- return m_entries[idx];
+ const long status = Parse(pos, len);
+
+ if (status < 0) //error
+ {
+ pNext = NULL;
+ return status;
+ }
+
+ if (status > 0)
+ {
+ pNext = NULL;
+ return 0;
+ }
+
+ assert(m_entries);
+ assert(m_entries_count > 0);
+ assert(idx < size_t(m_entries_count));
+ }
+
+ pNext = m_entries[idx];
+ assert(pNext);
+
+ return 0;
}
@@ -7520,6 +7681,8 @@
if (m_pSegment == NULL) //this is the special EOS cluster
return pTrack->GetEOS();
+#if 0
+
LoadBlockEntries();
if ((m_entries == NULL) || (m_entries_count <= 0))
@@ -7566,6 +7729,70 @@
}
return pResult;
+
+#else
+
+ const BlockEntry* pResult = pTrack->GetEOS();
+
+ long index = 0;
+
+ for (;;)
+ {
+ if (index >= m_entries_count)
+ {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+ assert(status >= 0);
+
+ if (status > 0) //completely parsed, and no more entries
+ return pResult;
+
+ if (status < 0) //should never happen
+ return 0;
+
+ assert(m_entries);
+ assert(index < m_entries_count);
+ }
+
+ const BlockEntry* const pEntry = m_entries[index];
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() != pTrack->GetNumber())
+ {
+ ++index;
+ continue;
+ }
+
+ if (pTrack->VetEntry(pEntry))
+ {
+ if (time_ns < 0) //just want first candidate block
+ return pEntry;
+
+ const long long ns = pBlock->GetTime(this);
+
+ if (ns > time_ns)
+ return pResult;
+
+ pResult = pEntry; //have a candidate
+ }
+ else if (time_ns >= 0)
+ {
+ const long long ns = pBlock->GetTime(this);
+
+ if (ns > time_ns)
+ return pResult;
+ }
+
+ ++index;
+ }
+
+#endif
}
@@ -7576,6 +7803,8 @@
{
assert(m_pSegment);
+#if 0
+
LoadBlockEntries();
if (m_entries == NULL)
@@ -7660,9 +7889,119 @@
}
return NULL;
+
+#else
+
+ const long long tc = cp.GetTimeCode();
+
+ if (tp.m_block > 0)
+ {
+ const long block = static_cast<long>(tp.m_block);
+ const long index = block - 1;
+
+ while (index >= m_entries_count)
+ {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) //TODO: can this happen?
+ return NULL;
+
+ if (status > 0) //nothing remains to be parsed
+ return NULL;
+ }
+
+ const BlockEntry* const pEntry = m_entries[index];
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if ((pBlock->GetTrackNumber() == tp.m_track) &&
+ (pBlock->GetTimeCode(this) == tc))
+ {
+ return pEntry;
+ }
+ }
+
+ long index = 0;
+
+ for (;;)
+ {
+ if (index >= m_entries_count)
+ {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) //TODO: can this happen?
+ return NULL;
+
+ if (status > 0) //nothing remains to be parsed
+ return NULL;
+
+ assert(m_entries);
+ assert(index < m_entries_count);
+ }
+
+ const BlockEntry* const pEntry = m_entries[index];
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() != tp.m_track)
+ {
+ ++index;
+ continue;
+ }
+
+ const long long tc_ = pBlock->GetTimeCode(this);
+ assert(tc_ >= 0);
+
+ if (tc_ < tc)
+ {
+ ++index;
+ continue;
+ }
+
+ if (tc_ > tc)
+ return NULL;
+
+ const Tracks* const pTracks = m_pSegment->GetTracks();
+ assert(pTracks);
+
+ const long tn = static_cast<long>(tp.m_track);
+ const Track* const pTrack = pTracks->GetTrackByNumber(tn);
+
+ if (pTrack == NULL)
+ return NULL;
+
+ const long long type = pTrack->GetType();
+
+ if (type == 2) //audio
+ return pEntry;
+
+ if (type != 1) //not video
+ return NULL;
+
+ if (!pBlock->IsKey())
+ return NULL;
+
+ return pEntry;
+ }
+
+#endif
+
}
+#if 0
const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack) const
{
assert(pTrack);
@@ -7696,10 +8035,12 @@
return pTrack->GetEOS(); //no satisfactory block found
}
+#endif
-
-BlockEntry::BlockEntry()
+BlockEntry::BlockEntry(Cluster* p, long idx) :
+ m_pCluster(p),
+ m_index(idx)
{
}
@@ -7709,130 +8050,84 @@
}
-SimpleBlock::SimpleBlock(
- Cluster* pCluster,
- size_t idx,
- long long start,
- long long size) :
- m_pCluster(pCluster),
- m_index(idx),
- m_block(start, size, pCluster->m_pSegment->m_pReader)
+bool BlockEntry::EOS() const
{
+ return (GetKind() == kBlockEOS);
}
-bool SimpleBlock::EOS() const
-{
- return false;
-}
-
-
-const Cluster* SimpleBlock::GetCluster() const
+const Cluster* BlockEntry::GetCluster() const
{
return m_pCluster;
}
-size_t SimpleBlock::GetIndex() const
+long BlockEntry::GetIndex() const
{
return m_index;
}
+SimpleBlock::SimpleBlock(
+ Cluster* pCluster,
+ long idx,
+ long long start,
+ long long size) :
+ BlockEntry(pCluster, idx),
+ m_block(start, size)
+{
+}
+
+
+long SimpleBlock::Parse()
+{
+ return m_block.Parse(m_pCluster->m_pSegment->m_pReader);
+}
+
+
+BlockEntry::Kind SimpleBlock::GetKind() const
+{
+ return kBlockSimple;
+}
+
+
const Block* SimpleBlock::GetBlock() const
{
return &m_block;
}
-//bool SimpleBlock::IsBFrame() const
-//{
-// return false;
-//}
-
-
BlockGroup::BlockGroup(
Cluster* pCluster,
- size_t idx,
- long long start,
- long long size_) :
- m_pCluster(pCluster),
- m_index(idx),
- m_prevTimeCode(0),
- m_nextTimeCode(0),
- m_pBlock(NULL) //TODO: accept multiple blocks within a block group
+ long idx,
+ long long block_start,
+ long long block_size,
+ long long prev,
+ long long next,
+ long long duration) :
+ BlockEntry(pCluster, idx),
+ m_block(block_start, block_size),
+ m_prev(prev),
+ m_next(next),
+ m_duration(duration)
{
- IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
-
- long long pos = start;
- const long long stop = start + size_;
-
- bool bSimpleBlock = false;
- bool bReferenceBlock = false;
-
- while (pos < stop)
- {
- short t;
-
- if (Match(pReader, pos, 0x7B, t))
- {
- if (t < 0)
- m_prevTimeCode = t;
- else if (t > 0)
- m_nextTimeCode = t;
- else
- assert(false);
-
- bReferenceBlock = true;
- }
- else
- {
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume size
-
- switch (id)
- {
- case 0x23: //SimpleBlock ID
- bSimpleBlock = true;
- //YES, FALL THROUGH TO NEXT CASE
-
- case 0x21: //Block ID
- ParseBlock(pos, size);
- break;
-
- default:
- break;
- }
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
- }
-
- assert(pos == stop);
- assert(m_pBlock);
-
- if (!bSimpleBlock)
- m_pBlock->SetKey(!bReferenceBlock);
}
-BlockGroup::~BlockGroup()
+long BlockGroup::Parse()
{
- delete m_pBlock;
+ const long status = m_block.Parse(m_pCluster->m_pSegment->m_pReader);
+
+ if (status)
+ return status;
+
+ m_block.SetKey((m_prev > 0) && (m_next <= 0));
+
+ return 0;
}
+#if 0
void BlockGroup::ParseBlock(long long start, long long size)
{
IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
@@ -7846,87 +8141,113 @@
assert(m_pBlock == NULL);
m_pBlock = pBlock;
}
+#endif
-bool BlockGroup::EOS() const
+BlockEntry::Kind BlockGroup::GetKind() const
{
- return false;
-}
-
-
-const Cluster* BlockGroup::GetCluster() const
-{
- return m_pCluster;
-}
-
-
-size_t BlockGroup::GetIndex() const
-{
- return m_index;
+ return kBlockGroup;
}
const Block* BlockGroup::GetBlock() const
{
- return m_pBlock;
+ return &m_block;
}
-short BlockGroup::GetPrevTimeCode() const
+long long BlockGroup::GetPrevTimeCode() const
{
- return m_prevTimeCode;
+ return m_prev;
}
-short BlockGroup::GetNextTimeCode() const
+long long BlockGroup::GetNextTimeCode() const
{
- return m_nextTimeCode;
+ return m_next;
}
-//bool BlockGroup::IsBFrame() const
-//{
-// return (m_nextTimeCode > 0);
-//}
-
-
-Block::Block(long long start, long long size_, IMkvReader* pReader) :
+Block::Block(long long start, long long size_) :
m_start(start),
- m_size(size_)
+ m_size(size_),
+ m_track(0),
+ m_timecode(-1),
+ m_flags(0),
+ m_frames(NULL),
+ m_frame_count(-1)
{
- long long pos = start;
- const long long stop = start + size_;
+}
+
+
+Block::~Block()
+{
+ delete[] m_frames;
+}
+
+
+long Block::Parse(IMkvReader* pReader)
+{
+ assert(pReader);
+ assert(m_start >= 0);
+ assert(m_size >= 0);
+ assert(m_track <= 0);
+ assert(m_frames == NULL);
+ assert(m_frame_count <= 0);
+
+ long long pos = m_start;
+ const long long stop = m_start + m_size;
long len;
m_track = ReadUInt(pReader, pos, len);
- assert(m_track > 0);
- assert((pos + len) <= stop);
+
+ if (m_track <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
pos += len; //consume track number
- assert((stop - pos) >= 2);
- m_timecode = Unserialize2SInt(pReader, pos);
+ if ((stop - pos) < 2)
+ return E_FILE_FORMAT_INVALID;
+
+ long status;
+ long long value;
+
+ status = UnserializeInt(pReader, pos, 2, value);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
+
+ if (value < SHRT_MIN)
+ return E_FILE_FORMAT_INVALID;
+
+ if (value > SHRT_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ m_timecode = static_cast<short>(value);
pos += 2;
- assert((stop - pos) >= 1);
- long status = pReader->Read(pos, 1, &m_flags);
- assert(status == 0);
+ if ((stop - pos) <= 0)
+ return E_FILE_FORMAT_INVALID;
-#if 0
- const int invisible = int(m_flags & 0x08) >> 3;
- invisible;
- assert(!invisible); //TODO
-#endif
+ status = pReader->Read(pos, 1, &m_flags);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
const int lacing = int(m_flags & 0x06) >> 1;
++pos; //consume flags byte
- assert(pos <= stop);
if (lacing == 0) //no lacing
{
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+
m_frame_count = 1;
m_frames = new Frame[m_frame_count];
@@ -7934,25 +8255,32 @@
f.pos = pos;
const long long frame_size = stop - pos;
- assert(frame_size <= LONG_MAX);
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
f.len = static_cast<long>(frame_size);
- return;
+ return 0; //success
}
- assert(pos < stop);
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
- unsigned char count;
+ unsigned char biased_count;
- status = pReader->Read(pos, 1, &count);
- assert(status == 0);
+ status = pReader->Read(pos, 1, &biased_count);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
++pos; //consume frame count
assert(pos <= stop);
- m_frame_count = ++count;
+ m_frame_count = int(biased_count) + 1;
+
m_frames = new Frame[m_frame_count];
+ assert(m_frames);
if (lacing == 1) //Xiph
{
@@ -7960,8 +8288,9 @@
Frame* const pf_end = pf + m_frame_count;
long size = 0;
+ int frame_count = m_frame_count;
- while (count > 1)
+ while (frame_count > 1)
{
long frame_size = 0;
@@ -7969,8 +8298,13 @@
{
unsigned char val;
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
status = pReader->Read(pos, 1, &val);
- assert(status == 0);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
++pos; //consume xiph size byte
@@ -7983,24 +8317,34 @@
Frame& f = *pf++;
assert(pf < pf_end);
+ f.pos = 0; //patch later
+
f.len = frame_size;
size += frame_size; //contribution of this frame
- --count;
+ --frame_count;
}
assert(pf < pf_end);
- assert(pos < stop);
+ assert(pos <= stop);
{
Frame& f = *pf++;
- assert(pf == pf_end);
+
+ if (pf != pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ f.pos = 0; //patch later
const long long total_size = stop - pos;
- assert(total_size > size);
+
+ if (total_size < size)
+ return E_FILE_FORMAT_INVALID;
const long long frame_size = total_size - size;
- assert(frame_size <= LONG_MAX);
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
f.len = static_cast<long>(frame_size);
}
@@ -8020,10 +8364,14 @@
else if (lacing == 2) //fixed-size lacing
{
const long long total_size = stop - pos;
- assert((total_size % m_frame_count) == 0);
+
+ if ((total_size % m_frame_count) != 0)
+ return E_FILE_FORMAT_INVALID;
const long long frame_size = total_size / m_frame_count;
- assert(frame_size <= LONG_MAX);
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
Frame* pf = m_frames;
Frame* const pf_end = pf + m_frame_count;
@@ -8045,17 +8393,28 @@
else
{
assert(lacing == 3); //EBML lacing
- assert(pos < stop);
+
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
long size = 0;
+ int frame_count = m_frame_count;
long long frame_size = ReadUInt(pReader, pos, len);
- assert(frame_size > 0);
- assert(frame_size <= LONG_MAX);
- assert((pos + len) <= stop);
+
+ if (frame_size < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
pos += len; //consume length of size of first frame
- assert((pos + frame_size) <= stop);
+
+ if ((pos + frame_size) > stop)
+ return E_FILE_FORMAT_INVALID;
Frame* pf = m_frames;
Frame* const pf_end = pf + m_frame_count;
@@ -8063,26 +8422,37 @@
{
Frame& curr = *pf;
+ curr.pos = 0; //patch later
+
curr.len = static_cast<long>(frame_size);
size += curr.len; //contribution of this frame
}
- --count;
+ --frame_count;
- while (count > 1)
+ while (frame_count > 1)
{
- assert(pos < stop);
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
assert(pf < pf_end);
const Frame& prev = *pf++;
- assert(pf < pf_end);
assert(prev.len == frame_size);
+ assert(pf < pf_end);
+
Frame& curr = *pf;
+ curr.pos = 0; //patch later
+
const long long delta_size_ = ReadUInt(pReader, pos, len);
- assert(delta_size_ >= 0);
- assert((pos + len) <= stop);
+
+ if (delta_size_ < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
pos += len; //consume length of (delta) size
assert(pos <= stop);
@@ -8092,33 +8462,42 @@
const long long delta_size = delta_size_ - bias;
frame_size += delta_size;
- assert(frame_size > 0);
- assert(frame_size <= LONG_MAX);
+
+ if (frame_size < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
curr.len = static_cast<long>(frame_size);
size += curr.len; //contribution of this frame
- --count;
+ --frame_count;
}
{
- assert(pos < stop);
+ assert(pos <= stop);
assert(pf < pf_end);
const Frame& prev = *pf++;
- assert(pf < pf_end);
assert(prev.len == frame_size);
+ assert(pf < pf_end);
+
Frame& curr = *pf++;
assert(pf == pf_end);
+ curr.pos = 0; //patch later
+
const long long total_size = stop - pos;
- assert(total_size > 0);
- assert(total_size > size);
+
+ if (total_size < size)
+ return E_FILE_FORMAT_INVALID;
frame_size = total_size - size;
- assert(frame_size > 0);
- assert(frame_size <= LONG_MAX);
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
curr.len = static_cast<long>(frame_size);
}
@@ -8135,23 +8514,20 @@
assert(pos == stop);
}
-}
-
-Block::~Block()
-{
- delete[] m_frames;
+ return 0; //success
}
long long Block::GetTimeCode(const Cluster* pCluster) const
{
- assert(pCluster);
+ if (pCluster == 0)
+ return m_timecode;
const long long tc0 = pCluster->GetTimeCode();
assert(tc0 >= 0);
- const long long tc = tc0 + static_cast<long long>(m_timecode);
+ const long long tc = tc0 + m_timecode;
assert(tc >= 0);
return tc; //unscaled timecode units
@@ -8204,6 +8580,13 @@
}
+Block::Lacing Block::GetLacing() const
+{
+ const int value = int(m_flags & 0x06) >> 1;
+ return static_cast<Lacing>(value);
+}
+
+
int Block::GetFrameCount() const
{
return m_frame_count;
diff --git a/mkvparser/mkvparser.hpp b/mkvparser/mkvparser.hpp
index f769972..7e4abec 100644
--- a/mkvparser/mkvparser.hpp
+++ b/mkvparser/mkvparser.hpp
@@ -11,6 +11,7 @@
#include <cstdlib>
#include <cstdio>
+#include <cstddef>
namespace mkvparser
{
@@ -29,17 +30,26 @@
long long GetUIntLength(IMkvReader*, long long, long&);
long long ReadUInt(IMkvReader*, long long, long&);
-long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&);
long long UnserializeUInt(IMkvReader*, long long pos, long long size);
-float Unserialize4Float(IMkvReader*, long long);
-double Unserialize8Double(IMkvReader*, long long);
-short Unserialize2SInt(IMkvReader*, long long);
-signed char Unserialize1SInt(IMkvReader*, long long);
+
+long UnserializeFloat(IMkvReader*, long long pos, long long size, double&);
+long UnserializeInt(IMkvReader*, long long pos, long len, long long& result);
+
+long UnserializeString(
+ IMkvReader*,
+ long long pos,
+ long long size,
+ char*& str);
+
+long ParseElementHeader(
+ IMkvReader* pReader,
+ long long& pos, //consume id and size fields
+ long long stop, //if you know size of element's parent
+ long long& id,
+ long long& size);
+
bool Match(IMkvReader*, long long&, unsigned long, long long&);
-bool Match(IMkvReader*, long long&, unsigned long, char*&);
bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&);
-bool Match(IMkvReader*, long long&, unsigned long, double&);
-bool Match(IMkvReader*, long long&, unsigned long, short&);
void GetVersion(int& major, int& minor, int& build, int& revision);
@@ -73,9 +83,11 @@
const long long m_start;
const long long m_size;
- Block(long long start, long long size, IMkvReader*);
+ Block(long long start, long long size);
~Block();
+ long Parse(IMkvReader*);
+
long long GetTrackNumber() const;
long long GetTimeCode(const Cluster*) const; //absolute, but not scaled
long long GetTime(const Cluster*) const; //absolute, and scaled (ns)
@@ -83,6 +95,9 @@
void SetKey(bool);
bool IsInvisible() const;
+ enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml };
+ Lacing GetLacing() const;
+
int GetFrameCount() const; //to index frames: [0, count)
struct Frame
@@ -111,16 +126,23 @@
BlockEntry(const BlockEntry&);
BlockEntry& operator=(const BlockEntry&);
+protected:
+ BlockEntry(Cluster*, long index);
+
public:
virtual ~BlockEntry();
- virtual bool EOS() const = 0;
- virtual const Cluster* GetCluster() const = 0;
- virtual size_t GetIndex() const = 0;
+
+ bool EOS() const;
+ const Cluster* GetCluster() const;
+ long GetIndex() const;
virtual const Block* GetBlock() const = 0;
- //virtual bool IsBFrame() const = 0;
+
+ enum Kind { kBlockEOS, kBlockSimple, kBlockGroup };
+ virtual Kind GetKind() const = 0;
protected:
- BlockEntry();
+ Cluster* const m_pCluster;
+ const long m_index;
};
@@ -131,17 +153,13 @@
SimpleBlock& operator=(const SimpleBlock&);
public:
- SimpleBlock(Cluster*, size_t, long long start, long long size);
+ SimpleBlock(Cluster*, long index, long long start, long long size);
+ long Parse();
- bool EOS() const;
- const Cluster* GetCluster() const;
- size_t GetIndex() const;
+ Kind GetKind() const;
const Block* GetBlock() const;
- //bool IsBFrame() const;
protected:
- Cluster* const m_pCluster;
- const size_t m_index;
Block m_block;
};
@@ -153,41 +171,120 @@
BlockGroup& operator=(const BlockGroup&);
public:
- BlockGroup(Cluster*, size_t, long long, long long);
- ~BlockGroup();
+ BlockGroup(
+ Cluster*,
+ long index,
+ long long block_start, //absolute pos of block's payload
+ long long block_size, //size of block's payload
+ long long prev,
+ long long next,
+ long long duration);
- bool EOS() const;
- const Cluster* GetCluster() const;
- size_t GetIndex() const;
+ long Parse();
+
+ Kind GetKind() const;
const Block* GetBlock() const;
- //bool IsBFrame() const;
- short GetPrevTimeCode() const; //relative to block's time
- short GetNextTimeCode() const; //as above
-
-protected:
- Cluster* const m_pCluster;
- const size_t m_index;
+ long long GetPrevTimeCode() const; //relative to block's time
+ long long GetNextTimeCode() const; //as above
+ long long GetDuration() const;
private:
- BlockGroup(Cluster*, size_t, unsigned long);
- void ParseBlock(long long start, long long size);
-
- short m_prevTimeCode;
- short m_nextTimeCode;
-
- //TODO: the Matroska spec says you can have multiple blocks within the
- //same block group, with blocks ranked by priority (the flag bits).
- //For now we just cache a single block.
-#if 0
- typedef std::deque<Block*> blocks_t;
- blocks_t m_blocks; //In practice should contain only a single element.
-#else
- Block* m_pBlock;
-#endif
+ Block m_block;
+ const long long m_prev;
+ const long long m_next;
+ const long long m_duration;
};
+///////////////////////////////////////////////////////////////
+// ContentEncoding element
+// Elements used to describe if the track data has been encrypted or
+// compressed with zlib or header stripping.
+class ContentEncoding {
+public:
+ ContentEncoding();
+ ~ContentEncoding();
+
+ // ContentCompression element names
+ struct ContentCompression {
+ ContentCompression();
+ ~ContentCompression();
+
+ unsigned long long algo;
+ unsigned char* settings;
+ };
+
+ // ContentEncryption element names
+ struct ContentEncryption {
+ ContentEncryption();
+ ~ContentEncryption();
+
+ unsigned long long algo;
+ unsigned char* key_id;
+ long long key_id_len;
+ unsigned char* signature;
+ long long signature_len;
+ unsigned char* sig_key_id;
+ long long sig_key_id_len;
+ unsigned long long sig_algo;
+ unsigned long long sig_hash_algo;
+ };
+
+ // Returns ContentCompression represented by |idx|. Returns NULL if |idx|
+ // is out of bounds.
+ const ContentCompression* GetCompressionByIndex(unsigned long idx) const;
+
+ // Returns number of ContentCompression elements in this ContentEncoding
+ // element.
+ unsigned long GetCompressionCount() const;
+
+ // Returns ContentEncryption represented by |idx|. Returns NULL if |idx|
+ // is out of bounds.
+ const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const;
+
+ // Returns number of ContentEncryption elements in this ContentEncoding
+ // element.
+ unsigned long GetEncryptionCount() const;
+
+ // Parses the ContentEncoding element from |pReader|. |start| is the
+ // starting offset of the ContentEncoding payload. |size| is the size in
+ // bytes of the ContentEncoding payload. Returns true on success.
+ bool ParseContentEncodingEntry(long long start,
+ long long size,
+ IMkvReader* const pReader);
+
+ // Parses the ContentEncryption element from |pReader|. |start| is the
+ // starting offset of the ContentEncryption payload. |size| is the size in
+ // bytes of the ContentEncryption payload. |encryption| is where the parsed
+ // values will be stored.
+ void ParseEncryptionEntry(long long start,
+ long long size,
+ IMkvReader* const pReader,
+ ContentEncryption* const encryption);
+
+ unsigned long long encoding_order() const { return encoding_order_; }
+ unsigned long long encoding_scope() const { return encoding_scope_; }
+ unsigned long long encoding_type() const { return encoding_type_; }
+
+private:
+ // Member variables for list of ContentCompression elements.
+ ContentCompression** compression_entries_;
+ ContentCompression** compression_entries_end_;
+
+ // Member variables for list of ContentEncryption elements.
+ ContentEncryption** encryption_entries_;
+ ContentEncryption** encryption_entries_end_;
+
+ // ContentEncoding element names
+ unsigned long long encoding_order_;
+ unsigned long long encoding_scope_;
+ unsigned long long encoding_type_;
+
+ // LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
+ ContentEncoding(const ContentEncoding&);
+ ContentEncoding& operator=(const ContentEncoding&);
+};
class Track
{
@@ -195,13 +292,15 @@
Track& operator=(const Track&);
public:
+ enum Type { kVideo = 1, kAudio = 2 };
+
Segment* const m_pSegment;
const long long m_element_start;
const long long m_element_size;
virtual ~Track();
- long long GetType() const;
- long long GetNumber() const;
+ long GetType() const;
+ long GetNumber() const;
unsigned long long GetUid() const;
const char* GetNameAsUTF8() const;
const char* GetCodecNameAsUTF8() const;
@@ -217,21 +316,29 @@
long long size;
};
- struct Info
+ class Info
{
- long long type;
- long long number;
+ public:
+ Info();
+ ~Info();
+ int Copy(Info&) const;
+ void Clear();
+ private:
+ Info(const Info&);
+ Info& operator=(const Info&);
+ public:
+ long type;
+ long number;
unsigned long long uid;
char* nameAsUTF8;
char* codecId;
+ char* codecNameAsUTF8;
unsigned char* codecPrivate;
size_t codecPrivateSize;
- char* codecNameAsUTF8;
bool lacing;
Settings settings;
-
- Info();
- void Clear();
+ private:
+ int CopyStr(char* Info::*str, Info&) const;
};
long GetFirst(const BlockEntry*&) const;
@@ -239,28 +346,33 @@
virtual bool VetEntry(const BlockEntry*) const = 0;
virtual long Seek(long long time_ns, const BlockEntry*&) const = 0;
+ const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const;
+ unsigned long GetContentEncodingCount() const;
+
+ void ParseContentEncodingsEntry(long long start, long long size);
+
protected:
Track(
Segment*,
- const Info&,
long long element_start,
long long element_size);
- const Info m_info;
+
+ Info m_info;
class EOSBlock : public BlockEntry
{
public:
EOSBlock();
- bool EOS() const;
- const Cluster* GetCluster() const;
- size_t GetIndex() const;
+ Kind GetKind() const;
const Block* GetBlock() const;
- bool IsBFrame() const;
};
EOSBlock m_eos;
+private:
+ ContentEncoding** content_encoding_entries_;
+ ContentEncoding** content_encoding_entries_end_;
};
@@ -269,12 +381,19 @@
VideoTrack(const VideoTrack&);
VideoTrack& operator=(const VideoTrack&);
-public:
VideoTrack(
Segment*,
+ long long element_start,
+ long long element_size);
+
+public:
+ static long Parse(
+ Segment*,
const Info&,
long long element_start,
- long long element_size);
+ long long element_size,
+ VideoTrack*&);
+
long long GetWidth() const;
long long GetHeight() const;
double GetFrameRate() const;
@@ -295,12 +414,18 @@
AudioTrack(const AudioTrack&);
AudioTrack& operator=(const AudioTrack&);
-public:
AudioTrack(
Segment*,
+ long long element_start,
+ long long element_size);
+public:
+ static long Parse(
+ Segment*,
const Info&,
long long element_start,
- long long element_size);
+ long long element_size,
+ AudioTrack*&);
+
double GetSamplingRate() const;
long long GetChannels() const;
long long GetBitDepth() const;
@@ -332,24 +457,27 @@
long long size,
long long element_start,
long long element_size);
- virtual ~Tracks();
- const Track* GetTrackByNumber(unsigned long tn) const;
+ ~Tracks();
+
+ long Parse();
+
+ unsigned long GetTracksCount() const;
+
+ const Track* GetTrackByNumber(long tn) const;
const Track* GetTrackByIndex(unsigned long idx) const;
private:
Track** m_trackEntries;
Track** m_trackEntriesEnd;
- void ParseTrackEntry(
- long long,
- long long,
- Track*&,
+ long ParseTrackEntry(
+ long long payload_start,
+ long long payload_size,
long long element_start,
- long long element_size);
+ long long element_size,
+ Track*&) const;
-public:
- unsigned long GetTracksCount() const;
};
@@ -374,6 +502,8 @@
~SegmentInfo();
+ long Parse();
+
long long GetTimeCodeScale() const;
long long GetDuration() const; //scaled
const char* GetMuxingAppAsUTF8() const;
@@ -410,24 +540,48 @@
~SeekHead();
+ long Parse();
+
struct Entry
{
+ //the SeekHead entry payload
long long id;
long long pos;
+
+ //absolute pos of SeekEntry ID
+ long long element_start;
+
+ //SeekEntry ID size + size size + payload
+ long long element_size;
};
int GetCount() const;
const Entry* GetEntry(int idx) const;
+ struct VoidElement
+ {
+ //absolute pos of Void ID
+ long long element_start;
+
+ //ID size + size size + payload size
+ long long element_size;
+ };
+
+ int GetVoidElementCount() const;
+ const VoidElement* GetVoidElement(int idx) const;
+
private:
Entry* m_entries;
- int m_count;
+ int m_entry_count;
- static void ParseEntry(
+ VoidElement* m_void_elements;
+ int m_void_element_count;
+
+ static bool ParseEntry(
IMkvReader*,
- long long pos,
+ long long pos, //payload
long long size,
- Entry*&);
+ Entry*);
};
@@ -562,14 +716,15 @@
long long GetFirstTime() const; //time (ns) of first (earliest) block
long long GetLastTime() const; //time (ns) of last (latest) block
- const BlockEntry* GetFirst() const;
- const BlockEntry* GetLast() const;
- const BlockEntry* GetNext(const BlockEntry*) const;
+ long GetFirst(const BlockEntry*&) const;
+ long GetLast(const BlockEntry*&) const;
+ long GetNext(const BlockEntry* curr, const BlockEntry*& next) const;
+
const BlockEntry* GetEntry(const Track*, long long ns = -1) const;
const BlockEntry* GetEntry(
const CuePoint&,
const CuePoint::TrackPosition&) const;
- const BlockEntry* GetMaxKey(const VideoTrack*) const;
+ //const BlockEntry* GetMaxKey(const VideoTrack*) const;
// static bool HasBlockEntries(const Segment*, long long);
@@ -581,11 +736,8 @@
long GetEntryCount() const;
- void Load() const;
long Load(long long& pos, long& size) const;
- void LoadBlockEntries() const;
-
long Parse(long long& pos, long& size) const;
long GetEntry(long index, const mkvparser::BlockEntry*&) const;
@@ -616,12 +768,12 @@
mutable long m_entries_size;
mutable long m_entries_count;
- long ParseSimpleBlock(long long, long long&, long&) const;
- long ParseBlockGroup(long long, long long&, long&) const;
+ long ParseSimpleBlock(long long, long long&, long&);
+ long ParseBlockGroup(long long, long long&, long&);
- void CreateBlock(long long id, long long pos, long long size) const;
- void CreateBlockGroup(long long, long long, BlockEntry**&) const;
- void CreateSimpleBlock(long long, long long, BlockEntry**&) const;
+ long CreateBlock(long long id, long long pos, long long size);
+ long CreateBlockGroup(long long, long long);
+ long CreateSimpleBlock(long long, long long);
};
@@ -636,10 +788,17 @@
Segment& operator=(const Segment&);
private:
- Segment(IMkvReader*, long long pos, long long size);
+ Segment(
+ IMkvReader*,
+ long long elem_start,
+ //long long elem_size,
+ long long pos,
+ long long size);
public:
IMkvReader* const m_pReader;
+ const long long m_element_start;
+ //const long long m_element_size;
const long long m_start; //posn of segment payload
const long long m_size; //size of segment payload
Cluster m_eos; //TODO: make private?
@@ -650,7 +809,8 @@
long Load(); //loads headers and all clusters
//for incremental loading
- long long Unparsed() const;
+ //long long Unparsed() const;
+ bool DoneParsing() const;
long long ParseHeaders(); //stops when first cluster is found
//long FindNextCluster(long long& pos, long& size) const;
long LoadCluster(long long& pos, long& size); //load one cluster