/external/libvpx/mkvparser/mkvparser.cpp
https://gitlab.com/brian0218/rk3188_r-box_android4.2.2_sdk · C++ · 2062 lines · 1365 code · 581 blank · 116 comment · 388 complexity · 60d2d32c0653bab330da450f1efaa4e7 MD5 · raw file
- // Copyright (c) 2010 The WebM project authors. All Rights Reserved.
- //
- // Use of this source code is governed by a BSD-style license
- // that can be found in the LICENSE file in the root of the source
- // tree. An additional intellectual property rights grant can be found
- // in the file PATENTS. All contributing project authors may
- // be found in the AUTHORS file in the root of the source tree.
-
- #include "mkvparser.hpp"
- #include <cassert>
- #include <cstring>
- #include <new>
- #include <climits>
-
- mkvparser::IMkvReader::~IMkvReader()
- {
- }
-
- void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision)
- {
- major = 1;
- minor = 0;
- build = 0;
- revision = 24;
- }
-
- long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
- {
- assert(pReader);
- assert(pos >= 0);
-
- int status;
-
- //#ifdef _DEBUG
- // long long total, available;
- // status = pReader->Length(&total, &available);
- // assert(status >= 0);
- // assert((total < 0) || (available <= total));
- // assert(pos < available);
- // assert((available - pos) >= 1); //assume here max u-int len is 8
- //#endif
-
- len = 1;
-
- unsigned char b;
-
- status = pReader->Read(pos, 1, &b);
-
- if (status < 0) //error or underflow
- return status;
-
- if (status > 0) //interpreted as "underflow"
- return E_BUFFER_NOT_FULL;
-
- if (b == 0) //we can't handle u-int values larger than 8 bytes
- return E_FILE_FORMAT_INVALID;
-
- unsigned char m = 0x80;
-
- while (!(b & m))
- {
- m >>= 1;
- ++len;
- }
-
- //#ifdef _DEBUG
- // assert((available - pos) >= len);
- //#endif
-
- long long result = b & (~m);
- ++pos;
-
- for (int i = 1; i < len; ++i)
- {
- status = pReader->Read(pos, 1, &b);
-
- if (status < 0)
- {
- len = 1;
- return status;
- }
-
- if (status > 0)
- {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
- }
-
- long long mkvparser::GetUIntLength(
- IMkvReader* pReader,
- long long pos,
- long& len)
- {
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- int status = pReader->Length(&total, &available);
- assert(status >= 0);
- assert((total < 0) || (available <= total));
-
- len = 1;
-
- if (pos >= available)
- return pos; //too few bytes available
-
- unsigned char b;
-
- status = pReader->Read(pos, 1, &b);
-
- if (status < 0)
- return status;
-
- assert(status == 0);
-
- if (b == 0) //we can't handle u-int values larger than 8 bytes
- return E_FILE_FORMAT_INVALID;
-
- unsigned char m = 0x80;
-
- while (!(b & m))
- {
- m >>= 1;
- ++len;
- }
-
- return 0; //success
- }
-
-
- long long mkvparser::UnserializeUInt(
- IMkvReader* pReader,
- long long pos,
- long long size)
- {
- assert(pReader);
- assert(pos >= 0);
-
- if ((size <= 0) || (size > 8))
- return E_FILE_FORMAT_INVALID;
-
- long long result = 0;
-
- for (long long i = 0; i < size; ++i)
- {
- unsigned char b;
-
- const long status = pReader->Read(pos, 1, &b);
-
- if (status < 0)
- return status;
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
- }
-
-
- long mkvparser::UnserializeFloat(
- IMkvReader* pReader,
- long long pos,
- long long size_,
- double& result)
- {
- assert(pReader);
- assert(pos >= 0);
-
- if ((size_ != 4) && (size_ != 8))
- return E_FILE_FORMAT_INVALID;
-
- 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;
- }
- else
- {
- assert(size == 8);
-
- union
- {
- double d;
- unsigned long long dd;
- };
-
- dd = 0;
-
- for (int i = 0;;)
- {
- dd |= buf[i];
-
- if (++i >= 8)
- break;
-
- dd <<= 8;
- }
-
- result = d;
- }
-
- return 0;
- }
-
-
- long mkvparser::UnserializeInt(
- IMkvReader* pReader,
- long long pos,
- long size,
- long long& result)
- {
- assert(pReader);
- assert(pos >= 0);
- assert(size > 0);
- assert(size <= 8);
-
- {
- signed char b;
-
- const long status = pReader->Read(pos, 1, (unsigned char*)&b);
-
- if (status < 0)
- return status;
-
- result = b;
-
- ++pos;
- }
-
- for (long i = 1; i < size; ++i)
- {
- unsigned char b;
-
- const long status = pReader->Read(pos, 1, &b);
-
- if (status < 0)
- return status;
-
- result <<= 8;
- result |= b;
-
- ++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
- }
-
-
- bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- long long& 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(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(size <= 8);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- pos += len; //consume length of size of payload
-
- val = UnserializeUInt(pReader, pos, size);
- assert(val >= 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)
- {
- 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 long buflen_ = static_cast<long>(size_);
-
- buf = new (std::nothrow) unsigned char[buflen_];
- assert(buf); //TODO
-
- status = pReader->Read(pos, buflen_, buf);
- assert(status == 0); //TODO
-
- buflen = buflen_;
-
- pos += size_; //consume size of payload
- return true;
- }
-
-
- namespace mkvparser
- {
-
- EBMLHeader::EBMLHeader() :
- m_docType(NULL)
- {
- Init();
- }
-
- EBMLHeader::~EBMLHeader()
- {
- delete[] m_docType;
- }
-
- void EBMLHeader::Init()
- {
- m_version = 1;
- m_readVersion = 1;
- m_maxIdLength = 4;
- m_maxSizeLength = 8;
-
- if (m_docType)
- {
- delete[] m_docType;
- m_docType = NULL;
- }
-
- m_docTypeVersion = 1;
- m_docTypeReadVersion = 1;
- }
-
- long long EBMLHeader::Parse(
- IMkvReader* pReader,
- long long& pos)
- {
- assert(pReader);
-
- long long total, available;
-
- long status = pReader->Length(&total, &available);
-
- if (status < 0) //error
- return status;
-
- pos = 0;
- long long end = (available >= 1024) ? 1024 : available;
-
- for (;;)
- {
- unsigned char b = 0;
-
- while (pos < end)
- {
- status = pReader->Read(pos, 1, &b);
-
- if (status < 0) //error
- return status;
-
- if (b == 0x1A)
- break;
-
- ++pos;
- }
-
- if (b != 0x1A)
- {
- if (pos >= 1024)
- return E_FILE_FORMAT_INVALID; //don't bother looking anymore
-
- if ((total >= 0) && ((total - available) < 5))
- return E_FILE_FORMAT_INVALID;
-
- return available + 5; //5 = 4-byte ID + 1st byte of size
- }
-
- if ((total >= 0) && ((total - pos) < 5))
- return E_FILE_FORMAT_INVALID;
-
- if ((available - pos) < 5)
- return pos + 5; //try again later
-
- long len;
-
- const long long result = ReadUInt(pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- if (result == 0x0A45DFA3) //EBML Header ID
- {
- pos += len; //consume ID
- break;
- }
-
- ++pos; //throw away just the 0x1A byte, and try again
- }
-
- //pos designates start of size field
-
- //get length of size field
-
- long len;
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- if (result > 0) //need more data
- return result;
-
- assert(len > 0);
- assert(len <= 8);
-
- if ((total >= 0) && ((total - pos) < len))
- return E_FILE_FORMAT_INVALID;
-
- if ((available - pos) < len)
- return pos + len; //try again later
-
- //get the EBML header size
-
- result = ReadUInt(pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- pos += len; //consume size field
-
- //pos now designates start of payload
-
- if ((total >= 0) && ((total - pos) < result))
- return E_FILE_FORMAT_INVALID;
-
- if ((available - pos) < result)
- return pos + result;
-
- end = pos + result;
-
- Init();
-
- while (pos < end)
- {
- 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
- {
- m_version = UnserializeUInt(pReader, pos, size);
-
- 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);
- return 0;
- }
-
-
- 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),
- m_pUnknownSize(0),
- m_pSeekHead(NULL),
- m_pInfo(NULL),
- m_pTracks(NULL),
- m_pCues(NULL),
- m_clusters(NULL),
- m_clusterCount(0),
- m_clusterPreloadCount(0),
- m_clusterSize(0)
- {
- }
-
-
- Segment::~Segment()
- {
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- Cluster** i = m_clusters;
- Cluster** j = m_clusters + count;
-
- while (i != j)
- {
- Cluster* const p = *i++;
- assert(p);
-
- delete p;
- }
-
- delete[] m_clusters;
-
- delete m_pTracks;
- delete m_pInfo;
- delete m_pCues;
- delete m_pSeekHead;
- }
-
-
- long long Segment::CreateInstance(
- IMkvReader* pReader,
- long long pos,
- Segment*& pSegment)
- {
- assert(pReader);
- assert(pos >= 0);
-
- pSegment = NULL;
-
- long long total, available;
-
- const long status = pReader->Length(&total, &available);
-
- if (status < 0) //error
- return status;
-
- if (available < 0)
- return -1;
-
- if ((total >= 0) && (available > total))
- return -1;
-
- const long long end = (total >= 0) ? total : available;
- //TODO: this might need to be liberalized
-
- //I would assume that in practice this loop would execute
- //exactly once, but we allow for other elements (e.g. Void)
- //to immediately follow the EBML header. This is fine for
- //the source filter case (since the entire file is available),
- //but in the splitter case over a network we should probably
- //just give up early. We could for example decide only to
- //execute this loop a maximum of, say, 10 times.
- //TODO:
- //There is an implied "give up early" by only parsing up
- //to the available limit. We do do that, but only if the
- //total file size is unknown. We could decide to always
- //use what's available as our limit (irrespective of whether
- //we happen to know the total file length). This would have
- //as its sense "parse this much of the file before giving up",
- //which a slightly different sense from "try to parse up to
- //10 EMBL elements before giving up".
-
- while (pos < end)
- {
- //Read ID
-
- long len;
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result) //error, or too few available bytes
- return result;
-
- if ((pos + len) > end)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long idpos = pos;
- const long long id = ReadUInt(pReader, pos, len);
-
- if (id < 0) //error
- return id;
-
- pos += len; //consume ID
-
- //Read Size
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result) //error, or too few available bytes
- return result;
-
- if ((pos + len) > end)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) //error
- return size;
-
- pos += len; //consume length of size of element
-
- //Pos now points to start of payload
-
- //Handle "unknown size" for live streaming of webm files.
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (id == 0x08538067) //Segment ID
- {
- if (size == unknown_size)
- size = -1;
-
- else if (total < 0)
- size = -1;
-
- else if ((pos + size) > total)
- size = -1;
-
- pSegment = new (std::nothrow) Segment(
- pReader,
- idpos,
- //elem_size
- pos,
- size);
-
- if (pSegment == 0)
- return -1; //generic error
-
- return 0; //success
- }
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + size) > end)
- return E_FILE_FORMAT_INVALID;
-
- pos += size; //consume payload
- }
-
- return E_FILE_FORMAT_INVALID; //there is no segment
- //TODO: this might need to be liberalized. See comments above.
- }
-
-
- long long Segment::ParseHeaders()
- {
- //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);
-
- if (status < 0) //error
- return status;
-
- assert((total < 0) || (available <= total));
-
- const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
- assert((segment_stop < 0) || (total < 0) || (segment_stop <= total));
- assert((segment_stop < 0) || (m_pos <= segment_stop));
-
- for (;;)
- {
- if ((total >= 0) && (m_pos >= total))
- break;
-
- if ((segment_stop >= 0) && (m_pos >= segment_stop))
- break;
-
- long long pos = m_pos;
- const long long element_start = pos;
-
- if ((pos + 1) > available)
- return (pos + 1);
-
- long len;
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- if (result > 0) //underflow (weird)
- return (pos + 1);
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long idpos = pos;
- const long long id = ReadUInt(m_pReader, idpos, len);
-
- if (id < 0) //error
- return id;
-
- if (id == 0x0F43B675) //Cluster ID
- break;
-
- pos += len; //consume ID
-
- if ((pos + 1) > available)
- return (pos + 1);
-
- //Read Size
- result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- if (result > 0) //underflow (weird)
- return (pos + 1);
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0) //error
- return size;
-
- pos += len; //consume length of size of element
-
- const long long element_size = size + pos - element_start;
-
- //Pos now points to start of payload
-
- if ((segment_stop >= 0) && ((pos + size) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- //We read EBML elements either in total or nothing at all.
-
- if ((pos + size) > available)
- return pos + size;
-
- if (id == 0x0549A966) //Segment Info ID
- {
- if (m_pInfo)
- return E_FILE_FORMAT_INVALID;
-
- 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
- {
- if (m_pTracks)
- return E_FILE_FORMAT_INVALID;
-
- 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 (std::nothrow) Cues(
- this,
- pos,
- size,
- element_start,
- element_size);
-
- if (m_pCues == NULL)
- return -1;
- }
- }
- else if (id == 0x014D9B74) //SeekHead ID
- {
- if (m_pSeekHead == NULL)
- {
- m_pSeekHead = new (std::nothrow) SeekHead(
- this,
- pos,
- size,
- element_start,
- element_size);
-
- if (m_pSeekHead == NULL)
- return -1;
-
- const long status = m_pSeekHead->Parse();
-
- if (status)
- return status;
- }
- }
-
- m_pos = pos + size; //consume payload
- }
-
- assert((segment_stop < 0) || (m_pos <= segment_stop));
-
- if (m_pInfo == NULL) //TODO: liberalize this behavior
- return E_FILE_FORMAT_INVALID;
-
- if (m_pTracks == NULL)
- return E_FILE_FORMAT_INVALID;
-
- return 0; //success
- }
-
-
- long Segment::LoadCluster(
- long long& pos,
- long& len)
- {
- for (;;)
- {
- const long result = DoLoadCluster(pos, len);
-
- if (result <= 1)
- return result;
- }
- }
-
-
- long Segment::DoLoadCluster(
- long long& pos,
- long& len)
- {
- if (m_pos < 0)
- return DoLoadClusterUnknownSize(pos, len);
-
- 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 cluster_off = -1; //offset relative to start of segment
- long long cluster_size = -1; //size of cluster payload
-
- for (;;)
- {
- if ((total >= 0) && (m_pos >= total))
- return 1; //no more clusters
-
- if ((segment_stop >= 0) && (m_pos >= segment_stop))
- return 1; //no more clusters
-
- pos = m_pos;
-
- //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);
-
- pos += len; //consume ID
-
- //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 length of size of element
-
- //pos now points to start of payload
-
- if (size == 0) //weird
- {
- m_pos = pos;
- continue;
- }
-
- 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
-
- if (m_pCues == NULL)
- {
- const long long element_size = (pos - idpos) + size;
-
- m_pCues = new Cues(this,
- pos,
- size,
- idpos,
- element_size);
- assert(m_pCues); //TODO
- }
-
- m_pos = pos + size; //consume payload
- continue;
- }
-
- if (id != 0x0F43B675) //Cluster ID
- {
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID; //TODO: liberalize
-
- m_pos = pos + size; //consume payload
- continue;
- }
-
- //We have a cluster.
-
- cluster_off = idpos - m_start; //relative pos
-
- if (size != unknown_size)
- cluster_size = size;
-
- break;
- }
-
- assert(cluster_off >= 0); //have cluster
-
- long long pos_;
- long len_;
-
- status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
-
- if (status < 0) //error, or underflow
- {
- pos = pos_;
- len = len_;
-
- return status;
- }
-
- //status == 0 means "no block entries found"
- //status > 0 means "found at least one block entry"
-
- //TODO:
- //The issue here is that the segment increments its own
- //pos ptr past the most recent cluster parsed, and then
- //starts from there to parse the next cluster. If we
- //don't know the size of the current cluster, then we
- //must either parse its payload (as we do below), looking
- //for the cluster (or cues) ID to terminate the parse.
- //This isn't really what we want: rather, we really need
- //a way to create the curr cluster object immediately.
- //The pity is that cluster::parse can determine its own
- //boundary, and we largely duplicate that same logic here.
- //
- //Maybe we need to get rid of our look-ahead preloading
- //in source::parse???
- //
- //As we're parsing the blocks in the curr cluster
- //(in cluster::parse), we should have some way to signal
- //to the segment that we have determined the boundary,
- //so it can adjust its own segment::m_pos member.
- //
- //The problem is that we're asserting in asyncreadinit,
- //because we adjust the pos down to the curr seek pos,
- //and the resulting adjusted len is > 2GB. I'm suspicious
- //that this is even correct, but even if it is, we can't
- //be loading that much data in the cache anyway.
-
- const long idx = m_clusterCount;
-
- 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
- {
- if (status == 0) //no entries found
- return E_FILE_FORMAT_INVALID;
-
- if (cluster_size >= 0)
- pos += cluster_size;
- else
- {
- const long long element_size = pCluster->GetElementSize();
-
- if (element_size <= 0)
- return E_FILE_FORMAT_INVALID; //TODO: handle this case
-
- pos = pCluster->m_element_start + element_size;
- }
-
- pCluster->m_index = idx; //move from preloaded to loaded
- ++m_clusterCount;
- --m_clusterPreloadCount;
-
- m_pos = pos; //consume payload
- assert((segment_stop < 0) || (m_pos <= segment_stop));
-
- return 0; //success
- }
- }
-
- if (status == 0) //no entries found
- {
- if (cluster_size < 0)
- return E_FILE_FORMAT_INVALID; //TODO: handle this
-
- pos += cluster_size;
-
- if ((total >= 0) && (pos >= total))
- {
- m_pos = total;
- return 1; //no more clusters
- }
-
- if ((segment_stop >= 0) && (pos >= segment_stop))
- {
- m_pos = segment_stop;
- return 1; //no more clusters
- }
-
- m_pos = pos;
- return 2; //try again
- }
-
- //status > 0 means we have an entry
-
- Cluster* const pCluster = Cluster::Create(this,
- idx,
- cluster_off);
- //element_size);
- assert(pCluster);
-
- AppendCluster(pCluster);
- assert(m_clusters);
- assert(idx < m_clusterSize);
- assert(m_clusters[idx] == pCluster);
-
- if (cluster_size >= 0)
- {
- pos += cluster_size;
-
- m_pos = pos;
- assert((segment_stop < 0) || (m_pos <= segment_stop));
-
- return 0;
- }
-
- m_pUnknownSize = pCluster;
- m_pos = -pos;
-
- 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
-
- }
-
-
- 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
- return status;
-
- if (status == 0) //parsed a block
- return 2; //continue parsing
-
- assert(status > 0); //nothing left to parse of this cluster
-
- const long long start = m_pUnknownSize->m_element_start;
-
- const long long size = m_pUnknownSize->GetElementSize();
- assert(size >= 0);
-
- pos = start + size;
- m_pos = pos;
-
- m_pUnknownSize = 0;
-
- return 2; //continue parsing
- #endif
- }
-
-
- void Segment::AppendCluster(Cluster* pCluster)
- {
- assert(pCluster);
- assert(pCluster->m_index >= 0);
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- long& size = m_clusterSize;
- assert(size >= count);
-
- const long idx = pCluster->m_index;
- assert(idx == m_clusterCount);
-
- if (count >= size)
- {
- const long n = (size <= 0) ? 2048 : 2*size;
-
- Cluster** const qq = new Cluster*[n];
- Cluster** q = qq;
-
- Cluster** p = m_clusters;
- Cluster** const pp = p + count;
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_clusters;
-
- m_clusters = qq;
- size = n;
- }
-
- if (m_clusterPreloadCount > 0)
- {
- assert(m_clusters);
-
- Cluster** const p = m_clusters + m_clusterCount;
- assert(*p);
- assert((*p)->m_index < 0);
-
- Cluster** q = p + m_clusterPreloadCount;
- assert(q < (m_clusters + size));
-
- for (;;)
- {
- Cluster** const qq = q - 1;
- assert((*qq)->m_index < 0);
-
- *q = *qq;
- q = qq;
-
- if (q == p)
- break;
- }
- }
-
- m_clusters[idx] = pCluster;
- ++m_clusterCount;
- }
-
-
- void Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx)
- {
- assert(pCluster);
- assert(pCluster->m_index < 0);
- assert(idx >= m_clusterCount);
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- long& size = m_clusterSize;
- assert(size >= count);
-
- if (count >= size)
- {
- const long n = (size <= 0) ? 2048 : 2*size;
-
- Cluster** const qq = new Cluster*[n];
- Cluster** q = qq;
-
- Cluster** p = m_clusters;
- Cluster** const pp = p + count;
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_clusters;
-
- m_clusters = qq;
- size = n;
- }
-
- assert(m_clusters);
-
- Cluster** const p = m_clusters + idx;
-
- Cluster** q = m_clusters + count;
- assert(q >= p);
- assert(q < (m_clusters + size));
-
- while (q > p)
- {
- Cluster** const qq = q - 1;
- assert((*qq)->m_index < 0);
-
- *q = *qq;
- q = qq;
- }
-
- m_clusters[idx] = pCluster;
- ++m_clusterPreloadCount;
- }
-
-
- long Segment::Load()
- {
- assert(m_clusters == NULL);
- assert(m_clusterSize == 0);
- assert(m_clusterCount == 0);
- //assert(m_size >= 0);
-
- //Outermost (level 0) segment object has been constructed,
- //and pos designates start of payload. We need to find the
- //inner (level 1) elements.
-
- const long long header_status = ParseHeaders();
-
- if (header_status < 0) //error
- return static_cast<long>(header_status);
-
- if (header_status > 0) //underflow
- return E_BUFFER_NOT_FULL;
-
- assert(m_pInfo);
- assert(m_pTracks);
-
- for (;;)
- {
- const int status = LoadCluster();
-
- if (status < 0) //error
- return status;
-
- if (status >= 1) //no more clusters
- return 0;
- }
- }
-
-
- SeekHead::SeekHead(
- Segment* pSegment,
- long long start,
- long long size_,
- long long element_start,
- long long element_size) :
- m_pSegment(pSegment),
- m_start(start),
- m_size(size_),
- m_element_start(element_start),
- m_element_size(element_size),
- m_entries(0),
- m_entry_count(0),
- m_void_elements(0),
- m_void_element_count(0)
- {
- }
-
-
- 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_entry_count;
- }
-
- const SeekHead::Entry* SeekHead::GetEntry(int idx) const
- {
- if (idx < 0)
- return 0;
-
- if (idx >= m_entry_count)
- return 0;
-
- return m_entries + idx;
- }
-
- 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
- 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
-
- if (off < 0)
- return -1;
-
- long long tota