From c498a2747314b1e863eab6a22aaf7323642ba62a Mon Sep 17 00:00:00 2001 From: James Dong Date: Tue, 22 Feb 2011 15:12:15 -0800 Subject: Remove mkvparser code from /frameworks/base bug - 3322129 Change-Id: I94dca6234ce57c9a32290aa7ac4885b0a2f566a9 --- media/libstagefright/matroska/Android.mk | 6 +- media/libstagefright/matroska/mkvparser.cpp | 4514 --------------------------- media/libstagefright/matroska/mkvparser.hpp | 556 ---- 3 files changed, 3 insertions(+), 5073 deletions(-) delete mode 100644 media/libstagefright/matroska/mkvparser.cpp delete mode 100644 media/libstagefright/matroska/mkvparser.hpp (limited to 'media') diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk index 33c9d97..1f1c68b 100644 --- a/media/libstagefright/matroska/Android.mk +++ b/media/libstagefright/matroska/Android.mk @@ -2,11 +2,11 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - MatroskaExtractor.cpp \ - mkvparser.cpp \ + MatroskaExtractor.cpp LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ + $(JNI_H_INCLUDE) \ + $(TOP)/external/libvpx/mkvparser \ $(TOP)/frameworks/base/include/media/stagefright/openmax \ LOCAL_CFLAGS += -Wno-multichar diff --git a/media/libstagefright/matroska/mkvparser.cpp b/media/libstagefright/matroska/mkvparser.cpp deleted file mode 100644 index 7448d96..0000000 --- a/media/libstagefright/matroska/mkvparser.cpp +++ /dev/null @@ -1,4514 +0,0 @@ -// 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 -#include -#include -//#include -//#include "odbgstream.hpp" -//using std::endl; - -mkvparser::IMkvReader::~IMkvReader() -{ -} - - -void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision) -{ - major = 1; - minor = 0; - build = 0; - revision = 4; -} - - -long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(pos < available); - assert((available - pos) >= 1); //assume here max u-int len is 8 - - unsigned char b; - - hr = pReader->Read(pos, 1, &b); - if (hr < 0) - return hr; - - assert(hr == 0L); - - if (b & 0x80) //1000 0000 - { - len = 1; - b &= 0x7F; //0111 1111 - } - else if (b & 0x40) //0100 0000 - { - len = 2; - b &= 0x3F; //0011 1111 - } - else if (b & 0x20) //0010 0000 - { - len = 3; - b &= 0x1F; //0001 1111 - } - else if (b & 0x10) //0001 0000 - { - len = 4; - b &= 0x0F; //0000 1111 - } - else if (b & 0x08) //0000 1000 - { - len = 5; - b &= 0x07; //0000 0111 - } - else if (b & 0x04) //0000 0100 - { - len = 6; - b &= 0x03; //0000 0011 - } - else if (b & 0x02) //0000 0010 - { - len = 7; - b &= 0x01; //0000 0001 - } - else - { - assert(b & 0x01); //0000 0001 - len = 8; - b = 0; //0000 0000 - } - - assert((available - pos) >= len); - - long long result = b; - ++pos; - for (long i = 1; i < len; ++i) - { - hr = pReader->Read(pos, 1, &b); - - if (hr < 0) - return hr; - - assert(hr == 0L); - - 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; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - - if (pos >= available) - return pos; //too few bytes available - - unsigned char b; - - hr = pReader->Read(pos, 1, &b); - - if (hr < 0) - return hr; - - assert(hr == 0L); - - if (b == 0) //we can't handle u-int values larger than 8 bytes - return E_FILE_FORMAT_INVALID; - - unsigned char m = 0x80; - len = 1; - - while (!(b & m)) - { - m >>= 1; - ++len; - } - - return 0; //success -} - - -long long mkvparser::SyncReadUInt( - IMkvReader* pReader, - long long pos, - long long stop, - long& len) -{ - assert(pReader); - - if (pos >= stop) - return E_FILE_FORMAT_INVALID; - - unsigned char b; - - long hr = pReader->Read(pos, 1, &b); - - if (hr < 0) - return hr; - - if (hr != 0L) - return E_BUFFER_NOT_FULL; - - if (b == 0) //we can't handle u-int values larger than 8 bytes - return E_FILE_FORMAT_INVALID; - - unsigned char m = 0x80; - len = 1; - - while (!(b & m)) - { - m >>= 1; - ++len; - } - - if ((pos + len) > stop) - return E_FILE_FORMAT_INVALID; - - long long result = b & (~m); - ++pos; - - for (int i = 1; i < len; ++i) - { - hr = pReader->Read(pos, 1, &b); - - if (hr < 0) - return hr; - - if (hr != 0L) - return E_BUFFER_NOT_FULL; - - result <<= 8; - result |= b; - - ++pos; - } - - return result; -} - - -long long mkvparser::UnserializeUInt( - IMkvReader* pReader, - long long pos, - long long size) -{ - assert(pReader); - assert(pos >= 0); - assert(size > 0); - assert(size <= 8); - - long long result = 0; - - for (long long i = 0; i < size; ++i) - { - unsigned char b; - - const long hr = pReader->Read(pos, 1, &b); - - if (hr < 0) - return hr; - result <<= 8; - result |= b; - - ++pos; - } - - return result; -} - - -float mkvparser::Unserialize4Float( - IMkvReader* pReader, - long long pos) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - assert((pos + 4) <= available); - - float result; - - unsigned char* const p = (unsigned char*)&result; - unsigned char* q = p + 4; - - for (;;) - { - hr = pReader->Read(pos, 1, --q); - assert(hr == 0L); - - if (q == p) - break; - - ++pos; - } - - return result; -} - - -double mkvparser::Unserialize8Double( - IMkvReader* pReader, - long long pos) -{ - assert(pReader); - assert(pos >= 0); - - double result; - - unsigned char* const p = (unsigned char*)&result; - unsigned char* q = p + 8; - - for (;;) - { - const long hr = pReader->Read(pos, 1, --q); - assert(hr == 0L); - - if (q == p) - break; - - ++pos; - } - - return result; -} - -signed char mkvparser::Unserialize1SInt( - IMkvReader* pReader, - long long pos) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr == 0); - assert(available <= total); - assert(pos < available); - - signed char result; - - hr = pReader->Read(pos, 1, (unsigned char*)&result); - assert(hr == 0); - - return result; -} - -short mkvparser::Unserialize2SInt( - IMkvReader* pReader, - long long pos) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - assert((pos + 2) <= available); - - short result; - - unsigned char* const p = (unsigned char*)&result; - unsigned char* q = p + 2; - - for (;;) - { - hr = pReader->Read(pos, 1, --q); - assert(hr == 0L); - - if (q == p) - break; - - ++pos; - } - - return result; -} - - -bool mkvparser::Match( - IMkvReader* pReader, - long long& pos, - unsigned long id_, - long long& val) - -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(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_, - char*& val) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - - long len; - - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); - assert(len > 0); - assert(len <= 8); - assert((pos + len) <= available); - - if ((unsigned long)id != id_) - return false; - - pos += len; //consume id - - const long long size_ = ReadUInt(pReader, pos, len); - assert(size_ >= 0); - assert(len > 0); - assert(len <= 8); - assert((pos + len) <= available); - - pos += len; //consume length of size of payload - assert((pos + size_) <= available); - - const size_t size = static_cast(size_); - val = new char[size+1]; - - for (size_t i = 0; i < size; ++i) - { - char c; - - hr = pReader->Read(pos + i, 1, (unsigned char*)&c); - assert(hr == 0L); - - val[i] = c; - - if (c == '\0') - break; - - } - - val[size] = '\0'; - pos += size_; //consume size of payload - - return true; -} - -bool mkvparser::Match( - IMkvReader* pReader, - long long& pos, - unsigned long id_, - unsigned char*& buf, - size_t& buflen) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(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(size_); - - buf = new (std::nothrow) unsigned char[buflen_]; - assert(buf); //TODO - - hr = pReader->Read(pos, buflen_, buf); - assert(hr == 0L); - - buflen = buflen_; - - pos += size_; //consume size of payload - return true; -} - - -bool mkvparser::Match( - IMkvReader* pReader, - long long& pos, - unsigned long id_, - double& val) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - long idlen; - const long long id = ReadUInt(pReader, pos, idlen); - assert(id >= 0); //TODO - - if ((unsigned long)id != id_) - return false; - - long sizelen; - const long long size = ReadUInt(pReader, pos + idlen, sizelen); - - switch (size) - { - case 4: - case 8: - break; - default: - return false; - } - - pos += idlen + sizelen; //consume id and size fields - assert((pos + size) <= available); - - if (size == 4) - val = Unserialize4Float(pReader, pos); - else - { - assert(size == 8); - val = Unserialize8Double(pReader, pos); - } - - pos += size; //consume size of payload - - return true; -} - - -bool mkvparser::Match( - IMkvReader* pReader, - long long& pos, - unsigned long id_, - short& val) -{ - assert(pReader); - assert(pos >= 0); - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); - assert((pos + len) <= available); - - if ((unsigned long)id != id_) - return false; - - pos += len; //consume id - - const long long size = ReadUInt(pReader, pos, len); - assert(size <= 2); - assert((pos + len) <= available); - - pos += len; //consume length of size of payload - assert((pos + size) <= available); - - //TODO: - // Generalize this to work for any size signed int - if (size == 1) - val = Unserialize1SInt(pReader, pos); - else - val = Unserialize2SInt(pReader, pos); - - pos += size; //consume size of payload - - return true; -} - - -namespace mkvparser -{ - -EBMLHeader::EBMLHeader(): - m_docType(NULL) -{ -} - -EBMLHeader::~EBMLHeader() -{ - delete[] m_docType; -} - -long long EBMLHeader::Parse( - IMkvReader* pReader, - long long& pos) -{ - assert(pReader); - - long long total, available; - - long hr = pReader->Length(&total, &available); - - if (hr < 0) - return hr; - - pos = 0; - long long end = (1024 < available)? 1024: available; - - for (;;) - { - unsigned char b = 0; - - while (pos < end) - { - hr = pReader->Read(pos, 1, &b); - - if (hr < 0) - return hr; - - if (b == 0x1A) - break; - - ++pos; - } - - if (b != 0x1A) - { - if ((pos >= 1024) || - (available >= total) || - ((total - available) < 5)) - return -1; - - return available + 5; //5 = 4-byte ID + 1st byte of size - } - - if ((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) //ReadId masks-off length indicator bits - { - assert(len == 4); - pos += len; - break; - } - - ++pos; //throw away just the 0x1A byte, and try again - } - - 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 - pos) < len) - return E_FILE_FORMAT_INVALID; - if ((available - pos) < len) - return pos + len; //try again later - - result = ReadUInt(pReader, pos, len); - - if (result < 0) //error - return result; - - pos += len; //consume u-int - - if ((total - pos) < result) - return E_FILE_FORMAT_INVALID; - - if ((available - pos) < result) - return pos + result; - - end = pos + result; - - m_version = 1; - m_readVersion = 1; - m_maxIdLength = 4; - m_maxSizeLength = 8; - m_docTypeVersion = 1; - m_docTypeReadVersion = 1; - - while (pos < end) - { - if (Match(pReader, pos, 0x0286, m_version)) - ; - else if (Match(pReader, pos, 0x02F7, m_readVersion)) - ; - else if (Match(pReader, pos, 0x02F2, m_maxIdLength)) - ; - else if (Match(pReader, pos, 0x02F3, m_maxSizeLength)) - ; - else if (Match(pReader, pos, 0x0282, m_docType)) - ; - else if (Match(pReader, pos, 0x0287, m_docTypeVersion)) - ; - else if (Match(pReader, pos, 0x0285, m_docTypeReadVersion)) - ; - else - { - result = ReadUInt(pReader, pos, len); - assert(result > 0); - assert(len > 0); - assert(len <= 8); - - pos += len; - assert(pos < end); - - result = ReadUInt(pReader, pos, len); - assert(result >= 0); - assert(len > 0); - assert(len <= 8); - - pos += len + result; - assert(pos <= end); - } - } - - assert(pos == end); - - return 0; -} - - -Segment::Segment( - IMkvReader* pReader, - long long start, - long long size) : - m_pReader(pReader), - m_start(start), - m_size(size), - m_pos(start), - 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; -} - - -long long Segment::CreateInstance( - IMkvReader* pReader, - long long pos, - Segment*& pSegment) -{ - assert(pReader); - assert(pos >= 0); - - pSegment = NULL; - - long long total, available; - - long hr = pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - - //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. - - while (pos < total) - { - //Read ID - - long len; - long long result = GetUIntLength(pReader, pos, len); - - if (result) //error, or too few available bytes - return result; - - if ((pos + len) > total) - return E_FILE_FORMAT_INVALID; - - if ((pos + len) > available) - return pos + len; - - //TODO: if we liberalize the behavior of ReadUInt, we can - //probably eliminate having to use GetUIntLength here. - 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) > total) - return E_FILE_FORMAT_INVALID; - - if ((pos + len) > available) - return pos + len; - - //TODO: if we liberalize the behavior of ReadUInt, we can - //probably eliminate having to use GetUIntLength here. - const long long size = ReadUInt(pReader, pos, len); - - if (size < 0) - return size; - - pos += len; //consume length of size of element - - //Pos now points to start of payload - - if ((pos + size) > total) - return E_FILE_FORMAT_INVALID; - - if (id == 0x08538067) //Segment ID - { - pSegment = new Segment(pReader, pos, size); - assert(pSegment); //TODO - - return 0; //success - } - - pos += size; //consume payload - } - - assert(pos == total); - - pSegment = new Segment(pReader, pos, 0); - assert(pSegment); //TODO - - return 0; //success (sort of) -} - - -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; - - long hr = m_pReader->Length(&total, &available); - assert(hr >= 0); - assert(available <= total); - - const long long stop = m_start + m_size; - assert(stop <= total); - assert(m_pos <= stop); - - bool bQuit = false; - - while ((m_pos < stop) && !bQuit) - { - long long pos = m_pos; - - long len; - long long result = GetUIntLength(m_pReader, pos, len); - - if (result) //error, or too few available bytes - return result; - - if ((pos + len) > 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; - - pos += len; //consume ID - - //Read Size - result = GetUIntLength(m_pReader, pos, len); - - if (result) //error, or too few available bytes - return result; - - if ((pos + len) > 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) - return size; - - pos += len; //consume length of size of element - - //Pos now points to start of payload - - if ((pos + size) > stop) - return E_FILE_FORMAT_INVALID; - - //We read EBML elements either in total or nothing at all. - - if ((pos + size) > available) - return pos + size; - - if (id == 0x0549A966) //Segment Info ID - { - assert(m_pInfo == NULL); - - m_pInfo = new SegmentInfo(this, pos, size); - assert(m_pInfo); //TODO - } - else if (id == 0x0654AE6B) //Tracks ID - { - assert(m_pTracks == NULL); - - m_pTracks = new Tracks(this, pos, size); - assert(m_pTracks); //TODO - } - else if (id == 0x0C53BB6B) //Cues ID - { - if (m_pCues == NULL) - { - m_pCues = new Cues(this, pos, size); - assert(m_pCues); //TODO - } - } - else if (id == 0x014D9B74) //SeekHead ID - { - ParseSeekHead(pos, size); - } - else if (id == 0x0F43B675) //Cluster ID - { - bQuit = true; - } - - if (!bQuit) - m_pos = pos + size; //consume payload - } - - assert(m_pos <= 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 -} - - -#if 0 -long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const -{ - pCluster = NULL; - pos_ = -1; - - const long long stop = m_start + m_size; - assert(m_pos <= stop); - - long long pos = m_pos; - long long off = -1; - - while (pos < stop) - { - long len; - const long long idpos = pos; - - const long long id = SyncReadUInt(m_pReader, pos, stop, len); - - if (id < 0) //error - return static_cast(id); - - if (id == 0) - return E_FILE_FORMAT_INVALID; - - pos += len; //consume id - assert(pos < stop); - - const long long size = SyncReadUInt(m_pReader, pos, stop, len); - - if (size < 0) //error - return static_cast(size); - - pos += len; //consume size - assert(pos <= stop); - - if (size == 0) //weird - continue; - - //pos now points to start of payload - - pos += size; //consume payload - assert(pos <= stop); - - if (id == 0x0F43B675) //Cluster ID - { - off = idpos - m_start; // >= 0 means we found a cluster - break; - } - } - - assert(pos <= stop); - - //Indicate to caller how much of file has been consumed. This is - //used later in AddCluster to adjust the current parse position - //(the value cached in the segment object itself) to the - //file position value just past the cluster we parsed. - - if (off < 0) //we did not found any more clusters - { - pos_ = stop; //pos_ >= 0 here means EOF (cluster is NULL) - return 0; //TODO: confirm this return value - } - - //We found a cluster. Now read something, to ensure that it is - //fully loaded in the network cache. - - if (pos >= stop) //we parsed the entire segment - { - //We did find a cluster, but it was very last element in the segment. - //Our preference is that the loop above runs 1 1/2 times: - //the first pass finds the cluster, and the second pass - //finds the element the follows the cluster. In this case, however, - //we reached the end of the file without finding another element, - //so we didn't actually read anything yet associated with "end of the - //cluster". And we must perform an actual read, in order - //to guarantee that all of the data that belongs to this - //cluster has been loaded into the network cache. So instead - //of reading the next element that follows the cluster, we - //read the last byte of the cluster (which is also the last - //byte in the file). - - //Read the last byte of the file. (Reading 0 bytes at pos - //might work too -- it would depend on how the reader is - //implemented. Here we take the more conservative approach, - //since this makes fewer assumptions about the network - //reader abstraction.) - - unsigned char b; - - const int result = m_pReader->Read(pos - 1, 1, &b); - assert(result == 0); - - pos_ = stop; - } - else - { - long len; - const long long idpos = pos; - - const long long id = SyncReadUInt(m_pReader, pos, stop, len); - - if (id < 0) //error - return static_cast(id); - - if (id == 0) - return E_BUFFER_NOT_FULL; - - pos += len; //consume id - assert(pos < stop); - - const long long size = SyncReadUInt(m_pReader, pos, stop, len); - - if (size < 0) //error - return static_cast(size); - - pos_ = idpos; - } - - //We found a cluster, and it has been completely loaded into the - //network cache. (We can guarantee this because we actually read - //the EBML tag that follows the cluster, or, if we reached EOF, - //because we actually read the last byte of the cluster). - - Segment* const this_ = const_cast(this); - - pCluster = Cluster::Parse(this_, m_clusterCount, off); - assert(pCluster); - assert(pCluster->m_index == m_clusterCount); - - return 0; -} - - -bool Segment::AddCluster(Cluster* pCluster, long long pos) -{ - assert(pos >= m_start); - - const long long stop = m_start + m_size; - assert(pos <= stop); - - if (pCluster) - { - AppendCluster(pCluster); - assert(m_clusters); - assert(m_clusterSize > pCluster->m_index); - assert(m_clusters[pCluster->m_index] == pCluster); - } - - m_pos = pos; //m_pos >= stop is now we know we have all clusters - - return (pos >= stop); -} -#endif - - -long Segment::LoadCluster() -{ - const long long stop = m_start + m_size; - - while (m_pos < stop) - { - long long pos = m_pos; - - long len; - - long long result = GetUIntLength(m_pReader, pos, len); - - if (result < 0) //error - return static_cast(result); - - if ((pos + len) > stop) - return E_FILE_FORMAT_INVALID; - - const long long idpos = pos; - const long long id = ReadUInt(m_pReader, idpos, len); - - if (id < 0) //error - return static_cast(id); - - pos += len; //consume ID - - //Read Size - result = GetUIntLength(m_pReader, pos, len); - - if (result < 0) //error - return static_cast(result); - - if ((pos + len) > stop) - return E_FILE_FORMAT_INVALID; - - const long long size = ReadUInt(m_pReader, pos, len); - - if (size < 0) //error - return static_cast(size); - - pos += len; //consume length of size of element - - if (size == 0) //weird - { - m_pos = pos; - continue; - } - - //Pos now points to start of payload - - if ((pos + size) > stop) - return E_FILE_FORMAT_INVALID; - - if (id == 0x0C53BB6B) //Cues ID - { - if (m_pCues == NULL) - { - m_pCues = new Cues(this, pos, size); - assert(m_pCues); //TODO - } - - m_pos = pos + size; //consume payload - continue; - } - - if (id != 0x0F43B675) //Cluster ID - { - m_pos = pos + size; //consume payload - continue; - } - - const long idx = m_clusterCount; - const long long idoff = idpos - m_start; - - 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->m_pos; - assert(off_); - - const long long off = off_ * ((off_ >= 0) ? 1 : -1); - assert(idoff <= off); - - if (idoff == off) //cluster has been preloaded already - { - pCluster->m_index = idx; - ++m_clusterCount; - --m_clusterPreloadCount; - - m_pos = pos + size; //consume payload - break; - } - } - - Cluster* const pCluster = Cluster::Parse(this, idx, idoff); - assert(pCluster); - assert(pCluster->m_index == idx); - - AppendCluster(pCluster); - assert(m_clusters); - assert(idx < m_clusterSize); - assert(m_clusters[idx] == pCluster); - - m_pos = pos + size; //consume payload - break; - } - - assert(m_pos <= stop); - return 0; -} - - -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) - { - long n; - - if (size > 0) - n = 2 * size; - else if (m_pInfo == 0) - n = 2048; - else - { - const long long ns = m_pInfo->GetDuration(); - - if (ns <= 0) - n = 2048; - else - { - const long long sec = (ns + 999999999LL) / 1000000000LL; - n = static_cast(sec); - } - } - - 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) - { - long n; - - if (size > 0) - n = 2 * size; - else if (m_pInfo == 0) - n = 2048; - else - { - const long long ns = m_pInfo->GetDuration(); - - if (ns <= 0) - n = 2048; - else - { - const long long sec = (ns + 999999999LL) / 1000000000LL; - n = static_cast(sec); - } - } - - 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); - - //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 stop = m_start + m_size; - -#ifdef _DEBUG //TODO: this is really Microsoft-specific - { - long long total, available; - - long hr = m_pReader->Length(&total, &available); - assert(hr >= 0); - assert(available >= total); - assert(stop <= total); - } -#endif - - while (m_pos < stop) - { - long long pos = m_pos; - - long len; - - long long result = GetUIntLength(m_pReader, pos, len); - - if (result < 0) //error - return static_cast(result); - - if ((pos + len) > stop) - return E_FILE_FORMAT_INVALID; - - const long long idpos = pos; - const long long id = ReadUInt(m_pReader, idpos, len); - - if (id < 0) //error - return static_cast(id); - - pos += len; //consume ID - - //Read Size - result = GetUIntLength(m_pReader, pos, len); - - if (result < 0) //error - return static_cast(result); - - if ((pos + len) > stop) - return E_FILE_FORMAT_INVALID; - - const long long size = ReadUInt(m_pReader, pos, len); - - if (size < 0) //error - return static_cast(size); - - pos += len; //consume length of size of element - - //Pos now points to start of payload - - if ((pos + size) > stop) - return E_FILE_FORMAT_INVALID; - - if (id == 0x0F43B675) //Cluster ID - { - const long idx = m_clusterCount; - const long long off = idpos - m_start; - - Cluster* const pCluster = Cluster::Parse(this, idx, off); - assert(pCluster); - assert(pCluster->m_index == idx); - - AppendCluster(pCluster); - assert(m_clusters); - assert(m_clusterSize > idx); - assert(m_clusters[idx] == pCluster); - } - else if (id == 0x0C53BB6B) //Cues ID - { - assert(m_pCues == NULL); - - m_pCues = new Cues(this, pos, size); - assert(m_pCues); //TODO - } - else if (id == 0x0549A966) //SegmentInfo ID - { - assert(m_pInfo == NULL); - - m_pInfo = new SegmentInfo(this, pos, size); - assert(m_pInfo); - } - else if (id == 0x0654AE6B) //Tracks ID - { - assert(m_pTracks == NULL); - - m_pTracks = new Tracks(this, pos, size); - assert(m_pTracks); //TODO - } - - m_pos = pos + size; //consume payload - } - - assert(m_pos >= stop); - - if (m_pInfo == NULL) - return E_FILE_FORMAT_INVALID; //TODO: ignore this case? - - if (m_pTracks == NULL) - return E_FILE_FORMAT_INVALID; - - if (m_clusters == NULL) //TODO: ignore this case? - return E_FILE_FORMAT_INVALID; - - //TODO: decide whether we require Cues element - //if (m_pCues == NULL) - // return E_FILE_FORMAT_INVALID; - - return 0; -} - - -void Segment::ParseSeekHead(long long start, long long size_) -{ - long long pos = start; - const long long stop = start + size_; - - while (pos < stop) - { - long len; - - const long long id = ReadUInt(m_pReader, pos, len); - assert(id >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume ID - - const long long size = ReadUInt(m_pReader, pos, len); - assert(size >= 0); - assert((pos + len) <= stop); - - pos += len; //consume Size field - assert((pos + size) <= stop); - - if (id == 0x0DBB) //SeekEntry ID - ParseSeekEntry(pos, size); - - pos += size; //consume payload - assert(pos <= stop); - } - - assert(pos == stop); -} - - -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 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); - - //Pos now points to start of payload - - m_pCues = new Cues(this, pos, size); - assert(m_pCues); //TODO - - //os << "Segment::ParseCues (end)" << endl; -} - - -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); -} - - -Cues::Cues(Segment* pSegment, long long start_, long long size_) : - m_pSegment(pSegment), - m_start(start_), - m_size(size_), - m_cue_points(NULL), - m_count(0), - m_preload_count(0), - m_pos(start_) -{ -} - - -Cues::~Cues() -{ - const size_t n = m_count + m_preload_count; - - CuePoint** p = m_cue_points; - CuePoint** const q = p + n; - - while (p != q) - { - CuePoint* const pCP = *p++; - assert(pCP); - - delete pCP; - } - - delete[] m_cue_points; -} - - -void Cues::Init() const -{ - if (m_cue_points) - return; - - assert(m_count == 0); - assert(m_preload_count == 0); - - IMkvReader* const pReader = m_pSegment->m_pReader; - - const long long stop = m_start + m_size; - long long pos = m_start; - - size_t cue_points_size = 0; - - while (pos < stop) - { - const long long idpos = pos; - - long len; - - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume ID - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); - assert((pos + len) <= stop); - - pos += len; //consume Size field - assert((pos + size) <= stop); - - if (id == 0x3B) //CuePoint ID - PreloadCuePoint(cue_points_size, idpos); - - pos += size; //consume payload - assert(pos <= stop); - } -} - - -void Cues::PreloadCuePoint( - size_t& cue_points_size, - long long pos) const -{ - assert(m_count == 0); - - if (m_preload_count >= cue_points_size) - { - size_t n; - - if (cue_points_size > 0) - n = static_cast(2 * cue_points_size); - else - { - const SegmentInfo* const pInfo = m_pSegment->GetInfo(); - - if (pInfo == NULL) - n = 2048; - else - { - const long long ns = pInfo->GetDuration(); - - if (ns <= 0) - n = 2048; - else - { - const long long sec = (ns + 999999999LL) / 1000000000LL; - n = static_cast(sec); - } - } - } - - CuePoint** const qq = new CuePoint*[n]; - CuePoint** q = qq; //beginning of target - - CuePoint** p = m_cue_points; //beginning of source - CuePoint** const pp = p + m_preload_count; //end of source - - while (p != pp) - *q++ = *p++; - - delete[] m_cue_points; - - m_cue_points = qq; - cue_points_size = n; - } - - CuePoint* const pCP = new CuePoint(m_preload_count, pos); - m_cue_points[m_preload_count++] = pCP; -} - - -bool Cues::LoadCuePoint() const -{ - //odbgstream os; - //os << "Cues::LoadCuePoint" << endl; - - const long long stop = m_start + m_size; - - if (m_pos >= stop) - return false; //nothing else to do - - Init(); - - IMkvReader* const pReader = m_pSegment->m_pReader; - - while (m_pos < stop) - { - const long long idpos = m_pos; - - long len; - - const long long id = ReadUInt(pReader, m_pos, len); - assert(id >= 0); //TODO - assert((m_pos + len) <= stop); - - m_pos += len; //consume ID - - const long long size = ReadUInt(pReader, m_pos, len); - assert(size >= 0); - assert((m_pos + len) <= stop); - - m_pos += len; //consume Size field - assert((m_pos + size) <= stop); - - if (id != 0x3B) //CuePoint ID - { - m_pos += size; //consume payload - assert(m_pos <= stop); - - continue; - } - - assert(m_preload_count > 0); - - CuePoint* const pCP = m_cue_points[m_count]; - assert(pCP); - assert((pCP->GetTimeCode() >= 0) || (-pCP->GetTimeCode() == idpos)); - - pCP->Load(pReader); - ++m_count; - --m_preload_count; - - m_pos += size; //consume payload - assert(m_pos <= stop); - - break; - } - - return (m_pos < stop); -} - - -bool Cues::Find( - long long time_ns, - const Track* pTrack, - const CuePoint*& pCP, - const CuePoint::TrackPosition*& pTP) const -{ - assert(time_ns >= 0); - assert(pTrack); - - LoadCuePoint(); - - 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); - - //TODO: here and elsewhere, it's probably not correct to search - //for the cue point with this time, and then search for a matching - //track. In principle, the matching track could be on some earlier - //cue point, and with our current algorithm, we'd miss it. To make - //this bullet-proof, we'd need to create a secondary structure, - //with a list of cue points that apply to a track, and then search - //that track-based structure for a matching cue point. - - pTP = pCP->Find(pTrack); - 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 -{ - LoadCuePoint(); //init cues - - const size_t count = m_count + m_preload_count; - - if (count == 0) //weird - return NULL; - - CuePoint* const* const pp = m_cue_points; - assert(pp); - - CuePoint* const pCP = pp[0]; - assert(pCP); - assert(pCP->GetTimeCode() >= 0); - - return pCP; -} - - -const CuePoint* Cues::GetLast() const -{ - LoadCuePoint(); //init cues - - const size_t count = m_count + m_preload_count; - - 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); - - return pCP; -} - - -const CuePoint* Cues::GetNext(const CuePoint* pCurr) const -{ - if (pCurr == NULL) - return NULL; - - assert(pCurr->GetTimeCode() >= 0); - assert(m_cue_points); - assert(m_count >= 1); - - 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); - - return pNext; -} - - -const BlockEntry* Cues::GetBlock( - const CuePoint* pCP, - const CuePoint::TrackPosition* pTP) const -{ - if (pCP == NULL) - return NULL; - - if (pTP == NULL) - return NULL; - - return m_pSegment->GetBlock(*pCP, *pTP); -} - - -const BlockEntry* Segment::GetBlock( - const CuePoint& cp, - const CuePoint::TrackPosition& tp) -{ - Cluster** const ii = m_clusters; - Cluster** i = ii; - - const long count = m_clusterCount + m_clusterPreloadCount; - - Cluster** const jj = ii + count; - Cluster** j = jj; - - while (i < j) - { - //INVARIANT: - //[ii, i) < pTP->m_pos - //[i, j) ? - //[j, jj) > pTP->m_pos - - Cluster** const k = i + (j - i) / 2; - assert(k < jj); - - Cluster* const pCluster = *k; - assert(pCluster); - - const long long pos_ = pCluster->m_pos; - assert(pos_); - - const long long pos = pos_ * ((pos_ < 0) ? -1 : 1); - - if (pos < tp.m_pos) - i = k + 1; - else if (pos > tp.m_pos) - j = k; - else - return pCluster->GetEntry(cp, tp); - } - - assert(i == j); - - Cluster* const pCluster = Cluster::Parse(this, -1, tp.m_pos); - const ptrdiff_t idx = i - m_clusters; - - PreloadCluster(pCluster, idx); - assert(m_clusters); - assert(m_clusterPreloadCount > 0); - assert(m_clusters[idx] == pCluster); - - return pCluster->GetEntry(cp, tp); -} - - - -CuePoint::CuePoint(size_t idx, long long pos) : - m_index(idx), - m_timecode(-1 * pos), - m_track_positions(NULL), - m_track_positions_count(0) -{ - assert(pos > 0); -} - - -CuePoint::~CuePoint() -{ - delete[] m_track_positions; -} - - -void CuePoint::Load(IMkvReader* pReader) -{ - //odbgstream os; - //os << "CuePoint::Load(begin): timecode=" << m_timecode << endl; - - if (m_timecode >= 0) //already loaded - return; - - assert(m_track_positions == NULL); - assert(m_track_positions_count == 0); - - long long pos_ = -m_timecode; - - long long stop; - - { - long len; - - const long long id = ReadUInt(pReader, pos_, len); - assert(id == 0x3B); //CuePoint ID - //assert((pos + len) <= stop); - - pos_ += len; //consume ID - - const long long size = ReadUInt(pReader, pos_, len); - assert(size >= 0); - //assert((pos + len) <= stop); - - pos_ += len; //consume Size field - //assert((pos + size) <= stop); - - //pos_ now points to start of payload - - stop = pos_ + size; - } - - long long pos = pos_; - - //First count number of track positions - - while (pos < stop) - { - long len; - - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume ID - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); - assert((pos + len) <= stop); - - pos += len; //consume Size field - assert((pos + size) <= stop); - - if (id == 0x33) //CueTime ID - m_timecode = UnserializeUInt(pReader, pos, size); - - else if (id == 0x37) //CueTrackPosition(s) ID - ++m_track_positions_count; - - pos += size; //consume payload - assert(pos <= stop); - } - - assert(m_timecode >= 0); - assert(m_track_positions_count > 0); - - //os << "CuePoint::Load(cont'd): idpos=" << idpos - // << " timecode=" << m_timecode - // << endl; - - m_track_positions = new TrackPosition[m_track_positions_count]; - - //Now parse track positions - - TrackPosition* p = m_track_positions; - pos = pos_; - - while (pos < stop) - { - long len; - - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume ID - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); - assert((pos + len) <= stop); - - pos += len; //consume Size field - assert((pos + size) <= stop); - - if (id == 0x37) //CueTrackPosition(s) ID - { - TrackPosition& tp = *p++; - tp.Parse(pReader, pos, size); - } - - pos += size; //consume payload - assert(pos <= stop); - } - - assert(size_t(p - m_track_positions) == m_track_positions_count); -} - - - -void CuePoint::TrackPosition::Parse( - IMkvReader* pReader, - long long start_, - long long size_) -{ - const long long stop = start_ + size_; - long long pos = start_; - - m_track = -1; - m_pos = -1; - m_block = 1; //default - - while (pos < stop) - { - long len; - - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume ID - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); - assert((pos + len) <= stop); - - pos += len; //consume Size field - assert((pos + size) <= stop); - - if (id == 0x77) //CueTrack ID - m_track = UnserializeUInt(pReader, pos, size); - - else if (id == 0x71) //CueClusterPos ID - m_pos = UnserializeUInt(pReader, pos, size); - - else if (id == 0x1378) //CueBlockNumber - 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); -} - - -const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const -{ - assert(pTrack); - - const long long n = pTrack->GetNumber(); - - const TrackPosition* i = m_track_positions; - const TrackPosition* const j = i + m_track_positions_count; - - while (i != j) - { - const TrackPosition& p = *i++; - - if (p.m_track == n) - return &p; - } - - return NULL; //no matching track number found -} - - -long long CuePoint::GetTimeCode() const -{ - return m_timecode; -} - -long long CuePoint::GetTime(Segment* pSegment) const -{ - assert(pSegment); - assert(m_timecode >= 0); - - const SegmentInfo* const pInfo = pSegment->GetInfo(); - assert(pInfo); - - const long long scale = pInfo->GetTimeCodeScale(); - assert(scale >= 1); - - const long long time = scale * m_timecode; - - return time; -} - - -long long Segment::Unparsed() const -{ - const long long stop = m_start + m_size; - - const long long result = stop - m_pos; - assert(result >= 0); - - return result; -} - - -Cluster* Segment::GetFirst() -{ - if ((m_clusters == NULL) || (m_clusterCount <= 0)) - return &m_eos; - - Cluster* const pCluster = m_clusters[0]; - assert(pCluster); - - return pCluster; -} - - -Cluster* Segment::GetLast() -{ - if ((m_clusters == NULL) || (m_clusterCount <= 0)) - return &m_eos; - - const long idx = m_clusterCount - 1; - - Cluster* const pCluster = m_clusters[idx]; - assert(pCluster); - - return pCluster; -} - - -unsigned long Segment::GetCount() const -{ - return m_clusterCount; -} - - -Cluster* Segment::GetNext(const Cluster* pCurr) -{ - assert(pCurr); - assert(pCurr != &m_eos); - assert(m_clusters); - - long idx = pCurr->m_index; - - if (idx >= 0) - { - assert(m_clusterCount > 0); - assert(idx < m_clusterCount); - assert(pCurr == m_clusters[idx]); - - ++idx; - - if (idx >= m_clusterCount) - return &m_eos; //caller will LoadCluster as desired - - Cluster* const pNext = m_clusters[idx]; - assert(pNext); - assert(pNext->m_index >= 0); - assert(pNext->m_index == idx); - - return pNext; - } - - assert(m_clusterPreloadCount > 0); - - const long long off_ = pCurr->m_pos; - const long long off = off_ * ((off_ < 0) ? -1 : 1); - - long long pos = m_start + off; - const long long stop = m_start + m_size; //end of segment - - { - long len; - - long long result = GetUIntLength(m_pReader, pos, len); - assert(result == 0); //TODO - assert((pos + len) <= stop); //TODO - - const long long id = ReadUInt(m_pReader, pos, len); - assert(id == 0x0F43B675); //Cluster ID //TODO - - pos += len; //consume ID - - //Read Size - result = GetUIntLength(m_pReader, pos, len); - assert(result == 0); //TODO - assert((pos + len) <= stop); //TODO - - const long long size = ReadUInt(m_pReader, pos, len); - assert(size > 0); //TODO - assert((pCurr->m_size <= 0) || (pCurr->m_size == size)); - - pos += len; //consume length of size of element - assert((pos + size) <= stop); //TODO - - //Pos now points to start of payload - - pos += size; //consume payload - } - - long long off_next = 0; - - while (pos < stop) - { - long len; - - long long result = GetUIntLength(m_pReader, pos, len); - assert(result == 0); //TODO - assert((pos + len) <= stop); //TODO - - const long long idpos = pos; //pos of next (potential) cluster - - const long long id = ReadUInt(m_pReader, idpos, len); - assert(id > 0); //TODO - - pos += len; //consume ID - - //Read Size - result = GetUIntLength(m_pReader, pos, len); - assert(result == 0); //TODO - assert((pos + len) <= stop); //TODO - - const long long size = ReadUInt(m_pReader, pos, len); - assert(size >= 0); //TODO - - pos += len; //consume length of size of element - assert((pos + size) <= stop); //TODO - - //Pos now points to start of payload - - if (size == 0) //weird - continue; - - if (id == 0x0F43B675) //Cluster ID - { - off_next = idpos - m_start; - break; - } - - pos += size; //consume payload - } - - if (off_next <= 0) - return 0; - - Cluster** const ii = m_clusters + m_clusterCount; - Cluster** i = ii; - - Cluster** const jj = ii + m_clusterPreloadCount; - Cluster** j = jj; - - while (i < j) - { - //INVARIANT: - //[0, i) < pos_next - //[i, j) ? - //[j, jj) > pos_next - - Cluster** const k = i + (j - i) / 2; - assert(k < jj); - - Cluster* const pNext = *k; - assert(pNext); - assert(pNext->m_index < 0); - - const long long pos_ = pNext->m_pos; - assert(pos_); - - pos = pos_ * ((pos_ < 0) ? -1 : 1); - - if (pos < off_next) - i = k + 1; - else if (pos > off_next) - j = k; - else - return pNext; - } - - assert(i == j); - - Cluster* const pNext = Cluster::Parse(this, -1, off_next); - const ptrdiff_t idx_next = i - m_clusters; //insertion position - - PreloadCluster(pNext, idx_next); - assert(m_clusters); - assert(idx_next < m_clusterSize); - assert(m_clusters[idx_next] == pNext); - - return pNext; -} - - -Cluster* Segment::FindCluster(long long time_ns) -{ - if ((m_clusters == NULL) || (m_clusterCount <= 0)) - return &m_eos; - - { - Cluster* const pCluster = m_clusters[0]; - assert(pCluster); - assert(pCluster->m_index == 0); - - if (time_ns <= pCluster->GetTime()) - return pCluster; - } - - //Binary search of cluster array - - long i = 0; - long j = m_clusterCount; - - while (i < j) - { - //INVARIANT: - //[0, i) <= time_ns - //[i, j) ? - //[j, m_clusterCount) > time_ns - - const long k = i + (j - i) / 2; - assert(k < m_clusterCount); - - Cluster* const pCluster = m_clusters[k]; - assert(pCluster); - assert(pCluster->m_index == k); - - const long long t = pCluster->GetTime(); - - if (t <= time_ns) - i = k + 1; - else - j = k; - - assert(i <= j); - } - - assert(i == j); - assert(i > 0); - assert(i <= m_clusterCount); - - const long k = i - 1; - - Cluster* const pCluster = m_clusters[k]; - assert(pCluster); - assert(pCluster->m_index == k); - assert(pCluster->GetTime() <= time_ns); - - return pCluster; -} - - -const BlockEntry* Segment::Seek( - long long time_ns, - const Track* pTrack) -{ - 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); - - Cluster* const pCluster = *--lo; - assert(pCluster); - assert(pCluster->GetTime() <= time_ns); - - return pCluster->GetEntry(pTrack); - } - - 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 pBlockEntry = pCluster->GetEntry(pTrack); - assert(pBlockEntry); - - if (!pBlockEntry->EOS()) //found a keyframe - { - const Block* const pBlock = pBlockEntry->GetBlock(); - assert(pBlock); - - //TODO: this isn't necessarily the keyframe we want, - //since there might another keyframe on this same - //cluster with a greater timecode that but that is - //still less than the requested time. For now we - //simply return the first keyframe we find. - - if (pBlock->GetTime(pCluster) <= time_ns) - return pBlockEntry; - } - } - - const VideoTrack* const pVideo = static_cast(pTrack); - - while (lo != i) - { - pCluster = *--lo; - assert(pCluster); - assert(pCluster->GetTime() <= time_ns); - - const BlockEntry* const pBlockEntry = pCluster->GetMaxKey(pVideo); - assert(pBlockEntry); - - if (!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(); -} - - -#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 - - -Tracks* Segment::GetTracks() const -{ - return m_pTracks; -} - - -const SegmentInfo* Segment::GetInfo() const -{ - return m_pInfo; -} - - -const Cues* Segment::GetCues() const -{ - return m_pCues; -} - - -long long Segment::GetDuration() const -{ - assert(m_pInfo); - return m_pInfo->GetDuration(); -} - - -SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_) : - m_pSegment(pSegment), - m_start(start), - m_size(size_), - m_pMuxingAppAsUTF8(NULL), - m_pWritingAppAsUTF8(NULL), - m_pTitleAsUTF8(NULL) -{ - IMkvReader* const pReader = m_pSegment->m_pReader; - - long long pos = start; - const long long stop = start + size_; - - m_timecodeScale = 1000000; - m_duration = -1; - - while (pos < stop) - { - if (Match(pReader, pos, 0x0AD7B1, m_timecodeScale)) - assert(m_timecodeScale > 0); - - else if (Match(pReader, pos, 0x0489, m_duration)) - assert(m_duration >= 0); - - else if (Match(pReader, pos, 0x0D80, m_pMuxingAppAsUTF8)) //[4D][80] - assert(m_pMuxingAppAsUTF8); - - else if (Match(pReader, pos, 0x1741, m_pWritingAppAsUTF8)) //[57][41] - assert(m_pWritingAppAsUTF8); - - else if (Match(pReader, pos, 0x3BA9, m_pTitleAsUTF8)) //[7B][A9] - assert(m_pTitleAsUTF8); - - else - { - long len; - - const long long id = ReadUInt(pReader, pos, len); - //id; - assert(id >= 0); - assert((pos + len) <= stop); - - pos += len; //consume id - assert((stop - pos) > 0); - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); - assert((pos + len) <= stop); - - pos += len + size; //consume size and payload - assert(pos <= stop); - } - } - - assert(pos == stop); -} - -SegmentInfo::~SegmentInfo() -{ - if (m_pMuxingAppAsUTF8) - { - delete[] m_pMuxingAppAsUTF8; - m_pMuxingAppAsUTF8 = NULL; - } - - if (m_pWritingAppAsUTF8) - { - delete[] m_pWritingAppAsUTF8; - m_pWritingAppAsUTF8 = NULL; - } - - if (m_pTitleAsUTF8) - { - delete[] m_pTitleAsUTF8; - m_pTitleAsUTF8 = NULL; - } -} - -long long SegmentInfo::GetTimeCodeScale() const -{ - return m_timecodeScale; -} - - -long long SegmentInfo::GetDuration() const -{ - if (m_duration < 0) - return -1; - - assert(m_timecodeScale >= 1); - - const double dd = double(m_duration) * double(m_timecodeScale); - const long long d = static_cast(dd); - - return d; -} - -const char* SegmentInfo::GetMuxingAppAsUTF8() const -{ - return m_pMuxingAppAsUTF8; -} - - -const char* SegmentInfo::GetWritingAppAsUTF8() const -{ - return m_pWritingAppAsUTF8; -} - -const char* SegmentInfo::GetTitleAsUTF8() const -{ - return m_pTitleAsUTF8; -} - -Track::Track(Segment* pSegment, const Info& i) : - m_pSegment(pSegment), - m_info(i) -{ -} - -Track::~Track() -{ - Info& info = const_cast(m_info); - info.Clear(); -} - -Track::Info::Info(): - type(-1), - number(-1), - uid(-1), - nameAsUTF8(NULL), - codecId(NULL), - codecPrivate(NULL), - codecPrivateSize(0), - codecNameAsUTF8(NULL) -{ -} - - -void Track::Info::Clear() -{ - delete[] nameAsUTF8; - nameAsUTF8 = NULL; - - delete[] codecId; - codecId = NULL; - - delete[] codecPrivate; - codecPrivate = NULL; - - codecPrivateSize = 0; - - delete[] codecNameAsUTF8; - codecNameAsUTF8 = NULL; -} - -const BlockEntry* Track::GetEOS() const -{ - return &m_eos; -} - -long long Track::GetType() const -{ - return m_info.type; -} - -long long Track::GetNumber() const -{ - return m_info.number; -} - -const char* Track::GetNameAsUTF8() const -{ - return m_info.nameAsUTF8; -} - -const char* Track::GetCodecNameAsUTF8() const -{ - return m_info.codecNameAsUTF8; -} - - -const char* Track::GetCodecId() const -{ - return m_info.codecId; -} - -const unsigned char* Track::GetCodecPrivate(size_t& size) const -{ - size = m_info.codecPrivateSize; - return m_info.codecPrivate; -} - - -long Track::GetFirst(const BlockEntry*& pBlockEntry) const -{ - Cluster* pCluster = m_pSegment->GetFirst(); - - //If Segment::GetFirst returns NULL, then this must be a network - //download, and we haven't loaded any clusters yet. In this case, - //returning NULL from Track::GetFirst means the same thing. - - for (int i = 0; i < 100; ++i) //arbitrary upper bound - { - if (pCluster == NULL) - { - pBlockEntry = GetEOS(); - return 1; - } - - if (pCluster->EOS()) - { - if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded - { - pBlockEntry = GetEOS(); - return 1; - } - - pBlockEntry = 0; - return E_BUFFER_NOT_FULL; - } - - pBlockEntry = pCluster->GetFirst(); - - while (pBlockEntry) - { - const Block* const pBlock = pBlockEntry->GetBlock(); - assert(pBlock); - - if (pBlock->GetTrackNumber() == m_info.number) - return 0; - - pBlockEntry = pCluster->GetNext(pBlockEntry); - } - - pCluster = m_pSegment->GetNext(pCluster); - } - - //NOTE: if we get here, it means that we didn't find a block with - //a matching track number. We interpret that as an error (which - //might be too conservative). - - pBlockEntry = GetEOS(); //so we can return a non-NULL value - return 1; -} - - -long Track::GetNext( - const BlockEntry* pCurrEntry, - const BlockEntry*& pNextEntry) const -{ - assert(pCurrEntry); - assert(!pCurrEntry->EOS()); //? - - const Block* const pCurrBlock = pCurrEntry->GetBlock(); - assert(pCurrBlock->GetTrackNumber() == m_info.number); - - Cluster* pCluster = pCurrEntry->GetCluster(); - assert(pCluster); - assert(!pCluster->EOS()); - - pNextEntry = pCluster->GetNext(pCurrEntry); - - for (int i = 0; i < 100; ++i) //arbitrary upper bound to search - { - while (pNextEntry) - { - const Block* const pNextBlock = pNextEntry->GetBlock(); - assert(pNextBlock); - - if (pNextBlock->GetTrackNumber() == m_info.number) - return 0; - - pNextEntry = pCluster->GetNext(pNextEntry); - } - - pCluster = m_pSegment->GetNext(pCluster); - - if (pCluster == NULL) - { - pNextEntry = GetEOS(); - return 1; - } - - if (pCluster->EOS()) - { - if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded - { - pNextEntry = GetEOS(); - return 1; - } - - //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 - //calls GetNext again, which repeats the same search. This is - //a pathological case, since the only way it can happen is if - //there exists a long sequence of clusters none of which contain a - // block from this track. One way around this problem is for the - //caller to be smarter when he loads another cluster: don't call - //us back until you have a cluster that contains a block from this - //track. (Of course, that's not cheap either, since our caller - //would have to scan the each cluster as it's loaded, so that - //would just push back the problem.) - - pNextEntry = NULL; - return E_BUFFER_NOT_FULL; - } - - pNextEntry = pCluster->GetFirst(); - } - - //NOTE: if we get here, it means that we didn't find a block with - //a matching track number after lots of searching, so we give - //up trying. - - pNextEntry = GetEOS(); //so we can return a non-NULL value - return 1; -} - - -Track::EOSBlock::EOSBlock() -{ -} - - -bool Track::EOSBlock::EOS() const -{ - return true; -} - - -Cluster* Track::EOSBlock::GetCluster() const -{ - return NULL; -} - - -size_t Track::EOSBlock::GetIndex() const -{ - return 0; -} - - -const Block* Track::EOSBlock::GetBlock() const -{ - return NULL; -} - - -bool Track::EOSBlock::IsBFrame() const -{ - return false; -} - - -VideoTrack::VideoTrack(Segment* pSegment, const Info& i) : - Track(pSegment, i), - m_width(-1), - m_height(-1), - m_rate(-1) -{ - assert(i.type == 1); - assert(i.number > 0); - - IMkvReader* const pReader = pSegment->m_pReader; - - const Settings& s = i.settings; - assert(s.start >= 0); - assert(s.size >= 0); - - long long pos = s.start; - assert(pos >= 0); - - const long long stop = pos + s.size; - - while (pos < stop) - { -#ifdef _DEBUG - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO: handle error case - assert((pos + len) <= stop); -#endif - if (Match(pReader, pos, 0x30, m_width)) - ; - else if (Match(pReader, pos, 0x3A, m_height)) - ; - else if (Match(pReader, pos, 0x0383E3, m_rate)) - ; - else - { - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO: handle error case - assert((pos + len) <= stop); - - pos += len; //consume id - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); //TODO: handle error case - assert((pos + len) <= stop); - - pos += len; //consume length of size - assert((pos + size) <= stop); - - //pos now designates start of payload - - pos += size; //consume payload - assert(pos <= stop); - } - } - - return; -} - - -bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const -{ - assert(pBlockEntry); - - const Block* const pBlock = pBlockEntry->GetBlock(); - assert(pBlock); - assert(pBlock->GetTrackNumber() == m_info.number); - - return pBlock->IsKey(); -} - - -long long VideoTrack::GetWidth() const -{ - return m_width; -} - - -long long VideoTrack::GetHeight() const -{ - return m_height; -} - - -double VideoTrack::GetFrameRate() const -{ - return m_rate; -} - - -AudioTrack::AudioTrack(Segment* pSegment, const Info& i) : - Track(pSegment, i), - m_rate(0.0), - m_channels(0), - m_bitDepth(-1) -{ - assert(i.type == 2); - assert(i.number > 0); - - IMkvReader* const pReader = pSegment->m_pReader; - - const Settings& s = i.settings; - assert(s.start >= 0); - assert(s.size >= 0); - - long long pos = s.start; - assert(pos >= 0); - - const long long stop = pos + s.size; - - while (pos < stop) - { -#ifdef _DEBUG - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO: handle error case - assert((pos + len) <= stop); -#endif - if (Match(pReader, pos, 0x35, m_rate)) - ; - else if (Match(pReader, pos, 0x1F, m_channels)) - ; - else if (Match(pReader, pos, 0x2264, m_bitDepth)) - ; - else - { - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO: handle error case - assert((pos + len) <= stop); - - pos += len; //consume id - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); //TODO: handle error case - assert((pos + len) <= stop); - - pos += len; //consume length of size - assert((pos + size) <= stop); - - //pos now designates start of payload - - pos += size; //consume payload - assert(pos <= stop); - } - } - - return; -} - - -bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const -{ - assert(pBlockEntry); - - const Block* const pBlock = pBlockEntry->GetBlock(); - assert(pBlock); - assert(pBlock->GetTrackNumber() == m_info.number); - - return true; -} - - -double AudioTrack::GetSamplingRate() const -{ - return m_rate; -} - - -long long AudioTrack::GetChannels() const -{ - return m_channels; -} - -long long AudioTrack::GetBitDepth() const -{ - return m_bitDepth; -} - -Tracks::Tracks(Segment* pSegment, long long start, long long size_) : - m_pSegment(pSegment), - m_start(start), - m_size(size_), - m_trackEntries(NULL), - m_trackEntriesEnd(NULL) -{ - long long stop = m_start + m_size; - IMkvReader* const pReader = m_pSegment->m_pReader; - - long long pos1 = m_start; - int count = 0; - - while (pos1 < stop) - { - long len; - const long long id = ReadUInt(pReader, pos1, len); - assert(id >= 0); - assert((pos1 + len) <= stop); - - pos1 += len; //consume id - - const long long size = ReadUInt(pReader, pos1, len); - assert(size >= 0); - assert((pos1 + len) <= stop); - - pos1 += len; //consume length of size - - //pos now desinates start of element - if (id == 0x2E) //TrackEntry ID - ++count; - - pos1 += size; //consume payload - assert(pos1 <= stop); - } - - if (count <= 0) - return; - - m_trackEntries = new Track*[count]; - m_trackEntriesEnd = m_trackEntries; - - long long pos = m_start; - - while (pos < stop) - { - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); - assert((pos + len) <= stop); - - pos += len; //consume id - - const long long size1 = ReadUInt(pReader, pos, len); - assert(size1 >= 0); - assert((pos + len) <= stop); - - pos += len; //consume length of size - - //pos now desinates start of element - - if (id == 0x2E) //TrackEntry ID - ParseTrackEntry(pos, size1, *m_trackEntriesEnd++); - - pos += size1; //consume payload - assert(pos <= stop); - } -} - - -unsigned long Tracks::GetTracksCount() const -{ - const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries; - assert(result >= 0); - - return static_cast(result); -} - - -void Tracks::ParseTrackEntry( - long long start, - long long size, - Track*& pTrack) -{ - IMkvReader* const pReader = m_pSegment->m_pReader; - - long long pos = start; - const long long stop = start + size; - - Track::Info i; - - Track::Settings videoSettings; - videoSettings.start = -1; - - Track::Settings audioSettings; - audioSettings.start = -1; - - while (pos < stop) - { -#ifdef _DEBUG - long len; - const long long id = ReadUInt(pReader, pos, len); - len; - id; -#endif - if (Match(pReader, pos, 0x57, i.number)) - assert(i.number > 0); - else if (Match(pReader, pos, 0x33C5, i.uid)) - ; - else if (Match(pReader, pos, 0x03, i.type)) - ; - else if (Match(pReader, pos, 0x136E, i.nameAsUTF8)) - assert(i.nameAsUTF8); - else if (Match(pReader, pos, 0x06, i.codecId)) - ; - else if (Match(pReader, - pos, - 0x23A2, - i.codecPrivate, - i.codecPrivateSize)) - ; - else if (Match(pReader, pos, 0x058688, i.codecNameAsUTF8)) - assert(i.codecNameAsUTF8); - else - { - long len; - - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO: handle error case - assert((pos + len) <= stop); - - pos += len; //consume id - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); //TODO: handle error case - assert((pos + len) <= stop); - - pos += len; //consume length of size - const long long start = pos; - - pos += size; //consume payload - assert(pos <= stop); - - if (id == 0x60) - { - videoSettings.start = start; - videoSettings.size = size; - } - else if (id == 0x61) - { - audioSettings.start = start; - audioSettings.size = size; - } - } - } - - assert(pos == stop); - //TODO: propertly vet info.number, to ensure both its existence, - //and that it is unique among all tracks. - assert(i.number > 0); - - //TODO: vet settings, to ensure that video settings (0x60) - //were specified when type = 1, and that audio settings (0x61) - //were specified when type = 2. - if (i.type == 1) //video - { - assert(audioSettings.start < 0); - assert(videoSettings.start >= 0); - - i.settings = videoSettings; - - VideoTrack* const t = new VideoTrack(m_pSegment, i); - assert(t); //TODO - pTrack = t; - } - else if (i.type == 2) //audio - { - assert(videoSettings.start < 0); - assert(audioSettings.start >= 0); - - i.settings = audioSettings; - - AudioTrack* const t = new AudioTrack(m_pSegment, i); - assert(t); //TODO - pTrack = t; - } - else - { - // for now we do not support other track types yet. - // TODO: support other track types - i.Clear(); - - pTrack = NULL; - } - - return; -} - - -Tracks::~Tracks() -{ - Track** i = m_trackEntries; - Track** const j = m_trackEntriesEnd; - - while (i != j) - { - Track* const pTrack = *i++; - delete pTrack; - } - - delete[] m_trackEntries; -} - - -Track* Tracks::GetTrackByNumber(unsigned long tn_) const -{ - const long long tn = tn_; - - Track** i = m_trackEntries; - Track** const j = m_trackEntriesEnd; - - while (i != j) - { - Track* const pTrack = *i++; - - if (pTrack == NULL) - continue; - - if (tn == pTrack->GetNumber()) - return pTrack; - } - - return NULL; //not found -} - - -Track* Tracks::GetTrackByIndex(unsigned long idx) const -{ - const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries; - - if (idx >= static_cast(count)) - return NULL; - - return m_trackEntries[idx]; -} - - -void Cluster::Load() -{ - assert(m_pSegment); - assert(m_pos); - assert(m_size); - - if (m_pos > 0) //loaded - { - assert(m_size > 0); - assert(m_timecode >= 0); - return; - } - - assert(m_pos < 0); //not loaded yet - assert(m_size < 0); - assert(m_timecode < 0); - - IMkvReader* const pReader = m_pSegment->m_pReader; - - m_pos *= -1; //relative to segment - long long pos = m_pSegment->m_start + m_pos; //absolute - - long len; - - const long long id_ = ReadUInt(pReader, pos, len); - assert(id_ >= 0); - assert(id_ == 0x0F43B675); //Cluster ID - - pos += len; //consume id - - const long long size_ = ReadUInt(pReader, pos, len); - assert(size_ >= 0); - - pos += len; //consume size - - m_size = size_; - const long long stop = pos + size_; - - long long timecode = -1; - - while (pos < stop) - { - if (Match(pReader, pos, 0x67, timecode)) - break; - else - { - 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 - break; - - if (id == 0x23) //SimpleBlock ID - break; - - pos += size; //consume payload - assert(pos <= stop); - } - } - - assert(pos <= stop); - assert(timecode >= 0); - - m_timecode = timecode; -} - - -Cluster* Cluster::Parse( - Segment* pSegment, - long idx, - long long off) -{ - assert(pSegment); - assert(off >= 0); - assert(off < pSegment->m_size); - - Cluster* const pCluster = new Cluster(pSegment, idx, -off); - assert(pCluster); - - return pCluster; -} - - -Cluster::Cluster() : - m_pSegment(NULL), - m_index(0), - m_pos(0), - m_size(0), - m_timecode(0), - m_entries(NULL), - m_entriesCount(0) -{ -} - - -Cluster::Cluster( - Segment* pSegment, - long idx, - long long off) : - m_pSegment(pSegment), - m_index(idx), - m_pos(off), - m_size(-1), - m_timecode(-1), - m_entries(NULL), - m_entriesCount(0) -{ -} - - -Cluster::~Cluster() -{ - BlockEntry** i = m_entries; - BlockEntry** const j = m_entries + m_entriesCount; - - while (i != j) - { - BlockEntry* p = *i++; - assert(p); - - delete p; - } - - delete[] m_entries; -} - - -bool Cluster::EOS() const -{ - return (m_pSegment == NULL); -} - - -void Cluster::LoadBlockEntries() -{ - if (m_entries) - return; - - assert(m_pSegment); - assert(m_pos); - assert(m_size); - assert(m_entriesCount == 0); - - IMkvReader* const pReader = m_pSegment->m_pReader; - - if (m_pos < 0) - m_pos *= -1; //relative to segment - - long long pos = m_pSegment->m_start + m_pos; //absolute - - { - long len; - - const long long id = ReadUInt(pReader, pos, len); - id; - assert(id >= 0); - assert(id == 0x0F43B675); //Cluster ID - - pos += len; //consume id - - const long long size = ReadUInt(pReader, pos, len); - assert(size > 0); - - pos += len; //consume size - - //pos now points to start of payload - - if (m_size >= 0) - assert(size == m_size); - else - m_size = size; - } - - const long long stop = pos + m_size; - long long timecode = -1; //of cluster itself - - //First count the number of entries - - long long idx = pos; //points to start of payload - m_entriesCount = 0; - - while (idx < stop) - { - if (Match(pReader, idx, 0x67, timecode)) - { - if (m_timecode >= 0) - assert(timecode == m_timecode); - else - m_timecode = timecode; - } - else - { - long len; - - const long long id = ReadUInt(pReader, idx, len); - assert(id >= 0); //TODO - assert((idx + len) <= stop); - - idx += len; //consume id - - const long long size = ReadUInt(pReader, idx, len); - assert(size >= 0); //TODO - assert((idx + len) <= stop); - - idx += len; //consume size - - if (id == 0x20) //BlockGroup ID - ++m_entriesCount; - else if (id == 0x23) //SimpleBlock ID - ++m_entriesCount; - - idx += size; //consume payload - assert(idx <= stop); - } - } - - assert(idx == stop); - assert(m_timecode >= 0); - - if (m_entriesCount == 0) //TODO: handle empty clusters - return; - - m_entries = new BlockEntry*[m_entriesCount]; - size_t index = 0; - - while (pos < stop) - { - if (Match(pReader, pos, 0x67, timecode)) - assert(timecode == m_timecode); - else - { - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume id - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume size - - if (id == 0x20) //BlockGroup ID - ParseBlockGroup(pos, size, index++); - else if (id == 0x23) //SimpleBlock ID - ParseSimpleBlock(pos, size, index++); - - pos += size; //consume payload - assert(pos <= stop); - } - } - - assert(pos == stop); - assert(timecode >= 0); - assert(index == m_entriesCount); -} - - - -long long Cluster::GetTimeCode() -{ - Load(); - return m_timecode; -} - - -long long Cluster::GetTime() -{ - const long long tc = GetTimeCode(); - assert(tc >= 0); - - const SegmentInfo* const pInfo = m_pSegment->GetInfo(); - assert(pInfo); - - const long long scale = pInfo->GetTimeCodeScale(); - assert(scale >= 1); - - const long long t = m_timecode * scale; - - return t; -} - - -long long Cluster::GetFirstTime() -{ - const BlockEntry* const pEntry = GetFirst(); - - if (pEntry == NULL) //empty cluster - return GetTime(); - - const Block* const pBlock = pEntry->GetBlock(); - assert(pBlock); - - return pBlock->GetTime(this); -} - - -long long Cluster::GetLastTime() -{ - const BlockEntry* const pEntry = GetLast(); - - if (pEntry == NULL) //empty cluster - return GetTime(); - - const Block* const pBlock = pEntry->GetBlock(); - assert(pBlock); - - return pBlock->GetTime(this); -} - - -void Cluster::ParseBlockGroup(long long start, long long size, size_t index) -{ - assert(m_entries); - assert(m_entriesCount); - assert(index < m_entriesCount); - - BlockGroup* const pGroup = - new (std::nothrow) BlockGroup(this, index, start, size); - assert(pGroup); //TODO - - m_entries[index] = pGroup; -} - - - -void Cluster::ParseSimpleBlock(long long start, long long size, size_t index) -{ - assert(m_entries); - assert(m_entriesCount); - assert(index < m_entriesCount); - - SimpleBlock* const pSimpleBlock = - new (std::nothrow) SimpleBlock(this, index, start, size); - assert(pSimpleBlock); //TODO - - m_entries[index] = pSimpleBlock; -} - - -const BlockEntry* Cluster::GetFirst() -{ - LoadBlockEntries(); - //assert(m_entries); - //assert(m_entriesCount >= 1); - - if ((m_entries == NULL) || (m_entriesCount == 0)) - return NULL; - - const BlockEntry* const pFirst = m_entries[0]; - assert(pFirst); - - return pFirst; -} - - -const BlockEntry* Cluster::GetLast() -{ - LoadBlockEntries(); - //assert(m_entries); - //assert(m_entriesCount >= 1); - - if ((m_entries == NULL) || (m_entriesCount == 0)) - return NULL; - - const size_t idx = m_entriesCount - 1; - - const BlockEntry* const pLast = m_entries[idx]; - assert(pLast); - - return pLast; -} - - -const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const -{ - assert(pEntry); - assert(m_entries); - assert(m_entriesCount); - - size_t idx = pEntry->GetIndex(); - assert(idx < m_entriesCount); - assert(m_entries[idx] == pEntry); - - ++idx; - - if (idx >= m_entriesCount) - return NULL; - - return m_entries[idx]; -} - - -const BlockEntry* Cluster::GetEntry(const Track* pTrack) -{ - assert(pTrack); - - if (m_pSegment == NULL) //EOS - return pTrack->GetEOS(); - - LoadBlockEntries(); - - if ((m_entries == NULL) || (m_entriesCount == 0)) - return NULL; - - BlockEntry** i = m_entries; - assert(i); - - BlockEntry** const j = i + m_entriesCount; - - 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)) - return pEntry; - } - - return pTrack->GetEOS(); //no satisfactory block found -} - - -const BlockEntry* -Cluster::GetEntry( - const CuePoint& cp, - const CuePoint::TrackPosition& tp) -{ - assert(m_pSegment); - - LoadBlockEntries(); - - if (m_entries == NULL) - return NULL; - - const long long count = m_entriesCount; - - 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(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) - { - 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); - - if (tc_ < tc) - continue; - - if (tc_ > tc) - return NULL; - - const Tracks* const pTracks = m_pSegment->GetTracks(); - assert(pTracks); - - const long tn = static_cast(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; -} - - -const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack) -{ - assert(pTrack); - - if (m_pSegment == NULL) //EOS - return pTrack->GetEOS(); - - LoadBlockEntries(); - //assert(m_entries); - - BlockEntry** i = m_entries + m_entriesCount; - 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 -} - - - -BlockEntry::BlockEntry() -{ -} - - -BlockEntry::~BlockEntry() -{ -} - - -SimpleBlock::SimpleBlock( - Cluster* pCluster, - size_t idx, - long long start, - long long size) : - m_pCluster(pCluster), - m_index(idx), - m_block(start, size, pCluster->m_pSegment->m_pReader) -{ -} - - -bool SimpleBlock::EOS() const -{ - return false; -} - - -Cluster* SimpleBlock::GetCluster() const -{ - return m_pCluster; -} - - -size_t SimpleBlock::GetIndex() const -{ - return m_index; -} - - -const Block* SimpleBlock::GetBlock() const -{ - return &m_block; -} - - -bool SimpleBlock::IsBFrame() const -{ - return false; -} - - -BlockGroup::BlockGroup( - Cluster* pCluster, - size_t idx, - long long start, - long long size_) : - m_pCluster(pCluster), - m_index(idx), - m_prevTimeCode(0), - m_nextTimeCode(0), - m_pBlock(NULL) //TODO: accept multiple blocks within a block group -{ - IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader; - - long long pos = start; - const long long stop = start + size_; - - bool bSimpleBlock = false; - bool bReferenceBlock = false; - - while (pos < stop) - { - short t; - - if (Match(pReader, pos, 0x7B, t)) - { - if (t < 0) - m_prevTimeCode = t; - else if (t > 0) - m_nextTimeCode = t; - else - assert(false); - - bReferenceBlock = true; - } - else - { - long len; - const long long id = ReadUInt(pReader, pos, len); - assert(id >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume ID - - const long long size = ReadUInt(pReader, pos, len); - assert(size >= 0); //TODO - assert((pos + len) <= stop); - - pos += len; //consume size - - switch (id) - { - case 0x23: //SimpleBlock ID - bSimpleBlock = true; - //YES, FALL THROUGH TO NEXT CASE - - case 0x21: //Block ID - ParseBlock(pos, size); - break; - - default: - break; - } - - pos += size; //consume payload - assert(pos <= stop); - } - } - - assert(pos == stop); - assert(m_pBlock); - - if (!bSimpleBlock) - m_pBlock->SetKey(!bReferenceBlock); -} - - -BlockGroup::~BlockGroup() -{ - delete m_pBlock; -} - - -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; -} - - -bool BlockGroup::EOS() const -{ - return false; -} - - -Cluster* BlockGroup::GetCluster() const -{ - return m_pCluster; -} - - -size_t BlockGroup::GetIndex() const -{ - return m_index; -} - - -const Block* BlockGroup::GetBlock() const -{ - return m_pBlock; -} - - -short BlockGroup::GetPrevTimeCode() const -{ - return m_prevTimeCode; -} - - -short BlockGroup::GetNextTimeCode() const -{ - return m_nextTimeCode; -} - - -bool BlockGroup::IsBFrame() const -{ - return (m_nextTimeCode > 0); -} - - - -Block::Block(long long start, long long size_, IMkvReader* pReader) : - m_start(start), - m_size(size_) -{ - long long pos = start; - const long long stop = start + size_; - - long len; - - m_track = ReadUInt(pReader, pos, len); - assert(m_track > 0); - assert((pos + len) <= stop); - - pos += len; //consume track number - assert((stop - pos) >= 2); - - m_timecode = Unserialize2SInt(pReader, pos); - - pos += 2; - assert((stop - pos) >= 1); - - const long hr = pReader->Read(pos, 1, &m_flags); - assert(hr == 0L); - - ++pos; - assert(pos <= stop); - - m_frameOff = pos; - - const long long frame_size = stop - pos; - - assert(frame_size <= 2147483647L); - - m_frameSize = static_cast(frame_size); -} - - -long long Block::GetTimeCode(Cluster* pCluster) const -{ - assert(pCluster); - - const long long tc0 = pCluster->GetTimeCode(); - assert(tc0 >= 0); - - const long long tc = tc0 + static_cast(m_timecode); - assert(tc >= 0); - - return tc; //unscaled timecode units -} - - -long long Block::GetTime(Cluster* pCluster) const -{ - assert(pCluster); - - const long long tc = GetTimeCode(pCluster); - - const Segment* const pSegment = pCluster->m_pSegment; - const SegmentInfo* const pInfo = pSegment->GetInfo(); - assert(pInfo); - - const long long scale = pInfo->GetTimeCodeScale(); - assert(scale >= 1); - - const long long ns = tc * scale; - - return ns; -} - - -long long Block::GetTrackNumber() const -{ - return m_track; -} - - -bool Block::IsKey() const -{ - return ((m_flags & static_cast(1 << 7)) != 0); -} - -unsigned char Block::Flags() const { - return m_flags; -} - -void Block::SetKey(bool bKey) -{ - if (bKey) - m_flags |= static_cast(1 << 7); - else - m_flags &= 0x7F; -} - - -long long Block::GetOffset() const -{ - return m_frameOff; -} - - -long Block::GetSize() const -{ - return m_frameSize; -} - - -long Block::Read(IMkvReader* pReader, unsigned char* buf) const -{ - - assert(pReader); - assert(buf); - - const long hr = pReader->Read(m_frameOff, m_frameSize, buf); - - return hr; -} - - -} //end namespace mkvparser diff --git a/media/libstagefright/matroska/mkvparser.hpp b/media/libstagefright/matroska/mkvparser.hpp deleted file mode 100644 index f7d8948..0000000 --- a/media/libstagefright/matroska/mkvparser.hpp +++ /dev/null @@ -1,556 +0,0 @@ -// 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. - -#ifndef MKVPARSER_HPP -#define MKVPARSER_HPP - -#include -#include - -namespace mkvparser -{ - -const int E_FILE_FORMAT_INVALID = -2; -const int E_BUFFER_NOT_FULL = -3; - -class IMkvReader -{ -public: - virtual int Read(long long pos, long len, unsigned char* buf) = 0; - virtual int Length(long long* total, long long* available) = 0; -protected: - virtual ~IMkvReader(); -}; - -long long GetUIntLength(IMkvReader*, long long, long&); -long long ReadUInt(IMkvReader*, long long, long&); -long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&); -long long UnserializeUInt(IMkvReader*, long long pos, long long size); -float Unserialize4Float(IMkvReader*, long long); -double Unserialize8Double(IMkvReader*, long long); -short Unserialize2SInt(IMkvReader*, long long); -signed char Unserialize1SInt(IMkvReader*, long long); -bool Match(IMkvReader*, long long&, unsigned long, long long&); -bool Match(IMkvReader*, long long&, unsigned long, char*&); -bool Match(IMkvReader*, long long&, unsigned long,unsigned char*&, size_t&); -bool Match(IMkvReader*, long long&, unsigned long, double&); -bool Match(IMkvReader*, long long&, unsigned long, short&); - -void GetVersion(int& major, int& minor, int& build, int& revision); - -struct EBMLHeader -{ - EBMLHeader(); - ~EBMLHeader(); - long long m_version; - long long m_readVersion; - long long m_maxIdLength; - long long m_maxSizeLength; - char* m_docType; - long long m_docTypeVersion; - long long m_docTypeReadVersion; - - long long Parse(IMkvReader*, long long&); -}; - - -class Segment; -class Track; -class Cluster; - -class Block -{ - Block(const Block&); - Block& operator=(const Block&); - -public: - const long long m_start; - const long long m_size; - - Block(long long start, long long size, IMkvReader*); - - long long GetTrackNumber() const; - long long GetTimeCode(Cluster*) const; //absolute, but not scaled - long long GetTime(Cluster*) const; //absolute, and scaled (ns units) - bool IsKey() const; - void SetKey(bool); - - unsigned char Flags() const; - - long long GetOffset() const; - long GetSize() const; - long Read(IMkvReader*, unsigned char*) const; - -private: - long long m_track; //Track::Number() - short m_timecode; //relative to cluster - unsigned char m_flags; - long long m_frameOff; - long m_frameSize; - -}; - - -class BlockEntry -{ - BlockEntry(const BlockEntry&); - BlockEntry& operator=(const BlockEntry&); - -public: - virtual ~BlockEntry(); - virtual bool EOS() const = 0; - virtual Cluster* GetCluster() const = 0; - virtual size_t GetIndex() const = 0; - virtual const Block* GetBlock() const = 0; - virtual bool IsBFrame() const = 0; - -protected: - BlockEntry(); - -}; - - -class SimpleBlock : public BlockEntry -{ - SimpleBlock(const SimpleBlock&); - SimpleBlock& operator=(const SimpleBlock&); - -public: - SimpleBlock(Cluster*, size_t, long long start, long long size); - - bool EOS() const; - Cluster* GetCluster() const; - size_t GetIndex() const; - const Block* GetBlock() const; - bool IsBFrame() const; - -protected: - Cluster* const m_pCluster; - const size_t m_index; - Block m_block; - -}; - - -class BlockGroup : public BlockEntry -{ - BlockGroup(const BlockGroup&); - BlockGroup& operator=(const BlockGroup&); - -public: - BlockGroup(Cluster*, size_t, long long, long long); - ~BlockGroup(); - - bool EOS() const; - Cluster* GetCluster() const; - size_t GetIndex() const; - const Block* GetBlock() const; - bool IsBFrame() const; - - short GetPrevTimeCode() const; //relative to block's time - short GetNextTimeCode() const; //as above - -protected: - Cluster* const m_pCluster; - const size_t m_index; - -private: - BlockGroup(Cluster*, size_t, unsigned long); - void ParseBlock(long long start, long long size); - - short m_prevTimeCode; - short m_nextTimeCode; - - //TODO: the Matroska spec says you can have multiple blocks within the - //same block group, with blocks ranked by priority (the flag bits). - //For now we just cache a single block. -#if 0 - typedef std::deque blocks_t; - blocks_t m_blocks; //In practice should contain only a single element. -#else - Block* m_pBlock; -#endif - -}; - - -class Track -{ - Track(const Track&); - Track& operator=(const Track&); - -public: - Segment* const m_pSegment; - virtual ~Track(); - - long long GetType() const; - long long GetNumber() const; - const char* GetNameAsUTF8() const; - const char* GetCodecNameAsUTF8() const; - const char* GetCodecId() const; - const unsigned char* GetCodecPrivate(size_t&) const; - - const BlockEntry* GetEOS() const; - - struct Settings - { - long long start; - long long size; - }; - - struct Info - { - long long type; - long long number; - long long uid; - char* nameAsUTF8; - char* codecId; - unsigned char* codecPrivate; - size_t codecPrivateSize; - char* codecNameAsUTF8; - Settings settings; - Info(); - void Clear(); - }; - - long GetFirst(const BlockEntry*&) const; - long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; - virtual bool VetEntry(const BlockEntry*) const = 0; - -protected: - Track(Segment*, const Info&); - const Info m_info; - - class EOSBlock : public BlockEntry - { - public: - EOSBlock(); - - bool EOS() const; - Cluster* GetCluster() const; - size_t GetIndex() const; - const Block* GetBlock() const; - bool IsBFrame() const; - }; - - EOSBlock m_eos; - -}; - - -class VideoTrack : public Track -{ - VideoTrack(const VideoTrack&); - VideoTrack& operator=(const VideoTrack&); - -public: - VideoTrack(Segment*, const Info&); - long long GetWidth() const; - long long GetHeight() const; - double GetFrameRate() const; - - bool VetEntry(const BlockEntry*) const; - -private: - long long m_width; - long long m_height; - double m_rate; - -}; - - -class AudioTrack : public Track -{ - AudioTrack(const AudioTrack&); - AudioTrack& operator=(const AudioTrack&); - -public: - AudioTrack(Segment*, const Info&); - double GetSamplingRate() const; - long long GetChannels() const; - long long GetBitDepth() const; - bool VetEntry(const BlockEntry*) const; - -private: - double m_rate; - long long m_channels; - long long m_bitDepth; -}; - - -class Tracks -{ - Tracks(const Tracks&); - Tracks& operator=(const Tracks&); - -public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - - Tracks(Segment*, long long start, long long size); - virtual ~Tracks(); - - Track* GetTrackByNumber(unsigned long tn) const; - Track* GetTrackByIndex(unsigned long idx) const; - -private: - Track** m_trackEntries; - Track** m_trackEntriesEnd; - - void ParseTrackEntry(long long, long long, Track*&); - -public: - unsigned long GetTracksCount() const; -}; - - -class SegmentInfo -{ - SegmentInfo(const SegmentInfo&); - SegmentInfo& operator=(const SegmentInfo&); - -public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - - SegmentInfo(Segment*, long long start, long long size); - ~SegmentInfo(); - long long GetTimeCodeScale() const; - long long GetDuration() const; //scaled - const char* GetMuxingAppAsUTF8() const; - const char* GetWritingAppAsUTF8() const; - const char* GetTitleAsUTF8() const; - -private: - long long m_timecodeScale; - double m_duration; - char* m_pMuxingAppAsUTF8; - char* m_pWritingAppAsUTF8; - char* m_pTitleAsUTF8; -}; - -class Cues; -class CuePoint -{ - friend class Cues; - - CuePoint(size_t, long long); - ~CuePoint(); - - CuePoint(const CuePoint&); - CuePoint& operator=(const CuePoint&); - -public: - void Load(IMkvReader*); - - long long GetTimeCode() const; //absolute but unscaled - long long GetTime(Segment*) const; //absolute and scaled (ns units) - - struct TrackPosition - { - long long m_track; - long long m_pos; //of cluster - long long m_block; - //codec_state //defaults to 0 - //reference = clusters containing req'd referenced blocks - // reftime = timecode of the referenced block - - void Parse(IMkvReader*, long long, long long); - }; - - const TrackPosition* Find(const Track*) const; - -private: - const size_t m_index; - long long m_timecode; - TrackPosition* m_track_positions; - size_t m_track_positions_count; - -}; - - -class Cues -{ - friend class Segment; - - Cues(Segment*, long long start, long long size); - ~Cues(); - - Cues(const Cues&); - Cues& operator=(const Cues&); - -public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - - bool Find( //lower bound of time_ns - long long time_ns, - const Track*, - const CuePoint*&, - const CuePoint::TrackPosition*&) const; - -#if 0 - bool FindNext( //upper_bound of time_ns - long long time_ns, - const Track*, - const CuePoint*&, - const CuePoint::TrackPosition*&) const; -#endif - - const CuePoint* GetFirst() const; - const CuePoint* GetLast() const; - - const CuePoint* GetNext(const CuePoint*) const; - - const BlockEntry* GetBlock( - const CuePoint*, - const CuePoint::TrackPosition*) const; - -private: - void Init() const; - bool LoadCuePoint() const; - void PreloadCuePoint(size_t&, long long) const; - - mutable CuePoint** m_cue_points; - mutable size_t m_count; - mutable size_t m_preload_count; - mutable long long m_pos; - -}; - - -class Cluster -{ - Cluster(const Cluster&); - Cluster& operator=(const Cluster&); - -public: - Segment* const m_pSegment; - -public: - static Cluster* Parse(Segment*, long, long long off); - - Cluster(); //EndOfStream - ~Cluster(); - - bool EOS() const; - - long long GetTimeCode(); //absolute, but not scaled - long long GetTime(); //absolute, and scaled (nanosecond units) - long long GetFirstTime(); //time (ns) of first (earliest) block - long long GetLastTime(); //time (ns) of last (latest) block - - const BlockEntry* GetFirst(); - const BlockEntry* GetLast(); - const BlockEntry* GetNext(const BlockEntry*) const; - const BlockEntry* GetEntry(const Track*); - const BlockEntry* GetEntry( - const CuePoint&, - const CuePoint::TrackPosition&); - const BlockEntry* GetMaxKey(const VideoTrack*); - -protected: - Cluster(Segment*, long, long long off); - -public: - //TODO: these should all be private, with public selector functions - long m_index; - long long m_pos; - long long m_size; - -private: - long long m_timecode; - BlockEntry** m_entries; - size_t m_entriesCount; - - void Load(); - void LoadBlockEntries(); - void ParseBlockGroup(long long, long long, size_t); - void ParseSimpleBlock(long long, long long, size_t); - -}; - - -class Segment -{ - friend class Cues; - - Segment(const Segment&); - Segment& operator=(const Segment&); - -private: - Segment(IMkvReader*, long long pos, long long size); - -public: - IMkvReader* const m_pReader; - const long long m_start; //posn of segment payload - const long long m_size; //size of segment payload - Cluster m_eos; //TODO: make private? - - static long long CreateInstance(IMkvReader*, long long, Segment*&); - ~Segment(); - - long Load(); //loads headers and all clusters - - //for incremental loading (splitter) - long long Unparsed() const; - long long ParseHeaders(); //stops when first cluster is found - long LoadCluster(); //loads one cluster - -#if 0 - //This pair parses one cluster, but only changes the state of the - //segment object when the cluster is actually added to the index. - long ParseCluster(Cluster*&, long long& newpos) const; - bool AddCluster(Cluster*, long long); -#endif - - Tracks* GetTracks() const; - const SegmentInfo* GetInfo() const; - const Cues* GetCues() const; - - long long GetDuration() const; - - unsigned long GetCount() const; - Cluster* GetFirst(); - Cluster* GetLast(); - Cluster* GetNext(const Cluster*); - - Cluster* FindCluster(long long time_nanoseconds); - const BlockEntry* Seek(long long time_nanoseconds, const Track*); - -private: - - long long m_pos; //absolute file posn; what has been consumed so far - SegmentInfo* m_pInfo; - Tracks* m_pTracks; - Cues* m_pCues; - Cluster** m_clusters; - long m_clusterCount; //number of entries for which m_index >= 0 - long m_clusterPreloadCount; //number of entries for which m_index < 0 - long m_clusterSize; //array size - - void AppendCluster(Cluster*); - void PreloadCluster(Cluster*, ptrdiff_t); - - void ParseSeekHead(long long pos, long long size); - void ParseSeekEntry(long long pos, long long size); - void ParseCues(long long); - - const BlockEntry* GetBlock( - const CuePoint&, - const CuePoint::TrackPosition&); - -}; - - -} //end namespace mkvparser - -#endif //MKVPARSER_HPP -- cgit v1.1