| Index: source/libvpx/third_party/libwebm/mkvparser.cpp
|
| diff --git a/source/libvpx/third_party/libwebm/mkvparser.cpp b/source/libvpx/third_party/libwebm/mkvparser.cpp
|
| index 441f16596b3d11f87375acb8ad040b5c2ea2afad..fc01be526409dafd5c1b6e4c173f052d5fbf2dab 100644
|
| --- a/source/libvpx/third_party/libwebm/mkvparser.cpp
|
| +++ b/source/libvpx/third_party/libwebm/mkvparser.cpp
|
| @@ -23,7 +23,7 @@ void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision) {
|
| major = 1;
|
| minor = 0;
|
| build = 0;
|
| - revision = 28;
|
| + revision = 30;
|
| }
|
|
|
| long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len) {
|
| @@ -130,6 +130,8 @@ long long mkvparser::GetUIntLength(IMkvReader* pReader, long long pos,
|
| return 0; // success
|
| }
|
|
|
| +// TODO(vigneshv): This function assumes that unsigned values never have their
|
| +// high bit set.
|
| long long mkvparser::UnserializeUInt(IMkvReader* pReader, long long pos,
|
| long long size) {
|
| assert(pReader);
|
| @@ -217,8 +219,8 @@ long mkvparser::UnserializeFloat(IMkvReader* pReader, long long pos,
|
| return 0;
|
| }
|
|
|
| -long mkvparser::UnserializeInt(IMkvReader* pReader, long long pos, long size,
|
| - long long& result) {
|
| +long mkvparser::UnserializeInt(IMkvReader* pReader, long long pos,
|
| + long long size, long long& result) {
|
| assert(pReader);
|
| assert(pos >= 0);
|
| assert(size > 0);
|
| @@ -605,6 +607,7 @@ Segment::Segment(IMkvReader* pReader, long long elem_start,
|
| m_pTracks(NULL),
|
| m_pCues(NULL),
|
| m_pChapters(NULL),
|
| + m_pTags(NULL),
|
| m_clusters(NULL),
|
| m_clusterCount(0),
|
| m_clusterPreloadCount(0),
|
| @@ -629,6 +632,7 @@ Segment::~Segment() {
|
| delete m_pInfo;
|
| delete m_pCues;
|
| delete m_pChapters;
|
| + delete m_pTags;
|
| delete m_pSeekHead;
|
| }
|
|
|
| @@ -907,6 +911,19 @@ long long Segment::ParseHeaders() {
|
| if (status)
|
| return status;
|
| }
|
| + } else if (id == 0x0254C367) { // Tags ID
|
| + if (m_pTags == NULL) {
|
| + m_pTags = new (std::nothrow)
|
| + Tags(this, pos, size, element_start, element_size);
|
| +
|
| + if (m_pTags == NULL)
|
| + return -1;
|
| +
|
| + const long status = m_pTags->Parse();
|
| +
|
| + if (status)
|
| + return status;
|
| + }
|
| }
|
|
|
| m_pos = pos + size; // consume payload
|
| @@ -1025,23 +1042,11 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
|
|
|
| const long long unknown_size = (1LL << (7 * len)) - 1;
|
|
|
| -#if 0 // we must handle this to support live webm
|
| - if (size == unknown_size)
|
| - return E_FILE_FORMAT_INVALID; //TODO: allow this
|
| -#endif
|
| -
|
| if ((segment_stop >= 0) && (size != unknown_size) &&
|
| ((pos + size) > segment_stop)) {
|
| return E_FILE_FORMAT_INVALID;
|
| }
|
|
|
| -#if 0 // commented-out, to support incremental cluster parsing
|
| - len = static_cast<long>(size);
|
| -
|
| - if ((pos + size) > avail)
|
| - return E_BUFFER_NOT_FULL;
|
| -#endif
|
| -
|
| if (id == 0x0C53BB6B) { // Cues ID
|
| if (size == unknown_size)
|
| return E_FILE_FORMAT_INVALID; // TODO: liberalize
|
| @@ -1157,10 +1162,8 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
|
| }
|
|
|
| if (status == 0) { // no entries found
|
| - if (cluster_size < 0)
|
| - return E_FILE_FORMAT_INVALID; // TODO: handle this
|
| -
|
| - pos += cluster_size;
|
| + if (cluster_size >= 0)
|
| + pos += cluster_size;
|
|
|
| if ((total >= 0) && (pos >= total)) {
|
| m_pos = total;
|
| @@ -1201,306 +1204,15 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
|
|
|
| return 0; // partial success, since we have a new cluster
|
|
|
| -// status == 0 means "no block entries found"
|
| -
|
| -// pos designates start of payload
|
| -// m_pos has NOT been adjusted yet (in case we need to come back here)
|
| -
|
| -#if 0
|
| -
|
| - if (cluster_size < 0) { //unknown size
|
| - const long long payload_pos = pos; //absolute pos of cluster payload
|
| -
|
| - for (;;) { //determine cluster size
|
| - if ((total >= 0) && (pos >= total))
|
| - break;
|
| -
|
| - if ((segment_stop >= 0) && (pos >= segment_stop))
|
| - break; //no more clusters
|
| -
|
| - //Read ID
|
| -
|
| - if ((pos + 1) > avail)
|
| - {
|
| - len = 1;
|
| - return E_BUFFER_NOT_FULL;
|
| - }
|
| -
|
| - long long result = GetUIntLength(m_pReader, pos, len);
|
| -
|
| - if (result < 0) //error
|
| - return static_cast<long>(result);
|
| -
|
| - if (result > 0) //weird
|
| - return E_BUFFER_NOT_FULL;
|
| -
|
| - if ((segment_stop >= 0) && ((pos + len) > segment_stop))
|
| - return E_FILE_FORMAT_INVALID;
|
| -
|
| - if ((pos + len) > avail)
|
| - return E_BUFFER_NOT_FULL;
|
| -
|
| - const long long idpos = pos;
|
| - const long long id = ReadUInt(m_pReader, idpos, len);
|
| -
|
| - if (id < 0) //error (or underflow)
|
| - return static_cast<long>(id);
|
| -
|
| - //This is the distinguished set of ID's we use to determine
|
| - //that we have exhausted the sub-element's inside the cluster
|
| - //whose ID we parsed earlier.
|
| -
|
| - if (id == 0x0F43B675) //Cluster ID
|
| - break;
|
| -
|
| - if (id == 0x0C53BB6B) //Cues ID
|
| - break;
|
| -
|
| - switch (id)
|
| - {
|
| - case 0x20: //BlockGroup
|
| - case 0x23: //Simple Block
|
| - case 0x67: //TimeCode
|
| - case 0x2B: //PrevSize
|
| - break;
|
| -
|
| - default:
|
| - assert(false);
|
| - break;
|
| - }
|
| -
|
| - pos += len; //consume ID (of sub-element)
|
| -
|
| - //Read Size
|
| -
|
| - if ((pos + 1) > avail)
|
| - {
|
| - len = 1;
|
| - return E_BUFFER_NOT_FULL;
|
| - }
|
| -
|
| - result = GetUIntLength(m_pReader, pos, len);
|
| -
|
| - if (result < 0) //error
|
| - return static_cast<long>(result);
|
| -
|
| - if (result > 0) //weird
|
| - return E_BUFFER_NOT_FULL;
|
| -
|
| - if ((segment_stop >= 0) && ((pos + len) > segment_stop))
|
| - return E_FILE_FORMAT_INVALID;
|
| -
|
| - if ((pos + len) > avail)
|
| - 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 size field of element
|
| -
|
| - //pos now points to start of sub-element's payload
|
| -
|
| - if (size == 0) //weird
|
| - continue;
|
| -
|
| - const long long unknown_size = (1LL << (7 * len)) - 1;
|
| -
|
| - if (size == unknown_size)
|
| - return E_FILE_FORMAT_INVALID; //not allowed for sub-elements
|
| -
|
| - if ((segment_stop >= 0) && ((pos + size) > segment_stop)) //weird
|
| - return E_FILE_FORMAT_INVALID;
|
| -
|
| - pos += size; //consume payload of sub-element
|
| - assert((segment_stop < 0) || (pos <= segment_stop));
|
| - } //determine cluster size
|
| -
|
| - cluster_size = pos - payload_pos;
|
| - assert(cluster_size >= 0);
|
| -
|
| - pos = payload_pos; //reset and re-parse original cluster
|
| - }
|
| -
|
| - if (m_clusterPreloadCount > 0)
|
| - {
|
| - assert(idx < m_clusterSize);
|
| -
|
| - Cluster* const pCluster = m_clusters[idx];
|
| - assert(pCluster);
|
| - assert(pCluster->m_index < 0);
|
| -
|
| - const long long off = pCluster->GetPosition();
|
| - assert(off >= 0);
|
| -
|
| - if (off == cluster_off) //preloaded already
|
| - return E_FILE_FORMAT_INVALID; //subtle
|
| - }
|
| -
|
| - m_pos = pos + cluster_size; //consume payload
|
| - assert((segment_stop < 0) || (m_pos <= segment_stop));
|
| -
|
| - return 2; //try to find another cluster
|
| -
|
| -#endif
|
| + // status == 0 means "no block entries found"
|
| + // pos designates start of payload
|
| + // m_pos has NOT been adjusted yet (in case we need to come back here)
|
| }
|
|
|
| long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
|
| assert(m_pos < 0);
|
| assert(m_pUnknownSize);
|
|
|
| -#if 0
|
| - assert(m_pUnknownSize->GetElementSize() < 0); //TODO: verify this
|
| -
|
| - const long long element_start = m_pUnknownSize->m_element_start;
|
| -
|
| - pos = -m_pos;
|
| - assert(pos > element_start);
|
| -
|
| - //We have already consumed the (cluster) ID and size fields.
|
| - //We just need to consume the blocks and other sub-elements
|
| - //of this cluster, until we discover the boundary.
|
| -
|
| - long long total, avail;
|
| -
|
| - long status = m_pReader->Length(&total, &avail);
|
| -
|
| - if (status < 0) //error
|
| - return status;
|
| -
|
| - assert((total < 0) || (avail <= total));
|
| -
|
| - const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
|
| -
|
| - long long element_size = -1;
|
| -
|
| - for (;;) { //determine cluster size
|
| - if ((total >= 0) && (pos >= total))
|
| - {
|
| - element_size = total - element_start;
|
| - assert(element_size > 0);
|
| -
|
| - break;
|
| - }
|
| -
|
| - if ((segment_stop >= 0) && (pos >= segment_stop))
|
| - {
|
| - element_size = segment_stop - element_start;
|
| - assert(element_size > 0);
|
| -
|
| - break;
|
| - }
|
| -
|
| - //Read ID
|
| -
|
| - if ((pos + 1) > avail)
|
| - {
|
| - len = 1;
|
| - return E_BUFFER_NOT_FULL;
|
| - }
|
| -
|
| - long long result = GetUIntLength(m_pReader, pos, len);
|
| -
|
| - if (result < 0) //error
|
| - return static_cast<long>(result);
|
| -
|
| - if (result > 0) //weird
|
| - return E_BUFFER_NOT_FULL;
|
| -
|
| - if ((segment_stop >= 0) && ((pos + len) > segment_stop))
|
| - return E_FILE_FORMAT_INVALID;
|
| -
|
| - if ((pos + len) > avail)
|
| - return E_BUFFER_NOT_FULL;
|
| -
|
| - const long long idpos = pos;
|
| - const long long id = ReadUInt(m_pReader, idpos, len);
|
| -
|
| - if (id < 0) //error (or underflow)
|
| - return static_cast<long>(id);
|
| -
|
| - //This is the distinguished set of ID's we use to determine
|
| - //that we have exhausted the sub-element's inside the cluster
|
| - //whose ID we parsed earlier.
|
| -
|
| - if ((id == 0x0F43B675) || (id == 0x0C53BB6B)) { //Cluster ID or Cues ID
|
| - element_size = pos - element_start;
|
| - assert(element_size > 0);
|
| -
|
| - break;
|
| - }
|
| -
|
| -#ifdef _DEBUG
|
| - switch (id)
|
| - {
|
| - case 0x20: //BlockGroup
|
| - case 0x23: //Simple Block
|
| - case 0x67: //TimeCode
|
| - case 0x2B: //PrevSize
|
| - break;
|
| -
|
| - default:
|
| - assert(false);
|
| - break;
|
| - }
|
| -#endif
|
| -
|
| - pos += len; //consume ID (of sub-element)
|
| -
|
| - //Read Size
|
| -
|
| - if ((pos + 1) > avail)
|
| - {
|
| - len = 1;
|
| - return E_BUFFER_NOT_FULL;
|
| - }
|
| -
|
| - result = GetUIntLength(m_pReader, pos, len);
|
| -
|
| - if (result < 0) //error
|
| - return static_cast<long>(result);
|
| -
|
| - if (result > 0) //weird
|
| - return E_BUFFER_NOT_FULL;
|
| -
|
| - if ((segment_stop >= 0) && ((pos + len) > segment_stop))
|
| - return E_FILE_FORMAT_INVALID;
|
| -
|
| - if ((pos + len) > avail)
|
| - 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 size field of element
|
| -
|
| - //pos now points to start of sub-element's payload
|
| -
|
| - if (size == 0) //weird
|
| - continue;
|
| -
|
| - const long long unknown_size = (1LL << (7 * len)) - 1;
|
| -
|
| - if (size == unknown_size)
|
| - return E_FILE_FORMAT_INVALID; //not allowed for sub-elements
|
| -
|
| - if ((segment_stop >= 0) && ((pos + size) > segment_stop)) //weird
|
| - return E_FILE_FORMAT_INVALID;
|
| -
|
| - pos += size; //consume payload of sub-element
|
| - assert((segment_stop < 0) || (pos <= segment_stop));
|
| - } //determine cluster size
|
| -
|
| - assert(element_size >= 0);
|
| -
|
| - m_pos = element_start + element_size;
|
| - m_pUnknownSize = 0;
|
| -
|
| - return 2; //continue parsing
|
| -#else
|
| const long status = m_pUnknownSize->Parse(pos, len);
|
|
|
| if (status < 0) // error or underflow
|
| @@ -1522,7 +1234,6 @@ long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
|
| m_pUnknownSize = 0;
|
|
|
| return 2; // continue parsing
|
| -#endif
|
| }
|
|
|
| void Segment::AppendCluster(Cluster* pCluster) {
|
| @@ -1540,7 +1251,7 @@ void Segment::AppendCluster(Cluster* pCluster) {
|
| if (count >= size) {
|
| const long n = (size <= 0) ? 2048 : 2 * size;
|
|
|
| - Cluster** const qq = new Cluster* [n];
|
| + Cluster** const qq = new Cluster*[n];
|
| Cluster** q = qq;
|
|
|
| Cluster** p = m_clusters;
|
| @@ -1594,7 +1305,7 @@ void Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
|
| if (count >= size) {
|
| const long n = (size <= 0) ? 2048 : 2 * size;
|
|
|
| - Cluster** const qq = new Cluster* [n];
|
| + Cluster** const qq = new Cluster*[n];
|
| Cluster** q = qq;
|
|
|
| Cluster** p = m_clusters;
|
| @@ -1794,55 +1505,6 @@ const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
|
| return m_void_elements + idx;
|
| }
|
|
|
| -#if 0
|
| -void Segment::ParseCues(long long off)
|
| -{
|
| - if (m_pCues)
|
| - return;
|
| -
|
| - //odbgstream os;
|
| - //os << "Segment::ParseCues (begin)" << endl;
|
| -
|
| - long long pos = m_start + off;
|
| - const long long element_start = pos;
|
| - const long long stop = m_start + m_size;
|
| -
|
| - long len;
|
| -
|
| - long long result = GetUIntLength(m_pReader, pos, len);
|
| - assert(result == 0);
|
| - assert((pos + len) <= stop);
|
| -
|
| - const long long idpos = pos;
|
| -
|
| - const long long id = ReadUInt(m_pReader, idpos, len);
|
| - assert(id == 0x0C53BB6B); //Cues ID
|
| -
|
| - pos += len; //consume ID
|
| - assert(pos < stop);
|
| -
|
| - //Read Size
|
| -
|
| - result = GetUIntLength(m_pReader, pos, len);
|
| - assert(result == 0);
|
| - assert((pos + len) <= stop);
|
| -
|
| - const long long size = ReadUInt(m_pReader, pos, len);
|
| - assert(size >= 0);
|
| -
|
| - pos += len; //consume length of size of element
|
| - assert((pos + size) <= stop);
|
| -
|
| - const long long element_size = size + pos - element_start;
|
| -
|
| - //Pos now points to start of payload
|
| -
|
| - m_pCues = new Cues(this, pos, size, element_start, element_size);
|
| - assert(m_pCues); //TODO
|
| -
|
| - //os << "Segment::ParseCues (end)" << endl;
|
| -}
|
| -#else
|
| long Segment::ParseCues(long long off, long long& pos, long& len) {
|
| if (m_pCues)
|
| return 0; // success
|
| @@ -1957,67 +1619,7 @@ long Segment::ParseCues(long long off, long long& pos, long& len) {
|
|
|
| return 0; // success
|
| }
|
| -#endif
|
| -
|
| -#if 0
|
| -void Segment::ParseSeekEntry(
|
| - long long start,
|
| - long long size_)
|
| -{
|
| - long long pos = start;
|
| -
|
| - const long long stop = start + size_;
|
| -
|
| - long len;
|
| -
|
| - const long long seekIdId = ReadUInt(m_pReader, pos, len);
|
| - //seekIdId;
|
| - assert(seekIdId == 0x13AB); //SeekID ID
|
| - assert((pos + len) <= stop);
|
|
|
| - pos += len; //consume id
|
| -
|
| - const long long seekIdSize = ReadUInt(m_pReader, pos, len);
|
| - assert(seekIdSize >= 0);
|
| - assert((pos + len) <= stop);
|
| -
|
| - pos += len; //consume size
|
| -
|
| - const long long seekId = ReadUInt(m_pReader, pos, len); //payload
|
| - assert(seekId >= 0);
|
| - assert(len == seekIdSize);
|
| - assert((pos + len) <= stop);
|
| -
|
| - pos += seekIdSize; //consume payload
|
| -
|
| - const long long seekPosId = ReadUInt(m_pReader, pos, len);
|
| - //seekPosId;
|
| - assert(seekPosId == 0x13AC); //SeekPos ID
|
| - assert((pos + len) <= stop);
|
| -
|
| - pos += len; //consume id
|
| -
|
| - const long long seekPosSize = ReadUInt(m_pReader, pos, len);
|
| - assert(seekPosSize >= 0);
|
| - assert((pos + len) <= stop);
|
| -
|
| - pos += len; //consume size
|
| - assert((pos + seekPosSize) <= stop);
|
| -
|
| - const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);
|
| - assert(seekOff >= 0);
|
| - assert(seekOff < m_size);
|
| -
|
| - pos += seekPosSize; //consume payload
|
| - assert(pos == stop);
|
| -
|
| - const long long seekPos = m_start + seekOff;
|
| - assert(seekPos < (m_start + m_size));
|
| -
|
| - if (seekId == 0x0C53BB6B) //Cues ID
|
| - ParseCues(seekOff);
|
| -}
|
| -#else
|
| bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
|
| Entry* pEntry) {
|
| if (size_ <= 0)
|
| @@ -2110,7 +1712,6 @@ bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
|
|
|
| return true;
|
| }
|
| -#endif
|
|
|
| Cues::Cues(Segment* pSegment, long long start_, long long size_,
|
| long long element_start, long long element_size)
|
| @@ -2152,9 +1753,9 @@ bool Cues::DoneParsing() const {
|
| return (m_pos >= stop);
|
| }
|
|
|
| -void Cues::Init() const {
|
| +bool Cues::Init() const {
|
| if (m_cue_points)
|
| - return;
|
| + return true;
|
|
|
| assert(m_count == 0);
|
| assert(m_preload_count == 0);
|
| @@ -2172,24 +1773,28 @@ void Cues::Init() const {
|
| long len;
|
|
|
| const long long id = ReadUInt(pReader, pos, len);
|
| - assert(id >= 0); // TODO
|
| - assert((pos + len) <= stop);
|
| + if (id < 0 || (pos + len) > stop) {
|
| + return false;
|
| + }
|
|
|
| pos += len; // consume ID
|
|
|
| const long long size = ReadUInt(pReader, pos, len);
|
| - assert(size >= 0);
|
| - assert((pos + len) <= stop);
|
| + if (size < 0 || (pos + len > stop)) {
|
| + return false;
|
| + }
|
|
|
| pos += len; // consume Size field
|
| - assert((pos + size) <= stop);
|
| + if (pos + size > stop) {
|
| + return false;
|
| + }
|
|
|
| if (id == 0x3B) // CuePoint ID
|
| PreloadCuePoint(cue_points_size, idpos);
|
|
|
| - pos += size; // consume payload
|
| - assert(pos <= stop);
|
| + pos += size; // skip payload
|
| }
|
| + return true;
|
| }
|
|
|
| void Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
|
| @@ -2198,7 +1803,7 @@ void Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
|
| if (m_preload_count >= cue_points_size) {
|
| const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
|
|
|
| - CuePoint** const qq = new CuePoint* [n];
|
| + CuePoint** const qq = new CuePoint*[n];
|
| CuePoint** q = qq; // beginning of target
|
|
|
| CuePoint** p = m_cue_points; // beginning of source
|
| @@ -2226,7 +1831,10 @@ bool Cues::LoadCuePoint() const {
|
| if (m_pos >= stop)
|
| return false; // nothing else to do
|
|
|
| - Init();
|
| + if (!Init()) {
|
| + m_pos = stop;
|
| + return false;
|
| + }
|
|
|
| IMkvReader* const pReader = m_pSegment->m_pReader;
|
|
|
| @@ -2263,7 +1871,10 @@ bool Cues::LoadCuePoint() const {
|
| if (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos))
|
| return false;
|
|
|
| - pCP->Load(pReader);
|
| + if (!pCP->Load(pReader)) {
|
| + m_pos = stop;
|
| + return false;
|
| + }
|
| ++m_count;
|
| --m_preload_count;
|
|
|
| @@ -2282,62 +1893,6 @@ bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
|
| assert(time_ns >= 0);
|
| assert(pTrack);
|
|
|
| -#if 0
|
| - LoadCuePoint(); //establish invariant
|
| -
|
| - assert(m_cue_points);
|
| - assert(m_count > 0);
|
| -
|
| - CuePoint** const ii = m_cue_points;
|
| - CuePoint** i = ii;
|
| -
|
| - CuePoint** const jj = ii + m_count + m_preload_count;
|
| - CuePoint** j = jj;
|
| -
|
| - pCP = *i;
|
| - assert(pCP);
|
| -
|
| - if (time_ns <= pCP->GetTime(m_pSegment))
|
| - {
|
| - pTP = pCP->Find(pTrack);
|
| - return (pTP != NULL);
|
| - }
|
| -
|
| - IMkvReader* const pReader = m_pSegment->m_pReader;
|
| -
|
| - while (i < j)
|
| - {
|
| - //INVARIANT:
|
| - //[ii, i) <= time_ns
|
| - //[i, j) ?
|
| - //[j, jj) > time_ns
|
| -
|
| - CuePoint** const k = i + (j - i) / 2;
|
| - assert(k < jj);
|
| -
|
| - CuePoint* const pCP = *k;
|
| - assert(pCP);
|
| -
|
| - pCP->Load(pReader);
|
| -
|
| - const long long t = pCP->GetTime(m_pSegment);
|
| -
|
| - if (t <= time_ns)
|
| - i = k + 1;
|
| - else
|
| - j = k;
|
| -
|
| - assert(i <= j);
|
| - }
|
| -
|
| - assert(i == j);
|
| - assert(i <= jj);
|
| - assert(i > ii);
|
| -
|
| - pCP = *--i;
|
| - assert(pCP);
|
| - assert(pCP->GetTime(m_pSegment) <= time_ns);
|
| -#else
|
| if (m_cue_points == NULL)
|
| return false;
|
|
|
| @@ -2387,7 +1942,6 @@ bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
|
| pCP = *--i;
|
| assert(pCP);
|
| assert(pCP->GetTime(m_pSegment) <= time_ns);
|
| -#endif
|
|
|
| // TODO: here and elsewhere, it's probably not correct to search
|
| // for the cue point with this time, and then search for a matching
|
| @@ -2401,65 +1955,6 @@ bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
|
| return (pTP != NULL);
|
| }
|
|
|
| -#if 0
|
| -bool Cues::FindNext(
|
| - long long time_ns,
|
| - const Track* pTrack,
|
| - const CuePoint*& pCP,
|
| - const CuePoint::TrackPosition*& pTP) const
|
| -{
|
| - pCP = 0;
|
| - pTP = 0;
|
| -
|
| - if (m_count == 0)
|
| - return false;
|
| -
|
| - assert(m_cue_points);
|
| -
|
| - const CuePoint* const* const ii = m_cue_points;
|
| - const CuePoint* const* i = ii;
|
| -
|
| - const CuePoint* const* const jj = ii + m_count;
|
| - const CuePoint* const* j = jj;
|
| -
|
| - while (i < j)
|
| - {
|
| - //INVARIANT:
|
| - //[ii, i) <= time_ns
|
| - //[i, j) ?
|
| - //[j, jj) > time_ns
|
| -
|
| - const CuePoint* const* const k = i + (j - i) / 2;
|
| - assert(k < jj);
|
| -
|
| - pCP = *k;
|
| - assert(pCP);
|
| -
|
| - const long long t = pCP->GetTime(m_pSegment);
|
| -
|
| - if (t <= time_ns)
|
| - i = k + 1;
|
| - else
|
| - j = k;
|
| -
|
| - assert(i <= j);
|
| - }
|
| -
|
| - assert(i == j);
|
| - assert(i <= jj);
|
| -
|
| - if (i >= jj) //time_ns is greater than max cue point
|
| - return false;
|
| -
|
| - pCP = *i;
|
| - assert(pCP);
|
| - assert(pCP->GetTime(m_pSegment) > time_ns);
|
| -
|
| - pTP = pCP->Find(pTrack);
|
| - return (pTP != NULL);
|
| -}
|
| -#endif
|
| -
|
| const CuePoint* Cues::GetFirst() const {
|
| if (m_cue_points == NULL)
|
| return NULL;
|
| @@ -2467,15 +1962,6 @@ const CuePoint* Cues::GetFirst() const {
|
| if (m_count == 0)
|
| return NULL;
|
|
|
| -#if 0
|
| - LoadCuePoint(); //init cues
|
| -
|
| - const size_t count = m_count + m_preload_count;
|
| -
|
| - if (count == 0) //weird
|
| - return NULL;
|
| -#endif
|
| -
|
| CuePoint* const* const pp = m_cue_points;
|
| assert(pp);
|
|
|
| @@ -2493,34 +1979,14 @@ const CuePoint* Cues::GetLast() const {
|
| if (m_count <= 0)
|
| return NULL;
|
|
|
| -#if 0
|
| - LoadCuePoint(); //init cues
|
| -
|
| - const size_t count = m_count + m_preload_count;
|
| + const long index = m_count - 1;
|
|
|
| - if (count == 0) //weird
|
| - return NULL;
|
| -
|
| - const size_t index = count - 1;
|
| -
|
| - CuePoint* const* const pp = m_cue_points;
|
| - assert(pp);
|
| -
|
| - CuePoint* const pCP = pp[index];
|
| - assert(pCP);
|
| -
|
| - pCP->Load(m_pSegment->m_pReader);
|
| - assert(pCP->GetTimeCode() >= 0);
|
| -#else
|
| - const long index = m_count - 1;
|
| -
|
| - CuePoint* const* const pp = m_cue_points;
|
| - assert(pp);
|
| + CuePoint* const* const pp = m_cue_points;
|
| + assert(pp);
|
|
|
| CuePoint* const pCP = pp[index];
|
| assert(pCP);
|
| assert(pCP->GetTimeCode() >= 0);
|
| -#endif
|
|
|
| return pCP;
|
| }
|
| @@ -2533,26 +1999,6 @@ const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
|
| assert(m_cue_points);
|
| assert(m_count >= 1);
|
|
|
| -#if 0
|
| - const size_t count = m_count + m_preload_count;
|
| -
|
| - size_t index = pCurr->m_index;
|
| - assert(index < count);
|
| -
|
| - CuePoint* const* const pp = m_cue_points;
|
| - assert(pp);
|
| - assert(pp[index] == pCurr);
|
| -
|
| - ++index;
|
| -
|
| - if (index >= count)
|
| - return NULL;
|
| -
|
| - CuePoint* const pNext = pp[index];
|
| - assert(pNext);
|
| -
|
| - pNext->Load(m_pSegment->m_pReader);
|
| -#else
|
| long index = pCurr->m_index;
|
| assert(index < m_count);
|
|
|
| @@ -2568,7 +2014,6 @@ const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
|
| CuePoint* const pNext = pp[index];
|
| assert(pNext);
|
| assert(pNext->GetTimeCode() >= 0);
|
| -#endif
|
|
|
| return pNext;
|
| }
|
| @@ -2705,12 +2150,12 @@ CuePoint::CuePoint(long idx, long long pos)
|
|
|
| CuePoint::~CuePoint() { delete[] m_track_positions; }
|
|
|
| -void CuePoint::Load(IMkvReader* pReader) {
|
| +bool CuePoint::Load(IMkvReader* pReader) {
|
| // odbgstream os;
|
| // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
|
|
|
| if (m_timecode >= 0) // already loaded
|
| - return;
|
| + return true;
|
|
|
| assert(m_track_positions == NULL);
|
| assert(m_track_positions_count == 0);
|
| @@ -2726,7 +2171,7 @@ void CuePoint::Load(IMkvReader* pReader) {
|
| const long long id = ReadUInt(pReader, pos_, len);
|
| assert(id == 0x3B); // CuePoint ID
|
| if (id != 0x3B)
|
| - return;
|
| + return false;
|
|
|
| pos_ += len; // consume ID
|
|
|
| @@ -2749,17 +2194,21 @@ void CuePoint::Load(IMkvReader* pReader) {
|
| long len;
|
|
|
| const long long id = ReadUInt(pReader, pos, len);
|
| - assert(id >= 0); // TODO
|
| - assert((pos + len) <= stop);
|
| + if ((id < 0) || (pos + len > stop)) {
|
| + return false;
|
| + }
|
|
|
| pos += len; // consume ID
|
|
|
| const long long size = ReadUInt(pReader, pos, len);
|
| - assert(size >= 0);
|
| - assert((pos + len) <= stop);
|
| + if ((size < 0) || (pos + len > stop)) {
|
| + return false;
|
| + }
|
|
|
| pos += len; // consume Size field
|
| - assert((pos + size) <= stop);
|
| + if ((pos + size) > stop) {
|
| + return false;
|
| + }
|
|
|
| if (id == 0x33) // CueTime ID
|
| m_timecode = UnserializeUInt(pReader, pos, size);
|
| @@ -2768,11 +2217,11 @@ void CuePoint::Load(IMkvReader* pReader) {
|
| ++m_track_positions_count;
|
|
|
| pos += size; // consume payload
|
| - assert(pos <= stop);
|
| }
|
|
|
| - assert(m_timecode >= 0);
|
| - assert(m_track_positions_count > 0);
|
| + if (m_timecode < 0 || m_track_positions_count <= 0) {
|
| + return false;
|
| + }
|
|
|
| // os << "CuePoint::Load(cont'd): idpos=" << idpos
|
| // << " timecode=" << m_timecode
|
| @@ -2789,7 +2238,7 @@ void CuePoint::Load(IMkvReader* pReader) {
|
| long len;
|
|
|
| const long long id = ReadUInt(pReader, pos, len);
|
| - assert(id >= 0); // TODO
|
| + assert(id >= 0);
|
| assert((pos + len) <= stop);
|
|
|
| pos += len; // consume ID
|
| @@ -2803,7 +2252,9 @@ void CuePoint::Load(IMkvReader* pReader) {
|
|
|
| if (id == 0x37) { // CueTrackPosition(s) ID
|
| TrackPosition& tp = *p++;
|
| - tp.Parse(pReader, pos, size);
|
| + if (!tp.Parse(pReader, pos, size)) {
|
| + return false;
|
| + }
|
| }
|
|
|
| pos += size; // consume payload
|
| @@ -2814,9 +2265,11 @@ void CuePoint::Load(IMkvReader* pReader) {
|
|
|
| m_element_start = element_start;
|
| m_element_size = element_size;
|
| +
|
| + return true;
|
| }
|
|
|
| -void CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
|
| +bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
|
| long long size_) {
|
| const long long stop = start_ + size_;
|
| long long pos = start_;
|
| @@ -2829,17 +2282,21 @@ void CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
|
| long len;
|
|
|
| const long long id = ReadUInt(pReader, pos, len);
|
| - assert(id >= 0); // TODO
|
| - assert((pos + len) <= stop);
|
| + if ((id < 0) || ((pos + len) > stop)) {
|
| + return false;
|
| + }
|
|
|
| pos += len; // consume ID
|
|
|
| const long long size = ReadUInt(pReader, pos, len);
|
| - assert(size >= 0);
|
| - assert((pos + len) <= stop);
|
| + if ((size < 0) || ((pos + len) > stop)) {
|
| + return false;
|
| + }
|
|
|
| pos += len; // consume Size field
|
| - assert((pos + size) <= stop);
|
| + if ((pos + size) > stop) {
|
| + return false;
|
| + }
|
|
|
| if (id == 0x77) // CueTrack ID
|
| m_track = UnserializeUInt(pReader, pos, size);
|
| @@ -2851,12 +2308,13 @@ void CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
|
| m_block = UnserializeUInt(pReader, pos, size);
|
|
|
| pos += size; // consume payload
|
| - assert(pos <= stop);
|
| }
|
|
|
| - assert(m_pos >= 0);
|
| - assert(m_track > 0);
|
| - // assert(m_block > 0);
|
| + if ((m_pos < 0) || (m_track <= 0)) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| }
|
|
|
| const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
|
| @@ -2894,20 +2352,6 @@ long long CuePoint::GetTime(const Segment* pSegment) const {
|
| return time;
|
| }
|
|
|
| -#if 0
|
| -long long Segment::Unparsed() const
|
| -{
|
| - if (m_size < 0)
|
| - return LLONG_MAX;
|
| -
|
| - const long long stop = m_start + m_size;
|
| -
|
| - const long long result = stop - m_pos;
|
| - assert(result >= 0);
|
| -
|
| - return result;
|
| -}
|
| -#else
|
| bool Segment::DoneParsing() const {
|
| if (m_size < 0) {
|
| long long total, avail;
|
| @@ -2927,7 +2371,6 @@ bool Segment::DoneParsing() const {
|
|
|
| return (m_pos >= stop);
|
| }
|
| -#endif
|
|
|
| const Cluster* Segment::GetFirst() const {
|
| if ((m_clusters == NULL) || (m_clusterCount <= 0))
|
| @@ -3395,15 +2838,7 @@ long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
|
| continue;
|
| }
|
|
|
| -#if 0 // this is commented-out to support incremental cluster parsing
|
| - len = static_cast<long>(size);
|
| -
|
| - if (element_stop > avail)
|
| - return E_BUFFER_NOT_FULL;
|
| -#endif
|
| -
|
| // We have a cluster.
|
| -
|
| off_next = idoff;
|
|
|
| if (size != unknown_size)
|
| @@ -3647,188 +3082,11 @@ const Cluster* Segment::FindCluster(long long time_ns) const {
|
| return pCluster;
|
| }
|
|
|
| -#if 0
|
| -const BlockEntry* Segment::Seek(
|
| - long long time_ns,
|
| - const Track* pTrack) const
|
| -{
|
| - assert(pTrack);
|
| -
|
| - if ((m_clusters == NULL) || (m_clusterCount <= 0))
|
| - return pTrack->GetEOS();
|
| -
|
| - Cluster** const i = m_clusters;
|
| - assert(i);
|
| -
|
| - {
|
| - Cluster* const pCluster = *i;
|
| - assert(pCluster);
|
| - assert(pCluster->m_index == 0); //m_clusterCount > 0
|
| - assert(pCluster->m_pSegment == this);
|
| -
|
| - if (time_ns <= pCluster->GetTime())
|
| - return pCluster->GetEntry(pTrack);
|
| - }
|
| -
|
| - Cluster** const j = i + m_clusterCount;
|
| -
|
| - if (pTrack->GetType() == 2) { //audio
|
| - //TODO: we could decide to use cues for this, as we do for video.
|
| - //But we only use it for video because looking around for a keyframe
|
| - //can get expensive. Audio doesn't require anything special so a
|
| - //straight cluster search is good enough (we assume).
|
| -
|
| - Cluster** lo = i;
|
| - Cluster** hi = j;
|
| -
|
| - while (lo < hi)
|
| - {
|
| - //INVARIANT:
|
| - //[i, lo) <= time_ns
|
| - //[lo, hi) ?
|
| - //[hi, j) > time_ns
|
| -
|
| - Cluster** const mid = lo + (hi - lo) / 2;
|
| - assert(mid < hi);
|
| -
|
| - Cluster* const pCluster = *mid;
|
| - assert(pCluster);
|
| - assert(pCluster->m_index == long(mid - m_clusters));
|
| - assert(pCluster->m_pSegment == this);
|
| -
|
| - const long long t = pCluster->GetTime();
|
| -
|
| - if (t <= time_ns)
|
| - lo = mid + 1;
|
| - else
|
| - hi = mid;
|
| -
|
| - assert(lo <= hi);
|
| - }
|
| -
|
| - assert(lo == hi);
|
| - assert(lo > i);
|
| - assert(lo <= j);
|
| -
|
| - while (lo > i)
|
| - {
|
| - Cluster* const pCluster = *--lo;
|
| - assert(pCluster);
|
| - assert(pCluster->GetTime() <= time_ns);
|
| -
|
| - const BlockEntry* const pBE = pCluster->GetEntry(pTrack);
|
| -
|
| - if ((pBE != 0) && !pBE->EOS())
|
| - return pBE;
|
| -
|
| - //landed on empty cluster (no entries)
|
| - }
|
| -
|
| - return pTrack->GetEOS(); //weird
|
| - }
|
| -
|
| - assert(pTrack->GetType() == 1); //video
|
| -
|
| - Cluster** lo = i;
|
| - Cluster** hi = j;
|
| -
|
| - while (lo < hi)
|
| - {
|
| - //INVARIANT:
|
| - //[i, lo) <= time_ns
|
| - //[lo, hi) ?
|
| - //[hi, j) > time_ns
|
| -
|
| - Cluster** const mid = lo + (hi - lo) / 2;
|
| - assert(mid < hi);
|
| -
|
| - Cluster* const pCluster = *mid;
|
| - assert(pCluster);
|
| -
|
| - const long long t = pCluster->GetTime();
|
| -
|
| - if (t <= time_ns)
|
| - lo = mid + 1;
|
| - else
|
| - hi = mid;
|
| -
|
| - assert(lo <= hi);
|
| - }
|
| -
|
| - assert(lo == hi);
|
| - assert(lo > i);
|
| - assert(lo <= j);
|
| -
|
| - Cluster* pCluster = *--lo;
|
| - assert(pCluster);
|
| - assert(pCluster->GetTime() <= time_ns);
|
| -
|
| - {
|
| - const BlockEntry* const pBE = pCluster->GetEntry(pTrack, time_ns);
|
| -
|
| - if ((pBE != 0) && !pBE->EOS()) //found a keyframe
|
| - return pBE;
|
| - }
|
| -
|
| - const VideoTrack* const pVideo = static_cast<const VideoTrack*>(pTrack);
|
| -
|
| - while (lo != i)
|
| - {
|
| - pCluster = *--lo;
|
| - assert(pCluster);
|
| - assert(pCluster->GetTime() <= time_ns);
|
| -
|
| - const BlockEntry* const pBlockEntry = pCluster->GetMaxKey(pVideo);
|
| -
|
| - if ((pBlockEntry != 0) && !pBlockEntry->EOS())
|
| - return pBlockEntry;
|
| - }
|
| -
|
| - //weird: we're on the first cluster, but no keyframe found
|
| - //should never happen but we must return something anyway
|
| -
|
| - return pTrack->GetEOS();
|
| -}
|
| -#endif
|
| -
|
| -#if 0
|
| -bool Segment::SearchCues(
|
| - long long time_ns,
|
| - Track* pTrack,
|
| - Cluster*& pCluster,
|
| - const BlockEntry*& pBlockEntry,
|
| - const CuePoint*& pCP,
|
| - const CuePoint::TrackPosition*& pTP)
|
| -{
|
| - if (pTrack->GetType() != 1) //not video
|
| - return false; //TODO: for now, just handle video stream
|
| -
|
| - if (m_pCues == NULL)
|
| - return false;
|
| -
|
| - if (!m_pCues->Find(time_ns, pTrack, pCP, pTP))
|
| - return false; //weird
|
| -
|
| - assert(pCP);
|
| - assert(pTP);
|
| - assert(pTP->m_track == pTrack->GetNumber());
|
| -
|
| - //We have the cue point and track position we want,
|
| - //so we now need to search for the cluster having
|
| - //the indicated position.
|
| -
|
| - return GetCluster(pCP, pTP, pCluster, pBlockEntry);
|
| -}
|
| -#endif
|
| -
|
| const Tracks* Segment::GetTracks() const { return m_pTracks; }
|
| -
|
| const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
|
| -
|
| const Cues* Segment::GetCues() const { return m_pCues; }
|
| -
|
| const Chapters* Segment::GetChapters() const { return m_pChapters; }
|
| -
|
| +const Tags* Segment::GetTags() const { return m_pTags; }
|
| const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
|
|
|
| long long Segment::GetDuration() const {
|
| @@ -3853,6 +3111,7 @@ Chapters::~Chapters() {
|
| Edition& e = m_editions[--m_editions_count];
|
| e.Clear();
|
| }
|
| + delete[] m_editions;
|
| }
|
|
|
| long Chapters::Parse() {
|
| @@ -4128,12 +3387,13 @@ long Chapters::Atom::Parse(IMkvReader* pReader, long long pos, long long size) {
|
| if (status < 0) // error
|
| return status;
|
| } else if (id == 0x33C4) { // UID ID
|
| - const long long val = UnserializeUInt(pReader, pos, size);
|
| + long long val;
|
| + status = UnserializeInt(pReader, pos, size, val);
|
|
|
| - if (val < 0) // error
|
| - return static_cast<long>(val);
|
| + if (status < 0) // error
|
| + return status;
|
|
|
| - m_uid = val;
|
| + m_uid = static_cast<unsigned long long>(val);
|
| } else if (id == 0x11) { // TimeStart ID
|
| const long long val = UnserializeUInt(pReader, pos, size);
|
|
|
| @@ -4181,79 +3441,351 @@ long long Chapters::Atom::GetTime(const Chapters* pChapters,
|
| if (timecode < 0)
|
| return -1;
|
|
|
| - const long long result = timecode_scale * timecode;
|
| + const long long result = timecode_scale * timecode;
|
| +
|
| + return result;
|
| +}
|
| +
|
| +long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
|
| + long long size) {
|
| + if (!ExpandDisplaysArray())
|
| + return -1;
|
| +
|
| + Display& d = m_displays[m_displays_count++];
|
| + d.Init();
|
| +
|
| + return d.Parse(pReader, pos, size);
|
| +}
|
| +
|
| +bool Chapters::Atom::ExpandDisplaysArray() {
|
| + if (m_displays_size > m_displays_count)
|
| + return true; // nothing else to do
|
| +
|
| + const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
|
| +
|
| + Display* const displays = new (std::nothrow) Display[size];
|
| +
|
| + if (displays == NULL)
|
| + return false;
|
| +
|
| + for (int idx = 0; idx < m_displays_count; ++idx) {
|
| + m_displays[idx].ShallowCopy(displays[idx]);
|
| + }
|
| +
|
| + delete[] m_displays;
|
| + m_displays = displays;
|
| +
|
| + m_displays_size = size;
|
| + return true;
|
| +}
|
| +
|
| +Chapters::Display::Display() {}
|
| +
|
| +Chapters::Display::~Display() {}
|
| +
|
| +const char* Chapters::Display::GetString() const { return m_string; }
|
| +
|
| +const char* Chapters::Display::GetLanguage() const { return m_language; }
|
| +
|
| +const char* Chapters::Display::GetCountry() const { return m_country; }
|
| +
|
| +void Chapters::Display::Init() {
|
| + m_string = NULL;
|
| + m_language = NULL;
|
| + m_country = NULL;
|
| +}
|
| +
|
| +void Chapters::Display::ShallowCopy(Display& rhs) const {
|
| + rhs.m_string = m_string;
|
| + rhs.m_language = m_language;
|
| + rhs.m_country = m_country;
|
| +}
|
| +
|
| +void Chapters::Display::Clear() {
|
| + delete[] m_string;
|
| + m_string = NULL;
|
| +
|
| + delete[] m_language;
|
| + m_language = NULL;
|
| +
|
| + delete[] m_country;
|
| + m_country = NULL;
|
| +}
|
| +
|
| +long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
|
| + long long size) {
|
| + const long long stop = pos + size;
|
| +
|
| + while (pos < stop) {
|
| + long long id, size;
|
| +
|
| + long status = ParseElementHeader(pReader, pos, stop, id, size);
|
| +
|
| + if (status < 0) // error
|
| + return status;
|
| +
|
| + if (size == 0) // weird
|
| + continue;
|
| +
|
| + if (id == 0x05) { // ChapterString ID
|
| + status = UnserializeString(pReader, pos, size, m_string);
|
| +
|
| + if (status)
|
| + return status;
|
| + } else if (id == 0x037C) { // ChapterLanguage ID
|
| + status = UnserializeString(pReader, pos, size, m_language);
|
| +
|
| + if (status)
|
| + return status;
|
| + } else if (id == 0x037E) { // ChapterCountry ID
|
| + status = UnserializeString(pReader, pos, size, m_country);
|
| +
|
| + if (status)
|
| + return status;
|
| + }
|
| +
|
| + pos += size;
|
| + assert(pos <= stop);
|
| + }
|
| +
|
| + assert(pos == stop);
|
| + return 0;
|
| +}
|
| +
|
| +Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size,
|
| + long long element_start, long long element_size)
|
| + : m_pSegment(pSegment),
|
| + m_start(payload_start),
|
| + m_size(payload_size),
|
| + m_element_start(element_start),
|
| + m_element_size(element_size),
|
| + m_tags(NULL),
|
| + m_tags_size(0),
|
| + m_tags_count(0) {}
|
| +
|
| +Tags::~Tags() {
|
| + while (m_tags_count > 0) {
|
| + Tag& t = m_tags[--m_tags_count];
|
| + t.Clear();
|
| + }
|
| + delete[] m_tags;
|
| +}
|
| +
|
| +long Tags::Parse() {
|
| + IMkvReader* const pReader = m_pSegment->m_pReader;
|
| +
|
| + long long pos = m_start; // payload start
|
| + const long long stop = pos + m_size; // payload stop
|
| +
|
| + while (pos < stop) {
|
| + long long id, size;
|
| +
|
| + long status = ParseElementHeader(pReader, pos, stop, id, size);
|
| +
|
| + if (status < 0)
|
| + return status;
|
| +
|
| + if (size == 0) // 0 length tag, read another
|
| + continue;
|
| +
|
| + if (id == 0x3373) { // Tag ID
|
| + status = ParseTag(pos, size);
|
| +
|
| + if (status < 0)
|
| + return status;
|
| + }
|
| +
|
| + pos += size;
|
| + assert(pos <= stop);
|
| + if (pos > stop)
|
| + return -1;
|
| + }
|
| +
|
| + assert(pos == stop);
|
| + if (pos != stop)
|
| + return -1;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int Tags::GetTagCount() const { return m_tags_count; }
|
| +
|
| +const Tags::Tag* Tags::GetTag(int idx) const {
|
| + if (idx < 0)
|
| + return NULL;
|
| +
|
| + if (idx >= m_tags_count)
|
| + return NULL;
|
| +
|
| + return m_tags + idx;
|
| +}
|
| +
|
| +bool Tags::ExpandTagsArray() {
|
| + if (m_tags_size > m_tags_count)
|
| + return true; // nothing else to do
|
| +
|
| + const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
|
| +
|
| + Tag* const tags = new (std::nothrow) Tag[size];
|
| +
|
| + if (tags == NULL)
|
| + return false;
|
| +
|
| + for (int idx = 0; idx < m_tags_count; ++idx) {
|
| + m_tags[idx].ShallowCopy(tags[idx]);
|
| + }
|
| +
|
| + delete[] m_tags;
|
| + m_tags = tags;
|
| +
|
| + m_tags_size = size;
|
| + return true;
|
| +}
|
| +
|
| +long Tags::ParseTag(long long pos, long long size) {
|
| + if (!ExpandTagsArray())
|
| + return -1;
|
| +
|
| + Tag& t = m_tags[m_tags_count++];
|
| + t.Init();
|
| +
|
| + return t.Parse(m_pSegment->m_pReader, pos, size);
|
| +}
|
| +
|
| +Tags::Tag::Tag() {}
|
| +
|
| +Tags::Tag::~Tag() {}
|
| +
|
| +int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
|
| +
|
| +const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
|
| + if (index < 0)
|
| + return NULL;
|
| +
|
| + if (index >= m_simple_tags_count)
|
| + return NULL;
|
| +
|
| + return m_simple_tags + index;
|
| +}
|
| +
|
| +void Tags::Tag::Init() {
|
| + m_simple_tags = NULL;
|
| + m_simple_tags_size = 0;
|
| + m_simple_tags_count = 0;
|
| +}
|
| +
|
| +void Tags::Tag::ShallowCopy(Tag& rhs) const {
|
| + rhs.m_simple_tags = m_simple_tags;
|
| + rhs.m_simple_tags_size = m_simple_tags_size;
|
| + rhs.m_simple_tags_count = m_simple_tags_count;
|
| +}
|
| +
|
| +void Tags::Tag::Clear() {
|
| + while (m_simple_tags_count > 0) {
|
| + SimpleTag& d = m_simple_tags[--m_simple_tags_count];
|
| + d.Clear();
|
| + }
|
| +
|
| + delete[] m_simple_tags;
|
| + m_simple_tags = NULL;
|
| +
|
| + m_simple_tags_size = 0;
|
| +}
|
| +
|
| +long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) {
|
| + const long long stop = pos + size;
|
| +
|
| + while (pos < stop) {
|
| + long long id, size;
|
| +
|
| + long status = ParseElementHeader(pReader, pos, stop, id, size);
|
| +
|
| + if (status < 0)
|
| + return status;
|
| +
|
| + if (size == 0) // 0 length tag, read another
|
| + continue;
|
| +
|
| + if (id == 0x27C8) { // SimpleTag ID
|
| + status = ParseSimpleTag(pReader, pos, size);
|
| +
|
| + if (status < 0)
|
| + return status;
|
| + }
|
| +
|
| + pos += size;
|
| + assert(pos <= stop);
|
| + if (pos > stop)
|
| + return -1;
|
| + }
|
|
|
| - return result;
|
| + assert(pos == stop);
|
| + if (pos != stop)
|
| + return -1;
|
| + return 0;
|
| }
|
|
|
| -long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
|
| - long long size) {
|
| - if (!ExpandDisplaysArray())
|
| +long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
|
| + long long size) {
|
| + if (!ExpandSimpleTagsArray())
|
| return -1;
|
|
|
| - Display& d = m_displays[m_displays_count++];
|
| - d.Init();
|
| + SimpleTag& st = m_simple_tags[m_simple_tags_count++];
|
| + st.Init();
|
|
|
| - return d.Parse(pReader, pos, size);
|
| + return st.Parse(pReader, pos, size);
|
| }
|
|
|
| -bool Chapters::Atom::ExpandDisplaysArray() {
|
| - if (m_displays_size > m_displays_count)
|
| +bool Tags::Tag::ExpandSimpleTagsArray() {
|
| + if (m_simple_tags_size > m_simple_tags_count)
|
| return true; // nothing else to do
|
|
|
| - const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
|
| + const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
|
|
|
| - Display* const displays = new (std::nothrow) Display[size];
|
| + SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
|
|
|
| if (displays == NULL)
|
| return false;
|
|
|
| - for (int idx = 0; idx < m_displays_count; ++idx) {
|
| - m_displays[idx].ShallowCopy(displays[idx]);
|
| + for (int idx = 0; idx < m_simple_tags_count; ++idx) {
|
| + m_simple_tags[idx].ShallowCopy(displays[idx]);
|
| }
|
|
|
| - delete[] m_displays;
|
| - m_displays = displays;
|
| + delete[] m_simple_tags;
|
| + m_simple_tags = displays;
|
|
|
| - m_displays_size = size;
|
| + m_simple_tags_size = size;
|
| return true;
|
| }
|
|
|
| -Chapters::Display::Display() {}
|
| -
|
| -Chapters::Display::~Display() {}
|
| +Tags::SimpleTag::SimpleTag() {}
|
|
|
| -const char* Chapters::Display::GetString() const { return m_string; }
|
| +Tags::SimpleTag::~SimpleTag() {}
|
|
|
| -const char* Chapters::Display::GetLanguage() const { return m_language; }
|
| +const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
|
|
|
| -const char* Chapters::Display::GetCountry() const { return m_country; }
|
| +const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
|
|
|
| -void Chapters::Display::Init() {
|
| - m_string = NULL;
|
| - m_language = NULL;
|
| - m_country = NULL;
|
| +void Tags::SimpleTag::Init() {
|
| + m_tag_name = NULL;
|
| + m_tag_string = NULL;
|
| }
|
|
|
| -void Chapters::Display::ShallowCopy(Display& rhs) const {
|
| - rhs.m_string = m_string;
|
| - rhs.m_language = m_language;
|
| - rhs.m_country = m_country;
|
| +void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
|
| + rhs.m_tag_name = m_tag_name;
|
| + rhs.m_tag_string = m_tag_string;
|
| }
|
|
|
| -void Chapters::Display::Clear() {
|
| - delete[] m_string;
|
| - m_string = NULL;
|
| -
|
| - delete[] m_language;
|
| - m_language = NULL;
|
| +void Tags::SimpleTag::Clear() {
|
| + delete[] m_tag_name;
|
| + m_tag_name = NULL;
|
|
|
| - delete[] m_country;
|
| - m_country = NULL;
|
| + delete[] m_tag_string;
|
| + m_tag_string = NULL;
|
| }
|
|
|
| -long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
|
| - long long size) {
|
| +long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos,
|
| + long long size) {
|
| const long long stop = pos + size;
|
|
|
| while (pos < stop) {
|
| @@ -4267,18 +3799,13 @@ long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
|
| if (size == 0) // weird
|
| continue;
|
|
|
| - if (id == 0x05) { // ChapterString ID
|
| - status = UnserializeString(pReader, pos, size, m_string);
|
| -
|
| - if (status)
|
| - return status;
|
| - } else if (id == 0x037C) { // ChapterLanguage ID
|
| - status = UnserializeString(pReader, pos, size, m_language);
|
| + if (id == 0x5A3) { // TagName ID
|
| + status = UnserializeString(pReader, pos, size, m_tag_name);
|
|
|
| if (status)
|
| return status;
|
| - } else if (id == 0x037E) { // ChapterCountry ID
|
| - status = UnserializeString(pReader, pos, size, m_country);
|
| + } else if (id == 0x487) { // TagString ID
|
| + status = UnserializeString(pReader, pos, size, m_tag_string);
|
|
|
| if (status)
|
| return status;
|
| @@ -4286,9 +3813,13 @@ long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
|
|
|
| pos += size;
|
| assert(pos <= stop);
|
| + if (pos > stop)
|
| + return -1;
|
| }
|
|
|
| assert(pos == stop);
|
| + if (pos != stop)
|
| + return -1;
|
| return 0;
|
| }
|
|
|
| @@ -4458,7 +3989,7 @@ ContentEncoding::~ContentEncoding() {
|
| }
|
|
|
| const ContentEncoding::ContentCompression*
|
| -ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
|
| + ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
|
| const ptrdiff_t count = compression_entries_end_ - compression_entries_;
|
| assert(count >= 0);
|
|
|
| @@ -4554,7 +4085,7 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
|
|
|
| if (compression_count > 0) {
|
| compression_entries_ =
|
| - new (std::nothrow) ContentCompression* [compression_count];
|
| + new (std::nothrow) ContentCompression*[compression_count];
|
| if (!compression_entries_)
|
| return -1;
|
| compression_entries_end_ = compression_entries_;
|
| @@ -4562,7 +4093,7 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
|
|
|
| if (encryption_count > 0) {
|
| encryption_entries_ =
|
| - new (std::nothrow) ContentEncryption* [encryption_count];
|
| + new (std::nothrow) ContentEncryption*[encryption_count];
|
| if (!encryption_entries_) {
|
| delete[] compression_entries_;
|
| return -1;
|
| @@ -4703,7 +4234,7 @@ long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
|
| return E_FILE_FORMAT_INVALID;
|
| } else if (id == 0x7E2) {
|
| // ContentEncKeyID
|
| - delete[] encryption -> key_id;
|
| + delete[] encryption->key_id;
|
| encryption->key_id = NULL;
|
| encryption->key_id_len = 0;
|
|
|
| @@ -4727,7 +4258,7 @@ long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
|
| encryption->key_id_len = buflen;
|
| } else if (id == 0x7E3) {
|
| // ContentSignature
|
| - delete[] encryption -> signature;
|
| + delete[] encryption->signature;
|
| encryption->signature = NULL;
|
| encryption->signature_len = 0;
|
|
|
| @@ -4751,7 +4282,7 @@ long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
|
| encryption->signature_len = buflen;
|
| } else if (id == 0x7E4) {
|
| // ContentSigKeyID
|
| - delete[] encryption -> sig_key_id;
|
| + delete[] encryption->sig_key_id;
|
| encryption->sig_key_id = NULL;
|
| encryption->sig_key_id_len = 0;
|
|
|
| @@ -4991,17 +4522,10 @@ long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
|
| }
|
|
|
| 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;
|
| @@ -5098,18 +4622,10 @@ long Track::GetNext(const BlockEntry* pCurrEntry,
|
| }
|
|
|
| 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
|
| @@ -5291,7 +4807,7 @@ long Track::ParseContentEncodingsEntry(long long start, long long size) {
|
| if (count <= 0)
|
| return -1;
|
|
|
| - content_encoding_entries_ = new (std::nothrow) ContentEncoding* [count];
|
| + content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
|
| if (!content_encoding_entries_)
|
| return -1;
|
|
|
| @@ -5350,6 +4866,11 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
|
|
|
| long long width = 0;
|
| long long height = 0;
|
| + long long display_width = 0;
|
| + long long display_height = 0;
|
| + long long display_unit = 0;
|
| + long long stereo_mode = 0;
|
| +
|
| double rate = 0.0;
|
|
|
| IMkvReader* const pReader = pSegment->m_pReader;
|
| @@ -5381,6 +4902,26 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
|
|
|
| if (height <= 0)
|
| return E_FILE_FORMAT_INVALID;
|
| + } else if (id == 0x14B0) { // display width
|
| + display_width = UnserializeUInt(pReader, pos, size);
|
| +
|
| + if (display_width <= 0)
|
| + return E_FILE_FORMAT_INVALID;
|
| + } else if (id == 0x14BA) { // display height
|
| + display_height = UnserializeUInt(pReader, pos, size);
|
| +
|
| + if (display_height <= 0)
|
| + return E_FILE_FORMAT_INVALID;
|
| + } else if (id == 0x14B2) { // display unit
|
| + display_unit = UnserializeUInt(pReader, pos, size);
|
| +
|
| + if (display_unit < 0)
|
| + return E_FILE_FORMAT_INVALID;
|
| + } else if (id == 0x13B8) { // stereo mode
|
| + stereo_mode = UnserializeUInt(pReader, pos, size);
|
| +
|
| + if (stereo_mode < 0)
|
| + return E_FILE_FORMAT_INVALID;
|
| } else if (id == 0x0383E3) { // frame rate
|
| const long status = UnserializeFloat(pReader, pos, size, rate);
|
|
|
| @@ -5412,6 +4953,10 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
|
|
|
| pTrack->m_width = width;
|
| pTrack->m_height = height;
|
| + pTrack->m_display_width = display_width;
|
| + pTrack->m_display_height = display_height;
|
| + pTrack->m_display_unit = display_unit;
|
| + pTrack->m_stereo_mode = stereo_mode;
|
| pTrack->m_rate = rate;
|
|
|
| pResult = pTrack;
|
| @@ -5498,16 +5043,7 @@ long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
|
| 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;
|
| @@ -5524,6 +5060,18 @@ long long VideoTrack::GetWidth() const { return m_width; }
|
|
|
| long long VideoTrack::GetHeight() const { return m_height; }
|
|
|
| +long long VideoTrack::GetDisplayWidth() const {
|
| + return m_display_width > 0 ? m_display_width : GetWidth();
|
| +}
|
| +
|
| +long long VideoTrack::GetDisplayHeight() const {
|
| + return m_display_height > 0 ? m_display_height : GetHeight();
|
| +}
|
| +
|
| +long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
|
| +
|
| +long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
|
| +
|
| double VideoTrack::GetFrameRate() const { return m_rate; }
|
|
|
| AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
|
| @@ -5658,7 +5206,7 @@ long Tracks::Parse() {
|
| if (count <= 0)
|
| return 0; // success
|
|
|
| - m_trackEntries = new (std::nothrow) Track* [count];
|
| + m_trackEntries = new (std::nothrow) Track*[count];
|
|
|
| if (m_trackEntries == NULL)
|
| return -1;
|
| @@ -5941,7 +5489,7 @@ long Tracks::ParseTrackEntry(long long track_start, long long track_size,
|
| if (v.start >= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| - if (e.start >= 0)
|
| + if (info.type == Track::kMetadata && e.start >= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| info.settings.start = -1;
|
| @@ -6003,25 +5551,6 @@ const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
|
| return m_trackEntries[idx];
|
| }
|
|
|
| -#if 0
|
| -long long Cluster::Unparsed() const
|
| -{
|
| - if (m_timecode < 0) //not even partially loaded
|
| - return LLONG_MAX;
|
| -
|
| - assert(m_pos >= m_element_start);
|
| - //assert(m_element_size > m_size);
|
| -
|
| - const long long element_stop = m_element_start + m_element_size;
|
| - assert(m_pos <= element_stop);
|
| -
|
| - const long long result = element_stop - m_pos;
|
| - assert(result >= 0);
|
| -
|
| - return result;
|
| -}
|
| -#endif
|
| -
|
| long Cluster::Load(long long& pos, long& len) const {
|
| assert(m_pSegment);
|
| assert(m_pos >= m_element_start);
|
| @@ -6115,15 +5644,7 @@ long Cluster::Load(long long& pos, long& len) const {
|
| cluster_size = size;
|
| }
|
|
|
| -// pos points to start of payload
|
| -
|
| -#if 0
|
| - len = static_cast<long>(size_);
|
| -
|
| - if (cluster_stop > avail)
|
| - return E_BUFFER_NOT_FULL;
|
| -#endif
|
| -
|
| + // pos points to start of payload
|
| long long timecode = -1;
|
| long long new_pos = -1;
|
| bool bBlock = false;
|
| @@ -6495,36 +6016,6 @@ long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
|
| 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);
|
| -
|
| - const long tn = static_cast<long>(track);
|
| -
|
| - const Track* const pTrack = pTracks->GetTrackByNumber(tn);
|
| -
|
| - if (pTrack == NULL)
|
| - return E_FILE_FORMAT_INVALID;
|
| -#endif
|
| -
|
| pos += len; // consume track number
|
|
|
| if ((pos + 2) > block_stop)
|
| @@ -6679,12 +6170,7 @@ long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| if (id == 0x35A2) { // DiscardPadding
|
| - result = GetUIntLength(pReader, pos, len);
|
| -
|
| - if (result < 0) // error
|
| - return static_cast<long>(result);
|
| -
|
| - status = UnserializeInt(pReader, pos, len, discard_padding);
|
| + status = UnserializeInt(pReader, pos, size, discard_padding);
|
|
|
| if (status < 0) // error
|
| return status;
|
| @@ -6733,36 +6219,6 @@ long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
|
| 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);
|
| -
|
| - const long tn = static_cast<long>(track);
|
| -
|
| - const Track* const pTrack = pTracks->GetTrackByNumber(tn);
|
| -
|
| - if (pTrack == NULL)
|
| - return E_FILE_FORMAT_INVALID;
|
| -#endif
|
| -
|
| pos += len; // consume track number
|
|
|
| if ((pos + 2) > block_stop)
|
| @@ -6924,68 +6380,6 @@ long long Cluster::GetPosition() const {
|
|
|
| long long Cluster::GetElementSize() const { return m_element_size; }
|
|
|
| -#if 0
|
| -bool Cluster::HasBlockEntries(
|
| - const Segment* pSegment,
|
| - long long off) {
|
| - assert(pSegment);
|
| - assert(off >= 0); //relative to start of segment payload
|
| -
|
| - IMkvReader* const pReader = pSegment->m_pReader;
|
| -
|
| - long long pos = pSegment->m_start + off; //absolute
|
| - long long size;
|
| -
|
| - {
|
| - long len;
|
| -
|
| - const long long id = ReadUInt(pReader, pos, len);
|
| - (void)id;
|
| - assert(id >= 0);
|
| - assert(id == 0x0F43B675); //Cluster ID
|
| -
|
| - pos += len; //consume id
|
| -
|
| - size = ReadUInt(pReader, pos, len);
|
| - assert(size > 0);
|
| -
|
| - pos += len; //consume size
|
| -
|
| - //pos now points to start of payload
|
| - }
|
| -
|
| - const long long stop = pos + size;
|
| -
|
| - 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); //TODO
|
| - assert((pos + len) <= stop);
|
| -
|
| - pos += len; //consume size
|
| -
|
| - if (id == 0x20) //BlockGroup ID
|
| - return true;
|
| -
|
| - if (id == 0x23) //SimpleBlock ID
|
| - return true;
|
| -
|
| - pos += size; //consume payload
|
| - assert(pos <= stop);
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -#endif
|
| -
|
| long Cluster::HasBlockEntries(
|
| const Segment* pSegment,
|
| long long off, // relative to start of segment payload
|
| @@ -7269,7 +6663,7 @@ long Cluster::CreateBlock(long long id,
|
| assert(m_entries_size == 0);
|
|
|
| m_entries_size = 1024;
|
| - m_entries = new BlockEntry* [m_entries_size];
|
| + m_entries = new BlockEntry*[m_entries_size];
|
|
|
| m_entries_count = 0;
|
| } else {
|
| @@ -7280,7 +6674,7 @@ long Cluster::CreateBlock(long long id,
|
| if (m_entries_count >= m_entries_size) {
|
| const long entries_size = 2 * m_entries_size;
|
|
|
| - BlockEntry** const entries = new BlockEntry* [entries_size];
|
| + BlockEntry** const entries = new BlockEntry*[entries_size];
|
| assert(entries);
|
|
|
| BlockEntry** src = m_entries;
|
| @@ -7349,12 +6743,16 @@ long Cluster::CreateBlockGroup(long long start_offset, long long size,
|
| bsize = size;
|
| }
|
| } else if (id == 0x1B) { // Duration ID
|
| - assert(size <= 8);
|
| + if (size > 8)
|
| + return E_FILE_FORMAT_INVALID;
|
|
|
| duration = UnserializeUInt(pReader, pos, size);
|
| - assert(duration >= 0); // TODO
|
| +
|
| + if (duration < 0)
|
| + return E_FILE_FORMAT_INVALID;
|
| } else if (id == 0x7B) { // ReferenceBlock
|
| - assert(size <= 8);
|
| + if (size > 8 || size <= 0)
|
| + return E_FILE_FORMAT_INVALID;
|
| const long size_ = static_cast<long>(size);
|
|
|
| long long time;
|
| @@ -7373,9 +6771,10 @@ long Cluster::CreateBlockGroup(long long start_offset, long long size,
|
| pos += size; // consume payload
|
| assert(pos <= stop);
|
| }
|
| + if (bpos < 0)
|
| + return E_FILE_FORMAT_INVALID;
|
|
|
| assert(pos == stop);
|
| - assert(bpos >= 0);
|
| assert(bsize >= 0);
|
|
|
| const long idx = m_entries_count;
|
| @@ -7539,57 +6938,6 @@ const BlockEntry* Cluster::GetEntry(const Track* pTrack,
|
| if (m_pSegment == NULL) // this is the special EOS cluster
|
| return pTrack->GetEOS();
|
|
|
| -#if 0
|
| -
|
| - LoadBlockEntries();
|
| -
|
| - if ((m_entries == NULL) || (m_entries_count <= 0))
|
| - return NULL; //return EOS here?
|
| -
|
| - const BlockEntry* pResult = pTrack->GetEOS();
|
| -
|
| - BlockEntry** i = m_entries;
|
| - assert(i);
|
| -
|
| - BlockEntry** const j = i + m_entries_count;
|
| -
|
| - while (i != j)
|
| - {
|
| - const BlockEntry* const pEntry = *i++;
|
| - assert(pEntry);
|
| - assert(!pEntry->EOS());
|
| -
|
| - const Block* const pBlock = pEntry->GetBlock();
|
| - assert(pBlock);
|
| -
|
| - if (pBlock->GetTrackNumber() != pTrack->GetNumber())
|
| - 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)
|
| - break;
|
| -
|
| - pResult = pEntry;
|
| - }
|
| - else if (time_ns >= 0)
|
| - {
|
| - const long long ns = pBlock->GetTime(this);
|
| -
|
| - if (ns > time_ns)
|
| - break;
|
| - }
|
| - }
|
| -
|
| - return pResult;
|
| -
|
| -#else
|
| -
|
| const BlockEntry* pResult = pTrack->GetEOS();
|
|
|
| long index = 0;
|
| @@ -7643,103 +6991,11 @@ const BlockEntry* Cluster::GetEntry(const Track* pTrack,
|
|
|
| ++index;
|
| }
|
| -
|
| -#endif
|
| }
|
|
|
| const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
|
| const CuePoint::TrackPosition& tp) const {
|
| assert(m_pSegment);
|
| -
|
| -#if 0
|
| -
|
| - LoadBlockEntries();
|
| -
|
| - if (m_entries == NULL)
|
| - return NULL;
|
| -
|
| - const long long count = m_entries_count;
|
| -
|
| - if (count <= 0)
|
| - return NULL;
|
| -
|
| - const long long tc = cp.GetTimeCode();
|
| -
|
| - if ((tp.m_block > 0) && (tp.m_block <= count))
|
| - {
|
| - const size_t block = static_cast<size_t>(tp.m_block);
|
| - const size_t index = block - 1;
|
| -
|
| - 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;
|
| - }
|
| - }
|
| -
|
| - const BlockEntry* const* i = m_entries;
|
| - const BlockEntry* const* const j = i + count;
|
| -
|
| - while (i != j)
|
| - {
|
| -#ifdef _DEBUG
|
| - const ptrdiff_t idx = i - m_entries;
|
| - idx;
|
| -#endif
|
| -
|
| - const BlockEntry* const pEntry = *i++;
|
| - assert(pEntry);
|
| - assert(!pEntry->EOS());
|
| -
|
| - const Block* const pBlock = pEntry->GetBlock();
|
| - assert(pBlock);
|
| -
|
| - if (pBlock->GetTrackNumber() != tp.m_track)
|
| - continue;
|
| -
|
| - const long long tc_ = pBlock->GetTimeCode(this);
|
| - assert(tc_ >= 0);
|
| -
|
| - if (tc_ < tc)
|
| - 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;
|
| - }
|
| -
|
| - return NULL;
|
| -
|
| -#else
|
| -
|
| const long long tc = cp.GetTimeCode();
|
|
|
| if (tp.m_block > 0) {
|
| @@ -7835,54 +7091,12 @@ const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
|
|
|
| return pEntry;
|
| }
|
| -
|
| -#endif
|
| -}
|
| -
|
| -#if 0
|
| -const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack) const
|
| -{
|
| - assert(pTrack);
|
| -
|
| - if (m_pSegment == NULL) //EOS
|
| - return pTrack->GetEOS();
|
| -
|
| - LoadBlockEntries();
|
| -
|
| - if ((m_entries == NULL) || (m_entries_count <= 0))
|
| - return pTrack->GetEOS();
|
| -
|
| - BlockEntry** i = m_entries + m_entries_count;
|
| - BlockEntry** const j = m_entries;
|
| -
|
| - while (i != j)
|
| - {
|
| - const BlockEntry* const pEntry = *--i;
|
| - assert(pEntry);
|
| - assert(!pEntry->EOS());
|
| -
|
| - const Block* const pBlock = pEntry->GetBlock();
|
| - assert(pBlock);
|
| -
|
| - if (pBlock->GetTrackNumber() != pTrack->GetNumber())
|
| - continue;
|
| -
|
| - if (pBlock->IsKey())
|
| - return pEntry;
|
| - }
|
| -
|
| - return pTrack->GetEOS(); //no satisfactory block found
|
| }
|
| -#endif
|
|
|
| BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
|
| -
|
| BlockEntry::~BlockEntry() {}
|
| -
|
| bool BlockEntry::EOS() const { return (GetKind() == kBlockEOS); }
|
| -
|
| const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
|
| -
|
| long BlockEntry::GetIndex() const { return m_index; }
|
|
|
| SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
|
| @@ -7890,9 +7104,7 @@ SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
|
| : BlockEntry(pCluster, idx), m_block(start, size, 0) {}
|
|
|
| long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
|
| -
|
| BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
|
| -
|
| const Block* SimpleBlock::GetBlock() const { return &m_block; }
|
|
|
| BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
|
| @@ -7915,30 +7127,10 @@ long BlockGroup::Parse() {
|
| return 0;
|
| }
|
|
|
| -#if 0
|
| -void BlockGroup::ParseBlock(long long start, long long size)
|
| -{
|
| - IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
|
| -
|
| - Block* const pBlock = new Block(start, size, pReader);
|
| - assert(pBlock); //TODO
|
| -
|
| - //TODO: the Matroska spec says you have multiple blocks within the
|
| - //same block group, with blocks ranked by priority (the flag bits).
|
| -
|
| - assert(m_pBlock == NULL);
|
| - m_pBlock = pBlock;
|
| -}
|
| -#endif
|
| -
|
| BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
|
| -
|
| const Block* BlockGroup::GetBlock() const { return &m_block; }
|
| -
|
| long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
|
| -
|
| long long BlockGroup::GetNextTimeCode() const { return m_next; }
|
| -
|
| long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
|
|
|
| Block::Block(long long start, long long size_, long long discard_padding)
|
| @@ -8028,7 +7220,7 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| const long long frame_size = stop - pos;
|
|
|
| - if (frame_size > LONG_MAX)
|
| + if (frame_size > LONG_MAX || frame_size <= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| f.len = static_cast<long>(frame_size);
|
| @@ -8088,6 +7280,9 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| f.pos = 0; // patch later
|
|
|
| + if (frame_size <= 0)
|
| + return E_FILE_FORMAT_INVALID;
|
| +
|
| f.len = frame_size;
|
| size += frame_size; // contribution of this frame
|
|
|
| @@ -8112,7 +7307,7 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| const long long frame_size = total_size - size;
|
|
|
| - if (frame_size > LONG_MAX)
|
| + if (frame_size > LONG_MAX || frame_size <= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| f.len = static_cast<long>(frame_size);
|
| @@ -8129,6 +7324,9 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| assert(pos == stop);
|
| } else if (lacing == 2) { // fixed-size lacing
|
| + if (pos >= stop)
|
| + return E_FILE_FORMAT_INVALID;
|
| +
|
| const long long total_size = stop - pos;
|
|
|
| if ((total_size % m_frame_count) != 0)
|
| @@ -8136,7 +7334,7 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| const long long frame_size = total_size / m_frame_count;
|
|
|
| - if (frame_size > LONG_MAX)
|
| + if (frame_size > LONG_MAX || frame_size <= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| Frame* pf = m_frames;
|
| @@ -8165,7 +7363,7 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| long long frame_size = ReadUInt(pReader, pos, len);
|
|
|
| - if (frame_size < 0)
|
| + if (frame_size <= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| if (frame_size > LONG_MAX)
|
| @@ -8227,7 +7425,7 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| frame_size += delta_size;
|
|
|
| - if (frame_size < 0)
|
| + if (frame_size <= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| if (frame_size > LONG_MAX)
|
| @@ -8239,7 +7437,8 @@ long Block::Parse(const Cluster* pCluster) {
|
| --frame_count;
|
| }
|
|
|
| - {
|
| + // parse last frame
|
| + if (frame_count > 0) {
|
| assert(pos <= stop);
|
| assert(pf < pf_end);
|
|
|
| @@ -8262,7 +7461,7 @@ long Block::Parse(const Cluster* pCluster) {
|
|
|
| frame_size = total_size - size;
|
|
|
| - if (frame_size > LONG_MAX)
|
| + if (frame_size > LONG_MAX || frame_size <= 0)
|
| return E_FILE_FORMAT_INVALID;
|
|
|
| curr.len = static_cast<long>(frame_size);
|
| @@ -8277,7 +7476,8 @@ long Block::Parse(const Cluster* pCluster) {
|
| pos += f.len;
|
| }
|
|
|
| - assert(pos == stop);
|
| + if (pos != stop)
|
| + return E_FILE_FORMAT_INVALID;
|
| }
|
|
|
| return 0; // success
|
|
|