1 // Copyright (c) 2012 The WebM project authors. All Rights Reserved.
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 #include "mkvparser/mkvparser.h"
10 #if defined(_MSC_VER) && _MSC_VER < 1800
11 #include <float.h> // _isnan() / _finite()
23 #include "common/webmids.h"
26 const float MasteringMetadata::kValueNotPresent = FLT_MAX;
27 const long long Colour::kValueNotPresent = LLONG_MAX;
30 inline bool isnan(double val) { return !!_isnan(val); }
31 inline bool isinf(double val) { return !_finite(val); }
33 inline bool isnan(double val) { return std::isnan(val); }
34 inline bool isinf(double val) { return std::isinf(val); }
37 IMkvReader::~IMkvReader() {}
39 template <typename Type>
40 Type* SafeArrayAlloc(unsigned long long num_elements,
41 unsigned long long element_size) {
42 if (num_elements == 0 || element_size == 0)
45 const size_t kMaxAllocSize = 0x80000000; // 2GiB
46 const unsigned long long num_bytes = num_elements * element_size;
47 if (element_size > (kMaxAllocSize / num_elements))
49 if (num_bytes != static_cast<size_t>(num_bytes))
52 return new (std::nothrow) Type[static_cast<size_t>(num_bytes)];
55 void GetVersion(int& major, int& minor, int& build, int& revision) {
62 long long ReadUInt(IMkvReader* pReader, long long pos, long& len) {
63 if (!pReader || pos < 0)
64 return E_FILE_FORMAT_INVALID;
68 int status = pReader->Read(pos, 1, &b);
70 if (status < 0) // error or underflow
73 if (status > 0) // interpreted as "underflow"
74 return E_BUFFER_NOT_FULL;
76 if (b == 0) // we can't handle u-int values larger than 8 bytes
77 return E_FILE_FORMAT_INVALID;
79 unsigned char m = 0x80;
86 long long result = b & (~m);
89 for (int i = 1; i < len; ++i) {
90 status = pReader->Read(pos, 1, &b);
99 return E_BUFFER_NOT_FULL;
111 // Reads an EBML ID and returns it.
112 // An ID must at least 1 byte long, cannot exceed 4, and its value must be
114 // See known EBML values and EBMLMaxIDLength:
115 // http://www.matroska.org/technical/specs/index.html
116 // Returns the ID, or a value less than 0 to report an error while reading the
118 long long ReadID(IMkvReader* pReader, long long pos, long& len) {
119 if (pReader == NULL || pos < 0)
120 return E_FILE_FORMAT_INVALID;
122 // Read the first byte. The length in bytes of the ID is determined by
123 // finding the first set bit in the first byte of the ID.
124 unsigned char temp_byte = 0;
125 int read_status = pReader->Read(pos, 1, &temp_byte);
128 return E_FILE_FORMAT_INVALID;
129 else if (read_status > 0) // No data to read.
130 return E_BUFFER_NOT_FULL;
132 if (temp_byte == 0) // ID length > 8 bytes; invalid file.
133 return E_FILE_FORMAT_INVALID;
136 const int kMaxIdLengthInBytes = 4;
137 const int kCheckByte = 0x80;
139 // Find the first bit that's set.
140 bool found_bit = false;
141 for (; bit_pos < kMaxIdLengthInBytes; ++bit_pos) {
142 if ((kCheckByte >> bit_pos) & temp_byte) {
149 // The value is too large to be a valid ID.
150 return E_FILE_FORMAT_INVALID;
153 // Read the remaining bytes of the ID (if any).
154 const int id_length = bit_pos + 1;
155 long long ebml_id = temp_byte;
156 for (int i = 1; i < id_length; ++i) {
158 read_status = pReader->Read(pos + i, 1, &temp_byte);
161 return E_FILE_FORMAT_INVALID;
162 else if (read_status > 0)
163 return E_BUFFER_NOT_FULL;
165 ebml_id |= temp_byte;
172 long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) {
173 if (!pReader || pos < 0)
174 return E_FILE_FORMAT_INVALID;
176 long long total, available;
178 int status = pReader->Length(&total, &available);
179 if (status < 0 || (total >= 0 && available > total))
180 return E_FILE_FORMAT_INVALID;
184 if (pos >= available)
185 return pos; // too few bytes available
189 status = pReader->Read(pos, 1, &b);
194 if (b == 0) // we can't handle u-int values larger than 8 bytes
195 return E_FILE_FORMAT_INVALID;
197 unsigned char m = 0x80;
207 // TODO(vigneshv): This function assumes that unsigned values never have their
209 long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) {
210 if (!pReader || pos < 0 || (size <= 0) || (size > 8))
211 return E_FILE_FORMAT_INVALID;
213 long long result = 0;
215 for (long long i = 0; i < size; ++i) {
218 const long status = pReader->Read(pos, 1, &b);
232 long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
234 if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8)))
235 return E_FILE_FORMAT_INVALID;
237 const long size = static_cast<long>(size_);
239 unsigned char buf[8];
241 const int status = pReader->Read(pos, size, buf);
243 if (status < 0) // error
267 unsigned long long dd;
284 if (mkvparser::isinf(result) || mkvparser::isnan(result))
285 return E_FILE_FORMAT_INVALID;
290 long UnserializeInt(IMkvReader* pReader, long long pos, long long size,
291 long long& result_ref) {
292 if (!pReader || pos < 0 || size < 1 || size > 8)
293 return E_FILE_FORMAT_INVALID;
295 signed char first_byte = 0;
296 const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte);
301 unsigned long long result = first_byte;
304 for (long i = 1; i < size; ++i) {
307 const long status = pReader->Read(pos, 1, &b);
318 result_ref = static_cast<long long>(result);
322 long UnserializeString(IMkvReader* pReader, long long pos, long long size,
327 if (size >= LONG_MAX || size < 0)
328 return E_FILE_FORMAT_INVALID;
330 // +1 for '\0' terminator
331 const long required_size = static_cast<long>(size) + 1;
333 str = SafeArrayAlloc<char>(1, required_size);
335 return E_FILE_FORMAT_INVALID;
337 unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
339 const long status = pReader->Read(pos, static_cast<long>(size), buf);
348 str[required_size - 1] = '\0';
352 long ParseElementHeader(IMkvReader* pReader, long long& pos, long long stop,
353 long long& id, long long& size) {
354 if (stop >= 0 && pos >= stop)
355 return E_FILE_FORMAT_INVALID;
359 id = ReadID(pReader, pos, len);
362 return E_FILE_FORMAT_INVALID;
364 pos += len; // consume id
366 if (stop >= 0 && pos >= stop)
367 return E_FILE_FORMAT_INVALID;
369 size = ReadUInt(pReader, pos, len);
371 if (size < 0 || len < 1 || len > 8) {
372 // Invalid: Negative payload size, negative or 0 length integer, or integer
373 // larger than 64 bits (libwebm cannot handle them).
374 return E_FILE_FORMAT_INVALID;
377 // Avoid rolling over pos when very close to LLONG_MAX.
378 const unsigned long long rollover_check =
379 static_cast<unsigned long long>(pos) + len;
380 if (rollover_check > LLONG_MAX)
381 return E_FILE_FORMAT_INVALID;
383 pos += len; // consume length of size
385 // pos now designates payload
387 if (stop >= 0 && pos > stop)
388 return E_FILE_FORMAT_INVALID;
393 bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
395 if (!pReader || pos < 0)
399 long long available = 0;
401 const long status = pReader->Length(&total, &available);
402 if (status < 0 || (total >= 0 && available > total))
407 const long long id = ReadID(pReader, pos, len);
408 if (id < 0 || (available - pos) > len)
411 if (static_cast<unsigned long>(id) != expected_id)
414 pos += len; // consume id
416 const long long size = ReadUInt(pReader, pos, len);
417 if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len)
420 pos += len; // consume length of size of payload
422 val = UnserializeUInt(pReader, pos, size);
426 pos += size; // consume size of payload
431 bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
432 unsigned char*& buf, size_t& buflen) {
433 if (!pReader || pos < 0)
437 long long available = 0;
439 long status = pReader->Length(&total, &available);
440 if (status < 0 || (total >= 0 && available > total))
444 const long long id = ReadID(pReader, pos, len);
445 if (id < 0 || (available - pos) > len)
448 if (static_cast<unsigned long>(id) != expected_id)
451 pos += len; // consume id
453 const long long size = ReadUInt(pReader, pos, len);
454 if (size < 0 || len <= 0 || len > 8 || (available - pos) > len)
457 unsigned long long rollover_check =
458 static_cast<unsigned long long>(pos) + len;
459 if (rollover_check > LLONG_MAX)
462 pos += len; // consume length of size of payload
464 rollover_check = static_cast<unsigned long long>(pos) + size;
465 if (rollover_check > LLONG_MAX)
468 if ((pos + size) > available)
471 if (size >= LONG_MAX)
474 const long buflen_ = static_cast<long>(size);
476 buf = SafeArrayAlloc<unsigned char>(1, buflen_);
480 status = pReader->Read(pos, buflen_, buf);
486 pos += size; // consume size of payload
490 EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); }
492 EBMLHeader::~EBMLHeader() { delete[] m_docType; }
494 void EBMLHeader::Init() {
505 m_docTypeVersion = 1;
506 m_docTypeReadVersion = 1;
509 long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) {
511 return E_FILE_FORMAT_INVALID;
513 long long total, available;
515 long status = pReader->Length(&total, &available);
517 if (status < 0) // error
522 // Scan until we find what looks like the first byte of the EBML header.
523 const long long kMaxScanBytes = (available >= 1024) ? 1024 : available;
524 const unsigned char kEbmlByte0 = 0x1A;
525 unsigned char scan_byte = 0;
527 while (pos < kMaxScanBytes) {
528 status = pReader->Read(pos, 1, &scan_byte);
530 if (status < 0) // error
533 return E_BUFFER_NOT_FULL;
535 if (scan_byte == kEbmlByte0)
542 const long long ebml_id = ReadID(pReader, pos, len);
544 if (ebml_id == E_BUFFER_NOT_FULL)
545 return E_BUFFER_NOT_FULL;
547 if (len != 4 || ebml_id != libwebm::kMkvEBML)
548 return E_FILE_FORMAT_INVALID;
550 // Move read pos forward to the EBML header size field.
553 // Read length of size field.
554 long long result = GetUIntLength(pReader, pos, len);
556 if (result < 0) // error
557 return E_FILE_FORMAT_INVALID;
558 else if (result > 0) // need more data
559 return E_BUFFER_NOT_FULL;
561 if (len < 1 || len > 8)
562 return E_FILE_FORMAT_INVALID;
564 if ((total >= 0) && ((total - pos) < len))
565 return E_FILE_FORMAT_INVALID;
567 if ((available - pos) < len)
568 return pos + len; // try again later
570 // Read the EBML header size.
571 result = ReadUInt(pReader, pos, len);
573 if (result < 0) // error
576 pos += len; // consume size field
578 // pos now designates start of payload
580 if ((total >= 0) && ((total - pos) < result))
581 return E_FILE_FORMAT_INVALID;
583 if ((available - pos) < result)
586 const long long end = pos + result;
593 status = ParseElementHeader(pReader, pos, end, id, size);
595 if (status < 0) // error
599 return E_FILE_FORMAT_INVALID;
601 if (id == libwebm::kMkvEBMLVersion) {
602 m_version = UnserializeUInt(pReader, pos, size);
605 return E_FILE_FORMAT_INVALID;
606 } else if (id == libwebm::kMkvEBMLReadVersion) {
607 m_readVersion = UnserializeUInt(pReader, pos, size);
609 if (m_readVersion <= 0)
610 return E_FILE_FORMAT_INVALID;
611 } else if (id == libwebm::kMkvEBMLMaxIDLength) {
612 m_maxIdLength = UnserializeUInt(pReader, pos, size);
614 if (m_maxIdLength <= 0)
615 return E_FILE_FORMAT_INVALID;
616 } else if (id == libwebm::kMkvEBMLMaxSizeLength) {
617 m_maxSizeLength = UnserializeUInt(pReader, pos, size);
619 if (m_maxSizeLength <= 0)
620 return E_FILE_FORMAT_INVALID;
621 } else if (id == libwebm::kMkvDocType) {
623 return E_FILE_FORMAT_INVALID;
625 status = UnserializeString(pReader, pos, size, m_docType);
629 } else if (id == libwebm::kMkvDocTypeVersion) {
630 m_docTypeVersion = UnserializeUInt(pReader, pos, size);
632 if (m_docTypeVersion <= 0)
633 return E_FILE_FORMAT_INVALID;
634 } else if (id == libwebm::kMkvDocTypeReadVersion) {
635 m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
637 if (m_docTypeReadVersion <= 0)
638 return E_FILE_FORMAT_INVALID;
645 return E_FILE_FORMAT_INVALID;
647 // Make sure DocType, DocTypeReadVersion, and DocTypeVersion are valid.
648 if (m_docType == NULL || m_docTypeReadVersion <= 0 || m_docTypeVersion <= 0)
649 return E_FILE_FORMAT_INVALID;
651 // Make sure EBMLMaxIDLength and EBMLMaxSizeLength are valid.
652 if (m_maxIdLength <= 0 || m_maxIdLength > 4 || m_maxSizeLength <= 0 ||
654 return E_FILE_FORMAT_INVALID;
659 Segment::Segment(IMkvReader* pReader, long long elem_start,
660 // long long elem_size,
661 long long start, long long size)
662 : m_pReader(pReader),
663 m_element_start(elem_start),
664 // m_element_size(elem_size),
677 m_clusterPreloadCount(0),
680 Segment::~Segment() {
681 const long count = m_clusterCount + m_clusterPreloadCount;
683 Cluster** i = m_clusters;
684 Cluster** j = m_clusters + count;
687 Cluster* const p = *i++;
701 long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
702 Segment*& pSegment) {
703 if (pReader == NULL || pos < 0)
704 return E_PARSE_FAILED;
708 long long total, available;
710 const long status = pReader->Length(&total, &available);
712 if (status < 0) // error
718 if ((total >= 0) && (available > total))
721 // I would assume that in practice this loop would execute
722 // exactly once, but we allow for other elements (e.g. Void)
723 // to immediately follow the EBML header. This is fine for
724 // the source filter case (since the entire file is available),
725 // but in the splitter case over a network we should probably
726 // just give up early. We could for example decide only to
727 // execute this loop a maximum of, say, 10 times.
729 // There is an implied "give up early" by only parsing up
730 // to the available limit. We do do that, but only if the
731 // total file size is unknown. We could decide to always
732 // use what's available as our limit (irrespective of whether
733 // we happen to know the total file length). This would have
734 // as its sense "parse this much of the file before giving up",
735 // which a slightly different sense from "try to parse up to
736 // 10 EMBL elements before giving up".
739 if ((total >= 0) && (pos >= total))
740 return E_FILE_FORMAT_INVALID;
744 long long result = GetUIntLength(pReader, pos, len);
746 if (result) // error, or too few available bytes
749 if ((total >= 0) && ((pos + len) > total))
750 return E_FILE_FORMAT_INVALID;
752 if ((pos + len) > available)
755 const long long idpos = pos;
756 const long long id = ReadID(pReader, pos, len);
759 return E_FILE_FORMAT_INVALID;
761 pos += len; // consume ID
765 result = GetUIntLength(pReader, pos, len);
767 if (result) // error, or too few available bytes
770 if ((total >= 0) && ((pos + len) > total))
771 return E_FILE_FORMAT_INVALID;
773 if ((pos + len) > available)
776 long long size = ReadUInt(pReader, pos, len);
778 if (size < 0) // error
781 pos += len; // consume length of size of element
783 // Pos now points to start of payload
785 // Handle "unknown size" for live streaming of webm files.
786 const long long unknown_size = (1LL << (7 * len)) - 1;
788 if (id == libwebm::kMkvSegment) {
789 if (size == unknown_size)
795 else if ((pos + size) > total)
798 pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size);
799 if (pSegment == NULL)
800 return E_PARSE_FAILED;
805 if (size == unknown_size)
806 return E_FILE_FORMAT_INVALID;
808 if ((total >= 0) && ((pos + size) > total))
809 return E_FILE_FORMAT_INVALID;
811 if ((pos + size) > available)
814 pos += size; // consume payload
818 long long Segment::ParseHeaders() {
819 // Outermost (level 0) segment object has been constructed,
820 // and pos designates start of payload. We need to find the
821 // inner (level 1) elements.
822 long long total, available;
824 const int status = m_pReader->Length(&total, &available);
826 if (status < 0) // error
829 if (total > 0 && available > total)
830 return E_FILE_FORMAT_INVALID;
832 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
834 if ((segment_stop >= 0 && total >= 0 && segment_stop > total) ||
835 (segment_stop >= 0 && m_pos > segment_stop)) {
836 return E_FILE_FORMAT_INVALID;
840 if ((total >= 0) && (m_pos >= total))
843 if ((segment_stop >= 0) && (m_pos >= segment_stop))
846 long long pos = m_pos;
847 const long long element_start = pos;
849 // Avoid rolling over pos when very close to LLONG_MAX.
850 unsigned long long rollover_check = pos + 1ULL;
851 if (rollover_check > LLONG_MAX)
852 return E_FILE_FORMAT_INVALID;
854 if ((pos + 1) > available)
858 long long result = GetUIntLength(m_pReader, pos, len);
860 if (result < 0) // error
864 // MkvReader doesn't have enough data to satisfy this read attempt.
868 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
869 return E_FILE_FORMAT_INVALID;
871 if ((pos + len) > available)
874 const long long idpos = pos;
875 const long long id = ReadID(m_pReader, idpos, len);
878 return E_FILE_FORMAT_INVALID;
880 if (id == libwebm::kMkvCluster)
883 pos += len; // consume ID
885 if ((pos + 1) > available)
889 result = GetUIntLength(m_pReader, pos, len);
891 if (result < 0) // error
895 // MkvReader doesn't have enough data to satisfy this read attempt.
899 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
900 return E_FILE_FORMAT_INVALID;
902 if ((pos + len) > available)
905 const long long size = ReadUInt(m_pReader, pos, len);
907 if (size < 0 || len < 1 || len > 8) {
908 // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or
909 // len > 8 is true instead of checking this _everywhere_.
913 pos += len; // consume length of size of element
915 // Avoid rolling over pos when very close to LLONG_MAX.
916 rollover_check = static_cast<unsigned long long>(pos) + size;
917 if (rollover_check > LLONG_MAX)
918 return E_FILE_FORMAT_INVALID;
920 const long long element_size = size + pos - element_start;
922 // Pos now points to start of payload
924 if ((segment_stop >= 0) && ((pos + size) > segment_stop))
925 return E_FILE_FORMAT_INVALID;
927 // We read EBML elements either in total or nothing at all.
929 if ((pos + size) > available)
932 if (id == libwebm::kMkvInfo) {
934 return E_FILE_FORMAT_INVALID;
936 m_pInfo = new (std::nothrow)
937 SegmentInfo(this, pos, size, element_start, element_size);
942 const long status = m_pInfo->Parse();
946 } else if (id == libwebm::kMkvTracks) {
948 return E_FILE_FORMAT_INVALID;
950 m_pTracks = new (std::nothrow)
951 Tracks(this, pos, size, element_start, element_size);
953 if (m_pTracks == NULL)
956 const long status = m_pTracks->Parse();
960 } else if (id == libwebm::kMkvCues) {
961 if (m_pCues == NULL) {
962 m_pCues = new (std::nothrow)
963 Cues(this, pos, size, element_start, element_size);
968 } else if (id == libwebm::kMkvSeekHead) {
969 if (m_pSeekHead == NULL) {
970 m_pSeekHead = new (std::nothrow)
971 SeekHead(this, pos, size, element_start, element_size);
973 if (m_pSeekHead == NULL)
976 const long status = m_pSeekHead->Parse();
981 } else if (id == libwebm::kMkvChapters) {
982 if (m_pChapters == NULL) {
983 m_pChapters = new (std::nothrow)
984 Chapters(this, pos, size, element_start, element_size);
986 if (m_pChapters == NULL)
989 const long status = m_pChapters->Parse();
994 } else if (id == libwebm::kMkvTags) {
995 if (m_pTags == NULL) {
996 m_pTags = new (std::nothrow)
997 Tags(this, pos, size, element_start, element_size);
1002 const long status = m_pTags->Parse();
1009 m_pos = pos + size; // consume payload
1012 if (segment_stop >= 0 && m_pos > segment_stop)
1013 return E_FILE_FORMAT_INVALID;
1015 if (m_pInfo == NULL) // TODO: liberalize this behavior
1016 return E_FILE_FORMAT_INVALID;
1018 if (m_pTracks == NULL)
1019 return E_FILE_FORMAT_INVALID;
1021 return 0; // success
1024 long Segment::LoadCluster(long long& pos, long& len) {
1026 const long result = DoLoadCluster(pos, len);
1033 long Segment::DoLoadCluster(long long& pos, long& len) {
1035 return DoLoadClusterUnknownSize(pos, len);
1037 long long total, avail;
1039 long status = m_pReader->Length(&total, &avail);
1041 if (status < 0) // error
1044 if (total >= 0 && avail > total)
1045 return E_FILE_FORMAT_INVALID;
1047 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
1049 long long cluster_off = -1; // offset relative to start of segment
1050 long long cluster_size = -1; // size of cluster payload
1053 if ((total >= 0) && (m_pos >= total))
1054 return 1; // no more clusters
1056 if ((segment_stop >= 0) && (m_pos >= segment_stop))
1057 return 1; // no more clusters
1063 if ((pos + 1) > avail) {
1065 return E_BUFFER_NOT_FULL;
1068 long long result = GetUIntLength(m_pReader, pos, len);
1070 if (result < 0) // error
1071 return static_cast<long>(result);
1074 return E_BUFFER_NOT_FULL;
1076 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1077 return E_FILE_FORMAT_INVALID;
1079 if ((pos + len) > avail)
1080 return E_BUFFER_NOT_FULL;
1082 const long long idpos = pos;
1083 const long long id = ReadID(m_pReader, idpos, len);
1086 return E_FILE_FORMAT_INVALID;
1088 pos += len; // consume ID
1092 if ((pos + 1) > avail) {
1094 return E_BUFFER_NOT_FULL;
1097 result = GetUIntLength(m_pReader, pos, len);
1099 if (result < 0) // error
1100 return static_cast<long>(result);
1103 return E_BUFFER_NOT_FULL;
1105 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1106 return E_FILE_FORMAT_INVALID;
1108 if ((pos + len) > avail)
1109 return E_BUFFER_NOT_FULL;
1111 const long long size = ReadUInt(m_pReader, pos, len);
1113 if (size < 0) // error
1114 return static_cast<long>(size);
1116 pos += len; // consume length of size of element
1118 // pos now points to start of payload
1121 // Missing element payload: move on.
1126 const long long unknown_size = (1LL << (7 * len)) - 1;
1128 if ((segment_stop >= 0) && (size != unknown_size) &&
1129 ((pos + size) > segment_stop)) {
1130 return E_FILE_FORMAT_INVALID;
1133 if (id == libwebm::kMkvCues) {
1134 if (size == unknown_size) {
1135 // Cues element of unknown size: Not supported.
1136 return E_FILE_FORMAT_INVALID;
1139 if (m_pCues == NULL) {
1140 const long long element_size = (pos - idpos) + size;
1142 m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
1143 if (m_pCues == NULL)
1147 m_pos = pos + size; // consume payload
1151 if (id != libwebm::kMkvCluster) {
1152 // Besides the Segment, Libwebm allows only cluster elements of unknown
1153 // size. Fail the parse upon encountering a non-cluster element reporting
1155 if (size == unknown_size)
1156 return E_FILE_FORMAT_INVALID;
1158 m_pos = pos + size; // consume payload
1162 // We have a cluster.
1164 cluster_off = idpos - m_start; // relative pos
1166 if (size != unknown_size)
1167 cluster_size = size;
1172 if (cluster_off < 0) {
1174 return E_FILE_FORMAT_INVALID;
1180 status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
1182 if (status < 0) { // error, or underflow
1189 // status == 0 means "no block entries found"
1190 // status > 0 means "found at least one block entry"
1193 // The issue here is that the segment increments its own
1194 // pos ptr past the most recent cluster parsed, and then
1195 // starts from there to parse the next cluster. If we
1196 // don't know the size of the current cluster, then we
1197 // must either parse its payload (as we do below), looking
1198 // for the cluster (or cues) ID to terminate the parse.
1199 // This isn't really what we want: rather, we really need
1200 // a way to create the curr cluster object immediately.
1201 // The pity is that cluster::parse can determine its own
1202 // boundary, and we largely duplicate that same logic here.
1204 // Maybe we need to get rid of our look-ahead preloading
1205 // in source::parse???
1207 // As we're parsing the blocks in the curr cluster
1208 //(in cluster::parse), we should have some way to signal
1209 // to the segment that we have determined the boundary,
1210 // so it can adjust its own segment::m_pos member.
1212 // The problem is that we're asserting in asyncreadinit,
1213 // because we adjust the pos down to the curr seek pos,
1214 // and the resulting adjusted len is > 2GB. I'm suspicious
1215 // that this is even correct, but even if it is, we can't
1216 // be loading that much data in the cache anyway.
1218 const long idx = m_clusterCount;
1220 if (m_clusterPreloadCount > 0) {
1221 if (idx >= m_clusterSize)
1222 return E_FILE_FORMAT_INVALID;
1224 Cluster* const pCluster = m_clusters[idx];
1225 if (pCluster == NULL || pCluster->m_index >= 0)
1226 return E_FILE_FORMAT_INVALID;
1228 const long long off = pCluster->GetPosition();
1230 return E_FILE_FORMAT_INVALID;
1232 if (off == cluster_off) { // preloaded already
1233 if (status == 0) // no entries found
1234 return E_FILE_FORMAT_INVALID;
1236 if (cluster_size >= 0)
1237 pos += cluster_size;
1239 const long long element_size = pCluster->GetElementSize();
1241 if (element_size <= 0)
1242 return E_FILE_FORMAT_INVALID; // TODO: handle this case
1244 pos = pCluster->m_element_start + element_size;
1247 pCluster->m_index = idx; // move from preloaded to loaded
1249 --m_clusterPreloadCount;
1251 m_pos = pos; // consume payload
1252 if (segment_stop >= 0 && m_pos > segment_stop)
1253 return E_FILE_FORMAT_INVALID;
1255 return 0; // success
1259 if (status == 0) { // no entries found
1260 if (cluster_size >= 0)
1261 pos += cluster_size;
1263 if ((total >= 0) && (pos >= total)) {
1265 return 1; // no more clusters
1268 if ((segment_stop >= 0) && (pos >= segment_stop)) {
1269 m_pos = segment_stop;
1270 return 1; // no more clusters
1274 return 2; // try again
1277 // status > 0 means we have an entry
1279 Cluster* const pCluster = Cluster::Create(this, idx, cluster_off);
1280 if (pCluster == NULL)
1283 if (!AppendCluster(pCluster)) {
1288 if (cluster_size >= 0) {
1289 pos += cluster_size;
1293 if (segment_stop > 0 && m_pos > segment_stop)
1294 return E_FILE_FORMAT_INVALID;
1299 m_pUnknownSize = pCluster;
1302 return 0; // partial success, since we have a new cluster
1304 // status == 0 means "no block entries found"
1305 // pos designates start of payload
1306 // m_pos has NOT been adjusted yet (in case we need to come back here)
1309 long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
1310 if (m_pos >= 0 || m_pUnknownSize == NULL)
1311 return E_PARSE_FAILED;
1313 const long status = m_pUnknownSize->Parse(pos, len);
1315 if (status < 0) // error or underflow
1318 if (status == 0) // parsed a block
1319 return 2; // continue parsing
1321 const long long start = m_pUnknownSize->m_element_start;
1322 const long long size = m_pUnknownSize->GetElementSize();
1325 return E_FILE_FORMAT_INVALID;
1332 return 2; // continue parsing
1335 bool Segment::AppendCluster(Cluster* pCluster) {
1336 if (pCluster == NULL || pCluster->m_index < 0)
1339 const long count = m_clusterCount + m_clusterPreloadCount;
1341 long& size = m_clusterSize;
1342 const long idx = pCluster->m_index;
1344 if (size < count || idx != m_clusterCount)
1347 if (count >= size) {
1348 const long n = (size <= 0) ? 2048 : 2 * size;
1350 Cluster** const qq = new (std::nothrow) Cluster*[n];
1355 Cluster** p = m_clusters;
1356 Cluster** const pp = p + count;
1361 delete[] m_clusters;
1367 if (m_clusterPreloadCount > 0) {
1368 Cluster** const p = m_clusters + m_clusterCount;
1369 if (*p == NULL || (*p)->m_index >= 0)
1372 Cluster** q = p + m_clusterPreloadCount;
1373 if (q >= (m_clusters + size))
1377 Cluster** const qq = q - 1;
1378 if ((*qq)->m_index >= 0)
1389 m_clusters[idx] = pCluster;
1394 bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
1395 if (pCluster == NULL || pCluster->m_index >= 0 || idx < m_clusterCount)
1398 const long count = m_clusterCount + m_clusterPreloadCount;
1400 long& size = m_clusterSize;
1404 if (count >= size) {
1405 const long n = (size <= 0) ? 2048 : 2 * size;
1407 Cluster** const qq = new (std::nothrow) Cluster*[n];
1412 Cluster** p = m_clusters;
1413 Cluster** const pp = p + count;
1418 delete[] m_clusters;
1424 if (m_clusters == NULL)
1427 Cluster** const p = m_clusters + idx;
1429 Cluster** q = m_clusters + count;
1430 if (q < p || q >= (m_clusters + size))
1434 Cluster** const qq = q - 1;
1436 if ((*qq)->m_index >= 0)
1443 m_clusters[idx] = pCluster;
1444 ++m_clusterPreloadCount;
1448 long Segment::Load() {
1449 if (m_clusters != NULL || m_clusterSize != 0 || m_clusterCount != 0)
1450 return E_PARSE_FAILED;
1452 // Outermost (level 0) segment object has been constructed,
1453 // and pos designates start of payload. We need to find the
1454 // inner (level 1) elements.
1456 const long long header_status = ParseHeaders();
1458 if (header_status < 0) // error
1459 return static_cast<long>(header_status);
1461 if (header_status > 0) // underflow
1462 return E_BUFFER_NOT_FULL;
1464 if (m_pInfo == NULL || m_pTracks == NULL)
1465 return E_FILE_FORMAT_INVALID;
1468 const int status = LoadCluster();
1470 if (status < 0) // error
1473 if (status >= 1) // no more clusters
1478 SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
1479 long long element_start, long long element_size)
1480 : m_pSegment(pSegment),
1483 m_element_start(element_start),
1484 m_element_size(element_size),
1488 m_void_element_count(0) {}
1490 SeekHead::~SeekHead() {
1492 delete[] m_void_elements;
1495 long SeekHead::Parse() {
1496 IMkvReader* const pReader = m_pSegment->m_pReader;
1498 long long pos = m_start;
1499 const long long stop = m_start + m_size;
1501 // first count the seek head entries
1503 int entry_count = 0;
1504 int void_element_count = 0;
1506 while (pos < stop) {
1509 const long status = ParseElementHeader(pReader, pos, stop, id, size);
1511 if (status < 0) // error
1514 if (id == libwebm::kMkvSeek)
1516 else if (id == libwebm::kMkvVoid)
1517 ++void_element_count;
1519 pos += size; // consume payload
1522 return E_FILE_FORMAT_INVALID;
1526 return E_FILE_FORMAT_INVALID;
1528 m_entries = new (std::nothrow) Entry[entry_count];
1530 if (m_entries == NULL)
1533 m_void_elements = new (std::nothrow) VoidElement[void_element_count];
1535 if (m_void_elements == NULL)
1538 // now parse the entries and void elements
1540 Entry* pEntry = m_entries;
1541 VoidElement* pVoidElement = m_void_elements;
1545 while (pos < stop) {
1546 const long long idpos = pos;
1550 const long status = ParseElementHeader(pReader, pos, stop, id, size);
1552 if (status < 0) // error
1555 if (id == libwebm::kMkvSeek) {
1556 if (ParseEntry(pReader, pos, size, pEntry)) {
1557 Entry& e = *pEntry++;
1559 e.element_start = idpos;
1560 e.element_size = (pos + size) - idpos;
1562 } else if (id == libwebm::kMkvVoid) {
1563 VoidElement& e = *pVoidElement++;
1565 e.element_start = idpos;
1566 e.element_size = (pos + size) - idpos;
1569 pos += size; // consume payload
1571 return E_FILE_FORMAT_INVALID;
1575 return E_FILE_FORMAT_INVALID;
1577 ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
1578 assert(count_ >= 0);
1579 assert(count_ <= entry_count);
1581 m_entry_count = static_cast<int>(count_);
1583 count_ = ptrdiff_t(pVoidElement - m_void_elements);
1584 assert(count_ >= 0);
1585 assert(count_ <= void_element_count);
1587 m_void_element_count = static_cast<int>(count_);
1592 int SeekHead::GetCount() const { return m_entry_count; }
1594 const SeekHead::Entry* SeekHead::GetEntry(int idx) const {
1598 if (idx >= m_entry_count)
1601 return m_entries + idx;
1604 int SeekHead::GetVoidElementCount() const { return m_void_element_count; }
1606 const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
1610 if (idx >= m_void_element_count)
1613 return m_void_elements + idx;
1616 long Segment::ParseCues(long long off, long long& pos, long& len) {
1618 return 0; // success
1623 long long total, avail;
1625 const int status = m_pReader->Length(&total, &avail);
1627 if (status < 0) // error
1630 assert((total < 0) || (avail <= total));
1632 pos = m_start + off;
1634 if ((total < 0) || (pos >= total))
1635 return 1; // don't bother parsing cues
1637 const long long element_start = pos;
1638 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
1640 if ((pos + 1) > avail) {
1642 return E_BUFFER_NOT_FULL;
1645 long long result = GetUIntLength(m_pReader, pos, len);
1647 if (result < 0) // error
1648 return static_cast<long>(result);
1650 if (result > 0) // underflow (weird)
1653 return E_BUFFER_NOT_FULL;
1656 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1657 return E_FILE_FORMAT_INVALID;
1659 if ((pos + len) > avail)
1660 return E_BUFFER_NOT_FULL;
1662 const long long idpos = pos;
1664 const long long id = ReadID(m_pReader, idpos, len);
1666 if (id != libwebm::kMkvCues)
1667 return E_FILE_FORMAT_INVALID;
1669 pos += len; // consume ID
1670 assert((segment_stop < 0) || (pos <= segment_stop));
1674 if ((pos + 1) > avail) {
1676 return E_BUFFER_NOT_FULL;
1679 result = GetUIntLength(m_pReader, pos, len);
1681 if (result < 0) // error
1682 return static_cast<long>(result);
1684 if (result > 0) // underflow (weird)
1687 return E_BUFFER_NOT_FULL;
1690 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1691 return E_FILE_FORMAT_INVALID;
1693 if ((pos + len) > avail)
1694 return E_BUFFER_NOT_FULL;
1696 const long long size = ReadUInt(m_pReader, pos, len);
1698 if (size < 0) // error
1699 return static_cast<long>(size);
1701 if (size == 0) // weird, although technically not illegal
1704 pos += len; // consume length of size of element
1705 assert((segment_stop < 0) || (pos <= segment_stop));
1707 // Pos now points to start of payload
1709 const long long element_stop = pos + size;
1711 if ((segment_stop >= 0) && (element_stop > segment_stop))
1712 return E_FILE_FORMAT_INVALID;
1714 if ((total >= 0) && (element_stop > total))
1715 return 1; // don't bother parsing anymore
1717 len = static_cast<long>(size);
1719 if (element_stop > avail)
1720 return E_BUFFER_NOT_FULL;
1722 const long long element_size = element_stop - element_start;
1725 new (std::nothrow) Cues(this, pos, size, element_start, element_size);
1726 if (m_pCues == NULL)
1729 return 0; // success
1732 bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
1737 long long pos = start;
1738 const long long stop = start + size_;
1742 // parse the container for the level-1 element ID
1744 const long long seekIdId = ReadID(pReader, pos, len);
1748 if (seekIdId != libwebm::kMkvSeekID)
1751 if ((pos + len) > stop)
1754 pos += len; // consume SeekID id
1756 const long long seekIdSize = ReadUInt(pReader, pos, len);
1758 if (seekIdSize <= 0)
1761 if ((pos + len) > stop)
1764 pos += len; // consume size of field
1766 if ((pos + seekIdSize) > stop)
1769 // Note that the SeekId payload really is serialized
1770 // as a "Matroska integer", not as a plain binary value.
1771 // In fact, Matroska requires that ID values in the
1772 // stream exactly match the binary representation as listed
1773 // in the Matroska specification.
1775 // This parser is more liberal, and permits IDs to have
1776 // any width. (This could make the representation in the stream
1777 // different from what's in the spec, but it doesn't matter here,
1778 // since we always normalize "Matroska integer" values.)
1780 pEntry->id = ReadUInt(pReader, pos, len); // payload
1782 if (pEntry->id <= 0)
1785 if (len != seekIdSize)
1788 pos += seekIdSize; // consume SeekID payload
1790 const long long seekPosId = ReadID(pReader, pos, len);
1792 if (seekPosId != libwebm::kMkvSeekPosition)
1795 if ((pos + len) > stop)
1798 pos += len; // consume id
1800 const long long seekPosSize = ReadUInt(pReader, pos, len);
1802 if (seekPosSize <= 0)
1805 if ((pos + len) > stop)
1808 pos += len; // consume size
1810 if ((pos + seekPosSize) > stop)
1813 pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
1815 if (pEntry->pos < 0)
1818 pos += seekPosSize; // consume payload
1826 Cues::Cues(Segment* pSegment, long long start_, long long size_,
1827 long long element_start, long long element_size)
1828 : m_pSegment(pSegment),
1831 m_element_start(element_start),
1832 m_element_size(element_size),
1839 const long n = m_count + m_preload_count;
1841 CuePoint** p = m_cue_points;
1842 CuePoint** const q = p + n;
1845 CuePoint* const pCP = *p++;
1851 delete[] m_cue_points;
1854 long Cues::GetCount() const {
1855 if (m_cue_points == NULL)
1858 return m_count; // TODO: really ignore preload count?
1861 bool Cues::DoneParsing() const {
1862 const long long stop = m_start + m_size;
1863 return (m_pos >= stop);
1866 bool Cues::Init() const {
1870 if (m_count != 0 || m_preload_count != 0)
1873 IMkvReader* const pReader = m_pSegment->m_pReader;
1875 const long long stop = m_start + m_size;
1876 long long pos = m_start;
1878 long cue_points_size = 0;
1880 while (pos < stop) {
1881 const long long idpos = pos;
1885 const long long id = ReadID(pReader, pos, len);
1886 if (id < 0 || (pos + len) > stop) {
1890 pos += len; // consume ID
1892 const long long size = ReadUInt(pReader, pos, len);
1893 if (size < 0 || (pos + len > stop)) {
1897 pos += len; // consume Size field
1898 if (pos + size > stop) {
1902 if (id == libwebm::kMkvCuePoint) {
1903 if (!PreloadCuePoint(cue_points_size, idpos))
1907 pos += size; // skip payload
1912 bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
1916 if (m_preload_count >= cue_points_size) {
1917 const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
1919 CuePoint** const qq = new (std::nothrow) CuePoint*[n];
1923 CuePoint** q = qq; // beginning of target
1925 CuePoint** p = m_cue_points; // beginning of source
1926 CuePoint** const pp = p + m_preload_count; // end of source
1931 delete[] m_cue_points;
1934 cue_points_size = n;
1937 CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
1941 m_cue_points[m_preload_count++] = pCP;
1945 bool Cues::LoadCuePoint() const {
1946 const long long stop = m_start + m_size;
1949 return false; // nothing else to do
1956 IMkvReader* const pReader = m_pSegment->m_pReader;
1958 while (m_pos < stop) {
1959 const long long idpos = m_pos;
1963 const long long id = ReadID(pReader, m_pos, len);
1964 if (id < 0 || (m_pos + len) > stop)
1967 m_pos += len; // consume ID
1969 const long long size = ReadUInt(pReader, m_pos, len);
1970 if (size < 0 || (m_pos + len) > stop)
1973 m_pos += len; // consume Size field
1974 if ((m_pos + size) > stop)
1977 if (id != libwebm::kMkvCuePoint) {
1978 m_pos += size; // consume payload
1985 if (m_preload_count < 1)
1988 CuePoint* const pCP = m_cue_points[m_count];
1989 if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos)))
1992 if (!pCP->Load(pReader)) {
1999 m_pos += size; // consume payload
2003 return true; // yes, we loaded a cue point
2006 return false; // no, we did not load a cue point
2009 bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
2010 const CuePoint::TrackPosition*& pTP) const {
2011 if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0)
2014 CuePoint** const ii = m_cue_points;
2017 CuePoint** const jj = ii + m_count;
2024 if (time_ns <= pCP->GetTime(m_pSegment)) {
2025 pTP = pCP->Find(pTrack);
2026 return (pTP != NULL);
2031 //[ii, i) <= time_ns
2035 CuePoint** const k = i + (j - i) / 2;
2039 CuePoint* const pCP = *k;
2043 const long long t = pCP->GetTime(m_pSegment);
2054 if (i != j || i > jj || i <= ii)
2059 if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns)
2062 // TODO: here and elsewhere, it's probably not correct to search
2063 // for the cue point with this time, and then search for a matching
2064 // track. In principle, the matching track could be on some earlier
2065 // cue point, and with our current algorithm, we'd miss it. To make
2066 // this bullet-proof, we'd need to create a secondary structure,
2067 // with a list of cue points that apply to a track, and then search
2068 // that track-based structure for a matching cue point.
2070 pTP = pCP->Find(pTrack);
2071 return (pTP != NULL);
2074 const CuePoint* Cues::GetFirst() const {
2075 if (m_cue_points == NULL || m_count == 0)
2078 CuePoint* const* const pp = m_cue_points;
2082 CuePoint* const pCP = pp[0];
2083 if (pCP == NULL || pCP->GetTimeCode() < 0)
2089 const CuePoint* Cues::GetLast() const {
2090 if (m_cue_points == NULL || m_count <= 0)
2093 const long index = m_count - 1;
2095 CuePoint* const* const pp = m_cue_points;
2099 CuePoint* const pCP = pp[index];
2100 if (pCP == NULL || pCP->GetTimeCode() < 0)
2106 const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
2107 if (pCurr == NULL || pCurr->GetTimeCode() < 0 || m_cue_points == NULL ||
2112 long index = pCurr->m_index;
2113 if (index >= m_count)
2116 CuePoint* const* const pp = m_cue_points;
2117 if (pp == NULL || pp[index] != pCurr)
2122 if (index >= m_count)
2125 CuePoint* const pNext = pp[index];
2127 if (pNext == NULL || pNext->GetTimeCode() < 0)
2133 const BlockEntry* Cues::GetBlock(const CuePoint* pCP,
2134 const CuePoint::TrackPosition* pTP) const {
2135 if (pCP == NULL || pTP == NULL)
2138 return m_pSegment->GetBlock(*pCP, *pTP);
2141 const BlockEntry* Segment::GetBlock(const CuePoint& cp,
2142 const CuePoint::TrackPosition& tp) {
2143 Cluster** const ii = m_clusters;
2146 const long count = m_clusterCount + m_clusterPreloadCount;
2148 Cluster** const jj = ii + count;
2153 //[ii, i) < pTP->m_pos
2155 //[j, jj) > pTP->m_pos
2157 Cluster** const k = i + (j - i) / 2;
2160 Cluster* const pCluster = *k;
2163 // const long long pos_ = pCluster->m_pos;
2165 // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2167 const long long pos = pCluster->GetPosition();
2172 else if (pos > tp.m_pos)
2175 return pCluster->GetEntry(cp, tp);
2179 // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2181 Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos); //, -1);
2182 if (pCluster == NULL)
2185 const ptrdiff_t idx = i - m_clusters;
2187 if (!PreloadCluster(pCluster, idx)) {
2192 assert(m_clusterPreloadCount > 0);
2193 assert(m_clusters[idx] == pCluster);
2195 return pCluster->GetEntry(cp, tp);
2198 const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) {
2199 if (requested_pos < 0)
2202 Cluster** const ii = m_clusters;
2205 const long count = m_clusterCount + m_clusterPreloadCount;
2207 Cluster** const jj = ii + count;
2212 //[ii, i) < pTP->m_pos
2214 //[j, jj) > pTP->m_pos
2216 Cluster** const k = i + (j - i) / 2;
2219 Cluster* const pCluster = *k;
2222 // const long long pos_ = pCluster->m_pos;
2224 // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2226 const long long pos = pCluster->GetPosition();
2229 if (pos < requested_pos)
2231 else if (pos > requested_pos)
2238 // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2240 Cluster* const pCluster = Cluster::Create(this, -1, requested_pos);
2241 if (pCluster == NULL)
2244 const ptrdiff_t idx = i - m_clusters;
2246 if (!PreloadCluster(pCluster, idx)) {
2251 assert(m_clusterPreloadCount > 0);
2252 assert(m_clusters[idx] == pCluster);
2257 CuePoint::CuePoint(long idx, long long pos)
2258 : m_element_start(0),
2261 m_timecode(-1 * pos),
2262 m_track_positions(NULL),
2263 m_track_positions_count(0) {
2267 CuePoint::~CuePoint() { delete[] m_track_positions; }
2269 bool CuePoint::Load(IMkvReader* pReader) {
2271 // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
2273 if (m_timecode >= 0) // already loaded
2276 assert(m_track_positions == NULL);
2277 assert(m_track_positions_count == 0);
2279 long long pos_ = -m_timecode;
2280 const long long element_start = pos_;
2287 const long long id = ReadID(pReader, pos_, len);
2288 if (id != libwebm::kMkvCuePoint)
2291 pos_ += len; // consume ID
2293 const long long size = ReadUInt(pReader, pos_, len);
2296 pos_ += len; // consume Size field
2297 // pos_ now points to start of payload
2302 const long long element_size = stop - element_start;
2304 long long pos = pos_;
2306 // First count number of track positions
2308 while (pos < stop) {
2311 const long long id = ReadID(pReader, pos, len);
2312 if ((id < 0) || (pos + len > stop)) {
2316 pos += len; // consume ID
2318 const long long size = ReadUInt(pReader, pos, len);
2319 if ((size < 0) || (pos + len > stop)) {
2323 pos += len; // consume Size field
2324 if ((pos + size) > stop) {
2328 if (id == libwebm::kMkvCueTime)
2329 m_timecode = UnserializeUInt(pReader, pos, size);
2331 else if (id == libwebm::kMkvCueTrackPositions)
2332 ++m_track_positions_count;
2334 pos += size; // consume payload
2337 if (m_timecode < 0 || m_track_positions_count <= 0) {
2341 // os << "CuePoint::Load(cont'd): idpos=" << idpos
2342 // << " timecode=" << m_timecode
2345 m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
2346 if (m_track_positions == NULL)
2349 // Now parse track positions
2351 TrackPosition* p = m_track_positions;
2354 while (pos < stop) {
2357 const long long id = ReadID(pReader, pos, len);
2358 if (id < 0 || (pos + len) > stop)
2361 pos += len; // consume ID
2363 const long long size = ReadUInt(pReader, pos, len);
2365 assert((pos + len) <= stop);
2367 pos += len; // consume Size field
2368 assert((pos + size) <= stop);
2370 if (id == libwebm::kMkvCueTrackPositions) {
2371 TrackPosition& tp = *p++;
2372 if (!tp.Parse(pReader, pos, size)) {
2377 pos += size; // consume payload
2382 assert(size_t(p - m_track_positions) == m_track_positions_count);
2384 m_element_start = element_start;
2385 m_element_size = element_size;
2390 bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
2392 const long long stop = start_ + size_;
2393 long long pos = start_;
2397 m_block = 1; // default
2399 while (pos < stop) {
2402 const long long id = ReadID(pReader, pos, len);
2403 if ((id < 0) || ((pos + len) > stop)) {
2407 pos += len; // consume ID
2409 const long long size = ReadUInt(pReader, pos, len);
2410 if ((size < 0) || ((pos + len) > stop)) {
2414 pos += len; // consume Size field
2415 if ((pos + size) > stop) {
2419 if (id == libwebm::kMkvCueTrack)
2420 m_track = UnserializeUInt(pReader, pos, size);
2421 else if (id == libwebm::kMkvCueClusterPosition)
2422 m_pos = UnserializeUInt(pReader, pos, size);
2423 else if (id == libwebm::kMkvCueBlockNumber)
2424 m_block = UnserializeUInt(pReader, pos, size);
2426 pos += size; // consume payload
2429 if ((m_pos < 0) || (m_track <= 0)) {
2436 const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
2439 const long long n = pTrack->GetNumber();
2441 const TrackPosition* i = m_track_positions;
2442 const TrackPosition* const j = i + m_track_positions_count;
2445 const TrackPosition& p = *i++;
2451 return NULL; // no matching track number found
2454 long long CuePoint::GetTimeCode() const { return m_timecode; }
2456 long long CuePoint::GetTime(const Segment* pSegment) const {
2458 assert(m_timecode >= 0);
2460 const SegmentInfo* const pInfo = pSegment->GetInfo();
2463 const long long scale = pInfo->GetTimeCodeScale();
2466 const long long time = scale * m_timecode;
2471 bool Segment::DoneParsing() const {
2473 long long total, avail;
2475 const int status = m_pReader->Length(&total, &avail);
2477 if (status < 0) // error
2478 return true; // must assume done
2481 return false; // assume live stream
2483 return (m_pos >= total);
2486 const long long stop = m_start + m_size;
2488 return (m_pos >= stop);
2491 const Cluster* Segment::GetFirst() const {
2492 if ((m_clusters == NULL) || (m_clusterCount <= 0))
2495 Cluster* const pCluster = m_clusters[0];
2501 const Cluster* Segment::GetLast() const {
2502 if ((m_clusters == NULL) || (m_clusterCount <= 0))
2505 const long idx = m_clusterCount - 1;
2507 Cluster* const pCluster = m_clusters[idx];
2513 unsigned long Segment::GetCount() const { return m_clusterCount; }
2515 const Cluster* Segment::GetNext(const Cluster* pCurr) {
2517 assert(pCurr != &m_eos);
2520 long idx = pCurr->m_index;
2523 assert(m_clusterCount > 0);
2524 assert(idx < m_clusterCount);
2525 assert(pCurr == m_clusters[idx]);
2529 if (idx >= m_clusterCount)
2530 return &m_eos; // caller will LoadCluster as desired
2532 Cluster* const pNext = m_clusters[idx];
2534 assert(pNext->m_index >= 0);
2535 assert(pNext->m_index == idx);
2540 assert(m_clusterPreloadCount > 0);
2542 long long pos = pCurr->m_element_start;
2544 assert(m_size >= 0); // TODO
2545 const long long stop = m_start + m_size; // end of segment
2550 long long result = GetUIntLength(m_pReader, pos, len);
2551 assert(result == 0);
2552 assert((pos + len) <= stop); // TODO
2556 const long long id = ReadID(m_pReader, pos, len);
2557 if (id != libwebm::kMkvCluster)
2560 pos += len; // consume ID
2563 result = GetUIntLength(m_pReader, pos, len);
2564 assert(result == 0); // TODO
2565 assert((pos + len) <= stop); // TODO
2567 const long long size = ReadUInt(m_pReader, pos, len);
2568 assert(size > 0); // TODO
2569 // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2571 pos += len; // consume length of size of element
2572 assert((pos + size) <= stop); // TODO
2574 // Pos now points to start of payload
2576 pos += size; // consume payload
2579 long long off_next = 0;
2581 while (pos < stop) {
2584 long long result = GetUIntLength(m_pReader, pos, len);
2585 assert(result == 0);
2586 assert((pos + len) <= stop); // TODO
2590 const long long idpos = pos; // pos of next (potential) cluster
2592 const long long id = ReadID(m_pReader, idpos, len);
2596 pos += len; // consume ID
2599 result = GetUIntLength(m_pReader, pos, len);
2600 assert(result == 0); // TODO
2601 assert((pos + len) <= stop); // TODO
2603 const long long size = ReadUInt(m_pReader, pos, len);
2604 assert(size >= 0); // TODO
2606 pos += len; // consume length of size of element
2607 assert((pos + size) <= stop); // TODO
2609 // Pos now points to start of payload
2611 if (size == 0) // weird
2614 if (id == libwebm::kMkvCluster) {
2615 const long long off_next_ = idpos - m_start;
2620 const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_);
2622 assert(status >= 0);
2625 off_next = off_next_;
2630 pos += size; // consume payload
2636 Cluster** const ii = m_clusters + m_clusterCount;
2639 Cluster** const jj = ii + m_clusterPreloadCount;
2646 //[j, jj) > pos_next
2648 Cluster** const k = i + (j - i) / 2;
2651 Cluster* const pNext = *k;
2653 assert(pNext->m_index < 0);
2655 // const long long pos_ = pNext->m_pos;
2657 // pos = pos_ * ((pos_ < 0) ? -1 : 1);
2659 pos = pNext->GetPosition();
2663 else if (pos > off_next)
2671 Cluster* const pNext = Cluster::Create(this, -1, off_next);
2675 const ptrdiff_t idx_next = i - m_clusters; // insertion position
2677 if (!PreloadCluster(pNext, idx_next)) {
2682 assert(idx_next < m_clusterSize);
2683 assert(m_clusters[idx_next] == pNext);
2688 long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult,
2689 long long& pos, long& len) {
2691 assert(!pCurr->EOS());
2696 if (pCurr->m_index >= 0) { // loaded (not merely preloaded)
2697 assert(m_clusters[pCurr->m_index] == pCurr);
2699 const long next_idx = pCurr->m_index + 1;
2701 if (next_idx < m_clusterCount) {
2702 pResult = m_clusters[next_idx];
2703 return 0; // success
2706 // curr cluster is last among loaded
2708 const long result = LoadCluster(pos, len);
2710 if (result < 0) // error or underflow
2713 if (result > 0) // no more clusters
2715 // pResult = &m_eos;
2719 pResult = GetLast();
2720 return 0; // success
2725 long long total, avail;
2727 long status = m_pReader->Length(&total, &avail);
2729 if (status < 0) // error
2732 assert((total < 0) || (avail <= total));
2734 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2736 // interrogate curr cluster
2738 pos = pCurr->m_element_start;
2740 if (pCurr->m_element_size >= 0)
2741 pos += pCurr->m_element_size;
2743 if ((pos + 1) > avail) {
2745 return E_BUFFER_NOT_FULL;
2748 long long result = GetUIntLength(m_pReader, pos, len);
2750 if (result < 0) // error
2751 return static_cast<long>(result);
2753 if (result > 0) // weird
2754 return E_BUFFER_NOT_FULL;
2756 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2757 return E_FILE_FORMAT_INVALID;
2759 if ((pos + len) > avail)
2760 return E_BUFFER_NOT_FULL;
2762 const long long id = ReadUInt(m_pReader, pos, len);
2764 if (id != libwebm::kMkvCluster)
2767 pos += len; // consume ID
2771 if ((pos + 1) > avail) {
2773 return E_BUFFER_NOT_FULL;
2776 result = GetUIntLength(m_pReader, pos, len);
2778 if (result < 0) // error
2779 return static_cast<long>(result);
2781 if (result > 0) // weird
2782 return E_BUFFER_NOT_FULL;
2784 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2785 return E_FILE_FORMAT_INVALID;
2787 if ((pos + len) > avail)
2788 return E_BUFFER_NOT_FULL;
2790 const long long size = ReadUInt(m_pReader, pos, len);
2792 if (size < 0) // error
2793 return static_cast<long>(size);
2795 pos += len; // consume size field
2797 const long long unknown_size = (1LL << (7 * len)) - 1;
2799 if (size == unknown_size) // TODO: should never happen
2800 return E_FILE_FORMAT_INVALID; // TODO: resolve this
2802 // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2804 if ((segment_stop >= 0) && ((pos + size) > segment_stop))
2805 return E_FILE_FORMAT_INVALID;
2807 // Pos now points to start of payload
2809 pos += size; // consume payload (that is, the current cluster)
2810 if (segment_stop >= 0 && pos > segment_stop)
2811 return E_FILE_FORMAT_INVALID;
2813 // By consuming the payload, we are assuming that the curr
2814 // cluster isn't interesting. That is, we don't bother checking
2815 // whether the payload of the curr cluster is less than what
2816 // happens to be available (obtained via IMkvReader::Length).
2817 // Presumably the caller has already dispensed with the current
2818 // cluster, and really does want the next cluster.
2821 // pos now points to just beyond the last fully-loaded cluster
2824 const long status = DoParseNext(pResult, pos, len);
2831 long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
2832 long long total, avail;
2834 long status = m_pReader->Length(&total, &avail);
2836 if (status < 0) // error
2839 assert((total < 0) || (avail <= total));
2841 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2843 // Parse next cluster. This is strictly a parsing activity.
2844 // Creation of a new cluster object happens later, after the
2847 long long off_next = 0;
2848 long long cluster_size = -1;
2851 if ((total >= 0) && (pos >= total))
2854 if ((segment_stop >= 0) && (pos >= segment_stop))
2857 if ((pos + 1) > avail) {
2859 return E_BUFFER_NOT_FULL;
2862 long long result = GetUIntLength(m_pReader, pos, len);
2864 if (result < 0) // error
2865 return static_cast<long>(result);
2867 if (result > 0) // weird
2868 return E_BUFFER_NOT_FULL;
2870 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2871 return E_FILE_FORMAT_INVALID;
2873 if ((pos + len) > avail)
2874 return E_BUFFER_NOT_FULL;
2876 const long long idpos = pos; // absolute
2877 const long long idoff = pos - m_start; // relative
2879 const long long id = ReadID(m_pReader, idpos, len); // absolute
2881 if (id < 0) // error
2882 return static_cast<long>(id);
2884 if (id == 0) // weird
2885 return -1; // generic error
2887 pos += len; // consume ID
2891 if ((pos + 1) > avail) {
2893 return E_BUFFER_NOT_FULL;
2896 result = GetUIntLength(m_pReader, pos, len);
2898 if (result < 0) // error
2899 return static_cast<long>(result);
2901 if (result > 0) // weird
2902 return E_BUFFER_NOT_FULL;
2904 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2905 return E_FILE_FORMAT_INVALID;
2907 if ((pos + len) > avail)
2908 return E_BUFFER_NOT_FULL;
2910 const long long size = ReadUInt(m_pReader, pos, len);
2912 if (size < 0) // error
2913 return static_cast<long>(size);
2915 pos += len; // consume length of size of element
2917 // Pos now points to start of payload
2919 if (size == 0) // weird
2922 const long long unknown_size = (1LL << (7 * len)) - 1;
2924 if ((segment_stop >= 0) && (size != unknown_size) &&
2925 ((pos + size) > segment_stop)) {
2926 return E_FILE_FORMAT_INVALID;
2929 if (id == libwebm::kMkvCues) {
2930 if (size == unknown_size)
2931 return E_FILE_FORMAT_INVALID;
2933 const long long element_stop = pos + size;
2935 if ((segment_stop >= 0) && (element_stop > segment_stop))
2936 return E_FILE_FORMAT_INVALID;
2938 const long long element_start = idpos;
2939 const long long element_size = element_stop - element_start;
2941 if (m_pCues == NULL) {
2942 m_pCues = new (std::nothrow)
2943 Cues(this, pos, size, element_start, element_size);
2944 if (m_pCues == NULL)
2948 pos += size; // consume payload
2949 if (segment_stop >= 0 && pos > segment_stop)
2950 return E_FILE_FORMAT_INVALID;
2955 if (id != libwebm::kMkvCluster) { // not a Cluster ID
2956 if (size == unknown_size)
2957 return E_FILE_FORMAT_INVALID;
2959 pos += size; // consume payload
2960 if (segment_stop >= 0 && pos > segment_stop)
2961 return E_FILE_FORMAT_INVALID;
2966 // We have a cluster.
2969 if (size != unknown_size)
2970 cluster_size = size;
2975 assert(off_next > 0); // have cluster
2977 // We have parsed the next cluster.
2978 // We have not created a cluster object yet. What we need
2979 // to do now is determine whether it has already be preloaded
2980 //(in which case, an object for this cluster has already been
2981 // created), and if not, create a new cluster object.
2983 Cluster** const ii = m_clusters + m_clusterCount;
2986 Cluster** const jj = ii + m_clusterPreloadCount;
2993 //[j, jj) > pos_next
2995 Cluster** const k = i + (j - i) / 2;
2998 const Cluster* const pNext = *k;
3000 assert(pNext->m_index < 0);
3002 pos = pNext->GetPosition();
3007 else if (pos > off_next)
3011 return 0; // success
3020 status = Cluster::HasBlockEntries(this, off_next, pos_, len_);
3022 if (status < 0) { // error or underflow
3029 if (status > 0) { // means "found at least one block entry"
3030 Cluster* const pNext = Cluster::Create(this,
3036 const ptrdiff_t idx_next = i - m_clusters; // insertion position
3038 if (!PreloadCluster(pNext, idx_next)) {
3043 assert(idx_next < m_clusterSize);
3044 assert(m_clusters[idx_next] == pNext);
3047 return 0; // success
3050 // status == 0 means "no block entries found"
3052 if (cluster_size < 0) { // unknown size
3053 const long long payload_pos = pos; // absolute pos of cluster payload
3055 for (;;) { // determine cluster size
3056 if ((total >= 0) && (pos >= total))
3059 if ((segment_stop >= 0) && (pos >= segment_stop))
3060 break; // no more clusters
3064 if ((pos + 1) > avail) {
3066 return E_BUFFER_NOT_FULL;
3069 long long result = GetUIntLength(m_pReader, pos, len);
3071 if (result < 0) // error
3072 return static_cast<long>(result);
3074 if (result > 0) // weird
3075 return E_BUFFER_NOT_FULL;
3077 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3078 return E_FILE_FORMAT_INVALID;
3080 if ((pos + len) > avail)
3081 return E_BUFFER_NOT_FULL;
3083 const long long idpos = pos;
3084 const long long id = ReadID(m_pReader, idpos, len);
3086 if (id < 0) // error (or underflow)
3087 return static_cast<long>(id);
3089 // This is the distinguished set of ID's we use to determine
3090 // that we have exhausted the sub-element's inside the cluster
3091 // whose ID we parsed earlier.
3093 if (id == libwebm::kMkvCluster || id == libwebm::kMkvCues)
3096 pos += len; // consume ID (of sub-element)
3100 if ((pos + 1) > avail) {
3102 return E_BUFFER_NOT_FULL;
3105 result = GetUIntLength(m_pReader, pos, len);
3107 if (result < 0) // error
3108 return static_cast<long>(result);
3110 if (result > 0) // weird
3111 return E_BUFFER_NOT_FULL;
3113 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3114 return E_FILE_FORMAT_INVALID;
3116 if ((pos + len) > avail)
3117 return E_BUFFER_NOT_FULL;
3119 const long long size = ReadUInt(m_pReader, pos, len);
3121 if (size < 0) // error
3122 return static_cast<long>(size);
3124 pos += len; // consume size field of element
3126 // pos now points to start of sub-element's payload
3128 if (size == 0) // weird
3131 const long long unknown_size = (1LL << (7 * len)) - 1;
3133 if (size == unknown_size)
3134 return E_FILE_FORMAT_INVALID; // not allowed for sub-elements
3136 if ((segment_stop >= 0) && ((pos + size) > segment_stop)) // weird
3137 return E_FILE_FORMAT_INVALID;
3139 pos += size; // consume payload of sub-element
3140 if (segment_stop >= 0 && pos > segment_stop)
3141 return E_FILE_FORMAT_INVALID;
3142 } // determine cluster size
3144 cluster_size = pos - payload_pos;
3145 assert(cluster_size >= 0); // TODO: handle cluster_size = 0
3147 pos = payload_pos; // reset and re-parse original cluster
3150 pos += cluster_size; // consume payload
3151 if (segment_stop >= 0 && pos > segment_stop)
3152 return E_FILE_FORMAT_INVALID;
3154 return 2; // try to find a cluster that follows next
3157 const Cluster* Segment::FindCluster(long long time_ns) const {
3158 if ((m_clusters == NULL) || (m_clusterCount <= 0))
3162 Cluster* const pCluster = m_clusters[0];
3164 assert(pCluster->m_index == 0);
3166 if (time_ns <= pCluster->GetTime())
3170 // Binary search of cluster array
3173 long j = m_clusterCount;
3179 //[j, m_clusterCount) > time_ns
3181 const long k = i + (j - i) / 2;
3182 assert(k < m_clusterCount);
3184 Cluster* const pCluster = m_clusters[k];
3186 assert(pCluster->m_index == k);
3188 const long long t = pCluster->GetTime();
3200 assert(i <= m_clusterCount);
3202 const long k = i - 1;
3204 Cluster* const pCluster = m_clusters[k];
3206 assert(pCluster->m_index == k);
3207 assert(pCluster->GetTime() <= time_ns);
3212 const Tracks* Segment::GetTracks() const { return m_pTracks; }
3213 const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
3214 const Cues* Segment::GetCues() const { return m_pCues; }
3215 const Chapters* Segment::GetChapters() const { return m_pChapters; }
3216 const Tags* Segment::GetTags() const { return m_pTags; }
3217 const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
3219 long long Segment::GetDuration() const {
3221 return m_pInfo->GetDuration();
3224 Chapters::Chapters(Segment* pSegment, long long payload_start,
3225 long long payload_size, long long element_start,
3226 long long element_size)
3227 : m_pSegment(pSegment),
3228 m_start(payload_start),
3229 m_size(payload_size),
3230 m_element_start(element_start),
3231 m_element_size(element_size),
3234 m_editions_count(0) {}
3236 Chapters::~Chapters() {
3237 while (m_editions_count > 0) {
3238 Edition& e = m_editions[--m_editions_count];
3241 delete[] m_editions;
3244 long Chapters::Parse() {
3245 IMkvReader* const pReader = m_pSegment->m_pReader;
3247 long long pos = m_start; // payload start
3248 const long long stop = pos + m_size; // payload stop
3250 while (pos < stop) {
3253 long status = ParseElementHeader(pReader, pos, stop, id, size);
3255 if (status < 0) // error
3258 if (size == 0) // weird
3261 if (id == libwebm::kMkvEditionEntry) {
3262 status = ParseEdition(pos, size);
3264 if (status < 0) // error
3270 return E_FILE_FORMAT_INVALID;
3274 return E_FILE_FORMAT_INVALID;
3278 int Chapters::GetEditionCount() const { return m_editions_count; }
3280 const Chapters::Edition* Chapters::GetEdition(int idx) const {
3284 if (idx >= m_editions_count)
3287 return m_editions + idx;
3290 bool Chapters::ExpandEditionsArray() {
3291 if (m_editions_size > m_editions_count)
3292 return true; // nothing else to do
3294 const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
3296 Edition* const editions = new (std::nothrow) Edition[size];
3298 if (editions == NULL)
3301 for (int idx = 0; idx < m_editions_count; ++idx) {
3302 m_editions[idx].ShallowCopy(editions[idx]);
3305 delete[] m_editions;
3306 m_editions = editions;
3308 m_editions_size = size;
3312 long Chapters::ParseEdition(long long pos, long long size) {
3313 if (!ExpandEditionsArray())
3316 Edition& e = m_editions[m_editions_count++];
3319 return e.Parse(m_pSegment->m_pReader, pos, size);
3322 Chapters::Edition::Edition() {}
3324 Chapters::Edition::~Edition() {}
3326 int Chapters::Edition::GetAtomCount() const { return m_atoms_count; }
3328 const Chapters::Atom* Chapters::Edition::GetAtom(int index) const {
3332 if (index >= m_atoms_count)
3335 return m_atoms + index;
3338 void Chapters::Edition::Init() {
3344 void Chapters::Edition::ShallowCopy(Edition& rhs) const {
3345 rhs.m_atoms = m_atoms;
3346 rhs.m_atoms_size = m_atoms_size;
3347 rhs.m_atoms_count = m_atoms_count;
3350 void Chapters::Edition::Clear() {
3351 while (m_atoms_count > 0) {
3352 Atom& a = m_atoms[--m_atoms_count];
3362 long Chapters::Edition::Parse(IMkvReader* pReader, long long pos,
3364 const long long stop = pos + size;
3366 while (pos < stop) {
3369 long status = ParseElementHeader(pReader, pos, stop, id, size);
3371 if (status < 0) // error
3377 if (id == libwebm::kMkvChapterAtom) {
3378 status = ParseAtom(pReader, pos, size);
3380 if (status < 0) // error
3386 return E_FILE_FORMAT_INVALID;
3390 return E_FILE_FORMAT_INVALID;
3394 long Chapters::Edition::ParseAtom(IMkvReader* pReader, long long pos,
3396 if (!ExpandAtomsArray())
3399 Atom& a = m_atoms[m_atoms_count++];
3402 return a.Parse(pReader, pos, size);
3405 bool Chapters::Edition::ExpandAtomsArray() {
3406 if (m_atoms_size > m_atoms_count)
3407 return true; // nothing else to do
3409 const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
3411 Atom* const atoms = new (std::nothrow) Atom[size];
3416 for (int idx = 0; idx < m_atoms_count; ++idx) {
3417 m_atoms[idx].ShallowCopy(atoms[idx]);
3423 m_atoms_size = size;
3427 Chapters::Atom::Atom() {}
3429 Chapters::Atom::~Atom() {}
3431 unsigned long long Chapters::Atom::GetUID() const { return m_uid; }
3433 const char* Chapters::Atom::GetStringUID() const { return m_string_uid; }
3435 long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; }
3437 long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; }
3439 long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const {
3440 return GetTime(pChapters, m_start_timecode);
3443 long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const {
3444 return GetTime(pChapters, m_stop_timecode);
3447 int Chapters::Atom::GetDisplayCount() const { return m_displays_count; }
3449 const Chapters::Display* Chapters::Atom::GetDisplay(int index) const {
3453 if (index >= m_displays_count)
3456 return m_displays + index;
3459 void Chapters::Atom::Init() {
3460 m_string_uid = NULL;
3462 m_start_timecode = -1;
3463 m_stop_timecode = -1;
3466 m_displays_size = 0;
3467 m_displays_count = 0;
3470 void Chapters::Atom::ShallowCopy(Atom& rhs) const {
3471 rhs.m_string_uid = m_string_uid;
3473 rhs.m_start_timecode = m_start_timecode;
3474 rhs.m_stop_timecode = m_stop_timecode;
3476 rhs.m_displays = m_displays;
3477 rhs.m_displays_size = m_displays_size;
3478 rhs.m_displays_count = m_displays_count;
3481 void Chapters::Atom::Clear() {
3482 delete[] m_string_uid;
3483 m_string_uid = NULL;
3485 while (m_displays_count > 0) {
3486 Display& d = m_displays[--m_displays_count];
3490 delete[] m_displays;
3493 m_displays_size = 0;
3496 long Chapters::Atom::Parse(IMkvReader* pReader, long long pos, long long size) {
3497 const long long stop = pos + size;
3499 while (pos < stop) {
3502 long status = ParseElementHeader(pReader, pos, stop, id, size);
3504 if (status < 0) // error
3507 if (size == 0) // 0 length payload, skip.
3510 if (id == libwebm::kMkvChapterDisplay) {
3511 status = ParseDisplay(pReader, pos, size);
3513 if (status < 0) // error
3515 } else if (id == libwebm::kMkvChapterStringUID) {
3516 status = UnserializeString(pReader, pos, size, m_string_uid);
3518 if (status < 0) // error
3520 } else if (id == libwebm::kMkvChapterUID) {
3522 status = UnserializeInt(pReader, pos, size, val);
3524 if (status < 0) // error
3527 m_uid = static_cast<unsigned long long>(val);
3528 } else if (id == libwebm::kMkvChapterTimeStart) {
3529 const long long val = UnserializeUInt(pReader, pos, size);
3531 if (val < 0) // error
3532 return static_cast<long>(val);
3534 m_start_timecode = val;
3535 } else if (id == libwebm::kMkvChapterTimeEnd) {
3536 const long long val = UnserializeUInt(pReader, pos, size);
3538 if (val < 0) // error
3539 return static_cast<long>(val);
3541 m_stop_timecode = val;
3546 return E_FILE_FORMAT_INVALID;
3550 return E_FILE_FORMAT_INVALID;
3554 long long Chapters::Atom::GetTime(const Chapters* pChapters,
3555 long long timecode) {
3556 if (pChapters == NULL)
3559 Segment* const pSegment = pChapters->m_pSegment;
3561 if (pSegment == NULL) // weird
3564 const SegmentInfo* const pInfo = pSegment->GetInfo();
3569 const long long timecode_scale = pInfo->GetTimeCodeScale();
3571 if (timecode_scale < 1) // weird
3577 const long long result = timecode_scale * timecode;
3582 long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
3584 if (!ExpandDisplaysArray())
3587 Display& d = m_displays[m_displays_count++];
3590 return d.Parse(pReader, pos, size);
3593 bool Chapters::Atom::ExpandDisplaysArray() {
3594 if (m_displays_size > m_displays_count)
3595 return true; // nothing else to do
3597 const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
3599 Display* const displays = new (std::nothrow) Display[size];
3601 if (displays == NULL)
3604 for (int idx = 0; idx < m_displays_count; ++idx) {
3605 m_displays[idx].ShallowCopy(displays[idx]);
3608 delete[] m_displays;
3609 m_displays = displays;
3611 m_displays_size = size;
3615 Chapters::Display::Display() {}
3617 Chapters::Display::~Display() {}
3619 const char* Chapters::Display::GetString() const { return m_string; }
3621 const char* Chapters::Display::GetLanguage() const { return m_language; }
3623 const char* Chapters::Display::GetCountry() const { return m_country; }
3625 void Chapters::Display::Init() {
3631 void Chapters::Display::ShallowCopy(Display& rhs) const {
3632 rhs.m_string = m_string;
3633 rhs.m_language = m_language;
3634 rhs.m_country = m_country;
3637 void Chapters::Display::Clear() {
3641 delete[] m_language;
3648 long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
3650 const long long stop = pos + size;
3652 while (pos < stop) {
3655 long status = ParseElementHeader(pReader, pos, stop, id, size);
3657 if (status < 0) // error
3660 if (size == 0) // No payload.
3663 if (id == libwebm::kMkvChapString) {
3664 status = UnserializeString(pReader, pos, size, m_string);
3668 } else if (id == libwebm::kMkvChapLanguage) {
3669 status = UnserializeString(pReader, pos, size, m_language);
3673 } else if (id == libwebm::kMkvChapCountry) {
3674 status = UnserializeString(pReader, pos, size, m_country);
3682 return E_FILE_FORMAT_INVALID;
3686 return E_FILE_FORMAT_INVALID;
3690 Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size,
3691 long long element_start, long long element_size)
3692 : m_pSegment(pSegment),
3693 m_start(payload_start),
3694 m_size(payload_size),
3695 m_element_start(element_start),
3696 m_element_size(element_size),
3702 while (m_tags_count > 0) {
3703 Tag& t = m_tags[--m_tags_count];
3709 long Tags::Parse() {
3710 IMkvReader* const pReader = m_pSegment->m_pReader;
3712 long long pos = m_start; // payload start
3713 const long long stop = pos + m_size; // payload stop
3715 while (pos < stop) {
3718 long status = ParseElementHeader(pReader, pos, stop, id, size);
3723 if (size == 0) // 0 length tag, read another
3726 if (id == libwebm::kMkvTag) {
3727 status = ParseTag(pos, size);
3735 return E_FILE_FORMAT_INVALID;
3739 return E_FILE_FORMAT_INVALID;
3744 int Tags::GetTagCount() const { return m_tags_count; }
3746 const Tags::Tag* Tags::GetTag(int idx) const {
3750 if (idx >= m_tags_count)
3753 return m_tags + idx;
3756 bool Tags::ExpandTagsArray() {
3757 if (m_tags_size > m_tags_count)
3758 return true; // nothing else to do
3760 const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
3762 Tag* const tags = new (std::nothrow) Tag[size];
3767 for (int idx = 0; idx < m_tags_count; ++idx) {
3768 m_tags[idx].ShallowCopy(tags[idx]);
3778 long Tags::ParseTag(long long pos, long long size) {
3779 if (!ExpandTagsArray())
3782 Tag& t = m_tags[m_tags_count++];
3785 return t.Parse(m_pSegment->m_pReader, pos, size);
3790 Tags::Tag::~Tag() {}
3792 int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
3794 const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
3798 if (index >= m_simple_tags_count)
3801 return m_simple_tags + index;
3804 void Tags::Tag::Init() {
3805 m_simple_tags = NULL;
3806 m_simple_tags_size = 0;
3807 m_simple_tags_count = 0;
3810 void Tags::Tag::ShallowCopy(Tag& rhs) const {
3811 rhs.m_simple_tags = m_simple_tags;
3812 rhs.m_simple_tags_size = m_simple_tags_size;
3813 rhs.m_simple_tags_count = m_simple_tags_count;
3816 void Tags::Tag::Clear() {
3817 while (m_simple_tags_count > 0) {
3818 SimpleTag& d = m_simple_tags[--m_simple_tags_count];
3822 delete[] m_simple_tags;
3823 m_simple_tags = NULL;
3825 m_simple_tags_size = 0;
3828 long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) {
3829 const long long stop = pos + size;
3831 while (pos < stop) {
3834 long status = ParseElementHeader(pReader, pos, stop, id, size);
3839 if (size == 0) // 0 length tag, read another
3842 if (id == libwebm::kMkvSimpleTag) {
3843 status = ParseSimpleTag(pReader, pos, size);
3851 return E_FILE_FORMAT_INVALID;
3855 return E_FILE_FORMAT_INVALID;
3859 long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
3861 if (!ExpandSimpleTagsArray())
3864 SimpleTag& st = m_simple_tags[m_simple_tags_count++];
3867 return st.Parse(pReader, pos, size);
3870 bool Tags::Tag::ExpandSimpleTagsArray() {
3871 if (m_simple_tags_size > m_simple_tags_count)
3872 return true; // nothing else to do
3874 const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
3876 SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
3878 if (displays == NULL)
3881 for (int idx = 0; idx < m_simple_tags_count; ++idx) {
3882 m_simple_tags[idx].ShallowCopy(displays[idx]);
3885 delete[] m_simple_tags;
3886 m_simple_tags = displays;
3888 m_simple_tags_size = size;
3892 Tags::SimpleTag::SimpleTag() {}
3894 Tags::SimpleTag::~SimpleTag() {}
3896 const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
3898 const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
3900 void Tags::SimpleTag::Init() {
3902 m_tag_string = NULL;
3905 void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
3906 rhs.m_tag_name = m_tag_name;
3907 rhs.m_tag_string = m_tag_string;
3910 void Tags::SimpleTag::Clear() {
3911 delete[] m_tag_name;
3914 delete[] m_tag_string;
3915 m_tag_string = NULL;
3918 long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos,
3920 const long long stop = pos + size;
3922 while (pos < stop) {
3925 long status = ParseElementHeader(pReader, pos, stop, id, size);
3927 if (status < 0) // error
3930 if (size == 0) // weird
3933 if (id == libwebm::kMkvTagName) {
3934 status = UnserializeString(pReader, pos, size, m_tag_name);
3938 } else if (id == libwebm::kMkvTagString) {
3939 status = UnserializeString(pReader, pos, size, m_tag_string);
3947 return E_FILE_FORMAT_INVALID;
3951 return E_FILE_FORMAT_INVALID;
3955 SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
3956 long long element_start, long long element_size)
3957 : m_pSegment(pSegment),
3960 m_element_start(element_start),
3961 m_element_size(element_size),
3962 m_pMuxingAppAsUTF8(NULL),
3963 m_pWritingAppAsUTF8(NULL),
3964 m_pTitleAsUTF8(NULL) {}
3966 SegmentInfo::~SegmentInfo() {
3967 delete[] m_pMuxingAppAsUTF8;
3968 m_pMuxingAppAsUTF8 = NULL;
3970 delete[] m_pWritingAppAsUTF8;
3971 m_pWritingAppAsUTF8 = NULL;
3973 delete[] m_pTitleAsUTF8;
3974 m_pTitleAsUTF8 = NULL;
3977 long SegmentInfo::Parse() {
3978 assert(m_pMuxingAppAsUTF8 == NULL);
3979 assert(m_pWritingAppAsUTF8 == NULL);
3980 assert(m_pTitleAsUTF8 == NULL);
3982 IMkvReader* const pReader = m_pSegment->m_pReader;
3984 long long pos = m_start;
3985 const long long stop = m_start + m_size;
3987 m_timecodeScale = 1000000;
3990 while (pos < stop) {
3993 const long status = ParseElementHeader(pReader, pos, stop, id, size);
3995 if (status < 0) // error
3998 if (id == libwebm::kMkvTimecodeScale) {
3999 m_timecodeScale = UnserializeUInt(pReader, pos, size);
4001 if (m_timecodeScale <= 0)
4002 return E_FILE_FORMAT_INVALID;
4003 } else if (id == libwebm::kMkvDuration) {
4004 const long status = UnserializeFloat(pReader, pos, size, m_duration);
4010 return E_FILE_FORMAT_INVALID;
4011 } else if (id == libwebm::kMkvMuxingApp) {
4013 UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8);
4017 } else if (id == libwebm::kMkvWritingApp) {
4019 UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8);
4023 } else if (id == libwebm::kMkvTitle) {
4024 const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8);
4033 return E_FILE_FORMAT_INVALID;
4036 const double rollover_check = m_duration * m_timecodeScale;
4037 if (rollover_check > LLONG_MAX)
4038 return E_FILE_FORMAT_INVALID;
4041 return E_FILE_FORMAT_INVALID;
4046 long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; }
4048 long long SegmentInfo::GetDuration() const {
4052 assert(m_timecodeScale >= 1);
4054 const double dd = double(m_duration) * double(m_timecodeScale);
4055 const long long d = static_cast<long long>(dd);
4060 const char* SegmentInfo::GetMuxingAppAsUTF8() const {
4061 return m_pMuxingAppAsUTF8;
4064 const char* SegmentInfo::GetWritingAppAsUTF8() const {
4065 return m_pWritingAppAsUTF8;
4068 const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; }
4070 ///////////////////////////////////////////////////////////////
4071 // ContentEncoding element
4072 ContentEncoding::ContentCompression::ContentCompression()
4073 : algo(0), settings(NULL), settings_len(0) {}
4075 ContentEncoding::ContentCompression::~ContentCompression() {
4079 ContentEncoding::ContentEncryption::ContentEncryption()
4090 ContentEncoding::ContentEncryption::~ContentEncryption() {
4093 delete[] sig_key_id;
4096 ContentEncoding::ContentEncoding()
4097 : compression_entries_(NULL),
4098 compression_entries_end_(NULL),
4099 encryption_entries_(NULL),
4100 encryption_entries_end_(NULL),
4103 encoding_type_(0) {}
4105 ContentEncoding::~ContentEncoding() {
4106 ContentCompression** comp_i = compression_entries_;
4107 ContentCompression** const comp_j = compression_entries_end_;
4109 while (comp_i != comp_j) {
4110 ContentCompression* const comp = *comp_i++;
4114 delete[] compression_entries_;
4116 ContentEncryption** enc_i = encryption_entries_;
4117 ContentEncryption** const enc_j = encryption_entries_end_;
4119 while (enc_i != enc_j) {
4120 ContentEncryption* const enc = *enc_i++;
4124 delete[] encryption_entries_;
4127 const ContentEncoding::ContentCompression*
4128 ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
4129 const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4132 if (idx >= static_cast<unsigned long>(count))
4135 return compression_entries_[idx];
4138 unsigned long ContentEncoding::GetCompressionCount() const {
4139 const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4142 return static_cast<unsigned long>(count);
4145 const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex(
4146 unsigned long idx) const {
4147 const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4150 if (idx >= static_cast<unsigned long>(count))
4153 return encryption_entries_[idx];
4156 unsigned long ContentEncoding::GetEncryptionCount() const {
4157 const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4160 return static_cast<unsigned long>(count);
4163 long ContentEncoding::ParseContentEncAESSettingsEntry(
4164 long long start, long long size, IMkvReader* pReader,
4165 ContentEncAESSettings* aes) {
4169 long long pos = start;
4170 const long long stop = start + size;
4172 while (pos < stop) {
4174 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4175 if (status < 0) // error
4178 if (id == libwebm::kMkvAESSettingsCipherMode) {
4179 aes->cipher_mode = UnserializeUInt(pReader, pos, size);
4180 if (aes->cipher_mode != 1)
4181 return E_FILE_FORMAT_INVALID;
4184 pos += size; // consume payload
4186 return E_FILE_FORMAT_INVALID;
4192 long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
4193 IMkvReader* pReader) {
4196 long long pos = start;
4197 const long long stop = start + size;
4199 // Count ContentCompression and ContentEncryption elements.
4200 int compression_count = 0;
4201 int encryption_count = 0;
4203 while (pos < stop) {
4205 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4206 if (status < 0) // error
4209 if (id == libwebm::kMkvContentCompression)
4210 ++compression_count;
4212 if (id == libwebm::kMkvContentEncryption)
4215 pos += size; // consume payload
4217 return E_FILE_FORMAT_INVALID;
4220 if (compression_count <= 0 && encryption_count <= 0)
4223 if (compression_count > 0) {
4224 compression_entries_ =
4225 new (std::nothrow) ContentCompression*[compression_count];
4226 if (!compression_entries_)
4228 compression_entries_end_ = compression_entries_;
4231 if (encryption_count > 0) {
4232 encryption_entries_ =
4233 new (std::nothrow) ContentEncryption*[encryption_count];
4234 if (!encryption_entries_) {
4235 delete[] compression_entries_;
4238 encryption_entries_end_ = encryption_entries_;
4242 while (pos < stop) {
4244 long status = ParseElementHeader(pReader, pos, stop, id, size);
4245 if (status < 0) // error
4248 if (id == libwebm::kMkvContentEncodingOrder) {
4249 encoding_order_ = UnserializeUInt(pReader, pos, size);
4250 } else if (id == libwebm::kMkvContentEncodingScope) {
4251 encoding_scope_ = UnserializeUInt(pReader, pos, size);
4252 if (encoding_scope_ < 1)
4254 } else if (id == libwebm::kMkvContentEncodingType) {
4255 encoding_type_ = UnserializeUInt(pReader, pos, size);
4256 } else if (id == libwebm::kMkvContentCompression) {
4257 ContentCompression* const compression =
4258 new (std::nothrow) ContentCompression();
4262 status = ParseCompressionEntry(pos, size, pReader, compression);
4267 *compression_entries_end_++ = compression;
4268 } else if (id == libwebm::kMkvContentEncryption) {
4269 ContentEncryption* const encryption =
4270 new (std::nothrow) ContentEncryption();
4274 status = ParseEncryptionEntry(pos, size, pReader, encryption);
4279 *encryption_entries_end_++ = encryption;
4282 pos += size; // consume payload
4284 return E_FILE_FORMAT_INVALID;
4288 return E_FILE_FORMAT_INVALID;
4292 long ContentEncoding::ParseCompressionEntry(long long start, long long size,
4293 IMkvReader* pReader,
4294 ContentCompression* compression) {
4296 assert(compression);
4298 long long pos = start;
4299 const long long stop = start + size;
4303 while (pos < stop) {
4305 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4306 if (status < 0) // error
4309 if (id == libwebm::kMkvContentCompAlgo) {
4310 long long algo = UnserializeUInt(pReader, pos, size);
4312 return E_FILE_FORMAT_INVALID;
4313 compression->algo = algo;
4315 } else if (id == libwebm::kMkvContentCompSettings) {
4317 return E_FILE_FORMAT_INVALID;
4319 const size_t buflen = static_cast<size_t>(size);
4320 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4324 const int read_status =
4325 pReader->Read(pos, static_cast<long>(buflen), buf);
4331 compression->settings = buf;
4332 compression->settings_len = buflen;
4335 pos += size; // consume payload
4337 return E_FILE_FORMAT_INVALID;
4340 // ContentCompAlgo is mandatory
4342 return E_FILE_FORMAT_INVALID;
4347 long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
4348 IMkvReader* pReader,
4349 ContentEncryption* encryption) {
4353 long long pos = start;
4354 const long long stop = start + size;
4356 while (pos < stop) {
4358 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4359 if (status < 0) // error
4362 if (id == libwebm::kMkvContentEncAlgo) {
4363 encryption->algo = UnserializeUInt(pReader, pos, size);
4364 if (encryption->algo != 5)
4365 return E_FILE_FORMAT_INVALID;
4366 } else if (id == libwebm::kMkvContentEncKeyID) {
4367 delete[] encryption->key_id;
4368 encryption->key_id = NULL;
4369 encryption->key_id_len = 0;
4372 return E_FILE_FORMAT_INVALID;
4374 const size_t buflen = static_cast<size_t>(size);
4375 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4379 const int read_status =
4380 pReader->Read(pos, static_cast<long>(buflen), buf);
4386 encryption->key_id = buf;
4387 encryption->key_id_len = buflen;
4388 } else if (id == libwebm::kMkvContentSignature) {
4389 delete[] encryption->signature;
4390 encryption->signature = NULL;
4391 encryption->signature_len = 0;
4394 return E_FILE_FORMAT_INVALID;
4396 const size_t buflen = static_cast<size_t>(size);
4397 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4401 const int read_status =
4402 pReader->Read(pos, static_cast<long>(buflen), buf);
4408 encryption->signature = buf;
4409 encryption->signature_len = buflen;
4410 } else if (id == libwebm::kMkvContentSigKeyID) {
4411 delete[] encryption->sig_key_id;
4412 encryption->sig_key_id = NULL;
4413 encryption->sig_key_id_len = 0;
4416 return E_FILE_FORMAT_INVALID;
4418 const size_t buflen = static_cast<size_t>(size);
4419 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4423 const int read_status =
4424 pReader->Read(pos, static_cast<long>(buflen), buf);
4430 encryption->sig_key_id = buf;
4431 encryption->sig_key_id_len = buflen;
4432 } else if (id == libwebm::kMkvContentSigAlgo) {
4433 encryption->sig_algo = UnserializeUInt(pReader, pos, size);
4434 } else if (id == libwebm::kMkvContentSigHashAlgo) {
4435 encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
4436 } else if (id == libwebm::kMkvContentEncAESSettings) {
4437 const long status = ParseContentEncAESSettingsEntry(
4438 pos, size, pReader, &encryption->aes_settings);
4443 pos += size; // consume payload
4445 return E_FILE_FORMAT_INVALID;
4451 Track::Track(Segment* pSegment, long long element_start, long long element_size)
4452 : m_pSegment(pSegment),
4453 m_element_start(element_start),
4454 m_element_size(element_size),
4455 content_encoding_entries_(NULL),
4456 content_encoding_entries_end_(NULL) {}
4459 Info& info = const_cast<Info&>(m_info);
4462 ContentEncoding** i = content_encoding_entries_;
4463 ContentEncoding** const j = content_encoding_entries_end_;
4466 ContentEncoding* const encoding = *i++;
4470 delete[] content_encoding_entries_;
4473 long Track::Create(Segment* pSegment, const Info& info, long long element_start,
4474 long long element_size, Track*& pResult) {
4478 Track* const pTrack =
4479 new (std::nothrow) Track(pSegment, element_start, element_size);
4482 return -1; // generic error
4484 const int status = info.Copy(pTrack->m_info);
4486 if (status) { // error
4492 return 0; // success
4503 codecNameAsUTF8(NULL),
4505 codecPrivateSize(0),
4508 Track::Info::~Info() { Clear(); }
4510 void Track::Info::Clear() {
4511 delete[] nameAsUTF8;
4520 delete[] codecPrivate;
4521 codecPrivate = NULL;
4522 codecPrivateSize = 0;
4524 delete[] codecNameAsUTF8;
4525 codecNameAsUTF8 = NULL;
4528 int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
4529 if (str == static_cast<char * Info::*>(NULL))
4532 char*& dst = dst_.*str;
4534 if (dst) // should be NULL already
4537 const char* const src = this->*str;
4542 const size_t len = strlen(src);
4544 dst = SafeArrayAlloc<char>(1, len + 1);
4554 int Track::Info::Copy(Info& dst) const {
4559 dst.number = number;
4560 dst.defaultDuration = defaultDuration;
4561 dst.codecDelay = codecDelay;
4562 dst.seekPreRoll = seekPreRoll;
4564 dst.lacing = lacing;
4565 dst.settings = settings;
4567 // We now copy the string member variables from src to dst.
4568 // This involves memory allocation so in principle the operation
4569 // can fail (indeed, that's why we have Info::Copy), so we must
4570 // report this to the caller. An error return from this function
4571 // therefore implies that the copy was only partially successful.
4573 if (int status = CopyStr(&Info::nameAsUTF8, dst))
4576 if (int status = CopyStr(&Info::language, dst))
4579 if (int status = CopyStr(&Info::codecId, dst))
4582 if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
4585 if (codecPrivateSize > 0) {
4586 if (codecPrivate == NULL)
4589 if (dst.codecPrivate)
4592 if (dst.codecPrivateSize != 0)
4595 dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
4597 if (dst.codecPrivate == NULL)
4600 memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
4601 dst.codecPrivateSize = codecPrivateSize;
4607 const BlockEntry* Track::GetEOS() const { return &m_eos; }
4609 long Track::GetType() const { return m_info.type; }
4611 long Track::GetNumber() const { return m_info.number; }
4613 unsigned long long Track::GetUid() const { return m_info.uid; }
4615 const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
4617 const char* Track::GetLanguage() const { return m_info.language; }
4619 const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
4621 const char* Track::GetCodecId() const { return m_info.codecId; }
4623 const unsigned char* Track::GetCodecPrivate(size_t& size) const {
4624 size = m_info.codecPrivateSize;
4625 return m_info.codecPrivate;
4628 bool Track::GetLacing() const { return m_info.lacing; }
4630 unsigned long long Track::GetDefaultDuration() const {
4631 return m_info.defaultDuration;
4634 unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
4636 unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
4638 long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
4639 const Cluster* pCluster = m_pSegment->GetFirst();
4642 if (pCluster == NULL) {
4643 pBlockEntry = GetEOS();
4647 if (pCluster->EOS()) {
4648 if (m_pSegment->DoneParsing()) {
4649 pBlockEntry = GetEOS();
4654 return E_BUFFER_NOT_FULL;
4657 long status = pCluster->GetFirst(pBlockEntry);
4659 if (status < 0) // error
4662 if (pBlockEntry == 0) { // empty cluster
4663 pCluster = m_pSegment->GetNext(pCluster);
4668 const Block* const pBlock = pBlockEntry->GetBlock();
4671 const long long tn = pBlock->GetTrackNumber();
4673 if ((tn == m_info.number) && VetEntry(pBlockEntry))
4676 const BlockEntry* pNextEntry;
4678 status = pCluster->GetNext(pBlockEntry, pNextEntry);
4680 if (status < 0) // error
4683 if (pNextEntry == 0)
4686 pBlockEntry = pNextEntry;
4694 pCluster = m_pSegment->GetNext(pCluster);
4697 // NOTE: if we get here, it means that we didn't find a block with
4698 // a matching track number. We interpret that as an error (which
4699 // might be too conservative).
4701 pBlockEntry = GetEOS(); // so we can return a non-NULL value
4705 long Track::GetNext(const BlockEntry* pCurrEntry,
4706 const BlockEntry*& pNextEntry) const {
4708 assert(!pCurrEntry->EOS()); //?
4710 const Block* const pCurrBlock = pCurrEntry->GetBlock();
4711 assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
4712 if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
4715 const Cluster* pCluster = pCurrEntry->GetCluster();
4717 assert(!pCluster->EOS());
4719 long status = pCluster->GetNext(pCurrEntry, pNextEntry);
4721 if (status < 0) // error
4725 while (pNextEntry) {
4726 const Block* const pNextBlock = pNextEntry->GetBlock();
4729 if (pNextBlock->GetTrackNumber() == m_info.number)
4732 pCurrEntry = pNextEntry;
4734 status = pCluster->GetNext(pCurrEntry, pNextEntry);
4736 if (status < 0) // error
4740 pCluster = m_pSegment->GetNext(pCluster);
4742 if (pCluster == NULL) {
4743 pNextEntry = GetEOS();
4747 if (pCluster->EOS()) {
4748 if (m_pSegment->DoneParsing()) {
4749 pNextEntry = GetEOS();
4753 // TODO: there is a potential O(n^2) problem here: we tell the
4754 // caller to (pre)load another cluster, which he does, but then he
4755 // calls GetNext again, which repeats the same search. This is
4756 // a pathological case, since the only way it can happen is if
4757 // there exists a long sequence of clusters none of which contain a
4758 // block from this track. One way around this problem is for the
4759 // caller to be smarter when he loads another cluster: don't call
4760 // us back until you have a cluster that contains a block from this
4761 // track. (Of course, that's not cheap either, since our caller
4762 // would have to scan the each cluster as it's loaded, so that
4763 // would just push back the problem.)
4766 return E_BUFFER_NOT_FULL;
4769 status = pCluster->GetFirst(pNextEntry);
4771 if (status < 0) // error
4774 if (pNextEntry == NULL) // empty cluster
4783 // NOTE: if we get here, it means that we didn't find a block with
4784 // a matching track number after lots of searching, so we give
4787 pNextEntry = GetEOS(); // so we can return a non-NULL value
4791 bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
4792 assert(pBlockEntry);
4793 const Block* const pBlock = pBlockEntry->GetBlock();
4795 assert(pBlock->GetTrackNumber() == m_info.number);
4796 if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
4799 // This function is used during a seek to determine whether the
4800 // frame is a valid seek target. This default function simply
4801 // returns true, which means all frames are valid seek targets.
4802 // It gets overridden by the VideoTrack class, because only video
4803 // keyframes can be used as seek target.
4808 long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
4809 const long status = GetFirst(pResult);
4811 if (status < 0) // buffer underflow, etc
4819 const Cluster* pCluster = pResult->GetCluster();
4821 assert(pCluster->GetIndex() >= 0);
4823 if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
4826 Cluster** const clusters = m_pSegment->m_clusters;
4829 const long count = m_pSegment->GetCount(); // loaded only, not preloaded
4832 Cluster** const i = clusters + pCluster->GetIndex();
4834 assert(*i == pCluster);
4835 assert(pCluster->GetTime() <= time_ns);
4837 Cluster** const j = clusters + count;
4844 //[i, lo) <= time_ns
4848 Cluster** const mid = lo + (hi - lo) / 2;
4853 assert(pCluster->GetIndex() >= 0);
4854 assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
4856 const long long t = pCluster->GetTime();
4873 assert(pCluster->GetTime() <= time_ns);
4875 pResult = pCluster->GetEntry(this);
4877 if ((pResult != 0) && !pResult->EOS())
4880 // landed on empty cluster (no entries)
4883 pResult = GetEOS(); // weird
4887 const ContentEncoding* Track::GetContentEncodingByIndex(
4888 unsigned long idx) const {
4889 const ptrdiff_t count =
4890 content_encoding_entries_end_ - content_encoding_entries_;
4893 if (idx >= static_cast<unsigned long>(count))
4896 return content_encoding_entries_[idx];
4899 unsigned long Track::GetContentEncodingCount() const {
4900 const ptrdiff_t count =
4901 content_encoding_entries_end_ - content_encoding_entries_;
4904 return static_cast<unsigned long>(count);
4907 long Track::ParseContentEncodingsEntry(long long start, long long size) {
4908 IMkvReader* const pReader = m_pSegment->m_pReader;
4911 long long pos = start;
4912 const long long stop = start + size;
4914 // Count ContentEncoding elements.
4916 while (pos < stop) {
4918 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4919 if (status < 0) // error
4922 // pos now designates start of element
4923 if (id == libwebm::kMkvContentEncoding)
4926 pos += size; // consume payload
4928 return E_FILE_FORMAT_INVALID;
4934 content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
4935 if (!content_encoding_entries_)
4938 content_encoding_entries_end_ = content_encoding_entries_;
4941 while (pos < stop) {
4943 long status = ParseElementHeader(pReader, pos, stop, id, size);
4944 if (status < 0) // error
4947 // pos now designates start of element
4948 if (id == libwebm::kMkvContentEncoding) {
4949 ContentEncoding* const content_encoding =
4950 new (std::nothrow) ContentEncoding();
4951 if (!content_encoding)
4954 status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
4956 delete content_encoding;
4960 *content_encoding_entries_end_++ = content_encoding;
4963 pos += size; // consume payload
4965 return E_FILE_FORMAT_INVALID;
4969 return E_FILE_FORMAT_INVALID;
4974 Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
4976 BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
4978 const Block* Track::EOSBlock::GetBlock() const { return NULL; }
4980 bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos,
4981 long long value_size, bool is_x,
4982 PrimaryChromaticity** chromaticity) {
4986 std::auto_ptr<PrimaryChromaticity> chromaticity_ptr;
4988 if (!*chromaticity) {
4989 chromaticity_ptr.reset(new PrimaryChromaticity());
4991 chromaticity_ptr.reset(*chromaticity);
4994 if (!chromaticity_ptr.get())
4997 float* value = is_x ? &chromaticity_ptr->x : &chromaticity_ptr->y;
4999 double parser_value = 0;
5000 const long long value_parse_status =
5001 UnserializeFloat(reader, read_pos, value_size, parser_value);
5003 *value = static_cast<float>(parser_value);
5005 if (value_parse_status < 0 || *value < 0.0 || *value > 1.0)
5008 *chromaticity = chromaticity_ptr.release();
5012 bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
5013 long long mm_size, MasteringMetadata** mm) {
5017 std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
5021 const long long mm_end = mm_start + mm_size;
5022 long long read_pos = mm_start;
5024 while (read_pos < mm_end) {
5025 long long child_id = 0;
5026 long long child_size = 0;
5028 const long long status =
5029 ParseElementHeader(reader, read_pos, mm_end, child_id, child_size);
5033 if (child_id == libwebm::kMkvLuminanceMax) {
5035 const long long value_parse_status =
5036 UnserializeFloat(reader, read_pos, child_size, value);
5037 mm_ptr->luminance_max = static_cast<float>(value);
5038 if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 ||
5039 mm_ptr->luminance_max > 9999.99) {
5042 } else if (child_id == libwebm::kMkvLuminanceMin) {
5044 const long long value_parse_status =
5045 UnserializeFloat(reader, read_pos, child_size, value);
5046 mm_ptr->luminance_min = static_cast<float>(value);
5047 if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 ||
5048 mm_ptr->luminance_min > 999.9999) {
5053 PrimaryChromaticity** chromaticity;
5055 case libwebm::kMkvPrimaryRChromaticityX:
5056 case libwebm::kMkvPrimaryRChromaticityY:
5057 is_x = child_id == libwebm::kMkvPrimaryRChromaticityX;
5058 chromaticity = &mm_ptr->r;
5060 case libwebm::kMkvPrimaryGChromaticityX:
5061 case libwebm::kMkvPrimaryGChromaticityY:
5062 is_x = child_id == libwebm::kMkvPrimaryGChromaticityX;
5063 chromaticity = &mm_ptr->g;
5065 case libwebm::kMkvPrimaryBChromaticityX:
5066 case libwebm::kMkvPrimaryBChromaticityY:
5067 is_x = child_id == libwebm::kMkvPrimaryBChromaticityX;
5068 chromaticity = &mm_ptr->b;
5070 case libwebm::kMkvWhitePointChromaticityX:
5071 case libwebm::kMkvWhitePointChromaticityY:
5072 is_x = child_id == libwebm::kMkvWhitePointChromaticityX;
5073 chromaticity = &mm_ptr->white_point;
5078 const bool value_parse_status = PrimaryChromaticity::Parse(
5079 reader, read_pos, child_size, is_x, chromaticity);
5080 if (!value_parse_status)
5084 read_pos += child_size;
5085 if (read_pos > mm_end)
5089 *mm = mm_ptr.release();
5093 bool Colour::Parse(IMkvReader* reader, long long colour_start,
5094 long long colour_size, Colour** colour) {
5095 if (!reader || *colour)
5098 std::auto_ptr<Colour> colour_ptr(new Colour());
5099 if (!colour_ptr.get())
5102 const long long colour_end = colour_start + colour_size;
5103 long long read_pos = colour_start;
5105 while (read_pos < colour_end) {
5106 long long child_id = 0;
5107 long long child_size = 0;
5110 ParseElementHeader(reader, read_pos, colour_end, child_id, child_size);
5114 if (child_id == libwebm::kMkvMatrixCoefficients) {
5115 colour_ptr->matrix_coefficients =
5116 UnserializeUInt(reader, read_pos, child_size);
5117 if (colour_ptr->matrix_coefficients < 0)
5119 } else if (child_id == libwebm::kMkvBitsPerChannel) {
5120 colour_ptr->bits_per_channel =
5121 UnserializeUInt(reader, read_pos, child_size);
5122 if (colour_ptr->bits_per_channel < 0)
5124 } else if (child_id == libwebm::kMkvChromaSubsamplingHorz) {
5125 colour_ptr->chroma_subsampling_horz =
5126 UnserializeUInt(reader, read_pos, child_size);
5127 if (colour_ptr->chroma_subsampling_horz < 0)
5129 } else if (child_id == libwebm::kMkvChromaSubsamplingVert) {
5130 colour_ptr->chroma_subsampling_vert =
5131 UnserializeUInt(reader, read_pos, child_size);
5132 if (colour_ptr->chroma_subsampling_vert < 0)
5134 } else if (child_id == libwebm::kMkvCbSubsamplingHorz) {
5135 colour_ptr->cb_subsampling_horz =
5136 UnserializeUInt(reader, read_pos, child_size);
5137 if (colour_ptr->cb_subsampling_horz < 0)
5139 } else if (child_id == libwebm::kMkvCbSubsamplingVert) {
5140 colour_ptr->cb_subsampling_vert =
5141 UnserializeUInt(reader, read_pos, child_size);
5142 if (colour_ptr->cb_subsampling_vert < 0)
5144 } else if (child_id == libwebm::kMkvChromaSitingHorz) {
5145 colour_ptr->chroma_siting_horz =
5146 UnserializeUInt(reader, read_pos, child_size);
5147 if (colour_ptr->chroma_siting_horz < 0)
5149 } else if (child_id == libwebm::kMkvChromaSitingVert) {
5150 colour_ptr->chroma_siting_vert =
5151 UnserializeUInt(reader, read_pos, child_size);
5152 if (colour_ptr->chroma_siting_vert < 0)
5154 } else if (child_id == libwebm::kMkvRange) {
5155 colour_ptr->range = UnserializeUInt(reader, read_pos, child_size);
5156 if (colour_ptr->range < 0)
5158 } else if (child_id == libwebm::kMkvTransferCharacteristics) {
5159 colour_ptr->transfer_characteristics =
5160 UnserializeUInt(reader, read_pos, child_size);
5161 if (colour_ptr->transfer_characteristics < 0)
5163 } else if (child_id == libwebm::kMkvPrimaries) {
5164 colour_ptr->primaries = UnserializeUInt(reader, read_pos, child_size);
5165 if (colour_ptr->primaries < 0)
5167 } else if (child_id == libwebm::kMkvMaxCLL) {
5168 colour_ptr->max_cll = UnserializeUInt(reader, read_pos, child_size);
5169 if (colour_ptr->max_cll < 0)
5171 } else if (child_id == libwebm::kMkvMaxFALL) {
5172 colour_ptr->max_fall = UnserializeUInt(reader, read_pos, child_size);
5173 if (colour_ptr->max_fall < 0)
5175 } else if (child_id == libwebm::kMkvMasteringMetadata) {
5176 if (!MasteringMetadata::Parse(reader, read_pos, child_size,
5177 &colour_ptr->mastering_metadata))
5183 read_pos += child_size;
5184 if (read_pos > colour_end)
5187 *colour = colour_ptr.release();
5191 VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
5192 long long element_size)
5193 : Track(pSegment, element_start, element_size), m_colour(NULL) {}
5195 VideoTrack::~VideoTrack() { delete m_colour; }
5197 long VideoTrack::Parse(Segment* pSegment, const Info& info,
5198 long long element_start, long long element_size,
5199 VideoTrack*& pResult) {
5203 if (info.type != Track::kVideo)
5206 long long width = 0;
5207 long long height = 0;
5208 long long display_width = 0;
5209 long long display_height = 0;
5210 long long display_unit = 0;
5211 long long stereo_mode = 0;
5215 IMkvReader* const pReader = pSegment->m_pReader;
5217 const Settings& s = info.settings;
5218 assert(s.start >= 0);
5219 assert(s.size >= 0);
5221 long long pos = s.start;
5224 const long long stop = pos + s.size;
5226 Colour* colour = NULL;
5228 while (pos < stop) {
5231 const long status = ParseElementHeader(pReader, pos, stop, id, size);
5233 if (status < 0) // error
5236 if (id == libwebm::kMkvPixelWidth) {
5237 width = UnserializeUInt(pReader, pos, size);
5240 return E_FILE_FORMAT_INVALID;
5241 } else if (id == libwebm::kMkvPixelHeight) {
5242 height = UnserializeUInt(pReader, pos, size);
5245 return E_FILE_FORMAT_INVALID;
5246 } else if (id == libwebm::kMkvDisplayWidth) {
5247 display_width = UnserializeUInt(pReader, pos, size);
5249 if (display_width <= 0)
5250 return E_FILE_FORMAT_INVALID;
5251 } else if (id == libwebm::kMkvDisplayHeight) {
5252 display_height = UnserializeUInt(pReader, pos, size);
5254 if (display_height <= 0)
5255 return E_FILE_FORMAT_INVALID;
5256 } else if (id == libwebm::kMkvDisplayUnit) {
5257 display_unit = UnserializeUInt(pReader, pos, size);
5259 if (display_unit < 0)
5260 return E_FILE_FORMAT_INVALID;
5261 } else if (id == libwebm::kMkvStereoMode) {
5262 stereo_mode = UnserializeUInt(pReader, pos, size);
5264 if (stereo_mode < 0)
5265 return E_FILE_FORMAT_INVALID;
5266 } else if (id == libwebm::kMkvFrameRate) {
5267 const long status = UnserializeFloat(pReader, pos, size, rate);
5273 return E_FILE_FORMAT_INVALID;
5274 } else if (id == libwebm::kMkvColour) {
5275 if (!Colour::Parse(pReader, pos, size, &colour))
5276 return E_FILE_FORMAT_INVALID;
5279 pos += size; // consume payload
5281 return E_FILE_FORMAT_INVALID;
5285 return E_FILE_FORMAT_INVALID;
5287 VideoTrack* const pTrack =
5288 new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
5291 return -1; // generic error
5293 const int status = info.Copy(pTrack->m_info);
5295 if (status) { // error
5300 pTrack->m_width = width;
5301 pTrack->m_height = height;
5302 pTrack->m_display_width = display_width;
5303 pTrack->m_display_height = display_height;
5304 pTrack->m_display_unit = display_unit;
5305 pTrack->m_stereo_mode = stereo_mode;
5306 pTrack->m_rate = rate;
5307 pTrack->m_colour = colour;
5310 return 0; // success
5313 bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
5314 return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
5317 long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
5318 const long status = GetFirst(pResult);
5320 if (status < 0) // buffer underflow, etc
5328 const Cluster* pCluster = pResult->GetCluster();
5330 assert(pCluster->GetIndex() >= 0);
5332 if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
5335 Cluster** const clusters = m_pSegment->m_clusters;
5338 const long count = m_pSegment->GetCount(); // loaded only, not pre-loaded
5341 Cluster** const i = clusters + pCluster->GetIndex();
5343 assert(*i == pCluster);
5344 assert(pCluster->GetTime() <= time_ns);
5346 Cluster** const j = clusters + count;
5353 //[i, lo) <= time_ns
5357 Cluster** const mid = lo + (hi - lo) / 2;
5362 assert(pCluster->GetIndex() >= 0);
5363 assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
5365 const long long t = pCluster->GetTime();
5381 assert(pCluster->GetTime() <= time_ns);
5383 pResult = pCluster->GetEntry(this, time_ns);
5385 if ((pResult != 0) && !pResult->EOS()) // found a keyframe
5391 assert(pCluster->GetTime() <= time_ns);
5393 pResult = pCluster->GetEntry(this, time_ns);
5395 if ((pResult != 0) && !pResult->EOS())
5399 // weird: we're on the first cluster, but no keyframe found
5400 // should never happen but we must return something anyway
5406 Colour* VideoTrack::GetColour() const { return m_colour; }
5408 long long VideoTrack::GetWidth() const { return m_width; }
5410 long long VideoTrack::GetHeight() const { return m_height; }
5412 long long VideoTrack::GetDisplayWidth() const {
5413 return m_display_width > 0 ? m_display_width : GetWidth();
5416 long long VideoTrack::GetDisplayHeight() const {
5417 return m_display_height > 0 ? m_display_height : GetHeight();
5420 long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
5422 long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
5424 double VideoTrack::GetFrameRate() const { return m_rate; }
5426 AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
5427 long long element_size)
5428 : Track(pSegment, element_start, element_size) {}
5430 long AudioTrack::Parse(Segment* pSegment, const Info& info,
5431 long long element_start, long long element_size,
5432 AudioTrack*& pResult) {
5436 if (info.type != Track::kAudio)
5439 IMkvReader* const pReader = pSegment->m_pReader;
5441 const Settings& s = info.settings;
5442 assert(s.start >= 0);
5443 assert(s.size >= 0);
5445 long long pos = s.start;
5448 const long long stop = pos + s.size;
5450 double rate = 8000.0; // MKV default
5451 long long channels = 1;
5452 long long bit_depth = 0;
5454 while (pos < stop) {
5457 long status = ParseElementHeader(pReader, pos, stop, id, size);
5459 if (status < 0) // error
5462 if (id == libwebm::kMkvSamplingFrequency) {
5463 status = UnserializeFloat(pReader, pos, size, rate);
5469 return E_FILE_FORMAT_INVALID;
5470 } else if (id == libwebm::kMkvChannels) {
5471 channels = UnserializeUInt(pReader, pos, size);
5474 return E_FILE_FORMAT_INVALID;
5475 } else if (id == libwebm::kMkvBitDepth) {
5476 bit_depth = UnserializeUInt(pReader, pos, size);
5479 return E_FILE_FORMAT_INVALID;
5482 pos += size; // consume payload
5484 return E_FILE_FORMAT_INVALID;
5488 return E_FILE_FORMAT_INVALID;
5490 AudioTrack* const pTrack =
5491 new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
5494 return -1; // generic error
5496 const int status = info.Copy(pTrack->m_info);
5503 pTrack->m_rate = rate;
5504 pTrack->m_channels = channels;
5505 pTrack->m_bitDepth = bit_depth;
5508 return 0; // success
5511 double AudioTrack::GetSamplingRate() const { return m_rate; }
5513 long long AudioTrack::GetChannels() const { return m_channels; }
5515 long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
5517 Tracks::Tracks(Segment* pSegment, long long start, long long size_,
5518 long long element_start, long long element_size)
5519 : m_pSegment(pSegment),
5522 m_element_start(element_start),
5523 m_element_size(element_size),
5524 m_trackEntries(NULL),
5525 m_trackEntriesEnd(NULL) {}
5527 long Tracks::Parse() {
5528 assert(m_trackEntries == NULL);
5529 assert(m_trackEntriesEnd == NULL);
5531 const long long stop = m_start + m_size;
5532 IMkvReader* const pReader = m_pSegment->m_pReader;
5535 long long pos = m_start;
5537 while (pos < stop) {
5540 const long status = ParseElementHeader(pReader, pos, stop, id, size);
5542 if (status < 0) // error
5545 if (size == 0) // weird
5548 if (id == libwebm::kMkvTrackEntry)
5551 pos += size; // consume payload
5553 return E_FILE_FORMAT_INVALID;
5557 return E_FILE_FORMAT_INVALID;
5560 return 0; // success
5562 m_trackEntries = new (std::nothrow) Track*[count];
5564 if (m_trackEntries == NULL)
5567 m_trackEntriesEnd = m_trackEntries;
5571 while (pos < stop) {
5572 const long long element_start = pos;
5574 long long id, payload_size;
5577 ParseElementHeader(pReader, pos, stop, id, payload_size);
5579 if (status < 0) // error
5582 if (payload_size == 0) // weird
5585 const long long payload_stop = pos + payload_size;
5586 assert(payload_stop <= stop); // checked in ParseElement
5588 const long long element_size = payload_stop - element_start;
5590 if (id == libwebm::kMkvTrackEntry) {
5591 Track*& pTrack = *m_trackEntriesEnd;
5594 const long status = ParseTrackEntry(pos, payload_size, element_start,
5595 element_size, pTrack);
5600 ++m_trackEntriesEnd;
5605 return E_FILE_FORMAT_INVALID;
5609 return E_FILE_FORMAT_INVALID;
5611 return 0; // success
5614 unsigned long Tracks::GetTracksCount() const {
5615 const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
5616 assert(result >= 0);
5618 return static_cast<unsigned long>(result);
5621 long Tracks::ParseTrackEntry(long long track_start, long long track_size,
5622 long long element_start, long long element_size,
5623 Track*& pResult) const {
5627 IMkvReader* const pReader = m_pSegment->m_pReader;
5629 long long pos = track_start;
5630 const long long track_stop = track_start + track_size;
5637 info.defaultDuration = 0;
5647 Track::Settings e; // content_encodings_settings;
5651 long long lacing = 1; // default is true
5653 while (pos < track_stop) {
5656 const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
5658 if (status < 0) // error
5662 return E_FILE_FORMAT_INVALID;
5664 const long long start = pos;
5666 if (id == libwebm::kMkvVideo) {
5669 } else if (id == libwebm::kMkvAudio) {
5672 } else if (id == libwebm::kMkvContentEncodings) {
5675 } else if (id == libwebm::kMkvTrackUID) {
5677 return E_FILE_FORMAT_INVALID;
5681 long long pos_ = start;
5682 const long long pos_end = start + size;
5684 while (pos_ != pos_end) {
5687 const int status = pReader->Read(pos_, 1, &b);
5697 } else if (id == libwebm::kMkvTrackNumber) {
5698 const long long num = UnserializeUInt(pReader, pos, size);
5700 if ((num <= 0) || (num > 127))
5701 return E_FILE_FORMAT_INVALID;
5703 info.number = static_cast<long>(num);
5704 } else if (id == libwebm::kMkvTrackType) {
5705 const long long type = UnserializeUInt(pReader, pos, size);
5707 if ((type <= 0) || (type > 254))
5708 return E_FILE_FORMAT_INVALID;
5710 info.type = static_cast<long>(type);
5711 } else if (id == libwebm::kMkvName) {
5713 UnserializeString(pReader, pos, size, info.nameAsUTF8);
5717 } else if (id == libwebm::kMkvLanguage) {
5718 const long status = UnserializeString(pReader, pos, size, info.language);
5722 } else if (id == libwebm::kMkvDefaultDuration) {
5723 const long long duration = UnserializeUInt(pReader, pos, size);
5726 return E_FILE_FORMAT_INVALID;
5728 info.defaultDuration = static_cast<unsigned long long>(duration);
5729 } else if (id == libwebm::kMkvCodecID) {
5730 const long status = UnserializeString(pReader, pos, size, info.codecId);
5734 } else if (id == libwebm::kMkvFlagLacing) {
5735 lacing = UnserializeUInt(pReader, pos, size);
5737 if ((lacing < 0) || (lacing > 1))
5738 return E_FILE_FORMAT_INVALID;
5739 } else if (id == libwebm::kMkvCodecPrivate) {
5740 delete[] info.codecPrivate;
5741 info.codecPrivate = NULL;
5742 info.codecPrivateSize = 0;
5744 const size_t buflen = static_cast<size_t>(size);
5747 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
5752 const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
5759 info.codecPrivate = buf;
5760 info.codecPrivateSize = buflen;
5762 } else if (id == libwebm::kMkvCodecName) {
5764 UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
5768 } else if (id == libwebm::kMkvCodecDelay) {
5769 info.codecDelay = UnserializeUInt(pReader, pos, size);
5770 } else if (id == libwebm::kMkvSeekPreRoll) {
5771 info.seekPreRoll = UnserializeUInt(pReader, pos, size);
5774 pos += size; // consume payload
5775 if (pos > track_stop)
5776 return E_FILE_FORMAT_INVALID;
5779 if (pos != track_stop)
5780 return E_FILE_FORMAT_INVALID;
5782 if (info.number <= 0) // not specified
5783 return E_FILE_FORMAT_INVALID;
5785 if (GetTrackByNumber(info.number))
5786 return E_FILE_FORMAT_INVALID;
5788 if (info.type <= 0) // not specified
5789 return E_FILE_FORMAT_INVALID;
5791 info.lacing = (lacing > 0) ? true : false;
5793 if (info.type == Track::kVideo) {
5795 return E_FILE_FORMAT_INVALID;
5798 return E_FILE_FORMAT_INVALID;
5802 VideoTrack* pTrack = NULL;
5804 const long status = VideoTrack::Parse(m_pSegment, info, element_start,
5805 element_size, pTrack);
5814 pResult->ParseContentEncodingsEntry(e.start, e.size);
5815 } else if (info.type == Track::kAudio) {
5817 return E_FILE_FORMAT_INVALID;
5820 return E_FILE_FORMAT_INVALID;
5824 AudioTrack* pTrack = NULL;
5826 const long status = AudioTrack::Parse(m_pSegment, info, element_start,
5827 element_size, pTrack);
5836 pResult->ParseContentEncodingsEntry(e.start, e.size);
5838 // neither video nor audio - probably metadata or subtitles
5841 return E_FILE_FORMAT_INVALID;
5844 return E_FILE_FORMAT_INVALID;
5846 if (info.type == Track::kMetadata && e.start >= 0)
5847 return E_FILE_FORMAT_INVALID;
5849 info.settings.start = -1;
5850 info.settings.size = 0;
5852 Track* pTrack = NULL;
5855 Track::Create(m_pSegment, info, element_start, element_size, pTrack);
5864 return 0; // success
5868 Track** i = m_trackEntries;
5869 Track** const j = m_trackEntriesEnd;
5872 Track* const pTrack = *i++;
5876 delete[] m_trackEntries;
5879 const Track* Tracks::GetTrackByNumber(long tn) const {
5883 Track** i = m_trackEntries;
5884 Track** const j = m_trackEntriesEnd;
5887 Track* const pTrack = *i++;
5892 if (tn == pTrack->GetNumber())
5896 return NULL; // not found
5899 const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
5900 const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
5902 if (idx >= static_cast<unsigned long>(count))
5905 return m_trackEntries[idx];
5908 long Cluster::Load(long long& pos, long& len) const {
5909 if (m_pSegment == NULL)
5910 return E_PARSE_FAILED;
5912 if (m_timecode >= 0) // at least partially loaded
5915 if (m_pos != m_element_start || m_element_size >= 0)
5916 return E_PARSE_FAILED;
5918 IMkvReader* const pReader = m_pSegment->m_pReader;
5919 long long total, avail;
5920 const int status = pReader->Length(&total, &avail);
5922 if (status < 0) // error
5925 if (total >= 0 && (avail > total || m_pos > total))
5926 return E_FILE_FORMAT_INVALID;
5930 long long cluster_size = -1;
5932 if ((pos + 1) > avail) {
5934 return E_BUFFER_NOT_FULL;
5937 long long result = GetUIntLength(pReader, pos, len);
5939 if (result < 0) // error or underflow
5940 return static_cast<long>(result);
5943 return E_BUFFER_NOT_FULL;
5945 if ((pos + len) > avail)
5946 return E_BUFFER_NOT_FULL;
5948 const long long id_ = ReadID(pReader, pos, len);
5950 if (id_ < 0) // error
5951 return static_cast<long>(id_);
5953 if (id_ != libwebm::kMkvCluster)
5954 return E_FILE_FORMAT_INVALID;
5956 pos += len; // consume id
5958 // read cluster size
5960 if ((pos + 1) > avail) {
5962 return E_BUFFER_NOT_FULL;
5965 result = GetUIntLength(pReader, pos, len);
5967 if (result < 0) // error
5968 return static_cast<long>(result);
5971 return E_BUFFER_NOT_FULL;
5973 if ((pos + len) > avail)
5974 return E_BUFFER_NOT_FULL;
5976 const long long size = ReadUInt(pReader, pos, len);
5978 if (size < 0) // error
5979 return static_cast<long>(cluster_size);
5982 return E_FILE_FORMAT_INVALID;
5984 pos += len; // consume length of size of element
5986 const long long unknown_size = (1LL << (7 * len)) - 1;
5988 if (size != unknown_size)
5989 cluster_size = size;
5991 // pos points to start of payload
5992 long long timecode = -1;
5993 long long new_pos = -1;
5994 bool bBlock = false;
5996 long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
5999 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6004 if ((pos + 1) > avail) {
6006 return E_BUFFER_NOT_FULL;
6009 long long result = GetUIntLength(pReader, pos, len);
6011 if (result < 0) // error
6012 return static_cast<long>(result);
6015 return E_BUFFER_NOT_FULL;
6017 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6018 return E_FILE_FORMAT_INVALID;
6020 if ((pos + len) > avail)
6021 return E_BUFFER_NOT_FULL;
6023 const long long id = ReadID(pReader, pos, len);
6025 if (id < 0) // error
6026 return static_cast<long>(id);
6029 return E_FILE_FORMAT_INVALID;
6031 // This is the distinguished set of ID's we use to determine
6032 // that we have exhausted the sub-element's inside the cluster
6033 // whose ID we parsed earlier.
6035 if (id == libwebm::kMkvCluster)
6038 if (id == libwebm::kMkvCues)
6041 pos += len; // consume ID field
6045 if ((pos + 1) > avail) {
6047 return E_BUFFER_NOT_FULL;
6050 result = GetUIntLength(pReader, pos, len);
6052 if (result < 0) // error
6053 return static_cast<long>(result);
6056 return E_BUFFER_NOT_FULL;
6058 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6059 return E_FILE_FORMAT_INVALID;
6061 if ((pos + len) > avail)
6062 return E_BUFFER_NOT_FULL;
6064 const long long size = ReadUInt(pReader, pos, len);
6066 if (size < 0) // error
6067 return static_cast<long>(size);
6069 const long long unknown_size = (1LL << (7 * len)) - 1;
6071 if (size == unknown_size)
6072 return E_FILE_FORMAT_INVALID;
6074 pos += len; // consume size field
6076 if ((cluster_stop >= 0) && (pos > cluster_stop))
6077 return E_FILE_FORMAT_INVALID;
6079 // pos now points to start of payload
6084 if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6085 return E_FILE_FORMAT_INVALID;
6087 if (id == libwebm::kMkvTimecode) {
6088 len = static_cast<long>(size);
6090 if ((pos + size) > avail)
6091 return E_BUFFER_NOT_FULL;
6093 timecode = UnserializeUInt(pReader, pos, size);
6095 if (timecode < 0) // error (or underflow)
6096 return static_cast<long>(timecode);
6098 new_pos = pos + size;
6102 } else if (id == libwebm::kMkvBlockGroup) {
6105 } else if (id == libwebm::kMkvSimpleBlock) {
6110 pos += size; // consume payload
6111 if (cluster_stop >= 0 && pos > cluster_stop)
6112 return E_FILE_FORMAT_INVALID;
6115 if (cluster_stop >= 0 && pos > cluster_stop)
6116 return E_FILE_FORMAT_INVALID;
6118 if (timecode < 0) // no timecode found
6119 return E_FILE_FORMAT_INVALID;
6122 return E_FILE_FORMAT_INVALID;
6124 m_pos = new_pos; // designates position just beyond timecode payload
6125 m_timecode = timecode; // m_timecode >= 0 means we're partially loaded
6127 if (cluster_size >= 0)
6128 m_element_size = cluster_stop - m_element_start;
6133 long Cluster::Parse(long long& pos, long& len) const {
6134 long status = Load(pos, len);
6139 if (m_pos < m_element_start || m_timecode < 0)
6140 return E_PARSE_FAILED;
6142 const long long cluster_stop =
6143 (m_element_size < 0) ? -1 : m_element_start + m_element_size;
6145 if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
6146 return 1; // nothing else to do
6148 IMkvReader* const pReader = m_pSegment->m_pReader;
6150 long long total, avail;
6152 status = pReader->Length(&total, &avail);
6154 if (status < 0) // error
6157 if (total >= 0 && avail > total)
6158 return E_FILE_FORMAT_INVALID;
6163 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6166 if ((total >= 0) && (pos >= total)) {
6167 if (m_element_size < 0)
6168 m_element_size = pos - m_element_start;
6175 if ((pos + 1) > avail) {
6177 return E_BUFFER_NOT_FULL;
6180 long long result = GetUIntLength(pReader, pos, len);
6182 if (result < 0) // error
6183 return static_cast<long>(result);
6186 return E_BUFFER_NOT_FULL;
6188 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6189 return E_FILE_FORMAT_INVALID;
6191 if ((pos + len) > avail)
6192 return E_BUFFER_NOT_FULL;
6194 const long long id = ReadID(pReader, pos, len);
6197 return E_FILE_FORMAT_INVALID;
6199 // This is the distinguished set of ID's we use to determine
6200 // that we have exhausted the sub-element's inside the cluster
6201 // whose ID we parsed earlier.
6203 if ((id == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) {
6204 if (m_element_size < 0)
6205 m_element_size = pos - m_element_start;
6210 pos += len; // consume ID field
6214 if ((pos + 1) > avail) {
6216 return E_BUFFER_NOT_FULL;
6219 result = GetUIntLength(pReader, pos, len);
6221 if (result < 0) // error
6222 return static_cast<long>(result);
6225 return E_BUFFER_NOT_FULL;
6227 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6228 return E_FILE_FORMAT_INVALID;
6230 if ((pos + len) > avail)
6231 return E_BUFFER_NOT_FULL;
6233 const long long size = ReadUInt(pReader, pos, len);
6235 if (size < 0) // error
6236 return static_cast<long>(size);
6238 const long long unknown_size = (1LL << (7 * len)) - 1;
6240 if (size == unknown_size)
6241 return E_FILE_FORMAT_INVALID;
6243 pos += len; // consume size field
6245 if ((cluster_stop >= 0) && (pos > cluster_stop))
6246 return E_FILE_FORMAT_INVALID;
6248 // pos now points to start of payload
6253 // const long long block_start = pos;
6254 const long long block_stop = pos + size;
6256 if (cluster_stop >= 0) {
6257 if (block_stop > cluster_stop) {
6258 if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) {
6259 return E_FILE_FORMAT_INVALID;
6265 } else if ((total >= 0) && (block_stop > total)) {
6266 m_element_size = total - m_element_start;
6269 } else if (block_stop > avail) {
6270 len = static_cast<long>(size);
6271 return E_BUFFER_NOT_FULL;
6274 Cluster* const this_ = const_cast<Cluster*>(this);
6276 if (id == libwebm::kMkvBlockGroup)
6277 return this_->ParseBlockGroup(size, pos, len);
6279 if (id == libwebm::kMkvSimpleBlock)
6280 return this_->ParseSimpleBlock(size, pos, len);
6282 pos += size; // consume payload
6283 if (cluster_stop >= 0 && pos > cluster_stop)
6284 return E_FILE_FORMAT_INVALID;
6287 if (m_element_size < 1)
6288 return E_FILE_FORMAT_INVALID;
6291 if (cluster_stop >= 0 && m_pos > cluster_stop)
6292 return E_FILE_FORMAT_INVALID;
6294 if (m_entries_count > 0) {
6295 const long idx = m_entries_count - 1;
6297 const BlockEntry* const pLast = m_entries[idx];
6299 return E_PARSE_FAILED;
6301 const Block* const pBlock = pLast->GetBlock();
6303 return E_PARSE_FAILED;
6305 const long long start = pBlock->m_start;
6307 if ((total >= 0) && (start > total))
6308 return E_PARSE_FAILED; // defend against trucated stream
6310 const long long size = pBlock->m_size;
6312 const long long stop = start + size;
6313 if (cluster_stop >= 0 && stop > cluster_stop)
6314 return E_FILE_FORMAT_INVALID;
6316 if ((total >= 0) && (stop > total))
6317 return E_PARSE_FAILED; // defend against trucated stream
6320 return 1; // no more entries
6323 long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
6325 const long long block_start = pos;
6326 const long long block_stop = pos + block_size;
6328 IMkvReader* const pReader = m_pSegment->m_pReader;
6330 long long total, avail;
6332 long status = pReader->Length(&total, &avail);
6334 if (status < 0) // error
6337 assert((total < 0) || (avail <= total));
6339 // parse track number
6341 if ((pos + 1) > avail) {
6343 return E_BUFFER_NOT_FULL;
6346 long long result = GetUIntLength(pReader, pos, len);
6348 if (result < 0) // error
6349 return static_cast<long>(result);
6351 if (result > 0) // weird
6352 return E_BUFFER_NOT_FULL;
6354 if ((pos + len) > block_stop)
6355 return E_FILE_FORMAT_INVALID;
6357 if ((pos + len) > avail)
6358 return E_BUFFER_NOT_FULL;
6360 const long long track = ReadUInt(pReader, pos, len);
6362 if (track < 0) // error
6363 return static_cast<long>(track);
6366 return E_FILE_FORMAT_INVALID;
6368 pos += len; // consume track number
6370 if ((pos + 2) > block_stop)
6371 return E_FILE_FORMAT_INVALID;
6373 if ((pos + 2) > avail) {
6375 return E_BUFFER_NOT_FULL;
6378 pos += 2; // consume timecode
6380 if ((pos + 1) > block_stop)
6381 return E_FILE_FORMAT_INVALID;
6383 if ((pos + 1) > avail) {
6385 return E_BUFFER_NOT_FULL;
6388 unsigned char flags;
6390 status = pReader->Read(pos, 1, &flags);
6392 if (status < 0) { // error or underflow
6397 ++pos; // consume flags byte
6398 assert(pos <= avail);
6400 if (pos >= block_stop)
6401 return E_FILE_FORMAT_INVALID;
6403 const int lacing = int(flags & 0x06) >> 1;
6405 if ((lacing != 0) && (block_stop > avail)) {
6406 len = static_cast<long>(block_stop - pos);
6407 return E_BUFFER_NOT_FULL;
6410 status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size,
6411 0); // DiscardPadding
6418 return 0; // success
6421 long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
6423 const long long payload_start = pos;
6424 const long long payload_stop = pos + payload_size;
6426 IMkvReader* const pReader = m_pSegment->m_pReader;
6428 long long total, avail;
6430 long status = pReader->Length(&total, &avail);
6432 if (status < 0) // error
6435 assert((total < 0) || (avail <= total));
6437 if ((total >= 0) && (payload_stop > total))
6438 return E_FILE_FORMAT_INVALID;
6440 if (payload_stop > avail) {
6441 len = static_cast<long>(payload_size);
6442 return E_BUFFER_NOT_FULL;
6445 long long discard_padding = 0;
6447 while (pos < payload_stop) {
6448 // parse sub-block element ID
6450 if ((pos + 1) > avail) {
6452 return E_BUFFER_NOT_FULL;
6455 long long result = GetUIntLength(pReader, pos, len);
6457 if (result < 0) // error
6458 return static_cast<long>(result);
6460 if (result > 0) // weird
6461 return E_BUFFER_NOT_FULL;
6463 if ((pos + len) > payload_stop)
6464 return E_FILE_FORMAT_INVALID;
6466 if ((pos + len) > avail)
6467 return E_BUFFER_NOT_FULL;
6469 const long long id = ReadID(pReader, pos, len);
6471 if (id < 0) // error
6472 return static_cast<long>(id);
6474 if (id == 0) // not a valid ID
6475 return E_FILE_FORMAT_INVALID;
6477 pos += len; // consume ID field
6481 if ((pos + 1) > avail) {
6483 return E_BUFFER_NOT_FULL;
6486 result = GetUIntLength(pReader, pos, len);
6488 if (result < 0) // error
6489 return static_cast<long>(result);
6491 if (result > 0) // weird
6492 return E_BUFFER_NOT_FULL;
6494 if ((pos + len) > payload_stop)
6495 return E_FILE_FORMAT_INVALID;
6497 if ((pos + len) > avail)
6498 return E_BUFFER_NOT_FULL;
6500 const long long size = ReadUInt(pReader, pos, len);
6502 if (size < 0) // error
6503 return static_cast<long>(size);
6505 pos += len; // consume size field
6507 // pos now points to start of sub-block group payload
6509 if (pos > payload_stop)
6510 return E_FILE_FORMAT_INVALID;
6512 if (size == 0) // weird
6515 const long long unknown_size = (1LL << (7 * len)) - 1;
6517 if (size == unknown_size)
6518 return E_FILE_FORMAT_INVALID;
6520 if (id == libwebm::kMkvDiscardPadding) {
6521 status = UnserializeInt(pReader, pos, size, discard_padding);
6523 if (status < 0) // error
6527 if (id != libwebm::kMkvBlock) {
6528 pos += size; // consume sub-part of block group
6530 if (pos > payload_stop)
6531 return E_FILE_FORMAT_INVALID;
6536 const long long block_stop = pos + size;
6538 if (block_stop > payload_stop)
6539 return E_FILE_FORMAT_INVALID;
6541 // parse track number
6543 if ((pos + 1) > avail) {
6545 return E_BUFFER_NOT_FULL;
6548 result = GetUIntLength(pReader, pos, len);
6550 if (result < 0) // error
6551 return static_cast<long>(result);
6553 if (result > 0) // weird
6554 return E_BUFFER_NOT_FULL;
6556 if ((pos + len) > block_stop)
6557 return E_FILE_FORMAT_INVALID;
6559 if ((pos + len) > avail)
6560 return E_BUFFER_NOT_FULL;
6562 const long long track = ReadUInt(pReader, pos, len);
6564 if (track < 0) // error
6565 return static_cast<long>(track);
6568 return E_FILE_FORMAT_INVALID;
6570 pos += len; // consume track number
6572 if ((pos + 2) > block_stop)
6573 return E_FILE_FORMAT_INVALID;
6575 if ((pos + 2) > avail) {
6577 return E_BUFFER_NOT_FULL;
6580 pos += 2; // consume timecode
6582 if ((pos + 1) > block_stop)
6583 return E_FILE_FORMAT_INVALID;
6585 if ((pos + 1) > avail) {
6587 return E_BUFFER_NOT_FULL;
6590 unsigned char flags;
6592 status = pReader->Read(pos, 1, &flags);
6594 if (status < 0) { // error or underflow
6599 ++pos; // consume flags byte
6600 assert(pos <= avail);
6602 if (pos >= block_stop)
6603 return E_FILE_FORMAT_INVALID;
6605 const int lacing = int(flags & 0x06) >> 1;
6607 if ((lacing != 0) && (block_stop > avail)) {
6608 len = static_cast<long>(block_stop - pos);
6609 return E_BUFFER_NOT_FULL;
6612 pos = block_stop; // consume block-part of block group
6613 if (pos > payload_stop)
6614 return E_FILE_FORMAT_INVALID;
6617 if (pos != payload_stop)
6618 return E_FILE_FORMAT_INVALID;
6620 status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size,
6625 m_pos = payload_stop;
6627 return 0; // success
6630 long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
6631 assert(m_pos >= m_element_start);
6636 return -1; // generic error
6638 if (m_entries_count < 0)
6639 return E_BUFFER_NOT_FULL;
6642 assert(m_entries_size > 0);
6643 assert(m_entries_count <= m_entries_size);
6645 if (index < m_entries_count) {
6646 pEntry = m_entries[index];
6649 return 1; // found entry
6652 if (m_element_size < 0) // we don't know cluster end yet
6653 return E_BUFFER_NOT_FULL; // underflow
6655 const long long element_stop = m_element_start + m_element_size;
6657 if (m_pos >= element_stop)
6658 return 0; // nothing left to parse
6660 return E_BUFFER_NOT_FULL; // underflow, since more remains to be parsed
6663 Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
6664 if (!pSegment || off < 0)
6667 const long long element_start = pSegment->m_start + off;
6669 Cluster* const pCluster =
6670 new (std::nothrow) Cluster(pSegment, idx, element_start);
6684 m_entries_count(0) // means "no entries"
6687 Cluster::Cluster(Segment* pSegment, long idx, long long element_start
6688 /* long long element_size */)
6689 : m_pSegment(pSegment),
6690 m_element_start(element_start),
6692 m_pos(element_start),
6693 m_element_size(-1 /* element_size */),
6697 m_entries_count(-1) // means "has not been parsed yet"
6700 Cluster::~Cluster() {
6701 if (m_entries_count <= 0)
6704 BlockEntry** i = m_entries;
6705 BlockEntry** const j = m_entries + m_entries_count;
6708 BlockEntry* p = *i++;
6717 bool Cluster::EOS() const { return (m_pSegment == NULL); }
6719 long Cluster::GetIndex() const { return m_index; }
6721 long long Cluster::GetPosition() const {
6722 const long long pos = m_element_start - m_pSegment->m_start;
6728 long long Cluster::GetElementSize() const { return m_element_size; }
6730 long Cluster::HasBlockEntries(
6731 const Segment* pSegment,
6732 long long off, // relative to start of segment payload
6733 long long& pos, long& len) {
6735 assert(off >= 0); // relative to segment
6737 IMkvReader* const pReader = pSegment->m_pReader;
6739 long long total, avail;
6741 long status = pReader->Length(&total, &avail);
6743 if (status < 0) // error
6746 assert((total < 0) || (avail <= total));
6748 pos = pSegment->m_start + off; // absolute
6750 if ((total >= 0) && (pos >= total))
6751 return 0; // we don't even have a complete cluster
6753 const long long segment_stop =
6754 (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
6756 long long cluster_stop = -1; // interpreted later to mean "unknown size"
6759 if ((pos + 1) > avail) {
6761 return E_BUFFER_NOT_FULL;
6764 long long result = GetUIntLength(pReader, pos, len);
6766 if (result < 0) // error
6767 return static_cast<long>(result);
6769 if (result > 0) // need more data
6770 return E_BUFFER_NOT_FULL;
6772 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6773 return E_FILE_FORMAT_INVALID;
6775 if ((total >= 0) && ((pos + len) > total))
6778 if ((pos + len) > avail)
6779 return E_BUFFER_NOT_FULL;
6781 const long long id = ReadID(pReader, pos, len);
6783 if (id < 0) // error
6784 return static_cast<long>(id);
6786 if (id != libwebm::kMkvCluster)
6787 return E_PARSE_FAILED;
6789 pos += len; // consume Cluster ID field
6793 if ((pos + 1) > avail) {
6795 return E_BUFFER_NOT_FULL;
6798 result = GetUIntLength(pReader, pos, len);
6800 if (result < 0) // error
6801 return static_cast<long>(result);
6803 if (result > 0) // weird
6804 return E_BUFFER_NOT_FULL;
6806 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6807 return E_FILE_FORMAT_INVALID;
6809 if ((total >= 0) && ((pos + len) > total))
6812 if ((pos + len) > avail)
6813 return E_BUFFER_NOT_FULL;
6815 const long long size = ReadUInt(pReader, pos, len);
6817 if (size < 0) // error
6818 return static_cast<long>(size);
6821 return 0; // cluster does not have entries
6823 pos += len; // consume size field
6825 // pos now points to start of payload
6827 const long long unknown_size = (1LL << (7 * len)) - 1;
6829 if (size != unknown_size) {
6830 cluster_stop = pos + size;
6831 assert(cluster_stop >= 0);
6833 if ((segment_stop >= 0) && (cluster_stop > segment_stop))
6834 return E_FILE_FORMAT_INVALID;
6836 if ((total >= 0) && (cluster_stop > total))
6837 // return E_FILE_FORMAT_INVALID; //too conservative
6838 return 0; // cluster does not have any entries
6843 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6844 return 0; // no entries detected
6846 if ((pos + 1) > avail) {
6848 return E_BUFFER_NOT_FULL;
6851 long long result = GetUIntLength(pReader, pos, len);
6853 if (result < 0) // error
6854 return static_cast<long>(result);
6856 if (result > 0) // need more data
6857 return E_BUFFER_NOT_FULL;
6859 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6860 return E_FILE_FORMAT_INVALID;
6862 if ((pos + len) > avail)
6863 return E_BUFFER_NOT_FULL;
6865 const long long id = ReadID(pReader, pos, len);
6867 if (id < 0) // error
6868 return static_cast<long>(id);
6870 // This is the distinguished set of ID's we use to determine
6871 // that we have exhausted the sub-element's inside the cluster
6872 // whose ID we parsed earlier.
6874 if (id == libwebm::kMkvCluster)
6875 return 0; // no entries found
6877 if (id == libwebm::kMkvCues)
6878 return 0; // no entries found
6880 pos += len; // consume id field
6882 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6883 return E_FILE_FORMAT_INVALID;
6887 if ((pos + 1) > avail) {
6889 return E_BUFFER_NOT_FULL;
6892 result = GetUIntLength(pReader, pos, len);
6894 if (result < 0) // error
6895 return static_cast<long>(result);
6897 if (result > 0) // underflow
6898 return E_BUFFER_NOT_FULL;
6900 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6901 return E_FILE_FORMAT_INVALID;
6903 if ((pos + len) > avail)
6904 return E_BUFFER_NOT_FULL;
6906 const long long size = ReadUInt(pReader, pos, len);
6908 if (size < 0) // error
6909 return static_cast<long>(size);
6911 pos += len; // consume size field
6913 // pos now points to start of payload
6915 if ((cluster_stop >= 0) && (pos > cluster_stop))
6916 return E_FILE_FORMAT_INVALID;
6918 if (size == 0) // weird
6921 const long long unknown_size = (1LL << (7 * len)) - 1;
6923 if (size == unknown_size)
6924 return E_FILE_FORMAT_INVALID; // not supported inside cluster
6926 if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6927 return E_FILE_FORMAT_INVALID;
6929 if (id == libwebm::kMkvBlockGroup)
6930 return 1; // have at least one entry
6932 if (id == libwebm::kMkvSimpleBlock)
6933 return 1; // have at least one entry
6935 pos += size; // consume payload
6936 if (cluster_stop >= 0 && pos > cluster_stop)
6937 return E_FILE_FORMAT_INVALID;
6941 long long Cluster::GetTimeCode() const {
6945 const long status = Load(pos, len);
6947 if (status < 0) // error
6953 long long Cluster::GetTime() const {
6954 const long long tc = GetTimeCode();
6959 const SegmentInfo* const pInfo = m_pSegment->GetInfo();
6962 const long long scale = pInfo->GetTimeCodeScale();
6965 const long long t = m_timecode * scale;
6970 long long Cluster::GetFirstTime() const {
6971 const BlockEntry* pEntry;
6973 const long status = GetFirst(pEntry);
6975 if (status < 0) // error
6978 if (pEntry == NULL) // empty cluster
6981 const Block* const pBlock = pEntry->GetBlock();
6984 return pBlock->GetTime(this);
6987 long long Cluster::GetLastTime() const {
6988 const BlockEntry* pEntry;
6990 const long status = GetLast(pEntry);
6992 if (status < 0) // error
6995 if (pEntry == NULL) // empty cluster
6998 const Block* const pBlock = pEntry->GetBlock();
7001 return pBlock->GetTime(this);
7004 long Cluster::CreateBlock(long long id,
7005 long long pos, // absolute pos of payload
7006 long long size, long long discard_padding) {
7007 if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock)
7008 return E_PARSE_FAILED;
7010 if (m_entries_count < 0) { // haven't parsed anything yet
7011 assert(m_entries == NULL);
7012 assert(m_entries_size == 0);
7014 m_entries_size = 1024;
7015 m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
7016 if (m_entries == NULL)
7019 m_entries_count = 0;
7022 assert(m_entries_size > 0);
7023 assert(m_entries_count <= m_entries_size);
7025 if (m_entries_count >= m_entries_size) {
7026 const long entries_size = 2 * m_entries_size;
7028 BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
7029 if (entries == NULL)
7032 BlockEntry** src = m_entries;
7033 BlockEntry** const src_end = src + m_entries_count;
7035 BlockEntry** dst = entries;
7037 while (src != src_end)
7042 m_entries = entries;
7043 m_entries_size = entries_size;
7047 if (id == libwebm::kMkvBlockGroup)
7048 return CreateBlockGroup(pos, size, discard_padding);
7050 return CreateSimpleBlock(pos, size);
7053 long Cluster::CreateBlockGroup(long long start_offset, long long size,
7054 long long discard_padding) {
7056 assert(m_entries_size > 0);
7057 assert(m_entries_count >= 0);
7058 assert(m_entries_count < m_entries_size);
7060 IMkvReader* const pReader = m_pSegment->m_pReader;
7062 long long pos = start_offset;
7063 const long long stop = start_offset + size;
7065 // For WebM files, there is a bias towards previous reference times
7066 //(in order to support alt-ref frames, which refer back to the previous
7067 // keyframe). Normally a 0 value is not possible, but here we tenatively
7068 // allow 0 as the value of a reference frame, with the interpretation
7069 // that this is a "previous" reference time.
7071 long long prev = 1; // nonce
7072 long long next = 0; // nonce
7073 long long duration = -1; // really, this is unsigned
7075 long long bpos = -1;
7076 long long bsize = -1;
7078 while (pos < stop) {
7080 const long long id = ReadID(pReader, pos, len);
7081 if (id < 0 || (pos + len) > stop)
7082 return E_FILE_FORMAT_INVALID;
7084 pos += len; // consume ID
7086 const long long size = ReadUInt(pReader, pos, len);
7087 assert(size >= 0); // TODO
7088 assert((pos + len) <= stop);
7090 pos += len; // consume size
7092 if (id == libwebm::kMkvBlock) {
7093 if (bpos < 0) { // Block ID
7097 } else if (id == libwebm::kMkvBlockDuration) {
7099 return E_FILE_FORMAT_INVALID;
7101 duration = UnserializeUInt(pReader, pos, size);
7104 return E_FILE_FORMAT_INVALID;
7105 } else if (id == libwebm::kMkvReferenceBlock) {
7106 if (size > 8 || size <= 0)
7107 return E_FILE_FORMAT_INVALID;
7108 const long size_ = static_cast<long>(size);
7112 long status = UnserializeInt(pReader, pos, size_, time);
7113 assert(status == 0);
7117 if (time <= 0) // see note above
7123 pos += size; // consume payload
7125 return E_FILE_FORMAT_INVALID;
7128 return E_FILE_FORMAT_INVALID;
7131 return E_FILE_FORMAT_INVALID;
7134 const long idx = m_entries_count;
7136 BlockEntry** const ppEntry = m_entries + idx;
7137 BlockEntry*& pEntry = *ppEntry;
7139 pEntry = new (std::nothrow)
7140 BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
7143 return -1; // generic error
7145 BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
7147 const long status = p->Parse();
7149 if (status == 0) { // success
7160 long Cluster::CreateSimpleBlock(long long st, long long sz) {
7162 assert(m_entries_size > 0);
7163 assert(m_entries_count >= 0);
7164 assert(m_entries_count < m_entries_size);
7166 const long idx = m_entries_count;
7168 BlockEntry** const ppEntry = m_entries + idx;
7169 BlockEntry*& pEntry = *ppEntry;
7171 pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
7174 return -1; // generic error
7176 SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
7178 const long status = p->Parse();
7191 long Cluster::GetFirst(const BlockEntry*& pFirst) const {
7192 if (m_entries_count <= 0) {
7196 const long status = Parse(pos, len);
7198 if (status < 0) { // error
7203 if (m_entries_count <= 0) { // empty cluster
7211 pFirst = m_entries[0];
7214 return 0; // success
7217 long Cluster::GetLast(const BlockEntry*& pLast) const {
7222 const long status = Parse(pos, len);
7224 if (status < 0) { // error
7229 if (status > 0) // no new block
7233 if (m_entries_count <= 0) {
7240 const long idx = m_entries_count - 1;
7242 pLast = m_entries[idx];
7248 long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
7251 assert(m_entries_count > 0);
7253 size_t idx = pCurr->GetIndex();
7254 assert(idx < size_t(m_entries_count));
7255 assert(m_entries[idx] == pCurr);
7259 if (idx >= size_t(m_entries_count)) {
7263 const long status = Parse(pos, len);
7265 if (status < 0) { // error
7276 assert(m_entries_count > 0);
7277 assert(idx < size_t(m_entries_count));
7280 pNext = m_entries[idx];
7286 long Cluster::GetEntryCount() const { return m_entries_count; }
7288 const BlockEntry* Cluster::GetEntry(const Track* pTrack,
7289 long long time_ns) const {
7292 if (m_pSegment == NULL) // this is the special EOS cluster
7293 return pTrack->GetEOS();
7295 const BlockEntry* pResult = pTrack->GetEOS();
7300 if (index >= m_entries_count) {
7304 const long status = Parse(pos, len);
7305 assert(status >= 0);
7307 if (status > 0) // completely parsed, and no more entries
7310 if (status < 0) // should never happen
7314 assert(index < m_entries_count);
7317 const BlockEntry* const pEntry = m_entries[index];
7319 assert(!pEntry->EOS());
7321 const Block* const pBlock = pEntry->GetBlock();
7324 if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
7329 if (pTrack->VetEntry(pEntry)) {
7330 if (time_ns < 0) // just want first candidate block
7333 const long long ns = pBlock->GetTime(this);
7338 pResult = pEntry; // have a candidate
7339 } else if (time_ns >= 0) {
7340 const long long ns = pBlock->GetTime(this);
7350 const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
7351 const CuePoint::TrackPosition& tp) const {
7353 const long long tc = cp.GetTimeCode();
7355 if (tp.m_block > 0) {
7356 const long block = static_cast<long>(tp.m_block);
7357 const long index = block - 1;
7359 while (index >= m_entries_count) {
7363 const long status = Parse(pos, len);
7365 if (status < 0) // TODO: can this happen?
7368 if (status > 0) // nothing remains to be parsed
7372 const BlockEntry* const pEntry = m_entries[index];
7374 assert(!pEntry->EOS());
7376 const Block* const pBlock = pEntry->GetBlock();
7379 if ((pBlock->GetTrackNumber() == tp.m_track) &&
7380 (pBlock->GetTimeCode(this) == tc)) {
7388 if (index >= m_entries_count) {
7392 const long status = Parse(pos, len);
7394 if (status < 0) // TODO: can this happen?
7397 if (status > 0) // nothing remains to be parsed
7401 assert(index < m_entries_count);
7404 const BlockEntry* const pEntry = m_entries[index];
7406 assert(!pEntry->EOS());
7408 const Block* const pBlock = pEntry->GetBlock();
7411 if (pBlock->GetTrackNumber() != tp.m_track) {
7416 const long long tc_ = pBlock->GetTimeCode(this);
7426 const Tracks* const pTracks = m_pSegment->GetTracks();
7429 const long tn = static_cast<long>(tp.m_track);
7430 const Track* const pTrack = pTracks->GetTrackByNumber(tn);
7435 const long long type = pTrack->GetType();
7437 if (type == 2) // audio
7440 if (type != 1) // not video
7443 if (!pBlock->IsKey())
7450 BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
7451 BlockEntry::~BlockEntry() {}
7452 const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
7453 long BlockEntry::GetIndex() const { return m_index; }
7455 SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
7457 : BlockEntry(pCluster, idx), m_block(start, size, 0) {}
7459 long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
7460 BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
7461 const Block* SimpleBlock::GetBlock() const { return &m_block; }
7463 BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
7464 long long block_size, long long prev, long long next,
7465 long long duration, long long discard_padding)
7466 : BlockEntry(pCluster, idx),
7467 m_block(block_start, block_size, discard_padding),
7470 m_duration(duration) {}
7472 long BlockGroup::Parse() {
7473 const long status = m_block.Parse(m_pCluster);
7478 m_block.SetKey((m_prev > 0) && (m_next <= 0));
7483 BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
7484 const Block* BlockGroup::GetBlock() const { return &m_block; }
7485 long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
7486 long long BlockGroup::GetNextTimeCode() const { return m_next; }
7487 long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
7489 Block::Block(long long start, long long size_, long long discard_padding)
7497 m_discard_padding(discard_padding) {}
7499 Block::~Block() { delete[] m_frames; }
7501 long Block::Parse(const Cluster* pCluster) {
7502 if (pCluster == NULL)
7505 if (pCluster->m_pSegment == NULL)
7508 assert(m_start >= 0);
7509 assert(m_size >= 0);
7510 assert(m_track <= 0);
7511 assert(m_frames == NULL);
7512 assert(m_frame_count <= 0);
7514 long long pos = m_start;
7515 const long long stop = m_start + m_size;
7519 IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
7521 m_track = ReadUInt(pReader, pos, len);
7524 return E_FILE_FORMAT_INVALID;
7526 if ((pos + len) > stop)
7527 return E_FILE_FORMAT_INVALID;
7529 pos += len; // consume track number
7531 if ((stop - pos) < 2)
7532 return E_FILE_FORMAT_INVALID;
7537 status = UnserializeInt(pReader, pos, 2, value);
7540 return E_FILE_FORMAT_INVALID;
7542 if (value < SHRT_MIN)
7543 return E_FILE_FORMAT_INVALID;
7545 if (value > SHRT_MAX)
7546 return E_FILE_FORMAT_INVALID;
7548 m_timecode = static_cast<short>(value);
7552 if ((stop - pos) <= 0)
7553 return E_FILE_FORMAT_INVALID;
7555 status = pReader->Read(pos, 1, &m_flags);
7558 return E_FILE_FORMAT_INVALID;
7560 const int lacing = int(m_flags & 0x06) >> 1;
7562 ++pos; // consume flags byte
7564 if (lacing == 0) { // no lacing
7566 return E_FILE_FORMAT_INVALID;
7569 m_frames = new (std::nothrow) Frame[m_frame_count];
7570 if (m_frames == NULL)
7573 Frame& f = m_frames[0];
7576 const long long frame_size = stop - pos;
7578 if (frame_size > LONG_MAX || frame_size <= 0)
7579 return E_FILE_FORMAT_INVALID;
7581 f.len = static_cast<long>(frame_size);
7583 return 0; // success
7587 return E_FILE_FORMAT_INVALID;
7589 unsigned char biased_count;
7591 status = pReader->Read(pos, 1, &biased_count);
7594 return E_FILE_FORMAT_INVALID;
7596 ++pos; // consume frame count
7598 return E_FILE_FORMAT_INVALID;
7600 m_frame_count = int(biased_count) + 1;
7602 m_frames = new (std::nothrow) Frame[m_frame_count];
7603 if (m_frames == NULL)
7607 return E_FILE_FORMAT_INVALID;
7609 if (lacing == 1) { // Xiph
7610 Frame* pf = m_frames;
7611 Frame* const pf_end = pf + m_frame_count;
7614 int frame_count = m_frame_count;
7616 while (frame_count > 1) {
7617 long frame_size = 0;
7623 return E_FILE_FORMAT_INVALID;
7625 status = pReader->Read(pos, 1, &val);
7628 return E_FILE_FORMAT_INVALID;
7630 ++pos; // consume xiph size byte
7639 assert(pf < pf_end);
7641 return E_FILE_FORMAT_INVALID;
7643 f.pos = 0; // patch later
7645 if (frame_size <= 0)
7646 return E_FILE_FORMAT_INVALID;
7649 size += frame_size; // contribution of this frame
7654 if (pf >= pf_end || pos > stop)
7655 return E_FILE_FORMAT_INVALID;
7661 return E_FILE_FORMAT_INVALID;
7663 f.pos = 0; // patch later
7665 const long long total_size = stop - pos;
7667 if (total_size < size)
7668 return E_FILE_FORMAT_INVALID;
7670 const long long frame_size = total_size - size;
7672 if (frame_size > LONG_MAX || frame_size <= 0)
7673 return E_FILE_FORMAT_INVALID;
7675 f.len = static_cast<long>(frame_size);
7679 while (pf != pf_end) {
7681 assert((pos + f.len) <= stop);
7683 if ((pos + f.len) > stop)
7684 return E_FILE_FORMAT_INVALID;
7690 assert(pos == stop);
7692 return E_FILE_FORMAT_INVALID;
7694 } else if (lacing == 2) { // fixed-size lacing
7696 return E_FILE_FORMAT_INVALID;
7698 const long long total_size = stop - pos;
7700 if ((total_size % m_frame_count) != 0)
7701 return E_FILE_FORMAT_INVALID;
7703 const long long frame_size = total_size / m_frame_count;
7705 if (frame_size > LONG_MAX || frame_size <= 0)
7706 return E_FILE_FORMAT_INVALID;
7708 Frame* pf = m_frames;
7709 Frame* const pf_end = pf + m_frame_count;
7711 while (pf != pf_end) {
7712 assert((pos + frame_size) <= stop);
7713 if ((pos + frame_size) > stop)
7714 return E_FILE_FORMAT_INVALID;
7719 f.len = static_cast<long>(frame_size);
7724 assert(pos == stop);
7726 return E_FILE_FORMAT_INVALID;
7729 assert(lacing == 3); // EBML lacing
7732 return E_FILE_FORMAT_INVALID;
7735 int frame_count = m_frame_count;
7737 long long frame_size = ReadUInt(pReader, pos, len);
7739 if (frame_size <= 0)
7740 return E_FILE_FORMAT_INVALID;
7742 if (frame_size > LONG_MAX)
7743 return E_FILE_FORMAT_INVALID;
7745 if ((pos + len) > stop)
7746 return E_FILE_FORMAT_INVALID;
7748 pos += len; // consume length of size of first frame
7750 if ((pos + frame_size) > stop)
7751 return E_FILE_FORMAT_INVALID;
7753 Frame* pf = m_frames;
7754 Frame* const pf_end = pf + m_frame_count;
7759 curr.pos = 0; // patch later
7761 curr.len = static_cast<long>(frame_size);
7762 size += curr.len; // contribution of this frame
7767 while (frame_count > 1) {
7769 return E_FILE_FORMAT_INVALID;
7771 assert(pf < pf_end);
7773 return E_FILE_FORMAT_INVALID;
7775 const Frame& prev = *pf++;
7776 assert(prev.len == frame_size);
7777 if (prev.len != frame_size)
7778 return E_FILE_FORMAT_INVALID;
7780 assert(pf < pf_end);
7782 return E_FILE_FORMAT_INVALID;
7786 curr.pos = 0; // patch later
7788 const long long delta_size_ = ReadUInt(pReader, pos, len);
7790 if (delta_size_ < 0)
7791 return E_FILE_FORMAT_INVALID;
7793 if ((pos + len) > stop)
7794 return E_FILE_FORMAT_INVALID;
7796 pos += len; // consume length of (delta) size
7798 return E_FILE_FORMAT_INVALID;
7800 const int exp = 7 * len - 1;
7801 const long long bias = (1LL << exp) - 1LL;
7802 const long long delta_size = delta_size_ - bias;
7804 frame_size += delta_size;
7806 if (frame_size <= 0)
7807 return E_FILE_FORMAT_INVALID;
7809 if (frame_size > LONG_MAX)
7810 return E_FILE_FORMAT_INVALID;
7812 curr.len = static_cast<long>(frame_size);
7813 size += curr.len; // contribution of this frame
7819 if (frame_count > 0) {
7820 if (pos > stop || pf >= pf_end)
7821 return E_FILE_FORMAT_INVALID;
7823 const Frame& prev = *pf++;
7824 assert(prev.len == frame_size);
7825 if (prev.len != frame_size)
7826 return E_FILE_FORMAT_INVALID;
7829 return E_FILE_FORMAT_INVALID;
7831 Frame& curr = *pf++;
7833 return E_FILE_FORMAT_INVALID;
7835 curr.pos = 0; // patch later
7837 const long long total_size = stop - pos;
7839 if (total_size < size)
7840 return E_FILE_FORMAT_INVALID;
7842 frame_size = total_size - size;
7844 if (frame_size > LONG_MAX || frame_size <= 0)
7845 return E_FILE_FORMAT_INVALID;
7847 curr.len = static_cast<long>(frame_size);
7851 while (pf != pf_end) {
7853 assert((pos + f.len) <= stop);
7854 if ((pos + f.len) > stop)
7855 return E_FILE_FORMAT_INVALID;
7862 return E_FILE_FORMAT_INVALID;
7865 return 0; // success
7868 long long Block::GetTimeCode(const Cluster* pCluster) const {
7872 const long long tc0 = pCluster->GetTimeCode();
7875 const long long tc = tc0 + m_timecode;
7877 return tc; // unscaled timecode units
7880 long long Block::GetTime(const Cluster* pCluster) const {
7883 const long long tc = GetTimeCode(pCluster);
7885 const Segment* const pSegment = pCluster->m_pSegment;
7886 const SegmentInfo* const pInfo = pSegment->GetInfo();
7889 const long long scale = pInfo->GetTimeCodeScale();
7892 const long long ns = tc * scale;
7897 long long Block::GetTrackNumber() const { return m_track; }
7899 bool Block::IsKey() const {
7900 return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
7903 void Block::SetKey(bool bKey) {
7905 m_flags |= static_cast<unsigned char>(1 << 7);
7910 bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
7912 Block::Lacing Block::GetLacing() const {
7913 const int value = int(m_flags & 0x06) >> 1;
7914 return static_cast<Lacing>(value);
7917 int Block::GetFrameCount() const { return m_frame_count; }
7919 const Block::Frame& Block::GetFrame(int idx) const {
7921 assert(idx < m_frame_count);
7923 const Frame& f = m_frames[idx];
7930 long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
7934 const long status = pReader->Read(pos, len, buf);
7938 long long Block::GetDiscardPadding() const { return m_discard_padding; }
7940 } // namespace mkvparser