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