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 // Disable MSVC warnings that suggest making code non-portable.
27 #pragma warning(disable : 4996)
31 const double MasteringMetadata::kValueNotPresent = DBL_MAX;
32 const long long Colour::kValueNotPresent = LLONG_MAX;
35 inline bool isnan(double val) { return !!_isnan(val); }
36 inline bool isinf(double val) { return !_finite(val); }
38 inline bool isnan(double val) { return std::isnan(val); }
39 inline bool isinf(double val) { return std::isinf(val); }
42 IMkvReader::~IMkvReader() {}
44 template <typename Type>
45 Type* SafeArrayAlloc(unsigned long long num_elements,
46 unsigned long long element_size) {
47 if (num_elements == 0 || element_size == 0)
50 const size_t kMaxAllocSize = 0x80000000; // 2GiB
51 const unsigned long long num_bytes = num_elements * element_size;
52 if (element_size > (kMaxAllocSize / num_elements))
54 if (num_bytes != static_cast<size_t>(num_bytes))
57 return new (std::nothrow) Type[static_cast<size_t>(num_bytes)];
60 void GetVersion(int& major, int& minor, int& build, int& revision) {
67 long long ReadUInt(IMkvReader* pReader, long long pos, long& len) {
68 if (!pReader || pos < 0)
69 return E_FILE_FORMAT_INVALID;
73 int status = pReader->Read(pos, 1, &b);
75 if (status < 0) // error or underflow
78 if (status > 0) // interpreted as "underflow"
79 return E_BUFFER_NOT_FULL;
81 if (b == 0) // we can't handle u-int values larger than 8 bytes
82 return E_FILE_FORMAT_INVALID;
84 unsigned char m = 0x80;
91 long long result = b & (~m);
94 for (int i = 1; i < len; ++i) {
95 status = pReader->Read(pos, 1, &b);
104 return E_BUFFER_NOT_FULL;
116 // Reads an EBML ID and returns it.
117 // An ID must at least 1 byte long, cannot exceed 4, and its value must be
119 // See known EBML values and EBMLMaxIDLength:
120 // http://www.matroska.org/technical/specs/index.html
121 // Returns the ID, or a value less than 0 to report an error while reading the
123 long long ReadID(IMkvReader* pReader, long long pos, long& len) {
124 if (pReader == NULL || pos < 0)
125 return E_FILE_FORMAT_INVALID;
127 // Read the first byte. The length in bytes of the ID is determined by
128 // finding the first set bit in the first byte of the ID.
129 unsigned char temp_byte = 0;
130 int read_status = pReader->Read(pos, 1, &temp_byte);
133 return E_FILE_FORMAT_INVALID;
134 else if (read_status > 0) // No data to read.
135 return E_BUFFER_NOT_FULL;
137 if (temp_byte == 0) // ID length > 8 bytes; invalid file.
138 return E_FILE_FORMAT_INVALID;
141 const int kMaxIdLengthInBytes = 4;
142 const int kCheckByte = 0x80;
144 // Find the first bit that's set.
145 bool found_bit = false;
146 for (; bit_pos < kMaxIdLengthInBytes; ++bit_pos) {
147 if ((kCheckByte >> bit_pos) & temp_byte) {
154 // The value is too large to be a valid ID.
155 return E_FILE_FORMAT_INVALID;
158 // Read the remaining bytes of the ID (if any).
159 const int id_length = bit_pos + 1;
160 long long ebml_id = temp_byte;
161 for (int i = 1; i < id_length; ++i) {
163 read_status = pReader->Read(pos + i, 1, &temp_byte);
166 return E_FILE_FORMAT_INVALID;
167 else if (read_status > 0)
168 return E_BUFFER_NOT_FULL;
170 ebml_id |= temp_byte;
177 long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) {
178 if (!pReader || pos < 0)
179 return E_FILE_FORMAT_INVALID;
181 long long total, available;
183 int status = pReader->Length(&total, &available);
184 if (status < 0 || (total >= 0 && available > total))
185 return E_FILE_FORMAT_INVALID;
189 if (pos >= available)
190 return pos; // too few bytes available
194 status = pReader->Read(pos, 1, &b);
199 if (b == 0) // we can't handle u-int values larger than 8 bytes
200 return E_FILE_FORMAT_INVALID;
202 unsigned char m = 0x80;
212 // TODO(vigneshv): This function assumes that unsigned values never have their
214 long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) {
215 if (!pReader || pos < 0 || (size <= 0) || (size > 8))
216 return E_FILE_FORMAT_INVALID;
218 long long result = 0;
220 for (long long i = 0; i < size; ++i) {
223 const long status = pReader->Read(pos, 1, &b);
237 long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
239 if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8)))
240 return E_FILE_FORMAT_INVALID;
242 const long size = static_cast<long>(size_);
244 unsigned char buf[8];
246 const int status = pReader->Read(pos, size, buf);
248 if (status < 0) // error
272 unsigned long long dd;
289 if (mkvparser::isinf(result) || mkvparser::isnan(result))
290 return E_FILE_FORMAT_INVALID;
295 long UnserializeInt(IMkvReader* pReader, long long pos, long long size,
296 long long& result_ref) {
297 if (!pReader || pos < 0 || size < 1 || size > 8)
298 return E_FILE_FORMAT_INVALID;
300 signed char first_byte = 0;
301 const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte);
306 unsigned long long result = first_byte;
309 for (long i = 1; i < size; ++i) {
312 const long status = pReader->Read(pos, 1, &b);
323 result_ref = static_cast<long long>(result);
327 long UnserializeString(IMkvReader* pReader, long long pos, long long size,
332 if (size >= LONG_MAX || size < 0)
333 return E_FILE_FORMAT_INVALID;
335 // +1 for '\0' terminator
336 const long required_size = static_cast<long>(size) + 1;
338 str = SafeArrayAlloc<char>(1, required_size);
340 return E_FILE_FORMAT_INVALID;
342 unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
344 const long status = pReader->Read(pos, static_cast<long>(size), buf);
353 str[required_size - 1] = '\0';
357 long ParseElementHeader(IMkvReader* pReader, long long& pos, long long stop,
358 long long& id, long long& size) {
359 if (stop >= 0 && pos >= stop)
360 return E_FILE_FORMAT_INVALID;
364 id = ReadID(pReader, pos, len);
367 return E_FILE_FORMAT_INVALID;
369 pos += len; // consume id
371 if (stop >= 0 && pos >= stop)
372 return E_FILE_FORMAT_INVALID;
374 size = ReadUInt(pReader, pos, len);
376 if (size < 0 || len < 1 || len > 8) {
377 // Invalid: Negative payload size, negative or 0 length integer, or integer
378 // larger than 64 bits (libwebm cannot handle them).
379 return E_FILE_FORMAT_INVALID;
382 // Avoid rolling over pos when very close to LLONG_MAX.
383 const unsigned long long rollover_check =
384 static_cast<unsigned long long>(pos) + len;
385 if (rollover_check > LLONG_MAX)
386 return E_FILE_FORMAT_INVALID;
388 pos += len; // consume length of size
390 // pos now designates payload
392 if (stop >= 0 && pos > stop)
393 return E_FILE_FORMAT_INVALID;
398 bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
400 if (!pReader || pos < 0)
404 long long available = 0;
406 const long status = pReader->Length(&total, &available);
407 if (status < 0 || (total >= 0 && available > total))
412 const long long id = ReadID(pReader, pos, len);
413 if (id < 0 || (available - pos) > len)
416 if (static_cast<unsigned long>(id) != expected_id)
419 pos += len; // consume id
421 const long long size = ReadUInt(pReader, pos, len);
422 if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len)
425 pos += len; // consume length of size of payload
427 val = UnserializeUInt(pReader, pos, size);
431 pos += size; // consume size of payload
436 bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
437 unsigned char*& buf, size_t& buflen) {
438 if (!pReader || pos < 0)
442 long long available = 0;
444 long status = pReader->Length(&total, &available);
445 if (status < 0 || (total >= 0 && available > total))
449 const long long id = ReadID(pReader, pos, len);
450 if (id < 0 || (available - pos) > len)
453 if (static_cast<unsigned long>(id) != expected_id)
456 pos += len; // consume id
458 const long long size = ReadUInt(pReader, pos, len);
459 if (size < 0 || len <= 0 || len > 8 || (available - pos) > len)
462 unsigned long long rollover_check =
463 static_cast<unsigned long long>(pos) + len;
464 if (rollover_check > LLONG_MAX)
467 pos += len; // consume length of size of payload
469 rollover_check = static_cast<unsigned long long>(pos) + size;
470 if (rollover_check > LLONG_MAX)
473 if ((pos + size) > available)
476 if (size >= LONG_MAX)
479 const long buflen_ = static_cast<long>(size);
481 buf = SafeArrayAlloc<unsigned char>(1, buflen_);
485 status = pReader->Read(pos, buflen_, buf);
491 pos += size; // consume size of payload
495 EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); }
497 EBMLHeader::~EBMLHeader() { delete[] m_docType; }
499 void EBMLHeader::Init() {
510 m_docTypeVersion = 1;
511 m_docTypeReadVersion = 1;
514 long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) {
516 return E_FILE_FORMAT_INVALID;
518 long long total, available;
520 long status = pReader->Length(&total, &available);
522 if (status < 0) // error
527 // Scan until we find what looks like the first byte of the EBML header.
528 const long long kMaxScanBytes = (available >= 1024) ? 1024 : available;
529 const unsigned char kEbmlByte0 = 0x1A;
530 unsigned char scan_byte = 0;
532 while (pos < kMaxScanBytes) {
533 status = pReader->Read(pos, 1, &scan_byte);
535 if (status < 0) // error
538 return E_BUFFER_NOT_FULL;
540 if (scan_byte == kEbmlByte0)
547 const long long ebml_id = ReadID(pReader, pos, len);
549 if (ebml_id == E_BUFFER_NOT_FULL)
550 return E_BUFFER_NOT_FULL;
552 if (len != 4 || ebml_id != libwebm::kMkvEBML)
553 return E_FILE_FORMAT_INVALID;
555 // Move read pos forward to the EBML header size field.
558 // Read length of size field.
559 long long result = GetUIntLength(pReader, pos, len);
561 if (result < 0) // error
562 return E_FILE_FORMAT_INVALID;
563 else if (result > 0) // need more data
564 return E_BUFFER_NOT_FULL;
566 if (len < 1 || len > 8)
567 return E_FILE_FORMAT_INVALID;
569 if ((total >= 0) && ((total - pos) < len))
570 return E_FILE_FORMAT_INVALID;
572 if ((available - pos) < len)
573 return pos + len; // try again later
575 // Read the EBML header size.
576 result = ReadUInt(pReader, pos, len);
578 if (result < 0) // error
581 pos += len; // consume size field
583 // pos now designates start of payload
585 if ((total >= 0) && ((total - pos) < result))
586 return E_FILE_FORMAT_INVALID;
588 if ((available - pos) < result)
591 const long long end = pos + result;
598 status = ParseElementHeader(pReader, pos, end, id, size);
600 if (status < 0) // error
604 return E_FILE_FORMAT_INVALID;
606 if (id == libwebm::kMkvEBMLVersion) {
607 m_version = UnserializeUInt(pReader, pos, size);
610 return E_FILE_FORMAT_INVALID;
611 } else if (id == libwebm::kMkvEBMLReadVersion) {
612 m_readVersion = UnserializeUInt(pReader, pos, size);
614 if (m_readVersion <= 0)
615 return E_FILE_FORMAT_INVALID;
616 } else if (id == libwebm::kMkvEBMLMaxIDLength) {
617 m_maxIdLength = UnserializeUInt(pReader, pos, size);
619 if (m_maxIdLength <= 0)
620 return E_FILE_FORMAT_INVALID;
621 } else if (id == libwebm::kMkvEBMLMaxSizeLength) {
622 m_maxSizeLength = UnserializeUInt(pReader, pos, size);
624 if (m_maxSizeLength <= 0)
625 return E_FILE_FORMAT_INVALID;
626 } else if (id == libwebm::kMkvDocType) {
628 return E_FILE_FORMAT_INVALID;
630 status = UnserializeString(pReader, pos, size, m_docType);
634 } else if (id == libwebm::kMkvDocTypeVersion) {
635 m_docTypeVersion = UnserializeUInt(pReader, pos, size);
637 if (m_docTypeVersion <= 0)
638 return E_FILE_FORMAT_INVALID;
639 } else if (id == libwebm::kMkvDocTypeReadVersion) {
640 m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
642 if (m_docTypeReadVersion <= 0)
643 return E_FILE_FORMAT_INVALID;
650 return E_FILE_FORMAT_INVALID;
652 // Make sure DocType, DocTypeReadVersion, and DocTypeVersion are valid.
653 if (m_docType == NULL || m_docTypeReadVersion <= 0 || m_docTypeVersion <= 0)
654 return E_FILE_FORMAT_INVALID;
656 // Make sure EBMLMaxIDLength and EBMLMaxSizeLength are valid.
657 if (m_maxIdLength <= 0 || m_maxIdLength > 4 || m_maxSizeLength <= 0 ||
659 return E_FILE_FORMAT_INVALID;
664 Segment::Segment(IMkvReader* pReader, long long elem_start,
665 // long long elem_size,
666 long long start, long long size)
667 : m_pReader(pReader),
668 m_element_start(elem_start),
669 // m_element_size(elem_size),
682 m_clusterPreloadCount(0),
685 Segment::~Segment() {
686 const long count = m_clusterCount + m_clusterPreloadCount;
688 Cluster** i = m_clusters;
689 Cluster** j = m_clusters + count;
692 Cluster* const p = *i++;
706 long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
707 Segment*& pSegment) {
708 if (pReader == NULL || pos < 0)
709 return E_PARSE_FAILED;
713 long long total, available;
715 const long status = pReader->Length(&total, &available);
717 if (status < 0) // error
723 if ((total >= 0) && (available > total))
726 // I would assume that in practice this loop would execute
727 // exactly once, but we allow for other elements (e.g. Void)
728 // to immediately follow the EBML header. This is fine for
729 // the source filter case (since the entire file is available),
730 // but in the splitter case over a network we should probably
731 // just give up early. We could for example decide only to
732 // execute this loop a maximum of, say, 10 times.
734 // There is an implied "give up early" by only parsing up
735 // to the available limit. We do do that, but only if the
736 // total file size is unknown. We could decide to always
737 // use what's available as our limit (irrespective of whether
738 // we happen to know the total file length). This would have
739 // as its sense "parse this much of the file before giving up",
740 // which a slightly different sense from "try to parse up to
741 // 10 EMBL elements before giving up".
744 if ((total >= 0) && (pos >= total))
745 return E_FILE_FORMAT_INVALID;
749 long long result = GetUIntLength(pReader, pos, len);
751 if (result) // error, or too few available bytes
754 if ((total >= 0) && ((pos + len) > total))
755 return E_FILE_FORMAT_INVALID;
757 if ((pos + len) > available)
760 const long long idpos = pos;
761 const long long id = ReadID(pReader, pos, len);
764 return E_FILE_FORMAT_INVALID;
766 pos += len; // consume ID
770 result = GetUIntLength(pReader, pos, len);
772 if (result) // error, or too few available bytes
775 if ((total >= 0) && ((pos + len) > total))
776 return E_FILE_FORMAT_INVALID;
778 if ((pos + len) > available)
781 long long size = ReadUInt(pReader, pos, len);
783 if (size < 0) // error
786 pos += len; // consume length of size of element
788 // Pos now points to start of payload
790 // Handle "unknown size" for live streaming of webm files.
791 const long long unknown_size = (1LL << (7 * len)) - 1;
793 if (id == libwebm::kMkvSegment) {
794 if (size == unknown_size)
800 else if ((pos + size) > total)
803 pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size);
804 if (pSegment == NULL)
805 return E_PARSE_FAILED;
810 if (size == unknown_size)
811 return E_FILE_FORMAT_INVALID;
813 if ((total >= 0) && ((pos + size) > total))
814 return E_FILE_FORMAT_INVALID;
816 if ((pos + size) > available)
819 pos += size; // consume payload
823 long long Segment::ParseHeaders() {
824 // Outermost (level 0) segment object has been constructed,
825 // and pos designates start of payload. We need to find the
826 // inner (level 1) elements.
827 long long total, available;
829 const int status = m_pReader->Length(&total, &available);
831 if (status < 0) // error
834 if (total > 0 && available > total)
835 return E_FILE_FORMAT_INVALID;
837 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
839 if ((segment_stop >= 0 && total >= 0 && segment_stop > total) ||
840 (segment_stop >= 0 && m_pos > segment_stop)) {
841 return E_FILE_FORMAT_INVALID;
845 if ((total >= 0) && (m_pos >= total))
848 if ((segment_stop >= 0) && (m_pos >= segment_stop))
851 long long pos = m_pos;
852 const long long element_start = pos;
854 // Avoid rolling over pos when very close to LLONG_MAX.
855 unsigned long long rollover_check = pos + 1ULL;
856 if (rollover_check > LLONG_MAX)
857 return E_FILE_FORMAT_INVALID;
859 if ((pos + 1) > available)
863 long long result = GetUIntLength(m_pReader, pos, len);
865 if (result < 0) // error
869 // MkvReader doesn't have enough data to satisfy this read attempt.
873 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
874 return E_FILE_FORMAT_INVALID;
876 if ((pos + len) > available)
879 const long long idpos = pos;
880 const long long id = ReadID(m_pReader, idpos, len);
883 return E_FILE_FORMAT_INVALID;
885 if (id == libwebm::kMkvCluster)
888 pos += len; // consume ID
890 if ((pos + 1) > available)
894 result = GetUIntLength(m_pReader, pos, len);
896 if (result < 0) // error
900 // MkvReader doesn't have enough data to satisfy this read attempt.
904 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
905 return E_FILE_FORMAT_INVALID;
907 if ((pos + len) > available)
910 const long long size = ReadUInt(m_pReader, pos, len);
912 if (size < 0 || len < 1 || len > 8) {
913 // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or
914 // len > 8 is true instead of checking this _everywhere_.
918 pos += len; // consume length of size of element
920 // Avoid rolling over pos when very close to LLONG_MAX.
921 rollover_check = static_cast<unsigned long long>(pos) + size;
922 if (rollover_check > LLONG_MAX)
923 return E_FILE_FORMAT_INVALID;
925 const long long element_size = size + pos - element_start;
927 // Pos now points to start of payload
929 if ((segment_stop >= 0) && ((pos + size) > segment_stop))
930 return E_FILE_FORMAT_INVALID;
932 // We read EBML elements either in total or nothing at all.
934 if ((pos + size) > available)
937 if (id == libwebm::kMkvInfo) {
939 return E_FILE_FORMAT_INVALID;
941 m_pInfo = new (std::nothrow)
942 SegmentInfo(this, pos, size, element_start, element_size);
947 const long status = m_pInfo->Parse();
951 } else if (id == libwebm::kMkvTracks) {
953 return E_FILE_FORMAT_INVALID;
955 m_pTracks = new (std::nothrow)
956 Tracks(this, pos, size, element_start, element_size);
958 if (m_pTracks == NULL)
961 const long status = m_pTracks->Parse();
965 } else if (id == libwebm::kMkvCues) {
966 if (m_pCues == NULL) {
967 m_pCues = new (std::nothrow)
968 Cues(this, pos, size, element_start, element_size);
973 } else if (id == libwebm::kMkvSeekHead) {
974 if (m_pSeekHead == NULL) {
975 m_pSeekHead = new (std::nothrow)
976 SeekHead(this, pos, size, element_start, element_size);
978 if (m_pSeekHead == NULL)
981 const long status = m_pSeekHead->Parse();
986 } else if (id == libwebm::kMkvChapters) {
987 if (m_pChapters == NULL) {
988 m_pChapters = new (std::nothrow)
989 Chapters(this, pos, size, element_start, element_size);
991 if (m_pChapters == NULL)
994 const long status = m_pChapters->Parse();
999 } else if (id == libwebm::kMkvTags) {
1000 if (m_pTags == NULL) {
1001 m_pTags = new (std::nothrow)
1002 Tags(this, pos, size, element_start, element_size);
1004 if (m_pTags == NULL)
1007 const long status = m_pTags->Parse();
1014 m_pos = pos + size; // consume payload
1017 if (segment_stop >= 0 && m_pos > segment_stop)
1018 return E_FILE_FORMAT_INVALID;
1020 if (m_pInfo == NULL) // TODO: liberalize this behavior
1021 return E_FILE_FORMAT_INVALID;
1023 if (m_pTracks == NULL)
1024 return E_FILE_FORMAT_INVALID;
1026 return 0; // success
1029 long Segment::LoadCluster(long long& pos, long& len) {
1031 const long result = DoLoadCluster(pos, len);
1038 long Segment::DoLoadCluster(long long& pos, long& len) {
1040 return DoLoadClusterUnknownSize(pos, len);
1042 long long total, avail;
1044 long status = m_pReader->Length(&total, &avail);
1046 if (status < 0) // error
1049 if (total >= 0 && avail > total)
1050 return E_FILE_FORMAT_INVALID;
1052 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
1054 long long cluster_off = -1; // offset relative to start of segment
1055 long long cluster_size = -1; // size of cluster payload
1058 if ((total >= 0) && (m_pos >= total))
1059 return 1; // no more clusters
1061 if ((segment_stop >= 0) && (m_pos >= segment_stop))
1062 return 1; // no more clusters
1068 if ((pos + 1) > avail) {
1070 return E_BUFFER_NOT_FULL;
1073 long long result = GetUIntLength(m_pReader, pos, len);
1075 if (result < 0) // error
1076 return static_cast<long>(result);
1079 return E_BUFFER_NOT_FULL;
1081 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1082 return E_FILE_FORMAT_INVALID;
1084 if ((pos + len) > avail)
1085 return E_BUFFER_NOT_FULL;
1087 const long long idpos = pos;
1088 const long long id = ReadID(m_pReader, idpos, len);
1091 return E_FILE_FORMAT_INVALID;
1093 pos += len; // consume ID
1097 if ((pos + 1) > avail) {
1099 return E_BUFFER_NOT_FULL;
1102 result = GetUIntLength(m_pReader, pos, len);
1104 if (result < 0) // error
1105 return static_cast<long>(result);
1108 return E_BUFFER_NOT_FULL;
1110 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1111 return E_FILE_FORMAT_INVALID;
1113 if ((pos + len) > avail)
1114 return E_BUFFER_NOT_FULL;
1116 const long long size = ReadUInt(m_pReader, pos, len);
1118 if (size < 0) // error
1119 return static_cast<long>(size);
1121 pos += len; // consume length of size of element
1123 // pos now points to start of payload
1126 // Missing element payload: move on.
1131 const long long unknown_size = (1LL << (7 * len)) - 1;
1133 if ((segment_stop >= 0) && (size != unknown_size) &&
1134 ((pos + size) > segment_stop)) {
1135 return E_FILE_FORMAT_INVALID;
1138 if (id == libwebm::kMkvCues) {
1139 if (size == unknown_size) {
1140 // Cues element of unknown size: Not supported.
1141 return E_FILE_FORMAT_INVALID;
1144 if (m_pCues == NULL) {
1145 const long long element_size = (pos - idpos) + size;
1147 m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
1148 if (m_pCues == NULL)
1152 m_pos = pos + size; // consume payload
1156 if (id != libwebm::kMkvCluster) {
1157 // Besides the Segment, Libwebm allows only cluster elements of unknown
1158 // size. Fail the parse upon encountering a non-cluster element reporting
1160 if (size == unknown_size)
1161 return E_FILE_FORMAT_INVALID;
1163 m_pos = pos + size; // consume payload
1167 // We have a cluster.
1169 cluster_off = idpos - m_start; // relative pos
1171 if (size != unknown_size)
1172 cluster_size = size;
1177 if (cluster_off < 0) {
1179 return E_FILE_FORMAT_INVALID;
1185 status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
1187 if (status < 0) { // error, or underflow
1194 // status == 0 means "no block entries found"
1195 // status > 0 means "found at least one block entry"
1198 // The issue here is that the segment increments its own
1199 // pos ptr past the most recent cluster parsed, and then
1200 // starts from there to parse the next cluster. If we
1201 // don't know the size of the current cluster, then we
1202 // must either parse its payload (as we do below), looking
1203 // for the cluster (or cues) ID to terminate the parse.
1204 // This isn't really what we want: rather, we really need
1205 // a way to create the curr cluster object immediately.
1206 // The pity is that cluster::parse can determine its own
1207 // boundary, and we largely duplicate that same logic here.
1209 // Maybe we need to get rid of our look-ahead preloading
1210 // in source::parse???
1212 // As we're parsing the blocks in the curr cluster
1213 //(in cluster::parse), we should have some way to signal
1214 // to the segment that we have determined the boundary,
1215 // so it can adjust its own segment::m_pos member.
1217 // The problem is that we're asserting in asyncreadinit,
1218 // because we adjust the pos down to the curr seek pos,
1219 // and the resulting adjusted len is > 2GB. I'm suspicious
1220 // that this is even correct, but even if it is, we can't
1221 // be loading that much data in the cache anyway.
1223 const long idx = m_clusterCount;
1225 if (m_clusterPreloadCount > 0) {
1226 if (idx >= m_clusterSize)
1227 return E_FILE_FORMAT_INVALID;
1229 Cluster* const pCluster = m_clusters[idx];
1230 if (pCluster == NULL || pCluster->m_index >= 0)
1231 return E_FILE_FORMAT_INVALID;
1233 const long long off = pCluster->GetPosition();
1235 return E_FILE_FORMAT_INVALID;
1237 if (off == cluster_off) { // preloaded already
1238 if (status == 0) // no entries found
1239 return E_FILE_FORMAT_INVALID;
1241 if (cluster_size >= 0)
1242 pos += cluster_size;
1244 const long long element_size = pCluster->GetElementSize();
1246 if (element_size <= 0)
1247 return E_FILE_FORMAT_INVALID; // TODO: handle this case
1249 pos = pCluster->m_element_start + element_size;
1252 pCluster->m_index = idx; // move from preloaded to loaded
1254 --m_clusterPreloadCount;
1256 m_pos = pos; // consume payload
1257 if (segment_stop >= 0 && m_pos > segment_stop)
1258 return E_FILE_FORMAT_INVALID;
1260 return 0; // success
1264 if (status == 0) { // no entries found
1265 if (cluster_size >= 0)
1266 pos += cluster_size;
1268 if ((total >= 0) && (pos >= total)) {
1270 return 1; // no more clusters
1273 if ((segment_stop >= 0) && (pos >= segment_stop)) {
1274 m_pos = segment_stop;
1275 return 1; // no more clusters
1279 return 2; // try again
1282 // status > 0 means we have an entry
1284 Cluster* const pCluster = Cluster::Create(this, idx, cluster_off);
1285 if (pCluster == NULL)
1288 if (!AppendCluster(pCluster)) {
1293 if (cluster_size >= 0) {
1294 pos += cluster_size;
1298 if (segment_stop > 0 && m_pos > segment_stop)
1299 return E_FILE_FORMAT_INVALID;
1304 m_pUnknownSize = pCluster;
1307 return 0; // partial success, since we have a new cluster
1309 // status == 0 means "no block entries found"
1310 // pos designates start of payload
1311 // m_pos has NOT been adjusted yet (in case we need to come back here)
1314 long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
1315 if (m_pos >= 0 || m_pUnknownSize == NULL)
1316 return E_PARSE_FAILED;
1318 const long status = m_pUnknownSize->Parse(pos, len);
1320 if (status < 0) // error or underflow
1323 if (status == 0) // parsed a block
1324 return 2; // continue parsing
1326 const long long start = m_pUnknownSize->m_element_start;
1327 const long long size = m_pUnknownSize->GetElementSize();
1330 return E_FILE_FORMAT_INVALID;
1337 return 2; // continue parsing
1340 bool Segment::AppendCluster(Cluster* pCluster) {
1341 if (pCluster == NULL || pCluster->m_index < 0)
1344 const long count = m_clusterCount + m_clusterPreloadCount;
1346 long& size = m_clusterSize;
1347 const long idx = pCluster->m_index;
1349 if (size < count || idx != m_clusterCount)
1352 if (count >= size) {
1353 const long n = (size <= 0) ? 2048 : 2 * size;
1355 Cluster** const qq = new (std::nothrow) Cluster*[n];
1360 Cluster** p = m_clusters;
1361 Cluster** const pp = p + count;
1366 delete[] m_clusters;
1372 if (m_clusterPreloadCount > 0) {
1373 Cluster** const p = m_clusters + m_clusterCount;
1374 if (*p == NULL || (*p)->m_index >= 0)
1377 Cluster** q = p + m_clusterPreloadCount;
1378 if (q >= (m_clusters + size))
1382 Cluster** const qq = q - 1;
1383 if ((*qq)->m_index >= 0)
1394 m_clusters[idx] = pCluster;
1399 bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
1400 if (pCluster == NULL || pCluster->m_index >= 0 || idx < m_clusterCount)
1403 const long count = m_clusterCount + m_clusterPreloadCount;
1405 long& size = m_clusterSize;
1409 if (count >= size) {
1410 const long n = (size <= 0) ? 2048 : 2 * size;
1412 Cluster** const qq = new (std::nothrow) Cluster*[n];
1417 Cluster** p = m_clusters;
1418 Cluster** const pp = p + count;
1423 delete[] m_clusters;
1429 if (m_clusters == NULL)
1432 Cluster** const p = m_clusters + idx;
1434 Cluster** q = m_clusters + count;
1435 if (q < p || q >= (m_clusters + size))
1439 Cluster** const qq = q - 1;
1441 if ((*qq)->m_index >= 0)
1448 m_clusters[idx] = pCluster;
1449 ++m_clusterPreloadCount;
1453 long Segment::Load() {
1454 if (m_clusters != NULL || m_clusterSize != 0 || m_clusterCount != 0)
1455 return E_PARSE_FAILED;
1457 // Outermost (level 0) segment object has been constructed,
1458 // and pos designates start of payload. We need to find the
1459 // inner (level 1) elements.
1461 const long long header_status = ParseHeaders();
1463 if (header_status < 0) // error
1464 return static_cast<long>(header_status);
1466 if (header_status > 0) // underflow
1467 return E_BUFFER_NOT_FULL;
1469 if (m_pInfo == NULL || m_pTracks == NULL)
1470 return E_FILE_FORMAT_INVALID;
1473 const int status = LoadCluster();
1475 if (status < 0) // error
1478 if (status >= 1) // no more clusters
1483 SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
1484 long long element_start, long long element_size)
1485 : m_pSegment(pSegment),
1488 m_element_start(element_start),
1489 m_element_size(element_size),
1493 m_void_element_count(0) {}
1495 SeekHead::~SeekHead() {
1497 delete[] m_void_elements;
1500 long SeekHead::Parse() {
1501 IMkvReader* const pReader = m_pSegment->m_pReader;
1503 long long pos = m_start;
1504 const long long stop = m_start + m_size;
1506 // first count the seek head entries
1508 int entry_count = 0;
1509 int void_element_count = 0;
1511 while (pos < stop) {
1514 const long status = ParseElementHeader(pReader, pos, stop, id, size);
1516 if (status < 0) // error
1519 if (id == libwebm::kMkvSeek)
1521 else if (id == libwebm::kMkvVoid)
1522 ++void_element_count;
1524 pos += size; // consume payload
1527 return E_FILE_FORMAT_INVALID;
1531 return E_FILE_FORMAT_INVALID;
1533 m_entries = new (std::nothrow) Entry[entry_count];
1535 if (m_entries == NULL)
1538 m_void_elements = new (std::nothrow) VoidElement[void_element_count];
1540 if (m_void_elements == NULL)
1543 // now parse the entries and void elements
1545 Entry* pEntry = m_entries;
1546 VoidElement* pVoidElement = m_void_elements;
1550 while (pos < stop) {
1551 const long long idpos = pos;
1555 const long status = ParseElementHeader(pReader, pos, stop, id, size);
1557 if (status < 0) // error
1560 if (id == libwebm::kMkvSeek) {
1561 if (ParseEntry(pReader, pos, size, pEntry)) {
1562 Entry& e = *pEntry++;
1564 e.element_start = idpos;
1565 e.element_size = (pos + size) - idpos;
1567 } else if (id == libwebm::kMkvVoid) {
1568 VoidElement& e = *pVoidElement++;
1570 e.element_start = idpos;
1571 e.element_size = (pos + size) - idpos;
1574 pos += size; // consume payload
1576 return E_FILE_FORMAT_INVALID;
1580 return E_FILE_FORMAT_INVALID;
1582 ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
1583 assert(count_ >= 0);
1584 assert(count_ <= entry_count);
1586 m_entry_count = static_cast<int>(count_);
1588 count_ = ptrdiff_t(pVoidElement - m_void_elements);
1589 assert(count_ >= 0);
1590 assert(count_ <= void_element_count);
1592 m_void_element_count = static_cast<int>(count_);
1597 int SeekHead::GetCount() const { return m_entry_count; }
1599 const SeekHead::Entry* SeekHead::GetEntry(int idx) const {
1603 if (idx >= m_entry_count)
1606 return m_entries + idx;
1609 int SeekHead::GetVoidElementCount() const { return m_void_element_count; }
1611 const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
1615 if (idx >= m_void_element_count)
1618 return m_void_elements + idx;
1621 long Segment::ParseCues(long long off, long long& pos, long& len) {
1623 return 0; // success
1628 long long total, avail;
1630 const int status = m_pReader->Length(&total, &avail);
1632 if (status < 0) // error
1635 assert((total < 0) || (avail <= total));
1637 pos = m_start + off;
1639 if ((total < 0) || (pos >= total))
1640 return 1; // don't bother parsing cues
1642 const long long element_start = pos;
1643 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
1645 if ((pos + 1) > avail) {
1647 return E_BUFFER_NOT_FULL;
1650 long long result = GetUIntLength(m_pReader, pos, len);
1652 if (result < 0) // error
1653 return static_cast<long>(result);
1655 if (result > 0) // underflow (weird)
1658 return E_BUFFER_NOT_FULL;
1661 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1662 return E_FILE_FORMAT_INVALID;
1664 if ((pos + len) > avail)
1665 return E_BUFFER_NOT_FULL;
1667 const long long idpos = pos;
1669 const long long id = ReadID(m_pReader, idpos, len);
1671 if (id != libwebm::kMkvCues)
1672 return E_FILE_FORMAT_INVALID;
1674 pos += len; // consume ID
1675 assert((segment_stop < 0) || (pos <= segment_stop));
1679 if ((pos + 1) > avail) {
1681 return E_BUFFER_NOT_FULL;
1684 result = GetUIntLength(m_pReader, pos, len);
1686 if (result < 0) // error
1687 return static_cast<long>(result);
1689 if (result > 0) // underflow (weird)
1692 return E_BUFFER_NOT_FULL;
1695 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1696 return E_FILE_FORMAT_INVALID;
1698 if ((pos + len) > avail)
1699 return E_BUFFER_NOT_FULL;
1701 const long long size = ReadUInt(m_pReader, pos, len);
1703 if (size < 0) // error
1704 return static_cast<long>(size);
1706 if (size == 0) // weird, although technically not illegal
1709 pos += len; // consume length of size of element
1710 assert((segment_stop < 0) || (pos <= segment_stop));
1712 // Pos now points to start of payload
1714 const long long element_stop = pos + size;
1716 if ((segment_stop >= 0) && (element_stop > segment_stop))
1717 return E_FILE_FORMAT_INVALID;
1719 if ((total >= 0) && (element_stop > total))
1720 return 1; // don't bother parsing anymore
1722 len = static_cast<long>(size);
1724 if (element_stop > avail)
1725 return E_BUFFER_NOT_FULL;
1727 const long long element_size = element_stop - element_start;
1730 new (std::nothrow) Cues(this, pos, size, element_start, element_size);
1731 if (m_pCues == NULL)
1734 return 0; // success
1737 bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
1742 long long pos = start;
1743 const long long stop = start + size_;
1747 // parse the container for the level-1 element ID
1749 const long long seekIdId = ReadID(pReader, pos, len);
1753 if (seekIdId != libwebm::kMkvSeekID)
1756 if ((pos + len) > stop)
1759 pos += len; // consume SeekID id
1761 const long long seekIdSize = ReadUInt(pReader, pos, len);
1763 if (seekIdSize <= 0)
1766 if ((pos + len) > stop)
1769 pos += len; // consume size of field
1771 if ((pos + seekIdSize) > stop)
1774 // Note that the SeekId payload really is serialized
1775 // as a "Matroska integer", not as a plain binary value.
1776 // In fact, Matroska requires that ID values in the
1777 // stream exactly match the binary representation as listed
1778 // in the Matroska specification.
1780 // This parser is more liberal, and permits IDs to have
1781 // any width. (This could make the representation in the stream
1782 // different from what's in the spec, but it doesn't matter here,
1783 // since we always normalize "Matroska integer" values.)
1785 pEntry->id = ReadUInt(pReader, pos, len); // payload
1787 if (pEntry->id <= 0)
1790 if (len != seekIdSize)
1793 pos += seekIdSize; // consume SeekID payload
1795 const long long seekPosId = ReadID(pReader, pos, len);
1797 if (seekPosId != libwebm::kMkvSeekPosition)
1800 if ((pos + len) > stop)
1803 pos += len; // consume id
1805 const long long seekPosSize = ReadUInt(pReader, pos, len);
1807 if (seekPosSize <= 0)
1810 if ((pos + len) > stop)
1813 pos += len; // consume size
1815 if ((pos + seekPosSize) > stop)
1818 pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
1820 if (pEntry->pos < 0)
1823 pos += seekPosSize; // consume payload
1831 Cues::Cues(Segment* pSegment, long long start_, long long size_,
1832 long long element_start, long long element_size)
1833 : m_pSegment(pSegment),
1836 m_element_start(element_start),
1837 m_element_size(element_size),
1844 const long n = m_count + m_preload_count;
1846 CuePoint** p = m_cue_points;
1847 CuePoint** const q = p + n;
1850 CuePoint* const pCP = *p++;
1856 delete[] m_cue_points;
1859 long Cues::GetCount() const {
1860 if (m_cue_points == NULL)
1863 return m_count; // TODO: really ignore preload count?
1866 bool Cues::DoneParsing() const {
1867 const long long stop = m_start + m_size;
1868 return (m_pos >= stop);
1871 bool Cues::Init() const {
1875 if (m_count != 0 || m_preload_count != 0)
1878 IMkvReader* const pReader = m_pSegment->m_pReader;
1880 const long long stop = m_start + m_size;
1881 long long pos = m_start;
1883 long cue_points_size = 0;
1885 while (pos < stop) {
1886 const long long idpos = pos;
1890 const long long id = ReadID(pReader, pos, len);
1891 if (id < 0 || (pos + len) > stop) {
1895 pos += len; // consume ID
1897 const long long size = ReadUInt(pReader, pos, len);
1898 if (size < 0 || (pos + len > stop)) {
1902 pos += len; // consume Size field
1903 if (pos + size > stop) {
1907 if (id == libwebm::kMkvCuePoint) {
1908 if (!PreloadCuePoint(cue_points_size, idpos))
1912 pos += size; // skip payload
1917 bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
1921 if (m_preload_count >= cue_points_size) {
1922 const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
1924 CuePoint** const qq = new (std::nothrow) CuePoint*[n];
1928 CuePoint** q = qq; // beginning of target
1930 CuePoint** p = m_cue_points; // beginning of source
1931 CuePoint** const pp = p + m_preload_count; // end of source
1936 delete[] m_cue_points;
1939 cue_points_size = n;
1942 CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
1946 m_cue_points[m_preload_count++] = pCP;
1950 bool Cues::LoadCuePoint() const {
1951 const long long stop = m_start + m_size;
1954 return false; // nothing else to do
1961 IMkvReader* const pReader = m_pSegment->m_pReader;
1963 while (m_pos < stop) {
1964 const long long idpos = m_pos;
1968 const long long id = ReadID(pReader, m_pos, len);
1969 if (id < 0 || (m_pos + len) > stop)
1972 m_pos += len; // consume ID
1974 const long long size = ReadUInt(pReader, m_pos, len);
1975 if (size < 0 || (m_pos + len) > stop)
1978 m_pos += len; // consume Size field
1979 if ((m_pos + size) > stop)
1982 if (id != libwebm::kMkvCuePoint) {
1983 m_pos += size; // consume payload
1990 if (m_preload_count < 1)
1993 CuePoint* const pCP = m_cue_points[m_count];
1994 if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos)))
1997 if (!pCP->Load(pReader)) {
2004 m_pos += size; // consume payload
2008 return true; // yes, we loaded a cue point
2011 return false; // no, we did not load a cue point
2014 bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
2015 const CuePoint::TrackPosition*& pTP) const {
2016 if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0)
2019 CuePoint** const ii = m_cue_points;
2022 CuePoint** const jj = ii + m_count;
2029 if (time_ns <= pCP->GetTime(m_pSegment)) {
2030 pTP = pCP->Find(pTrack);
2031 return (pTP != NULL);
2036 //[ii, i) <= time_ns
2040 CuePoint** const k = i + (j - i) / 2;
2044 CuePoint* const pCP = *k;
2048 const long long t = pCP->GetTime(m_pSegment);
2059 if (i != j || i > jj || i <= ii)
2064 if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns)
2067 // TODO: here and elsewhere, it's probably not correct to search
2068 // for the cue point with this time, and then search for a matching
2069 // track. In principle, the matching track could be on some earlier
2070 // cue point, and with our current algorithm, we'd miss it. To make
2071 // this bullet-proof, we'd need to create a secondary structure,
2072 // with a list of cue points that apply to a track, and then search
2073 // that track-based structure for a matching cue point.
2075 pTP = pCP->Find(pTrack);
2076 return (pTP != NULL);
2079 const CuePoint* Cues::GetFirst() const {
2080 if (m_cue_points == NULL || m_count == 0)
2083 CuePoint* const* const pp = m_cue_points;
2087 CuePoint* const pCP = pp[0];
2088 if (pCP == NULL || pCP->GetTimeCode() < 0)
2094 const CuePoint* Cues::GetLast() const {
2095 if (m_cue_points == NULL || m_count <= 0)
2098 const long index = m_count - 1;
2100 CuePoint* const* const pp = m_cue_points;
2104 CuePoint* const pCP = pp[index];
2105 if (pCP == NULL || pCP->GetTimeCode() < 0)
2111 const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
2112 if (pCurr == NULL || pCurr->GetTimeCode() < 0 || m_cue_points == NULL ||
2117 long index = pCurr->m_index;
2118 if (index >= m_count)
2121 CuePoint* const* const pp = m_cue_points;
2122 if (pp == NULL || pp[index] != pCurr)
2127 if (index >= m_count)
2130 CuePoint* const pNext = pp[index];
2132 if (pNext == NULL || pNext->GetTimeCode() < 0)
2138 const BlockEntry* Cues::GetBlock(const CuePoint* pCP,
2139 const CuePoint::TrackPosition* pTP) const {
2140 if (pCP == NULL || pTP == NULL)
2143 return m_pSegment->GetBlock(*pCP, *pTP);
2146 const BlockEntry* Segment::GetBlock(const CuePoint& cp,
2147 const CuePoint::TrackPosition& tp) {
2148 Cluster** const ii = m_clusters;
2151 const long count = m_clusterCount + m_clusterPreloadCount;
2153 Cluster** const jj = ii + count;
2158 //[ii, i) < pTP->m_pos
2160 //[j, jj) > pTP->m_pos
2162 Cluster** const k = i + (j - i) / 2;
2165 Cluster* const pCluster = *k;
2168 // const long long pos_ = pCluster->m_pos;
2170 // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2172 const long long pos = pCluster->GetPosition();
2177 else if (pos > tp.m_pos)
2180 return pCluster->GetEntry(cp, tp);
2184 // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2186 Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos); //, -1);
2187 if (pCluster == NULL)
2190 const ptrdiff_t idx = i - m_clusters;
2192 if (!PreloadCluster(pCluster, idx)) {
2197 assert(m_clusterPreloadCount > 0);
2198 assert(m_clusters[idx] == pCluster);
2200 return pCluster->GetEntry(cp, tp);
2203 const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) {
2204 if (requested_pos < 0)
2207 Cluster** const ii = m_clusters;
2210 const long count = m_clusterCount + m_clusterPreloadCount;
2212 Cluster** const jj = ii + count;
2217 //[ii, i) < pTP->m_pos
2219 //[j, jj) > pTP->m_pos
2221 Cluster** const k = i + (j - i) / 2;
2224 Cluster* const pCluster = *k;
2227 // const long long pos_ = pCluster->m_pos;
2229 // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2231 const long long pos = pCluster->GetPosition();
2234 if (pos < requested_pos)
2236 else if (pos > requested_pos)
2243 // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2245 Cluster* const pCluster = Cluster::Create(this, -1, requested_pos);
2246 if (pCluster == NULL)
2249 const ptrdiff_t idx = i - m_clusters;
2251 if (!PreloadCluster(pCluster, idx)) {
2256 assert(m_clusterPreloadCount > 0);
2257 assert(m_clusters[idx] == pCluster);
2262 CuePoint::CuePoint(long idx, long long pos)
2263 : m_element_start(0),
2266 m_timecode(-1 * pos),
2267 m_track_positions(NULL),
2268 m_track_positions_count(0) {
2272 CuePoint::~CuePoint() { delete[] m_track_positions; }
2274 bool CuePoint::Load(IMkvReader* pReader) {
2276 // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
2278 if (m_timecode >= 0) // already loaded
2281 assert(m_track_positions == NULL);
2282 assert(m_track_positions_count == 0);
2284 long long pos_ = -m_timecode;
2285 const long long element_start = pos_;
2292 const long long id = ReadID(pReader, pos_, len);
2293 if (id != libwebm::kMkvCuePoint)
2296 pos_ += len; // consume ID
2298 const long long size = ReadUInt(pReader, pos_, len);
2301 pos_ += len; // consume Size field
2302 // pos_ now points to start of payload
2307 const long long element_size = stop - element_start;
2309 long long pos = pos_;
2311 // First count number of track positions
2313 while (pos < stop) {
2316 const long long id = ReadID(pReader, pos, len);
2317 if ((id < 0) || (pos + len > stop)) {
2321 pos += len; // consume ID
2323 const long long size = ReadUInt(pReader, pos, len);
2324 if ((size < 0) || (pos + len > stop)) {
2328 pos += len; // consume Size field
2329 if ((pos + size) > stop) {
2333 if (id == libwebm::kMkvCueTime)
2334 m_timecode = UnserializeUInt(pReader, pos, size);
2336 else if (id == libwebm::kMkvCueTrackPositions)
2337 ++m_track_positions_count;
2339 pos += size; // consume payload
2342 if (m_timecode < 0 || m_track_positions_count <= 0) {
2346 // os << "CuePoint::Load(cont'd): idpos=" << idpos
2347 // << " timecode=" << m_timecode
2350 m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
2351 if (m_track_positions == NULL)
2354 // Now parse track positions
2356 TrackPosition* p = m_track_positions;
2359 while (pos < stop) {
2362 const long long id = ReadID(pReader, pos, len);
2363 if (id < 0 || (pos + len) > stop)
2366 pos += len; // consume ID
2368 const long long size = ReadUInt(pReader, pos, len);
2370 assert((pos + len) <= stop);
2372 pos += len; // consume Size field
2373 assert((pos + size) <= stop);
2375 if (id == libwebm::kMkvCueTrackPositions) {
2376 TrackPosition& tp = *p++;
2377 if (!tp.Parse(pReader, pos, size)) {
2382 pos += size; // consume payload
2387 assert(size_t(p - m_track_positions) == m_track_positions_count);
2389 m_element_start = element_start;
2390 m_element_size = element_size;
2395 bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
2397 const long long stop = start_ + size_;
2398 long long pos = start_;
2402 m_block = 1; // default
2404 while (pos < stop) {
2407 const long long id = ReadID(pReader, pos, len);
2408 if ((id < 0) || ((pos + len) > stop)) {
2412 pos += len; // consume ID
2414 const long long size = ReadUInt(pReader, pos, len);
2415 if ((size < 0) || ((pos + len) > stop)) {
2419 pos += len; // consume Size field
2420 if ((pos + size) > stop) {
2424 if (id == libwebm::kMkvCueTrack)
2425 m_track = UnserializeUInt(pReader, pos, size);
2426 else if (id == libwebm::kMkvCueClusterPosition)
2427 m_pos = UnserializeUInt(pReader, pos, size);
2428 else if (id == libwebm::kMkvCueBlockNumber)
2429 m_block = UnserializeUInt(pReader, pos, size);
2431 pos += size; // consume payload
2434 if ((m_pos < 0) || (m_track <= 0)) {
2441 const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
2444 const long long n = pTrack->GetNumber();
2446 const TrackPosition* i = m_track_positions;
2447 const TrackPosition* const j = i + m_track_positions_count;
2450 const TrackPosition& p = *i++;
2456 return NULL; // no matching track number found
2459 long long CuePoint::GetTimeCode() const { return m_timecode; }
2461 long long CuePoint::GetTime(const Segment* pSegment) const {
2463 assert(m_timecode >= 0);
2465 const SegmentInfo* const pInfo = pSegment->GetInfo();
2468 const long long scale = pInfo->GetTimeCodeScale();
2471 const long long time = scale * m_timecode;
2476 bool Segment::DoneParsing() const {
2478 long long total, avail;
2480 const int status = m_pReader->Length(&total, &avail);
2482 if (status < 0) // error
2483 return true; // must assume done
2486 return false; // assume live stream
2488 return (m_pos >= total);
2491 const long long stop = m_start + m_size;
2493 return (m_pos >= stop);
2496 const Cluster* Segment::GetFirst() const {
2497 if ((m_clusters == NULL) || (m_clusterCount <= 0))
2500 Cluster* const pCluster = m_clusters[0];
2506 const Cluster* Segment::GetLast() const {
2507 if ((m_clusters == NULL) || (m_clusterCount <= 0))
2510 const long idx = m_clusterCount - 1;
2512 Cluster* const pCluster = m_clusters[idx];
2518 unsigned long Segment::GetCount() const { return m_clusterCount; }
2520 const Cluster* Segment::GetNext(const Cluster* pCurr) {
2522 assert(pCurr != &m_eos);
2525 long idx = pCurr->m_index;
2528 assert(m_clusterCount > 0);
2529 assert(idx < m_clusterCount);
2530 assert(pCurr == m_clusters[idx]);
2534 if (idx >= m_clusterCount)
2535 return &m_eos; // caller will LoadCluster as desired
2537 Cluster* const pNext = m_clusters[idx];
2539 assert(pNext->m_index >= 0);
2540 assert(pNext->m_index == idx);
2545 assert(m_clusterPreloadCount > 0);
2547 long long pos = pCurr->m_element_start;
2549 assert(m_size >= 0); // TODO
2550 const long long stop = m_start + m_size; // end of segment
2555 long long result = GetUIntLength(m_pReader, pos, len);
2556 assert(result == 0);
2557 assert((pos + len) <= stop); // TODO
2561 const long long id = ReadID(m_pReader, pos, len);
2562 if (id != libwebm::kMkvCluster)
2565 pos += len; // consume ID
2568 result = GetUIntLength(m_pReader, pos, len);
2569 assert(result == 0); // TODO
2570 assert((pos + len) <= stop); // TODO
2572 const long long size = ReadUInt(m_pReader, pos, len);
2573 assert(size > 0); // TODO
2574 // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2576 pos += len; // consume length of size of element
2577 assert((pos + size) <= stop); // TODO
2579 // Pos now points to start of payload
2581 pos += size; // consume payload
2584 long long off_next = 0;
2586 while (pos < stop) {
2589 long long result = GetUIntLength(m_pReader, pos, len);
2590 assert(result == 0);
2591 assert((pos + len) <= stop); // TODO
2595 const long long idpos = pos; // pos of next (potential) cluster
2597 const long long id = ReadID(m_pReader, idpos, len);
2601 pos += len; // consume ID
2604 result = GetUIntLength(m_pReader, pos, len);
2605 assert(result == 0); // TODO
2606 assert((pos + len) <= stop); // TODO
2608 const long long size = ReadUInt(m_pReader, pos, len);
2609 assert(size >= 0); // TODO
2611 pos += len; // consume length of size of element
2612 assert((pos + size) <= stop); // TODO
2614 // Pos now points to start of payload
2616 if (size == 0) // weird
2619 if (id == libwebm::kMkvCluster) {
2620 const long long off_next_ = idpos - m_start;
2625 const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_);
2627 assert(status >= 0);
2630 off_next = off_next_;
2635 pos += size; // consume payload
2641 Cluster** const ii = m_clusters + m_clusterCount;
2644 Cluster** const jj = ii + m_clusterPreloadCount;
2651 //[j, jj) > pos_next
2653 Cluster** const k = i + (j - i) / 2;
2656 Cluster* const pNext = *k;
2658 assert(pNext->m_index < 0);
2660 // const long long pos_ = pNext->m_pos;
2662 // pos = pos_ * ((pos_ < 0) ? -1 : 1);
2664 pos = pNext->GetPosition();
2668 else if (pos > off_next)
2676 Cluster* const pNext = Cluster::Create(this, -1, off_next);
2680 const ptrdiff_t idx_next = i - m_clusters; // insertion position
2682 if (!PreloadCluster(pNext, idx_next)) {
2687 assert(idx_next < m_clusterSize);
2688 assert(m_clusters[idx_next] == pNext);
2693 long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult,
2694 long long& pos, long& len) {
2696 assert(!pCurr->EOS());
2701 if (pCurr->m_index >= 0) { // loaded (not merely preloaded)
2702 assert(m_clusters[pCurr->m_index] == pCurr);
2704 const long next_idx = pCurr->m_index + 1;
2706 if (next_idx < m_clusterCount) {
2707 pResult = m_clusters[next_idx];
2708 return 0; // success
2711 // curr cluster is last among loaded
2713 const long result = LoadCluster(pos, len);
2715 if (result < 0) // error or underflow
2718 if (result > 0) // no more clusters
2720 // pResult = &m_eos;
2724 pResult = GetLast();
2725 return 0; // success
2730 long long total, avail;
2732 long status = m_pReader->Length(&total, &avail);
2734 if (status < 0) // error
2737 assert((total < 0) || (avail <= total));
2739 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2741 // interrogate curr cluster
2743 pos = pCurr->m_element_start;
2745 if (pCurr->m_element_size >= 0)
2746 pos += pCurr->m_element_size;
2748 if ((pos + 1) > avail) {
2750 return E_BUFFER_NOT_FULL;
2753 long long result = GetUIntLength(m_pReader, pos, len);
2755 if (result < 0) // error
2756 return static_cast<long>(result);
2758 if (result > 0) // weird
2759 return E_BUFFER_NOT_FULL;
2761 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2762 return E_FILE_FORMAT_INVALID;
2764 if ((pos + len) > avail)
2765 return E_BUFFER_NOT_FULL;
2767 const long long id = ReadUInt(m_pReader, pos, len);
2769 if (id != libwebm::kMkvCluster)
2772 pos += len; // consume ID
2776 if ((pos + 1) > avail) {
2778 return E_BUFFER_NOT_FULL;
2781 result = GetUIntLength(m_pReader, pos, len);
2783 if (result < 0) // error
2784 return static_cast<long>(result);
2786 if (result > 0) // weird
2787 return E_BUFFER_NOT_FULL;
2789 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2790 return E_FILE_FORMAT_INVALID;
2792 if ((pos + len) > avail)
2793 return E_BUFFER_NOT_FULL;
2795 const long long size = ReadUInt(m_pReader, pos, len);
2797 if (size < 0) // error
2798 return static_cast<long>(size);
2800 pos += len; // consume size field
2802 const long long unknown_size = (1LL << (7 * len)) - 1;
2804 if (size == unknown_size) // TODO: should never happen
2805 return E_FILE_FORMAT_INVALID; // TODO: resolve this
2807 // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2809 if ((segment_stop >= 0) && ((pos + size) > segment_stop))
2810 return E_FILE_FORMAT_INVALID;
2812 // Pos now points to start of payload
2814 pos += size; // consume payload (that is, the current cluster)
2815 if (segment_stop >= 0 && pos > segment_stop)
2816 return E_FILE_FORMAT_INVALID;
2818 // By consuming the payload, we are assuming that the curr
2819 // cluster isn't interesting. That is, we don't bother checking
2820 // whether the payload of the curr cluster is less than what
2821 // happens to be available (obtained via IMkvReader::Length).
2822 // Presumably the caller has already dispensed with the current
2823 // cluster, and really does want the next cluster.
2826 // pos now points to just beyond the last fully-loaded cluster
2829 const long status = DoParseNext(pResult, pos, len);
2836 long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
2837 long long total, avail;
2839 long status = m_pReader->Length(&total, &avail);
2841 if (status < 0) // error
2844 assert((total < 0) || (avail <= total));
2846 const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2848 // Parse next cluster. This is strictly a parsing activity.
2849 // Creation of a new cluster object happens later, after the
2852 long long off_next = 0;
2853 long long cluster_size = -1;
2856 if ((total >= 0) && (pos >= total))
2859 if ((segment_stop >= 0) && (pos >= segment_stop))
2862 if ((pos + 1) > avail) {
2864 return E_BUFFER_NOT_FULL;
2867 long long result = GetUIntLength(m_pReader, pos, len);
2869 if (result < 0) // error
2870 return static_cast<long>(result);
2872 if (result > 0) // weird
2873 return E_BUFFER_NOT_FULL;
2875 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2876 return E_FILE_FORMAT_INVALID;
2878 if ((pos + len) > avail)
2879 return E_BUFFER_NOT_FULL;
2881 const long long idpos = pos; // absolute
2882 const long long idoff = pos - m_start; // relative
2884 const long long id = ReadID(m_pReader, idpos, len); // absolute
2886 if (id < 0) // error
2887 return static_cast<long>(id);
2889 if (id == 0) // weird
2890 return -1; // generic error
2892 pos += len; // consume ID
2896 if ((pos + 1) > avail) {
2898 return E_BUFFER_NOT_FULL;
2901 result = GetUIntLength(m_pReader, pos, len);
2903 if (result < 0) // error
2904 return static_cast<long>(result);
2906 if (result > 0) // weird
2907 return E_BUFFER_NOT_FULL;
2909 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2910 return E_FILE_FORMAT_INVALID;
2912 if ((pos + len) > avail)
2913 return E_BUFFER_NOT_FULL;
2915 const long long size = ReadUInt(m_pReader, pos, len);
2917 if (size < 0) // error
2918 return static_cast<long>(size);
2920 pos += len; // consume length of size of element
2922 // Pos now points to start of payload
2924 if (size == 0) // weird
2927 const long long unknown_size = (1LL << (7 * len)) - 1;
2929 if ((segment_stop >= 0) && (size != unknown_size) &&
2930 ((pos + size) > segment_stop)) {
2931 return E_FILE_FORMAT_INVALID;
2934 if (id == libwebm::kMkvCues) {
2935 if (size == unknown_size)
2936 return E_FILE_FORMAT_INVALID;
2938 const long long element_stop = pos + size;
2940 if ((segment_stop >= 0) && (element_stop > segment_stop))
2941 return E_FILE_FORMAT_INVALID;
2943 const long long element_start = idpos;
2944 const long long element_size = element_stop - element_start;
2946 if (m_pCues == NULL) {
2947 m_pCues = new (std::nothrow)
2948 Cues(this, pos, size, element_start, element_size);
2949 if (m_pCues == NULL)
2953 pos += size; // consume payload
2954 if (segment_stop >= 0 && pos > segment_stop)
2955 return E_FILE_FORMAT_INVALID;
2960 if (id != libwebm::kMkvCluster) { // not a Cluster ID
2961 if (size == unknown_size)
2962 return E_FILE_FORMAT_INVALID;
2964 pos += size; // consume payload
2965 if (segment_stop >= 0 && pos > segment_stop)
2966 return E_FILE_FORMAT_INVALID;
2971 // We have a cluster.
2974 if (size != unknown_size)
2975 cluster_size = size;
2980 assert(off_next > 0); // have cluster
2982 // We have parsed the next cluster.
2983 // We have not created a cluster object yet. What we need
2984 // to do now is determine whether it has already be preloaded
2985 //(in which case, an object for this cluster has already been
2986 // created), and if not, create a new cluster object.
2988 Cluster** const ii = m_clusters + m_clusterCount;
2991 Cluster** const jj = ii + m_clusterPreloadCount;
2998 //[j, jj) > pos_next
3000 Cluster** const k = i + (j - i) / 2;
3003 const Cluster* const pNext = *k;
3005 assert(pNext->m_index < 0);
3007 pos = pNext->GetPosition();
3012 else if (pos > off_next)
3016 return 0; // success
3025 status = Cluster::HasBlockEntries(this, off_next, pos_, len_);
3027 if (status < 0) { // error or underflow
3034 if (status > 0) { // means "found at least one block entry"
3035 Cluster* const pNext = Cluster::Create(this,
3041 const ptrdiff_t idx_next = i - m_clusters; // insertion position
3043 if (!PreloadCluster(pNext, idx_next)) {
3048 assert(idx_next < m_clusterSize);
3049 assert(m_clusters[idx_next] == pNext);
3052 return 0; // success
3055 // status == 0 means "no block entries found"
3057 if (cluster_size < 0) { // unknown size
3058 const long long payload_pos = pos; // absolute pos of cluster payload
3060 for (;;) { // determine cluster size
3061 if ((total >= 0) && (pos >= total))
3064 if ((segment_stop >= 0) && (pos >= segment_stop))
3065 break; // no more clusters
3069 if ((pos + 1) > avail) {
3071 return E_BUFFER_NOT_FULL;
3074 long long result = GetUIntLength(m_pReader, pos, len);
3076 if (result < 0) // error
3077 return static_cast<long>(result);
3079 if (result > 0) // weird
3080 return E_BUFFER_NOT_FULL;
3082 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3083 return E_FILE_FORMAT_INVALID;
3085 if ((pos + len) > avail)
3086 return E_BUFFER_NOT_FULL;
3088 const long long idpos = pos;
3089 const long long id = ReadID(m_pReader, idpos, len);
3091 if (id < 0) // error (or underflow)
3092 return static_cast<long>(id);
3094 // This is the distinguished set of ID's we use to determine
3095 // that we have exhausted the sub-element's inside the cluster
3096 // whose ID we parsed earlier.
3098 if (id == libwebm::kMkvCluster || id == libwebm::kMkvCues)
3101 pos += len; // consume ID (of sub-element)
3105 if ((pos + 1) > avail) {
3107 return E_BUFFER_NOT_FULL;
3110 result = GetUIntLength(m_pReader, pos, len);
3112 if (result < 0) // error
3113 return static_cast<long>(result);
3115 if (result > 0) // weird
3116 return E_BUFFER_NOT_FULL;
3118 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3119 return E_FILE_FORMAT_INVALID;
3121 if ((pos + len) > avail)
3122 return E_BUFFER_NOT_FULL;
3124 const long long size = ReadUInt(m_pReader, pos, len);
3126 if (size < 0) // error
3127 return static_cast<long>(size);
3129 pos += len; // consume size field of element
3131 // pos now points to start of sub-element's payload
3133 if (size == 0) // weird
3136 const long long unknown_size = (1LL << (7 * len)) - 1;
3138 if (size == unknown_size)
3139 return E_FILE_FORMAT_INVALID; // not allowed for sub-elements
3141 if ((segment_stop >= 0) && ((pos + size) > segment_stop)) // weird
3142 return E_FILE_FORMAT_INVALID;
3144 pos += size; // consume payload of sub-element
3145 if (segment_stop >= 0 && pos > segment_stop)
3146 return E_FILE_FORMAT_INVALID;
3147 } // determine cluster size
3149 cluster_size = pos - payload_pos;
3150 assert(cluster_size >= 0); // TODO: handle cluster_size = 0
3152 pos = payload_pos; // reset and re-parse original cluster
3155 pos += cluster_size; // consume payload
3156 if (segment_stop >= 0 && pos > segment_stop)
3157 return E_FILE_FORMAT_INVALID;
3159 return 2; // try to find a cluster that follows next
3162 const Cluster* Segment::FindCluster(long long time_ns) const {
3163 if ((m_clusters == NULL) || (m_clusterCount <= 0))
3167 Cluster* const pCluster = m_clusters[0];
3169 assert(pCluster->m_index == 0);
3171 if (time_ns <= pCluster->GetTime())
3175 // Binary search of cluster array
3178 long j = m_clusterCount;
3184 //[j, m_clusterCount) > time_ns
3186 const long k = i + (j - i) / 2;
3187 assert(k < m_clusterCount);
3189 Cluster* const pCluster = m_clusters[k];
3191 assert(pCluster->m_index == k);
3193 const long long t = pCluster->GetTime();
3205 assert(i <= m_clusterCount);
3207 const long k = i - 1;
3209 Cluster* const pCluster = m_clusters[k];
3211 assert(pCluster->m_index == k);
3212 assert(pCluster->GetTime() <= time_ns);
3217 const Tracks* Segment::GetTracks() const { return m_pTracks; }
3218 const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
3219 const Cues* Segment::GetCues() const { return m_pCues; }
3220 const Chapters* Segment::GetChapters() const { return m_pChapters; }
3221 const Tags* Segment::GetTags() const { return m_pTags; }
3222 const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
3224 long long Segment::GetDuration() const {
3226 return m_pInfo->GetDuration();
3229 Chapters::Chapters(Segment* pSegment, long long payload_start,
3230 long long payload_size, long long element_start,
3231 long long element_size)
3232 : m_pSegment(pSegment),
3233 m_start(payload_start),
3234 m_size(payload_size),
3235 m_element_start(element_start),
3236 m_element_size(element_size),
3239 m_editions_count(0) {}
3241 Chapters::~Chapters() {
3242 while (m_editions_count > 0) {
3243 Edition& e = m_editions[--m_editions_count];
3246 delete[] m_editions;
3249 long Chapters::Parse() {
3250 IMkvReader* const pReader = m_pSegment->m_pReader;
3252 long long pos = m_start; // payload start
3253 const long long stop = pos + m_size; // payload stop
3255 while (pos < stop) {
3258 long status = ParseElementHeader(pReader, pos, stop, id, size);
3260 if (status < 0) // error
3263 if (size == 0) // weird
3266 if (id == libwebm::kMkvEditionEntry) {
3267 status = ParseEdition(pos, size);
3269 if (status < 0) // error
3275 return E_FILE_FORMAT_INVALID;
3279 return E_FILE_FORMAT_INVALID;
3283 int Chapters::GetEditionCount() const { return m_editions_count; }
3285 const Chapters::Edition* Chapters::GetEdition(int idx) const {
3289 if (idx >= m_editions_count)
3292 return m_editions + idx;
3295 bool Chapters::ExpandEditionsArray() {
3296 if (m_editions_size > m_editions_count)
3297 return true; // nothing else to do
3299 const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
3301 Edition* const editions = new (std::nothrow) Edition[size];
3303 if (editions == NULL)
3306 for (int idx = 0; idx < m_editions_count; ++idx) {
3307 m_editions[idx].ShallowCopy(editions[idx]);
3310 delete[] m_editions;
3311 m_editions = editions;
3313 m_editions_size = size;
3317 long Chapters::ParseEdition(long long pos, long long size) {
3318 if (!ExpandEditionsArray())
3321 Edition& e = m_editions[m_editions_count++];
3324 return e.Parse(m_pSegment->m_pReader, pos, size);
3327 Chapters::Edition::Edition() {}
3329 Chapters::Edition::~Edition() {}
3331 int Chapters::Edition::GetAtomCount() const { return m_atoms_count; }
3333 const Chapters::Atom* Chapters::Edition::GetAtom(int index) const {
3337 if (index >= m_atoms_count)
3340 return m_atoms + index;
3343 void Chapters::Edition::Init() {
3349 void Chapters::Edition::ShallowCopy(Edition& rhs) const {
3350 rhs.m_atoms = m_atoms;
3351 rhs.m_atoms_size = m_atoms_size;
3352 rhs.m_atoms_count = m_atoms_count;
3355 void Chapters::Edition::Clear() {
3356 while (m_atoms_count > 0) {
3357 Atom& a = m_atoms[--m_atoms_count];
3367 long Chapters::Edition::Parse(IMkvReader* pReader, long long pos,
3369 const long long stop = pos + size;
3371 while (pos < stop) {
3374 long status = ParseElementHeader(pReader, pos, stop, id, size);
3376 if (status < 0) // error
3382 if (id == libwebm::kMkvChapterAtom) {
3383 status = ParseAtom(pReader, pos, size);
3385 if (status < 0) // error
3391 return E_FILE_FORMAT_INVALID;
3395 return E_FILE_FORMAT_INVALID;
3399 long Chapters::Edition::ParseAtom(IMkvReader* pReader, long long pos,
3401 if (!ExpandAtomsArray())
3404 Atom& a = m_atoms[m_atoms_count++];
3407 return a.Parse(pReader, pos, size);
3410 bool Chapters::Edition::ExpandAtomsArray() {
3411 if (m_atoms_size > m_atoms_count)
3412 return true; // nothing else to do
3414 const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
3416 Atom* const atoms = new (std::nothrow) Atom[size];
3421 for (int idx = 0; idx < m_atoms_count; ++idx) {
3422 m_atoms[idx].ShallowCopy(atoms[idx]);
3428 m_atoms_size = size;
3432 Chapters::Atom::Atom() {}
3434 Chapters::Atom::~Atom() {}
3436 unsigned long long Chapters::Atom::GetUID() const { return m_uid; }
3438 const char* Chapters::Atom::GetStringUID() const { return m_string_uid; }
3440 long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; }
3442 long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; }
3444 long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const {
3445 return GetTime(pChapters, m_start_timecode);
3448 long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const {
3449 return GetTime(pChapters, m_stop_timecode);
3452 int Chapters::Atom::GetDisplayCount() const { return m_displays_count; }
3454 const Chapters::Display* Chapters::Atom::GetDisplay(int index) const {
3458 if (index >= m_displays_count)
3461 return m_displays + index;
3464 void Chapters::Atom::Init() {
3465 m_string_uid = NULL;
3467 m_start_timecode = -1;
3468 m_stop_timecode = -1;
3471 m_displays_size = 0;
3472 m_displays_count = 0;
3475 void Chapters::Atom::ShallowCopy(Atom& rhs) const {
3476 rhs.m_string_uid = m_string_uid;
3478 rhs.m_start_timecode = m_start_timecode;
3479 rhs.m_stop_timecode = m_stop_timecode;
3481 rhs.m_displays = m_displays;
3482 rhs.m_displays_size = m_displays_size;
3483 rhs.m_displays_count = m_displays_count;
3486 void Chapters::Atom::Clear() {
3487 delete[] m_string_uid;
3488 m_string_uid = NULL;
3490 while (m_displays_count > 0) {
3491 Display& d = m_displays[--m_displays_count];
3495 delete[] m_displays;
3498 m_displays_size = 0;
3501 long Chapters::Atom::Parse(IMkvReader* pReader, long long pos, long long size) {
3502 const long long stop = pos + size;
3504 while (pos < stop) {
3507 long status = ParseElementHeader(pReader, pos, stop, id, size);
3509 if (status < 0) // error
3512 if (size == 0) // 0 length payload, skip.
3515 if (id == libwebm::kMkvChapterDisplay) {
3516 status = ParseDisplay(pReader, pos, size);
3518 if (status < 0) // error
3520 } else if (id == libwebm::kMkvChapterStringUID) {
3521 status = UnserializeString(pReader, pos, size, m_string_uid);
3523 if (status < 0) // error
3525 } else if (id == libwebm::kMkvChapterUID) {
3527 status = UnserializeInt(pReader, pos, size, val);
3529 if (status < 0) // error
3532 m_uid = static_cast<unsigned long long>(val);
3533 } else if (id == libwebm::kMkvChapterTimeStart) {
3534 const long long val = UnserializeUInt(pReader, pos, size);
3536 if (val < 0) // error
3537 return static_cast<long>(val);
3539 m_start_timecode = val;
3540 } else if (id == libwebm::kMkvChapterTimeEnd) {
3541 const long long val = UnserializeUInt(pReader, pos, size);
3543 if (val < 0) // error
3544 return static_cast<long>(val);
3546 m_stop_timecode = val;
3551 return E_FILE_FORMAT_INVALID;
3555 return E_FILE_FORMAT_INVALID;
3559 long long Chapters::Atom::GetTime(const Chapters* pChapters,
3560 long long timecode) {
3561 if (pChapters == NULL)
3564 Segment* const pSegment = pChapters->m_pSegment;
3566 if (pSegment == NULL) // weird
3569 const SegmentInfo* const pInfo = pSegment->GetInfo();
3574 const long long timecode_scale = pInfo->GetTimeCodeScale();
3576 if (timecode_scale < 1) // weird
3582 const long long result = timecode_scale * timecode;
3587 long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
3589 if (!ExpandDisplaysArray())
3592 Display& d = m_displays[m_displays_count++];
3595 return d.Parse(pReader, pos, size);
3598 bool Chapters::Atom::ExpandDisplaysArray() {
3599 if (m_displays_size > m_displays_count)
3600 return true; // nothing else to do
3602 const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
3604 Display* const displays = new (std::nothrow) Display[size];
3606 if (displays == NULL)
3609 for (int idx = 0; idx < m_displays_count; ++idx) {
3610 m_displays[idx].ShallowCopy(displays[idx]);
3613 delete[] m_displays;
3614 m_displays = displays;
3616 m_displays_size = size;
3620 Chapters::Display::Display() {}
3622 Chapters::Display::~Display() {}
3624 const char* Chapters::Display::GetString() const { return m_string; }
3626 const char* Chapters::Display::GetLanguage() const { return m_language; }
3628 const char* Chapters::Display::GetCountry() const { return m_country; }
3630 void Chapters::Display::Init() {
3636 void Chapters::Display::ShallowCopy(Display& rhs) const {
3637 rhs.m_string = m_string;
3638 rhs.m_language = m_language;
3639 rhs.m_country = m_country;
3642 void Chapters::Display::Clear() {
3646 delete[] m_language;
3653 long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
3655 const long long stop = pos + size;
3657 while (pos < stop) {
3660 long status = ParseElementHeader(pReader, pos, stop, id, size);
3662 if (status < 0) // error
3665 if (size == 0) // No payload.
3668 if (id == libwebm::kMkvChapString) {
3669 status = UnserializeString(pReader, pos, size, m_string);
3673 } else if (id == libwebm::kMkvChapLanguage) {
3674 status = UnserializeString(pReader, pos, size, m_language);
3678 } else if (id == libwebm::kMkvChapCountry) {
3679 status = UnserializeString(pReader, pos, size, m_country);
3687 return E_FILE_FORMAT_INVALID;
3691 return E_FILE_FORMAT_INVALID;
3695 Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size,
3696 long long element_start, long long element_size)
3697 : m_pSegment(pSegment),
3698 m_start(payload_start),
3699 m_size(payload_size),
3700 m_element_start(element_start),
3701 m_element_size(element_size),
3707 while (m_tags_count > 0) {
3708 Tag& t = m_tags[--m_tags_count];
3714 long Tags::Parse() {
3715 IMkvReader* const pReader = m_pSegment->m_pReader;
3717 long long pos = m_start; // payload start
3718 const long long stop = pos + m_size; // payload stop
3720 while (pos < stop) {
3723 long status = ParseElementHeader(pReader, pos, stop, id, size);
3728 if (size == 0) // 0 length tag, read another
3731 if (id == libwebm::kMkvTag) {
3732 status = ParseTag(pos, size);
3740 return E_FILE_FORMAT_INVALID;
3744 return E_FILE_FORMAT_INVALID;
3749 int Tags::GetTagCount() const { return m_tags_count; }
3751 const Tags::Tag* Tags::GetTag(int idx) const {
3755 if (idx >= m_tags_count)
3758 return m_tags + idx;
3761 bool Tags::ExpandTagsArray() {
3762 if (m_tags_size > m_tags_count)
3763 return true; // nothing else to do
3765 const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
3767 Tag* const tags = new (std::nothrow) Tag[size];
3772 for (int idx = 0; idx < m_tags_count; ++idx) {
3773 m_tags[idx].ShallowCopy(tags[idx]);
3783 long Tags::ParseTag(long long pos, long long size) {
3784 if (!ExpandTagsArray())
3787 Tag& t = m_tags[m_tags_count++];
3790 return t.Parse(m_pSegment->m_pReader, pos, size);
3795 Tags::Tag::~Tag() {}
3797 int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
3799 const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
3803 if (index >= m_simple_tags_count)
3806 return m_simple_tags + index;
3809 void Tags::Tag::Init() {
3810 m_simple_tags = NULL;
3811 m_simple_tags_size = 0;
3812 m_simple_tags_count = 0;
3815 void Tags::Tag::ShallowCopy(Tag& rhs) const {
3816 rhs.m_simple_tags = m_simple_tags;
3817 rhs.m_simple_tags_size = m_simple_tags_size;
3818 rhs.m_simple_tags_count = m_simple_tags_count;
3821 void Tags::Tag::Clear() {
3822 while (m_simple_tags_count > 0) {
3823 SimpleTag& d = m_simple_tags[--m_simple_tags_count];
3827 delete[] m_simple_tags;
3828 m_simple_tags = NULL;
3830 m_simple_tags_size = 0;
3833 long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) {
3834 const long long stop = pos + size;
3836 while (pos < stop) {
3839 long status = ParseElementHeader(pReader, pos, stop, id, size);
3844 if (size == 0) // 0 length tag, read another
3847 if (id == libwebm::kMkvSimpleTag) {
3848 status = ParseSimpleTag(pReader, pos, size);
3856 return E_FILE_FORMAT_INVALID;
3860 return E_FILE_FORMAT_INVALID;
3864 long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
3866 if (!ExpandSimpleTagsArray())
3869 SimpleTag& st = m_simple_tags[m_simple_tags_count++];
3872 return st.Parse(pReader, pos, size);
3875 bool Tags::Tag::ExpandSimpleTagsArray() {
3876 if (m_simple_tags_size > m_simple_tags_count)
3877 return true; // nothing else to do
3879 const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
3881 SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
3883 if (displays == NULL)
3886 for (int idx = 0; idx < m_simple_tags_count; ++idx) {
3887 m_simple_tags[idx].ShallowCopy(displays[idx]);
3890 delete[] m_simple_tags;
3891 m_simple_tags = displays;
3893 m_simple_tags_size = size;
3897 Tags::SimpleTag::SimpleTag() {}
3899 Tags::SimpleTag::~SimpleTag() {}
3901 const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
3903 const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
3905 void Tags::SimpleTag::Init() {
3907 m_tag_string = NULL;
3910 void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
3911 rhs.m_tag_name = m_tag_name;
3912 rhs.m_tag_string = m_tag_string;
3915 void Tags::SimpleTag::Clear() {
3916 delete[] m_tag_name;
3919 delete[] m_tag_string;
3920 m_tag_string = NULL;
3923 long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos,
3925 const long long stop = pos + size;
3927 while (pos < stop) {
3930 long status = ParseElementHeader(pReader, pos, stop, id, size);
3932 if (status < 0) // error
3935 if (size == 0) // weird
3938 if (id == libwebm::kMkvTagName) {
3939 status = UnserializeString(pReader, pos, size, m_tag_name);
3943 } else if (id == libwebm::kMkvTagString) {
3944 status = UnserializeString(pReader, pos, size, m_tag_string);
3952 return E_FILE_FORMAT_INVALID;
3956 return E_FILE_FORMAT_INVALID;
3960 SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
3961 long long element_start, long long element_size)
3962 : m_pSegment(pSegment),
3965 m_element_start(element_start),
3966 m_element_size(element_size),
3967 m_pMuxingAppAsUTF8(NULL),
3968 m_pWritingAppAsUTF8(NULL),
3969 m_pTitleAsUTF8(NULL) {}
3971 SegmentInfo::~SegmentInfo() {
3972 delete[] m_pMuxingAppAsUTF8;
3973 m_pMuxingAppAsUTF8 = NULL;
3975 delete[] m_pWritingAppAsUTF8;
3976 m_pWritingAppAsUTF8 = NULL;
3978 delete[] m_pTitleAsUTF8;
3979 m_pTitleAsUTF8 = NULL;
3982 long SegmentInfo::Parse() {
3983 assert(m_pMuxingAppAsUTF8 == NULL);
3984 assert(m_pWritingAppAsUTF8 == NULL);
3985 assert(m_pTitleAsUTF8 == NULL);
3987 IMkvReader* const pReader = m_pSegment->m_pReader;
3989 long long pos = m_start;
3990 const long long stop = m_start + m_size;
3992 m_timecodeScale = 1000000;
3995 while (pos < stop) {
3998 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4000 if (status < 0) // error
4003 if (id == libwebm::kMkvTimecodeScale) {
4004 m_timecodeScale = UnserializeUInt(pReader, pos, size);
4006 if (m_timecodeScale <= 0)
4007 return E_FILE_FORMAT_INVALID;
4008 } else if (id == libwebm::kMkvDuration) {
4009 const long status = UnserializeFloat(pReader, pos, size, m_duration);
4015 return E_FILE_FORMAT_INVALID;
4016 } else if (id == libwebm::kMkvMuxingApp) {
4018 UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8);
4022 } else if (id == libwebm::kMkvWritingApp) {
4024 UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8);
4028 } else if (id == libwebm::kMkvTitle) {
4029 const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8);
4038 return E_FILE_FORMAT_INVALID;
4041 const double rollover_check = m_duration * m_timecodeScale;
4042 if (rollover_check > LLONG_MAX)
4043 return E_FILE_FORMAT_INVALID;
4046 return E_FILE_FORMAT_INVALID;
4051 long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; }
4053 long long SegmentInfo::GetDuration() const {
4057 assert(m_timecodeScale >= 1);
4059 const double dd = double(m_duration) * double(m_timecodeScale);
4060 const long long d = static_cast<long long>(dd);
4065 const char* SegmentInfo::GetMuxingAppAsUTF8() const {
4066 return m_pMuxingAppAsUTF8;
4069 const char* SegmentInfo::GetWritingAppAsUTF8() const {
4070 return m_pWritingAppAsUTF8;
4073 const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; }
4075 ///////////////////////////////////////////////////////////////
4076 // ContentEncoding element
4077 ContentEncoding::ContentCompression::ContentCompression()
4078 : algo(0), settings(NULL), settings_len(0) {}
4080 ContentEncoding::ContentCompression::~ContentCompression() {
4084 ContentEncoding::ContentEncryption::ContentEncryption()
4095 ContentEncoding::ContentEncryption::~ContentEncryption() {
4098 delete[] sig_key_id;
4101 ContentEncoding::ContentEncoding()
4102 : compression_entries_(NULL),
4103 compression_entries_end_(NULL),
4104 encryption_entries_(NULL),
4105 encryption_entries_end_(NULL),
4108 encoding_type_(0) {}
4110 ContentEncoding::~ContentEncoding() {
4111 ContentCompression** comp_i = compression_entries_;
4112 ContentCompression** const comp_j = compression_entries_end_;
4114 while (comp_i != comp_j) {
4115 ContentCompression* const comp = *comp_i++;
4119 delete[] compression_entries_;
4121 ContentEncryption** enc_i = encryption_entries_;
4122 ContentEncryption** const enc_j = encryption_entries_end_;
4124 while (enc_i != enc_j) {
4125 ContentEncryption* const enc = *enc_i++;
4129 delete[] encryption_entries_;
4132 const ContentEncoding::ContentCompression*
4133 ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
4134 const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4137 if (idx >= static_cast<unsigned long>(count))
4140 return compression_entries_[idx];
4143 unsigned long ContentEncoding::GetCompressionCount() const {
4144 const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4147 return static_cast<unsigned long>(count);
4150 const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex(
4151 unsigned long idx) const {
4152 const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4155 if (idx >= static_cast<unsigned long>(count))
4158 return encryption_entries_[idx];
4161 unsigned long ContentEncoding::GetEncryptionCount() const {
4162 const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4165 return static_cast<unsigned long>(count);
4168 long ContentEncoding::ParseContentEncAESSettingsEntry(
4169 long long start, long long size, IMkvReader* pReader,
4170 ContentEncAESSettings* aes) {
4174 long long pos = start;
4175 const long long stop = start + size;
4177 while (pos < stop) {
4179 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4180 if (status < 0) // error
4183 if (id == libwebm::kMkvAESSettingsCipherMode) {
4184 aes->cipher_mode = UnserializeUInt(pReader, pos, size);
4185 if (aes->cipher_mode != 1)
4186 return E_FILE_FORMAT_INVALID;
4189 pos += size; // consume payload
4191 return E_FILE_FORMAT_INVALID;
4197 long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
4198 IMkvReader* pReader) {
4201 long long pos = start;
4202 const long long stop = start + size;
4204 // Count ContentCompression and ContentEncryption elements.
4205 int compression_count = 0;
4206 int encryption_count = 0;
4208 while (pos < stop) {
4210 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4211 if (status < 0) // error
4214 if (id == libwebm::kMkvContentCompression)
4215 ++compression_count;
4217 if (id == libwebm::kMkvContentEncryption)
4220 pos += size; // consume payload
4222 return E_FILE_FORMAT_INVALID;
4225 if (compression_count <= 0 && encryption_count <= 0)
4228 if (compression_count > 0) {
4229 compression_entries_ =
4230 new (std::nothrow) ContentCompression*[compression_count];
4231 if (!compression_entries_)
4233 compression_entries_end_ = compression_entries_;
4236 if (encryption_count > 0) {
4237 encryption_entries_ =
4238 new (std::nothrow) ContentEncryption*[encryption_count];
4239 if (!encryption_entries_) {
4240 delete[] compression_entries_;
4243 encryption_entries_end_ = encryption_entries_;
4247 while (pos < stop) {
4249 long status = ParseElementHeader(pReader, pos, stop, id, size);
4250 if (status < 0) // error
4253 if (id == libwebm::kMkvContentEncodingOrder) {
4254 encoding_order_ = UnserializeUInt(pReader, pos, size);
4255 } else if (id == libwebm::kMkvContentEncodingScope) {
4256 encoding_scope_ = UnserializeUInt(pReader, pos, size);
4257 if (encoding_scope_ < 1)
4259 } else if (id == libwebm::kMkvContentEncodingType) {
4260 encoding_type_ = UnserializeUInt(pReader, pos, size);
4261 } else if (id == libwebm::kMkvContentCompression) {
4262 ContentCompression* const compression =
4263 new (std::nothrow) ContentCompression();
4267 status = ParseCompressionEntry(pos, size, pReader, compression);
4272 *compression_entries_end_++ = compression;
4273 } else if (id == libwebm::kMkvContentEncryption) {
4274 ContentEncryption* const encryption =
4275 new (std::nothrow) ContentEncryption();
4279 status = ParseEncryptionEntry(pos, size, pReader, encryption);
4284 *encryption_entries_end_++ = encryption;
4287 pos += size; // consume payload
4289 return E_FILE_FORMAT_INVALID;
4293 return E_FILE_FORMAT_INVALID;
4297 long ContentEncoding::ParseCompressionEntry(long long start, long long size,
4298 IMkvReader* pReader,
4299 ContentCompression* compression) {
4301 assert(compression);
4303 long long pos = start;
4304 const long long stop = start + size;
4308 while (pos < stop) {
4310 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4311 if (status < 0) // error
4314 if (id == libwebm::kMkvContentCompAlgo) {
4315 long long algo = UnserializeUInt(pReader, pos, size);
4317 return E_FILE_FORMAT_INVALID;
4318 compression->algo = algo;
4320 } else if (id == libwebm::kMkvContentCompSettings) {
4322 return E_FILE_FORMAT_INVALID;
4324 const size_t buflen = static_cast<size_t>(size);
4325 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4329 const int read_status =
4330 pReader->Read(pos, static_cast<long>(buflen), buf);
4336 compression->settings = buf;
4337 compression->settings_len = buflen;
4340 pos += size; // consume payload
4342 return E_FILE_FORMAT_INVALID;
4345 // ContentCompAlgo is mandatory
4347 return E_FILE_FORMAT_INVALID;
4352 long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
4353 IMkvReader* pReader,
4354 ContentEncryption* encryption) {
4358 long long pos = start;
4359 const long long stop = start + size;
4361 while (pos < stop) {
4363 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4364 if (status < 0) // error
4367 if (id == libwebm::kMkvContentEncAlgo) {
4368 encryption->algo = UnserializeUInt(pReader, pos, size);
4369 if (encryption->algo != 5)
4370 return E_FILE_FORMAT_INVALID;
4371 } else if (id == libwebm::kMkvContentEncKeyID) {
4372 delete[] encryption->key_id;
4373 encryption->key_id = NULL;
4374 encryption->key_id_len = 0;
4377 return E_FILE_FORMAT_INVALID;
4379 const size_t buflen = static_cast<size_t>(size);
4380 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4384 const int read_status =
4385 pReader->Read(pos, static_cast<long>(buflen), buf);
4391 encryption->key_id = buf;
4392 encryption->key_id_len = buflen;
4393 } else if (id == libwebm::kMkvContentSignature) {
4394 delete[] encryption->signature;
4395 encryption->signature = NULL;
4396 encryption->signature_len = 0;
4399 return E_FILE_FORMAT_INVALID;
4401 const size_t buflen = static_cast<size_t>(size);
4402 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4406 const int read_status =
4407 pReader->Read(pos, static_cast<long>(buflen), buf);
4413 encryption->signature = buf;
4414 encryption->signature_len = buflen;
4415 } else if (id == libwebm::kMkvContentSigKeyID) {
4416 delete[] encryption->sig_key_id;
4417 encryption->sig_key_id = NULL;
4418 encryption->sig_key_id_len = 0;
4421 return E_FILE_FORMAT_INVALID;
4423 const size_t buflen = static_cast<size_t>(size);
4424 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4428 const int read_status =
4429 pReader->Read(pos, static_cast<long>(buflen), buf);
4435 encryption->sig_key_id = buf;
4436 encryption->sig_key_id_len = buflen;
4437 } else if (id == libwebm::kMkvContentSigAlgo) {
4438 encryption->sig_algo = UnserializeUInt(pReader, pos, size);
4439 } else if (id == libwebm::kMkvContentSigHashAlgo) {
4440 encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
4441 } else if (id == libwebm::kMkvContentEncAESSettings) {
4442 const long status = ParseContentEncAESSettingsEntry(
4443 pos, size, pReader, &encryption->aes_settings);
4448 pos += size; // consume payload
4450 return E_FILE_FORMAT_INVALID;
4456 Track::Track(Segment* pSegment, long long element_start, long long element_size)
4457 : m_pSegment(pSegment),
4458 m_element_start(element_start),
4459 m_element_size(element_size),
4460 content_encoding_entries_(NULL),
4461 content_encoding_entries_end_(NULL) {}
4464 Info& info = const_cast<Info&>(m_info);
4467 ContentEncoding** i = content_encoding_entries_;
4468 ContentEncoding** const j = content_encoding_entries_end_;
4471 ContentEncoding* const encoding = *i++;
4475 delete[] content_encoding_entries_;
4478 long Track::Create(Segment* pSegment, const Info& info, long long element_start,
4479 long long element_size, Track*& pResult) {
4483 Track* const pTrack =
4484 new (std::nothrow) Track(pSegment, element_start, element_size);
4487 return -1; // generic error
4489 const int status = info.Copy(pTrack->m_info);
4491 if (status) { // error
4497 return 0; // success
4508 codecNameAsUTF8(NULL),
4510 codecPrivateSize(0),
4513 Track::Info::~Info() { Clear(); }
4515 void Track::Info::Clear() {
4516 delete[] nameAsUTF8;
4525 delete[] codecPrivate;
4526 codecPrivate = NULL;
4527 codecPrivateSize = 0;
4529 delete[] codecNameAsUTF8;
4530 codecNameAsUTF8 = NULL;
4533 int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
4534 if (str == static_cast<char * Info::*>(NULL))
4537 char*& dst = dst_.*str;
4539 if (dst) // should be NULL already
4542 const char* const src = this->*str;
4547 const size_t len = strlen(src);
4549 dst = SafeArrayAlloc<char>(1, len + 1);
4559 int Track::Info::Copy(Info& dst) const {
4564 dst.number = number;
4565 dst.defaultDuration = defaultDuration;
4566 dst.codecDelay = codecDelay;
4567 dst.seekPreRoll = seekPreRoll;
4569 dst.lacing = lacing;
4570 dst.settings = settings;
4572 // We now copy the string member variables from src to dst.
4573 // This involves memory allocation so in principle the operation
4574 // can fail (indeed, that's why we have Info::Copy), so we must
4575 // report this to the caller. An error return from this function
4576 // therefore implies that the copy was only partially successful.
4578 if (int status = CopyStr(&Info::nameAsUTF8, dst))
4581 if (int status = CopyStr(&Info::language, dst))
4584 if (int status = CopyStr(&Info::codecId, dst))
4587 if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
4590 if (codecPrivateSize > 0) {
4591 if (codecPrivate == NULL)
4594 if (dst.codecPrivate)
4597 if (dst.codecPrivateSize != 0)
4600 dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
4602 if (dst.codecPrivate == NULL)
4605 memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
4606 dst.codecPrivateSize = codecPrivateSize;
4612 const BlockEntry* Track::GetEOS() const { return &m_eos; }
4614 long Track::GetType() const { return m_info.type; }
4616 long Track::GetNumber() const { return m_info.number; }
4618 unsigned long long Track::GetUid() const { return m_info.uid; }
4620 const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
4622 const char* Track::GetLanguage() const { return m_info.language; }
4624 const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
4626 const char* Track::GetCodecId() const { return m_info.codecId; }
4628 const unsigned char* Track::GetCodecPrivate(size_t& size) const {
4629 size = m_info.codecPrivateSize;
4630 return m_info.codecPrivate;
4633 bool Track::GetLacing() const { return m_info.lacing; }
4635 unsigned long long Track::GetDefaultDuration() const {
4636 return m_info.defaultDuration;
4639 unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
4641 unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
4643 long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
4644 const Cluster* pCluster = m_pSegment->GetFirst();
4647 if (pCluster == NULL) {
4648 pBlockEntry = GetEOS();
4652 if (pCluster->EOS()) {
4653 if (m_pSegment->DoneParsing()) {
4654 pBlockEntry = GetEOS();
4659 return E_BUFFER_NOT_FULL;
4662 long status = pCluster->GetFirst(pBlockEntry);
4664 if (status < 0) // error
4667 if (pBlockEntry == 0) { // empty cluster
4668 pCluster = m_pSegment->GetNext(pCluster);
4673 const Block* const pBlock = pBlockEntry->GetBlock();
4676 const long long tn = pBlock->GetTrackNumber();
4678 if ((tn == m_info.number) && VetEntry(pBlockEntry))
4681 const BlockEntry* pNextEntry;
4683 status = pCluster->GetNext(pBlockEntry, pNextEntry);
4685 if (status < 0) // error
4688 if (pNextEntry == 0)
4691 pBlockEntry = pNextEntry;
4699 pCluster = m_pSegment->GetNext(pCluster);
4702 // NOTE: if we get here, it means that we didn't find a block with
4703 // a matching track number. We interpret that as an error (which
4704 // might be too conservative).
4706 pBlockEntry = GetEOS(); // so we can return a non-NULL value
4710 long Track::GetNext(const BlockEntry* pCurrEntry,
4711 const BlockEntry*& pNextEntry) const {
4713 assert(!pCurrEntry->EOS()); //?
4715 const Block* const pCurrBlock = pCurrEntry->GetBlock();
4716 assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
4717 if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
4720 const Cluster* pCluster = pCurrEntry->GetCluster();
4722 assert(!pCluster->EOS());
4724 long status = pCluster->GetNext(pCurrEntry, pNextEntry);
4726 if (status < 0) // error
4730 while (pNextEntry) {
4731 const Block* const pNextBlock = pNextEntry->GetBlock();
4734 if (pNextBlock->GetTrackNumber() == m_info.number)
4737 pCurrEntry = pNextEntry;
4739 status = pCluster->GetNext(pCurrEntry, pNextEntry);
4741 if (status < 0) // error
4745 pCluster = m_pSegment->GetNext(pCluster);
4747 if (pCluster == NULL) {
4748 pNextEntry = GetEOS();
4752 if (pCluster->EOS()) {
4753 if (m_pSegment->DoneParsing()) {
4754 pNextEntry = GetEOS();
4758 // TODO: there is a potential O(n^2) problem here: we tell the
4759 // caller to (pre)load another cluster, which he does, but then he
4760 // calls GetNext again, which repeats the same search. This is
4761 // a pathological case, since the only way it can happen is if
4762 // there exists a long sequence of clusters none of which contain a
4763 // block from this track. One way around this problem is for the
4764 // caller to be smarter when he loads another cluster: don't call
4765 // us back until you have a cluster that contains a block from this
4766 // track. (Of course, that's not cheap either, since our caller
4767 // would have to scan the each cluster as it's loaded, so that
4768 // would just push back the problem.)
4771 return E_BUFFER_NOT_FULL;
4774 status = pCluster->GetFirst(pNextEntry);
4776 if (status < 0) // error
4779 if (pNextEntry == NULL) // empty cluster
4788 // NOTE: if we get here, it means that we didn't find a block with
4789 // a matching track number after lots of searching, so we give
4792 pNextEntry = GetEOS(); // so we can return a non-NULL value
4796 bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
4797 assert(pBlockEntry);
4798 const Block* const pBlock = pBlockEntry->GetBlock();
4800 assert(pBlock->GetTrackNumber() == m_info.number);
4801 if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
4804 // This function is used during a seek to determine whether the
4805 // frame is a valid seek target. This default function simply
4806 // returns true, which means all frames are valid seek targets.
4807 // It gets overridden by the VideoTrack class, because only video
4808 // keyframes can be used as seek target.
4813 long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
4814 const long status = GetFirst(pResult);
4816 if (status < 0) // buffer underflow, etc
4824 const Cluster* pCluster = pResult->GetCluster();
4826 assert(pCluster->GetIndex() >= 0);
4828 if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
4831 Cluster** const clusters = m_pSegment->m_clusters;
4834 const long count = m_pSegment->GetCount(); // loaded only, not preloaded
4837 Cluster** const i = clusters + pCluster->GetIndex();
4839 assert(*i == pCluster);
4840 assert(pCluster->GetTime() <= time_ns);
4842 Cluster** const j = clusters + count;
4849 //[i, lo) <= time_ns
4853 Cluster** const mid = lo + (hi - lo) / 2;
4858 assert(pCluster->GetIndex() >= 0);
4859 assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
4861 const long long t = pCluster->GetTime();
4878 assert(pCluster->GetTime() <= time_ns);
4880 pResult = pCluster->GetEntry(this);
4882 if ((pResult != 0) && !pResult->EOS())
4885 // landed on empty cluster (no entries)
4888 pResult = GetEOS(); // weird
4892 const ContentEncoding* Track::GetContentEncodingByIndex(
4893 unsigned long idx) const {
4894 const ptrdiff_t count =
4895 content_encoding_entries_end_ - content_encoding_entries_;
4898 if (idx >= static_cast<unsigned long>(count))
4901 return content_encoding_entries_[idx];
4904 unsigned long Track::GetContentEncodingCount() const {
4905 const ptrdiff_t count =
4906 content_encoding_entries_end_ - content_encoding_entries_;
4909 return static_cast<unsigned long>(count);
4912 long Track::ParseContentEncodingsEntry(long long start, long long size) {
4913 IMkvReader* const pReader = m_pSegment->m_pReader;
4916 long long pos = start;
4917 const long long stop = start + size;
4919 // Count ContentEncoding elements.
4921 while (pos < stop) {
4923 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4924 if (status < 0) // error
4927 // pos now designates start of element
4928 if (id == libwebm::kMkvContentEncoding)
4931 pos += size; // consume payload
4933 return E_FILE_FORMAT_INVALID;
4939 content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
4940 if (!content_encoding_entries_)
4943 content_encoding_entries_end_ = content_encoding_entries_;
4946 while (pos < stop) {
4948 long status = ParseElementHeader(pReader, pos, stop, id, size);
4949 if (status < 0) // error
4952 // pos now designates start of element
4953 if (id == libwebm::kMkvContentEncoding) {
4954 ContentEncoding* const content_encoding =
4955 new (std::nothrow) ContentEncoding();
4956 if (!content_encoding)
4959 status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
4961 delete content_encoding;
4965 *content_encoding_entries_end_++ = content_encoding;
4968 pos += size; // consume payload
4970 return E_FILE_FORMAT_INVALID;
4974 return E_FILE_FORMAT_INVALID;
4979 Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
4981 BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
4983 const Block* Track::EOSBlock::GetBlock() const { return NULL; }
4985 bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos,
4986 long long value_size, bool is_x,
4987 PrimaryChromaticity** chromaticity) {
4991 std::auto_ptr<PrimaryChromaticity> chromaticity_ptr;
4993 if (!*chromaticity) {
4994 chromaticity_ptr.reset(new PrimaryChromaticity());
4996 chromaticity_ptr.reset(*chromaticity);
4999 if (!chromaticity_ptr.get())
5002 double* value = is_x ? &chromaticity_ptr->x : &chromaticity_ptr->y;
5004 const long long value_parse_status =
5005 UnserializeFloat(reader, read_pos, value_size, *value);
5007 if (value_parse_status < 0 || *value < 0.0 || *value > 1.0)
5010 *chromaticity = chromaticity_ptr.release();
5014 bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
5015 long long mm_size, MasteringMetadata** mm) {
5019 std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
5023 const long long mm_end = mm_start + mm_size;
5024 long long read_pos = mm_start;
5026 while (read_pos < mm_end) {
5027 long long child_id = 0;
5028 long long child_size = 0;
5030 const long long status =
5031 ParseElementHeader(reader, read_pos, mm_end, child_id, child_size);
5035 if (child_id == libwebm::kMkvLuminanceMax) {
5036 const long long value_parse_status =
5037 UnserializeFloat(reader, read_pos, child_size, mm_ptr->luminance_max);
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) {
5043 const long long value_parse_status =
5044 UnserializeFloat(reader, read_pos, child_size, mm_ptr->luminance_min);
5045 if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 ||
5046 mm_ptr->luminance_min > 999.9999) {
5051 PrimaryChromaticity** chromaticity;
5053 case libwebm::kMkvPrimaryRChromaticityX:
5054 case libwebm::kMkvPrimaryRChromaticityY:
5055 is_x = child_id == libwebm::kMkvPrimaryRChromaticityX;
5056 chromaticity = &mm_ptr->r;
5058 case libwebm::kMkvPrimaryGChromaticityX:
5059 case libwebm::kMkvPrimaryGChromaticityY:
5060 is_x = child_id == libwebm::kMkvPrimaryGChromaticityX;
5061 chromaticity = &mm_ptr->g;
5063 case libwebm::kMkvPrimaryBChromaticityX:
5064 case libwebm::kMkvPrimaryBChromaticityY:
5065 is_x = child_id == libwebm::kMkvPrimaryBChromaticityX;
5066 chromaticity = &mm_ptr->b;
5068 case libwebm::kMkvWhitePointChromaticityX:
5069 case libwebm::kMkvWhitePointChromaticityY:
5070 is_x = child_id == libwebm::kMkvWhitePointChromaticityX;
5071 chromaticity = &mm_ptr->white_point;
5076 const bool value_parse_status = PrimaryChromaticity::Parse(
5077 reader, read_pos, child_size, is_x, chromaticity);
5078 if (!value_parse_status)
5082 read_pos += child_size;
5083 if (read_pos > mm_end)
5087 *mm = mm_ptr.release();
5091 bool Colour::Parse(IMkvReader* reader, long long colour_start,
5092 long long colour_size, Colour** colour) {
5093 if (!reader || *colour)
5096 std::auto_ptr<Colour> colour_ptr(new Colour());
5097 if (!colour_ptr.get())
5100 const long long colour_end = colour_start + colour_size;
5101 long long read_pos = colour_start;
5103 while (read_pos < colour_end) {
5104 long long child_id = 0;
5105 long long child_size = 0;
5108 ParseElementHeader(reader, read_pos, colour_end, child_id, child_size);
5112 if (child_id == libwebm::kMkvMatrixCoefficients) {
5113 colour_ptr->matrix_coefficients =
5114 UnserializeUInt(reader, read_pos, child_size);
5115 if (colour_ptr->matrix_coefficients < 0)
5117 } else if (child_id == libwebm::kMkvBitsPerChannel) {
5118 colour_ptr->bits_per_channel =
5119 UnserializeUInt(reader, read_pos, child_size);
5120 if (colour_ptr->bits_per_channel < 0)
5122 } else if (child_id == libwebm::kMkvChromaSubsamplingHorz) {
5123 colour_ptr->chroma_subsampling_horz =
5124 UnserializeUInt(reader, read_pos, child_size);
5125 if (colour_ptr->chroma_subsampling_horz < 0)
5127 } else if (child_id == libwebm::kMkvChromaSubsamplingVert) {
5128 colour_ptr->chroma_subsampling_vert =
5129 UnserializeUInt(reader, read_pos, child_size);
5130 if (colour_ptr->chroma_subsampling_vert < 0)
5132 } else if (child_id == libwebm::kMkvCbSubsamplingHorz) {
5133 colour_ptr->cb_subsampling_horz =
5134 UnserializeUInt(reader, read_pos, child_size);
5135 if (colour_ptr->cb_subsampling_horz < 0)
5137 } else if (child_id == libwebm::kMkvCbSubsamplingVert) {
5138 colour_ptr->cb_subsampling_vert =
5139 UnserializeUInt(reader, read_pos, child_size);
5140 if (colour_ptr->cb_subsampling_vert < 0)
5142 } else if (child_id == libwebm::kMkvChromaSitingHorz) {
5143 colour_ptr->chroma_siting_horz =
5144 UnserializeUInt(reader, read_pos, child_size);
5145 if (colour_ptr->chroma_siting_horz < 0)
5147 } else if (child_id == libwebm::kMkvChromaSitingVert) {
5148 colour_ptr->chroma_siting_vert =
5149 UnserializeUInt(reader, read_pos, child_size);
5150 if (colour_ptr->chroma_siting_vert < 0)
5152 } else if (child_id == libwebm::kMkvRange) {
5153 colour_ptr->range = UnserializeUInt(reader, read_pos, child_size);
5154 if (colour_ptr->range < 0)
5156 } else if (child_id == libwebm::kMkvTransferCharacteristics) {
5157 colour_ptr->transfer_characteristics =
5158 UnserializeUInt(reader, read_pos, child_size);
5159 if (colour_ptr->transfer_characteristics < 0)
5161 } else if (child_id == libwebm::kMkvPrimaries) {
5162 colour_ptr->primaries = UnserializeUInt(reader, read_pos, child_size);
5163 if (colour_ptr->primaries < 0)
5165 } else if (child_id == libwebm::kMkvMaxCLL) {
5166 colour_ptr->max_cll = UnserializeUInt(reader, read_pos, child_size);
5167 if (colour_ptr->max_cll < 0)
5169 } else if (child_id == libwebm::kMkvMaxFALL) {
5170 colour_ptr->max_fall = UnserializeUInt(reader, read_pos, child_size);
5171 if (colour_ptr->max_fall < 0)
5173 } else if (child_id == libwebm::kMkvMasteringMetadata) {
5174 if (!MasteringMetadata::Parse(reader, read_pos, child_size,
5175 &colour_ptr->mastering_metadata))
5181 read_pos += child_size;
5182 if (read_pos > colour_end)
5185 *colour = colour_ptr.release();
5189 VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
5190 long long element_size)
5191 : Track(pSegment, element_start, element_size), m_colour(NULL) {}
5193 VideoTrack::~VideoTrack() { delete m_colour; }
5195 long VideoTrack::Parse(Segment* pSegment, const Info& info,
5196 long long element_start, long long element_size,
5197 VideoTrack*& pResult) {
5201 if (info.type != Track::kVideo)
5204 long long width = 0;
5205 long long height = 0;
5206 long long display_width = 0;
5207 long long display_height = 0;
5208 long long display_unit = 0;
5209 long long stereo_mode = 0;
5213 IMkvReader* const pReader = pSegment->m_pReader;
5215 const Settings& s = info.settings;
5216 assert(s.start >= 0);
5217 assert(s.size >= 0);
5219 long long pos = s.start;
5222 const long long stop = pos + s.size;
5224 Colour* colour = NULL;
5226 while (pos < stop) {
5229 const long status = ParseElementHeader(pReader, pos, stop, id, size);
5231 if (status < 0) // error
5234 if (id == libwebm::kMkvPixelWidth) {
5235 width = UnserializeUInt(pReader, pos, size);
5238 return E_FILE_FORMAT_INVALID;
5239 } else if (id == libwebm::kMkvPixelHeight) {
5240 height = UnserializeUInt(pReader, pos, size);
5243 return E_FILE_FORMAT_INVALID;
5244 } else if (id == libwebm::kMkvDisplayWidth) {
5245 display_width = UnserializeUInt(pReader, pos, size);
5247 if (display_width <= 0)
5248 return E_FILE_FORMAT_INVALID;
5249 } else if (id == libwebm::kMkvDisplayHeight) {
5250 display_height = UnserializeUInt(pReader, pos, size);
5252 if (display_height <= 0)
5253 return E_FILE_FORMAT_INVALID;
5254 } else if (id == libwebm::kMkvDisplayUnit) {
5255 display_unit = UnserializeUInt(pReader, pos, size);
5257 if (display_unit < 0)
5258 return E_FILE_FORMAT_INVALID;
5259 } else if (id == libwebm::kMkvStereoMode) {
5260 stereo_mode = UnserializeUInt(pReader, pos, size);
5262 if (stereo_mode < 0)
5263 return E_FILE_FORMAT_INVALID;
5264 } else if (id == libwebm::kMkvFrameRate) {
5265 const long status = UnserializeFloat(pReader, pos, size, rate);
5271 return E_FILE_FORMAT_INVALID;
5272 } else if (id == libwebm::kMkvColour) {
5273 if (!Colour::Parse(pReader, pos, size, &colour))
5274 return E_FILE_FORMAT_INVALID;
5277 pos += size; // consume payload
5279 return E_FILE_FORMAT_INVALID;
5283 return E_FILE_FORMAT_INVALID;
5285 VideoTrack* const pTrack =
5286 new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
5289 return -1; // generic error
5291 const int status = info.Copy(pTrack->m_info);
5293 if (status) { // error
5298 pTrack->m_width = width;
5299 pTrack->m_height = height;
5300 pTrack->m_display_width = display_width;
5301 pTrack->m_display_height = display_height;
5302 pTrack->m_display_unit = display_unit;
5303 pTrack->m_stereo_mode = stereo_mode;
5304 pTrack->m_rate = rate;
5305 pTrack->m_colour = colour;
5308 return 0; // success
5311 bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
5312 return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
5315 long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
5316 const long status = GetFirst(pResult);
5318 if (status < 0) // buffer underflow, etc
5326 const Cluster* pCluster = pResult->GetCluster();
5328 assert(pCluster->GetIndex() >= 0);
5330 if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
5333 Cluster** const clusters = m_pSegment->m_clusters;
5336 const long count = m_pSegment->GetCount(); // loaded only, not pre-loaded
5339 Cluster** const i = clusters + pCluster->GetIndex();
5341 assert(*i == pCluster);
5342 assert(pCluster->GetTime() <= time_ns);
5344 Cluster** const j = clusters + count;
5351 //[i, lo) <= time_ns
5355 Cluster** const mid = lo + (hi - lo) / 2;
5360 assert(pCluster->GetIndex() >= 0);
5361 assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
5363 const long long t = pCluster->GetTime();
5379 assert(pCluster->GetTime() <= time_ns);
5381 pResult = pCluster->GetEntry(this, time_ns);
5383 if ((pResult != 0) && !pResult->EOS()) // found a keyframe
5389 assert(pCluster->GetTime() <= time_ns);
5391 pResult = pCluster->GetEntry(this, time_ns);
5393 if ((pResult != 0) && !pResult->EOS())
5397 // weird: we're on the first cluster, but no keyframe found
5398 // should never happen but we must return something anyway
5404 Colour* VideoTrack::GetColour() const { return m_colour; }
5406 long long VideoTrack::GetWidth() const { return m_width; }
5408 long long VideoTrack::GetHeight() const { return m_height; }
5410 long long VideoTrack::GetDisplayWidth() const {
5411 return m_display_width > 0 ? m_display_width : GetWidth();
5414 long long VideoTrack::GetDisplayHeight() const {
5415 return m_display_height > 0 ? m_display_height : GetHeight();
5418 long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
5420 long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
5422 double VideoTrack::GetFrameRate() const { return m_rate; }
5424 AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
5425 long long element_size)
5426 : Track(pSegment, element_start, element_size) {}
5428 long AudioTrack::Parse(Segment* pSegment, const Info& info,
5429 long long element_start, long long element_size,
5430 AudioTrack*& pResult) {
5434 if (info.type != Track::kAudio)
5437 IMkvReader* const pReader = pSegment->m_pReader;
5439 const Settings& s = info.settings;
5440 assert(s.start >= 0);
5441 assert(s.size >= 0);
5443 long long pos = s.start;
5446 const long long stop = pos + s.size;
5448 double rate = 8000.0; // MKV default
5449 long long channels = 1;
5450 long long bit_depth = 0;
5452 while (pos < stop) {
5455 long status = ParseElementHeader(pReader, pos, stop, id, size);
5457 if (status < 0) // error
5460 if (id == libwebm::kMkvSamplingFrequency) {
5461 status = UnserializeFloat(pReader, pos, size, rate);
5467 return E_FILE_FORMAT_INVALID;
5468 } else if (id == libwebm::kMkvChannels) {
5469 channels = UnserializeUInt(pReader, pos, size);
5472 return E_FILE_FORMAT_INVALID;
5473 } else if (id == libwebm::kMkvBitDepth) {
5474 bit_depth = UnserializeUInt(pReader, pos, size);
5477 return E_FILE_FORMAT_INVALID;
5480 pos += size; // consume payload
5482 return E_FILE_FORMAT_INVALID;
5486 return E_FILE_FORMAT_INVALID;
5488 AudioTrack* const pTrack =
5489 new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
5492 return -1; // generic error
5494 const int status = info.Copy(pTrack->m_info);
5501 pTrack->m_rate = rate;
5502 pTrack->m_channels = channels;
5503 pTrack->m_bitDepth = bit_depth;
5506 return 0; // success
5509 double AudioTrack::GetSamplingRate() const { return m_rate; }
5511 long long AudioTrack::GetChannels() const { return m_channels; }
5513 long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
5515 Tracks::Tracks(Segment* pSegment, long long start, long long size_,
5516 long long element_start, long long element_size)
5517 : m_pSegment(pSegment),
5520 m_element_start(element_start),
5521 m_element_size(element_size),
5522 m_trackEntries(NULL),
5523 m_trackEntriesEnd(NULL) {}
5525 long Tracks::Parse() {
5526 assert(m_trackEntries == NULL);
5527 assert(m_trackEntriesEnd == NULL);
5529 const long long stop = m_start + m_size;
5530 IMkvReader* const pReader = m_pSegment->m_pReader;
5533 long long pos = m_start;
5535 while (pos < stop) {
5538 const long status = ParseElementHeader(pReader, pos, stop, id, size);
5540 if (status < 0) // error
5543 if (size == 0) // weird
5546 if (id == libwebm::kMkvTrackEntry)
5549 pos += size; // consume payload
5551 return E_FILE_FORMAT_INVALID;
5555 return E_FILE_FORMAT_INVALID;
5558 return 0; // success
5560 m_trackEntries = new (std::nothrow) Track*[count];
5562 if (m_trackEntries == NULL)
5565 m_trackEntriesEnd = m_trackEntries;
5569 while (pos < stop) {
5570 const long long element_start = pos;
5572 long long id, payload_size;
5575 ParseElementHeader(pReader, pos, stop, id, payload_size);
5577 if (status < 0) // error
5580 if (payload_size == 0) // weird
5583 const long long payload_stop = pos + payload_size;
5584 assert(payload_stop <= stop); // checked in ParseElement
5586 const long long element_size = payload_stop - element_start;
5588 if (id == libwebm::kMkvTrackEntry) {
5589 Track*& pTrack = *m_trackEntriesEnd;
5592 const long status = ParseTrackEntry(pos, payload_size, element_start,
5593 element_size, pTrack);
5598 ++m_trackEntriesEnd;
5603 return E_FILE_FORMAT_INVALID;
5607 return E_FILE_FORMAT_INVALID;
5609 return 0; // success
5612 unsigned long Tracks::GetTracksCount() const {
5613 const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
5614 assert(result >= 0);
5616 return static_cast<unsigned long>(result);
5619 long Tracks::ParseTrackEntry(long long track_start, long long track_size,
5620 long long element_start, long long element_size,
5621 Track*& pResult) const {
5625 IMkvReader* const pReader = m_pSegment->m_pReader;
5627 long long pos = track_start;
5628 const long long track_stop = track_start + track_size;
5635 info.defaultDuration = 0;
5645 Track::Settings e; // content_encodings_settings;
5649 long long lacing = 1; // default is true
5651 while (pos < track_stop) {
5654 const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
5656 if (status < 0) // error
5660 return E_FILE_FORMAT_INVALID;
5662 const long long start = pos;
5664 if (id == libwebm::kMkvVideo) {
5667 } else if (id == libwebm::kMkvAudio) {
5670 } else if (id == libwebm::kMkvContentEncodings) {
5673 } else if (id == libwebm::kMkvTrackUID) {
5675 return E_FILE_FORMAT_INVALID;
5679 long long pos_ = start;
5680 const long long pos_end = start + size;
5682 while (pos_ != pos_end) {
5685 const int status = pReader->Read(pos_, 1, &b);
5695 } else if (id == libwebm::kMkvTrackNumber) {
5696 const long long num = UnserializeUInt(pReader, pos, size);
5698 if ((num <= 0) || (num > 127))
5699 return E_FILE_FORMAT_INVALID;
5701 info.number = static_cast<long>(num);
5702 } else if (id == libwebm::kMkvTrackType) {
5703 const long long type = UnserializeUInt(pReader, pos, size);
5705 if ((type <= 0) || (type > 254))
5706 return E_FILE_FORMAT_INVALID;
5708 info.type = static_cast<long>(type);
5709 } else if (id == libwebm::kMkvName) {
5711 UnserializeString(pReader, pos, size, info.nameAsUTF8);
5715 } else if (id == libwebm::kMkvLanguage) {
5716 const long status = UnserializeString(pReader, pos, size, info.language);
5720 } else if (id == libwebm::kMkvDefaultDuration) {
5721 const long long duration = UnserializeUInt(pReader, pos, size);
5724 return E_FILE_FORMAT_INVALID;
5726 info.defaultDuration = static_cast<unsigned long long>(duration);
5727 } else if (id == libwebm::kMkvCodecID) {
5728 const long status = UnserializeString(pReader, pos, size, info.codecId);
5732 } else if (id == libwebm::kMkvFlagLacing) {
5733 lacing = UnserializeUInt(pReader, pos, size);
5735 if ((lacing < 0) || (lacing > 1))
5736 return E_FILE_FORMAT_INVALID;
5737 } else if (id == libwebm::kMkvCodecPrivate) {
5738 delete[] info.codecPrivate;
5739 info.codecPrivate = NULL;
5740 info.codecPrivateSize = 0;
5742 const size_t buflen = static_cast<size_t>(size);
5745 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
5750 const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
5757 info.codecPrivate = buf;
5758 info.codecPrivateSize = buflen;
5760 } else if (id == libwebm::kMkvCodecName) {
5762 UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
5766 } else if (id == libwebm::kMkvCodecDelay) {
5767 info.codecDelay = UnserializeUInt(pReader, pos, size);
5768 } else if (id == libwebm::kMkvSeekPreRoll) {
5769 info.seekPreRoll = UnserializeUInt(pReader, pos, size);
5772 pos += size; // consume payload
5773 if (pos > track_stop)
5774 return E_FILE_FORMAT_INVALID;
5777 if (pos != track_stop)
5778 return E_FILE_FORMAT_INVALID;
5780 if (info.number <= 0) // not specified
5781 return E_FILE_FORMAT_INVALID;
5783 if (GetTrackByNumber(info.number))
5784 return E_FILE_FORMAT_INVALID;
5786 if (info.type <= 0) // not specified
5787 return E_FILE_FORMAT_INVALID;
5789 info.lacing = (lacing > 0) ? true : false;
5791 if (info.type == Track::kVideo) {
5793 return E_FILE_FORMAT_INVALID;
5796 return E_FILE_FORMAT_INVALID;
5800 VideoTrack* pTrack = NULL;
5802 const long status = VideoTrack::Parse(m_pSegment, info, element_start,
5803 element_size, pTrack);
5812 pResult->ParseContentEncodingsEntry(e.start, e.size);
5813 } else if (info.type == Track::kAudio) {
5815 return E_FILE_FORMAT_INVALID;
5818 return E_FILE_FORMAT_INVALID;
5822 AudioTrack* pTrack = NULL;
5824 const long status = AudioTrack::Parse(m_pSegment, info, element_start,
5825 element_size, pTrack);
5834 pResult->ParseContentEncodingsEntry(e.start, e.size);
5836 // neither video nor audio - probably metadata or subtitles
5839 return E_FILE_FORMAT_INVALID;
5842 return E_FILE_FORMAT_INVALID;
5844 if (info.type == Track::kMetadata && e.start >= 0)
5845 return E_FILE_FORMAT_INVALID;
5847 info.settings.start = -1;
5848 info.settings.size = 0;
5850 Track* pTrack = NULL;
5853 Track::Create(m_pSegment, info, element_start, element_size, pTrack);
5862 return 0; // success
5866 Track** i = m_trackEntries;
5867 Track** const j = m_trackEntriesEnd;
5870 Track* const pTrack = *i++;
5874 delete[] m_trackEntries;
5877 const Track* Tracks::GetTrackByNumber(long tn) const {
5881 Track** i = m_trackEntries;
5882 Track** const j = m_trackEntriesEnd;
5885 Track* const pTrack = *i++;
5890 if (tn == pTrack->GetNumber())
5894 return NULL; // not found
5897 const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
5898 const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
5900 if (idx >= static_cast<unsigned long>(count))
5903 return m_trackEntries[idx];
5906 long Cluster::Load(long long& pos, long& len) const {
5907 if (m_pSegment == NULL)
5908 return E_PARSE_FAILED;
5910 if (m_timecode >= 0) // at least partially loaded
5913 if (m_pos != m_element_start || m_element_size >= 0)
5914 return E_PARSE_FAILED;
5916 IMkvReader* const pReader = m_pSegment->m_pReader;
5917 long long total, avail;
5918 const int status = pReader->Length(&total, &avail);
5920 if (status < 0) // error
5923 if (total >= 0 && (avail > total || m_pos > total))
5924 return E_FILE_FORMAT_INVALID;
5928 long long cluster_size = -1;
5930 if ((pos + 1) > avail) {
5932 return E_BUFFER_NOT_FULL;
5935 long long result = GetUIntLength(pReader, pos, len);
5937 if (result < 0) // error or underflow
5938 return static_cast<long>(result);
5941 return E_BUFFER_NOT_FULL;
5943 if ((pos + len) > avail)
5944 return E_BUFFER_NOT_FULL;
5946 const long long id_ = ReadID(pReader, pos, len);
5948 if (id_ < 0) // error
5949 return static_cast<long>(id_);
5951 if (id_ != libwebm::kMkvCluster)
5952 return E_FILE_FORMAT_INVALID;
5954 pos += len; // consume id
5956 // read cluster size
5958 if ((pos + 1) > avail) {
5960 return E_BUFFER_NOT_FULL;
5963 result = GetUIntLength(pReader, pos, len);
5965 if (result < 0) // error
5966 return static_cast<long>(result);
5969 return E_BUFFER_NOT_FULL;
5971 if ((pos + len) > avail)
5972 return E_BUFFER_NOT_FULL;
5974 const long long size = ReadUInt(pReader, pos, len);
5976 if (size < 0) // error
5977 return static_cast<long>(cluster_size);
5980 return E_FILE_FORMAT_INVALID;
5982 pos += len; // consume length of size of element
5984 const long long unknown_size = (1LL << (7 * len)) - 1;
5986 if (size != unknown_size)
5987 cluster_size = size;
5989 // pos points to start of payload
5990 long long timecode = -1;
5991 long long new_pos = -1;
5992 bool bBlock = false;
5994 long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
5997 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6002 if ((pos + 1) > avail) {
6004 return E_BUFFER_NOT_FULL;
6007 long long result = GetUIntLength(pReader, pos, len);
6009 if (result < 0) // error
6010 return static_cast<long>(result);
6013 return E_BUFFER_NOT_FULL;
6015 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6016 return E_FILE_FORMAT_INVALID;
6018 if ((pos + len) > avail)
6019 return E_BUFFER_NOT_FULL;
6021 const long long id = ReadID(pReader, pos, len);
6023 if (id < 0) // error
6024 return static_cast<long>(id);
6027 return E_FILE_FORMAT_INVALID;
6029 // This is the distinguished set of ID's we use to determine
6030 // that we have exhausted the sub-element's inside the cluster
6031 // whose ID we parsed earlier.
6033 if (id == libwebm::kMkvCluster)
6036 if (id == libwebm::kMkvCues)
6039 pos += len; // consume ID field
6043 if ((pos + 1) > avail) {
6045 return E_BUFFER_NOT_FULL;
6048 result = GetUIntLength(pReader, pos, len);
6050 if (result < 0) // error
6051 return static_cast<long>(result);
6054 return E_BUFFER_NOT_FULL;
6056 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6057 return E_FILE_FORMAT_INVALID;
6059 if ((pos + len) > avail)
6060 return E_BUFFER_NOT_FULL;
6062 const long long size = ReadUInt(pReader, pos, len);
6064 if (size < 0) // error
6065 return static_cast<long>(size);
6067 const long long unknown_size = (1LL << (7 * len)) - 1;
6069 if (size == unknown_size)
6070 return E_FILE_FORMAT_INVALID;
6072 pos += len; // consume size field
6074 if ((cluster_stop >= 0) && (pos > cluster_stop))
6075 return E_FILE_FORMAT_INVALID;
6077 // pos now points to start of payload
6082 if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6083 return E_FILE_FORMAT_INVALID;
6085 if (id == libwebm::kMkvTimecode) {
6086 len = static_cast<long>(size);
6088 if ((pos + size) > avail)
6089 return E_BUFFER_NOT_FULL;
6091 timecode = UnserializeUInt(pReader, pos, size);
6093 if (timecode < 0) // error (or underflow)
6094 return static_cast<long>(timecode);
6096 new_pos = pos + size;
6100 } else if (id == libwebm::kMkvBlockGroup) {
6103 } else if (id == libwebm::kMkvSimpleBlock) {
6108 pos += size; // consume payload
6109 if (cluster_stop >= 0 && pos > cluster_stop)
6110 return E_FILE_FORMAT_INVALID;
6113 if (cluster_stop >= 0 && pos > cluster_stop)
6114 return E_FILE_FORMAT_INVALID;
6116 if (timecode < 0) // no timecode found
6117 return E_FILE_FORMAT_INVALID;
6120 return E_FILE_FORMAT_INVALID;
6122 m_pos = new_pos; // designates position just beyond timecode payload
6123 m_timecode = timecode; // m_timecode >= 0 means we're partially loaded
6125 if (cluster_size >= 0)
6126 m_element_size = cluster_stop - m_element_start;
6131 long Cluster::Parse(long long& pos, long& len) const {
6132 long status = Load(pos, len);
6137 if (m_pos < m_element_start || m_timecode < 0)
6138 return E_PARSE_FAILED;
6140 const long long cluster_stop =
6141 (m_element_size < 0) ? -1 : m_element_start + m_element_size;
6143 if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
6144 return 1; // nothing else to do
6146 IMkvReader* const pReader = m_pSegment->m_pReader;
6148 long long total, avail;
6150 status = pReader->Length(&total, &avail);
6152 if (status < 0) // error
6155 if (total >= 0 && avail > total)
6156 return E_FILE_FORMAT_INVALID;
6161 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6164 if ((total >= 0) && (pos >= total)) {
6165 if (m_element_size < 0)
6166 m_element_size = pos - m_element_start;
6173 if ((pos + 1) > avail) {
6175 return E_BUFFER_NOT_FULL;
6178 long long result = GetUIntLength(pReader, pos, len);
6180 if (result < 0) // error
6181 return static_cast<long>(result);
6184 return E_BUFFER_NOT_FULL;
6186 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6187 return E_FILE_FORMAT_INVALID;
6189 if ((pos + len) > avail)
6190 return E_BUFFER_NOT_FULL;
6192 const long long id = ReadID(pReader, pos, len);
6195 return E_FILE_FORMAT_INVALID;
6197 // This is the distinguished set of ID's we use to determine
6198 // that we have exhausted the sub-element's inside the cluster
6199 // whose ID we parsed earlier.
6201 if ((id == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) {
6202 if (m_element_size < 0)
6203 m_element_size = pos - m_element_start;
6208 pos += len; // consume ID field
6212 if ((pos + 1) > avail) {
6214 return E_BUFFER_NOT_FULL;
6217 result = GetUIntLength(pReader, pos, len);
6219 if (result < 0) // error
6220 return static_cast<long>(result);
6223 return E_BUFFER_NOT_FULL;
6225 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6226 return E_FILE_FORMAT_INVALID;
6228 if ((pos + len) > avail)
6229 return E_BUFFER_NOT_FULL;
6231 const long long size = ReadUInt(pReader, pos, len);
6233 if (size < 0) // error
6234 return static_cast<long>(size);
6236 const long long unknown_size = (1LL << (7 * len)) - 1;
6238 if (size == unknown_size)
6239 return E_FILE_FORMAT_INVALID;
6241 pos += len; // consume size field
6243 if ((cluster_stop >= 0) && (pos > cluster_stop))
6244 return E_FILE_FORMAT_INVALID;
6246 // pos now points to start of payload
6251 // const long long block_start = pos;
6252 const long long block_stop = pos + size;
6254 if (cluster_stop >= 0) {
6255 if (block_stop > cluster_stop) {
6256 if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) {
6257 return E_FILE_FORMAT_INVALID;
6263 } else if ((total >= 0) && (block_stop > total)) {
6264 m_element_size = total - m_element_start;
6267 } else if (block_stop > avail) {
6268 len = static_cast<long>(size);
6269 return E_BUFFER_NOT_FULL;
6272 Cluster* const this_ = const_cast<Cluster*>(this);
6274 if (id == libwebm::kMkvBlockGroup)
6275 return this_->ParseBlockGroup(size, pos, len);
6277 if (id == libwebm::kMkvSimpleBlock)
6278 return this_->ParseSimpleBlock(size, pos, len);
6280 pos += size; // consume payload
6281 if (cluster_stop >= 0 && pos > cluster_stop)
6282 return E_FILE_FORMAT_INVALID;
6285 if (m_element_size < 1)
6286 return E_FILE_FORMAT_INVALID;
6289 if (cluster_stop >= 0 && m_pos > cluster_stop)
6290 return E_FILE_FORMAT_INVALID;
6292 if (m_entries_count > 0) {
6293 const long idx = m_entries_count - 1;
6295 const BlockEntry* const pLast = m_entries[idx];
6297 return E_PARSE_FAILED;
6299 const Block* const pBlock = pLast->GetBlock();
6301 return E_PARSE_FAILED;
6303 const long long start = pBlock->m_start;
6305 if ((total >= 0) && (start > total))
6306 return E_PARSE_FAILED; // defend against trucated stream
6308 const long long size = pBlock->m_size;
6310 const long long stop = start + size;
6311 if (cluster_stop >= 0 && stop > cluster_stop)
6312 return E_FILE_FORMAT_INVALID;
6314 if ((total >= 0) && (stop > total))
6315 return E_PARSE_FAILED; // defend against trucated stream
6318 return 1; // no more entries
6321 long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
6323 const long long block_start = pos;
6324 const long long block_stop = pos + block_size;
6326 IMkvReader* const pReader = m_pSegment->m_pReader;
6328 long long total, avail;
6330 long status = pReader->Length(&total, &avail);
6332 if (status < 0) // error
6335 assert((total < 0) || (avail <= total));
6337 // parse track number
6339 if ((pos + 1) > avail) {
6341 return E_BUFFER_NOT_FULL;
6344 long long result = GetUIntLength(pReader, pos, len);
6346 if (result < 0) // error
6347 return static_cast<long>(result);
6349 if (result > 0) // weird
6350 return E_BUFFER_NOT_FULL;
6352 if ((pos + len) > block_stop)
6353 return E_FILE_FORMAT_INVALID;
6355 if ((pos + len) > avail)
6356 return E_BUFFER_NOT_FULL;
6358 const long long track = ReadUInt(pReader, pos, len);
6360 if (track < 0) // error
6361 return static_cast<long>(track);
6364 return E_FILE_FORMAT_INVALID;
6366 pos += len; // consume track number
6368 if ((pos + 2) > block_stop)
6369 return E_FILE_FORMAT_INVALID;
6371 if ((pos + 2) > avail) {
6373 return E_BUFFER_NOT_FULL;
6376 pos += 2; // consume timecode
6378 if ((pos + 1) > block_stop)
6379 return E_FILE_FORMAT_INVALID;
6381 if ((pos + 1) > avail) {
6383 return E_BUFFER_NOT_FULL;
6386 unsigned char flags;
6388 status = pReader->Read(pos, 1, &flags);
6390 if (status < 0) { // error or underflow
6395 ++pos; // consume flags byte
6396 assert(pos <= avail);
6398 if (pos >= block_stop)
6399 return E_FILE_FORMAT_INVALID;
6401 const int lacing = int(flags & 0x06) >> 1;
6403 if ((lacing != 0) && (block_stop > avail)) {
6404 len = static_cast<long>(block_stop - pos);
6405 return E_BUFFER_NOT_FULL;
6408 status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size,
6409 0); // DiscardPadding
6416 return 0; // success
6419 long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
6421 const long long payload_start = pos;
6422 const long long payload_stop = pos + payload_size;
6424 IMkvReader* const pReader = m_pSegment->m_pReader;
6426 long long total, avail;
6428 long status = pReader->Length(&total, &avail);
6430 if (status < 0) // error
6433 assert((total < 0) || (avail <= total));
6435 if ((total >= 0) && (payload_stop > total))
6436 return E_FILE_FORMAT_INVALID;
6438 if (payload_stop > avail) {
6439 len = static_cast<long>(payload_size);
6440 return E_BUFFER_NOT_FULL;
6443 long long discard_padding = 0;
6445 while (pos < payload_stop) {
6446 // parse sub-block element ID
6448 if ((pos + 1) > avail) {
6450 return E_BUFFER_NOT_FULL;
6453 long long result = GetUIntLength(pReader, pos, len);
6455 if (result < 0) // error
6456 return static_cast<long>(result);
6458 if (result > 0) // weird
6459 return E_BUFFER_NOT_FULL;
6461 if ((pos + len) > payload_stop)
6462 return E_FILE_FORMAT_INVALID;
6464 if ((pos + len) > avail)
6465 return E_BUFFER_NOT_FULL;
6467 const long long id = ReadID(pReader, pos, len);
6469 if (id < 0) // error
6470 return static_cast<long>(id);
6472 if (id == 0) // not a valid ID
6473 return E_FILE_FORMAT_INVALID;
6475 pos += len; // consume ID field
6479 if ((pos + 1) > avail) {
6481 return E_BUFFER_NOT_FULL;
6484 result = GetUIntLength(pReader, pos, len);
6486 if (result < 0) // error
6487 return static_cast<long>(result);
6489 if (result > 0) // weird
6490 return E_BUFFER_NOT_FULL;
6492 if ((pos + len) > payload_stop)
6493 return E_FILE_FORMAT_INVALID;
6495 if ((pos + len) > avail)
6496 return E_BUFFER_NOT_FULL;
6498 const long long size = ReadUInt(pReader, pos, len);
6500 if (size < 0) // error
6501 return static_cast<long>(size);
6503 pos += len; // consume size field
6505 // pos now points to start of sub-block group payload
6507 if (pos > payload_stop)
6508 return E_FILE_FORMAT_INVALID;
6510 if (size == 0) // weird
6513 const long long unknown_size = (1LL << (7 * len)) - 1;
6515 if (size == unknown_size)
6516 return E_FILE_FORMAT_INVALID;
6518 if (id == libwebm::kMkvDiscardPadding) {
6519 status = UnserializeInt(pReader, pos, size, discard_padding);
6521 if (status < 0) // error
6525 if (id != libwebm::kMkvBlock) {
6526 pos += size; // consume sub-part of block group
6528 if (pos > payload_stop)
6529 return E_FILE_FORMAT_INVALID;
6534 const long long block_stop = pos + size;
6536 if (block_stop > payload_stop)
6537 return E_FILE_FORMAT_INVALID;
6539 // parse track number
6541 if ((pos + 1) > avail) {
6543 return E_BUFFER_NOT_FULL;
6546 result = GetUIntLength(pReader, pos, len);
6548 if (result < 0) // error
6549 return static_cast<long>(result);
6551 if (result > 0) // weird
6552 return E_BUFFER_NOT_FULL;
6554 if ((pos + len) > block_stop)
6555 return E_FILE_FORMAT_INVALID;
6557 if ((pos + len) > avail)
6558 return E_BUFFER_NOT_FULL;
6560 const long long track = ReadUInt(pReader, pos, len);
6562 if (track < 0) // error
6563 return static_cast<long>(track);
6566 return E_FILE_FORMAT_INVALID;
6568 pos += len; // consume track number
6570 if ((pos + 2) > block_stop)
6571 return E_FILE_FORMAT_INVALID;
6573 if ((pos + 2) > avail) {
6575 return E_BUFFER_NOT_FULL;
6578 pos += 2; // consume timecode
6580 if ((pos + 1) > block_stop)
6581 return E_FILE_FORMAT_INVALID;
6583 if ((pos + 1) > avail) {
6585 return E_BUFFER_NOT_FULL;
6588 unsigned char flags;
6590 status = pReader->Read(pos, 1, &flags);
6592 if (status < 0) { // error or underflow
6597 ++pos; // consume flags byte
6598 assert(pos <= avail);
6600 if (pos >= block_stop)
6601 return E_FILE_FORMAT_INVALID;
6603 const int lacing = int(flags & 0x06) >> 1;
6605 if ((lacing != 0) && (block_stop > avail)) {
6606 len = static_cast<long>(block_stop - pos);
6607 return E_BUFFER_NOT_FULL;
6610 pos = block_stop; // consume block-part of block group
6611 if (pos > payload_stop)
6612 return E_FILE_FORMAT_INVALID;
6615 if (pos != payload_stop)
6616 return E_FILE_FORMAT_INVALID;
6618 status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size,
6623 m_pos = payload_stop;
6625 return 0; // success
6628 long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
6629 assert(m_pos >= m_element_start);
6634 return -1; // generic error
6636 if (m_entries_count < 0)
6637 return E_BUFFER_NOT_FULL;
6640 assert(m_entries_size > 0);
6641 assert(m_entries_count <= m_entries_size);
6643 if (index < m_entries_count) {
6644 pEntry = m_entries[index];
6647 return 1; // found entry
6650 if (m_element_size < 0) // we don't know cluster end yet
6651 return E_BUFFER_NOT_FULL; // underflow
6653 const long long element_stop = m_element_start + m_element_size;
6655 if (m_pos >= element_stop)
6656 return 0; // nothing left to parse
6658 return E_BUFFER_NOT_FULL; // underflow, since more remains to be parsed
6661 Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
6662 if (!pSegment || off < 0)
6665 const long long element_start = pSegment->m_start + off;
6667 Cluster* const pCluster =
6668 new (std::nothrow) Cluster(pSegment, idx, element_start);
6682 m_entries_count(0) // means "no entries"
6685 Cluster::Cluster(Segment* pSegment, long idx, long long element_start
6686 /* long long element_size */)
6687 : m_pSegment(pSegment),
6688 m_element_start(element_start),
6690 m_pos(element_start),
6691 m_element_size(-1 /* element_size */),
6695 m_entries_count(-1) // means "has not been parsed yet"
6698 Cluster::~Cluster() {
6699 if (m_entries_count <= 0)
6702 BlockEntry** i = m_entries;
6703 BlockEntry** const j = m_entries + m_entries_count;
6706 BlockEntry* p = *i++;
6715 bool Cluster::EOS() const { return (m_pSegment == NULL); }
6717 long Cluster::GetIndex() const { return m_index; }
6719 long long Cluster::GetPosition() const {
6720 const long long pos = m_element_start - m_pSegment->m_start;
6726 long long Cluster::GetElementSize() const { return m_element_size; }
6728 long Cluster::HasBlockEntries(
6729 const Segment* pSegment,
6730 long long off, // relative to start of segment payload
6731 long long& pos, long& len) {
6733 assert(off >= 0); // relative to segment
6735 IMkvReader* const pReader = pSegment->m_pReader;
6737 long long total, avail;
6739 long status = pReader->Length(&total, &avail);
6741 if (status < 0) // error
6744 assert((total < 0) || (avail <= total));
6746 pos = pSegment->m_start + off; // absolute
6748 if ((total >= 0) && (pos >= total))
6749 return 0; // we don't even have a complete cluster
6751 const long long segment_stop =
6752 (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
6754 long long cluster_stop = -1; // interpreted later to mean "unknown size"
6757 if ((pos + 1) > avail) {
6759 return E_BUFFER_NOT_FULL;
6762 long long result = GetUIntLength(pReader, pos, len);
6764 if (result < 0) // error
6765 return static_cast<long>(result);
6767 if (result > 0) // need more data
6768 return E_BUFFER_NOT_FULL;
6770 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6771 return E_FILE_FORMAT_INVALID;
6773 if ((total >= 0) && ((pos + len) > total))
6776 if ((pos + len) > avail)
6777 return E_BUFFER_NOT_FULL;
6779 const long long id = ReadID(pReader, pos, len);
6781 if (id < 0) // error
6782 return static_cast<long>(id);
6784 if (id != libwebm::kMkvCluster)
6785 return E_PARSE_FAILED;
6787 pos += len; // consume Cluster ID field
6791 if ((pos + 1) > avail) {
6793 return E_BUFFER_NOT_FULL;
6796 result = GetUIntLength(pReader, pos, len);
6798 if (result < 0) // error
6799 return static_cast<long>(result);
6801 if (result > 0) // weird
6802 return E_BUFFER_NOT_FULL;
6804 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6805 return E_FILE_FORMAT_INVALID;
6807 if ((total >= 0) && ((pos + len) > total))
6810 if ((pos + len) > avail)
6811 return E_BUFFER_NOT_FULL;
6813 const long long size = ReadUInt(pReader, pos, len);
6815 if (size < 0) // error
6816 return static_cast<long>(size);
6819 return 0; // cluster does not have entries
6821 pos += len; // consume size field
6823 // pos now points to start of payload
6825 const long long unknown_size = (1LL << (7 * len)) - 1;
6827 if (size != unknown_size) {
6828 cluster_stop = pos + size;
6829 assert(cluster_stop >= 0);
6831 if ((segment_stop >= 0) && (cluster_stop > segment_stop))
6832 return E_FILE_FORMAT_INVALID;
6834 if ((total >= 0) && (cluster_stop > total))
6835 // return E_FILE_FORMAT_INVALID; //too conservative
6836 return 0; // cluster does not have any entries
6841 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6842 return 0; // no entries detected
6844 if ((pos + 1) > avail) {
6846 return E_BUFFER_NOT_FULL;
6849 long long result = GetUIntLength(pReader, pos, len);
6851 if (result < 0) // error
6852 return static_cast<long>(result);
6854 if (result > 0) // need more data
6855 return E_BUFFER_NOT_FULL;
6857 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6858 return E_FILE_FORMAT_INVALID;
6860 if ((pos + len) > avail)
6861 return E_BUFFER_NOT_FULL;
6863 const long long id = ReadID(pReader, pos, len);
6865 if (id < 0) // error
6866 return static_cast<long>(id);
6868 // This is the distinguished set of ID's we use to determine
6869 // that we have exhausted the sub-element's inside the cluster
6870 // whose ID we parsed earlier.
6872 if (id == libwebm::kMkvCluster)
6873 return 0; // no entries found
6875 if (id == libwebm::kMkvCues)
6876 return 0; // no entries found
6878 pos += len; // consume id field
6880 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6881 return E_FILE_FORMAT_INVALID;
6885 if ((pos + 1) > avail) {
6887 return E_BUFFER_NOT_FULL;
6890 result = GetUIntLength(pReader, pos, len);
6892 if (result < 0) // error
6893 return static_cast<long>(result);
6895 if (result > 0) // underflow
6896 return E_BUFFER_NOT_FULL;
6898 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6899 return E_FILE_FORMAT_INVALID;
6901 if ((pos + len) > avail)
6902 return E_BUFFER_NOT_FULL;
6904 const long long size = ReadUInt(pReader, pos, len);
6906 if (size < 0) // error
6907 return static_cast<long>(size);
6909 pos += len; // consume size field
6911 // pos now points to start of payload
6913 if ((cluster_stop >= 0) && (pos > cluster_stop))
6914 return E_FILE_FORMAT_INVALID;
6916 if (size == 0) // weird
6919 const long long unknown_size = (1LL << (7 * len)) - 1;
6921 if (size == unknown_size)
6922 return E_FILE_FORMAT_INVALID; // not supported inside cluster
6924 if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6925 return E_FILE_FORMAT_INVALID;
6927 if (id == libwebm::kMkvBlockGroup)
6928 return 1; // have at least one entry
6930 if (id == libwebm::kMkvSimpleBlock)
6931 return 1; // have at least one entry
6933 pos += size; // consume payload
6934 if (cluster_stop >= 0 && pos > cluster_stop)
6935 return E_FILE_FORMAT_INVALID;
6939 long long Cluster::GetTimeCode() const {
6943 const long status = Load(pos, len);
6945 if (status < 0) // error
6951 long long Cluster::GetTime() const {
6952 const long long tc = GetTimeCode();
6957 const SegmentInfo* const pInfo = m_pSegment->GetInfo();
6960 const long long scale = pInfo->GetTimeCodeScale();
6963 const long long t = m_timecode * scale;
6968 long long Cluster::GetFirstTime() const {
6969 const BlockEntry* pEntry;
6971 const long status = GetFirst(pEntry);
6973 if (status < 0) // error
6976 if (pEntry == NULL) // empty cluster
6979 const Block* const pBlock = pEntry->GetBlock();
6982 return pBlock->GetTime(this);
6985 long long Cluster::GetLastTime() const {
6986 const BlockEntry* pEntry;
6988 const long status = GetLast(pEntry);
6990 if (status < 0) // error
6993 if (pEntry == NULL) // empty cluster
6996 const Block* const pBlock = pEntry->GetBlock();
6999 return pBlock->GetTime(this);
7002 long Cluster::CreateBlock(long long id,
7003 long long pos, // absolute pos of payload
7004 long long size, long long discard_padding) {
7005 if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock)
7006 return E_PARSE_FAILED;
7008 if (m_entries_count < 0) { // haven't parsed anything yet
7009 assert(m_entries == NULL);
7010 assert(m_entries_size == 0);
7012 m_entries_size = 1024;
7013 m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
7014 if (m_entries == NULL)
7017 m_entries_count = 0;
7020 assert(m_entries_size > 0);
7021 assert(m_entries_count <= m_entries_size);
7023 if (m_entries_count >= m_entries_size) {
7024 const long entries_size = 2 * m_entries_size;
7026 BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
7027 if (entries == NULL)
7030 BlockEntry** src = m_entries;
7031 BlockEntry** const src_end = src + m_entries_count;
7033 BlockEntry** dst = entries;
7035 while (src != src_end)
7040 m_entries = entries;
7041 m_entries_size = entries_size;
7045 if (id == libwebm::kMkvBlockGroup)
7046 return CreateBlockGroup(pos, size, discard_padding);
7048 return CreateSimpleBlock(pos, size);
7051 long Cluster::CreateBlockGroup(long long start_offset, long long size,
7052 long long discard_padding) {
7054 assert(m_entries_size > 0);
7055 assert(m_entries_count >= 0);
7056 assert(m_entries_count < m_entries_size);
7058 IMkvReader* const pReader = m_pSegment->m_pReader;
7060 long long pos = start_offset;
7061 const long long stop = start_offset + size;
7063 // For WebM files, there is a bias towards previous reference times
7064 //(in order to support alt-ref frames, which refer back to the previous
7065 // keyframe). Normally a 0 value is not possible, but here we tenatively
7066 // allow 0 as the value of a reference frame, with the interpretation
7067 // that this is a "previous" reference time.
7069 long long prev = 1; // nonce
7070 long long next = 0; // nonce
7071 long long duration = -1; // really, this is unsigned
7073 long long bpos = -1;
7074 long long bsize = -1;
7076 while (pos < stop) {
7078 const long long id = ReadID(pReader, pos, len);
7079 if (id < 0 || (pos + len) > stop)
7080 return E_FILE_FORMAT_INVALID;
7082 pos += len; // consume ID
7084 const long long size = ReadUInt(pReader, pos, len);
7085 assert(size >= 0); // TODO
7086 assert((pos + len) <= stop);
7088 pos += len; // consume size
7090 if (id == libwebm::kMkvBlock) {
7091 if (bpos < 0) { // Block ID
7095 } else if (id == libwebm::kMkvBlockDuration) {
7097 return E_FILE_FORMAT_INVALID;
7099 duration = UnserializeUInt(pReader, pos, size);
7102 return E_FILE_FORMAT_INVALID;
7103 } else if (id == libwebm::kMkvReferenceBlock) {
7104 if (size > 8 || size <= 0)
7105 return E_FILE_FORMAT_INVALID;
7106 const long size_ = static_cast<long>(size);
7110 long status = UnserializeInt(pReader, pos, size_, time);
7111 assert(status == 0);
7115 if (time <= 0) // see note above
7121 pos += size; // consume payload
7123 return E_FILE_FORMAT_INVALID;
7126 return E_FILE_FORMAT_INVALID;
7129 return E_FILE_FORMAT_INVALID;
7132 const long idx = m_entries_count;
7134 BlockEntry** const ppEntry = m_entries + idx;
7135 BlockEntry*& pEntry = *ppEntry;
7137 pEntry = new (std::nothrow)
7138 BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
7141 return -1; // generic error
7143 BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
7145 const long status = p->Parse();
7147 if (status == 0) { // success
7158 long Cluster::CreateSimpleBlock(long long st, long long sz) {
7160 assert(m_entries_size > 0);
7161 assert(m_entries_count >= 0);
7162 assert(m_entries_count < m_entries_size);
7164 const long idx = m_entries_count;
7166 BlockEntry** const ppEntry = m_entries + idx;
7167 BlockEntry*& pEntry = *ppEntry;
7169 pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
7172 return -1; // generic error
7174 SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
7176 const long status = p->Parse();
7189 long Cluster::GetFirst(const BlockEntry*& pFirst) const {
7190 if (m_entries_count <= 0) {
7194 const long status = Parse(pos, len);
7196 if (status < 0) { // error
7201 if (m_entries_count <= 0) { // empty cluster
7209 pFirst = m_entries[0];
7212 return 0; // success
7215 long Cluster::GetLast(const BlockEntry*& pLast) const {
7220 const long status = Parse(pos, len);
7222 if (status < 0) { // error
7227 if (status > 0) // no new block
7231 if (m_entries_count <= 0) {
7238 const long idx = m_entries_count - 1;
7240 pLast = m_entries[idx];
7246 long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
7249 assert(m_entries_count > 0);
7251 size_t idx = pCurr->GetIndex();
7252 assert(idx < size_t(m_entries_count));
7253 assert(m_entries[idx] == pCurr);
7257 if (idx >= size_t(m_entries_count)) {
7261 const long status = Parse(pos, len);
7263 if (status < 0) { // error
7274 assert(m_entries_count > 0);
7275 assert(idx < size_t(m_entries_count));
7278 pNext = m_entries[idx];
7284 long Cluster::GetEntryCount() const { return m_entries_count; }
7286 const BlockEntry* Cluster::GetEntry(const Track* pTrack,
7287 long long time_ns) const {
7290 if (m_pSegment == NULL) // this is the special EOS cluster
7291 return pTrack->GetEOS();
7293 const BlockEntry* pResult = pTrack->GetEOS();
7298 if (index >= m_entries_count) {
7302 const long status = Parse(pos, len);
7303 assert(status >= 0);
7305 if (status > 0) // completely parsed, and no more entries
7308 if (status < 0) // should never happen
7312 assert(index < m_entries_count);
7315 const BlockEntry* const pEntry = m_entries[index];
7317 assert(!pEntry->EOS());
7319 const Block* const pBlock = pEntry->GetBlock();
7322 if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
7327 if (pTrack->VetEntry(pEntry)) {
7328 if (time_ns < 0) // just want first candidate block
7331 const long long ns = pBlock->GetTime(this);
7336 pResult = pEntry; // have a candidate
7337 } else if (time_ns >= 0) {
7338 const long long ns = pBlock->GetTime(this);
7348 const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
7349 const CuePoint::TrackPosition& tp) const {
7351 const long long tc = cp.GetTimeCode();
7353 if (tp.m_block > 0) {
7354 const long block = static_cast<long>(tp.m_block);
7355 const long index = block - 1;
7357 while (index >= m_entries_count) {
7361 const long status = Parse(pos, len);
7363 if (status < 0) // TODO: can this happen?
7366 if (status > 0) // nothing remains to be parsed
7370 const BlockEntry* const pEntry = m_entries[index];
7372 assert(!pEntry->EOS());
7374 const Block* const pBlock = pEntry->GetBlock();
7377 if ((pBlock->GetTrackNumber() == tp.m_track) &&
7378 (pBlock->GetTimeCode(this) == tc)) {
7386 if (index >= m_entries_count) {
7390 const long status = Parse(pos, len);
7392 if (status < 0) // TODO: can this happen?
7395 if (status > 0) // nothing remains to be parsed
7399 assert(index < m_entries_count);
7402 const BlockEntry* const pEntry = m_entries[index];
7404 assert(!pEntry->EOS());
7406 const Block* const pBlock = pEntry->GetBlock();
7409 if (pBlock->GetTrackNumber() != tp.m_track) {
7414 const long long tc_ = pBlock->GetTimeCode(this);
7424 const Tracks* const pTracks = m_pSegment->GetTracks();
7427 const long tn = static_cast<long>(tp.m_track);
7428 const Track* const pTrack = pTracks->GetTrackByNumber(tn);
7433 const long long type = pTrack->GetType();
7435 if (type == 2) // audio
7438 if (type != 1) // not video
7441 if (!pBlock->IsKey())
7448 BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
7449 BlockEntry::~BlockEntry() {}
7450 bool BlockEntry::EOS() const { return (GetKind() == kBlockEOS); }
7451 const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
7452 long BlockEntry::GetIndex() const { return m_index; }
7454 SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
7456 : BlockEntry(pCluster, idx), m_block(start, size, 0) {}
7458 long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
7459 BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
7460 const Block* SimpleBlock::GetBlock() const { return &m_block; }
7462 BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
7463 long long block_size, long long prev, long long next,
7464 long long duration, long long discard_padding)
7465 : BlockEntry(pCluster, idx),
7466 m_block(block_start, block_size, discard_padding),
7469 m_duration(duration) {}
7471 long BlockGroup::Parse() {
7472 const long status = m_block.Parse(m_pCluster);
7477 m_block.SetKey((m_prev > 0) && (m_next <= 0));
7482 BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
7483 const Block* BlockGroup::GetBlock() const { return &m_block; }
7484 long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
7485 long long BlockGroup::GetNextTimeCode() const { return m_next; }
7486 long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
7488 Block::Block(long long start, long long size_, long long discard_padding)
7496 m_discard_padding(discard_padding) {}
7498 Block::~Block() { delete[] m_frames; }
7500 long Block::Parse(const Cluster* pCluster) {
7501 if (pCluster == NULL)
7504 if (pCluster->m_pSegment == NULL)
7507 assert(m_start >= 0);
7508 assert(m_size >= 0);
7509 assert(m_track <= 0);
7510 assert(m_frames == NULL);
7511 assert(m_frame_count <= 0);
7513 long long pos = m_start;
7514 const long long stop = m_start + m_size;
7518 IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
7520 m_track = ReadUInt(pReader, pos, len);
7523 return E_FILE_FORMAT_INVALID;
7525 if ((pos + len) > stop)
7526 return E_FILE_FORMAT_INVALID;
7528 pos += len; // consume track number
7530 if ((stop - pos) < 2)
7531 return E_FILE_FORMAT_INVALID;
7536 status = UnserializeInt(pReader, pos, 2, value);
7539 return E_FILE_FORMAT_INVALID;
7541 if (value < SHRT_MIN)
7542 return E_FILE_FORMAT_INVALID;
7544 if (value > SHRT_MAX)
7545 return E_FILE_FORMAT_INVALID;
7547 m_timecode = static_cast<short>(value);
7551 if ((stop - pos) <= 0)
7552 return E_FILE_FORMAT_INVALID;
7554 status = pReader->Read(pos, 1, &m_flags);
7557 return E_FILE_FORMAT_INVALID;
7559 const int lacing = int(m_flags & 0x06) >> 1;
7561 ++pos; // consume flags byte
7563 if (lacing == 0) { // no lacing
7565 return E_FILE_FORMAT_INVALID;
7568 m_frames = new (std::nothrow) Frame[m_frame_count];
7569 if (m_frames == NULL)
7572 Frame& f = m_frames[0];
7575 const long long frame_size = stop - pos;
7577 if (frame_size > LONG_MAX || frame_size <= 0)
7578 return E_FILE_FORMAT_INVALID;
7580 f.len = static_cast<long>(frame_size);
7582 return 0; // success
7586 return E_FILE_FORMAT_INVALID;
7588 unsigned char biased_count;
7590 status = pReader->Read(pos, 1, &biased_count);
7593 return E_FILE_FORMAT_INVALID;
7595 ++pos; // consume frame count
7597 return E_FILE_FORMAT_INVALID;
7599 m_frame_count = int(biased_count) + 1;
7601 m_frames = new (std::nothrow) Frame[m_frame_count];
7602 if (m_frames == NULL)
7606 return E_FILE_FORMAT_INVALID;
7608 if (lacing == 1) { // Xiph
7609 Frame* pf = m_frames;
7610 Frame* const pf_end = pf + m_frame_count;
7613 int frame_count = m_frame_count;
7615 while (frame_count > 1) {
7616 long frame_size = 0;
7622 return E_FILE_FORMAT_INVALID;
7624 status = pReader->Read(pos, 1, &val);
7627 return E_FILE_FORMAT_INVALID;
7629 ++pos; // consume xiph size byte
7638 assert(pf < pf_end);
7640 return E_FILE_FORMAT_INVALID;
7642 f.pos = 0; // patch later
7644 if (frame_size <= 0)
7645 return E_FILE_FORMAT_INVALID;
7648 size += frame_size; // contribution of this frame
7653 if (pf >= pf_end || pos > stop)
7654 return E_FILE_FORMAT_INVALID;
7660 return E_FILE_FORMAT_INVALID;
7662 f.pos = 0; // patch later
7664 const long long total_size = stop - pos;
7666 if (total_size < size)
7667 return E_FILE_FORMAT_INVALID;
7669 const long long frame_size = total_size - size;
7671 if (frame_size > LONG_MAX || frame_size <= 0)
7672 return E_FILE_FORMAT_INVALID;
7674 f.len = static_cast<long>(frame_size);
7678 while (pf != pf_end) {
7680 assert((pos + f.len) <= stop);
7682 if ((pos + f.len) > stop)
7683 return E_FILE_FORMAT_INVALID;
7689 assert(pos == stop);
7691 return E_FILE_FORMAT_INVALID;
7693 } else if (lacing == 2) { // fixed-size lacing
7695 return E_FILE_FORMAT_INVALID;
7697 const long long total_size = stop - pos;
7699 if ((total_size % m_frame_count) != 0)
7700 return E_FILE_FORMAT_INVALID;
7702 const long long frame_size = total_size / m_frame_count;
7704 if (frame_size > LONG_MAX || frame_size <= 0)
7705 return E_FILE_FORMAT_INVALID;
7707 Frame* pf = m_frames;
7708 Frame* const pf_end = pf + m_frame_count;
7710 while (pf != pf_end) {
7711 assert((pos + frame_size) <= stop);
7712 if ((pos + frame_size) > stop)
7713 return E_FILE_FORMAT_INVALID;
7718 f.len = static_cast<long>(frame_size);
7723 assert(pos == stop);
7725 return E_FILE_FORMAT_INVALID;
7728 assert(lacing == 3); // EBML lacing
7731 return E_FILE_FORMAT_INVALID;
7734 int frame_count = m_frame_count;
7736 long long frame_size = ReadUInt(pReader, pos, len);
7738 if (frame_size <= 0)
7739 return E_FILE_FORMAT_INVALID;
7741 if (frame_size > LONG_MAX)
7742 return E_FILE_FORMAT_INVALID;
7744 if ((pos + len) > stop)
7745 return E_FILE_FORMAT_INVALID;
7747 pos += len; // consume length of size of first frame
7749 if ((pos + frame_size) > stop)
7750 return E_FILE_FORMAT_INVALID;
7752 Frame* pf = m_frames;
7753 Frame* const pf_end = pf + m_frame_count;
7758 curr.pos = 0; // patch later
7760 curr.len = static_cast<long>(frame_size);
7761 size += curr.len; // contribution of this frame
7766 while (frame_count > 1) {
7768 return E_FILE_FORMAT_INVALID;
7770 assert(pf < pf_end);
7772 return E_FILE_FORMAT_INVALID;
7774 const Frame& prev = *pf++;
7775 assert(prev.len == frame_size);
7776 if (prev.len != frame_size)
7777 return E_FILE_FORMAT_INVALID;
7779 assert(pf < pf_end);
7781 return E_FILE_FORMAT_INVALID;
7785 curr.pos = 0; // patch later
7787 const long long delta_size_ = ReadUInt(pReader, pos, len);
7789 if (delta_size_ < 0)
7790 return E_FILE_FORMAT_INVALID;
7792 if ((pos + len) > stop)
7793 return E_FILE_FORMAT_INVALID;
7795 pos += len; // consume length of (delta) size
7797 return E_FILE_FORMAT_INVALID;
7799 const int exp = 7 * len - 1;
7800 const long long bias = (1LL << exp) - 1LL;
7801 const long long delta_size = delta_size_ - bias;
7803 frame_size += delta_size;
7805 if (frame_size <= 0)
7806 return E_FILE_FORMAT_INVALID;
7808 if (frame_size > LONG_MAX)
7809 return E_FILE_FORMAT_INVALID;
7811 curr.len = static_cast<long>(frame_size);
7812 size += curr.len; // contribution of this frame
7818 if (frame_count > 0) {
7819 if (pos > stop || pf >= pf_end)
7820 return E_FILE_FORMAT_INVALID;
7822 const Frame& prev = *pf++;
7823 assert(prev.len == frame_size);
7824 if (prev.len != frame_size)
7825 return E_FILE_FORMAT_INVALID;
7828 return E_FILE_FORMAT_INVALID;
7830 Frame& curr = *pf++;
7832 return E_FILE_FORMAT_INVALID;
7834 curr.pos = 0; // patch later
7836 const long long total_size = stop - pos;
7838 if (total_size < size)
7839 return E_FILE_FORMAT_INVALID;
7841 frame_size = total_size - size;
7843 if (frame_size > LONG_MAX || frame_size <= 0)
7844 return E_FILE_FORMAT_INVALID;
7846 curr.len = static_cast<long>(frame_size);
7850 while (pf != pf_end) {
7852 assert((pos + f.len) <= stop);
7853 if ((pos + f.len) > stop)
7854 return E_FILE_FORMAT_INVALID;
7861 return E_FILE_FORMAT_INVALID;
7864 return 0; // success
7867 long long Block::GetTimeCode(const Cluster* pCluster) const {
7871 const long long tc0 = pCluster->GetTimeCode();
7874 const long long tc = tc0 + m_timecode;
7876 return tc; // unscaled timecode units
7879 long long Block::GetTime(const Cluster* pCluster) const {
7882 const long long tc = GetTimeCode(pCluster);
7884 const Segment* const pSegment = pCluster->m_pSegment;
7885 const SegmentInfo* const pInfo = pSegment->GetInfo();
7888 const long long scale = pInfo->GetTimeCodeScale();
7891 const long long ns = tc * scale;
7896 long long Block::GetTrackNumber() const { return m_track; }
7898 bool Block::IsKey() const {
7899 return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
7902 void Block::SetKey(bool bKey) {
7904 m_flags |= static_cast<unsigned char>(1 << 7);
7909 bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
7911 Block::Lacing Block::GetLacing() const {
7912 const int value = int(m_flags & 0x06) >> 1;
7913 return static_cast<Lacing>(value);
7916 int Block::GetFrameCount() const { return m_frame_count; }
7918 const Block::Frame& Block::GetFrame(int idx) const {
7920 assert(idx < m_frame_count);
7922 const Frame& f = m_frames[idx];
7929 long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
7933 const long status = pReader->Read(pos, len, buf);
7937 long long Block::GetDiscardPadding() const { return m_discard_padding; }
7939 } // namespace mkvparser