xref: /aosp_15_r20/external/libvpx/third_party/libwebm/mkvparser/mkvparser.cc (revision fb1b10ab9aebc7c7068eedab379b749d7e3900be)
1 // Copyright (c) 2012 The WebM project authors. All Rights Reserved.
2 //
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"
9 
10 #if defined(_MSC_VER) && _MSC_VER < 1800
11 #include <float.h>  // _isnan() / _finite()
12 #define MSC_COMPAT
13 #endif
14 
15 #include <cassert>
16 #include <cfloat>
17 #include <climits>
18 #include <cmath>
19 #include <cstdint>
20 #include <cstring>
21 #include <memory>
22 #include <new>
23 
24 #include "common/webmids.h"
25 
26 namespace mkvparser {
27 const long long kStringElementSizeLimit = 20 * 1000 * 1000;
28 const float MasteringMetadata::kValueNotPresent = FLT_MAX;
29 const long long Colour::kValueNotPresent = LLONG_MAX;
30 const float Projection::kValueNotPresent = FLT_MAX;
31 
32 #ifdef MSC_COMPAT
isnan(double val)33 inline bool isnan(double val) { return !!_isnan(val); }
isinf(double val)34 inline bool isinf(double val) { return !_finite(val); }
35 #else
isnan(double val)36 inline bool isnan(double val) { return std::isnan(val); }
isinf(double val)37 inline bool isinf(double val) { return std::isinf(val); }
38 #endif  // MSC_COMPAT
39 
40 template <typename Type>
SafeArrayAlloc(unsigned long long num_elements,unsigned long long element_size)41 Type* SafeArrayAlloc(unsigned long long num_elements,
42                      unsigned long long element_size) {
43   if (num_elements == 0 || element_size == 0)
44     return NULL;
45 
46   const size_t kMaxAllocSize = 0x80000000;  // 2GiB
47   const unsigned long long num_bytes = num_elements * element_size;
48   if (element_size > (kMaxAllocSize / num_elements))
49     return NULL;
50   if (num_bytes != static_cast<size_t>(num_bytes))
51     return NULL;
52 
53   return new (std::nothrow) Type[static_cast<size_t>(num_bytes)];
54 }
55 
GetVersion(int & major,int & minor,int & build,int & revision)56 void GetVersion(int& major, int& minor, int& build, int& revision) {
57   major = 1;
58   minor = 1;
59   build = 3;
60   revision = 0;
61 }
62 
ReadUInt(IMkvReader * pReader,long long pos,long & len)63 long long ReadUInt(IMkvReader* pReader, long long pos, long& len) {
64   if (!pReader || pos < 0)
65     return E_FILE_FORMAT_INVALID;
66 
67   len = 1;
68   unsigned char b;
69   int status = pReader->Read(pos, 1, &b);
70 
71   if (status < 0)  // error or underflow
72     return status;
73 
74   if (status > 0)  // interpreted as "underflow"
75     return E_BUFFER_NOT_FULL;
76 
77   if (b == 0)  // we can't handle u-int values larger than 8 bytes
78     return E_FILE_FORMAT_INVALID;
79 
80   unsigned char m = 0x80;
81 
82   while (!(b & m)) {
83     m >>= 1;
84     ++len;
85   }
86 
87   long long result = b & (~m);
88   ++pos;
89 
90   for (int i = 1; i < len; ++i) {
91     status = pReader->Read(pos, 1, &b);
92 
93     if (status < 0) {
94       len = 1;
95       return status;
96     }
97 
98     if (status > 0) {
99       len = 1;
100       return E_BUFFER_NOT_FULL;
101     }
102 
103     result <<= 8;
104     result |= b;
105 
106     ++pos;
107   }
108 
109   return result;
110 }
111 
112 // Reads an EBML ID and returns it.
113 // An ID must at least 1 byte long, cannot exceed 4, and its value must be
114 // greater than 0.
115 // See known EBML values and EBMLMaxIDLength:
116 // http://www.matroska.org/technical/specs/index.html
117 // Returns the ID, or a value less than 0 to report an error while reading the
118 // ID.
ReadID(IMkvReader * pReader,long long pos,long & len)119 long long ReadID(IMkvReader* pReader, long long pos, long& len) {
120   if (pReader == NULL || pos < 0)
121     return E_FILE_FORMAT_INVALID;
122 
123   // Read the first byte. The length in bytes of the ID is determined by
124   // finding the first set bit in the first byte of the ID.
125   unsigned char temp_byte = 0;
126   int read_status = pReader->Read(pos, 1, &temp_byte);
127 
128   if (read_status < 0)
129     return E_FILE_FORMAT_INVALID;
130   else if (read_status > 0)  // No data to read.
131     return E_BUFFER_NOT_FULL;
132 
133   if (temp_byte == 0)  // ID length > 8 bytes; invalid file.
134     return E_FILE_FORMAT_INVALID;
135 
136   int bit_pos = 0;
137   const int kMaxIdLengthInBytes = 4;
138   const int kCheckByte = 0x80;
139 
140   // Find the first bit that's set.
141   bool found_bit = false;
142   for (; bit_pos < kMaxIdLengthInBytes; ++bit_pos) {
143     if ((kCheckByte >> bit_pos) & temp_byte) {
144       found_bit = true;
145       break;
146     }
147   }
148 
149   if (!found_bit) {
150     // The value is too large to be a valid ID.
151     return E_FILE_FORMAT_INVALID;
152   }
153 
154   // Read the remaining bytes of the ID (if any).
155   const int id_length = bit_pos + 1;
156   long long ebml_id = temp_byte;
157   for (int i = 1; i < id_length; ++i) {
158     ebml_id <<= 8;
159     read_status = pReader->Read(pos + i, 1, &temp_byte);
160 
161     if (read_status < 0)
162       return E_FILE_FORMAT_INVALID;
163     else if (read_status > 0)
164       return E_BUFFER_NOT_FULL;
165 
166     ebml_id |= temp_byte;
167   }
168 
169   len = id_length;
170   return ebml_id;
171 }
172 
GetUIntLength(IMkvReader * pReader,long long pos,long & len)173 long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) {
174   if (!pReader || pos < 0)
175     return E_FILE_FORMAT_INVALID;
176 
177   long long total, available;
178 
179   int status = pReader->Length(&total, &available);
180   if (status < 0 || (total >= 0 && available > total))
181     return E_FILE_FORMAT_INVALID;
182 
183   len = 1;
184 
185   if (pos >= available)
186     return pos;  // too few bytes available
187 
188   unsigned char b;
189 
190   status = pReader->Read(pos, 1, &b);
191 
192   if (status != 0)
193     return status;
194 
195   if (b == 0)  // we can't handle u-int values larger than 8 bytes
196     return E_FILE_FORMAT_INVALID;
197 
198   unsigned char m = 0x80;
199 
200   while (!(b & m)) {
201     m >>= 1;
202     ++len;
203   }
204 
205   return 0;  // success
206 }
207 
208 // TODO(vigneshv): This function assumes that unsigned values never have their
209 // high bit set.
UnserializeUInt(IMkvReader * pReader,long long pos,long long size)210 long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) {
211   if (!pReader || pos < 0 || (size <= 0) || (size > 8))
212     return E_FILE_FORMAT_INVALID;
213 
214   long long result = 0;
215 
216   for (long long i = 0; i < size; ++i) {
217     unsigned char b;
218 
219     const long status = pReader->Read(pos, 1, &b);
220 
221     if (status < 0)
222       return status;
223 
224     result <<= 8;
225     result |= b;
226 
227     ++pos;
228   }
229 
230   return result;
231 }
232 
UnserializeFloat(IMkvReader * pReader,long long pos,long long size_,double & result)233 long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
234                       double& result) {
235   if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8)))
236     return E_FILE_FORMAT_INVALID;
237 
238   const long size = static_cast<long>(size_);
239 
240   unsigned char buf[8];
241 
242   const int status = pReader->Read(pos, size, buf);
243 
244   if (status < 0)  // error
245     return status;
246 
247   if (size == 4) {
248     union {
249       float f;
250       uint32_t ff;
251       static_assert(sizeof(float) == sizeof(uint32_t), "");
252     };
253 
254     ff = 0;
255 
256     for (int i = 0;;) {
257       ff |= buf[i];
258 
259       if (++i >= 4)
260         break;
261 
262       ff <<= 8;
263     }
264 
265     result = f;
266   } else {
267     union {
268       double d;
269       uint64_t dd;
270       static_assert(sizeof(double) == sizeof(uint64_t), "");
271     };
272 
273     dd = 0;
274 
275     for (int i = 0;;) {
276       dd |= buf[i];
277 
278       if (++i >= 8)
279         break;
280 
281       dd <<= 8;
282     }
283 
284     result = d;
285   }
286 
287   if (mkvparser::isinf(result) || mkvparser::isnan(result))
288     return E_FILE_FORMAT_INVALID;
289 
290   return 0;
291 }
292 
UnserializeInt(IMkvReader * pReader,long long pos,long long size,long long & result_ref)293 long UnserializeInt(IMkvReader* pReader, long long pos, long long size,
294                     long long& result_ref) {
295   if (!pReader || pos < 0 || size < 1 || size > 8)
296     return E_FILE_FORMAT_INVALID;
297 
298   signed char first_byte = 0;
299   const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte);
300 
301   if (status < 0)
302     return status;
303 
304   unsigned long long result = static_cast<unsigned long long>(first_byte);
305   ++pos;
306 
307   for (long i = 1; i < size; ++i) {
308     unsigned char b;
309 
310     const long status = pReader->Read(pos, 1, &b);
311 
312     if (status < 0)
313       return status;
314 
315     result <<= 8;
316     result |= b;
317 
318     ++pos;
319   }
320 
321   result_ref = static_cast<long long>(result);
322   return 0;
323 }
324 
UnserializeString(IMkvReader * pReader,long long pos,long long size,char * & str)325 long UnserializeString(IMkvReader* pReader, long long pos, long long size,
326                        char*& str) {
327   delete[] str;
328   str = NULL;
329 
330   if (size >= LONG_MAX || size < 0 || size > kStringElementSizeLimit)
331     return E_FILE_FORMAT_INVALID;
332 
333   // +1 for '\0' terminator
334   const long required_size = static_cast<long>(size) + 1;
335 
336   str = SafeArrayAlloc<char>(1, required_size);
337   if (str == NULL)
338     return E_FILE_FORMAT_INVALID;
339 
340   unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
341 
342   const long status = pReader->Read(pos, static_cast<long>(size), buf);
343 
344   if (status) {
345     delete[] str;
346     str = NULL;
347 
348     return status;
349   }
350 
351   str[required_size - 1] = '\0';
352   return 0;
353 }
354 
ParseElementHeader(IMkvReader * pReader,long long & pos,long long stop,long long & id,long long & size)355 long ParseElementHeader(IMkvReader* pReader, long long& pos, long long stop,
356                         long long& id, long long& size) {
357   if (stop >= 0 && pos >= stop)
358     return E_FILE_FORMAT_INVALID;
359 
360   long len;
361 
362   id = ReadID(pReader, pos, len);
363 
364   if (id < 0)
365     return E_FILE_FORMAT_INVALID;
366 
367   pos += len;  // consume id
368 
369   if (stop >= 0 && pos >= stop)
370     return E_FILE_FORMAT_INVALID;
371 
372   size = ReadUInt(pReader, pos, len);
373 
374   if (size < 0 || len < 1 || len > 8) {
375     // Invalid: Negative payload size, negative or 0 length integer, or integer
376     // larger than 64 bits (libwebm cannot handle them).
377     return E_FILE_FORMAT_INVALID;
378   }
379 
380   // Avoid rolling over pos when very close to LLONG_MAX.
381   const unsigned long long rollover_check =
382       static_cast<unsigned long long>(pos) + len;
383   if (rollover_check > LLONG_MAX)
384     return E_FILE_FORMAT_INVALID;
385 
386   pos += len;  // consume length of size
387 
388   // pos now designates payload
389 
390   if (stop >= 0 && pos > stop)
391     return E_FILE_FORMAT_INVALID;
392 
393   return 0;  // success
394 }
395 
Match(IMkvReader * pReader,long long & pos,unsigned long expected_id,long long & val)396 bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
397            long long& val) {
398   if (!pReader || pos < 0)
399     return false;
400 
401   long long total = 0;
402   long long available = 0;
403 
404   const long status = pReader->Length(&total, &available);
405   if (status < 0 || (total >= 0 && available > total))
406     return false;
407 
408   long len = 0;
409 
410   const long long id = ReadID(pReader, pos, len);
411   if (id < 0 || (available - pos) > len)
412     return false;
413 
414   if (static_cast<unsigned long>(id) != expected_id)
415     return false;
416 
417   pos += len;  // consume id
418 
419   const long long size = ReadUInt(pReader, pos, len);
420   if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len)
421     return false;
422 
423   pos += len;  // consume length of size of payload
424 
425   val = UnserializeUInt(pReader, pos, size);
426   if (val < 0)
427     return false;
428 
429   pos += size;  // consume size of payload
430 
431   return true;
432 }
433 
Match(IMkvReader * pReader,long long & pos,unsigned long expected_id,unsigned char * & buf,size_t & buflen)434 bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
435            unsigned char*& buf, size_t& buflen) {
436   if (!pReader || pos < 0)
437     return false;
438 
439   long long total = 0;
440   long long available = 0;
441 
442   long status = pReader->Length(&total, &available);
443   if (status < 0 || (total >= 0 && available > total))
444     return false;
445 
446   long len = 0;
447   const long long id = ReadID(pReader, pos, len);
448   if (id < 0 || (available - pos) > len)
449     return false;
450 
451   if (static_cast<unsigned long>(id) != expected_id)
452     return false;
453 
454   pos += len;  // consume id
455 
456   const long long size = ReadUInt(pReader, pos, len);
457   if (size < 0 || len <= 0 || len > 8 || (available - pos) > len)
458     return false;
459 
460   unsigned long long rollover_check =
461       static_cast<unsigned long long>(pos) + len;
462   if (rollover_check > LLONG_MAX)
463     return false;
464 
465   pos += len;  // consume length of size of payload
466 
467   rollover_check = static_cast<unsigned long long>(pos) + size;
468   if (rollover_check > LLONG_MAX)
469     return false;
470 
471   if ((pos + size) > available)
472     return false;
473 
474   if (size >= LONG_MAX)
475     return false;
476 
477   const long buflen_ = static_cast<long>(size);
478 
479   buf = SafeArrayAlloc<unsigned char>(1, buflen_);
480   if (!buf)
481     return false;
482 
483   status = pReader->Read(pos, buflen_, buf);
484   if (status != 0)
485     return false;
486 
487   buflen = buflen_;
488 
489   pos += size;  // consume size of payload
490   return true;
491 }
492 
EBMLHeader()493 EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); }
494 
~EBMLHeader()495 EBMLHeader::~EBMLHeader() { delete[] m_docType; }
496 
Init()497 void EBMLHeader::Init() {
498   m_version = 1;
499   m_readVersion = 1;
500   m_maxIdLength = 4;
501   m_maxSizeLength = 8;
502 
503   if (m_docType) {
504     delete[] m_docType;
505     m_docType = NULL;
506   }
507 
508   m_docTypeVersion = 1;
509   m_docTypeReadVersion = 1;
510 }
511 
Parse(IMkvReader * pReader,long long & pos)512 long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) {
513   if (!pReader)
514     return E_FILE_FORMAT_INVALID;
515 
516   long long total, available;
517 
518   long status = pReader->Length(&total, &available);
519 
520   if (status < 0)  // error
521     return status;
522 
523   pos = 0;
524 
525   // Scan until we find what looks like the first byte of the EBML header.
526   const long long kMaxScanBytes = (available >= 1024) ? 1024 : available;
527   const unsigned char kEbmlByte0 = 0x1A;
528   unsigned char scan_byte = 0;
529 
530   while (pos < kMaxScanBytes) {
531     status = pReader->Read(pos, 1, &scan_byte);
532 
533     if (status < 0)  // error
534       return status;
535     else if (status > 0)
536       return E_BUFFER_NOT_FULL;
537 
538     if (scan_byte == kEbmlByte0)
539       break;
540 
541     ++pos;
542   }
543 
544   long len = 0;
545   const long long ebml_id = ReadID(pReader, pos, len);
546 
547   if (ebml_id == E_BUFFER_NOT_FULL)
548     return E_BUFFER_NOT_FULL;
549 
550   if (len != 4 || ebml_id != libwebm::kMkvEBML)
551     return E_FILE_FORMAT_INVALID;
552 
553   // Move read pos forward to the EBML header size field.
554   pos += 4;
555 
556   // Read length of size field.
557   long long result = GetUIntLength(pReader, pos, len);
558 
559   if (result < 0)  // error
560     return E_FILE_FORMAT_INVALID;
561   else if (result > 0)  // need more data
562     return E_BUFFER_NOT_FULL;
563 
564   if (len < 1 || len > 8)
565     return E_FILE_FORMAT_INVALID;
566 
567   if ((total >= 0) && ((total - pos) < len))
568     return E_FILE_FORMAT_INVALID;
569 
570   if ((available - pos) < len)
571     return pos + len;  // try again later
572 
573   // Read the EBML header size.
574   result = ReadUInt(pReader, pos, len);
575 
576   if (result < 0)  // error
577     return result;
578 
579   pos += len;  // consume size field
580 
581   // pos now designates start of payload
582 
583   if ((total >= 0) && ((total - pos) < result))
584     return E_FILE_FORMAT_INVALID;
585 
586   if ((available - pos) < result)
587     return pos + result;
588 
589   const long long end = pos + result;
590 
591   Init();
592 
593   while (pos < end) {
594     long long id, size;
595 
596     status = ParseElementHeader(pReader, pos, end, id, size);
597 
598     if (status < 0)  // error
599       return status;
600 
601     if (size == 0)
602       return E_FILE_FORMAT_INVALID;
603 
604     if (id == libwebm::kMkvEBMLVersion) {
605       m_version = UnserializeUInt(pReader, pos, size);
606 
607       if (m_version <= 0)
608         return E_FILE_FORMAT_INVALID;
609     } else if (id == libwebm::kMkvEBMLReadVersion) {
610       m_readVersion = UnserializeUInt(pReader, pos, size);
611 
612       if (m_readVersion <= 0)
613         return E_FILE_FORMAT_INVALID;
614     } else if (id == libwebm::kMkvEBMLMaxIDLength) {
615       m_maxIdLength = UnserializeUInt(pReader, pos, size);
616 
617       if (m_maxIdLength <= 0)
618         return E_FILE_FORMAT_INVALID;
619     } else if (id == libwebm::kMkvEBMLMaxSizeLength) {
620       m_maxSizeLength = UnserializeUInt(pReader, pos, size);
621 
622       if (m_maxSizeLength <= 0)
623         return E_FILE_FORMAT_INVALID;
624     } else if (id == libwebm::kMkvDocType) {
625       if (m_docType)
626         return E_FILE_FORMAT_INVALID;
627 
628       status = UnserializeString(pReader, pos, size, m_docType);
629 
630       if (status)  // error
631         return status;
632     } else if (id == libwebm::kMkvDocTypeVersion) {
633       m_docTypeVersion = UnserializeUInt(pReader, pos, size);
634 
635       if (m_docTypeVersion <= 0)
636         return E_FILE_FORMAT_INVALID;
637     } else if (id == libwebm::kMkvDocTypeReadVersion) {
638       m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
639 
640       if (m_docTypeReadVersion <= 0)
641         return E_FILE_FORMAT_INVALID;
642     }
643 
644     pos += size;
645   }
646 
647   if (pos != end)
648     return E_FILE_FORMAT_INVALID;
649 
650   // Make sure DocType, DocTypeReadVersion, and DocTypeVersion are valid.
651   if (m_docType == NULL || m_docTypeReadVersion <= 0 || m_docTypeVersion <= 0)
652     return E_FILE_FORMAT_INVALID;
653 
654   // Make sure EBMLMaxIDLength and EBMLMaxSizeLength are valid.
655   if (m_maxIdLength <= 0 || m_maxIdLength > 4 || m_maxSizeLength <= 0 ||
656       m_maxSizeLength > 8)
657     return E_FILE_FORMAT_INVALID;
658 
659   return 0;
660 }
661 
Segment(IMkvReader * pReader,long long elem_start,long long start,long long size)662 Segment::Segment(IMkvReader* pReader, long long elem_start,
663                  // long long elem_size,
664                  long long start, long long size)
665     : m_pReader(pReader),
666       m_element_start(elem_start),
667       // m_element_size(elem_size),
668       m_start(start),
669       m_size(size),
670       m_pos(start),
671       m_pUnknownSize(0),
672       m_pSeekHead(NULL),
673       m_pInfo(NULL),
674       m_pTracks(NULL),
675       m_pCues(NULL),
676       m_pChapters(NULL),
677       m_pTags(NULL),
678       m_clusters(NULL),
679       m_clusterCount(0),
680       m_clusterPreloadCount(0),
681       m_clusterSize(0) {}
682 
~Segment()683 Segment::~Segment() {
684   const long count = m_clusterCount + m_clusterPreloadCount;
685 
686   Cluster** i = m_clusters;
687   Cluster** j = m_clusters + count;
688 
689   while (i != j) {
690     Cluster* const p = *i++;
691     delete p;
692   }
693 
694   delete[] m_clusters;
695 
696   delete m_pTracks;
697   delete m_pInfo;
698   delete m_pCues;
699   delete m_pChapters;
700   delete m_pTags;
701   delete m_pSeekHead;
702 }
703 
CreateInstance(IMkvReader * pReader,long long pos,Segment * & pSegment)704 long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
705                                   Segment*& pSegment) {
706   if (pReader == NULL || pos < 0)
707     return E_PARSE_FAILED;
708 
709   pSegment = NULL;
710 
711   long long total, available;
712 
713   const long status = pReader->Length(&total, &available);
714 
715   if (status < 0)  // error
716     return status;
717 
718   if (available < 0)
719     return -1;
720 
721   if ((total >= 0) && (available > total))
722     return -1;
723 
724   // I would assume that in practice this loop would execute
725   // exactly once, but we allow for other elements (e.g. Void)
726   // to immediately follow the EBML header.  This is fine for
727   // the source filter case (since the entire file is available),
728   // but in the splitter case over a network we should probably
729   // just give up early.  We could for example decide only to
730   // execute this loop a maximum of, say, 10 times.
731   // TODO:
732   // There is an implied "give up early" by only parsing up
733   // to the available limit.  We do do that, but only if the
734   // total file size is unknown.  We could decide to always
735   // use what's available as our limit (irrespective of whether
736   // we happen to know the total file length).  This would have
737   // as its sense "parse this much of the file before giving up",
738   // which a slightly different sense from "try to parse up to
739   // 10 EMBL elements before giving up".
740 
741   for (;;) {
742     if ((total >= 0) && (pos >= total))
743       return E_FILE_FORMAT_INVALID;
744 
745     // Read ID
746     long len;
747     long long result = GetUIntLength(pReader, pos, len);
748 
749     if (result)  // error, or too few available bytes
750       return result;
751 
752     if ((total >= 0) && ((pos + len) > total))
753       return E_FILE_FORMAT_INVALID;
754 
755     if ((pos + len) > available)
756       return pos + len;
757 
758     const long long idpos = pos;
759     const long long id = ReadID(pReader, pos, len);
760 
761     if (id < 0)
762       return E_FILE_FORMAT_INVALID;
763 
764     pos += len;  // consume ID
765 
766     // Read Size
767 
768     result = GetUIntLength(pReader, pos, len);
769 
770     if (result)  // error, or too few available bytes
771       return result;
772 
773     if ((total >= 0) && ((pos + len) > total))
774       return E_FILE_FORMAT_INVALID;
775 
776     if ((pos + len) > available)
777       return pos + len;
778 
779     long long size = ReadUInt(pReader, pos, len);
780 
781     if (size < 0)  // error
782       return size;
783 
784     pos += len;  // consume length of size of element
785 
786     // Pos now points to start of payload
787 
788     // Handle "unknown size" for live streaming of webm files.
789     const long long unknown_size = (1LL << (7 * len)) - 1;
790 
791     if (id == libwebm::kMkvSegment) {
792       if (size == unknown_size)
793         size = -1;
794 
795       else if (total < 0)
796         size = -1;
797 
798       else if ((pos + size) > total)
799         size = -1;
800 
801       pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size);
802       if (pSegment == NULL)
803         return E_PARSE_FAILED;
804 
805       return 0;  // success
806     }
807 
808     if (size == unknown_size)
809       return E_FILE_FORMAT_INVALID;
810 
811     if ((total >= 0) && ((pos + size) > total))
812       return E_FILE_FORMAT_INVALID;
813 
814     if ((pos + size) > available)
815       return pos + size;
816 
817     pos += size;  // consume payload
818   }
819 }
820 
ParseHeaders()821 long long Segment::ParseHeaders() {
822   // Outermost (level 0) segment object has been constructed,
823   // and pos designates start of payload.  We need to find the
824   // inner (level 1) elements.
825   long long total, available;
826 
827   const int status = m_pReader->Length(&total, &available);
828 
829   if (status < 0)  // error
830     return status;
831 
832   if (total > 0 && available > total)
833     return E_FILE_FORMAT_INVALID;
834 
835   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
836 
837   if ((segment_stop >= 0 && total >= 0 && segment_stop > total) ||
838       (segment_stop >= 0 && m_pos > segment_stop)) {
839     return E_FILE_FORMAT_INVALID;
840   }
841 
842   for (;;) {
843     if ((total >= 0) && (m_pos >= total))
844       break;
845 
846     if ((segment_stop >= 0) && (m_pos >= segment_stop))
847       break;
848 
849     long long pos = m_pos;
850     const long long element_start = pos;
851 
852     // Avoid rolling over pos when very close to LLONG_MAX.
853     unsigned long long rollover_check = pos + 1ULL;
854     if (rollover_check > LLONG_MAX)
855       return E_FILE_FORMAT_INVALID;
856 
857     if ((pos + 1) > available)
858       return (pos + 1);
859 
860     long len;
861     long long result = GetUIntLength(m_pReader, pos, len);
862 
863     if (result < 0)  // error
864       return result;
865 
866     if (result > 0) {
867       // MkvReader doesn't have enough data to satisfy this read attempt.
868       return (pos + 1);
869     }
870 
871     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
872       return E_FILE_FORMAT_INVALID;
873 
874     if ((pos + len) > available)
875       return pos + len;
876 
877     const long long idpos = pos;
878     const long long id = ReadID(m_pReader, idpos, len);
879 
880     if (id < 0)
881       return E_FILE_FORMAT_INVALID;
882 
883     if (id == libwebm::kMkvCluster)
884       break;
885 
886     pos += len;  // consume ID
887 
888     if ((pos + 1) > available)
889       return (pos + 1);
890 
891     // Read Size
892     result = GetUIntLength(m_pReader, pos, len);
893 
894     if (result < 0)  // error
895       return result;
896 
897     if (result > 0) {
898       // MkvReader doesn't have enough data to satisfy this read attempt.
899       return (pos + 1);
900     }
901 
902     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
903       return E_FILE_FORMAT_INVALID;
904 
905     if ((pos + len) > available)
906       return pos + len;
907 
908     const long long size = ReadUInt(m_pReader, pos, len);
909 
910     if (size < 0 || len < 1 || len > 8) {
911       // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or
912       // len > 8 is true instead of checking this _everywhere_.
913       return size;
914     }
915 
916     pos += len;  // consume length of size of element
917 
918     // Avoid rolling over pos when very close to LLONG_MAX.
919     rollover_check = static_cast<unsigned long long>(pos) + size;
920     if (rollover_check > LLONG_MAX)
921       return E_FILE_FORMAT_INVALID;
922 
923     const long long element_size = size + pos - element_start;
924 
925     // Pos now points to start of payload
926 
927     if ((segment_stop >= 0) && ((pos + size) > segment_stop))
928       return E_FILE_FORMAT_INVALID;
929 
930     // We read EBML elements either in total or nothing at all.
931 
932     if ((pos + size) > available)
933       return pos + size;
934 
935     if (id == libwebm::kMkvInfo) {
936       if (m_pInfo)
937         return E_FILE_FORMAT_INVALID;
938 
939       m_pInfo = new (std::nothrow)
940           SegmentInfo(this, pos, size, element_start, element_size);
941 
942       if (m_pInfo == NULL)
943         return -1;
944 
945       const long status = m_pInfo->Parse();
946 
947       if (status)
948         return status;
949     } else if (id == libwebm::kMkvTracks) {
950       if (m_pTracks)
951         return E_FILE_FORMAT_INVALID;
952 
953       m_pTracks = new (std::nothrow)
954           Tracks(this, pos, size, element_start, element_size);
955 
956       if (m_pTracks == NULL)
957         return -1;
958 
959       const long status = m_pTracks->Parse();
960 
961       if (status)
962         return status;
963     } else if (id == libwebm::kMkvCues) {
964       if (m_pCues == NULL) {
965         m_pCues = new (std::nothrow)
966             Cues(this, pos, size, element_start, element_size);
967 
968         if (m_pCues == NULL)
969           return -1;
970       }
971     } else if (id == libwebm::kMkvSeekHead) {
972       if (m_pSeekHead == NULL) {
973         m_pSeekHead = new (std::nothrow)
974             SeekHead(this, pos, size, element_start, element_size);
975 
976         if (m_pSeekHead == NULL)
977           return -1;
978 
979         const long status = m_pSeekHead->Parse();
980 
981         if (status)
982           return status;
983       }
984     } else if (id == libwebm::kMkvChapters) {
985       if (m_pChapters == NULL) {
986         m_pChapters = new (std::nothrow)
987             Chapters(this, pos, size, element_start, element_size);
988 
989         if (m_pChapters == NULL)
990           return -1;
991 
992         const long status = m_pChapters->Parse();
993 
994         if (status)
995           return status;
996       }
997     } else if (id == libwebm::kMkvTags) {
998       if (m_pTags == NULL) {
999         m_pTags = new (std::nothrow)
1000             Tags(this, pos, size, element_start, element_size);
1001 
1002         if (m_pTags == NULL)
1003           return -1;
1004 
1005         const long status = m_pTags->Parse();
1006 
1007         if (status)
1008           return status;
1009       }
1010     }
1011 
1012     m_pos = pos + size;  // consume payload
1013   }
1014 
1015   if (segment_stop >= 0 && m_pos > segment_stop)
1016     return E_FILE_FORMAT_INVALID;
1017 
1018   if (m_pInfo == NULL)  // TODO: liberalize this behavior
1019     return E_FILE_FORMAT_INVALID;
1020 
1021   if (m_pTracks == NULL)
1022     return E_FILE_FORMAT_INVALID;
1023 
1024   return 0;  // success
1025 }
1026 
LoadCluster(long long & pos,long & len)1027 long Segment::LoadCluster(long long& pos, long& len) {
1028   for (;;) {
1029     const long result = DoLoadCluster(pos, len);
1030 
1031     if (result <= 1)
1032       return result;
1033   }
1034 }
1035 
DoLoadCluster(long long & pos,long & len)1036 long Segment::DoLoadCluster(long long& pos, long& len) {
1037   if (m_pos < 0)
1038     return DoLoadClusterUnknownSize(pos, len);
1039 
1040   long long total, avail;
1041 
1042   long status = m_pReader->Length(&total, &avail);
1043 
1044   if (status < 0)  // error
1045     return status;
1046 
1047   if (total >= 0 && avail > total)
1048     return E_FILE_FORMAT_INVALID;
1049 
1050   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
1051 
1052   long long cluster_off = -1;  // offset relative to start of segment
1053   long long cluster_size = -1;  // size of cluster payload
1054 
1055   for (;;) {
1056     if ((total >= 0) && (m_pos >= total))
1057       return 1;  // no more clusters
1058 
1059     if ((segment_stop >= 0) && (m_pos >= segment_stop))
1060       return 1;  // no more clusters
1061 
1062     pos = m_pos;
1063 
1064     // Read ID
1065 
1066     if ((pos + 1) > avail) {
1067       len = 1;
1068       return E_BUFFER_NOT_FULL;
1069     }
1070 
1071     long long result = GetUIntLength(m_pReader, pos, len);
1072 
1073     if (result < 0)  // error
1074       return static_cast<long>(result);
1075 
1076     if (result > 0)
1077       return E_BUFFER_NOT_FULL;
1078 
1079     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1080       return E_FILE_FORMAT_INVALID;
1081 
1082     if ((pos + len) > avail)
1083       return E_BUFFER_NOT_FULL;
1084 
1085     const long long idpos = pos;
1086     const long long id = ReadID(m_pReader, idpos, len);
1087 
1088     if (id < 0)
1089       return E_FILE_FORMAT_INVALID;
1090 
1091     pos += len;  // consume ID
1092 
1093     // Read Size
1094 
1095     if ((pos + 1) > avail) {
1096       len = 1;
1097       return E_BUFFER_NOT_FULL;
1098     }
1099 
1100     result = GetUIntLength(m_pReader, pos, len);
1101 
1102     if (result < 0)  // error
1103       return static_cast<long>(result);
1104 
1105     if (result > 0)
1106       return E_BUFFER_NOT_FULL;
1107 
1108     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1109       return E_FILE_FORMAT_INVALID;
1110 
1111     if ((pos + len) > avail)
1112       return E_BUFFER_NOT_FULL;
1113 
1114     const long long size = ReadUInt(m_pReader, pos, len);
1115 
1116     if (size < 0)  // error
1117       return static_cast<long>(size);
1118 
1119     pos += len;  // consume length of size of element
1120 
1121     // pos now points to start of payload
1122 
1123     if (size == 0) {
1124       // Missing element payload: move on.
1125       m_pos = pos;
1126       continue;
1127     }
1128 
1129     const long long unknown_size = (1LL << (7 * len)) - 1;
1130 
1131     if ((segment_stop >= 0) && (size != unknown_size) &&
1132         ((pos + size) > segment_stop)) {
1133       return E_FILE_FORMAT_INVALID;
1134     }
1135 
1136     if (id == libwebm::kMkvCues) {
1137       if (size == unknown_size) {
1138         // Cues element of unknown size: Not supported.
1139         return E_FILE_FORMAT_INVALID;
1140       }
1141 
1142       if (m_pCues == NULL) {
1143         const long long element_size = (pos - idpos) + size;
1144 
1145         m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
1146         if (m_pCues == NULL)
1147           return -1;
1148       }
1149 
1150       m_pos = pos + size;  // consume payload
1151       continue;
1152     }
1153 
1154     if (id != libwebm::kMkvCluster) {
1155       // Besides the Segment, Libwebm allows only cluster elements of unknown
1156       // size. Fail the parse upon encountering a non-cluster element reporting
1157       // unknown size.
1158       if (size == unknown_size)
1159         return E_FILE_FORMAT_INVALID;
1160 
1161       m_pos = pos + size;  // consume payload
1162       continue;
1163     }
1164 
1165     // We have a cluster.
1166 
1167     cluster_off = idpos - m_start;  // relative pos
1168 
1169     if (size != unknown_size)
1170       cluster_size = size;
1171 
1172     break;
1173   }
1174 
1175   if (cluster_off < 0) {
1176     // No cluster, die.
1177     return E_FILE_FORMAT_INVALID;
1178   }
1179 
1180   long long pos_;
1181   long len_;
1182 
1183   status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
1184 
1185   if (status < 0) {  // error, or underflow
1186     pos = pos_;
1187     len = len_;
1188 
1189     return status;
1190   }
1191 
1192   // status == 0 means "no block entries found"
1193   // status > 0 means "found at least one block entry"
1194 
1195   // TODO:
1196   // The issue here is that the segment increments its own
1197   // pos ptr past the most recent cluster parsed, and then
1198   // starts from there to parse the next cluster.  If we
1199   // don't know the size of the current cluster, then we
1200   // must either parse its payload (as we do below), looking
1201   // for the cluster (or cues) ID to terminate the parse.
1202   // This isn't really what we want: rather, we really need
1203   // a way to create the curr cluster object immediately.
1204   // The pity is that cluster::parse can determine its own
1205   // boundary, and we largely duplicate that same logic here.
1206   //
1207   // Maybe we need to get rid of our look-ahead preloading
1208   // in source::parse???
1209   //
1210   // As we're parsing the blocks in the curr cluster
1211   //(in cluster::parse), we should have some way to signal
1212   // to the segment that we have determined the boundary,
1213   // so it can adjust its own segment::m_pos member.
1214   //
1215   // The problem is that we're asserting in asyncreadinit,
1216   // because we adjust the pos down to the curr seek pos,
1217   // and the resulting adjusted len is > 2GB.  I'm suspicious
1218   // that this is even correct, but even if it is, we can't
1219   // be loading that much data in the cache anyway.
1220 
1221   const long idx = m_clusterCount;
1222 
1223   if (m_clusterPreloadCount > 0) {
1224     if (idx >= m_clusterSize)
1225       return E_FILE_FORMAT_INVALID;
1226 
1227     Cluster* const pCluster = m_clusters[idx];
1228     if (pCluster == NULL || pCluster->m_index >= 0)
1229       return E_FILE_FORMAT_INVALID;
1230 
1231     const long long off = pCluster->GetPosition();
1232     if (off < 0)
1233       return E_FILE_FORMAT_INVALID;
1234 
1235     if (off == cluster_off) {  // preloaded already
1236       if (status == 0)  // no entries found
1237         return E_FILE_FORMAT_INVALID;
1238 
1239       if (cluster_size >= 0)
1240         pos += cluster_size;
1241       else {
1242         const long long element_size = pCluster->GetElementSize();
1243 
1244         if (element_size <= 0)
1245           return E_FILE_FORMAT_INVALID;  // TODO: handle this case
1246 
1247         pos = pCluster->m_element_start + element_size;
1248       }
1249 
1250       pCluster->m_index = idx;  // move from preloaded to loaded
1251       ++m_clusterCount;
1252       --m_clusterPreloadCount;
1253 
1254       m_pos = pos;  // consume payload
1255       if (segment_stop >= 0 && m_pos > segment_stop)
1256         return E_FILE_FORMAT_INVALID;
1257 
1258       return 0;  // success
1259     }
1260   }
1261 
1262   if (status == 0) {  // no entries found
1263     if (cluster_size >= 0)
1264       pos += cluster_size;
1265 
1266     if ((total >= 0) && (pos >= total)) {
1267       m_pos = total;
1268       return 1;  // no more clusters
1269     }
1270 
1271     if ((segment_stop >= 0) && (pos >= segment_stop)) {
1272       m_pos = segment_stop;
1273       return 1;  // no more clusters
1274     }
1275 
1276     m_pos = pos;
1277     return 2;  // try again
1278   }
1279 
1280   // status > 0 means we have an entry
1281 
1282   Cluster* const pCluster = Cluster::Create(this, idx, cluster_off);
1283   if (pCluster == NULL)
1284     return -1;
1285 
1286   if (!AppendCluster(pCluster)) {
1287     delete pCluster;
1288     return -1;
1289   }
1290 
1291   if (cluster_size >= 0) {
1292     pos += cluster_size;
1293 
1294     m_pos = pos;
1295 
1296     if (segment_stop > 0 && m_pos > segment_stop)
1297       return E_FILE_FORMAT_INVALID;
1298 
1299     return 0;
1300   }
1301 
1302   m_pUnknownSize = pCluster;
1303   m_pos = -pos;
1304 
1305   return 0;  // partial success, since we have a new cluster
1306 
1307   // status == 0 means "no block entries found"
1308   // pos designates start of payload
1309   // m_pos has NOT been adjusted yet (in case we need to come back here)
1310 }
1311 
DoLoadClusterUnknownSize(long long & pos,long & len)1312 long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
1313   if (m_pos >= 0 || m_pUnknownSize == NULL)
1314     return E_PARSE_FAILED;
1315 
1316   const long status = m_pUnknownSize->Parse(pos, len);
1317 
1318   if (status < 0)  // error or underflow
1319     return status;
1320 
1321   if (status == 0)  // parsed a block
1322     return 2;  // continue parsing
1323 
1324   const long long start = m_pUnknownSize->m_element_start;
1325   const long long size = m_pUnknownSize->GetElementSize();
1326 
1327   if (size < 0)
1328     return E_FILE_FORMAT_INVALID;
1329 
1330   pos = start + size;
1331   m_pos = pos;
1332 
1333   m_pUnknownSize = 0;
1334 
1335   return 2;  // continue parsing
1336 }
1337 
AppendCluster(Cluster * pCluster)1338 bool Segment::AppendCluster(Cluster* pCluster) {
1339   if (pCluster == NULL || pCluster->m_index < 0)
1340     return false;
1341 
1342   const long count = m_clusterCount + m_clusterPreloadCount;
1343 
1344   long& size = m_clusterSize;
1345   const long idx = pCluster->m_index;
1346 
1347   if (size < count || idx != m_clusterCount)
1348     return false;
1349 
1350   if (count >= size) {
1351     const long n = (size <= 0) ? 2048 : 2 * size;
1352 
1353     Cluster** const qq = new (std::nothrow) Cluster*[n];
1354     if (qq == NULL)
1355       return false;
1356 
1357     Cluster** q = qq;
1358     Cluster** p = m_clusters;
1359     Cluster** const pp = p + count;
1360 
1361     while (p != pp)
1362       *q++ = *p++;
1363 
1364     delete[] m_clusters;
1365 
1366     m_clusters = qq;
1367     size = n;
1368   }
1369 
1370   if (m_clusterPreloadCount > 0) {
1371     Cluster** const p = m_clusters + m_clusterCount;
1372     if (*p == NULL || (*p)->m_index >= 0)
1373       return false;
1374 
1375     Cluster** q = p + m_clusterPreloadCount;
1376     if (q >= (m_clusters + size))
1377       return false;
1378 
1379     for (;;) {
1380       Cluster** const qq = q - 1;
1381       if ((*qq)->m_index >= 0)
1382         return false;
1383 
1384       *q = *qq;
1385       q = qq;
1386 
1387       if (q == p)
1388         break;
1389     }
1390   }
1391 
1392   m_clusters[idx] = pCluster;
1393   ++m_clusterCount;
1394   return true;
1395 }
1396 
PreloadCluster(Cluster * pCluster,ptrdiff_t idx)1397 bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
1398   if (pCluster == NULL || pCluster->m_index >= 0 || idx < m_clusterCount)
1399     return false;
1400 
1401   const long count = m_clusterCount + m_clusterPreloadCount;
1402 
1403   long& size = m_clusterSize;
1404   if (size < count)
1405     return false;
1406 
1407   if (count >= size) {
1408     const long n = (size <= 0) ? 2048 : 2 * size;
1409 
1410     Cluster** const qq = new (std::nothrow) Cluster*[n];
1411     if (qq == NULL)
1412       return false;
1413     Cluster** q = qq;
1414 
1415     Cluster** p = m_clusters;
1416     Cluster** const pp = p + count;
1417 
1418     while (p != pp)
1419       *q++ = *p++;
1420 
1421     delete[] m_clusters;
1422 
1423     m_clusters = qq;
1424     size = n;
1425   }
1426 
1427   if (m_clusters == NULL)
1428     return false;
1429 
1430   Cluster** const p = m_clusters + idx;
1431 
1432   Cluster** q = m_clusters + count;
1433   if (q < p || q >= (m_clusters + size))
1434     return false;
1435 
1436   while (q > p) {
1437     Cluster** const qq = q - 1;
1438 
1439     if ((*qq)->m_index >= 0)
1440       return false;
1441 
1442     *q = *qq;
1443     q = qq;
1444   }
1445 
1446   m_clusters[idx] = pCluster;
1447   ++m_clusterPreloadCount;
1448   return true;
1449 }
1450 
Load()1451 long Segment::Load() {
1452   if (m_clusters != NULL || m_clusterSize != 0 || m_clusterCount != 0)
1453     return E_PARSE_FAILED;
1454 
1455   // Outermost (level 0) segment object has been constructed,
1456   // and pos designates start of payload.  We need to find the
1457   // inner (level 1) elements.
1458 
1459   const long long header_status = ParseHeaders();
1460 
1461   if (header_status < 0)  // error
1462     return static_cast<long>(header_status);
1463 
1464   if (header_status > 0)  // underflow
1465     return E_BUFFER_NOT_FULL;
1466 
1467   if (m_pInfo == NULL || m_pTracks == NULL)
1468     return E_FILE_FORMAT_INVALID;
1469 
1470   for (;;) {
1471     const long status = LoadCluster();
1472 
1473     if (status < 0)  // error
1474       return status;
1475 
1476     if (status >= 1)  // no more clusters
1477       return 0;
1478   }
1479 }
1480 
Entry()1481 SeekHead::Entry::Entry() : id(0), pos(0), element_start(0), element_size(0) {}
1482 
SeekHead(Segment * pSegment,long long start,long long size_,long long element_start,long long element_size)1483 SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
1484                    long long element_start, long long element_size)
1485     : m_pSegment(pSegment),
1486       m_start(start),
1487       m_size(size_),
1488       m_element_start(element_start),
1489       m_element_size(element_size),
1490       m_entries(0),
1491       m_entry_count(0),
1492       m_void_elements(0),
1493       m_void_element_count(0) {}
1494 
~SeekHead()1495 SeekHead::~SeekHead() {
1496   delete[] m_entries;
1497   delete[] m_void_elements;
1498 }
1499 
Parse()1500 long SeekHead::Parse() {
1501   IMkvReader* const pReader = m_pSegment->m_pReader;
1502 
1503   long long pos = m_start;
1504   const long long stop = m_start + m_size;
1505 
1506   // first count the seek head entries
1507 
1508   long long entry_count = 0;
1509   long long void_element_count = 0;
1510 
1511   while (pos < stop) {
1512     long long id, size;
1513 
1514     const long status = ParseElementHeader(pReader, pos, stop, id, size);
1515 
1516     if (status < 0)  // error
1517       return status;
1518 
1519     if (id == libwebm::kMkvSeek) {
1520       ++entry_count;
1521       if (entry_count > INT_MAX)
1522         return E_PARSE_FAILED;
1523     } else if (id == libwebm::kMkvVoid) {
1524       ++void_element_count;
1525       if (void_element_count > INT_MAX)
1526         return E_PARSE_FAILED;
1527     }
1528 
1529     pos += size;  // consume payload
1530 
1531     if (pos > stop)
1532       return E_FILE_FORMAT_INVALID;
1533   }
1534 
1535   if (pos != stop)
1536     return E_FILE_FORMAT_INVALID;
1537 
1538   if (entry_count > 0) {
1539     m_entries = new (std::nothrow) Entry[static_cast<size_t>(entry_count)];
1540 
1541     if (m_entries == NULL)
1542       return -1;
1543   }
1544 
1545   if (void_element_count > 0) {
1546     m_void_elements =
1547         new (std::nothrow) VoidElement[static_cast<size_t>(void_element_count)];
1548 
1549     if (m_void_elements == NULL)
1550       return -1;
1551   }
1552 
1553   // now parse the entries and void elements
1554 
1555   Entry* pEntry = m_entries;
1556   VoidElement* pVoidElement = m_void_elements;
1557 
1558   pos = m_start;
1559 
1560   while (pos < stop) {
1561     const long long idpos = pos;
1562 
1563     long long id, size;
1564 
1565     const long status = ParseElementHeader(pReader, pos, stop, id, size);
1566 
1567     if (status < 0)  // error
1568       return status;
1569 
1570     if (id == libwebm::kMkvSeek && entry_count > 0) {
1571       if (ParseEntry(pReader, pos, size, pEntry)) {
1572         Entry& e = *pEntry++;
1573 
1574         e.element_start = idpos;
1575         e.element_size = (pos + size) - idpos;
1576       }
1577     } else if (id == libwebm::kMkvVoid && void_element_count > 0) {
1578       VoidElement& e = *pVoidElement++;
1579 
1580       e.element_start = idpos;
1581       e.element_size = (pos + size) - idpos;
1582     }
1583 
1584     pos += size;  // consume payload
1585     if (pos > stop)
1586       return E_FILE_FORMAT_INVALID;
1587   }
1588 
1589   if (pos != stop)
1590     return E_FILE_FORMAT_INVALID;
1591 
1592   ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
1593   assert(count_ >= 0);
1594   assert(static_cast<long long>(count_) <= entry_count);
1595 
1596   m_entry_count = static_cast<int>(count_);
1597 
1598   count_ = ptrdiff_t(pVoidElement - m_void_elements);
1599   assert(count_ >= 0);
1600   assert(static_cast<long long>(count_) <= void_element_count);
1601 
1602   m_void_element_count = static_cast<int>(count_);
1603 
1604   return 0;
1605 }
1606 
GetCount() const1607 int SeekHead::GetCount() const { return m_entry_count; }
1608 
GetEntry(int idx) const1609 const SeekHead::Entry* SeekHead::GetEntry(int idx) const {
1610   if (idx < 0)
1611     return 0;
1612 
1613   if (idx >= m_entry_count)
1614     return 0;
1615 
1616   return m_entries + idx;
1617 }
1618 
GetVoidElementCount() const1619 int SeekHead::GetVoidElementCount() const { return m_void_element_count; }
1620 
GetVoidElement(int idx) const1621 const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
1622   if (idx < 0)
1623     return 0;
1624 
1625   if (idx >= m_void_element_count)
1626     return 0;
1627 
1628   return m_void_elements + idx;
1629 }
1630 
ParseCues(long long off,long long & pos,long & len)1631 long Segment::ParseCues(long long off, long long& pos, long& len) {
1632   if (m_pCues)
1633     return 0;  // success
1634 
1635   if (off < 0)
1636     return -1;
1637 
1638   long long total, avail;
1639 
1640   const int status = m_pReader->Length(&total, &avail);
1641 
1642   if (status < 0)  // error
1643     return status;
1644 
1645   assert((total < 0) || (avail <= total));
1646 
1647   pos = m_start + off;
1648 
1649   if ((total < 0) || (pos >= total))
1650     return 1;  // don't bother parsing cues
1651 
1652   const long long element_start = pos;
1653   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
1654 
1655   if ((pos + 1) > avail) {
1656     len = 1;
1657     return E_BUFFER_NOT_FULL;
1658   }
1659 
1660   long long result = GetUIntLength(m_pReader, pos, len);
1661 
1662   if (result < 0)  // error
1663     return static_cast<long>(result);
1664 
1665   if (result > 0)  // underflow (weird)
1666   {
1667     len = 1;
1668     return E_BUFFER_NOT_FULL;
1669   }
1670 
1671   if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1672     return E_FILE_FORMAT_INVALID;
1673 
1674   if ((pos + len) > avail)
1675     return E_BUFFER_NOT_FULL;
1676 
1677   const long long idpos = pos;
1678 
1679   const long long id = ReadID(m_pReader, idpos, len);
1680 
1681   if (id != libwebm::kMkvCues)
1682     return E_FILE_FORMAT_INVALID;
1683 
1684   pos += len;  // consume ID
1685   assert((segment_stop < 0) || (pos <= segment_stop));
1686 
1687   // Read Size
1688 
1689   if ((pos + 1) > avail) {
1690     len = 1;
1691     return E_BUFFER_NOT_FULL;
1692   }
1693 
1694   result = GetUIntLength(m_pReader, pos, len);
1695 
1696   if (result < 0)  // error
1697     return static_cast<long>(result);
1698 
1699   if (result > 0)  // underflow (weird)
1700   {
1701     len = 1;
1702     return E_BUFFER_NOT_FULL;
1703   }
1704 
1705   if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1706     return E_FILE_FORMAT_INVALID;
1707 
1708   if ((pos + len) > avail)
1709     return E_BUFFER_NOT_FULL;
1710 
1711   const long long size = ReadUInt(m_pReader, pos, len);
1712 
1713   if (size < 0)  // error
1714     return static_cast<long>(size);
1715 
1716   if (size == 0)  // weird, although technically not illegal
1717     return 1;  // done
1718 
1719   pos += len;  // consume length of size of element
1720   assert((segment_stop < 0) || (pos <= segment_stop));
1721 
1722   // Pos now points to start of payload
1723 
1724   const long long element_stop = pos + size;
1725 
1726   if ((segment_stop >= 0) && (element_stop > segment_stop))
1727     return E_FILE_FORMAT_INVALID;
1728 
1729   if ((total >= 0) && (element_stop > total))
1730     return 1;  // don't bother parsing anymore
1731 
1732   len = static_cast<long>(size);
1733 
1734   if (element_stop > avail)
1735     return E_BUFFER_NOT_FULL;
1736 
1737   const long long element_size = element_stop - element_start;
1738 
1739   m_pCues =
1740       new (std::nothrow) Cues(this, pos, size, element_start, element_size);
1741   if (m_pCues == NULL)
1742     return -1;
1743 
1744   return 0;  // success
1745 }
1746 
ParseEntry(IMkvReader * pReader,long long start,long long size_,Entry * pEntry)1747 bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
1748                           Entry* pEntry) {
1749   if (size_ <= 0)
1750     return false;
1751 
1752   long long pos = start;
1753   const long long stop = start + size_;
1754 
1755   long len;
1756 
1757   // parse the container for the level-1 element ID
1758 
1759   const long long seekIdId = ReadID(pReader, pos, len);
1760   if (seekIdId < 0)
1761     return false;
1762 
1763   if (seekIdId != libwebm::kMkvSeekID)
1764     return false;
1765 
1766   if ((pos + len) > stop)
1767     return false;
1768 
1769   pos += len;  // consume SeekID id
1770 
1771   const long long seekIdSize = ReadUInt(pReader, pos, len);
1772 
1773   if (seekIdSize <= 0)
1774     return false;
1775 
1776   if ((pos + len) > stop)
1777     return false;
1778 
1779   pos += len;  // consume size of field
1780 
1781   if ((pos + seekIdSize) > stop)
1782     return false;
1783 
1784   pEntry->id = ReadID(pReader, pos, len);  // payload
1785 
1786   if (pEntry->id <= 0)
1787     return false;
1788 
1789   if (len != seekIdSize)
1790     return false;
1791 
1792   pos += seekIdSize;  // consume SeekID payload
1793 
1794   const long long seekPosId = ReadID(pReader, pos, len);
1795 
1796   if (seekPosId != libwebm::kMkvSeekPosition)
1797     return false;
1798 
1799   if ((pos + len) > stop)
1800     return false;
1801 
1802   pos += len;  // consume id
1803 
1804   const long long seekPosSize = ReadUInt(pReader, pos, len);
1805 
1806   if (seekPosSize <= 0)
1807     return false;
1808 
1809   if ((pos + len) > stop)
1810     return false;
1811 
1812   pos += len;  // consume size
1813 
1814   if ((pos + seekPosSize) > stop)
1815     return false;
1816 
1817   pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
1818 
1819   if (pEntry->pos < 0)
1820     return false;
1821 
1822   pos += seekPosSize;  // consume payload
1823 
1824   if (pos != stop)
1825     return false;
1826 
1827   return true;
1828 }
1829 
Cues(Segment * pSegment,long long start_,long long size_,long long element_start,long long element_size)1830 Cues::Cues(Segment* pSegment, long long start_, long long size_,
1831            long long element_start, long long element_size)
1832     : m_pSegment(pSegment),
1833       m_start(start_),
1834       m_size(size_),
1835       m_element_start(element_start),
1836       m_element_size(element_size),
1837       m_cue_points(NULL),
1838       m_count(0),
1839       m_preload_count(0),
1840       m_pos(start_) {}
1841 
~Cues()1842 Cues::~Cues() {
1843   const long n = m_count + m_preload_count;
1844 
1845   CuePoint** p = m_cue_points;
1846   CuePoint** const q = p + n;
1847 
1848   while (p != q) {
1849     CuePoint* const pCP = *p++;
1850     assert(pCP);
1851 
1852     delete pCP;
1853   }
1854 
1855   delete[] m_cue_points;
1856 }
1857 
GetCount() const1858 long Cues::GetCount() const {
1859   if (m_cue_points == NULL)
1860     return -1;
1861 
1862   return m_count;  // TODO: really ignore preload count?
1863 }
1864 
DoneParsing() const1865 bool Cues::DoneParsing() const {
1866   const long long stop = m_start + m_size;
1867   return (m_pos >= stop);
1868 }
1869 
Init() const1870 bool Cues::Init() const {
1871   if (m_cue_points)
1872     return true;
1873 
1874   if (m_count != 0 || m_preload_count != 0)
1875     return false;
1876 
1877   IMkvReader* const pReader = m_pSegment->m_pReader;
1878 
1879   const long long stop = m_start + m_size;
1880   long long pos = m_start;
1881 
1882   long cue_points_size = 0;
1883 
1884   while (pos < stop) {
1885     const long long idpos = pos;
1886 
1887     long len;
1888 
1889     const long long id = ReadID(pReader, pos, len);
1890     if (id < 0 || (pos + len) > stop) {
1891       return false;
1892     }
1893 
1894     pos += len;  // consume ID
1895 
1896     const long long size = ReadUInt(pReader, pos, len);
1897     if (size < 0 || (pos + len > stop)) {
1898       return false;
1899     }
1900 
1901     pos += len;  // consume Size field
1902     if (pos + size > stop) {
1903       return false;
1904     }
1905 
1906     if (id == libwebm::kMkvCuePoint) {
1907       if (!PreloadCuePoint(cue_points_size, idpos))
1908         return false;
1909     }
1910 
1911     pos += size;  // skip payload
1912   }
1913   return true;
1914 }
1915 
PreloadCuePoint(long & cue_points_size,long long pos) const1916 bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
1917   if (m_count != 0)
1918     return false;
1919 
1920   if (m_preload_count >= cue_points_size) {
1921     const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
1922 
1923     CuePoint** const qq = new (std::nothrow) CuePoint*[n];
1924     if (qq == NULL)
1925       return false;
1926 
1927     CuePoint** q = qq;  // beginning of target
1928 
1929     CuePoint** p = m_cue_points;  // beginning of source
1930     CuePoint** const pp = p + m_preload_count;  // end of source
1931 
1932     while (p != pp)
1933       *q++ = *p++;
1934 
1935     delete[] m_cue_points;
1936 
1937     m_cue_points = qq;
1938     cue_points_size = n;
1939   }
1940 
1941   CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
1942   if (pCP == NULL)
1943     return false;
1944 
1945   m_cue_points[m_preload_count++] = pCP;
1946   return true;
1947 }
1948 
LoadCuePoint() const1949 bool Cues::LoadCuePoint() const {
1950   const long long stop = m_start + m_size;
1951 
1952   if (m_pos >= stop)
1953     return false;  // nothing else to do
1954 
1955   if (!Init()) {
1956     m_pos = stop;
1957     return false;
1958   }
1959 
1960   IMkvReader* const pReader = m_pSegment->m_pReader;
1961 
1962   while (m_pos < stop) {
1963     const long long idpos = m_pos;
1964 
1965     long len;
1966 
1967     const long long id = ReadID(pReader, m_pos, len);
1968     if (id < 0 || (m_pos + len) > stop)
1969       return false;
1970 
1971     m_pos += len;  // consume ID
1972 
1973     const long long size = ReadUInt(pReader, m_pos, len);
1974     if (size < 0 || (m_pos + len) > stop)
1975       return false;
1976 
1977     m_pos += len;  // consume Size field
1978     if ((m_pos + size) > stop)
1979       return false;
1980 
1981     if (id != libwebm::kMkvCuePoint) {
1982       m_pos += size;  // consume payload
1983       if (m_pos > stop)
1984         return false;
1985 
1986       continue;
1987     }
1988 
1989     if (m_preload_count < 1)
1990       return false;
1991 
1992     CuePoint* const pCP = m_cue_points[m_count];
1993     if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos)))
1994       return false;
1995 
1996     if (!pCP->Load(pReader)) {
1997       m_pos = stop;
1998       return false;
1999     }
2000     ++m_count;
2001     --m_preload_count;
2002 
2003     m_pos += size;  // consume payload
2004     if (m_pos > stop)
2005       return false;
2006 
2007     return true;  // yes, we loaded a cue point
2008   }
2009 
2010   return false;  // no, we did not load a cue point
2011 }
2012 
Find(long long time_ns,const Track * pTrack,const CuePoint * & pCP,const CuePoint::TrackPosition * & pTP) const2013 bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
2014                 const CuePoint::TrackPosition*& pTP) const {
2015   if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0)
2016     return false;
2017 
2018   CuePoint** const ii = m_cue_points;
2019   CuePoint** i = ii;
2020 
2021   CuePoint** const jj = ii + m_count;
2022   CuePoint** j = jj;
2023 
2024   pCP = *i;
2025   if (pCP == NULL)
2026     return false;
2027 
2028   if (time_ns <= pCP->GetTime(m_pSegment)) {
2029     pTP = pCP->Find(pTrack);
2030     return (pTP != NULL);
2031   }
2032 
2033   while (i < j) {
2034     // INVARIANT:
2035     //[ii, i) <= time_ns
2036     //[i, j)  ?
2037     //[j, jj) > time_ns
2038 
2039     CuePoint** const k = i + (j - i) / 2;
2040     if (k >= jj)
2041       return false;
2042 
2043     CuePoint* const pCP = *k;
2044     if (pCP == NULL)
2045       return false;
2046 
2047     const long long t = pCP->GetTime(m_pSegment);
2048 
2049     if (t <= time_ns)
2050       i = k + 1;
2051     else
2052       j = k;
2053 
2054     if (i > j)
2055       return false;
2056   }
2057 
2058   if (i != j || i > jj || i <= ii)
2059     return false;
2060 
2061   pCP = *--i;
2062 
2063   if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns)
2064     return false;
2065 
2066   // TODO: here and elsewhere, it's probably not correct to search
2067   // for the cue point with this time, and then search for a matching
2068   // track.  In principle, the matching track could be on some earlier
2069   // cue point, and with our current algorithm, we'd miss it.  To make
2070   // this bullet-proof, we'd need to create a secondary structure,
2071   // with a list of cue points that apply to a track, and then search
2072   // that track-based structure for a matching cue point.
2073 
2074   pTP = pCP->Find(pTrack);
2075   return (pTP != NULL);
2076 }
2077 
GetFirst() const2078 const CuePoint* Cues::GetFirst() const {
2079   if (m_cue_points == NULL || m_count == 0)
2080     return NULL;
2081 
2082   CuePoint* const* const pp = m_cue_points;
2083   if (pp == NULL)
2084     return NULL;
2085 
2086   CuePoint* const pCP = pp[0];
2087   if (pCP == NULL || pCP->GetTimeCode() < 0)
2088     return NULL;
2089 
2090   return pCP;
2091 }
2092 
GetLast() const2093 const CuePoint* Cues::GetLast() const {
2094   if (m_cue_points == NULL || m_count <= 0)
2095     return NULL;
2096 
2097   const long index = m_count - 1;
2098 
2099   CuePoint* const* const pp = m_cue_points;
2100   if (pp == NULL)
2101     return NULL;
2102 
2103   CuePoint* const pCP = pp[index];
2104   if (pCP == NULL || pCP->GetTimeCode() < 0)
2105     return NULL;
2106 
2107   return pCP;
2108 }
2109 
GetNext(const CuePoint * pCurr) const2110 const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
2111   if (pCurr == NULL || pCurr->GetTimeCode() < 0 || m_cue_points == NULL ||
2112       m_count < 1) {
2113     return NULL;
2114   }
2115 
2116   long index = pCurr->m_index;
2117   if (index >= m_count)
2118     return NULL;
2119 
2120   CuePoint* const* const pp = m_cue_points;
2121   if (pp == NULL || pp[index] != pCurr)
2122     return NULL;
2123 
2124   ++index;
2125 
2126   if (index >= m_count)
2127     return NULL;
2128 
2129   CuePoint* const pNext = pp[index];
2130 
2131   if (pNext == NULL || pNext->GetTimeCode() < 0)
2132     return NULL;
2133 
2134   return pNext;
2135 }
2136 
GetBlock(const CuePoint * pCP,const CuePoint::TrackPosition * pTP) const2137 const BlockEntry* Cues::GetBlock(const CuePoint* pCP,
2138                                  const CuePoint::TrackPosition* pTP) const {
2139   if (pCP == NULL || pTP == NULL)
2140     return NULL;
2141 
2142   return m_pSegment->GetBlock(*pCP, *pTP);
2143 }
2144 
GetBlock(const CuePoint & cp,const CuePoint::TrackPosition & tp)2145 const BlockEntry* Segment::GetBlock(const CuePoint& cp,
2146                                     const CuePoint::TrackPosition& tp) {
2147   Cluster** const ii = m_clusters;
2148   Cluster** i = ii;
2149 
2150   const long count = m_clusterCount + m_clusterPreloadCount;
2151 
2152   Cluster** const jj = ii + count;
2153   Cluster** j = jj;
2154 
2155   while (i < j) {
2156     // INVARIANT:
2157     //[ii, i) < pTP->m_pos
2158     //[i, j) ?
2159     //[j, jj)  > pTP->m_pos
2160 
2161     Cluster** const k = i + (j - i) / 2;
2162     assert(k < jj);
2163 
2164     Cluster* const pCluster = *k;
2165     assert(pCluster);
2166 
2167     // const long long pos_ = pCluster->m_pos;
2168     // assert(pos_);
2169     // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2170 
2171     const long long pos = pCluster->GetPosition();
2172     assert(pos >= 0);
2173 
2174     if (pos < tp.m_pos)
2175       i = k + 1;
2176     else if (pos > tp.m_pos)
2177       j = k;
2178     else
2179       return pCluster->GetEntry(cp, tp);
2180   }
2181 
2182   assert(i == j);
2183   // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2184 
2185   Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos);  //, -1);
2186   if (pCluster == NULL)
2187     return NULL;
2188 
2189   const ptrdiff_t idx = i - m_clusters;
2190 
2191   if (!PreloadCluster(pCluster, idx)) {
2192     delete pCluster;
2193     return NULL;
2194   }
2195   assert(m_clusters);
2196   assert(m_clusterPreloadCount > 0);
2197   assert(m_clusters[idx] == pCluster);
2198 
2199   return pCluster->GetEntry(cp, tp);
2200 }
2201 
FindOrPreloadCluster(long long requested_pos)2202 const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) {
2203   if (requested_pos < 0)
2204     return 0;
2205 
2206   Cluster** const ii = m_clusters;
2207   Cluster** i = ii;
2208 
2209   const long count = m_clusterCount + m_clusterPreloadCount;
2210 
2211   Cluster** const jj = ii + count;
2212   Cluster** j = jj;
2213 
2214   while (i < j) {
2215     // INVARIANT:
2216     //[ii, i) < pTP->m_pos
2217     //[i, j) ?
2218     //[j, jj)  > pTP->m_pos
2219 
2220     Cluster** const k = i + (j - i) / 2;
2221     assert(k < jj);
2222 
2223     Cluster* const pCluster = *k;
2224     assert(pCluster);
2225 
2226     // const long long pos_ = pCluster->m_pos;
2227     // assert(pos_);
2228     // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2229 
2230     const long long pos = pCluster->GetPosition();
2231     assert(pos >= 0);
2232 
2233     if (pos < requested_pos)
2234       i = k + 1;
2235     else if (pos > requested_pos)
2236       j = k;
2237     else
2238       return pCluster;
2239   }
2240 
2241   assert(i == j);
2242   // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2243 
2244   Cluster* const pCluster = Cluster::Create(this, -1, requested_pos);
2245   if (pCluster == NULL)
2246     return NULL;
2247 
2248   const ptrdiff_t idx = i - m_clusters;
2249 
2250   if (!PreloadCluster(pCluster, idx)) {
2251     delete pCluster;
2252     return NULL;
2253   }
2254   assert(m_clusters);
2255   assert(m_clusterPreloadCount > 0);
2256   assert(m_clusters[idx] == pCluster);
2257 
2258   return pCluster;
2259 }
2260 
CuePoint(long idx,long long pos)2261 CuePoint::CuePoint(long idx, long long pos)
2262     : m_element_start(0),
2263       m_element_size(0),
2264       m_index(idx),
2265       m_timecode(-1 * pos),
2266       m_track_positions(NULL),
2267       m_track_positions_count(0) {
2268   assert(pos > 0);
2269 }
2270 
~CuePoint()2271 CuePoint::~CuePoint() { delete[] m_track_positions; }
2272 
Load(IMkvReader * pReader)2273 bool CuePoint::Load(IMkvReader* pReader) {
2274   // odbgstream os;
2275   // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
2276 
2277   if (m_timecode >= 0)  // already loaded
2278     return true;
2279 
2280   assert(m_track_positions == NULL);
2281   assert(m_track_positions_count == 0);
2282 
2283   long long pos_ = -m_timecode;
2284   const long long element_start = pos_;
2285 
2286   long long stop;
2287 
2288   {
2289     long len;
2290 
2291     const long long id = ReadID(pReader, pos_, len);
2292     if (id != libwebm::kMkvCuePoint)
2293       return false;
2294 
2295     pos_ += len;  // consume ID
2296 
2297     const long long size = ReadUInt(pReader, pos_, len);
2298     assert(size >= 0);
2299 
2300     pos_ += len;  // consume Size field
2301     // pos_ now points to start of payload
2302 
2303     stop = pos_ + size;
2304   }
2305 
2306   const long long element_size = stop - element_start;
2307 
2308   long long pos = pos_;
2309 
2310   // First count number of track positions
2311   unsigned long long track_positions_count = 0;
2312   while (pos < stop) {
2313     long len;
2314 
2315     const long long id = ReadID(pReader, pos, len);
2316     if ((id < 0) || (pos + len > stop)) {
2317       return false;
2318     }
2319 
2320     pos += len;  // consume ID
2321 
2322     const long long size = ReadUInt(pReader, pos, len);
2323     if ((size < 0) || (pos + len > stop)) {
2324       return false;
2325     }
2326 
2327     pos += len;  // consume Size field
2328     if ((pos + size) > stop) {
2329       return false;
2330     }
2331 
2332     if (id == libwebm::kMkvCueTime)
2333       m_timecode = UnserializeUInt(pReader, pos, size);
2334 
2335     else if (id == libwebm::kMkvCueTrackPositions) {
2336       ++track_positions_count;
2337       if (track_positions_count > UINT_MAX)
2338         return E_PARSE_FAILED;
2339     }
2340 
2341     pos += size;  // consume payload
2342   }
2343 
2344   m_track_positions_count = static_cast<size_t>(track_positions_count);
2345 
2346   if (m_timecode < 0 || m_track_positions_count <= 0) {
2347     return false;
2348   }
2349 
2350   // os << "CuePoint::Load(cont'd): idpos=" << idpos
2351   //   << " timecode=" << m_timecode
2352   //   << endl;
2353 
2354   m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
2355   if (m_track_positions == NULL)
2356     return false;
2357 
2358   // Now parse track positions
2359 
2360   TrackPosition* p = m_track_positions;
2361   pos = pos_;
2362 
2363   while (pos < stop) {
2364     long len;
2365 
2366     const long long id = ReadID(pReader, pos, len);
2367     if (id < 0 || (pos + len) > stop)
2368       return false;
2369 
2370     pos += len;  // consume ID
2371 
2372     const long long size = ReadUInt(pReader, pos, len);
2373     assert(size >= 0);
2374     assert((pos + len) <= stop);
2375 
2376     pos += len;  // consume Size field
2377     assert((pos + size) <= stop);
2378 
2379     if (id == libwebm::kMkvCueTrackPositions) {
2380       TrackPosition& tp = *p++;
2381       if (!tp.Parse(pReader, pos, size)) {
2382         return false;
2383       }
2384     }
2385 
2386     pos += size;  // consume payload
2387     if (pos > stop)
2388       return false;
2389   }
2390 
2391   assert(size_t(p - m_track_positions) == m_track_positions_count);
2392 
2393   m_element_start = element_start;
2394   m_element_size = element_size;
2395 
2396   return true;
2397 }
2398 
Parse(IMkvReader * pReader,long long start_,long long size_)2399 bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
2400                                     long long size_) {
2401   const long long stop = start_ + size_;
2402   long long pos = start_;
2403 
2404   m_track = -1;
2405   m_pos = -1;
2406   m_block = 1;  // default
2407 
2408   while (pos < stop) {
2409     long len;
2410 
2411     const long long id = ReadID(pReader, pos, len);
2412     if ((id < 0) || ((pos + len) > stop)) {
2413       return false;
2414     }
2415 
2416     pos += len;  // consume ID
2417 
2418     const long long size = ReadUInt(pReader, pos, len);
2419     if ((size < 0) || ((pos + len) > stop)) {
2420       return false;
2421     }
2422 
2423     pos += len;  // consume Size field
2424     if ((pos + size) > stop) {
2425       return false;
2426     }
2427 
2428     if (id == libwebm::kMkvCueTrack)
2429       m_track = UnserializeUInt(pReader, pos, size);
2430     else if (id == libwebm::kMkvCueClusterPosition)
2431       m_pos = UnserializeUInt(pReader, pos, size);
2432     else if (id == libwebm::kMkvCueBlockNumber)
2433       m_block = UnserializeUInt(pReader, pos, size);
2434 
2435     pos += size;  // consume payload
2436   }
2437 
2438   if ((m_pos < 0) || (m_track <= 0) || (m_block < 0) || (m_block > LONG_MAX)) {
2439     return false;
2440   }
2441 
2442   return true;
2443 }
2444 
Find(const Track * pTrack) const2445 const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
2446   if (pTrack == NULL) {
2447     return NULL;
2448   }
2449 
2450   const long long n = pTrack->GetNumber();
2451 
2452   const TrackPosition* i = m_track_positions;
2453   const TrackPosition* const j = i + m_track_positions_count;
2454 
2455   while (i != j) {
2456     const TrackPosition& p = *i++;
2457 
2458     if (p.m_track == n)
2459       return &p;
2460   }
2461 
2462   return NULL;  // no matching track number found
2463 }
2464 
GetTimeCode() const2465 long long CuePoint::GetTimeCode() const { return m_timecode; }
2466 
GetTime(const Segment * pSegment) const2467 long long CuePoint::GetTime(const Segment* pSegment) const {
2468   assert(pSegment);
2469   assert(m_timecode >= 0);
2470 
2471   const SegmentInfo* const pInfo = pSegment->GetInfo();
2472   assert(pInfo);
2473 
2474   const long long scale = pInfo->GetTimeCodeScale();
2475   assert(scale >= 1);
2476 
2477   const long long time = scale * m_timecode;
2478 
2479   return time;
2480 }
2481 
DoneParsing() const2482 bool Segment::DoneParsing() const {
2483   if (m_size < 0) {
2484     long long total, avail;
2485 
2486     const int status = m_pReader->Length(&total, &avail);
2487 
2488     if (status < 0)  // error
2489       return true;  // must assume done
2490 
2491     if (total < 0)
2492       return false;  // assume live stream
2493 
2494     return (m_pos >= total);
2495   }
2496 
2497   const long long stop = m_start + m_size;
2498 
2499   return (m_pos >= stop);
2500 }
2501 
GetFirst() const2502 const Cluster* Segment::GetFirst() const {
2503   if ((m_clusters == NULL) || (m_clusterCount <= 0))
2504     return &m_eos;
2505 
2506   Cluster* const pCluster = m_clusters[0];
2507   assert(pCluster);
2508 
2509   return pCluster;
2510 }
2511 
GetLast() const2512 const Cluster* Segment::GetLast() const {
2513   if ((m_clusters == NULL) || (m_clusterCount <= 0))
2514     return &m_eos;
2515 
2516   const long idx = m_clusterCount - 1;
2517 
2518   Cluster* const pCluster = m_clusters[idx];
2519   assert(pCluster);
2520 
2521   return pCluster;
2522 }
2523 
GetCount() const2524 unsigned long Segment::GetCount() const { return m_clusterCount; }
2525 
GetNext(const Cluster * pCurr)2526 const Cluster* Segment::GetNext(const Cluster* pCurr) {
2527   assert(pCurr);
2528   assert(pCurr != &m_eos);
2529   assert(m_clusters);
2530 
2531   long idx = pCurr->m_index;
2532 
2533   if (idx >= 0) {
2534     assert(m_clusterCount > 0);
2535     assert(idx < m_clusterCount);
2536     assert(pCurr == m_clusters[idx]);
2537 
2538     ++idx;
2539 
2540     if (idx >= m_clusterCount)
2541       return &m_eos;  // caller will LoadCluster as desired
2542 
2543     Cluster* const pNext = m_clusters[idx];
2544     assert(pNext);
2545     assert(pNext->m_index >= 0);
2546     assert(pNext->m_index == idx);
2547 
2548     return pNext;
2549   }
2550 
2551   assert(m_clusterPreloadCount > 0);
2552 
2553   long long pos = pCurr->m_element_start;
2554 
2555   assert(m_size >= 0);  // TODO
2556   const long long stop = m_start + m_size;  // end of segment
2557 
2558   {
2559     long len;
2560 
2561     long long result = GetUIntLength(m_pReader, pos, len);
2562     assert(result == 0);
2563     assert((pos + len) <= stop);  // TODO
2564     if (result != 0)
2565       return NULL;
2566 
2567     const long long id = ReadID(m_pReader, pos, len);
2568     if (id != libwebm::kMkvCluster)
2569       return NULL;
2570 
2571     pos += len;  // consume ID
2572 
2573     // Read Size
2574     result = GetUIntLength(m_pReader, pos, len);
2575     assert(result == 0);  // TODO
2576     assert((pos + len) <= stop);  // TODO
2577 
2578     const long long size = ReadUInt(m_pReader, pos, len);
2579     assert(size > 0);  // TODO
2580     // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2581 
2582     pos += len;  // consume length of size of element
2583     assert((pos + size) <= stop);  // TODO
2584 
2585     // Pos now points to start of payload
2586 
2587     pos += size;  // consume payload
2588   }
2589 
2590   long long off_next = 0;
2591 
2592   while (pos < stop) {
2593     long len;
2594 
2595     long long result = GetUIntLength(m_pReader, pos, len);
2596     assert(result == 0);
2597     assert((pos + len) <= stop);  // TODO
2598     if (result != 0)
2599       return NULL;
2600 
2601     const long long idpos = pos;  // pos of next (potential) cluster
2602 
2603     const long long id = ReadID(m_pReader, idpos, len);
2604     if (id < 0)
2605       return NULL;
2606 
2607     pos += len;  // consume ID
2608 
2609     // Read Size
2610     result = GetUIntLength(m_pReader, pos, len);
2611     assert(result == 0);  // TODO
2612     assert((pos + len) <= stop);  // TODO
2613 
2614     const long long size = ReadUInt(m_pReader, pos, len);
2615     assert(size >= 0);  // TODO
2616 
2617     pos += len;  // consume length of size of element
2618     assert((pos + size) <= stop);  // TODO
2619 
2620     // Pos now points to start of payload
2621 
2622     if (size == 0)  // weird
2623       continue;
2624 
2625     if (id == libwebm::kMkvCluster) {
2626       const long long off_next_ = idpos - m_start;
2627 
2628       long long pos_;
2629       long len_;
2630 
2631       const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_);
2632 
2633       assert(status >= 0);
2634 
2635       if (status > 0) {
2636         off_next = off_next_;
2637         break;
2638       }
2639     }
2640 
2641     pos += size;  // consume payload
2642   }
2643 
2644   if (off_next <= 0)
2645     return 0;
2646 
2647   Cluster** const ii = m_clusters + m_clusterCount;
2648   Cluster** i = ii;
2649 
2650   Cluster** const jj = ii + m_clusterPreloadCount;
2651   Cluster** j = jj;
2652 
2653   while (i < j) {
2654     // INVARIANT:
2655     //[0, i) < pos_next
2656     //[i, j) ?
2657     //[j, jj)  > pos_next
2658 
2659     Cluster** const k = i + (j - i) / 2;
2660     assert(k < jj);
2661 
2662     Cluster* const pNext = *k;
2663     assert(pNext);
2664     assert(pNext->m_index < 0);
2665 
2666     // const long long pos_ = pNext->m_pos;
2667     // assert(pos_);
2668     // pos = pos_ * ((pos_ < 0) ? -1 : 1);
2669 
2670     pos = pNext->GetPosition();
2671 
2672     if (pos < off_next)
2673       i = k + 1;
2674     else if (pos > off_next)
2675       j = k;
2676     else
2677       return pNext;
2678   }
2679 
2680   assert(i == j);
2681 
2682   Cluster* const pNext = Cluster::Create(this, -1, off_next);
2683   if (pNext == NULL)
2684     return NULL;
2685 
2686   const ptrdiff_t idx_next = i - m_clusters;  // insertion position
2687 
2688   if (!PreloadCluster(pNext, idx_next)) {
2689     delete pNext;
2690     return NULL;
2691   }
2692   assert(m_clusters);
2693   assert(idx_next < m_clusterSize);
2694   assert(m_clusters[idx_next] == pNext);
2695 
2696   return pNext;
2697 }
2698 
ParseNext(const Cluster * pCurr,const Cluster * & pResult,long long & pos,long & len)2699 long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult,
2700                         long long& pos, long& len) {
2701   assert(pCurr);
2702   assert(!pCurr->EOS());
2703   assert(m_clusters);
2704 
2705   pResult = 0;
2706 
2707   if (pCurr->m_index >= 0) {  // loaded (not merely preloaded)
2708     assert(m_clusters[pCurr->m_index] == pCurr);
2709 
2710     const long next_idx = pCurr->m_index + 1;
2711 
2712     if (next_idx < m_clusterCount) {
2713       pResult = m_clusters[next_idx];
2714       return 0;  // success
2715     }
2716 
2717     // curr cluster is last among loaded
2718 
2719     const long result = LoadCluster(pos, len);
2720 
2721     if (result < 0)  // error or underflow
2722       return result;
2723 
2724     if (result > 0)  // no more clusters
2725     {
2726       // pResult = &m_eos;
2727       return 1;
2728     }
2729 
2730     pResult = GetLast();
2731     return 0;  // success
2732   }
2733 
2734   assert(m_pos > 0);
2735 
2736   long long total, avail;
2737 
2738   long status = m_pReader->Length(&total, &avail);
2739 
2740   if (status < 0)  // error
2741     return status;
2742 
2743   assert((total < 0) || (avail <= total));
2744 
2745   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2746 
2747   // interrogate curr cluster
2748 
2749   pos = pCurr->m_element_start;
2750 
2751   if (pCurr->m_element_size >= 0)
2752     pos += pCurr->m_element_size;
2753   else {
2754     if ((pos + 1) > avail) {
2755       len = 1;
2756       return E_BUFFER_NOT_FULL;
2757     }
2758 
2759     long long result = GetUIntLength(m_pReader, pos, len);
2760 
2761     if (result < 0)  // error
2762       return static_cast<long>(result);
2763 
2764     if (result > 0)  // weird
2765       return E_BUFFER_NOT_FULL;
2766 
2767     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2768       return E_FILE_FORMAT_INVALID;
2769 
2770     if ((pos + len) > avail)
2771       return E_BUFFER_NOT_FULL;
2772 
2773     const long long id = ReadUInt(m_pReader, pos, len);
2774 
2775     if (id != libwebm::kMkvCluster)
2776       return -1;
2777 
2778     pos += len;  // consume ID
2779 
2780     // Read Size
2781 
2782     if ((pos + 1) > avail) {
2783       len = 1;
2784       return E_BUFFER_NOT_FULL;
2785     }
2786 
2787     result = GetUIntLength(m_pReader, pos, len);
2788 
2789     if (result < 0)  // error
2790       return static_cast<long>(result);
2791 
2792     if (result > 0)  // weird
2793       return E_BUFFER_NOT_FULL;
2794 
2795     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2796       return E_FILE_FORMAT_INVALID;
2797 
2798     if ((pos + len) > avail)
2799       return E_BUFFER_NOT_FULL;
2800 
2801     const long long size = ReadUInt(m_pReader, pos, len);
2802 
2803     if (size < 0)  // error
2804       return static_cast<long>(size);
2805 
2806     pos += len;  // consume size field
2807 
2808     const long long unknown_size = (1LL << (7 * len)) - 1;
2809 
2810     if (size == unknown_size)  // TODO: should never happen
2811       return E_FILE_FORMAT_INVALID;  // TODO: resolve this
2812 
2813     // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2814 
2815     if ((segment_stop >= 0) && ((pos + size) > segment_stop))
2816       return E_FILE_FORMAT_INVALID;
2817 
2818     // Pos now points to start of payload
2819 
2820     pos += size;  // consume payload (that is, the current cluster)
2821     if (segment_stop >= 0 && pos > segment_stop)
2822       return E_FILE_FORMAT_INVALID;
2823 
2824     // By consuming the payload, we are assuming that the curr
2825     // cluster isn't interesting.  That is, we don't bother checking
2826     // whether the payload of the curr cluster is less than what
2827     // happens to be available (obtained via IMkvReader::Length).
2828     // Presumably the caller has already dispensed with the current
2829     // cluster, and really does want the next cluster.
2830   }
2831 
2832   // pos now points to just beyond the last fully-loaded cluster
2833 
2834   for (;;) {
2835     const long status = DoParseNext(pResult, pos, len);
2836 
2837     if (status <= 1)
2838       return status;
2839   }
2840 }
2841 
DoParseNext(const Cluster * & pResult,long long & pos,long & len)2842 long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
2843   long long total, avail;
2844 
2845   long status = m_pReader->Length(&total, &avail);
2846 
2847   if (status < 0)  // error
2848     return status;
2849 
2850   assert((total < 0) || (avail <= total));
2851 
2852   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2853 
2854   // Parse next cluster.  This is strictly a parsing activity.
2855   // Creation of a new cluster object happens later, after the
2856   // parsing is done.
2857 
2858   long long off_next = 0;
2859   long long cluster_size = -1;
2860 
2861   for (;;) {
2862     if ((total >= 0) && (pos >= total))
2863       return 1;  // EOF
2864 
2865     if ((segment_stop >= 0) && (pos >= segment_stop))
2866       return 1;  // EOF
2867 
2868     if ((pos + 1) > avail) {
2869       len = 1;
2870       return E_BUFFER_NOT_FULL;
2871     }
2872 
2873     long long result = GetUIntLength(m_pReader, pos, len);
2874 
2875     if (result < 0)  // error
2876       return static_cast<long>(result);
2877 
2878     if (result > 0)  // weird
2879       return E_BUFFER_NOT_FULL;
2880 
2881     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2882       return E_FILE_FORMAT_INVALID;
2883 
2884     if ((pos + len) > avail)
2885       return E_BUFFER_NOT_FULL;
2886 
2887     const long long idpos = pos;  // absolute
2888     const long long idoff = pos - m_start;  // relative
2889 
2890     const long long id = ReadID(m_pReader, idpos, len);  // absolute
2891 
2892     if (id < 0)  // error
2893       return static_cast<long>(id);
2894 
2895     if (id == 0)  // weird
2896       return -1;  // generic error
2897 
2898     pos += len;  // consume ID
2899 
2900     // Read Size
2901 
2902     if ((pos + 1) > avail) {
2903       len = 1;
2904       return E_BUFFER_NOT_FULL;
2905     }
2906 
2907     result = GetUIntLength(m_pReader, pos, len);
2908 
2909     if (result < 0)  // error
2910       return static_cast<long>(result);
2911 
2912     if (result > 0)  // weird
2913       return E_BUFFER_NOT_FULL;
2914 
2915     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2916       return E_FILE_FORMAT_INVALID;
2917 
2918     if ((pos + len) > avail)
2919       return E_BUFFER_NOT_FULL;
2920 
2921     const long long size = ReadUInt(m_pReader, pos, len);
2922 
2923     if (size < 0)  // error
2924       return static_cast<long>(size);
2925 
2926     pos += len;  // consume length of size of element
2927 
2928     // Pos now points to start of payload
2929 
2930     if (size == 0)  // weird
2931       continue;
2932 
2933     const long long unknown_size = (1LL << (7 * len)) - 1;
2934 
2935     if ((segment_stop >= 0) && (size != unknown_size) &&
2936         ((pos + size) > segment_stop)) {
2937       return E_FILE_FORMAT_INVALID;
2938     }
2939 
2940     if (id == libwebm::kMkvCues) {
2941       if (size == unknown_size)
2942         return E_FILE_FORMAT_INVALID;
2943 
2944       const long long element_stop = pos + size;
2945 
2946       if ((segment_stop >= 0) && (element_stop > segment_stop))
2947         return E_FILE_FORMAT_INVALID;
2948 
2949       const long long element_start = idpos;
2950       const long long element_size = element_stop - element_start;
2951 
2952       if (m_pCues == NULL) {
2953         m_pCues = new (std::nothrow)
2954             Cues(this, pos, size, element_start, element_size);
2955         if (m_pCues == NULL)
2956           return false;
2957       }
2958 
2959       pos += size;  // consume payload
2960       if (segment_stop >= 0 && pos > segment_stop)
2961         return E_FILE_FORMAT_INVALID;
2962 
2963       continue;
2964     }
2965 
2966     if (id != libwebm::kMkvCluster) {  // not a Cluster ID
2967       if (size == unknown_size)
2968         return E_FILE_FORMAT_INVALID;
2969 
2970       pos += size;  // consume payload
2971       if (segment_stop >= 0 && pos > segment_stop)
2972         return E_FILE_FORMAT_INVALID;
2973 
2974       continue;
2975     }
2976 
2977     // We have a cluster.
2978     off_next = idoff;
2979 
2980     if (size != unknown_size)
2981       cluster_size = size;
2982 
2983     break;
2984   }
2985 
2986   assert(off_next > 0);  // have cluster
2987 
2988   // We have parsed the next cluster.
2989   // We have not created a cluster object yet.  What we need
2990   // to do now is determine whether it has already be preloaded
2991   //(in which case, an object for this cluster has already been
2992   // created), and if not, create a new cluster object.
2993 
2994   Cluster** const ii = m_clusters + m_clusterCount;
2995   Cluster** i = ii;
2996 
2997   Cluster** const jj = ii + m_clusterPreloadCount;
2998   Cluster** j = jj;
2999 
3000   while (i < j) {
3001     // INVARIANT:
3002     //[0, i) < pos_next
3003     //[i, j) ?
3004     //[j, jj)  > pos_next
3005 
3006     Cluster** const k = i + (j - i) / 2;
3007     assert(k < jj);
3008 
3009     const Cluster* const pNext = *k;
3010     assert(pNext);
3011     assert(pNext->m_index < 0);
3012 
3013     pos = pNext->GetPosition();
3014     assert(pos >= 0);
3015 
3016     if (pos < off_next)
3017       i = k + 1;
3018     else if (pos > off_next)
3019       j = k;
3020     else {
3021       pResult = pNext;
3022       return 0;  // success
3023     }
3024   }
3025 
3026   assert(i == j);
3027 
3028   long long pos_;
3029   long len_;
3030 
3031   status = Cluster::HasBlockEntries(this, off_next, pos_, len_);
3032 
3033   if (status < 0) {  // error or underflow
3034     pos = pos_;
3035     len = len_;
3036 
3037     return status;
3038   }
3039 
3040   if (status > 0) {  // means "found at least one block entry"
3041     Cluster* const pNext = Cluster::Create(this,
3042                                            -1,  // preloaded
3043                                            off_next);
3044     if (pNext == NULL)
3045       return -1;
3046 
3047     const ptrdiff_t idx_next = i - m_clusters;  // insertion position
3048 
3049     if (!PreloadCluster(pNext, idx_next)) {
3050       delete pNext;
3051       return -1;
3052     }
3053     assert(m_clusters);
3054     assert(idx_next < m_clusterSize);
3055     assert(m_clusters[idx_next] == pNext);
3056 
3057     pResult = pNext;
3058     return 0;  // success
3059   }
3060 
3061   // status == 0 means "no block entries found"
3062 
3063   if (cluster_size < 0) {  // unknown size
3064     const long long payload_pos = pos;  // absolute pos of cluster payload
3065 
3066     for (;;) {  // determine cluster size
3067       if ((total >= 0) && (pos >= total))
3068         break;
3069 
3070       if ((segment_stop >= 0) && (pos >= segment_stop))
3071         break;  // no more clusters
3072 
3073       // Read ID
3074 
3075       if ((pos + 1) > avail) {
3076         len = 1;
3077         return E_BUFFER_NOT_FULL;
3078       }
3079 
3080       long long result = GetUIntLength(m_pReader, pos, len);
3081 
3082       if (result < 0)  // error
3083         return static_cast<long>(result);
3084 
3085       if (result > 0)  // weird
3086         return E_BUFFER_NOT_FULL;
3087 
3088       if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3089         return E_FILE_FORMAT_INVALID;
3090 
3091       if ((pos + len) > avail)
3092         return E_BUFFER_NOT_FULL;
3093 
3094       const long long idpos = pos;
3095       const long long id = ReadID(m_pReader, idpos, len);
3096 
3097       if (id < 0)  // error (or underflow)
3098         return static_cast<long>(id);
3099 
3100       // This is the distinguished set of ID's we use to determine
3101       // that we have exhausted the sub-element's inside the cluster
3102       // whose ID we parsed earlier.
3103 
3104       if (id == libwebm::kMkvCluster || id == libwebm::kMkvCues)
3105         break;
3106 
3107       pos += len;  // consume ID (of sub-element)
3108 
3109       // Read Size
3110 
3111       if ((pos + 1) > avail) {
3112         len = 1;
3113         return E_BUFFER_NOT_FULL;
3114       }
3115 
3116       result = GetUIntLength(m_pReader, pos, len);
3117 
3118       if (result < 0)  // error
3119         return static_cast<long>(result);
3120 
3121       if (result > 0)  // weird
3122         return E_BUFFER_NOT_FULL;
3123 
3124       if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3125         return E_FILE_FORMAT_INVALID;
3126 
3127       if ((pos + len) > avail)
3128         return E_BUFFER_NOT_FULL;
3129 
3130       const long long size = ReadUInt(m_pReader, pos, len);
3131 
3132       if (size < 0)  // error
3133         return static_cast<long>(size);
3134 
3135       pos += len;  // consume size field of element
3136 
3137       // pos now points to start of sub-element's payload
3138 
3139       if (size == 0)  // weird
3140         continue;
3141 
3142       const long long unknown_size = (1LL << (7 * len)) - 1;
3143 
3144       if (size == unknown_size)
3145         return E_FILE_FORMAT_INVALID;  // not allowed for sub-elements
3146 
3147       if ((segment_stop >= 0) && ((pos + size) > segment_stop))  // weird
3148         return E_FILE_FORMAT_INVALID;
3149 
3150       pos += size;  // consume payload of sub-element
3151       if (segment_stop >= 0 && pos > segment_stop)
3152         return E_FILE_FORMAT_INVALID;
3153     }  // determine cluster size
3154 
3155     cluster_size = pos - payload_pos;
3156     assert(cluster_size >= 0);  // TODO: handle cluster_size = 0
3157 
3158     pos = payload_pos;  // reset and re-parse original cluster
3159   }
3160 
3161   pos += cluster_size;  // consume payload
3162   if (segment_stop >= 0 && pos > segment_stop)
3163     return E_FILE_FORMAT_INVALID;
3164 
3165   return 2;  // try to find a cluster that follows next
3166 }
3167 
FindCluster(long long time_ns) const3168 const Cluster* Segment::FindCluster(long long time_ns) const {
3169   if ((m_clusters == NULL) || (m_clusterCount <= 0))
3170     return &m_eos;
3171 
3172   {
3173     Cluster* const pCluster = m_clusters[0];
3174     assert(pCluster);
3175     assert(pCluster->m_index == 0);
3176 
3177     if (time_ns <= pCluster->GetTime())
3178       return pCluster;
3179   }
3180 
3181   // Binary search of cluster array
3182 
3183   long i = 0;
3184   long j = m_clusterCount;
3185 
3186   while (i < j) {
3187     // INVARIANT:
3188     //[0, i) <= time_ns
3189     //[i, j) ?
3190     //[j, m_clusterCount)  > time_ns
3191 
3192     const long k = i + (j - i) / 2;
3193     assert(k < m_clusterCount);
3194 
3195     Cluster* const pCluster = m_clusters[k];
3196     assert(pCluster);
3197     assert(pCluster->m_index == k);
3198 
3199     const long long t = pCluster->GetTime();
3200 
3201     if (t <= time_ns)
3202       i = k + 1;
3203     else
3204       j = k;
3205 
3206     assert(i <= j);
3207   }
3208 
3209   assert(i == j);
3210   assert(i > 0);
3211   assert(i <= m_clusterCount);
3212 
3213   const long k = i - 1;
3214 
3215   Cluster* const pCluster = m_clusters[k];
3216   assert(pCluster);
3217   assert(pCluster->m_index == k);
3218   assert(pCluster->GetTime() <= time_ns);
3219 
3220   return pCluster;
3221 }
3222 
GetTracks() const3223 const Tracks* Segment::GetTracks() const { return m_pTracks; }
GetInfo() const3224 const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
GetCues() const3225 const Cues* Segment::GetCues() const { return m_pCues; }
GetChapters() const3226 const Chapters* Segment::GetChapters() const { return m_pChapters; }
GetTags() const3227 const Tags* Segment::GetTags() const { return m_pTags; }
GetSeekHead() const3228 const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
3229 
GetDuration() const3230 long long Segment::GetDuration() const {
3231   assert(m_pInfo);
3232   return m_pInfo->GetDuration();
3233 }
3234 
Chapters(Segment * pSegment,long long payload_start,long long payload_size,long long element_start,long long element_size)3235 Chapters::Chapters(Segment* pSegment, long long payload_start,
3236                    long long payload_size, long long element_start,
3237                    long long element_size)
3238     : m_pSegment(pSegment),
3239       m_start(payload_start),
3240       m_size(payload_size),
3241       m_element_start(element_start),
3242       m_element_size(element_size),
3243       m_editions(NULL),
3244       m_editions_size(0),
3245       m_editions_count(0) {}
3246 
~Chapters()3247 Chapters::~Chapters() {
3248   while (m_editions_count > 0) {
3249     Edition& e = m_editions[--m_editions_count];
3250     e.Clear();
3251   }
3252   delete[] m_editions;
3253 }
3254 
Parse()3255 long Chapters::Parse() {
3256   IMkvReader* const pReader = m_pSegment->m_pReader;
3257 
3258   long long pos = m_start;  // payload start
3259   const long long stop = pos + m_size;  // payload stop
3260 
3261   while (pos < stop) {
3262     long long id, size;
3263 
3264     long status = ParseElementHeader(pReader, pos, stop, id, size);
3265 
3266     if (status < 0)  // error
3267       return status;
3268 
3269     if (size == 0)  // weird
3270       continue;
3271 
3272     if (id == libwebm::kMkvEditionEntry) {
3273       status = ParseEdition(pos, size);
3274 
3275       if (status < 0)  // error
3276         return status;
3277     }
3278 
3279     pos += size;
3280     if (pos > stop)
3281       return E_FILE_FORMAT_INVALID;
3282   }
3283 
3284   if (pos != stop)
3285     return E_FILE_FORMAT_INVALID;
3286   return 0;
3287 }
3288 
GetEditionCount() const3289 int Chapters::GetEditionCount() const { return m_editions_count; }
3290 
GetEdition(int idx) const3291 const Chapters::Edition* Chapters::GetEdition(int idx) const {
3292   if (idx < 0)
3293     return NULL;
3294 
3295   if (idx >= m_editions_count)
3296     return NULL;
3297 
3298   return m_editions + idx;
3299 }
3300 
ExpandEditionsArray()3301 bool Chapters::ExpandEditionsArray() {
3302   if (m_editions_size > m_editions_count)
3303     return true;  // nothing else to do
3304 
3305   const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
3306 
3307   Edition* const editions = new (std::nothrow) Edition[size];
3308 
3309   if (editions == NULL)
3310     return false;
3311 
3312   for (int idx = 0; idx < m_editions_count; ++idx) {
3313     m_editions[idx].ShallowCopy(editions[idx]);
3314   }
3315 
3316   delete[] m_editions;
3317   m_editions = editions;
3318 
3319   m_editions_size = size;
3320   return true;
3321 }
3322 
ParseEdition(long long pos,long long size)3323 long Chapters::ParseEdition(long long pos, long long size) {
3324   if (!ExpandEditionsArray())
3325     return -1;
3326 
3327   Edition& e = m_editions[m_editions_count++];
3328   e.Init();
3329 
3330   return e.Parse(m_pSegment->m_pReader, pos, size);
3331 }
3332 
Edition()3333 Chapters::Edition::Edition() {}
3334 
~Edition()3335 Chapters::Edition::~Edition() {}
3336 
GetAtomCount() const3337 int Chapters::Edition::GetAtomCount() const { return m_atoms_count; }
3338 
GetAtom(int index) const3339 const Chapters::Atom* Chapters::Edition::GetAtom(int index) const {
3340   if (index < 0)
3341     return NULL;
3342 
3343   if (index >= m_atoms_count)
3344     return NULL;
3345 
3346   return m_atoms + index;
3347 }
3348 
Init()3349 void Chapters::Edition::Init() {
3350   m_atoms = NULL;
3351   m_atoms_size = 0;
3352   m_atoms_count = 0;
3353 }
3354 
ShallowCopy(Edition & rhs) const3355 void Chapters::Edition::ShallowCopy(Edition& rhs) const {
3356   rhs.m_atoms = m_atoms;
3357   rhs.m_atoms_size = m_atoms_size;
3358   rhs.m_atoms_count = m_atoms_count;
3359 }
3360 
Clear()3361 void Chapters::Edition::Clear() {
3362   while (m_atoms_count > 0) {
3363     Atom& a = m_atoms[--m_atoms_count];
3364     a.Clear();
3365   }
3366 
3367   delete[] m_atoms;
3368   m_atoms = NULL;
3369 
3370   m_atoms_size = 0;
3371 }
3372 
Parse(IMkvReader * pReader,long long pos,long long size)3373 long Chapters::Edition::Parse(IMkvReader* pReader, long long pos,
3374                               long long size) {
3375   const long long stop = pos + size;
3376 
3377   while (pos < stop) {
3378     long long id, size;
3379 
3380     long status = ParseElementHeader(pReader, pos, stop, id, size);
3381 
3382     if (status < 0)  // error
3383       return status;
3384 
3385     if (size == 0)
3386       continue;
3387 
3388     if (id == libwebm::kMkvChapterAtom) {
3389       status = ParseAtom(pReader, pos, size);
3390 
3391       if (status < 0)  // error
3392         return status;
3393     }
3394 
3395     pos += size;
3396     if (pos > stop)
3397       return E_FILE_FORMAT_INVALID;
3398   }
3399 
3400   if (pos != stop)
3401     return E_FILE_FORMAT_INVALID;
3402   return 0;
3403 }
3404 
ParseAtom(IMkvReader * pReader,long long pos,long long size)3405 long Chapters::Edition::ParseAtom(IMkvReader* pReader, long long pos,
3406                                   long long size) {
3407   if (!ExpandAtomsArray())
3408     return -1;
3409 
3410   Atom& a = m_atoms[m_atoms_count++];
3411   a.Init();
3412 
3413   return a.Parse(pReader, pos, size);
3414 }
3415 
ExpandAtomsArray()3416 bool Chapters::Edition::ExpandAtomsArray() {
3417   if (m_atoms_size > m_atoms_count)
3418     return true;  // nothing else to do
3419 
3420   const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
3421 
3422   Atom* const atoms = new (std::nothrow) Atom[size];
3423 
3424   if (atoms == NULL)
3425     return false;
3426 
3427   for (int idx = 0; idx < m_atoms_count; ++idx) {
3428     m_atoms[idx].ShallowCopy(atoms[idx]);
3429   }
3430 
3431   delete[] m_atoms;
3432   m_atoms = atoms;
3433 
3434   m_atoms_size = size;
3435   return true;
3436 }
3437 
Atom()3438 Chapters::Atom::Atom() {}
3439 
~Atom()3440 Chapters::Atom::~Atom() {}
3441 
GetUID() const3442 unsigned long long Chapters::Atom::GetUID() const { return m_uid; }
3443 
GetStringUID() const3444 const char* Chapters::Atom::GetStringUID() const { return m_string_uid; }
3445 
GetStartTimecode() const3446 long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; }
3447 
GetStopTimecode() const3448 long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; }
3449 
GetStartTime(const Chapters * pChapters) const3450 long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const {
3451   return GetTime(pChapters, m_start_timecode);
3452 }
3453 
GetStopTime(const Chapters * pChapters) const3454 long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const {
3455   return GetTime(pChapters, m_stop_timecode);
3456 }
3457 
GetDisplayCount() const3458 int Chapters::Atom::GetDisplayCount() const { return m_displays_count; }
3459 
GetDisplay(int index) const3460 const Chapters::Display* Chapters::Atom::GetDisplay(int index) const {
3461   if (index < 0)
3462     return NULL;
3463 
3464   if (index >= m_displays_count)
3465     return NULL;
3466 
3467   return m_displays + index;
3468 }
3469 
Init()3470 void Chapters::Atom::Init() {
3471   m_string_uid = NULL;
3472   m_uid = 0;
3473   m_start_timecode = -1;
3474   m_stop_timecode = -1;
3475 
3476   m_displays = NULL;
3477   m_displays_size = 0;
3478   m_displays_count = 0;
3479 }
3480 
ShallowCopy(Atom & rhs) const3481 void Chapters::Atom::ShallowCopy(Atom& rhs) const {
3482   rhs.m_string_uid = m_string_uid;
3483   rhs.m_uid = m_uid;
3484   rhs.m_start_timecode = m_start_timecode;
3485   rhs.m_stop_timecode = m_stop_timecode;
3486 
3487   rhs.m_displays = m_displays;
3488   rhs.m_displays_size = m_displays_size;
3489   rhs.m_displays_count = m_displays_count;
3490 }
3491 
Clear()3492 void Chapters::Atom::Clear() {
3493   delete[] m_string_uid;
3494   m_string_uid = NULL;
3495 
3496   while (m_displays_count > 0) {
3497     Display& d = m_displays[--m_displays_count];
3498     d.Clear();
3499   }
3500 
3501   delete[] m_displays;
3502   m_displays = NULL;
3503 
3504   m_displays_size = 0;
3505 }
3506 
Parse(IMkvReader * pReader,long long pos,long long size)3507 long Chapters::Atom::Parse(IMkvReader* pReader, long long pos, long long size) {
3508   const long long stop = pos + size;
3509 
3510   while (pos < stop) {
3511     long long id, size;
3512 
3513     long status = ParseElementHeader(pReader, pos, stop, id, size);
3514 
3515     if (status < 0)  // error
3516       return status;
3517 
3518     if (size == 0)  // 0 length payload, skip.
3519       continue;
3520 
3521     if (id == libwebm::kMkvChapterDisplay) {
3522       status = ParseDisplay(pReader, pos, size);
3523 
3524       if (status < 0)  // error
3525         return status;
3526     } else if (id == libwebm::kMkvChapterStringUID) {
3527       status = UnserializeString(pReader, pos, size, m_string_uid);
3528 
3529       if (status < 0)  // error
3530         return status;
3531     } else if (id == libwebm::kMkvChapterUID) {
3532       long long val;
3533       status = UnserializeInt(pReader, pos, size, val);
3534 
3535       if (status < 0)  // error
3536         return status;
3537 
3538       m_uid = static_cast<unsigned long long>(val);
3539     } else if (id == libwebm::kMkvChapterTimeStart) {
3540       const long long val = UnserializeUInt(pReader, pos, size);
3541 
3542       if (val < 0)  // error
3543         return static_cast<long>(val);
3544 
3545       m_start_timecode = val;
3546     } else if (id == libwebm::kMkvChapterTimeEnd) {
3547       const long long val = UnserializeUInt(pReader, pos, size);
3548 
3549       if (val < 0)  // error
3550         return static_cast<long>(val);
3551 
3552       m_stop_timecode = val;
3553     }
3554 
3555     pos += size;
3556     if (pos > stop)
3557       return E_FILE_FORMAT_INVALID;
3558   }
3559 
3560   if (pos != stop)
3561     return E_FILE_FORMAT_INVALID;
3562   return 0;
3563 }
3564 
GetTime(const Chapters * pChapters,long long timecode)3565 long long Chapters::Atom::GetTime(const Chapters* pChapters,
3566                                   long long timecode) {
3567   if (pChapters == NULL)
3568     return -1;
3569 
3570   Segment* const pSegment = pChapters->m_pSegment;
3571 
3572   if (pSegment == NULL)  // weird
3573     return -1;
3574 
3575   const SegmentInfo* const pInfo = pSegment->GetInfo();
3576 
3577   if (pInfo == NULL)
3578     return -1;
3579 
3580   const long long timecode_scale = pInfo->GetTimeCodeScale();
3581 
3582   if (timecode_scale < 1)  // weird
3583     return -1;
3584 
3585   if (timecode < 0)
3586     return -1;
3587 
3588   const long long result = timecode_scale * timecode;
3589 
3590   return result;
3591 }
3592 
ParseDisplay(IMkvReader * pReader,long long pos,long long size)3593 long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
3594                                   long long size) {
3595   if (!ExpandDisplaysArray())
3596     return -1;
3597 
3598   Display& d = m_displays[m_displays_count++];
3599   d.Init();
3600 
3601   return d.Parse(pReader, pos, size);
3602 }
3603 
ExpandDisplaysArray()3604 bool Chapters::Atom::ExpandDisplaysArray() {
3605   if (m_displays_size > m_displays_count)
3606     return true;  // nothing else to do
3607 
3608   const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
3609 
3610   Display* const displays = new (std::nothrow) Display[size];
3611 
3612   if (displays == NULL)
3613     return false;
3614 
3615   for (int idx = 0; idx < m_displays_count; ++idx) {
3616     m_displays[idx].ShallowCopy(displays[idx]);
3617   }
3618 
3619   delete[] m_displays;
3620   m_displays = displays;
3621 
3622   m_displays_size = size;
3623   return true;
3624 }
3625 
Display()3626 Chapters::Display::Display() {}
3627 
~Display()3628 Chapters::Display::~Display() {}
3629 
GetString() const3630 const char* Chapters::Display::GetString() const { return m_string; }
3631 
GetLanguage() const3632 const char* Chapters::Display::GetLanguage() const { return m_language; }
3633 
GetCountry() const3634 const char* Chapters::Display::GetCountry() const { return m_country; }
3635 
Init()3636 void Chapters::Display::Init() {
3637   m_string = NULL;
3638   m_language = NULL;
3639   m_country = NULL;
3640 }
3641 
ShallowCopy(Display & rhs) const3642 void Chapters::Display::ShallowCopy(Display& rhs) const {
3643   rhs.m_string = m_string;
3644   rhs.m_language = m_language;
3645   rhs.m_country = m_country;
3646 }
3647 
Clear()3648 void Chapters::Display::Clear() {
3649   delete[] m_string;
3650   m_string = NULL;
3651 
3652   delete[] m_language;
3653   m_language = NULL;
3654 
3655   delete[] m_country;
3656   m_country = NULL;
3657 }
3658 
Parse(IMkvReader * pReader,long long pos,long long size)3659 long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
3660                               long long size) {
3661   const long long stop = pos + size;
3662 
3663   while (pos < stop) {
3664     long long id, size;
3665 
3666     long status = ParseElementHeader(pReader, pos, stop, id, size);
3667 
3668     if (status < 0)  // error
3669       return status;
3670 
3671     if (size == 0)  // No payload.
3672       continue;
3673 
3674     if (id == libwebm::kMkvChapString) {
3675       status = UnserializeString(pReader, pos, size, m_string);
3676 
3677       if (status)
3678         return status;
3679     } else if (id == libwebm::kMkvChapLanguage) {
3680       status = UnserializeString(pReader, pos, size, m_language);
3681 
3682       if (status)
3683         return status;
3684     } else if (id == libwebm::kMkvChapCountry) {
3685       status = UnserializeString(pReader, pos, size, m_country);
3686 
3687       if (status)
3688         return status;
3689     }
3690 
3691     pos += size;
3692     if (pos > stop)
3693       return E_FILE_FORMAT_INVALID;
3694   }
3695 
3696   if (pos != stop)
3697     return E_FILE_FORMAT_INVALID;
3698   return 0;
3699 }
3700 
Tags(Segment * pSegment,long long payload_start,long long payload_size,long long element_start,long long element_size)3701 Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size,
3702            long long element_start, long long element_size)
3703     : m_pSegment(pSegment),
3704       m_start(payload_start),
3705       m_size(payload_size),
3706       m_element_start(element_start),
3707       m_element_size(element_size),
3708       m_tags(NULL),
3709       m_tags_size(0),
3710       m_tags_count(0) {}
3711 
~Tags()3712 Tags::~Tags() {
3713   while (m_tags_count > 0) {
3714     Tag& t = m_tags[--m_tags_count];
3715     t.Clear();
3716   }
3717   delete[] m_tags;
3718 }
3719 
Parse()3720 long Tags::Parse() {
3721   IMkvReader* const pReader = m_pSegment->m_pReader;
3722 
3723   long long pos = m_start;  // payload start
3724   const long long stop = pos + m_size;  // payload stop
3725 
3726   while (pos < stop) {
3727     long long id, size;
3728 
3729     long status = ParseElementHeader(pReader, pos, stop, id, size);
3730 
3731     if (status < 0)
3732       return status;
3733 
3734     if (size == 0)  // 0 length tag, read another
3735       continue;
3736 
3737     if (id == libwebm::kMkvTag) {
3738       status = ParseTag(pos, size);
3739 
3740       if (status < 0)
3741         return status;
3742     }
3743 
3744     pos += size;
3745     if (pos > stop)
3746       return E_FILE_FORMAT_INVALID;
3747   }
3748 
3749   if (pos != stop)
3750     return E_FILE_FORMAT_INVALID;
3751 
3752   return 0;
3753 }
3754 
GetTagCount() const3755 int Tags::GetTagCount() const { return m_tags_count; }
3756 
GetTag(int idx) const3757 const Tags::Tag* Tags::GetTag(int idx) const {
3758   if (idx < 0)
3759     return NULL;
3760 
3761   if (idx >= m_tags_count)
3762     return NULL;
3763 
3764   return m_tags + idx;
3765 }
3766 
ExpandTagsArray()3767 bool Tags::ExpandTagsArray() {
3768   if (m_tags_size > m_tags_count)
3769     return true;  // nothing else to do
3770 
3771   const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
3772 
3773   Tag* const tags = new (std::nothrow) Tag[size];
3774 
3775   if (tags == NULL)
3776     return false;
3777 
3778   for (int idx = 0; idx < m_tags_count; ++idx) {
3779     m_tags[idx].ShallowCopy(tags[idx]);
3780   }
3781 
3782   delete[] m_tags;
3783   m_tags = tags;
3784 
3785   m_tags_size = size;
3786   return true;
3787 }
3788 
ParseTag(long long pos,long long size)3789 long Tags::ParseTag(long long pos, long long size) {
3790   if (!ExpandTagsArray())
3791     return -1;
3792 
3793   Tag& t = m_tags[m_tags_count++];
3794   t.Init();
3795 
3796   return t.Parse(m_pSegment->m_pReader, pos, size);
3797 }
3798 
Tag()3799 Tags::Tag::Tag() {}
3800 
~Tag()3801 Tags::Tag::~Tag() {}
3802 
GetSimpleTagCount() const3803 int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
3804 
GetSimpleTag(int index) const3805 const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
3806   if (index < 0)
3807     return NULL;
3808 
3809   if (index >= m_simple_tags_count)
3810     return NULL;
3811 
3812   return m_simple_tags + index;
3813 }
3814 
Init()3815 void Tags::Tag::Init() {
3816   m_simple_tags = NULL;
3817   m_simple_tags_size = 0;
3818   m_simple_tags_count = 0;
3819 }
3820 
ShallowCopy(Tag & rhs) const3821 void Tags::Tag::ShallowCopy(Tag& rhs) const {
3822   rhs.m_simple_tags = m_simple_tags;
3823   rhs.m_simple_tags_size = m_simple_tags_size;
3824   rhs.m_simple_tags_count = m_simple_tags_count;
3825 }
3826 
Clear()3827 void Tags::Tag::Clear() {
3828   while (m_simple_tags_count > 0) {
3829     SimpleTag& d = m_simple_tags[--m_simple_tags_count];
3830     d.Clear();
3831   }
3832 
3833   delete[] m_simple_tags;
3834   m_simple_tags = NULL;
3835 
3836   m_simple_tags_size = 0;
3837 }
3838 
Parse(IMkvReader * pReader,long long pos,long long size)3839 long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) {
3840   const long long stop = pos + size;
3841 
3842   while (pos < stop) {
3843     long long id, size;
3844 
3845     long status = ParseElementHeader(pReader, pos, stop, id, size);
3846 
3847     if (status < 0)
3848       return status;
3849 
3850     if (size == 0)  // 0 length tag, read another
3851       continue;
3852 
3853     if (id == libwebm::kMkvSimpleTag) {
3854       status = ParseSimpleTag(pReader, pos, size);
3855 
3856       if (status < 0)
3857         return status;
3858     }
3859 
3860     pos += size;
3861     if (pos > stop)
3862       return E_FILE_FORMAT_INVALID;
3863   }
3864 
3865   if (pos != stop)
3866     return E_FILE_FORMAT_INVALID;
3867   return 0;
3868 }
3869 
ParseSimpleTag(IMkvReader * pReader,long long pos,long long size)3870 long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
3871                                long long size) {
3872   if (!ExpandSimpleTagsArray())
3873     return -1;
3874 
3875   SimpleTag& st = m_simple_tags[m_simple_tags_count++];
3876   st.Init();
3877 
3878   return st.Parse(pReader, pos, size);
3879 }
3880 
ExpandSimpleTagsArray()3881 bool Tags::Tag::ExpandSimpleTagsArray() {
3882   if (m_simple_tags_size > m_simple_tags_count)
3883     return true;  // nothing else to do
3884 
3885   const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
3886 
3887   SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
3888 
3889   if (displays == NULL)
3890     return false;
3891 
3892   for (int idx = 0; idx < m_simple_tags_count; ++idx) {
3893     m_simple_tags[idx].ShallowCopy(displays[idx]);
3894   }
3895 
3896   delete[] m_simple_tags;
3897   m_simple_tags = displays;
3898 
3899   m_simple_tags_size = size;
3900   return true;
3901 }
3902 
SimpleTag()3903 Tags::SimpleTag::SimpleTag() {}
3904 
~SimpleTag()3905 Tags::SimpleTag::~SimpleTag() {}
3906 
GetTagName() const3907 const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
3908 
GetTagString() const3909 const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
3910 
Init()3911 void Tags::SimpleTag::Init() {
3912   m_tag_name = NULL;
3913   m_tag_string = NULL;
3914 }
3915 
ShallowCopy(SimpleTag & rhs) const3916 void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
3917   rhs.m_tag_name = m_tag_name;
3918   rhs.m_tag_string = m_tag_string;
3919 }
3920 
Clear()3921 void Tags::SimpleTag::Clear() {
3922   delete[] m_tag_name;
3923   m_tag_name = NULL;
3924 
3925   delete[] m_tag_string;
3926   m_tag_string = NULL;
3927 }
3928 
Parse(IMkvReader * pReader,long long pos,long long size)3929 long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos,
3930                             long long size) {
3931   const long long stop = pos + size;
3932 
3933   while (pos < stop) {
3934     long long id, size;
3935 
3936     long status = ParseElementHeader(pReader, pos, stop, id, size);
3937 
3938     if (status < 0)  // error
3939       return status;
3940 
3941     if (size == 0)  // weird
3942       continue;
3943 
3944     if (id == libwebm::kMkvTagName) {
3945       status = UnserializeString(pReader, pos, size, m_tag_name);
3946 
3947       if (status)
3948         return status;
3949     } else if (id == libwebm::kMkvTagString) {
3950       status = UnserializeString(pReader, pos, size, m_tag_string);
3951 
3952       if (status)
3953         return status;
3954     }
3955 
3956     pos += size;
3957     if (pos > stop)
3958       return E_FILE_FORMAT_INVALID;
3959   }
3960 
3961   if (pos != stop)
3962     return E_FILE_FORMAT_INVALID;
3963   return 0;
3964 }
3965 
SegmentInfo(Segment * pSegment,long long start,long long size_,long long element_start,long long element_size)3966 SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
3967                          long long element_start, long long element_size)
3968     : m_pSegment(pSegment),
3969       m_start(start),
3970       m_size(size_),
3971       m_element_start(element_start),
3972       m_element_size(element_size),
3973       m_pMuxingAppAsUTF8(NULL),
3974       m_pWritingAppAsUTF8(NULL),
3975       m_pTitleAsUTF8(NULL) {}
3976 
~SegmentInfo()3977 SegmentInfo::~SegmentInfo() {
3978   delete[] m_pMuxingAppAsUTF8;
3979   m_pMuxingAppAsUTF8 = NULL;
3980 
3981   delete[] m_pWritingAppAsUTF8;
3982   m_pWritingAppAsUTF8 = NULL;
3983 
3984   delete[] m_pTitleAsUTF8;
3985   m_pTitleAsUTF8 = NULL;
3986 }
3987 
Parse()3988 long SegmentInfo::Parse() {
3989   assert(m_pMuxingAppAsUTF8 == NULL);
3990   assert(m_pWritingAppAsUTF8 == NULL);
3991   assert(m_pTitleAsUTF8 == NULL);
3992 
3993   IMkvReader* const pReader = m_pSegment->m_pReader;
3994 
3995   long long pos = m_start;
3996   const long long stop = m_start + m_size;
3997 
3998   m_timecodeScale = 1000000;
3999   m_duration = -1;
4000 
4001   while (pos < stop) {
4002     long long id, size;
4003 
4004     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4005 
4006     if (status < 0)  // error
4007       return status;
4008 
4009     if (id == libwebm::kMkvTimecodeScale) {
4010       m_timecodeScale = UnserializeUInt(pReader, pos, size);
4011 
4012       if (m_timecodeScale <= 0)
4013         return E_FILE_FORMAT_INVALID;
4014     } else if (id == libwebm::kMkvDuration) {
4015       const long status = UnserializeFloat(pReader, pos, size, m_duration);
4016 
4017       if (status < 0)
4018         return status;
4019 
4020       if (m_duration < 0)
4021         return E_FILE_FORMAT_INVALID;
4022     } else if (id == libwebm::kMkvMuxingApp) {
4023       const long status =
4024           UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8);
4025 
4026       if (status)
4027         return status;
4028     } else if (id == libwebm::kMkvWritingApp) {
4029       const long status =
4030           UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8);
4031 
4032       if (status)
4033         return status;
4034     } else if (id == libwebm::kMkvTitle) {
4035       const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8);
4036 
4037       if (status)
4038         return status;
4039     }
4040 
4041     pos += size;
4042 
4043     if (pos > stop)
4044       return E_FILE_FORMAT_INVALID;
4045   }
4046 
4047   const double rollover_check = m_duration * m_timecodeScale;
4048   if (rollover_check > static_cast<double>(LLONG_MAX))
4049     return E_FILE_FORMAT_INVALID;
4050 
4051   if (pos != stop)
4052     return E_FILE_FORMAT_INVALID;
4053 
4054   return 0;
4055 }
4056 
GetTimeCodeScale() const4057 long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; }
4058 
GetDuration() const4059 long long SegmentInfo::GetDuration() const {
4060   if (m_duration < 0)
4061     return -1;
4062 
4063   assert(m_timecodeScale >= 1);
4064 
4065   const double dd = double(m_duration) * double(m_timecodeScale);
4066   const long long d = static_cast<long long>(dd);
4067 
4068   return d;
4069 }
4070 
GetMuxingAppAsUTF8() const4071 const char* SegmentInfo::GetMuxingAppAsUTF8() const {
4072   return m_pMuxingAppAsUTF8;
4073 }
4074 
GetWritingAppAsUTF8() const4075 const char* SegmentInfo::GetWritingAppAsUTF8() const {
4076   return m_pWritingAppAsUTF8;
4077 }
4078 
GetTitleAsUTF8() const4079 const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; }
4080 
4081 ///////////////////////////////////////////////////////////////
4082 // ContentEncoding element
ContentCompression()4083 ContentEncoding::ContentCompression::ContentCompression()
4084     : algo(0), settings(NULL), settings_len(0) {}
4085 
~ContentCompression()4086 ContentEncoding::ContentCompression::~ContentCompression() {
4087   delete[] settings;
4088 }
4089 
ContentEncryption()4090 ContentEncoding::ContentEncryption::ContentEncryption()
4091     : algo(0),
4092       key_id(NULL),
4093       key_id_len(0),
4094       signature(NULL),
4095       signature_len(0),
4096       sig_key_id(NULL),
4097       sig_key_id_len(0),
4098       sig_algo(0),
4099       sig_hash_algo(0) {}
4100 
~ContentEncryption()4101 ContentEncoding::ContentEncryption::~ContentEncryption() {
4102   delete[] key_id;
4103   delete[] signature;
4104   delete[] sig_key_id;
4105 }
4106 
ContentEncoding()4107 ContentEncoding::ContentEncoding()
4108     : compression_entries_(NULL),
4109       compression_entries_end_(NULL),
4110       encryption_entries_(NULL),
4111       encryption_entries_end_(NULL),
4112       encoding_order_(0),
4113       encoding_scope_(1),
4114       encoding_type_(0) {}
4115 
~ContentEncoding()4116 ContentEncoding::~ContentEncoding() {
4117   ContentCompression** comp_i = compression_entries_;
4118   ContentCompression** const comp_j = compression_entries_end_;
4119 
4120   while (comp_i != comp_j) {
4121     ContentCompression* const comp = *comp_i++;
4122     delete comp;
4123   }
4124 
4125   delete[] compression_entries_;
4126 
4127   ContentEncryption** enc_i = encryption_entries_;
4128   ContentEncryption** const enc_j = encryption_entries_end_;
4129 
4130   while (enc_i != enc_j) {
4131     ContentEncryption* const enc = *enc_i++;
4132     delete enc;
4133   }
4134 
4135   delete[] encryption_entries_;
4136 }
4137 
4138 const ContentEncoding::ContentCompression*
GetCompressionByIndex(unsigned long idx) const4139 ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
4140   const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4141   assert(count >= 0);
4142 
4143   if (idx >= static_cast<unsigned long>(count))
4144     return NULL;
4145 
4146   return compression_entries_[idx];
4147 }
4148 
GetCompressionCount() const4149 unsigned long ContentEncoding::GetCompressionCount() const {
4150   const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4151   assert(count >= 0);
4152 
4153   return static_cast<unsigned long>(count);
4154 }
4155 
GetEncryptionByIndex(unsigned long idx) const4156 const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex(
4157     unsigned long idx) const {
4158   const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4159   assert(count >= 0);
4160 
4161   if (idx >= static_cast<unsigned long>(count))
4162     return NULL;
4163 
4164   return encryption_entries_[idx];
4165 }
4166 
GetEncryptionCount() const4167 unsigned long ContentEncoding::GetEncryptionCount() const {
4168   const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4169   assert(count >= 0);
4170 
4171   return static_cast<unsigned long>(count);
4172 }
4173 
ParseContentEncAESSettingsEntry(long long start,long long size,IMkvReader * pReader,ContentEncAESSettings * aes)4174 long ContentEncoding::ParseContentEncAESSettingsEntry(
4175     long long start, long long size, IMkvReader* pReader,
4176     ContentEncAESSettings* aes) {
4177   assert(pReader);
4178   assert(aes);
4179 
4180   long long pos = start;
4181   const long long stop = start + size;
4182 
4183   while (pos < stop) {
4184     long long id, size;
4185     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4186     if (status < 0)  // error
4187       return status;
4188 
4189     if (id == libwebm::kMkvAESSettingsCipherMode) {
4190       aes->cipher_mode = UnserializeUInt(pReader, pos, size);
4191       if (aes->cipher_mode != 1)
4192         return E_FILE_FORMAT_INVALID;
4193     }
4194 
4195     pos += size;  // consume payload
4196     if (pos > stop)
4197       return E_FILE_FORMAT_INVALID;
4198   }
4199 
4200   return 0;
4201 }
4202 
ParseContentEncodingEntry(long long start,long long size,IMkvReader * pReader)4203 long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
4204                                                 IMkvReader* pReader) {
4205   assert(pReader);
4206 
4207   long long pos = start;
4208   const long long stop = start + size;
4209 
4210   // Count ContentCompression and ContentEncryption elements.
4211   long long compression_count = 0;
4212   long long encryption_count = 0;
4213 
4214   while (pos < stop) {
4215     long long id, size;
4216     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4217     if (status < 0)  // error
4218       return status;
4219 
4220     if (id == libwebm::kMkvContentCompression) {
4221       ++compression_count;
4222       if (compression_count > INT_MAX)
4223         return E_PARSE_FAILED;
4224     }
4225 
4226     if (id == libwebm::kMkvContentEncryption) {
4227       ++encryption_count;
4228       if (encryption_count > INT_MAX)
4229         return E_PARSE_FAILED;
4230     }
4231 
4232     pos += size;  // consume payload
4233     if (pos > stop)
4234       return E_FILE_FORMAT_INVALID;
4235   }
4236 
4237   if (compression_count <= 0 && encryption_count <= 0)
4238     return -1;
4239 
4240   if (compression_count > 0) {
4241     compression_entries_ = new (std::nothrow)
4242         ContentCompression*[static_cast<size_t>(compression_count)];
4243     if (!compression_entries_)
4244       return -1;
4245     compression_entries_end_ = compression_entries_;
4246   }
4247 
4248   if (encryption_count > 0) {
4249     encryption_entries_ = new (std::nothrow)
4250         ContentEncryption*[static_cast<size_t>(encryption_count)];
4251     if (!encryption_entries_) {
4252       delete[] compression_entries_;
4253       compression_entries_ = NULL;
4254       return -1;
4255     }
4256     encryption_entries_end_ = encryption_entries_;
4257   }
4258 
4259   pos = start;
4260   while (pos < stop) {
4261     long long id, size;
4262     long status = ParseElementHeader(pReader, pos, stop, id, size);
4263     if (status < 0)  // error
4264       return status;
4265 
4266     if (id == libwebm::kMkvContentEncodingOrder) {
4267       encoding_order_ = UnserializeUInt(pReader, pos, size);
4268     } else if (id == libwebm::kMkvContentEncodingScope) {
4269       encoding_scope_ = UnserializeUInt(pReader, pos, size);
4270       if (encoding_scope_ < 1)
4271         return -1;
4272     } else if (id == libwebm::kMkvContentEncodingType) {
4273       encoding_type_ = UnserializeUInt(pReader, pos, size);
4274     } else if (id == libwebm::kMkvContentCompression) {
4275       ContentCompression* const compression =
4276           new (std::nothrow) ContentCompression();
4277       if (!compression)
4278         return -1;
4279 
4280       status = ParseCompressionEntry(pos, size, pReader, compression);
4281       if (status) {
4282         delete compression;
4283         return status;
4284       }
4285       assert(compression_count > 0);
4286       *compression_entries_end_++ = compression;
4287     } else if (id == libwebm::kMkvContentEncryption) {
4288       ContentEncryption* const encryption =
4289           new (std::nothrow) ContentEncryption();
4290       if (!encryption)
4291         return -1;
4292 
4293       status = ParseEncryptionEntry(pos, size, pReader, encryption);
4294       if (status) {
4295         delete encryption;
4296         return status;
4297       }
4298       assert(encryption_count > 0);
4299       *encryption_entries_end_++ = encryption;
4300     }
4301 
4302     pos += size;  // consume payload
4303     if (pos > stop)
4304       return E_FILE_FORMAT_INVALID;
4305   }
4306 
4307   if (pos != stop)
4308     return E_FILE_FORMAT_INVALID;
4309   return 0;
4310 }
4311 
ParseCompressionEntry(long long start,long long size,IMkvReader * pReader,ContentCompression * compression)4312 long ContentEncoding::ParseCompressionEntry(long long start, long long size,
4313                                             IMkvReader* pReader,
4314                                             ContentCompression* compression) {
4315   assert(pReader);
4316   assert(compression);
4317 
4318   long long pos = start;
4319   const long long stop = start + size;
4320 
4321   bool valid = false;
4322 
4323   while (pos < stop) {
4324     long long id, size;
4325     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4326     if (status < 0)  // error
4327       return status;
4328 
4329     if (id == libwebm::kMkvContentCompAlgo) {
4330       long long algo = UnserializeUInt(pReader, pos, size);
4331       if (algo < 0)
4332         return E_FILE_FORMAT_INVALID;
4333       compression->algo = algo;
4334       valid = true;
4335     } else if (id == libwebm::kMkvContentCompSettings) {
4336       if (size <= 0)
4337         return E_FILE_FORMAT_INVALID;
4338 
4339       const size_t buflen = static_cast<size_t>(size);
4340       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4341       if (buf == NULL)
4342         return -1;
4343 
4344       const int read_status =
4345           pReader->Read(pos, static_cast<long>(buflen), buf);
4346       if (read_status) {
4347         delete[] buf;
4348         return status;
4349       }
4350 
4351       // There should be only one settings element per content compression.
4352       if (compression->settings != NULL) {
4353         delete[] buf;
4354         return E_FILE_FORMAT_INVALID;
4355       }
4356 
4357       compression->settings = buf;
4358       compression->settings_len = buflen;
4359     }
4360 
4361     pos += size;  // consume payload
4362     if (pos > stop)
4363       return E_FILE_FORMAT_INVALID;
4364   }
4365 
4366   // ContentCompAlgo is mandatory
4367   if (!valid)
4368     return E_FILE_FORMAT_INVALID;
4369 
4370   return 0;
4371 }
4372 
ParseEncryptionEntry(long long start,long long size,IMkvReader * pReader,ContentEncryption * encryption)4373 long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
4374                                            IMkvReader* pReader,
4375                                            ContentEncryption* encryption) {
4376   assert(pReader);
4377   assert(encryption);
4378 
4379   long long pos = start;
4380   const long long stop = start + size;
4381 
4382   while (pos < stop) {
4383     long long id, size;
4384     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4385     if (status < 0)  // error
4386       return status;
4387 
4388     if (id == libwebm::kMkvContentEncAlgo) {
4389       encryption->algo = UnserializeUInt(pReader, pos, size);
4390       if (encryption->algo != 5)
4391         return E_FILE_FORMAT_INVALID;
4392     } else if (id == libwebm::kMkvContentEncKeyID) {
4393       delete[] encryption->key_id;
4394       encryption->key_id = NULL;
4395       encryption->key_id_len = 0;
4396 
4397       if (size <= 0)
4398         return E_FILE_FORMAT_INVALID;
4399 
4400       const size_t buflen = static_cast<size_t>(size);
4401       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4402       if (buf == NULL)
4403         return -1;
4404 
4405       const int read_status =
4406           pReader->Read(pos, static_cast<long>(buflen), buf);
4407       if (read_status) {
4408         delete[] buf;
4409         return status;
4410       }
4411 
4412       encryption->key_id = buf;
4413       encryption->key_id_len = buflen;
4414     } else if (id == libwebm::kMkvContentSignature) {
4415       delete[] encryption->signature;
4416       encryption->signature = NULL;
4417       encryption->signature_len = 0;
4418 
4419       if (size <= 0)
4420         return E_FILE_FORMAT_INVALID;
4421 
4422       const size_t buflen = static_cast<size_t>(size);
4423       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4424       if (buf == NULL)
4425         return -1;
4426 
4427       const int read_status =
4428           pReader->Read(pos, static_cast<long>(buflen), buf);
4429       if (read_status) {
4430         delete[] buf;
4431         return status;
4432       }
4433 
4434       encryption->signature = buf;
4435       encryption->signature_len = buflen;
4436     } else if (id == libwebm::kMkvContentSigKeyID) {
4437       delete[] encryption->sig_key_id;
4438       encryption->sig_key_id = NULL;
4439       encryption->sig_key_id_len = 0;
4440 
4441       if (size <= 0)
4442         return E_FILE_FORMAT_INVALID;
4443 
4444       const size_t buflen = static_cast<size_t>(size);
4445       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4446       if (buf == NULL)
4447         return -1;
4448 
4449       const int read_status =
4450           pReader->Read(pos, static_cast<long>(buflen), buf);
4451       if (read_status) {
4452         delete[] buf;
4453         return status;
4454       }
4455 
4456       encryption->sig_key_id = buf;
4457       encryption->sig_key_id_len = buflen;
4458     } else if (id == libwebm::kMkvContentSigAlgo) {
4459       encryption->sig_algo = UnserializeUInt(pReader, pos, size);
4460     } else if (id == libwebm::kMkvContentSigHashAlgo) {
4461       encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
4462     } else if (id == libwebm::kMkvContentEncAESSettings) {
4463       const long status = ParseContentEncAESSettingsEntry(
4464           pos, size, pReader, &encryption->aes_settings);
4465       if (status)
4466         return status;
4467     }
4468 
4469     pos += size;  // consume payload
4470     if (pos > stop)
4471       return E_FILE_FORMAT_INVALID;
4472   }
4473 
4474   return 0;
4475 }
4476 
Track(Segment * pSegment,long long element_start,long long element_size)4477 Track::Track(Segment* pSegment, long long element_start, long long element_size)
4478     : m_pSegment(pSegment),
4479       m_element_start(element_start),
4480       m_element_size(element_size),
4481       content_encoding_entries_(NULL),
4482       content_encoding_entries_end_(NULL) {}
4483 
~Track()4484 Track::~Track() {
4485   Info& info = const_cast<Info&>(m_info);
4486   info.Clear();
4487 
4488   ContentEncoding** i = content_encoding_entries_;
4489   ContentEncoding** const j = content_encoding_entries_end_;
4490 
4491   while (i != j) {
4492     ContentEncoding* const encoding = *i++;
4493     delete encoding;
4494   }
4495 
4496   delete[] content_encoding_entries_;
4497 }
4498 
Create(Segment * pSegment,const Info & info,long long element_start,long long element_size,Track * & pResult)4499 long Track::Create(Segment* pSegment, const Info& info, long long element_start,
4500                    long long element_size, Track*& pResult) {
4501   if (pResult)
4502     return -1;
4503 
4504   Track* const pTrack =
4505       new (std::nothrow) Track(pSegment, element_start, element_size);
4506 
4507   if (pTrack == NULL)
4508     return -1;  // generic error
4509 
4510   const int status = info.Copy(pTrack->m_info);
4511 
4512   if (status) {  // error
4513     delete pTrack;
4514     return status;
4515   }
4516 
4517   pResult = pTrack;
4518   return 0;  // success
4519 }
4520 
Info()4521 Track::Info::Info()
4522     : uid(0),
4523       defaultDuration(0),
4524       codecDelay(0),
4525       seekPreRoll(0),
4526       nameAsUTF8(NULL),
4527       language(NULL),
4528       codecId(NULL),
4529       codecNameAsUTF8(NULL),
4530       codecPrivate(NULL),
4531       codecPrivateSize(0),
4532       lacing(false) {}
4533 
~Info()4534 Track::Info::~Info() { Clear(); }
4535 
Clear()4536 void Track::Info::Clear() {
4537   delete[] nameAsUTF8;
4538   nameAsUTF8 = NULL;
4539 
4540   delete[] language;
4541   language = NULL;
4542 
4543   delete[] codecId;
4544   codecId = NULL;
4545 
4546   delete[] codecPrivate;
4547   codecPrivate = NULL;
4548   codecPrivateSize = 0;
4549 
4550   delete[] codecNameAsUTF8;
4551   codecNameAsUTF8 = NULL;
4552 }
4553 
CopyStr(char * Info::* str,Info & dst_) const4554 int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
4555   if (str == static_cast<char * Info::*>(NULL))
4556     return -1;
4557 
4558   char*& dst = dst_.*str;
4559 
4560   if (dst)  // should be NULL already
4561     return -1;
4562 
4563   const char* const src = this->*str;
4564 
4565   if (src == NULL)
4566     return 0;
4567 
4568   const size_t len = strlen(src);
4569 
4570   dst = SafeArrayAlloc<char>(1, len + 1);
4571 
4572   if (dst == NULL)
4573     return -1;
4574 
4575   memcpy(dst, src, len);
4576   dst[len] = '\0';
4577 
4578   return 0;
4579 }
4580 
Copy(Info & dst) const4581 int Track::Info::Copy(Info& dst) const {
4582   if (&dst == this)
4583     return 0;
4584 
4585   dst.type = type;
4586   dst.number = number;
4587   dst.defaultDuration = defaultDuration;
4588   dst.codecDelay = codecDelay;
4589   dst.seekPreRoll = seekPreRoll;
4590   dst.uid = uid;
4591   dst.lacing = lacing;
4592   dst.settings = settings;
4593 
4594   // We now copy the string member variables from src to dst.
4595   // This involves memory allocation so in principle the operation
4596   // can fail (indeed, that's why we have Info::Copy), so we must
4597   // report this to the caller.  An error return from this function
4598   // therefore implies that the copy was only partially successful.
4599 
4600   if (int status = CopyStr(&Info::nameAsUTF8, dst))
4601     return status;
4602 
4603   if (int status = CopyStr(&Info::language, dst))
4604     return status;
4605 
4606   if (int status = CopyStr(&Info::codecId, dst))
4607     return status;
4608 
4609   if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
4610     return status;
4611 
4612   if (codecPrivateSize > 0) {
4613     if (codecPrivate == NULL)
4614       return -1;
4615 
4616     if (dst.codecPrivate)
4617       return -1;
4618 
4619     if (dst.codecPrivateSize != 0)
4620       return -1;
4621 
4622     dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
4623 
4624     if (dst.codecPrivate == NULL)
4625       return -1;
4626 
4627     memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
4628     dst.codecPrivateSize = codecPrivateSize;
4629   }
4630 
4631   return 0;
4632 }
4633 
GetEOS() const4634 const BlockEntry* Track::GetEOS() const { return &m_eos; }
4635 
GetType() const4636 long Track::GetType() const { return m_info.type; }
4637 
GetNumber() const4638 long Track::GetNumber() const { return m_info.number; }
4639 
GetUid() const4640 unsigned long long Track::GetUid() const { return m_info.uid; }
4641 
GetNameAsUTF8() const4642 const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
4643 
GetLanguage() const4644 const char* Track::GetLanguage() const { return m_info.language; }
4645 
GetCodecNameAsUTF8() const4646 const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
4647 
GetCodecId() const4648 const char* Track::GetCodecId() const { return m_info.codecId; }
4649 
GetCodecPrivate(size_t & size) const4650 const unsigned char* Track::GetCodecPrivate(size_t& size) const {
4651   size = m_info.codecPrivateSize;
4652   return m_info.codecPrivate;
4653 }
4654 
GetLacing() const4655 bool Track::GetLacing() const { return m_info.lacing; }
4656 
GetDefaultDuration() const4657 unsigned long long Track::GetDefaultDuration() const {
4658   return m_info.defaultDuration;
4659 }
4660 
GetCodecDelay() const4661 unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
4662 
GetSeekPreRoll() const4663 unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
4664 
GetFirst(const BlockEntry * & pBlockEntry) const4665 long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
4666   const Cluster* pCluster = m_pSegment->GetFirst();
4667 
4668   for (int i = 0;;) {
4669     if (pCluster == NULL) {
4670       pBlockEntry = GetEOS();
4671       return 1;
4672     }
4673 
4674     if (pCluster->EOS()) {
4675       if (m_pSegment->DoneParsing()) {
4676         pBlockEntry = GetEOS();
4677         return 1;
4678       }
4679 
4680       pBlockEntry = 0;
4681       return E_BUFFER_NOT_FULL;
4682     }
4683 
4684     long status = pCluster->GetFirst(pBlockEntry);
4685 
4686     if (status < 0)  // error
4687       return status;
4688 
4689     if (pBlockEntry == 0) {  // empty cluster
4690       pCluster = m_pSegment->GetNext(pCluster);
4691       continue;
4692     }
4693 
4694     for (;;) {
4695       const Block* const pBlock = pBlockEntry->GetBlock();
4696       assert(pBlock);
4697 
4698       const long long tn = pBlock->GetTrackNumber();
4699 
4700       if ((tn == m_info.number) && VetEntry(pBlockEntry))
4701         return 0;
4702 
4703       const BlockEntry* pNextEntry;
4704 
4705       status = pCluster->GetNext(pBlockEntry, pNextEntry);
4706 
4707       if (status < 0)  // error
4708         return status;
4709 
4710       if (pNextEntry == 0)
4711         break;
4712 
4713       pBlockEntry = pNextEntry;
4714     }
4715 
4716     ++i;
4717 
4718     if (i >= 100)
4719       break;
4720 
4721     pCluster = m_pSegment->GetNext(pCluster);
4722   }
4723 
4724   // NOTE: if we get here, it means that we didn't find a block with
4725   // a matching track number.  We interpret that as an error (which
4726   // might be too conservative).
4727 
4728   pBlockEntry = GetEOS();  // so we can return a non-NULL value
4729   return 1;
4730 }
4731 
GetNext(const BlockEntry * pCurrEntry,const BlockEntry * & pNextEntry) const4732 long Track::GetNext(const BlockEntry* pCurrEntry,
4733                     const BlockEntry*& pNextEntry) const {
4734   assert(pCurrEntry);
4735   assert(!pCurrEntry->EOS());  //?
4736 
4737   const Block* const pCurrBlock = pCurrEntry->GetBlock();
4738   assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
4739   if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
4740     return -1;
4741 
4742   const Cluster* pCluster = pCurrEntry->GetCluster();
4743   assert(pCluster);
4744   assert(!pCluster->EOS());
4745 
4746   long status = pCluster->GetNext(pCurrEntry, pNextEntry);
4747 
4748   if (status < 0)  // error
4749     return status;
4750 
4751   for (int i = 0;;) {
4752     while (pNextEntry) {
4753       const Block* const pNextBlock = pNextEntry->GetBlock();
4754       assert(pNextBlock);
4755 
4756       if (pNextBlock->GetTrackNumber() == m_info.number)
4757         return 0;
4758 
4759       pCurrEntry = pNextEntry;
4760 
4761       status = pCluster->GetNext(pCurrEntry, pNextEntry);
4762 
4763       if (status < 0)  // error
4764         return status;
4765     }
4766 
4767     pCluster = m_pSegment->GetNext(pCluster);
4768 
4769     if (pCluster == NULL) {
4770       pNextEntry = GetEOS();
4771       return 1;
4772     }
4773 
4774     if (pCluster->EOS()) {
4775       if (m_pSegment->DoneParsing()) {
4776         pNextEntry = GetEOS();
4777         return 1;
4778       }
4779 
4780       // TODO: there is a potential O(n^2) problem here: we tell the
4781       // caller to (pre)load another cluster, which he does, but then he
4782       // calls GetNext again, which repeats the same search.  This is
4783       // a pathological case, since the only way it can happen is if
4784       // there exists a long sequence of clusters none of which contain a
4785       // block from this track.  One way around this problem is for the
4786       // caller to be smarter when he loads another cluster: don't call
4787       // us back until you have a cluster that contains a block from this
4788       // track. (Of course, that's not cheap either, since our caller
4789       // would have to scan the each cluster as it's loaded, so that
4790       // would just push back the problem.)
4791 
4792       pNextEntry = NULL;
4793       return E_BUFFER_NOT_FULL;
4794     }
4795 
4796     status = pCluster->GetFirst(pNextEntry);
4797 
4798     if (status < 0)  // error
4799       return status;
4800 
4801     if (pNextEntry == NULL)  // empty cluster
4802       continue;
4803 
4804     ++i;
4805 
4806     if (i >= 100)
4807       break;
4808   }
4809 
4810   // NOTE: if we get here, it means that we didn't find a block with
4811   // a matching track number after lots of searching, so we give
4812   // up trying.
4813 
4814   pNextEntry = GetEOS();  // so we can return a non-NULL value
4815   return 1;
4816 }
4817 
VetEntry(const BlockEntry * pBlockEntry) const4818 bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
4819   assert(pBlockEntry);
4820   const Block* const pBlock = pBlockEntry->GetBlock();
4821   assert(pBlock);
4822   assert(pBlock->GetTrackNumber() == m_info.number);
4823   if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
4824     return false;
4825 
4826   // This function is used during a seek to determine whether the
4827   // frame is a valid seek target.  This default function simply
4828   // returns true, which means all frames are valid seek targets.
4829   // It gets overridden by the VideoTrack class, because only video
4830   // keyframes can be used as seek target.
4831 
4832   return true;
4833 }
4834 
Seek(long long time_ns,const BlockEntry * & pResult) const4835 long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
4836   const long status = GetFirst(pResult);
4837 
4838   if (status < 0)  // buffer underflow, etc
4839     return status;
4840 
4841   assert(pResult);
4842 
4843   if (pResult->EOS())
4844     return 0;
4845 
4846   const Cluster* pCluster = pResult->GetCluster();
4847   assert(pCluster);
4848   assert(pCluster->GetIndex() >= 0);
4849 
4850   if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
4851     return 0;
4852 
4853   Cluster** const clusters = m_pSegment->m_clusters;
4854   assert(clusters);
4855 
4856   const long count = m_pSegment->GetCount();  // loaded only, not preloaded
4857   assert(count > 0);
4858 
4859   Cluster** const i = clusters + pCluster->GetIndex();
4860   assert(i);
4861   assert(*i == pCluster);
4862   assert(pCluster->GetTime() <= time_ns);
4863 
4864   Cluster** const j = clusters + count;
4865 
4866   Cluster** lo = i;
4867   Cluster** hi = j;
4868 
4869   while (lo < hi) {
4870     // INVARIANT:
4871     //[i, lo) <= time_ns
4872     //[lo, hi) ?
4873     //[hi, j)  > time_ns
4874 
4875     Cluster** const mid = lo + (hi - lo) / 2;
4876     assert(mid < hi);
4877 
4878     pCluster = *mid;
4879     assert(pCluster);
4880     assert(pCluster->GetIndex() >= 0);
4881     assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
4882 
4883     const long long t = pCluster->GetTime();
4884 
4885     if (t <= time_ns)
4886       lo = mid + 1;
4887     else
4888       hi = mid;
4889 
4890     assert(lo <= hi);
4891   }
4892 
4893   assert(lo == hi);
4894   assert(lo > i);
4895   assert(lo <= j);
4896 
4897   while (lo > i) {
4898     pCluster = *--lo;
4899     assert(pCluster);
4900     assert(pCluster->GetTime() <= time_ns);
4901 
4902     pResult = pCluster->GetEntry(this);
4903 
4904     if ((pResult != 0) && !pResult->EOS())
4905       return 0;
4906 
4907     // landed on empty cluster (no entries)
4908   }
4909 
4910   pResult = GetEOS();  // weird
4911   return 0;
4912 }
4913 
GetContentEncodingByIndex(unsigned long idx) const4914 const ContentEncoding* Track::GetContentEncodingByIndex(
4915     unsigned long idx) const {
4916   const ptrdiff_t count =
4917       content_encoding_entries_end_ - content_encoding_entries_;
4918   assert(count >= 0);
4919 
4920   if (idx >= static_cast<unsigned long>(count))
4921     return NULL;
4922 
4923   return content_encoding_entries_[idx];
4924 }
4925 
GetContentEncodingCount() const4926 unsigned long Track::GetContentEncodingCount() const {
4927   const ptrdiff_t count =
4928       content_encoding_entries_end_ - content_encoding_entries_;
4929   assert(count >= 0);
4930 
4931   return static_cast<unsigned long>(count);
4932 }
4933 
ParseContentEncodingsEntry(long long start,long long size)4934 long Track::ParseContentEncodingsEntry(long long start, long long size) {
4935   IMkvReader* const pReader = m_pSegment->m_pReader;
4936   assert(pReader);
4937 
4938   long long pos = start;
4939   const long long stop = start + size;
4940 
4941   // Count ContentEncoding elements.
4942   long long count = 0;
4943   while (pos < stop) {
4944     long long id, size;
4945     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4946     if (status < 0)  // error
4947       return status;
4948 
4949     // pos now designates start of element
4950     if (id == libwebm::kMkvContentEncoding) {
4951       ++count;
4952       if (count > INT_MAX)
4953         return E_PARSE_FAILED;
4954     }
4955 
4956     pos += size;  // consume payload
4957     if (pos > stop)
4958       return E_FILE_FORMAT_INVALID;
4959   }
4960 
4961   if (count <= 0)
4962     return -1;
4963 
4964   content_encoding_entries_ =
4965       new (std::nothrow) ContentEncoding*[static_cast<size_t>(count)];
4966   if (!content_encoding_entries_)
4967     return -1;
4968 
4969   content_encoding_entries_end_ = content_encoding_entries_;
4970 
4971   pos = start;
4972   while (pos < stop) {
4973     long long id, size;
4974     long status = ParseElementHeader(pReader, pos, stop, id, size);
4975     if (status < 0)  // error
4976       return status;
4977 
4978     // pos now designates start of element
4979     if (id == libwebm::kMkvContentEncoding) {
4980       ContentEncoding* const content_encoding =
4981           new (std::nothrow) ContentEncoding();
4982       if (!content_encoding)
4983         return -1;
4984 
4985       status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
4986       if (status) {
4987         delete content_encoding;
4988         return status;
4989       }
4990 
4991       *content_encoding_entries_end_++ = content_encoding;
4992     }
4993 
4994     pos += size;  // consume payload
4995     if (pos > stop)
4996       return E_FILE_FORMAT_INVALID;
4997   }
4998 
4999   if (pos != stop)
5000     return E_FILE_FORMAT_INVALID;
5001 
5002   return 0;
5003 }
5004 
EOSBlock()5005 Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
5006 
GetKind() const5007 BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
5008 
GetBlock() const5009 const Block* Track::EOSBlock::GetBlock() const { return NULL; }
5010 
Parse(IMkvReader * reader,long long read_pos,long long value_size,bool is_x,PrimaryChromaticity ** chromaticity)5011 bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos,
5012                                 long long value_size, bool is_x,
5013                                 PrimaryChromaticity** chromaticity) {
5014   if (!reader)
5015     return false;
5016 
5017   if (!*chromaticity)
5018     *chromaticity = new PrimaryChromaticity();
5019 
5020   if (!*chromaticity)
5021     return false;
5022 
5023   PrimaryChromaticity* pc = *chromaticity;
5024   float* value = is_x ? &pc->x : &pc->y;
5025 
5026   double parser_value = 0;
5027   const long long parse_status =
5028       UnserializeFloat(reader, read_pos, value_size, parser_value);
5029 
5030   // Valid range is [0, 1]. Make sure the double is representable as a float
5031   // before casting.
5032   if (parse_status < 0 || parser_value < 0.0 || parser_value > 1.0 ||
5033       (parser_value > 0.0 && parser_value < FLT_MIN))
5034     return false;
5035 
5036   *value = static_cast<float>(parser_value);
5037 
5038   return true;
5039 }
5040 
Parse(IMkvReader * reader,long long mm_start,long long mm_size,MasteringMetadata ** mm)5041 bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
5042                               long long mm_size, MasteringMetadata** mm) {
5043   if (!reader || *mm)
5044     return false;
5045 
5046   std::unique_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
5047   if (!mm_ptr.get())
5048     return false;
5049 
5050   const long long mm_end = mm_start + mm_size;
5051   long long read_pos = mm_start;
5052 
5053   while (read_pos < mm_end) {
5054     long long child_id = 0;
5055     long long child_size = 0;
5056 
5057     const long long status =
5058         ParseElementHeader(reader, read_pos, mm_end, child_id, child_size);
5059     if (status < 0)
5060       return false;
5061 
5062     if (child_id == libwebm::kMkvLuminanceMax) {
5063       double value = 0;
5064       const long long value_parse_status =
5065           UnserializeFloat(reader, read_pos, child_size, value);
5066       if (value < -FLT_MAX || value > FLT_MAX ||
5067           (value > 0.0 && value < FLT_MIN)) {
5068         return false;
5069       }
5070       mm_ptr->luminance_max = static_cast<float>(value);
5071       if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 ||
5072           mm_ptr->luminance_max > 9999.99) {
5073         return false;
5074       }
5075     } else if (child_id == libwebm::kMkvLuminanceMin) {
5076       double value = 0;
5077       const long long value_parse_status =
5078           UnserializeFloat(reader, read_pos, child_size, value);
5079       if (value < -FLT_MAX || value > FLT_MAX ||
5080           (value > 0.0 && value < FLT_MIN)) {
5081         return false;
5082       }
5083       mm_ptr->luminance_min = static_cast<float>(value);
5084       if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 ||
5085           mm_ptr->luminance_min > 999.9999) {
5086         return false;
5087       }
5088     } else {
5089       bool is_x = false;
5090       PrimaryChromaticity** chromaticity;
5091       switch (child_id) {
5092         case libwebm::kMkvPrimaryRChromaticityX:
5093         case libwebm::kMkvPrimaryRChromaticityY:
5094           is_x = child_id == libwebm::kMkvPrimaryRChromaticityX;
5095           chromaticity = &mm_ptr->r;
5096           break;
5097         case libwebm::kMkvPrimaryGChromaticityX:
5098         case libwebm::kMkvPrimaryGChromaticityY:
5099           is_x = child_id == libwebm::kMkvPrimaryGChromaticityX;
5100           chromaticity = &mm_ptr->g;
5101           break;
5102         case libwebm::kMkvPrimaryBChromaticityX:
5103         case libwebm::kMkvPrimaryBChromaticityY:
5104           is_x = child_id == libwebm::kMkvPrimaryBChromaticityX;
5105           chromaticity = &mm_ptr->b;
5106           break;
5107         case libwebm::kMkvWhitePointChromaticityX:
5108         case libwebm::kMkvWhitePointChromaticityY:
5109           is_x = child_id == libwebm::kMkvWhitePointChromaticityX;
5110           chromaticity = &mm_ptr->white_point;
5111           break;
5112         default:
5113           return false;
5114       }
5115       const bool value_parse_status = PrimaryChromaticity::Parse(
5116           reader, read_pos, child_size, is_x, chromaticity);
5117       if (!value_parse_status)
5118         return false;
5119     }
5120 
5121     read_pos += child_size;
5122     if (read_pos > mm_end)
5123       return false;
5124   }
5125 
5126   *mm = mm_ptr.release();
5127   return true;
5128 }
5129 
Parse(IMkvReader * reader,long long colour_start,long long colour_size,Colour ** colour)5130 bool Colour::Parse(IMkvReader* reader, long long colour_start,
5131                    long long colour_size, Colour** colour) {
5132   if (!reader || *colour)
5133     return false;
5134 
5135   std::unique_ptr<Colour> colour_ptr(new Colour());
5136   if (!colour_ptr.get())
5137     return false;
5138 
5139   const long long colour_end = colour_start + colour_size;
5140   long long read_pos = colour_start;
5141 
5142   while (read_pos < colour_end) {
5143     long long child_id = 0;
5144     long long child_size = 0;
5145 
5146     const long status =
5147         ParseElementHeader(reader, read_pos, colour_end, child_id, child_size);
5148     if (status < 0)
5149       return false;
5150 
5151     if (child_id == libwebm::kMkvMatrixCoefficients) {
5152       colour_ptr->matrix_coefficients =
5153           UnserializeUInt(reader, read_pos, child_size);
5154       if (colour_ptr->matrix_coefficients < 0)
5155         return false;
5156     } else if (child_id == libwebm::kMkvBitsPerChannel) {
5157       colour_ptr->bits_per_channel =
5158           UnserializeUInt(reader, read_pos, child_size);
5159       if (colour_ptr->bits_per_channel < 0)
5160         return false;
5161     } else if (child_id == libwebm::kMkvChromaSubsamplingHorz) {
5162       colour_ptr->chroma_subsampling_horz =
5163           UnserializeUInt(reader, read_pos, child_size);
5164       if (colour_ptr->chroma_subsampling_horz < 0)
5165         return false;
5166     } else if (child_id == libwebm::kMkvChromaSubsamplingVert) {
5167       colour_ptr->chroma_subsampling_vert =
5168           UnserializeUInt(reader, read_pos, child_size);
5169       if (colour_ptr->chroma_subsampling_vert < 0)
5170         return false;
5171     } else if (child_id == libwebm::kMkvCbSubsamplingHorz) {
5172       colour_ptr->cb_subsampling_horz =
5173           UnserializeUInt(reader, read_pos, child_size);
5174       if (colour_ptr->cb_subsampling_horz < 0)
5175         return false;
5176     } else if (child_id == libwebm::kMkvCbSubsamplingVert) {
5177       colour_ptr->cb_subsampling_vert =
5178           UnserializeUInt(reader, read_pos, child_size);
5179       if (colour_ptr->cb_subsampling_vert < 0)
5180         return false;
5181     } else if (child_id == libwebm::kMkvChromaSitingHorz) {
5182       colour_ptr->chroma_siting_horz =
5183           UnserializeUInt(reader, read_pos, child_size);
5184       if (colour_ptr->chroma_siting_horz < 0)
5185         return false;
5186     } else if (child_id == libwebm::kMkvChromaSitingVert) {
5187       colour_ptr->chroma_siting_vert =
5188           UnserializeUInt(reader, read_pos, child_size);
5189       if (colour_ptr->chroma_siting_vert < 0)
5190         return false;
5191     } else if (child_id == libwebm::kMkvRange) {
5192       colour_ptr->range = UnserializeUInt(reader, read_pos, child_size);
5193       if (colour_ptr->range < 0)
5194         return false;
5195     } else if (child_id == libwebm::kMkvTransferCharacteristics) {
5196       colour_ptr->transfer_characteristics =
5197           UnserializeUInt(reader, read_pos, child_size);
5198       if (colour_ptr->transfer_characteristics < 0)
5199         return false;
5200     } else if (child_id == libwebm::kMkvPrimaries) {
5201       colour_ptr->primaries = UnserializeUInt(reader, read_pos, child_size);
5202       if (colour_ptr->primaries < 0)
5203         return false;
5204     } else if (child_id == libwebm::kMkvMaxCLL) {
5205       colour_ptr->max_cll = UnserializeUInt(reader, read_pos, child_size);
5206       if (colour_ptr->max_cll < 0)
5207         return false;
5208     } else if (child_id == libwebm::kMkvMaxFALL) {
5209       colour_ptr->max_fall = UnserializeUInt(reader, read_pos, child_size);
5210       if (colour_ptr->max_fall < 0)
5211         return false;
5212     } else if (child_id == libwebm::kMkvMasteringMetadata) {
5213       if (!MasteringMetadata::Parse(reader, read_pos, child_size,
5214                                     &colour_ptr->mastering_metadata))
5215         return false;
5216     } else {
5217       return false;
5218     }
5219 
5220     read_pos += child_size;
5221     if (read_pos > colour_end)
5222       return false;
5223   }
5224   *colour = colour_ptr.release();
5225   return true;
5226 }
5227 
Parse(IMkvReader * reader,long long start,long long size,Projection ** projection)5228 bool Projection::Parse(IMkvReader* reader, long long start, long long size,
5229                        Projection** projection) {
5230   if (!reader || *projection)
5231     return false;
5232 
5233   std::unique_ptr<Projection> projection_ptr(new Projection());
5234   if (!projection_ptr.get())
5235     return false;
5236 
5237   const long long end = start + size;
5238   long long read_pos = start;
5239 
5240   while (read_pos < end) {
5241     long long child_id = 0;
5242     long long child_size = 0;
5243 
5244     const long long status =
5245         ParseElementHeader(reader, read_pos, end, child_id, child_size);
5246     if (status < 0)
5247       return false;
5248 
5249     if (child_id == libwebm::kMkvProjectionType) {
5250       long long projection_type = kTypeNotPresent;
5251       projection_type = UnserializeUInt(reader, read_pos, child_size);
5252       if (projection_type < 0)
5253         return false;
5254 
5255       projection_ptr->type = static_cast<ProjectionType>(projection_type);
5256     } else if (child_id == libwebm::kMkvProjectionPrivate) {
5257       if (projection_ptr->private_data != NULL)
5258         return false;
5259       unsigned char* data = SafeArrayAlloc<unsigned char>(1, child_size);
5260 
5261       if (data == NULL)
5262         return false;
5263 
5264       const int status =
5265           reader->Read(read_pos, static_cast<long>(child_size), data);
5266 
5267       if (status) {
5268         delete[] data;
5269         return false;
5270       }
5271 
5272       projection_ptr->private_data = data;
5273       projection_ptr->private_data_length = static_cast<size_t>(child_size);
5274     } else {
5275       double value = 0;
5276       const long long value_parse_status =
5277           UnserializeFloat(reader, read_pos, child_size, value);
5278       // Make sure value is representable as a float before casting.
5279       if (value_parse_status < 0 || value < -FLT_MAX || value > FLT_MAX ||
5280           (value > 0.0 && value < FLT_MIN)) {
5281         return false;
5282       }
5283 
5284       switch (child_id) {
5285         case libwebm::kMkvProjectionPoseYaw:
5286           projection_ptr->pose_yaw = static_cast<float>(value);
5287           break;
5288         case libwebm::kMkvProjectionPosePitch:
5289           projection_ptr->pose_pitch = static_cast<float>(value);
5290           break;
5291         case libwebm::kMkvProjectionPoseRoll:
5292           projection_ptr->pose_roll = static_cast<float>(value);
5293           break;
5294         default:
5295           return false;
5296       }
5297     }
5298 
5299     read_pos += child_size;
5300     if (read_pos > end)
5301       return false;
5302   }
5303 
5304   *projection = projection_ptr.release();
5305   return true;
5306 }
5307 
VideoTrack(Segment * pSegment,long long element_start,long long element_size)5308 VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
5309                        long long element_size)
5310     : Track(pSegment, element_start, element_size),
5311       m_colour_space(NULL),
5312       m_colour(NULL),
5313       m_projection(NULL) {}
5314 
~VideoTrack()5315 VideoTrack::~VideoTrack() {
5316   delete[] m_colour_space;
5317   delete m_colour;
5318   delete m_projection;
5319 }
5320 
Parse(Segment * pSegment,const Info & info,long long element_start,long long element_size,VideoTrack * & pResult)5321 long VideoTrack::Parse(Segment* pSegment, const Info& info,
5322                        long long element_start, long long element_size,
5323                        VideoTrack*& pResult) {
5324   if (pResult)
5325     return -1;
5326 
5327   if (info.type != Track::kVideo)
5328     return -1;
5329 
5330   long long width = 0;
5331   long long height = 0;
5332   long long display_width = 0;
5333   long long display_height = 0;
5334   long long display_unit = 0;
5335   long long stereo_mode = 0;
5336 
5337   double rate = 0.0;
5338   std::unique_ptr<char[]> colour_space_ptr;
5339 
5340   IMkvReader* const pReader = pSegment->m_pReader;
5341 
5342   const Settings& s = info.settings;
5343   assert(s.start >= 0);
5344   assert(s.size >= 0);
5345 
5346   long long pos = s.start;
5347   assert(pos >= 0);
5348 
5349   const long long stop = pos + s.size;
5350 
5351   std::unique_ptr<Colour> colour_ptr;
5352   std::unique_ptr<Projection> projection_ptr;
5353 
5354   while (pos < stop) {
5355     long long id, size;
5356 
5357     const long status = ParseElementHeader(pReader, pos, stop, id, size);
5358 
5359     if (status < 0)  // error
5360       return status;
5361 
5362     if (id == libwebm::kMkvPixelWidth) {
5363       width = UnserializeUInt(pReader, pos, size);
5364 
5365       if (width <= 0)
5366         return E_FILE_FORMAT_INVALID;
5367     } else if (id == libwebm::kMkvPixelHeight) {
5368       height = UnserializeUInt(pReader, pos, size);
5369 
5370       if (height <= 0)
5371         return E_FILE_FORMAT_INVALID;
5372     } else if (id == libwebm::kMkvDisplayWidth) {
5373       display_width = UnserializeUInt(pReader, pos, size);
5374 
5375       if (display_width <= 0)
5376         return E_FILE_FORMAT_INVALID;
5377     } else if (id == libwebm::kMkvDisplayHeight) {
5378       display_height = UnserializeUInt(pReader, pos, size);
5379 
5380       if (display_height <= 0)
5381         return E_FILE_FORMAT_INVALID;
5382     } else if (id == libwebm::kMkvDisplayUnit) {
5383       display_unit = UnserializeUInt(pReader, pos, size);
5384 
5385       if (display_unit < 0)
5386         return E_FILE_FORMAT_INVALID;
5387     } else if (id == libwebm::kMkvStereoMode) {
5388       stereo_mode = UnserializeUInt(pReader, pos, size);
5389 
5390       if (stereo_mode < 0)
5391         return E_FILE_FORMAT_INVALID;
5392     } else if (id == libwebm::kMkvFrameRate) {
5393       const long status = UnserializeFloat(pReader, pos, size, rate);
5394 
5395       if (status < 0)
5396         return status;
5397 
5398       if (rate <= 0)
5399         return E_FILE_FORMAT_INVALID;
5400     } else if (id == libwebm::kMkvColour) {
5401       Colour* colour = NULL;
5402       if (!Colour::Parse(pReader, pos, size, &colour)) {
5403         return E_FILE_FORMAT_INVALID;
5404       } else {
5405         colour_ptr.reset(colour);
5406       }
5407     } else if (id == libwebm::kMkvProjection) {
5408       Projection* projection = NULL;
5409       if (!Projection::Parse(pReader, pos, size, &projection)) {
5410         return E_FILE_FORMAT_INVALID;
5411       } else {
5412         projection_ptr.reset(projection);
5413       }
5414     } else if (id == libwebm::kMkvColourSpace) {
5415       char* colour_space = NULL;
5416       const long status = UnserializeString(pReader, pos, size, colour_space);
5417       if (status < 0)
5418         return status;
5419       colour_space_ptr.reset(colour_space);
5420     }
5421 
5422     pos += size;  // consume payload
5423     if (pos > stop)
5424       return E_FILE_FORMAT_INVALID;
5425   }
5426 
5427   if (pos != stop)
5428     return E_FILE_FORMAT_INVALID;
5429 
5430   VideoTrack* const pTrack =
5431       new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
5432 
5433   if (pTrack == NULL)
5434     return -1;  // generic error
5435 
5436   const int status = info.Copy(pTrack->m_info);
5437 
5438   if (status) {  // error
5439     delete pTrack;
5440     return status;
5441   }
5442 
5443   pTrack->m_width = width;
5444   pTrack->m_height = height;
5445   pTrack->m_display_width = display_width;
5446   pTrack->m_display_height = display_height;
5447   pTrack->m_display_unit = display_unit;
5448   pTrack->m_stereo_mode = stereo_mode;
5449   pTrack->m_rate = rate;
5450   pTrack->m_colour = colour_ptr.release();
5451   pTrack->m_colour_space = colour_space_ptr.release();
5452   pTrack->m_projection = projection_ptr.release();
5453 
5454   pResult = pTrack;
5455   return 0;  // success
5456 }
5457 
VetEntry(const BlockEntry * pBlockEntry) const5458 bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
5459   return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
5460 }
5461 
Seek(long long time_ns,const BlockEntry * & pResult) const5462 long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
5463   const long status = GetFirst(pResult);
5464 
5465   if (status < 0)  // buffer underflow, etc
5466     return status;
5467 
5468   assert(pResult);
5469 
5470   if (pResult->EOS())
5471     return 0;
5472 
5473   const Cluster* pCluster = pResult->GetCluster();
5474   assert(pCluster);
5475   assert(pCluster->GetIndex() >= 0);
5476 
5477   if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
5478     return 0;
5479 
5480   Cluster** const clusters = m_pSegment->m_clusters;
5481   assert(clusters);
5482 
5483   const long count = m_pSegment->GetCount();  // loaded only, not pre-loaded
5484   assert(count > 0);
5485 
5486   Cluster** const i = clusters + pCluster->GetIndex();
5487   assert(i);
5488   assert(*i == pCluster);
5489   assert(pCluster->GetTime() <= time_ns);
5490 
5491   Cluster** const j = clusters + count;
5492 
5493   Cluster** lo = i;
5494   Cluster** hi = j;
5495 
5496   while (lo < hi) {
5497     // INVARIANT:
5498     //[i, lo) <= time_ns
5499     //[lo, hi) ?
5500     //[hi, j)  > time_ns
5501 
5502     Cluster** const mid = lo + (hi - lo) / 2;
5503     assert(mid < hi);
5504 
5505     pCluster = *mid;
5506     assert(pCluster);
5507     assert(pCluster->GetIndex() >= 0);
5508     assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
5509 
5510     const long long t = pCluster->GetTime();
5511 
5512     if (t <= time_ns)
5513       lo = mid + 1;
5514     else
5515       hi = mid;
5516 
5517     assert(lo <= hi);
5518   }
5519 
5520   assert(lo == hi);
5521   assert(lo > i);
5522   assert(lo <= j);
5523 
5524   pCluster = *--lo;
5525   assert(pCluster);
5526   assert(pCluster->GetTime() <= time_ns);
5527 
5528   pResult = pCluster->GetEntry(this, time_ns);
5529 
5530   if ((pResult != 0) && !pResult->EOS())  // found a keyframe
5531     return 0;
5532 
5533   while (lo != i) {
5534     pCluster = *--lo;
5535     assert(pCluster);
5536     assert(pCluster->GetTime() <= time_ns);
5537 
5538     pResult = pCluster->GetEntry(this, time_ns);
5539 
5540     if ((pResult != 0) && !pResult->EOS())
5541       return 0;
5542   }
5543 
5544   // weird: we're on the first cluster, but no keyframe found
5545   // should never happen but we must return something anyway
5546 
5547   pResult = GetEOS();
5548   return 0;
5549 }
5550 
GetColour() const5551 Colour* VideoTrack::GetColour() const { return m_colour; }
5552 
GetProjection() const5553 Projection* VideoTrack::GetProjection() const { return m_projection; }
5554 
GetWidth() const5555 long long VideoTrack::GetWidth() const { return m_width; }
5556 
GetHeight() const5557 long long VideoTrack::GetHeight() const { return m_height; }
5558 
GetDisplayWidth() const5559 long long VideoTrack::GetDisplayWidth() const {
5560   return m_display_width > 0 ? m_display_width : GetWidth();
5561 }
5562 
GetDisplayHeight() const5563 long long VideoTrack::GetDisplayHeight() const {
5564   return m_display_height > 0 ? m_display_height : GetHeight();
5565 }
5566 
GetDisplayUnit() const5567 long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
5568 
GetStereoMode() const5569 long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
5570 
GetFrameRate() const5571 double VideoTrack::GetFrameRate() const { return m_rate; }
5572 
AudioTrack(Segment * pSegment,long long element_start,long long element_size)5573 AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
5574                        long long element_size)
5575     : Track(pSegment, element_start, element_size) {}
5576 
Parse(Segment * pSegment,const Info & info,long long element_start,long long element_size,AudioTrack * & pResult)5577 long AudioTrack::Parse(Segment* pSegment, const Info& info,
5578                        long long element_start, long long element_size,
5579                        AudioTrack*& pResult) {
5580   if (pResult)
5581     return -1;
5582 
5583   if (info.type != Track::kAudio)
5584     return -1;
5585 
5586   IMkvReader* const pReader = pSegment->m_pReader;
5587 
5588   const Settings& s = info.settings;
5589   assert(s.start >= 0);
5590   assert(s.size >= 0);
5591 
5592   long long pos = s.start;
5593   assert(pos >= 0);
5594 
5595   const long long stop = pos + s.size;
5596 
5597   double rate = 8000.0;  // MKV default
5598   long long channels = 1;
5599   long long bit_depth = 0;
5600 
5601   while (pos < stop) {
5602     long long id, size;
5603 
5604     long status = ParseElementHeader(pReader, pos, stop, id, size);
5605 
5606     if (status < 0)  // error
5607       return status;
5608 
5609     if (id == libwebm::kMkvSamplingFrequency) {
5610       status = UnserializeFloat(pReader, pos, size, rate);
5611 
5612       if (status < 0)
5613         return status;
5614 
5615       if (rate <= 0)
5616         return E_FILE_FORMAT_INVALID;
5617     } else if (id == libwebm::kMkvChannels) {
5618       channels = UnserializeUInt(pReader, pos, size);
5619 
5620       if (channels <= 0)
5621         return E_FILE_FORMAT_INVALID;
5622     } else if (id == libwebm::kMkvBitDepth) {
5623       bit_depth = UnserializeUInt(pReader, pos, size);
5624 
5625       if (bit_depth <= 0)
5626         return E_FILE_FORMAT_INVALID;
5627     }
5628 
5629     pos += size;  // consume payload
5630     if (pos > stop)
5631       return E_FILE_FORMAT_INVALID;
5632   }
5633 
5634   if (pos != stop)
5635     return E_FILE_FORMAT_INVALID;
5636 
5637   AudioTrack* const pTrack =
5638       new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
5639 
5640   if (pTrack == NULL)
5641     return -1;  // generic error
5642 
5643   const int status = info.Copy(pTrack->m_info);
5644 
5645   if (status) {
5646     delete pTrack;
5647     return status;
5648   }
5649 
5650   pTrack->m_rate = rate;
5651   pTrack->m_channels = channels;
5652   pTrack->m_bitDepth = bit_depth;
5653 
5654   pResult = pTrack;
5655   return 0;  // success
5656 }
5657 
GetSamplingRate() const5658 double AudioTrack::GetSamplingRate() const { return m_rate; }
5659 
GetChannels() const5660 long long AudioTrack::GetChannels() const { return m_channels; }
5661 
GetBitDepth() const5662 long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
5663 
Tracks(Segment * pSegment,long long start,long long size_,long long element_start,long long element_size)5664 Tracks::Tracks(Segment* pSegment, long long start, long long size_,
5665                long long element_start, long long element_size)
5666     : m_pSegment(pSegment),
5667       m_start(start),
5668       m_size(size_),
5669       m_element_start(element_start),
5670       m_element_size(element_size),
5671       m_trackEntries(NULL),
5672       m_trackEntriesEnd(NULL) {}
5673 
Parse()5674 long Tracks::Parse() {
5675   assert(m_trackEntries == NULL);
5676   assert(m_trackEntriesEnd == NULL);
5677 
5678   const long long stop = m_start + m_size;
5679   IMkvReader* const pReader = m_pSegment->m_pReader;
5680 
5681   long long count = 0;
5682   long long pos = m_start;
5683 
5684   while (pos < stop) {
5685     long long id, size;
5686 
5687     const long status = ParseElementHeader(pReader, pos, stop, id, size);
5688 
5689     if (status < 0)  // error
5690       return status;
5691 
5692     if (size == 0)  // weird
5693       continue;
5694 
5695     if (id == libwebm::kMkvTrackEntry) {
5696       ++count;
5697       if (count > INT_MAX)
5698         return E_PARSE_FAILED;
5699     }
5700 
5701     pos += size;  // consume payload
5702     if (pos > stop)
5703       return E_FILE_FORMAT_INVALID;
5704   }
5705 
5706   if (pos != stop)
5707     return E_FILE_FORMAT_INVALID;
5708 
5709   if (count <= 0)
5710     return 0;  // success
5711 
5712   m_trackEntries = new (std::nothrow) Track*[static_cast<size_t>(count)];
5713 
5714   if (m_trackEntries == NULL)
5715     return -1;
5716 
5717   m_trackEntriesEnd = m_trackEntries;
5718 
5719   pos = m_start;
5720 
5721   while (pos < stop) {
5722     const long long element_start = pos;
5723 
5724     long long id, payload_size;
5725 
5726     const long status =
5727         ParseElementHeader(pReader, pos, stop, id, payload_size);
5728 
5729     if (status < 0)  // error
5730       return status;
5731 
5732     if (payload_size == 0)  // weird
5733       continue;
5734 
5735     const long long payload_stop = pos + payload_size;
5736     assert(payload_stop <= stop);  // checked in ParseElement
5737 
5738     const long long element_size = payload_stop - element_start;
5739 
5740     if (id == libwebm::kMkvTrackEntry) {
5741       Track*& pTrack = *m_trackEntriesEnd;
5742       pTrack = NULL;
5743 
5744       const long status = ParseTrackEntry(pos, payload_size, element_start,
5745                                           element_size, pTrack);
5746       if (status)
5747         return status;
5748 
5749       if (pTrack)
5750         ++m_trackEntriesEnd;
5751     }
5752 
5753     pos = payload_stop;
5754     if (pos > stop)
5755       return E_FILE_FORMAT_INVALID;
5756   }
5757 
5758   if (pos != stop)
5759     return E_FILE_FORMAT_INVALID;
5760 
5761   return 0;  // success
5762 }
5763 
GetTracksCount() const5764 unsigned long Tracks::GetTracksCount() const {
5765   const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
5766   assert(result >= 0);
5767 
5768   return static_cast<unsigned long>(result);
5769 }
5770 
ParseTrackEntry(long long track_start,long long track_size,long long element_start,long long element_size,Track * & pResult) const5771 long Tracks::ParseTrackEntry(long long track_start, long long track_size,
5772                              long long element_start, long long element_size,
5773                              Track*& pResult) const {
5774   if (pResult)
5775     return -1;
5776 
5777   IMkvReader* const pReader = m_pSegment->m_pReader;
5778 
5779   long long pos = track_start;
5780   const long long track_stop = track_start + track_size;
5781 
5782   Track::Info info;
5783 
5784   info.type = 0;
5785   info.number = 0;
5786   info.uid = 0;
5787   info.defaultDuration = 0;
5788 
5789   Track::Settings v;
5790   v.start = -1;
5791   v.size = -1;
5792 
5793   Track::Settings a;
5794   a.start = -1;
5795   a.size = -1;
5796 
5797   Track::Settings e;  // content_encodings_settings;
5798   e.start = -1;
5799   e.size = -1;
5800 
5801   long long lacing = 1;  // default is true
5802 
5803   while (pos < track_stop) {
5804     long long id, size;
5805 
5806     const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
5807 
5808     if (status < 0)  // error
5809       return status;
5810 
5811     if (size < 0)
5812       return E_FILE_FORMAT_INVALID;
5813 
5814     const long long start = pos;
5815 
5816     if (id == libwebm::kMkvVideo) {
5817       v.start = start;
5818       v.size = size;
5819     } else if (id == libwebm::kMkvAudio) {
5820       a.start = start;
5821       a.size = size;
5822     } else if (id == libwebm::kMkvContentEncodings) {
5823       e.start = start;
5824       e.size = size;
5825     } else if (id == libwebm::kMkvTrackUID) {
5826       if (size > 8)
5827         return E_FILE_FORMAT_INVALID;
5828 
5829       info.uid = 0;
5830 
5831       long long pos_ = start;
5832       const long long pos_end = start + size;
5833 
5834       while (pos_ != pos_end) {
5835         unsigned char b;
5836 
5837         const int status = pReader->Read(pos_, 1, &b);
5838 
5839         if (status)
5840           return status;
5841 
5842         info.uid <<= 8;
5843         info.uid |= b;
5844 
5845         ++pos_;
5846       }
5847     } else if (id == libwebm::kMkvTrackNumber) {
5848       const long long num = UnserializeUInt(pReader, pos, size);
5849 
5850       if ((num <= 0) || (num > 127))
5851         return E_FILE_FORMAT_INVALID;
5852 
5853       info.number = static_cast<long>(num);
5854     } else if (id == libwebm::kMkvTrackType) {
5855       const long long type = UnserializeUInt(pReader, pos, size);
5856 
5857       if ((type <= 0) || (type > 254))
5858         return E_FILE_FORMAT_INVALID;
5859 
5860       info.type = static_cast<long>(type);
5861     } else if (id == libwebm::kMkvName) {
5862       const long status =
5863           UnserializeString(pReader, pos, size, info.nameAsUTF8);
5864 
5865       if (status)
5866         return status;
5867     } else if (id == libwebm::kMkvLanguage) {
5868       const long status = UnserializeString(pReader, pos, size, info.language);
5869 
5870       if (status)
5871         return status;
5872     } else if (id == libwebm::kMkvDefaultDuration) {
5873       const long long duration = UnserializeUInt(pReader, pos, size);
5874 
5875       if (duration < 0)
5876         return E_FILE_FORMAT_INVALID;
5877 
5878       info.defaultDuration = static_cast<unsigned long long>(duration);
5879     } else if (id == libwebm::kMkvCodecID) {
5880       const long status = UnserializeString(pReader, pos, size, info.codecId);
5881 
5882       if (status)
5883         return status;
5884     } else if (id == libwebm::kMkvFlagLacing) {
5885       lacing = UnserializeUInt(pReader, pos, size);
5886 
5887       if ((lacing < 0) || (lacing > 1))
5888         return E_FILE_FORMAT_INVALID;
5889     } else if (id == libwebm::kMkvCodecPrivate) {
5890       delete[] info.codecPrivate;
5891       info.codecPrivate = NULL;
5892       info.codecPrivateSize = 0;
5893 
5894       const size_t buflen = static_cast<size_t>(size);
5895 
5896       if (buflen) {
5897         unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
5898 
5899         if (buf == NULL)
5900           return -1;
5901 
5902         const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
5903 
5904         if (status) {
5905           delete[] buf;
5906           return status;
5907         }
5908 
5909         info.codecPrivate = buf;
5910         info.codecPrivateSize = buflen;
5911       }
5912     } else if (id == libwebm::kMkvCodecName) {
5913       const long status =
5914           UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
5915 
5916       if (status)
5917         return status;
5918     } else if (id == libwebm::kMkvCodecDelay) {
5919       info.codecDelay = UnserializeUInt(pReader, pos, size);
5920     } else if (id == libwebm::kMkvSeekPreRoll) {
5921       info.seekPreRoll = UnserializeUInt(pReader, pos, size);
5922     }
5923 
5924     pos += size;  // consume payload
5925     if (pos > track_stop)
5926       return E_FILE_FORMAT_INVALID;
5927   }
5928 
5929   if (pos != track_stop)
5930     return E_FILE_FORMAT_INVALID;
5931 
5932   if (info.number <= 0)  // not specified
5933     return E_FILE_FORMAT_INVALID;
5934 
5935   if (GetTrackByNumber(info.number))
5936     return E_FILE_FORMAT_INVALID;
5937 
5938   if (info.type <= 0)  // not specified
5939     return E_FILE_FORMAT_INVALID;
5940 
5941   info.lacing = (lacing > 0) ? true : false;
5942 
5943   if (info.type == Track::kVideo) {
5944     if (v.start < 0)
5945       return E_FILE_FORMAT_INVALID;
5946 
5947     if (a.start >= 0)
5948       return E_FILE_FORMAT_INVALID;
5949 
5950     info.settings = v;
5951 
5952     VideoTrack* pTrack = NULL;
5953 
5954     const long status = VideoTrack::Parse(m_pSegment, info, element_start,
5955                                           element_size, pTrack);
5956 
5957     if (status)
5958       return status;
5959 
5960     pResult = pTrack;
5961     assert(pResult);
5962 
5963     if (e.start >= 0)
5964       pResult->ParseContentEncodingsEntry(e.start, e.size);
5965   } else if (info.type == Track::kAudio) {
5966     if (a.start < 0)
5967       return E_FILE_FORMAT_INVALID;
5968 
5969     if (v.start >= 0)
5970       return E_FILE_FORMAT_INVALID;
5971 
5972     info.settings = a;
5973 
5974     AudioTrack* pTrack = NULL;
5975 
5976     const long status = AudioTrack::Parse(m_pSegment, info, element_start,
5977                                           element_size, pTrack);
5978 
5979     if (status)
5980       return status;
5981 
5982     pResult = pTrack;
5983     assert(pResult);
5984 
5985     if (e.start >= 0)
5986       pResult->ParseContentEncodingsEntry(e.start, e.size);
5987   } else {
5988     // neither video nor audio - probably metadata or subtitles
5989 
5990     if (a.start >= 0)
5991       return E_FILE_FORMAT_INVALID;
5992 
5993     if (v.start >= 0)
5994       return E_FILE_FORMAT_INVALID;
5995 
5996     if (info.type == Track::kMetadata && e.start >= 0)
5997       return E_FILE_FORMAT_INVALID;
5998 
5999     info.settings.start = -1;
6000     info.settings.size = 0;
6001 
6002     Track* pTrack = NULL;
6003 
6004     const long status =
6005         Track::Create(m_pSegment, info, element_start, element_size, pTrack);
6006 
6007     if (status)
6008       return status;
6009 
6010     pResult = pTrack;
6011     assert(pResult);
6012   }
6013 
6014   return 0;  // success
6015 }
6016 
~Tracks()6017 Tracks::~Tracks() {
6018   Track** i = m_trackEntries;
6019   Track** const j = m_trackEntriesEnd;
6020 
6021   while (i != j) {
6022     Track* const pTrack = *i++;
6023     delete pTrack;
6024   }
6025 
6026   delete[] m_trackEntries;
6027 }
6028 
GetTrackByNumber(long tn) const6029 const Track* Tracks::GetTrackByNumber(long tn) const {
6030   if (tn < 0)
6031     return NULL;
6032 
6033   Track** i = m_trackEntries;
6034   Track** const j = m_trackEntriesEnd;
6035 
6036   while (i != j) {
6037     Track* const pTrack = *i++;
6038 
6039     if (pTrack == NULL)
6040       continue;
6041 
6042     if (tn == pTrack->GetNumber())
6043       return pTrack;
6044   }
6045 
6046   return NULL;  // not found
6047 }
6048 
GetTrackByIndex(unsigned long idx) const6049 const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
6050   const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
6051 
6052   if (idx >= static_cast<unsigned long>(count))
6053     return NULL;
6054 
6055   return m_trackEntries[idx];
6056 }
6057 
Load(long long & pos,long & len) const6058 long Cluster::Load(long long& pos, long& len) const {
6059   if (m_pSegment == NULL)
6060     return E_PARSE_FAILED;
6061 
6062   if (m_timecode >= 0)  // at least partially loaded
6063     return 0;
6064 
6065   if (m_pos != m_element_start || m_element_size >= 0)
6066     return E_PARSE_FAILED;
6067 
6068   IMkvReader* const pReader = m_pSegment->m_pReader;
6069   long long total, avail;
6070   const int status = pReader->Length(&total, &avail);
6071 
6072   if (status < 0)  // error
6073     return status;
6074 
6075   if (total >= 0 && (avail > total || m_pos > total))
6076     return E_FILE_FORMAT_INVALID;
6077 
6078   pos = m_pos;
6079 
6080   long long cluster_size = -1;
6081 
6082   if ((pos + 1) > avail) {
6083     len = 1;
6084     return E_BUFFER_NOT_FULL;
6085   }
6086 
6087   long long result = GetUIntLength(pReader, pos, len);
6088 
6089   if (result < 0)  // error or underflow
6090     return static_cast<long>(result);
6091 
6092   if (result > 0)
6093     return E_BUFFER_NOT_FULL;
6094 
6095   if ((pos + len) > avail)
6096     return E_BUFFER_NOT_FULL;
6097 
6098   const long long id_ = ReadID(pReader, pos, len);
6099 
6100   if (id_ < 0)  // error
6101     return static_cast<long>(id_);
6102 
6103   if (id_ != libwebm::kMkvCluster)
6104     return E_FILE_FORMAT_INVALID;
6105 
6106   pos += len;  // consume id
6107 
6108   // read cluster size
6109 
6110   if ((pos + 1) > avail) {
6111     len = 1;
6112     return E_BUFFER_NOT_FULL;
6113   }
6114 
6115   result = GetUIntLength(pReader, pos, len);
6116 
6117   if (result < 0)  // error
6118     return static_cast<long>(result);
6119 
6120   if (result > 0)
6121     return E_BUFFER_NOT_FULL;
6122 
6123   if ((pos + len) > avail)
6124     return E_BUFFER_NOT_FULL;
6125 
6126   const long long size = ReadUInt(pReader, pos, len);
6127 
6128   if (size < 0)  // error
6129     return static_cast<long>(cluster_size);
6130 
6131   if (size == 0)
6132     return E_FILE_FORMAT_INVALID;
6133 
6134   pos += len;  // consume length of size of element
6135 
6136   const long long unknown_size = (1LL << (7 * len)) - 1;
6137 
6138   if (size != unknown_size)
6139     cluster_size = size;
6140 
6141   // pos points to start of payload
6142   long long timecode = -1;
6143   long long new_pos = -1;
6144   bool bBlock = false;
6145 
6146   long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
6147 
6148   for (;;) {
6149     if ((cluster_stop >= 0) && (pos >= cluster_stop))
6150       break;
6151 
6152     // Parse ID
6153 
6154     if ((pos + 1) > avail) {
6155       len = 1;
6156       return E_BUFFER_NOT_FULL;
6157     }
6158 
6159     long long result = GetUIntLength(pReader, pos, len);
6160 
6161     if (result < 0)  // error
6162       return static_cast<long>(result);
6163 
6164     if (result > 0)
6165       return E_BUFFER_NOT_FULL;
6166 
6167     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6168       return E_FILE_FORMAT_INVALID;
6169 
6170     if ((pos + len) > avail)
6171       return E_BUFFER_NOT_FULL;
6172 
6173     const long long id = ReadID(pReader, pos, len);
6174 
6175     if (id < 0)  // error
6176       return static_cast<long>(id);
6177 
6178     if (id == 0)
6179       return E_FILE_FORMAT_INVALID;
6180 
6181     // This is the distinguished set of ID's we use to determine
6182     // that we have exhausted the sub-element's inside the cluster
6183     // whose ID we parsed earlier.
6184 
6185     if (id == libwebm::kMkvCluster)
6186       break;
6187 
6188     if (id == libwebm::kMkvCues)
6189       break;
6190 
6191     pos += len;  // consume ID field
6192 
6193     // Parse Size
6194 
6195     if ((pos + 1) > avail) {
6196       len = 1;
6197       return E_BUFFER_NOT_FULL;
6198     }
6199 
6200     result = GetUIntLength(pReader, pos, len);
6201 
6202     if (result < 0)  // error
6203       return static_cast<long>(result);
6204 
6205     if (result > 0)
6206       return E_BUFFER_NOT_FULL;
6207 
6208     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6209       return E_FILE_FORMAT_INVALID;
6210 
6211     if ((pos + len) > avail)
6212       return E_BUFFER_NOT_FULL;
6213 
6214     const long long size = ReadUInt(pReader, pos, len);
6215 
6216     if (size < 0)  // error
6217       return static_cast<long>(size);
6218 
6219     const long long unknown_size = (1LL << (7 * len)) - 1;
6220 
6221     if (size == unknown_size)
6222       return E_FILE_FORMAT_INVALID;
6223 
6224     pos += len;  // consume size field
6225 
6226     if ((cluster_stop >= 0) && (pos > cluster_stop))
6227       return E_FILE_FORMAT_INVALID;
6228 
6229     // pos now points to start of payload
6230 
6231     if (size == 0)
6232       continue;
6233 
6234     if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6235       return E_FILE_FORMAT_INVALID;
6236 
6237     if (id == libwebm::kMkvTimecode) {
6238       len = static_cast<long>(size);
6239 
6240       if ((pos + size) > avail)
6241         return E_BUFFER_NOT_FULL;
6242 
6243       timecode = UnserializeUInt(pReader, pos, size);
6244 
6245       if (timecode < 0)  // error (or underflow)
6246         return static_cast<long>(timecode);
6247 
6248       new_pos = pos + size;
6249 
6250       if (bBlock)
6251         break;
6252     } else if (id == libwebm::kMkvBlockGroup) {
6253       bBlock = true;
6254       break;
6255     } else if (id == libwebm::kMkvSimpleBlock) {
6256       bBlock = true;
6257       break;
6258     }
6259 
6260     pos += size;  // consume payload
6261     if (cluster_stop >= 0 && pos > cluster_stop)
6262       return E_FILE_FORMAT_INVALID;
6263   }
6264 
6265   if (cluster_stop >= 0 && pos > cluster_stop)
6266     return E_FILE_FORMAT_INVALID;
6267 
6268   if (timecode < 0)  // no timecode found
6269     return E_FILE_FORMAT_INVALID;
6270 
6271   if (!bBlock)
6272     return E_FILE_FORMAT_INVALID;
6273 
6274   m_pos = new_pos;  // designates position just beyond timecode payload
6275   m_timecode = timecode;  // m_timecode >= 0 means we're partially loaded
6276 
6277   if (cluster_size >= 0)
6278     m_element_size = cluster_stop - m_element_start;
6279 
6280   return 0;
6281 }
6282 
Parse(long long & pos,long & len) const6283 long Cluster::Parse(long long& pos, long& len) const {
6284   long status = Load(pos, len);
6285 
6286   if (status < 0)
6287     return status;
6288 
6289   if (m_pos < m_element_start || m_timecode < 0)
6290     return E_PARSE_FAILED;
6291 
6292   const long long cluster_stop =
6293       (m_element_size < 0) ? -1 : m_element_start + m_element_size;
6294 
6295   if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
6296     return 1;  // nothing else to do
6297 
6298   IMkvReader* const pReader = m_pSegment->m_pReader;
6299 
6300   long long total, avail;
6301 
6302   status = pReader->Length(&total, &avail);
6303 
6304   if (status < 0)  // error
6305     return status;
6306 
6307   if (total >= 0 && avail > total)
6308     return E_FILE_FORMAT_INVALID;
6309 
6310   pos = m_pos;
6311 
6312   for (;;) {
6313     if ((cluster_stop >= 0) && (pos >= cluster_stop))
6314       break;
6315 
6316     if ((total >= 0) && (pos >= total)) {
6317       if (m_element_size < 0)
6318         m_element_size = pos - m_element_start;
6319 
6320       break;
6321     }
6322 
6323     // Parse ID
6324 
6325     if ((pos + 1) > avail) {
6326       len = 1;
6327       return E_BUFFER_NOT_FULL;
6328     }
6329 
6330     long long result = GetUIntLength(pReader, pos, len);
6331 
6332     if (result < 0)  // error
6333       return static_cast<long>(result);
6334 
6335     if (result > 0)
6336       return E_BUFFER_NOT_FULL;
6337 
6338     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6339       return E_FILE_FORMAT_INVALID;
6340 
6341     if ((pos + len) > avail)
6342       return E_BUFFER_NOT_FULL;
6343 
6344     const long long id = ReadID(pReader, pos, len);
6345 
6346     if (id < 0)
6347       return E_FILE_FORMAT_INVALID;
6348 
6349     // This is the distinguished set of ID's we use to determine
6350     // that we have exhausted the sub-element's inside the cluster
6351     // whose ID we parsed earlier.
6352 
6353     if ((id == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) {
6354       if (m_element_size < 0)
6355         m_element_size = pos - m_element_start;
6356 
6357       break;
6358     }
6359 
6360     pos += len;  // consume ID field
6361 
6362     // Parse Size
6363 
6364     if ((pos + 1) > avail) {
6365       len = 1;
6366       return E_BUFFER_NOT_FULL;
6367     }
6368 
6369     result = GetUIntLength(pReader, pos, len);
6370 
6371     if (result < 0)  // error
6372       return static_cast<long>(result);
6373 
6374     if (result > 0)
6375       return E_BUFFER_NOT_FULL;
6376 
6377     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6378       return E_FILE_FORMAT_INVALID;
6379 
6380     if ((pos + len) > avail)
6381       return E_BUFFER_NOT_FULL;
6382 
6383     const long long size = ReadUInt(pReader, pos, len);
6384 
6385     if (size < 0)  // error
6386       return static_cast<long>(size);
6387 
6388     const long long unknown_size = (1LL << (7 * len)) - 1;
6389 
6390     if (size == unknown_size)
6391       return E_FILE_FORMAT_INVALID;
6392 
6393     pos += len;  // consume size field
6394 
6395     if ((cluster_stop >= 0) && (pos > cluster_stop))
6396       return E_FILE_FORMAT_INVALID;
6397 
6398     // pos now points to start of payload
6399 
6400     if (size == 0)
6401       continue;
6402 
6403     // const long long block_start = pos;
6404     const long long block_stop = pos + size;
6405 
6406     if (cluster_stop >= 0) {
6407       if (block_stop > cluster_stop) {
6408         if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) {
6409           return E_FILE_FORMAT_INVALID;
6410         }
6411 
6412         pos = cluster_stop;
6413         break;
6414       }
6415     } else if ((total >= 0) && (block_stop > total)) {
6416       m_element_size = total - m_element_start;
6417       pos = total;
6418       break;
6419     } else if (block_stop > avail) {
6420       len = static_cast<long>(size);
6421       return E_BUFFER_NOT_FULL;
6422     }
6423 
6424     Cluster* const this_ = const_cast<Cluster*>(this);
6425 
6426     if (id == libwebm::kMkvBlockGroup)
6427       return this_->ParseBlockGroup(size, pos, len);
6428 
6429     if (id == libwebm::kMkvSimpleBlock)
6430       return this_->ParseSimpleBlock(size, pos, len);
6431 
6432     pos += size;  // consume payload
6433     if (cluster_stop >= 0 && pos > cluster_stop)
6434       return E_FILE_FORMAT_INVALID;
6435   }
6436 
6437   if (m_element_size < 1)
6438     return E_FILE_FORMAT_INVALID;
6439 
6440   m_pos = pos;
6441   if (cluster_stop >= 0 && m_pos > cluster_stop)
6442     return E_FILE_FORMAT_INVALID;
6443 
6444   if (m_entries_count > 0) {
6445     const long idx = m_entries_count - 1;
6446 
6447     const BlockEntry* const pLast = m_entries[idx];
6448     if (pLast == NULL)
6449       return E_PARSE_FAILED;
6450 
6451     const Block* const pBlock = pLast->GetBlock();
6452     if (pBlock == NULL)
6453       return E_PARSE_FAILED;
6454 
6455     const long long start = pBlock->m_start;
6456 
6457     if ((total >= 0) && (start > total))
6458       return E_PARSE_FAILED;  // defend against trucated stream
6459 
6460     const long long size = pBlock->m_size;
6461 
6462     const long long stop = start + size;
6463     if (cluster_stop >= 0 && stop > cluster_stop)
6464       return E_FILE_FORMAT_INVALID;
6465 
6466     if ((total >= 0) && (stop > total))
6467       return E_PARSE_FAILED;  // defend against trucated stream
6468   }
6469 
6470   return 1;  // no more entries
6471 }
6472 
ParseSimpleBlock(long long block_size,long long & pos,long & len)6473 long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
6474                                long& len) {
6475   const long long block_start = pos;
6476   const long long block_stop = pos + block_size;
6477 
6478   IMkvReader* const pReader = m_pSegment->m_pReader;
6479 
6480   long long total, avail;
6481 
6482   long status = pReader->Length(&total, &avail);
6483 
6484   if (status < 0)  // error
6485     return status;
6486 
6487   assert((total < 0) || (avail <= total));
6488 
6489   // parse track number
6490 
6491   if ((pos + 1) > avail) {
6492     len = 1;
6493     return E_BUFFER_NOT_FULL;
6494   }
6495 
6496   long long result = GetUIntLength(pReader, pos, len);
6497 
6498   if (result < 0)  // error
6499     return static_cast<long>(result);
6500 
6501   if (result > 0)  // weird
6502     return E_BUFFER_NOT_FULL;
6503 
6504   if ((pos + len) > block_stop)
6505     return E_FILE_FORMAT_INVALID;
6506 
6507   if ((pos + len) > avail)
6508     return E_BUFFER_NOT_FULL;
6509 
6510   const long long track = ReadUInt(pReader, pos, len);
6511 
6512   if (track < 0)  // error
6513     return static_cast<long>(track);
6514 
6515   if (track == 0)
6516     return E_FILE_FORMAT_INVALID;
6517 
6518   pos += len;  // consume track number
6519 
6520   if ((pos + 2) > block_stop)
6521     return E_FILE_FORMAT_INVALID;
6522 
6523   if ((pos + 2) > avail) {
6524     len = 2;
6525     return E_BUFFER_NOT_FULL;
6526   }
6527 
6528   pos += 2;  // consume timecode
6529 
6530   if ((pos + 1) > block_stop)
6531     return E_FILE_FORMAT_INVALID;
6532 
6533   if ((pos + 1) > avail) {
6534     len = 1;
6535     return E_BUFFER_NOT_FULL;
6536   }
6537 
6538   unsigned char flags;
6539 
6540   status = pReader->Read(pos, 1, &flags);
6541 
6542   if (status < 0) {  // error or underflow
6543     len = 1;
6544     return status;
6545   }
6546 
6547   ++pos;  // consume flags byte
6548   assert(pos <= avail);
6549 
6550   if (pos >= block_stop)
6551     return E_FILE_FORMAT_INVALID;
6552 
6553   const int lacing = int(flags & 0x06) >> 1;
6554 
6555   if ((lacing != 0) && (block_stop > avail)) {
6556     len = static_cast<long>(block_stop - pos);
6557     return E_BUFFER_NOT_FULL;
6558   }
6559 
6560   status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size,
6561                        0);  // DiscardPadding
6562 
6563   if (status != 0)
6564     return status;
6565 
6566   m_pos = block_stop;
6567 
6568   return 0;  // success
6569 }
6570 
ParseBlockGroup(long long payload_size,long long & pos,long & len)6571 long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
6572                               long& len) {
6573   const long long payload_start = pos;
6574   const long long payload_stop = pos + payload_size;
6575 
6576   IMkvReader* const pReader = m_pSegment->m_pReader;
6577 
6578   long long total, avail;
6579 
6580   long status = pReader->Length(&total, &avail);
6581 
6582   if (status < 0)  // error
6583     return status;
6584 
6585   assert((total < 0) || (avail <= total));
6586 
6587   if ((total >= 0) && (payload_stop > total))
6588     return E_FILE_FORMAT_INVALID;
6589 
6590   if (payload_stop > avail) {
6591     len = static_cast<long>(payload_size);
6592     return E_BUFFER_NOT_FULL;
6593   }
6594 
6595   long long discard_padding = 0;
6596 
6597   while (pos < payload_stop) {
6598     // parse sub-block element ID
6599 
6600     if ((pos + 1) > avail) {
6601       len = 1;
6602       return E_BUFFER_NOT_FULL;
6603     }
6604 
6605     long long result = GetUIntLength(pReader, pos, len);
6606 
6607     if (result < 0)  // error
6608       return static_cast<long>(result);
6609 
6610     if (result > 0)  // weird
6611       return E_BUFFER_NOT_FULL;
6612 
6613     if ((pos + len) > payload_stop)
6614       return E_FILE_FORMAT_INVALID;
6615 
6616     if ((pos + len) > avail)
6617       return E_BUFFER_NOT_FULL;
6618 
6619     const long long id = ReadID(pReader, pos, len);
6620 
6621     if (id < 0)  // error
6622       return static_cast<long>(id);
6623 
6624     if (id == 0)  // not a valid ID
6625       return E_FILE_FORMAT_INVALID;
6626 
6627     pos += len;  // consume ID field
6628 
6629     // Parse Size
6630 
6631     if ((pos + 1) > avail) {
6632       len = 1;
6633       return E_BUFFER_NOT_FULL;
6634     }
6635 
6636     result = GetUIntLength(pReader, pos, len);
6637 
6638     if (result < 0)  // error
6639       return static_cast<long>(result);
6640 
6641     if (result > 0)  // weird
6642       return E_BUFFER_NOT_FULL;
6643 
6644     if ((pos + len) > payload_stop)
6645       return E_FILE_FORMAT_INVALID;
6646 
6647     if ((pos + len) > avail)
6648       return E_BUFFER_NOT_FULL;
6649 
6650     const long long size = ReadUInt(pReader, pos, len);
6651 
6652     if (size < 0)  // error
6653       return static_cast<long>(size);
6654 
6655     pos += len;  // consume size field
6656 
6657     // pos now points to start of sub-block group payload
6658 
6659     if (pos > payload_stop)
6660       return E_FILE_FORMAT_INVALID;
6661 
6662     if (size == 0)  // weird
6663       continue;
6664 
6665     const long long unknown_size = (1LL << (7 * len)) - 1;
6666 
6667     if (size == unknown_size)
6668       return E_FILE_FORMAT_INVALID;
6669 
6670     if (id == libwebm::kMkvDiscardPadding) {
6671       status = UnserializeInt(pReader, pos, size, discard_padding);
6672 
6673       if (status < 0)  // error
6674         return status;
6675     }
6676 
6677     if (id != libwebm::kMkvBlock) {
6678       pos += size;  // consume sub-part of block group
6679 
6680       if (pos > payload_stop)
6681         return E_FILE_FORMAT_INVALID;
6682 
6683       continue;
6684     }
6685 
6686     const long long block_stop = pos + size;
6687 
6688     if (block_stop > payload_stop)
6689       return E_FILE_FORMAT_INVALID;
6690 
6691     // parse track number
6692 
6693     if ((pos + 1) > avail) {
6694       len = 1;
6695       return E_BUFFER_NOT_FULL;
6696     }
6697 
6698     result = GetUIntLength(pReader, pos, len);
6699 
6700     if (result < 0)  // error
6701       return static_cast<long>(result);
6702 
6703     if (result > 0)  // weird
6704       return E_BUFFER_NOT_FULL;
6705 
6706     if ((pos + len) > block_stop)
6707       return E_FILE_FORMAT_INVALID;
6708 
6709     if ((pos + len) > avail)
6710       return E_BUFFER_NOT_FULL;
6711 
6712     const long long track = ReadUInt(pReader, pos, len);
6713 
6714     if (track < 0)  // error
6715       return static_cast<long>(track);
6716 
6717     if (track == 0)
6718       return E_FILE_FORMAT_INVALID;
6719 
6720     pos += len;  // consume track number
6721 
6722     if ((pos + 2) > block_stop)
6723       return E_FILE_FORMAT_INVALID;
6724 
6725     if ((pos + 2) > avail) {
6726       len = 2;
6727       return E_BUFFER_NOT_FULL;
6728     }
6729 
6730     pos += 2;  // consume timecode
6731 
6732     if ((pos + 1) > block_stop)
6733       return E_FILE_FORMAT_INVALID;
6734 
6735     if ((pos + 1) > avail) {
6736       len = 1;
6737       return E_BUFFER_NOT_FULL;
6738     }
6739 
6740     unsigned char flags;
6741 
6742     status = pReader->Read(pos, 1, &flags);
6743 
6744     if (status < 0) {  // error or underflow
6745       len = 1;
6746       return status;
6747     }
6748 
6749     ++pos;  // consume flags byte
6750     assert(pos <= avail);
6751 
6752     if (pos >= block_stop)
6753       return E_FILE_FORMAT_INVALID;
6754 
6755     const int lacing = int(flags & 0x06) >> 1;
6756 
6757     if ((lacing != 0) && (block_stop > avail)) {
6758       len = static_cast<long>(block_stop - pos);
6759       return E_BUFFER_NOT_FULL;
6760     }
6761 
6762     pos = block_stop;  // consume block-part of block group
6763     if (pos > payload_stop)
6764       return E_FILE_FORMAT_INVALID;
6765   }
6766 
6767   if (pos != payload_stop)
6768     return E_FILE_FORMAT_INVALID;
6769 
6770   status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size,
6771                        discard_padding);
6772   if (status != 0)
6773     return status;
6774 
6775   m_pos = payload_stop;
6776 
6777   return 0;  // success
6778 }
6779 
GetEntry(long index,const mkvparser::BlockEntry * & pEntry) const6780 long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
6781   assert(m_pos >= m_element_start);
6782 
6783   pEntry = NULL;
6784 
6785   if (index < 0)
6786     return -1;  // generic error
6787 
6788   if (m_entries_count < 0)
6789     return E_BUFFER_NOT_FULL;
6790 
6791   assert(m_entries);
6792   assert(m_entries_size > 0);
6793   assert(m_entries_count <= m_entries_size);
6794 
6795   if (index < m_entries_count) {
6796     pEntry = m_entries[index];
6797     assert(pEntry);
6798 
6799     return 1;  // found entry
6800   }
6801 
6802   if (m_element_size < 0)  // we don't know cluster end yet
6803     return E_BUFFER_NOT_FULL;  // underflow
6804 
6805   const long long element_stop = m_element_start + m_element_size;
6806 
6807   if (m_pos >= element_stop)
6808     return 0;  // nothing left to parse
6809 
6810   return E_BUFFER_NOT_FULL;  // underflow, since more remains to be parsed
6811 }
6812 
Create(Segment * pSegment,long idx,long long off)6813 Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
6814   if (!pSegment || off < 0)
6815     return NULL;
6816 
6817   const long long element_start = pSegment->m_start + off;
6818 
6819   Cluster* const pCluster =
6820       new (std::nothrow) Cluster(pSegment, idx, element_start);
6821 
6822   return pCluster;
6823 }
6824 
Cluster()6825 Cluster::Cluster()
6826     : m_pSegment(NULL),
6827       m_element_start(0),
6828       m_index(0),
6829       m_pos(0),
6830       m_element_size(0),
6831       m_timecode(0),
6832       m_entries(NULL),
6833       m_entries_size(0),
6834       m_entries_count(0)  // means "no entries"
6835 {}
6836 
Cluster(Segment * pSegment,long idx,long long element_start)6837 Cluster::Cluster(Segment* pSegment, long idx, long long element_start
6838                  /* long long element_size */)
6839     : m_pSegment(pSegment),
6840       m_element_start(element_start),
6841       m_index(idx),
6842       m_pos(element_start),
6843       m_element_size(-1 /* element_size */),
6844       m_timecode(-1),
6845       m_entries(NULL),
6846       m_entries_size(0),
6847       m_entries_count(-1)  // means "has not been parsed yet"
6848 {}
6849 
~Cluster()6850 Cluster::~Cluster() {
6851   if (m_entries_count <= 0) {
6852     delete[] m_entries;
6853     return;
6854   }
6855 
6856   BlockEntry** i = m_entries;
6857   BlockEntry** const j = m_entries + m_entries_count;
6858 
6859   while (i != j) {
6860     BlockEntry* p = *i++;
6861     assert(p);
6862 
6863     delete p;
6864   }
6865 
6866   delete[] m_entries;
6867 }
6868 
EOS() const6869 bool Cluster::EOS() const { return (m_pSegment == NULL); }
6870 
GetIndex() const6871 long Cluster::GetIndex() const { return m_index; }
6872 
GetPosition() const6873 long long Cluster::GetPosition() const {
6874   const long long pos = m_element_start - m_pSegment->m_start;
6875   assert(pos >= 0);
6876 
6877   return pos;
6878 }
6879 
GetElementSize() const6880 long long Cluster::GetElementSize() const { return m_element_size; }
6881 
HasBlockEntries(const Segment * pSegment,long long off,long long & pos,long & len)6882 long Cluster::HasBlockEntries(
6883     const Segment* pSegment,
6884     long long off,  // relative to start of segment payload
6885     long long& pos, long& len) {
6886   assert(pSegment);
6887   assert(off >= 0);  // relative to segment
6888 
6889   IMkvReader* const pReader = pSegment->m_pReader;
6890 
6891   long long total, avail;
6892 
6893   long status = pReader->Length(&total, &avail);
6894 
6895   if (status < 0)  // error
6896     return status;
6897 
6898   assert((total < 0) || (avail <= total));
6899 
6900   pos = pSegment->m_start + off;  // absolute
6901 
6902   if ((total >= 0) && (pos >= total))
6903     return 0;  // we don't even have a complete cluster
6904 
6905   const long long segment_stop =
6906       (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
6907 
6908   long long cluster_stop = -1;  // interpreted later to mean "unknown size"
6909 
6910   {
6911     if ((pos + 1) > avail) {
6912       len = 1;
6913       return E_BUFFER_NOT_FULL;
6914     }
6915 
6916     long long result = GetUIntLength(pReader, pos, len);
6917 
6918     if (result < 0)  // error
6919       return static_cast<long>(result);
6920 
6921     if (result > 0)  // need more data
6922       return E_BUFFER_NOT_FULL;
6923 
6924     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6925       return E_FILE_FORMAT_INVALID;
6926 
6927     if ((total >= 0) && ((pos + len) > total))
6928       return 0;
6929 
6930     if ((pos + len) > avail)
6931       return E_BUFFER_NOT_FULL;
6932 
6933     const long long id = ReadID(pReader, pos, len);
6934 
6935     if (id < 0)  // error
6936       return static_cast<long>(id);
6937 
6938     if (id != libwebm::kMkvCluster)
6939       return E_PARSE_FAILED;
6940 
6941     pos += len;  // consume Cluster ID field
6942 
6943     // read size field
6944 
6945     if ((pos + 1) > avail) {
6946       len = 1;
6947       return E_BUFFER_NOT_FULL;
6948     }
6949 
6950     result = GetUIntLength(pReader, pos, len);
6951 
6952     if (result < 0)  // error
6953       return static_cast<long>(result);
6954 
6955     if (result > 0)  // weird
6956       return E_BUFFER_NOT_FULL;
6957 
6958     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6959       return E_FILE_FORMAT_INVALID;
6960 
6961     if ((total >= 0) && ((pos + len) > total))
6962       return 0;
6963 
6964     if ((pos + len) > avail)
6965       return E_BUFFER_NOT_FULL;
6966 
6967     const long long size = ReadUInt(pReader, pos, len);
6968 
6969     if (size < 0)  // error
6970       return static_cast<long>(size);
6971 
6972     if (size == 0)
6973       return 0;  // cluster does not have entries
6974 
6975     pos += len;  // consume size field
6976 
6977     // pos now points to start of payload
6978 
6979     const long long unknown_size = (1LL << (7 * len)) - 1;
6980 
6981     if (size != unknown_size) {
6982       cluster_stop = pos + size;
6983       assert(cluster_stop >= 0);
6984 
6985       if ((segment_stop >= 0) && (cluster_stop > segment_stop))
6986         return E_FILE_FORMAT_INVALID;
6987 
6988       if ((total >= 0) && (cluster_stop > total))
6989         // return E_FILE_FORMAT_INVALID;  //too conservative
6990         return 0;  // cluster does not have any entries
6991     }
6992   }
6993 
6994   for (;;) {
6995     if ((cluster_stop >= 0) && (pos >= cluster_stop))
6996       return 0;  // no entries detected
6997 
6998     if ((pos + 1) > avail) {
6999       len = 1;
7000       return E_BUFFER_NOT_FULL;
7001     }
7002 
7003     long long result = GetUIntLength(pReader, pos, len);
7004 
7005     if (result < 0)  // error
7006       return static_cast<long>(result);
7007 
7008     if (result > 0)  // need more data
7009       return E_BUFFER_NOT_FULL;
7010 
7011     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
7012       return E_FILE_FORMAT_INVALID;
7013 
7014     if ((pos + len) > avail)
7015       return E_BUFFER_NOT_FULL;
7016 
7017     const long long id = ReadID(pReader, pos, len);
7018 
7019     if (id < 0)  // error
7020       return static_cast<long>(id);
7021 
7022     // This is the distinguished set of ID's we use to determine
7023     // that we have exhausted the sub-element's inside the cluster
7024     // whose ID we parsed earlier.
7025 
7026     if (id == libwebm::kMkvCluster)
7027       return 0;  // no entries found
7028 
7029     if (id == libwebm::kMkvCues)
7030       return 0;  // no entries found
7031 
7032     pos += len;  // consume id field
7033 
7034     if ((cluster_stop >= 0) && (pos >= cluster_stop))
7035       return E_FILE_FORMAT_INVALID;
7036 
7037     // read size field
7038 
7039     if ((pos + 1) > avail) {
7040       len = 1;
7041       return E_BUFFER_NOT_FULL;
7042     }
7043 
7044     result = GetUIntLength(pReader, pos, len);
7045 
7046     if (result < 0)  // error
7047       return static_cast<long>(result);
7048 
7049     if (result > 0)  // underflow
7050       return E_BUFFER_NOT_FULL;
7051 
7052     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
7053       return E_FILE_FORMAT_INVALID;
7054 
7055     if ((pos + len) > avail)
7056       return E_BUFFER_NOT_FULL;
7057 
7058     const long long size = ReadUInt(pReader, pos, len);
7059 
7060     if (size < 0)  // error
7061       return static_cast<long>(size);
7062 
7063     pos += len;  // consume size field
7064 
7065     // pos now points to start of payload
7066 
7067     if ((cluster_stop >= 0) && (pos > cluster_stop))
7068       return E_FILE_FORMAT_INVALID;
7069 
7070     if (size == 0)  // weird
7071       continue;
7072 
7073     const long long unknown_size = (1LL << (7 * len)) - 1;
7074 
7075     if (size == unknown_size)
7076       return E_FILE_FORMAT_INVALID;  // not supported inside cluster
7077 
7078     if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
7079       return E_FILE_FORMAT_INVALID;
7080 
7081     if (id == libwebm::kMkvBlockGroup)
7082       return 1;  // have at least one entry
7083 
7084     if (id == libwebm::kMkvSimpleBlock)
7085       return 1;  // have at least one entry
7086 
7087     pos += size;  // consume payload
7088     if (cluster_stop >= 0 && pos > cluster_stop)
7089       return E_FILE_FORMAT_INVALID;
7090   }
7091 }
7092 
GetTimeCode() const7093 long long Cluster::GetTimeCode() const {
7094   long long pos;
7095   long len;
7096 
7097   const long status = Load(pos, len);
7098 
7099   if (status < 0)  // error
7100     return status;
7101 
7102   return m_timecode;
7103 }
7104 
GetTime() const7105 long long Cluster::GetTime() const {
7106   const long long tc = GetTimeCode();
7107 
7108   if (tc < 0)
7109     return tc;
7110 
7111   const SegmentInfo* const pInfo = m_pSegment->GetInfo();
7112   assert(pInfo);
7113 
7114   const long long scale = pInfo->GetTimeCodeScale();
7115   assert(scale >= 1);
7116 
7117   const long long t = m_timecode * scale;
7118 
7119   return t;
7120 }
7121 
GetFirstTime() const7122 long long Cluster::GetFirstTime() const {
7123   const BlockEntry* pEntry;
7124 
7125   const long status = GetFirst(pEntry);
7126 
7127   if (status < 0)  // error
7128     return status;
7129 
7130   if (pEntry == NULL)  // empty cluster
7131     return GetTime();
7132 
7133   const Block* const pBlock = pEntry->GetBlock();
7134   assert(pBlock);
7135 
7136   return pBlock->GetTime(this);
7137 }
7138 
GetLastTime() const7139 long long Cluster::GetLastTime() const {
7140   const BlockEntry* pEntry;
7141 
7142   const long status = GetLast(pEntry);
7143 
7144   if (status < 0)  // error
7145     return status;
7146 
7147   if (pEntry == NULL)  // empty cluster
7148     return GetTime();
7149 
7150   const Block* const pBlock = pEntry->GetBlock();
7151   assert(pBlock);
7152 
7153   return pBlock->GetTime(this);
7154 }
7155 
CreateBlock(long long id,long long pos,long long size,long long discard_padding)7156 long Cluster::CreateBlock(long long id,
7157                           long long pos,  // absolute pos of payload
7158                           long long size, long long discard_padding) {
7159   if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock)
7160     return E_PARSE_FAILED;
7161 
7162   if (m_entries_count < 0) {  // haven't parsed anything yet
7163     assert(m_entries == NULL);
7164     assert(m_entries_size == 0);
7165 
7166     m_entries_size = 1024;
7167     m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
7168     if (m_entries == NULL)
7169       return -1;
7170 
7171     m_entries_count = 0;
7172   } else {
7173     assert(m_entries);
7174     assert(m_entries_size > 0);
7175     assert(m_entries_count <= m_entries_size);
7176 
7177     if (m_entries_count >= m_entries_size) {
7178       const long entries_size = 2 * m_entries_size;
7179 
7180       BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
7181       if (entries == NULL)
7182         return -1;
7183 
7184       BlockEntry** src = m_entries;
7185       BlockEntry** const src_end = src + m_entries_count;
7186 
7187       BlockEntry** dst = entries;
7188 
7189       while (src != src_end)
7190         *dst++ = *src++;
7191 
7192       delete[] m_entries;
7193 
7194       m_entries = entries;
7195       m_entries_size = entries_size;
7196     }
7197   }
7198 
7199   if (id == libwebm::kMkvBlockGroup)
7200     return CreateBlockGroup(pos, size, discard_padding);
7201   else
7202     return CreateSimpleBlock(pos, size);
7203 }
7204 
CreateBlockGroup(long long start_offset,long long size,long long discard_padding)7205 long Cluster::CreateBlockGroup(long long start_offset, long long size,
7206                                long long discard_padding) {
7207   assert(m_entries);
7208   assert(m_entries_size > 0);
7209   assert(m_entries_count >= 0);
7210   assert(m_entries_count < m_entries_size);
7211 
7212   IMkvReader* const pReader = m_pSegment->m_pReader;
7213 
7214   long long pos = start_offset;
7215   const long long stop = start_offset + size;
7216 
7217   // For WebM files, there is a bias towards previous reference times
7218   //(in order to support alt-ref frames, which refer back to the previous
7219   // keyframe).  Normally a 0 value is not possible, but here we tenatively
7220   // allow 0 as the value of a reference frame, with the interpretation
7221   // that this is a "previous" reference time.
7222 
7223   long long prev = 1;  // nonce
7224   long long next = 0;  // nonce
7225   long long duration = -1;  // really, this is unsigned
7226 
7227   long long bpos = -1;
7228   long long bsize = -1;
7229 
7230   while (pos < stop) {
7231     long len;
7232     const long long id = ReadID(pReader, pos, len);
7233     if (id < 0 || (pos + len) > stop)
7234       return E_FILE_FORMAT_INVALID;
7235 
7236     pos += len;  // consume ID
7237 
7238     const long long size = ReadUInt(pReader, pos, len);
7239     assert(size >= 0);  // TODO
7240     assert((pos + len) <= stop);
7241 
7242     pos += len;  // consume size
7243 
7244     if (id == libwebm::kMkvBlock) {
7245       if (bpos < 0) {  // Block ID
7246         bpos = pos;
7247         bsize = size;
7248       }
7249     } else if (id == libwebm::kMkvBlockDuration) {
7250       if (size > 8)
7251         return E_FILE_FORMAT_INVALID;
7252 
7253       duration = UnserializeUInt(pReader, pos, size);
7254 
7255       if (duration < 0)
7256         return E_FILE_FORMAT_INVALID;
7257     } else if (id == libwebm::kMkvReferenceBlock) {
7258       if (size > 8 || size <= 0)
7259         return E_FILE_FORMAT_INVALID;
7260       const long size_ = static_cast<long>(size);
7261 
7262       long long time;
7263 
7264       long status = UnserializeInt(pReader, pos, size_, time);
7265       assert(status == 0);
7266       if (status != 0)
7267         return -1;
7268 
7269       if (time <= 0)  // see note above
7270         prev = time;
7271       else
7272         next = time;
7273     }
7274 
7275     pos += size;  // consume payload
7276     if (pos > stop)
7277       return E_FILE_FORMAT_INVALID;
7278   }
7279   if (bpos < 0)
7280     return E_FILE_FORMAT_INVALID;
7281 
7282   if (pos != stop)
7283     return E_FILE_FORMAT_INVALID;
7284   assert(bsize >= 0);
7285 
7286   const long idx = m_entries_count;
7287 
7288   BlockEntry** const ppEntry = m_entries + idx;
7289   BlockEntry*& pEntry = *ppEntry;
7290 
7291   pEntry = new (std::nothrow)
7292       BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
7293 
7294   if (pEntry == NULL)
7295     return -1;  // generic error
7296 
7297   BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
7298 
7299   const long status = p->Parse();
7300 
7301   if (status == 0) {  // success
7302     ++m_entries_count;
7303     return 0;
7304   }
7305 
7306   delete pEntry;
7307   pEntry = 0;
7308 
7309   return status;
7310 }
7311 
CreateSimpleBlock(long long st,long long sz)7312 long Cluster::CreateSimpleBlock(long long st, long long sz) {
7313   assert(m_entries);
7314   assert(m_entries_size > 0);
7315   assert(m_entries_count >= 0);
7316   assert(m_entries_count < m_entries_size);
7317 
7318   const long idx = m_entries_count;
7319 
7320   BlockEntry** const ppEntry = m_entries + idx;
7321   BlockEntry*& pEntry = *ppEntry;
7322 
7323   pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
7324 
7325   if (pEntry == NULL)
7326     return -1;  // generic error
7327 
7328   SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
7329 
7330   const long status = p->Parse();
7331 
7332   if (status == 0) {
7333     ++m_entries_count;
7334     return 0;
7335   }
7336 
7337   delete pEntry;
7338   pEntry = 0;
7339 
7340   return status;
7341 }
7342 
GetFirst(const BlockEntry * & pFirst) const7343 long Cluster::GetFirst(const BlockEntry*& pFirst) const {
7344   if (m_entries_count <= 0) {
7345     long long pos;
7346     long len;
7347 
7348     const long status = Parse(pos, len);
7349 
7350     if (status < 0) {  // error
7351       pFirst = NULL;
7352       return status;
7353     }
7354 
7355     if (m_entries_count <= 0) {  // empty cluster
7356       pFirst = NULL;
7357       return 0;
7358     }
7359   }
7360 
7361   assert(m_entries);
7362 
7363   pFirst = m_entries[0];
7364   assert(pFirst);
7365 
7366   return 0;  // success
7367 }
7368 
GetLast(const BlockEntry * & pLast) const7369 long Cluster::GetLast(const BlockEntry*& pLast) const {
7370   for (;;) {
7371     long long pos;
7372     long len;
7373 
7374     const long status = Parse(pos, len);
7375 
7376     if (status < 0) {  // error
7377       pLast = NULL;
7378       return status;
7379     }
7380 
7381     if (status > 0)  // no new block
7382       break;
7383   }
7384 
7385   if (m_entries_count <= 0) {
7386     pLast = NULL;
7387     return 0;
7388   }
7389 
7390   assert(m_entries);
7391 
7392   const long idx = m_entries_count - 1;
7393 
7394   pLast = m_entries[idx];
7395   assert(pLast);
7396 
7397   return 0;
7398 }
7399 
GetNext(const BlockEntry * pCurr,const BlockEntry * & pNext) const7400 long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
7401   assert(pCurr);
7402   assert(m_entries);
7403   assert(m_entries_count > 0);
7404 
7405   size_t idx = pCurr->GetIndex();
7406   assert(idx < size_t(m_entries_count));
7407   assert(m_entries[idx] == pCurr);
7408 
7409   ++idx;
7410 
7411   if (idx >= size_t(m_entries_count)) {
7412     long long pos;
7413     long len;
7414 
7415     const long status = Parse(pos, len);
7416 
7417     if (status < 0) {  // error
7418       pNext = NULL;
7419       return status;
7420     }
7421 
7422     if (status > 0) {
7423       pNext = NULL;
7424       return 0;
7425     }
7426 
7427     assert(m_entries);
7428     assert(m_entries_count > 0);
7429     assert(idx < size_t(m_entries_count));
7430   }
7431 
7432   pNext = m_entries[idx];
7433   assert(pNext);
7434 
7435   return 0;
7436 }
7437 
GetEntryCount() const7438 long Cluster::GetEntryCount() const { return m_entries_count; }
7439 
GetEntry(const Track * pTrack,long long time_ns) const7440 const BlockEntry* Cluster::GetEntry(const Track* pTrack,
7441                                     long long time_ns) const {
7442   assert(pTrack);
7443 
7444   if (m_pSegment == NULL)  // this is the special EOS cluster
7445     return pTrack->GetEOS();
7446 
7447   const BlockEntry* pResult = pTrack->GetEOS();
7448 
7449   long index = 0;
7450 
7451   for (;;) {
7452     if (index >= m_entries_count) {
7453       long long pos;
7454       long len;
7455 
7456       const long status = Parse(pos, len);
7457       assert(status >= 0);
7458 
7459       if (status > 0)  // completely parsed, and no more entries
7460         return pResult;
7461 
7462       if (status < 0)  // should never happen
7463         return 0;
7464 
7465       assert(m_entries);
7466       assert(index < m_entries_count);
7467     }
7468 
7469     const BlockEntry* const pEntry = m_entries[index];
7470     assert(pEntry);
7471     assert(!pEntry->EOS());
7472 
7473     const Block* const pBlock = pEntry->GetBlock();
7474     assert(pBlock);
7475 
7476     if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
7477       ++index;
7478       continue;
7479     }
7480 
7481     if (pTrack->VetEntry(pEntry)) {
7482       if (time_ns < 0)  // just want first candidate block
7483         return pEntry;
7484 
7485       const long long ns = pBlock->GetTime(this);
7486 
7487       if (ns > time_ns)
7488         return pResult;
7489 
7490       pResult = pEntry;  // have a candidate
7491     } else if (time_ns >= 0) {
7492       const long long ns = pBlock->GetTime(this);
7493 
7494       if (ns > time_ns)
7495         return pResult;
7496     }
7497 
7498     ++index;
7499   }
7500 }
7501 
GetEntry(const CuePoint & cp,const CuePoint::TrackPosition & tp) const7502 const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
7503                                     const CuePoint::TrackPosition& tp) const {
7504   assert(m_pSegment);
7505   const long long tc = cp.GetTimeCode();
7506 
7507   if (tp.m_block > 0) {
7508     const long block = static_cast<long>(tp.m_block);
7509     const long index = block - 1;
7510 
7511     while (index >= m_entries_count) {
7512       long long pos;
7513       long len;
7514 
7515       const long status = Parse(pos, len);
7516 
7517       if (status < 0)  // TODO: can this happen?
7518         return NULL;
7519 
7520       if (status > 0)  // nothing remains to be parsed
7521         return NULL;
7522     }
7523 
7524     const BlockEntry* const pEntry = m_entries[index];
7525     assert(pEntry);
7526     assert(!pEntry->EOS());
7527 
7528     const Block* const pBlock = pEntry->GetBlock();
7529     assert(pBlock);
7530 
7531     if ((pBlock->GetTrackNumber() == tp.m_track) &&
7532         (pBlock->GetTimeCode(this) == tc)) {
7533       return pEntry;
7534     }
7535   }
7536 
7537   long index = 0;
7538 
7539   for (;;) {
7540     if (index >= m_entries_count) {
7541       long long pos;
7542       long len;
7543 
7544       const long status = Parse(pos, len);
7545 
7546       if (status < 0)  // TODO: can this happen?
7547         return NULL;
7548 
7549       if (status > 0)  // nothing remains to be parsed
7550         return NULL;
7551 
7552       assert(m_entries);
7553       assert(index < m_entries_count);
7554     }
7555 
7556     const BlockEntry* const pEntry = m_entries[index];
7557     assert(pEntry);
7558     assert(!pEntry->EOS());
7559 
7560     const Block* const pBlock = pEntry->GetBlock();
7561     assert(pBlock);
7562 
7563     if (pBlock->GetTrackNumber() != tp.m_track) {
7564       ++index;
7565       continue;
7566     }
7567 
7568     const long long tc_ = pBlock->GetTimeCode(this);
7569 
7570     if (tc_ < tc) {
7571       ++index;
7572       continue;
7573     }
7574 
7575     if (tc_ > tc)
7576       return NULL;
7577 
7578     const Tracks* const pTracks = m_pSegment->GetTracks();
7579     assert(pTracks);
7580 
7581     const long tn = static_cast<long>(tp.m_track);
7582     const Track* const pTrack = pTracks->GetTrackByNumber(tn);
7583 
7584     if (pTrack == NULL)
7585       return NULL;
7586 
7587     const long long type = pTrack->GetType();
7588 
7589     if (type == 2)  // audio
7590       return pEntry;
7591 
7592     if (type != 1)  // not video
7593       return NULL;
7594 
7595     if (!pBlock->IsKey())
7596       return NULL;
7597 
7598     return pEntry;
7599   }
7600 }
7601 
BlockEntry(Cluster * p,long idx)7602 BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
~BlockEntry()7603 BlockEntry::~BlockEntry() {}
GetCluster() const7604 const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
GetIndex() const7605 long BlockEntry::GetIndex() const { return m_index; }
7606 
SimpleBlock(Cluster * pCluster,long idx,long long start,long long size)7607 SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
7608                          long long size)
7609     : BlockEntry(pCluster, idx), m_block(start, size, 0) {}
7610 
Parse()7611 long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
GetKind() const7612 BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
GetBlock() const7613 const Block* SimpleBlock::GetBlock() const { return &m_block; }
7614 
BlockGroup(Cluster * pCluster,long idx,long long block_start,long long block_size,long long prev,long long next,long long duration,long long discard_padding)7615 BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
7616                        long long block_size, long long prev, long long next,
7617                        long long duration, long long discard_padding)
7618     : BlockEntry(pCluster, idx),
7619       m_block(block_start, block_size, discard_padding),
7620       m_prev(prev),
7621       m_next(next),
7622       m_duration(duration) {}
7623 
Parse()7624 long BlockGroup::Parse() {
7625   const long status = m_block.Parse(m_pCluster);
7626 
7627   if (status)
7628     return status;
7629 
7630   m_block.SetKey((m_prev > 0) && (m_next <= 0));
7631 
7632   return 0;
7633 }
7634 
GetKind() const7635 BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
GetBlock() const7636 const Block* BlockGroup::GetBlock() const { return &m_block; }
GetPrevTimeCode() const7637 long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
GetNextTimeCode() const7638 long long BlockGroup::GetNextTimeCode() const { return m_next; }
GetDurationTimeCode() const7639 long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
7640 
Block(long long start,long long size_,long long discard_padding)7641 Block::Block(long long start, long long size_, long long discard_padding)
7642     : m_start(start),
7643       m_size(size_),
7644       m_track(0),
7645       m_timecode(-1),
7646       m_flags(0),
7647       m_frames(NULL),
7648       m_frame_count(-1),
7649       m_discard_padding(discard_padding) {}
7650 
~Block()7651 Block::~Block() { delete[] m_frames; }
7652 
Parse(const Cluster * pCluster)7653 long Block::Parse(const Cluster* pCluster) {
7654   if (pCluster == NULL)
7655     return -1;
7656 
7657   if (pCluster->m_pSegment == NULL)
7658     return -1;
7659 
7660   assert(m_start >= 0);
7661   assert(m_size >= 0);
7662   assert(m_track <= 0);
7663   assert(m_frames == NULL);
7664   assert(m_frame_count <= 0);
7665 
7666   long long pos = m_start;
7667   const long long stop = m_start + m_size;
7668 
7669   long len;
7670 
7671   IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
7672 
7673   m_track = ReadUInt(pReader, pos, len);
7674 
7675   if (m_track <= 0)
7676     return E_FILE_FORMAT_INVALID;
7677 
7678   if ((pos + len) > stop)
7679     return E_FILE_FORMAT_INVALID;
7680 
7681   pos += len;  // consume track number
7682 
7683   if ((stop - pos) < 2)
7684     return E_FILE_FORMAT_INVALID;
7685 
7686   long status;
7687   long long value;
7688 
7689   status = UnserializeInt(pReader, pos, 2, value);
7690 
7691   if (status)
7692     return E_FILE_FORMAT_INVALID;
7693 
7694   if (value < SHRT_MIN)
7695     return E_FILE_FORMAT_INVALID;
7696 
7697   if (value > SHRT_MAX)
7698     return E_FILE_FORMAT_INVALID;
7699 
7700   m_timecode = static_cast<short>(value);
7701 
7702   pos += 2;
7703 
7704   if ((stop - pos) <= 0)
7705     return E_FILE_FORMAT_INVALID;
7706 
7707   status = pReader->Read(pos, 1, &m_flags);
7708 
7709   if (status)
7710     return E_FILE_FORMAT_INVALID;
7711 
7712   const int lacing = int(m_flags & 0x06) >> 1;
7713 
7714   ++pos;  // consume flags byte
7715 
7716   if (lacing == 0) {  // no lacing
7717     if (pos > stop)
7718       return E_FILE_FORMAT_INVALID;
7719 
7720     m_frame_count = 1;
7721     m_frames = new (std::nothrow) Frame[m_frame_count];
7722     if (m_frames == NULL)
7723       return -1;
7724 
7725     Frame& f = m_frames[0];
7726     f.pos = pos;
7727 
7728     const long long frame_size = stop - pos;
7729 
7730     if (frame_size > LONG_MAX || frame_size <= 0)
7731       return E_FILE_FORMAT_INVALID;
7732 
7733     f.len = static_cast<long>(frame_size);
7734 
7735     return 0;  // success
7736   }
7737 
7738   if (pos >= stop)
7739     return E_FILE_FORMAT_INVALID;
7740 
7741   unsigned char biased_count;
7742 
7743   status = pReader->Read(pos, 1, &biased_count);
7744 
7745   if (status)
7746     return E_FILE_FORMAT_INVALID;
7747 
7748   ++pos;  // consume frame count
7749   if (pos > stop)
7750     return E_FILE_FORMAT_INVALID;
7751 
7752   m_frame_count = int(biased_count) + 1;
7753 
7754   m_frames = new (std::nothrow) Frame[m_frame_count];
7755   if (m_frames == NULL)
7756     return -1;
7757 
7758   if (!m_frames)
7759     return E_FILE_FORMAT_INVALID;
7760 
7761   if (lacing == 1) {  // Xiph
7762     Frame* pf = m_frames;
7763     Frame* const pf_end = pf + m_frame_count;
7764 
7765     long long size = 0;
7766     int frame_count = m_frame_count;
7767 
7768     while (frame_count > 1) {
7769       long frame_size = 0;
7770 
7771       for (;;) {
7772         unsigned char val;
7773 
7774         if (pos >= stop)
7775           return E_FILE_FORMAT_INVALID;
7776 
7777         status = pReader->Read(pos, 1, &val);
7778 
7779         if (status)
7780           return E_FILE_FORMAT_INVALID;
7781 
7782         ++pos;  // consume xiph size byte
7783 
7784         frame_size += val;
7785 
7786         if (val < 255)
7787           break;
7788       }
7789 
7790       Frame& f = *pf++;
7791       assert(pf < pf_end);
7792       if (pf >= pf_end)
7793         return E_FILE_FORMAT_INVALID;
7794 
7795       f.pos = 0;  // patch later
7796 
7797       if (frame_size <= 0)
7798         return E_FILE_FORMAT_INVALID;
7799 
7800       f.len = frame_size;
7801       size += frame_size;  // contribution of this frame
7802 
7803       --frame_count;
7804     }
7805 
7806     if (pf >= pf_end || pos > stop)
7807       return E_FILE_FORMAT_INVALID;
7808 
7809     {
7810       Frame& f = *pf++;
7811 
7812       if (pf != pf_end)
7813         return E_FILE_FORMAT_INVALID;
7814 
7815       f.pos = 0;  // patch later
7816 
7817       const long long total_size = stop - pos;
7818 
7819       if (total_size < size)
7820         return E_FILE_FORMAT_INVALID;
7821 
7822       const long long frame_size = total_size - size;
7823 
7824       if (frame_size > LONG_MAX || frame_size <= 0)
7825         return E_FILE_FORMAT_INVALID;
7826 
7827       f.len = static_cast<long>(frame_size);
7828     }
7829 
7830     pf = m_frames;
7831     while (pf != pf_end) {
7832       Frame& f = *pf++;
7833       assert((pos + f.len) <= stop);
7834 
7835       if ((pos + f.len) > stop)
7836         return E_FILE_FORMAT_INVALID;
7837 
7838       f.pos = pos;
7839       pos += f.len;
7840     }
7841 
7842     assert(pos == stop);
7843     if (pos != stop)
7844       return E_FILE_FORMAT_INVALID;
7845 
7846   } else if (lacing == 2) {  // fixed-size lacing
7847     if (pos >= stop)
7848       return E_FILE_FORMAT_INVALID;
7849 
7850     const long long total_size = stop - pos;
7851 
7852     if ((total_size % m_frame_count) != 0)
7853       return E_FILE_FORMAT_INVALID;
7854 
7855     const long long frame_size = total_size / m_frame_count;
7856 
7857     if (frame_size > LONG_MAX || frame_size <= 0)
7858       return E_FILE_FORMAT_INVALID;
7859 
7860     Frame* pf = m_frames;
7861     Frame* const pf_end = pf + m_frame_count;
7862 
7863     while (pf != pf_end) {
7864       assert((pos + frame_size) <= stop);
7865       if ((pos + frame_size) > stop)
7866         return E_FILE_FORMAT_INVALID;
7867 
7868       Frame& f = *pf++;
7869 
7870       f.pos = pos;
7871       f.len = static_cast<long>(frame_size);
7872 
7873       pos += frame_size;
7874     }
7875 
7876     assert(pos == stop);
7877     if (pos != stop)
7878       return E_FILE_FORMAT_INVALID;
7879 
7880   } else {
7881     assert(lacing == 3);  // EBML lacing
7882 
7883     if (pos >= stop)
7884       return E_FILE_FORMAT_INVALID;
7885 
7886     long long size = 0;
7887     int frame_count = m_frame_count;
7888 
7889     long long frame_size = ReadUInt(pReader, pos, len);
7890 
7891     if (frame_size <= 0)
7892       return E_FILE_FORMAT_INVALID;
7893 
7894     if (frame_size > LONG_MAX)
7895       return E_FILE_FORMAT_INVALID;
7896 
7897     if ((pos + len) > stop)
7898       return E_FILE_FORMAT_INVALID;
7899 
7900     pos += len;  // consume length of size of first frame
7901 
7902     if ((pos + frame_size) > stop)
7903       return E_FILE_FORMAT_INVALID;
7904 
7905     Frame* pf = m_frames;
7906     Frame* const pf_end = pf + m_frame_count;
7907 
7908     {
7909       Frame& curr = *pf;
7910 
7911       curr.pos = 0;  // patch later
7912 
7913       curr.len = static_cast<long>(frame_size);
7914       size += curr.len;  // contribution of this frame
7915     }
7916 
7917     --frame_count;
7918 
7919     while (frame_count > 1) {
7920       if (pos >= stop)
7921         return E_FILE_FORMAT_INVALID;
7922 
7923       assert(pf < pf_end);
7924       if (pf >= pf_end)
7925         return E_FILE_FORMAT_INVALID;
7926 
7927       const Frame& prev = *pf++;
7928       assert(prev.len == frame_size);
7929       if (prev.len != frame_size)
7930         return E_FILE_FORMAT_INVALID;
7931 
7932       assert(pf < pf_end);
7933       if (pf >= pf_end)
7934         return E_FILE_FORMAT_INVALID;
7935 
7936       Frame& curr = *pf;
7937 
7938       curr.pos = 0;  // patch later
7939 
7940       const long long delta_size_ = ReadUInt(pReader, pos, len);
7941 
7942       if (delta_size_ < 0)
7943         return E_FILE_FORMAT_INVALID;
7944 
7945       if ((pos + len) > stop)
7946         return E_FILE_FORMAT_INVALID;
7947 
7948       pos += len;  // consume length of (delta) size
7949       if (pos > stop)
7950         return E_FILE_FORMAT_INVALID;
7951 
7952       const long exp = 7 * len - 1;
7953       const long long bias = (1LL << exp) - 1LL;
7954       const long long delta_size = delta_size_ - bias;
7955 
7956       frame_size += delta_size;
7957 
7958       if (frame_size <= 0)
7959         return E_FILE_FORMAT_INVALID;
7960 
7961       if (frame_size > LONG_MAX)
7962         return E_FILE_FORMAT_INVALID;
7963 
7964       curr.len = static_cast<long>(frame_size);
7965       // Check if size + curr.len could overflow.
7966       if (size > LLONG_MAX - curr.len) {
7967         return E_FILE_FORMAT_INVALID;
7968       }
7969       size += curr.len;  // contribution of this frame
7970 
7971       --frame_count;
7972     }
7973 
7974     // parse last frame
7975     if (frame_count > 0) {
7976       if (pos > stop || pf >= pf_end)
7977         return E_FILE_FORMAT_INVALID;
7978 
7979       const Frame& prev = *pf++;
7980       assert(prev.len == frame_size);
7981       if (prev.len != frame_size)
7982         return E_FILE_FORMAT_INVALID;
7983 
7984       if (pf >= pf_end)
7985         return E_FILE_FORMAT_INVALID;
7986 
7987       Frame& curr = *pf++;
7988       if (pf != pf_end)
7989         return E_FILE_FORMAT_INVALID;
7990 
7991       curr.pos = 0;  // patch later
7992 
7993       const long long total_size = stop - pos;
7994 
7995       if (total_size < size)
7996         return E_FILE_FORMAT_INVALID;
7997 
7998       frame_size = total_size - size;
7999 
8000       if (frame_size > LONG_MAX || frame_size <= 0)
8001         return E_FILE_FORMAT_INVALID;
8002 
8003       curr.len = static_cast<long>(frame_size);
8004     }
8005 
8006     pf = m_frames;
8007     while (pf != pf_end) {
8008       Frame& f = *pf++;
8009       if ((pos + f.len) > stop)
8010         return E_FILE_FORMAT_INVALID;
8011 
8012       f.pos = pos;
8013       pos += f.len;
8014     }
8015 
8016     if (pos != stop)
8017       return E_FILE_FORMAT_INVALID;
8018   }
8019 
8020   return 0;  // success
8021 }
8022 
GetTimeCode(const Cluster * pCluster) const8023 long long Block::GetTimeCode(const Cluster* pCluster) const {
8024   if (pCluster == 0)
8025     return m_timecode;
8026 
8027   const long long tc0 = pCluster->GetTimeCode();
8028   assert(tc0 >= 0);
8029 
8030   // Check if tc0 + m_timecode would overflow.
8031   if (tc0 < 0 || LLONG_MAX - tc0 < m_timecode) {
8032     return -1;
8033   }
8034 
8035   const long long tc = tc0 + m_timecode;
8036 
8037   return tc;  // unscaled timecode units
8038 }
8039 
GetTime(const Cluster * pCluster) const8040 long long Block::GetTime(const Cluster* pCluster) const {
8041   assert(pCluster);
8042 
8043   const long long tc = GetTimeCode(pCluster);
8044 
8045   const Segment* const pSegment = pCluster->m_pSegment;
8046   const SegmentInfo* const pInfo = pSegment->GetInfo();
8047   assert(pInfo);
8048 
8049   const long long scale = pInfo->GetTimeCodeScale();
8050   assert(scale >= 1);
8051 
8052   // Check if tc * scale could overflow.
8053   if (tc != 0 && scale > LLONG_MAX / tc) {
8054     return -1;
8055   }
8056   const long long ns = tc * scale;
8057 
8058   return ns;
8059 }
8060 
GetTrackNumber() const8061 long long Block::GetTrackNumber() const { return m_track; }
8062 
IsKey() const8063 bool Block::IsKey() const {
8064   return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
8065 }
8066 
SetKey(bool bKey)8067 void Block::SetKey(bool bKey) {
8068   if (bKey)
8069     m_flags |= static_cast<unsigned char>(1 << 7);
8070   else
8071     m_flags &= 0x7F;
8072 }
8073 
IsInvisible() const8074 bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
8075 
GetLacing() const8076 Block::Lacing Block::GetLacing() const {
8077   const int value = int(m_flags & 0x06) >> 1;
8078   return static_cast<Lacing>(value);
8079 }
8080 
GetFrameCount() const8081 int Block::GetFrameCount() const { return m_frame_count; }
8082 
GetFrame(int idx) const8083 const Block::Frame& Block::GetFrame(int idx) const {
8084   assert(idx >= 0);
8085   assert(idx < m_frame_count);
8086 
8087   const Frame& f = m_frames[idx];
8088   assert(f.pos > 0);
8089   assert(f.len > 0);
8090 
8091   return f;
8092 }
8093 
Read(IMkvReader * pReader,unsigned char * buf) const8094 long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
8095   assert(pReader);
8096   assert(buf);
8097 
8098   const long status = pReader->Read(pos, len, buf);
8099   return status;
8100 }
8101 
GetDiscardPadding() const8102 long long Block::GetDiscardPadding() const { return m_discard_padding; }
8103 
8104 }  // namespace mkvparser
8105