• 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 float MasteringMetadata::kValueNotPresent = FLT_MAX;
27 const long long Colour::kValueNotPresent = LLONG_MAX;
28 
29 #ifdef MSC_COMPAT
isnan(double val)30 inline bool isnan(double val) { return !!_isnan(val); }
isinf(double val)31 inline bool isinf(double val) { return !_finite(val); }
32 #else
isnan(double val)33 inline bool isnan(double val) { return std::isnan(val); }
isinf(double val)34 inline bool isinf(double val) { return std::isinf(val); }
35 #endif  // MSC_COMPAT
36 
~IMkvReader()37 IMkvReader::~IMkvReader() {}
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 = 0;
58   build = 0;
59   revision = 30;
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 = 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)
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 int 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 
SeekHead(Segment * pSegment,long long start,long long size_,long long element_start,long long element_size)1478 SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
1479                    long long element_start, long long element_size)
1480     : m_pSegment(pSegment),
1481       m_start(start),
1482       m_size(size_),
1483       m_element_start(element_start),
1484       m_element_size(element_size),
1485       m_entries(0),
1486       m_entry_count(0),
1487       m_void_elements(0),
1488       m_void_element_count(0) {}
1489 
~SeekHead()1490 SeekHead::~SeekHead() {
1491   delete[] m_entries;
1492   delete[] m_void_elements;
1493 }
1494 
Parse()1495 long SeekHead::Parse() {
1496   IMkvReader* const pReader = m_pSegment->m_pReader;
1497 
1498   long long pos = m_start;
1499   const long long stop = m_start + m_size;
1500 
1501   // first count the seek head entries
1502 
1503   int entry_count = 0;
1504   int void_element_count = 0;
1505 
1506   while (pos < stop) {
1507     long long id, size;
1508 
1509     const long status = ParseElementHeader(pReader, pos, stop, id, size);
1510 
1511     if (status < 0)  // error
1512       return status;
1513 
1514     if (id == libwebm::kMkvSeek)
1515       ++entry_count;
1516     else if (id == libwebm::kMkvVoid)
1517       ++void_element_count;
1518 
1519     pos += size;  // consume payload
1520 
1521     if (pos > stop)
1522       return E_FILE_FORMAT_INVALID;
1523   }
1524 
1525   if (pos != stop)
1526     return E_FILE_FORMAT_INVALID;
1527 
1528   m_entries = new (std::nothrow) Entry[entry_count];
1529 
1530   if (m_entries == NULL)
1531     return -1;
1532 
1533   m_void_elements = new (std::nothrow) VoidElement[void_element_count];
1534 
1535   if (m_void_elements == NULL)
1536     return -1;
1537 
1538   // now parse the entries and void elements
1539 
1540   Entry* pEntry = m_entries;
1541   VoidElement* pVoidElement = m_void_elements;
1542 
1543   pos = m_start;
1544 
1545   while (pos < stop) {
1546     const long long idpos = pos;
1547 
1548     long long id, size;
1549 
1550     const long status = ParseElementHeader(pReader, pos, stop, id, size);
1551 
1552     if (status < 0)  // error
1553       return status;
1554 
1555     if (id == libwebm::kMkvSeek) {
1556       if (ParseEntry(pReader, pos, size, pEntry)) {
1557         Entry& e = *pEntry++;
1558 
1559         e.element_start = idpos;
1560         e.element_size = (pos + size) - idpos;
1561       }
1562     } else if (id == libwebm::kMkvVoid) {
1563       VoidElement& e = *pVoidElement++;
1564 
1565       e.element_start = idpos;
1566       e.element_size = (pos + size) - idpos;
1567     }
1568 
1569     pos += size;  // consume payload
1570     if (pos > stop)
1571       return E_FILE_FORMAT_INVALID;
1572   }
1573 
1574   if (pos != stop)
1575     return E_FILE_FORMAT_INVALID;
1576 
1577   ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
1578   assert(count_ >= 0);
1579   assert(count_ <= entry_count);
1580 
1581   m_entry_count = static_cast<int>(count_);
1582 
1583   count_ = ptrdiff_t(pVoidElement - m_void_elements);
1584   assert(count_ >= 0);
1585   assert(count_ <= void_element_count);
1586 
1587   m_void_element_count = static_cast<int>(count_);
1588 
1589   return 0;
1590 }
1591 
GetCount() const1592 int SeekHead::GetCount() const { return m_entry_count; }
1593 
GetEntry(int idx) const1594 const SeekHead::Entry* SeekHead::GetEntry(int idx) const {
1595   if (idx < 0)
1596     return 0;
1597 
1598   if (idx >= m_entry_count)
1599     return 0;
1600 
1601   return m_entries + idx;
1602 }
1603 
GetVoidElementCount() const1604 int SeekHead::GetVoidElementCount() const { return m_void_element_count; }
1605 
GetVoidElement(int idx) const1606 const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
1607   if (idx < 0)
1608     return 0;
1609 
1610   if (idx >= m_void_element_count)
1611     return 0;
1612 
1613   return m_void_elements + idx;
1614 }
1615 
ParseCues(long long off,long long & pos,long & len)1616 long Segment::ParseCues(long long off, long long& pos, long& len) {
1617   if (m_pCues)
1618     return 0;  // success
1619 
1620   if (off < 0)
1621     return -1;
1622 
1623   long long total, avail;
1624 
1625   const int status = m_pReader->Length(&total, &avail);
1626 
1627   if (status < 0)  // error
1628     return status;
1629 
1630   assert((total < 0) || (avail <= total));
1631 
1632   pos = m_start + off;
1633 
1634   if ((total < 0) || (pos >= total))
1635     return 1;  // don't bother parsing cues
1636 
1637   const long long element_start = pos;
1638   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
1639 
1640   if ((pos + 1) > avail) {
1641     len = 1;
1642     return E_BUFFER_NOT_FULL;
1643   }
1644 
1645   long long result = GetUIntLength(m_pReader, pos, len);
1646 
1647   if (result < 0)  // error
1648     return static_cast<long>(result);
1649 
1650   if (result > 0)  // underflow (weird)
1651   {
1652     len = 1;
1653     return E_BUFFER_NOT_FULL;
1654   }
1655 
1656   if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1657     return E_FILE_FORMAT_INVALID;
1658 
1659   if ((pos + len) > avail)
1660     return E_BUFFER_NOT_FULL;
1661 
1662   const long long idpos = pos;
1663 
1664   const long long id = ReadID(m_pReader, idpos, len);
1665 
1666   if (id != libwebm::kMkvCues)
1667     return E_FILE_FORMAT_INVALID;
1668 
1669   pos += len;  // consume ID
1670   assert((segment_stop < 0) || (pos <= segment_stop));
1671 
1672   // Read Size
1673 
1674   if ((pos + 1) > avail) {
1675     len = 1;
1676     return E_BUFFER_NOT_FULL;
1677   }
1678 
1679   result = GetUIntLength(m_pReader, pos, len);
1680 
1681   if (result < 0)  // error
1682     return static_cast<long>(result);
1683 
1684   if (result > 0)  // underflow (weird)
1685   {
1686     len = 1;
1687     return E_BUFFER_NOT_FULL;
1688   }
1689 
1690   if ((segment_stop >= 0) && ((pos + len) > segment_stop))
1691     return E_FILE_FORMAT_INVALID;
1692 
1693   if ((pos + len) > avail)
1694     return E_BUFFER_NOT_FULL;
1695 
1696   const long long size = ReadUInt(m_pReader, pos, len);
1697 
1698   if (size < 0)  // error
1699     return static_cast<long>(size);
1700 
1701   if (size == 0)  // weird, although technically not illegal
1702     return 1;  // done
1703 
1704   pos += len;  // consume length of size of element
1705   assert((segment_stop < 0) || (pos <= segment_stop));
1706 
1707   // Pos now points to start of payload
1708 
1709   const long long element_stop = pos + size;
1710 
1711   if ((segment_stop >= 0) && (element_stop > segment_stop))
1712     return E_FILE_FORMAT_INVALID;
1713 
1714   if ((total >= 0) && (element_stop > total))
1715     return 1;  // don't bother parsing anymore
1716 
1717   len = static_cast<long>(size);
1718 
1719   if (element_stop > avail)
1720     return E_BUFFER_NOT_FULL;
1721 
1722   const long long element_size = element_stop - element_start;
1723 
1724   m_pCues =
1725       new (std::nothrow) Cues(this, pos, size, element_start, element_size);
1726   if (m_pCues == NULL)
1727     return -1;
1728 
1729   return 0;  // success
1730 }
1731 
ParseEntry(IMkvReader * pReader,long long start,long long size_,Entry * pEntry)1732 bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
1733                           Entry* pEntry) {
1734   if (size_ <= 0)
1735     return false;
1736 
1737   long long pos = start;
1738   const long long stop = start + size_;
1739 
1740   long len;
1741 
1742   // parse the container for the level-1 element ID
1743 
1744   const long long seekIdId = ReadID(pReader, pos, len);
1745   if (seekIdId < 0)
1746     return false;
1747 
1748   if (seekIdId != libwebm::kMkvSeekID)
1749     return false;
1750 
1751   if ((pos + len) > stop)
1752     return false;
1753 
1754   pos += len;  // consume SeekID id
1755 
1756   const long long seekIdSize = ReadUInt(pReader, pos, len);
1757 
1758   if (seekIdSize <= 0)
1759     return false;
1760 
1761   if ((pos + len) > stop)
1762     return false;
1763 
1764   pos += len;  // consume size of field
1765 
1766   if ((pos + seekIdSize) > stop)
1767     return false;
1768 
1769   // Note that the SeekId payload really is serialized
1770   // as a "Matroska integer", not as a plain binary value.
1771   // In fact, Matroska requires that ID values in the
1772   // stream exactly match the binary representation as listed
1773   // in the Matroska specification.
1774   //
1775   // This parser is more liberal, and permits IDs to have
1776   // any width.  (This could make the representation in the stream
1777   // different from what's in the spec, but it doesn't matter here,
1778   // since we always normalize "Matroska integer" values.)
1779 
1780   pEntry->id = ReadUInt(pReader, pos, len);  // payload
1781 
1782   if (pEntry->id <= 0)
1783     return false;
1784 
1785   if (len != seekIdSize)
1786     return false;
1787 
1788   pos += seekIdSize;  // consume SeekID payload
1789 
1790   const long long seekPosId = ReadID(pReader, pos, len);
1791 
1792   if (seekPosId != libwebm::kMkvSeekPosition)
1793     return false;
1794 
1795   if ((pos + len) > stop)
1796     return false;
1797 
1798   pos += len;  // consume id
1799 
1800   const long long seekPosSize = ReadUInt(pReader, pos, len);
1801 
1802   if (seekPosSize <= 0)
1803     return false;
1804 
1805   if ((pos + len) > stop)
1806     return false;
1807 
1808   pos += len;  // consume size
1809 
1810   if ((pos + seekPosSize) > stop)
1811     return false;
1812 
1813   pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
1814 
1815   if (pEntry->pos < 0)
1816     return false;
1817 
1818   pos += seekPosSize;  // consume payload
1819 
1820   if (pos != stop)
1821     return false;
1822 
1823   return true;
1824 }
1825 
Cues(Segment * pSegment,long long start_,long long size_,long long element_start,long long element_size)1826 Cues::Cues(Segment* pSegment, long long start_, long long size_,
1827            long long element_start, long long element_size)
1828     : m_pSegment(pSegment),
1829       m_start(start_),
1830       m_size(size_),
1831       m_element_start(element_start),
1832       m_element_size(element_size),
1833       m_cue_points(NULL),
1834       m_count(0),
1835       m_preload_count(0),
1836       m_pos(start_) {}
1837 
~Cues()1838 Cues::~Cues() {
1839   const long n = m_count + m_preload_count;
1840 
1841   CuePoint** p = m_cue_points;
1842   CuePoint** const q = p + n;
1843 
1844   while (p != q) {
1845     CuePoint* const pCP = *p++;
1846     assert(pCP);
1847 
1848     delete pCP;
1849   }
1850 
1851   delete[] m_cue_points;
1852 }
1853 
GetCount() const1854 long Cues::GetCount() const {
1855   if (m_cue_points == NULL)
1856     return -1;
1857 
1858   return m_count;  // TODO: really ignore preload count?
1859 }
1860 
DoneParsing() const1861 bool Cues::DoneParsing() const {
1862   const long long stop = m_start + m_size;
1863   return (m_pos >= stop);
1864 }
1865 
Init() const1866 bool Cues::Init() const {
1867   if (m_cue_points)
1868     return true;
1869 
1870   if (m_count != 0 || m_preload_count != 0)
1871     return false;
1872 
1873   IMkvReader* const pReader = m_pSegment->m_pReader;
1874 
1875   const long long stop = m_start + m_size;
1876   long long pos = m_start;
1877 
1878   long cue_points_size = 0;
1879 
1880   while (pos < stop) {
1881     const long long idpos = pos;
1882 
1883     long len;
1884 
1885     const long long id = ReadID(pReader, pos, len);
1886     if (id < 0 || (pos + len) > stop) {
1887       return false;
1888     }
1889 
1890     pos += len;  // consume ID
1891 
1892     const long long size = ReadUInt(pReader, pos, len);
1893     if (size < 0 || (pos + len > stop)) {
1894       return false;
1895     }
1896 
1897     pos += len;  // consume Size field
1898     if (pos + size > stop) {
1899       return false;
1900     }
1901 
1902     if (id == libwebm::kMkvCuePoint) {
1903       if (!PreloadCuePoint(cue_points_size, idpos))
1904         return false;
1905     }
1906 
1907     pos += size;  // skip payload
1908   }
1909   return true;
1910 }
1911 
PreloadCuePoint(long & cue_points_size,long long pos) const1912 bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
1913   if (m_count != 0)
1914     return false;
1915 
1916   if (m_preload_count >= cue_points_size) {
1917     const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
1918 
1919     CuePoint** const qq = new (std::nothrow) CuePoint*[n];
1920     if (qq == NULL)
1921       return false;
1922 
1923     CuePoint** q = qq;  // beginning of target
1924 
1925     CuePoint** p = m_cue_points;  // beginning of source
1926     CuePoint** const pp = p + m_preload_count;  // end of source
1927 
1928     while (p != pp)
1929       *q++ = *p++;
1930 
1931     delete[] m_cue_points;
1932 
1933     m_cue_points = qq;
1934     cue_points_size = n;
1935   }
1936 
1937   CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
1938   if (pCP == NULL)
1939     return false;
1940 
1941   m_cue_points[m_preload_count++] = pCP;
1942   return true;
1943 }
1944 
LoadCuePoint() const1945 bool Cues::LoadCuePoint() const {
1946   const long long stop = m_start + m_size;
1947 
1948   if (m_pos >= stop)
1949     return false;  // nothing else to do
1950 
1951   if (!Init()) {
1952     m_pos = stop;
1953     return false;
1954   }
1955 
1956   IMkvReader* const pReader = m_pSegment->m_pReader;
1957 
1958   while (m_pos < stop) {
1959     const long long idpos = m_pos;
1960 
1961     long len;
1962 
1963     const long long id = ReadID(pReader, m_pos, len);
1964     if (id < 0 || (m_pos + len) > stop)
1965       return false;
1966 
1967     m_pos += len;  // consume ID
1968 
1969     const long long size = ReadUInt(pReader, m_pos, len);
1970     if (size < 0 || (m_pos + len) > stop)
1971       return false;
1972 
1973     m_pos += len;  // consume Size field
1974     if ((m_pos + size) > stop)
1975       return false;
1976 
1977     if (id != libwebm::kMkvCuePoint) {
1978       m_pos += size;  // consume payload
1979       if (m_pos > stop)
1980         return false;
1981 
1982       continue;
1983     }
1984 
1985     if (m_preload_count < 1)
1986       return false;
1987 
1988     CuePoint* const pCP = m_cue_points[m_count];
1989     if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos)))
1990       return false;
1991 
1992     if (!pCP->Load(pReader)) {
1993       m_pos = stop;
1994       return false;
1995     }
1996     ++m_count;
1997     --m_preload_count;
1998 
1999     m_pos += size;  // consume payload
2000     if (m_pos > stop)
2001       return false;
2002 
2003     return true;  // yes, we loaded a cue point
2004   }
2005 
2006   return false;  // no, we did not load a cue point
2007 }
2008 
Find(long long time_ns,const Track * pTrack,const CuePoint * & pCP,const CuePoint::TrackPosition * & pTP) const2009 bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
2010                 const CuePoint::TrackPosition*& pTP) const {
2011   if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0)
2012     return false;
2013 
2014   CuePoint** const ii = m_cue_points;
2015   CuePoint** i = ii;
2016 
2017   CuePoint** const jj = ii + m_count;
2018   CuePoint** j = jj;
2019 
2020   pCP = *i;
2021   if (pCP == NULL)
2022     return false;
2023 
2024   if (time_ns <= pCP->GetTime(m_pSegment)) {
2025     pTP = pCP->Find(pTrack);
2026     return (pTP != NULL);
2027   }
2028 
2029   while (i < j) {
2030     // INVARIANT:
2031     //[ii, i) <= time_ns
2032     //[i, j)  ?
2033     //[j, jj) > time_ns
2034 
2035     CuePoint** const k = i + (j - i) / 2;
2036     if (k >= jj)
2037       return false;
2038 
2039     CuePoint* const pCP = *k;
2040     if (pCP == NULL)
2041       return false;
2042 
2043     const long long t = pCP->GetTime(m_pSegment);
2044 
2045     if (t <= time_ns)
2046       i = k + 1;
2047     else
2048       j = k;
2049 
2050     if (i > j)
2051       return false;
2052   }
2053 
2054   if (i != j || i > jj || i <= ii)
2055     return false;
2056 
2057   pCP = *--i;
2058 
2059   if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns)
2060     return false;
2061 
2062   // TODO: here and elsewhere, it's probably not correct to search
2063   // for the cue point with this time, and then search for a matching
2064   // track.  In principle, the matching track could be on some earlier
2065   // cue point, and with our current algorithm, we'd miss it.  To make
2066   // this bullet-proof, we'd need to create a secondary structure,
2067   // with a list of cue points that apply to a track, and then search
2068   // that track-based structure for a matching cue point.
2069 
2070   pTP = pCP->Find(pTrack);
2071   return (pTP != NULL);
2072 }
2073 
GetFirst() const2074 const CuePoint* Cues::GetFirst() const {
2075   if (m_cue_points == NULL || m_count == 0)
2076     return NULL;
2077 
2078   CuePoint* const* const pp = m_cue_points;
2079   if (pp == NULL)
2080     return NULL;
2081 
2082   CuePoint* const pCP = pp[0];
2083   if (pCP == NULL || pCP->GetTimeCode() < 0)
2084     return NULL;
2085 
2086   return pCP;
2087 }
2088 
GetLast() const2089 const CuePoint* Cues::GetLast() const {
2090   if (m_cue_points == NULL || m_count <= 0)
2091     return NULL;
2092 
2093   const long index = m_count - 1;
2094 
2095   CuePoint* const* const pp = m_cue_points;
2096   if (pp == NULL)
2097     return NULL;
2098 
2099   CuePoint* const pCP = pp[index];
2100   if (pCP == NULL || pCP->GetTimeCode() < 0)
2101     return NULL;
2102 
2103   return pCP;
2104 }
2105 
GetNext(const CuePoint * pCurr) const2106 const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
2107   if (pCurr == NULL || pCurr->GetTimeCode() < 0 || m_cue_points == NULL ||
2108       m_count < 1) {
2109     return NULL;
2110   }
2111 
2112   long index = pCurr->m_index;
2113   if (index >= m_count)
2114     return NULL;
2115 
2116   CuePoint* const* const pp = m_cue_points;
2117   if (pp == NULL || pp[index] != pCurr)
2118     return NULL;
2119 
2120   ++index;
2121 
2122   if (index >= m_count)
2123     return NULL;
2124 
2125   CuePoint* const pNext = pp[index];
2126 
2127   if (pNext == NULL || pNext->GetTimeCode() < 0)
2128     return NULL;
2129 
2130   return pNext;
2131 }
2132 
GetBlock(const CuePoint * pCP,const CuePoint::TrackPosition * pTP) const2133 const BlockEntry* Cues::GetBlock(const CuePoint* pCP,
2134                                  const CuePoint::TrackPosition* pTP) const {
2135   if (pCP == NULL || pTP == NULL)
2136     return NULL;
2137 
2138   return m_pSegment->GetBlock(*pCP, *pTP);
2139 }
2140 
GetBlock(const CuePoint & cp,const CuePoint::TrackPosition & tp)2141 const BlockEntry* Segment::GetBlock(const CuePoint& cp,
2142                                     const CuePoint::TrackPosition& tp) {
2143   Cluster** const ii = m_clusters;
2144   Cluster** i = ii;
2145 
2146   const long count = m_clusterCount + m_clusterPreloadCount;
2147 
2148   Cluster** const jj = ii + count;
2149   Cluster** j = jj;
2150 
2151   while (i < j) {
2152     // INVARIANT:
2153     //[ii, i) < pTP->m_pos
2154     //[i, j) ?
2155     //[j, jj)  > pTP->m_pos
2156 
2157     Cluster** const k = i + (j - i) / 2;
2158     assert(k < jj);
2159 
2160     Cluster* const pCluster = *k;
2161     assert(pCluster);
2162 
2163     // const long long pos_ = pCluster->m_pos;
2164     // assert(pos_);
2165     // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2166 
2167     const long long pos = pCluster->GetPosition();
2168     assert(pos >= 0);
2169 
2170     if (pos < tp.m_pos)
2171       i = k + 1;
2172     else if (pos > tp.m_pos)
2173       j = k;
2174     else
2175       return pCluster->GetEntry(cp, tp);
2176   }
2177 
2178   assert(i == j);
2179   // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2180 
2181   Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos);  //, -1);
2182   if (pCluster == NULL)
2183     return NULL;
2184 
2185   const ptrdiff_t idx = i - m_clusters;
2186 
2187   if (!PreloadCluster(pCluster, idx)) {
2188     delete pCluster;
2189     return NULL;
2190   }
2191   assert(m_clusters);
2192   assert(m_clusterPreloadCount > 0);
2193   assert(m_clusters[idx] == pCluster);
2194 
2195   return pCluster->GetEntry(cp, tp);
2196 }
2197 
FindOrPreloadCluster(long long requested_pos)2198 const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) {
2199   if (requested_pos < 0)
2200     return 0;
2201 
2202   Cluster** const ii = m_clusters;
2203   Cluster** i = ii;
2204 
2205   const long count = m_clusterCount + m_clusterPreloadCount;
2206 
2207   Cluster** const jj = ii + count;
2208   Cluster** j = jj;
2209 
2210   while (i < j) {
2211     // INVARIANT:
2212     //[ii, i) < pTP->m_pos
2213     //[i, j) ?
2214     //[j, jj)  > pTP->m_pos
2215 
2216     Cluster** const k = i + (j - i) / 2;
2217     assert(k < jj);
2218 
2219     Cluster* const pCluster = *k;
2220     assert(pCluster);
2221 
2222     // const long long pos_ = pCluster->m_pos;
2223     // assert(pos_);
2224     // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
2225 
2226     const long long pos = pCluster->GetPosition();
2227     assert(pos >= 0);
2228 
2229     if (pos < requested_pos)
2230       i = k + 1;
2231     else if (pos > requested_pos)
2232       j = k;
2233     else
2234       return pCluster;
2235   }
2236 
2237   assert(i == j);
2238   // assert(Cluster::HasBlockEntries(this, tp.m_pos));
2239 
2240   Cluster* const pCluster = Cluster::Create(this, -1, requested_pos);
2241   if (pCluster == NULL)
2242     return NULL;
2243 
2244   const ptrdiff_t idx = i - m_clusters;
2245 
2246   if (!PreloadCluster(pCluster, idx)) {
2247     delete pCluster;
2248     return NULL;
2249   }
2250   assert(m_clusters);
2251   assert(m_clusterPreloadCount > 0);
2252   assert(m_clusters[idx] == pCluster);
2253 
2254   return pCluster;
2255 }
2256 
CuePoint(long idx,long long pos)2257 CuePoint::CuePoint(long idx, long long pos)
2258     : m_element_start(0),
2259       m_element_size(0),
2260       m_index(idx),
2261       m_timecode(-1 * pos),
2262       m_track_positions(NULL),
2263       m_track_positions_count(0) {
2264   assert(pos > 0);
2265 }
2266 
~CuePoint()2267 CuePoint::~CuePoint() { delete[] m_track_positions; }
2268 
Load(IMkvReader * pReader)2269 bool CuePoint::Load(IMkvReader* pReader) {
2270   // odbgstream os;
2271   // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
2272 
2273   if (m_timecode >= 0)  // already loaded
2274     return true;
2275 
2276   assert(m_track_positions == NULL);
2277   assert(m_track_positions_count == 0);
2278 
2279   long long pos_ = -m_timecode;
2280   const long long element_start = pos_;
2281 
2282   long long stop;
2283 
2284   {
2285     long len;
2286 
2287     const long long id = ReadID(pReader, pos_, len);
2288     if (id != libwebm::kMkvCuePoint)
2289       return false;
2290 
2291     pos_ += len;  // consume ID
2292 
2293     const long long size = ReadUInt(pReader, pos_, len);
2294     assert(size >= 0);
2295 
2296     pos_ += len;  // consume Size field
2297     // pos_ now points to start of payload
2298 
2299     stop = pos_ + size;
2300   }
2301 
2302   const long long element_size = stop - element_start;
2303 
2304   long long pos = pos_;
2305 
2306   // First count number of track positions
2307 
2308   while (pos < stop) {
2309     long len;
2310 
2311     const long long id = ReadID(pReader, pos, len);
2312     if ((id < 0) || (pos + len > stop)) {
2313       return false;
2314     }
2315 
2316     pos += len;  // consume ID
2317 
2318     const long long size = ReadUInt(pReader, pos, len);
2319     if ((size < 0) || (pos + len > stop)) {
2320       return false;
2321     }
2322 
2323     pos += len;  // consume Size field
2324     if ((pos + size) > stop) {
2325       return false;
2326     }
2327 
2328     if (id == libwebm::kMkvCueTime)
2329       m_timecode = UnserializeUInt(pReader, pos, size);
2330 
2331     else if (id == libwebm::kMkvCueTrackPositions)
2332       ++m_track_positions_count;
2333 
2334     pos += size;  // consume payload
2335   }
2336 
2337   if (m_timecode < 0 || m_track_positions_count <= 0) {
2338     return false;
2339   }
2340 
2341   // os << "CuePoint::Load(cont'd): idpos=" << idpos
2342   //   << " timecode=" << m_timecode
2343   //   << endl;
2344 
2345   m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
2346   if (m_track_positions == NULL)
2347     return false;
2348 
2349   // Now parse track positions
2350 
2351   TrackPosition* p = m_track_positions;
2352   pos = pos_;
2353 
2354   while (pos < stop) {
2355     long len;
2356 
2357     const long long id = ReadID(pReader, pos, len);
2358     if (id < 0 || (pos + len) > stop)
2359       return false;
2360 
2361     pos += len;  // consume ID
2362 
2363     const long long size = ReadUInt(pReader, pos, len);
2364     assert(size >= 0);
2365     assert((pos + len) <= stop);
2366 
2367     pos += len;  // consume Size field
2368     assert((pos + size) <= stop);
2369 
2370     if (id == libwebm::kMkvCueTrackPositions) {
2371       TrackPosition& tp = *p++;
2372       if (!tp.Parse(pReader, pos, size)) {
2373         return false;
2374       }
2375     }
2376 
2377     pos += size;  // consume payload
2378     if (pos > stop)
2379       return false;
2380   }
2381 
2382   assert(size_t(p - m_track_positions) == m_track_positions_count);
2383 
2384   m_element_start = element_start;
2385   m_element_size = element_size;
2386 
2387   return true;
2388 }
2389 
Parse(IMkvReader * pReader,long long start_,long long size_)2390 bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
2391                                     long long size_) {
2392   const long long stop = start_ + size_;
2393   long long pos = start_;
2394 
2395   m_track = -1;
2396   m_pos = -1;
2397   m_block = 1;  // default
2398 
2399   while (pos < stop) {
2400     long len;
2401 
2402     const long long id = ReadID(pReader, pos, len);
2403     if ((id < 0) || ((pos + len) > stop)) {
2404       return false;
2405     }
2406 
2407     pos += len;  // consume ID
2408 
2409     const long long size = ReadUInt(pReader, pos, len);
2410     if ((size < 0) || ((pos + len) > stop)) {
2411       return false;
2412     }
2413 
2414     pos += len;  // consume Size field
2415     if ((pos + size) > stop) {
2416       return false;
2417     }
2418 
2419     if (id == libwebm::kMkvCueTrack)
2420       m_track = UnserializeUInt(pReader, pos, size);
2421     else if (id == libwebm::kMkvCueClusterPosition)
2422       m_pos = UnserializeUInt(pReader, pos, size);
2423     else if (id == libwebm::kMkvCueBlockNumber)
2424       m_block = UnserializeUInt(pReader, pos, size);
2425 
2426     pos += size;  // consume payload
2427   }
2428 
2429   if ((m_pos < 0) || (m_track <= 0)) {
2430     return false;
2431   }
2432 
2433   return true;
2434 }
2435 
Find(const Track * pTrack) const2436 const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
2437   assert(pTrack);
2438 
2439   const long long n = pTrack->GetNumber();
2440 
2441   const TrackPosition* i = m_track_positions;
2442   const TrackPosition* const j = i + m_track_positions_count;
2443 
2444   while (i != j) {
2445     const TrackPosition& p = *i++;
2446 
2447     if (p.m_track == n)
2448       return &p;
2449   }
2450 
2451   return NULL;  // no matching track number found
2452 }
2453 
GetTimeCode() const2454 long long CuePoint::GetTimeCode() const { return m_timecode; }
2455 
GetTime(const Segment * pSegment) const2456 long long CuePoint::GetTime(const Segment* pSegment) const {
2457   assert(pSegment);
2458   assert(m_timecode >= 0);
2459 
2460   const SegmentInfo* const pInfo = pSegment->GetInfo();
2461   assert(pInfo);
2462 
2463   const long long scale = pInfo->GetTimeCodeScale();
2464   assert(scale >= 1);
2465 
2466   const long long time = scale * m_timecode;
2467 
2468   return time;
2469 }
2470 
DoneParsing() const2471 bool Segment::DoneParsing() const {
2472   if (m_size < 0) {
2473     long long total, avail;
2474 
2475     const int status = m_pReader->Length(&total, &avail);
2476 
2477     if (status < 0)  // error
2478       return true;  // must assume done
2479 
2480     if (total < 0)
2481       return false;  // assume live stream
2482 
2483     return (m_pos >= total);
2484   }
2485 
2486   const long long stop = m_start + m_size;
2487 
2488   return (m_pos >= stop);
2489 }
2490 
GetFirst() const2491 const Cluster* Segment::GetFirst() const {
2492   if ((m_clusters == NULL) || (m_clusterCount <= 0))
2493     return &m_eos;
2494 
2495   Cluster* const pCluster = m_clusters[0];
2496   assert(pCluster);
2497 
2498   return pCluster;
2499 }
2500 
GetLast() const2501 const Cluster* Segment::GetLast() const {
2502   if ((m_clusters == NULL) || (m_clusterCount <= 0))
2503     return &m_eos;
2504 
2505   const long idx = m_clusterCount - 1;
2506 
2507   Cluster* const pCluster = m_clusters[idx];
2508   assert(pCluster);
2509 
2510   return pCluster;
2511 }
2512 
GetCount() const2513 unsigned long Segment::GetCount() const { return m_clusterCount; }
2514 
GetNext(const Cluster * pCurr)2515 const Cluster* Segment::GetNext(const Cluster* pCurr) {
2516   assert(pCurr);
2517   assert(pCurr != &m_eos);
2518   assert(m_clusters);
2519 
2520   long idx = pCurr->m_index;
2521 
2522   if (idx >= 0) {
2523     assert(m_clusterCount > 0);
2524     assert(idx < m_clusterCount);
2525     assert(pCurr == m_clusters[idx]);
2526 
2527     ++idx;
2528 
2529     if (idx >= m_clusterCount)
2530       return &m_eos;  // caller will LoadCluster as desired
2531 
2532     Cluster* const pNext = m_clusters[idx];
2533     assert(pNext);
2534     assert(pNext->m_index >= 0);
2535     assert(pNext->m_index == idx);
2536 
2537     return pNext;
2538   }
2539 
2540   assert(m_clusterPreloadCount > 0);
2541 
2542   long long pos = pCurr->m_element_start;
2543 
2544   assert(m_size >= 0);  // TODO
2545   const long long stop = m_start + m_size;  // end of segment
2546 
2547   {
2548     long len;
2549 
2550     long long result = GetUIntLength(m_pReader, pos, len);
2551     assert(result == 0);
2552     assert((pos + len) <= stop);  // TODO
2553     if (result != 0)
2554       return NULL;
2555 
2556     const long long id = ReadID(m_pReader, pos, len);
2557     if (id != libwebm::kMkvCluster)
2558       return NULL;
2559 
2560     pos += len;  // consume ID
2561 
2562     // Read Size
2563     result = GetUIntLength(m_pReader, pos, len);
2564     assert(result == 0);  // TODO
2565     assert((pos + len) <= stop);  // TODO
2566 
2567     const long long size = ReadUInt(m_pReader, pos, len);
2568     assert(size > 0);  // TODO
2569     // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2570 
2571     pos += len;  // consume length of size of element
2572     assert((pos + size) <= stop);  // TODO
2573 
2574     // Pos now points to start of payload
2575 
2576     pos += size;  // consume payload
2577   }
2578 
2579   long long off_next = 0;
2580 
2581   while (pos < stop) {
2582     long len;
2583 
2584     long long result = GetUIntLength(m_pReader, pos, len);
2585     assert(result == 0);
2586     assert((pos + len) <= stop);  // TODO
2587     if (result != 0)
2588       return NULL;
2589 
2590     const long long idpos = pos;  // pos of next (potential) cluster
2591 
2592     const long long id = ReadID(m_pReader, idpos, len);
2593     if (id < 0)
2594       return NULL;
2595 
2596     pos += len;  // consume ID
2597 
2598     // Read Size
2599     result = GetUIntLength(m_pReader, pos, len);
2600     assert(result == 0);  // TODO
2601     assert((pos + len) <= stop);  // TODO
2602 
2603     const long long size = ReadUInt(m_pReader, pos, len);
2604     assert(size >= 0);  // TODO
2605 
2606     pos += len;  // consume length of size of element
2607     assert((pos + size) <= stop);  // TODO
2608 
2609     // Pos now points to start of payload
2610 
2611     if (size == 0)  // weird
2612       continue;
2613 
2614     if (id == libwebm::kMkvCluster) {
2615       const long long off_next_ = idpos - m_start;
2616 
2617       long long pos_;
2618       long len_;
2619 
2620       const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_);
2621 
2622       assert(status >= 0);
2623 
2624       if (status > 0) {
2625         off_next = off_next_;
2626         break;
2627       }
2628     }
2629 
2630     pos += size;  // consume payload
2631   }
2632 
2633   if (off_next <= 0)
2634     return 0;
2635 
2636   Cluster** const ii = m_clusters + m_clusterCount;
2637   Cluster** i = ii;
2638 
2639   Cluster** const jj = ii + m_clusterPreloadCount;
2640   Cluster** j = jj;
2641 
2642   while (i < j) {
2643     // INVARIANT:
2644     //[0, i) < pos_next
2645     //[i, j) ?
2646     //[j, jj)  > pos_next
2647 
2648     Cluster** const k = i + (j - i) / 2;
2649     assert(k < jj);
2650 
2651     Cluster* const pNext = *k;
2652     assert(pNext);
2653     assert(pNext->m_index < 0);
2654 
2655     // const long long pos_ = pNext->m_pos;
2656     // assert(pos_);
2657     // pos = pos_ * ((pos_ < 0) ? -1 : 1);
2658 
2659     pos = pNext->GetPosition();
2660 
2661     if (pos < off_next)
2662       i = k + 1;
2663     else if (pos > off_next)
2664       j = k;
2665     else
2666       return pNext;
2667   }
2668 
2669   assert(i == j);
2670 
2671   Cluster* const pNext = Cluster::Create(this, -1, off_next);
2672   if (pNext == NULL)
2673     return NULL;
2674 
2675   const ptrdiff_t idx_next = i - m_clusters;  // insertion position
2676 
2677   if (!PreloadCluster(pNext, idx_next)) {
2678     delete pNext;
2679     return NULL;
2680   }
2681   assert(m_clusters);
2682   assert(idx_next < m_clusterSize);
2683   assert(m_clusters[idx_next] == pNext);
2684 
2685   return pNext;
2686 }
2687 
ParseNext(const Cluster * pCurr,const Cluster * & pResult,long long & pos,long & len)2688 long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult,
2689                         long long& pos, long& len) {
2690   assert(pCurr);
2691   assert(!pCurr->EOS());
2692   assert(m_clusters);
2693 
2694   pResult = 0;
2695 
2696   if (pCurr->m_index >= 0) {  // loaded (not merely preloaded)
2697     assert(m_clusters[pCurr->m_index] == pCurr);
2698 
2699     const long next_idx = pCurr->m_index + 1;
2700 
2701     if (next_idx < m_clusterCount) {
2702       pResult = m_clusters[next_idx];
2703       return 0;  // success
2704     }
2705 
2706     // curr cluster is last among loaded
2707 
2708     const long result = LoadCluster(pos, len);
2709 
2710     if (result < 0)  // error or underflow
2711       return result;
2712 
2713     if (result > 0)  // no more clusters
2714     {
2715       // pResult = &m_eos;
2716       return 1;
2717     }
2718 
2719     pResult = GetLast();
2720     return 0;  // success
2721   }
2722 
2723   assert(m_pos > 0);
2724 
2725   long long total, avail;
2726 
2727   long status = m_pReader->Length(&total, &avail);
2728 
2729   if (status < 0)  // error
2730     return status;
2731 
2732   assert((total < 0) || (avail <= total));
2733 
2734   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2735 
2736   // interrogate curr cluster
2737 
2738   pos = pCurr->m_element_start;
2739 
2740   if (pCurr->m_element_size >= 0)
2741     pos += pCurr->m_element_size;
2742   else {
2743     if ((pos + 1) > avail) {
2744       len = 1;
2745       return E_BUFFER_NOT_FULL;
2746     }
2747 
2748     long long result = GetUIntLength(m_pReader, pos, len);
2749 
2750     if (result < 0)  // error
2751       return static_cast<long>(result);
2752 
2753     if (result > 0)  // weird
2754       return E_BUFFER_NOT_FULL;
2755 
2756     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2757       return E_FILE_FORMAT_INVALID;
2758 
2759     if ((pos + len) > avail)
2760       return E_BUFFER_NOT_FULL;
2761 
2762     const long long id = ReadUInt(m_pReader, pos, len);
2763 
2764     if (id != libwebm::kMkvCluster)
2765       return -1;
2766 
2767     pos += len;  // consume ID
2768 
2769     // Read Size
2770 
2771     if ((pos + 1) > avail) {
2772       len = 1;
2773       return E_BUFFER_NOT_FULL;
2774     }
2775 
2776     result = GetUIntLength(m_pReader, pos, len);
2777 
2778     if (result < 0)  // error
2779       return static_cast<long>(result);
2780 
2781     if (result > 0)  // weird
2782       return E_BUFFER_NOT_FULL;
2783 
2784     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2785       return E_FILE_FORMAT_INVALID;
2786 
2787     if ((pos + len) > avail)
2788       return E_BUFFER_NOT_FULL;
2789 
2790     const long long size = ReadUInt(m_pReader, pos, len);
2791 
2792     if (size < 0)  // error
2793       return static_cast<long>(size);
2794 
2795     pos += len;  // consume size field
2796 
2797     const long long unknown_size = (1LL << (7 * len)) - 1;
2798 
2799     if (size == unknown_size)  // TODO: should never happen
2800       return E_FILE_FORMAT_INVALID;  // TODO: resolve this
2801 
2802     // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
2803 
2804     if ((segment_stop >= 0) && ((pos + size) > segment_stop))
2805       return E_FILE_FORMAT_INVALID;
2806 
2807     // Pos now points to start of payload
2808 
2809     pos += size;  // consume payload (that is, the current cluster)
2810     if (segment_stop >= 0 && pos > segment_stop)
2811       return E_FILE_FORMAT_INVALID;
2812 
2813     // By consuming the payload, we are assuming that the curr
2814     // cluster isn't interesting.  That is, we don't bother checking
2815     // whether the payload of the curr cluster is less than what
2816     // happens to be available (obtained via IMkvReader::Length).
2817     // Presumably the caller has already dispensed with the current
2818     // cluster, and really does want the next cluster.
2819   }
2820 
2821   // pos now points to just beyond the last fully-loaded cluster
2822 
2823   for (;;) {
2824     const long status = DoParseNext(pResult, pos, len);
2825 
2826     if (status <= 1)
2827       return status;
2828   }
2829 }
2830 
DoParseNext(const Cluster * & pResult,long long & pos,long & len)2831 long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
2832   long long total, avail;
2833 
2834   long status = m_pReader->Length(&total, &avail);
2835 
2836   if (status < 0)  // error
2837     return status;
2838 
2839   assert((total < 0) || (avail <= total));
2840 
2841   const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
2842 
2843   // Parse next cluster.  This is strictly a parsing activity.
2844   // Creation of a new cluster object happens later, after the
2845   // parsing is done.
2846 
2847   long long off_next = 0;
2848   long long cluster_size = -1;
2849 
2850   for (;;) {
2851     if ((total >= 0) && (pos >= total))
2852       return 1;  // EOF
2853 
2854     if ((segment_stop >= 0) && (pos >= segment_stop))
2855       return 1;  // EOF
2856 
2857     if ((pos + 1) > avail) {
2858       len = 1;
2859       return E_BUFFER_NOT_FULL;
2860     }
2861 
2862     long long result = GetUIntLength(m_pReader, pos, len);
2863 
2864     if (result < 0)  // error
2865       return static_cast<long>(result);
2866 
2867     if (result > 0)  // weird
2868       return E_BUFFER_NOT_FULL;
2869 
2870     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2871       return E_FILE_FORMAT_INVALID;
2872 
2873     if ((pos + len) > avail)
2874       return E_BUFFER_NOT_FULL;
2875 
2876     const long long idpos = pos;  // absolute
2877     const long long idoff = pos - m_start;  // relative
2878 
2879     const long long id = ReadID(m_pReader, idpos, len);  // absolute
2880 
2881     if (id < 0)  // error
2882       return static_cast<long>(id);
2883 
2884     if (id == 0)  // weird
2885       return -1;  // generic error
2886 
2887     pos += len;  // consume ID
2888 
2889     // Read Size
2890 
2891     if ((pos + 1) > avail) {
2892       len = 1;
2893       return E_BUFFER_NOT_FULL;
2894     }
2895 
2896     result = GetUIntLength(m_pReader, pos, len);
2897 
2898     if (result < 0)  // error
2899       return static_cast<long>(result);
2900 
2901     if (result > 0)  // weird
2902       return E_BUFFER_NOT_FULL;
2903 
2904     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
2905       return E_FILE_FORMAT_INVALID;
2906 
2907     if ((pos + len) > avail)
2908       return E_BUFFER_NOT_FULL;
2909 
2910     const long long size = ReadUInt(m_pReader, pos, len);
2911 
2912     if (size < 0)  // error
2913       return static_cast<long>(size);
2914 
2915     pos += len;  // consume length of size of element
2916 
2917     // Pos now points to start of payload
2918 
2919     if (size == 0)  // weird
2920       continue;
2921 
2922     const long long unknown_size = (1LL << (7 * len)) - 1;
2923 
2924     if ((segment_stop >= 0) && (size != unknown_size) &&
2925         ((pos + size) > segment_stop)) {
2926       return E_FILE_FORMAT_INVALID;
2927     }
2928 
2929     if (id == libwebm::kMkvCues) {
2930       if (size == unknown_size)
2931         return E_FILE_FORMAT_INVALID;
2932 
2933       const long long element_stop = pos + size;
2934 
2935       if ((segment_stop >= 0) && (element_stop > segment_stop))
2936         return E_FILE_FORMAT_INVALID;
2937 
2938       const long long element_start = idpos;
2939       const long long element_size = element_stop - element_start;
2940 
2941       if (m_pCues == NULL) {
2942         m_pCues = new (std::nothrow)
2943             Cues(this, pos, size, element_start, element_size);
2944         if (m_pCues == NULL)
2945           return false;
2946       }
2947 
2948       pos += size;  // consume payload
2949       if (segment_stop >= 0 && pos > segment_stop)
2950         return E_FILE_FORMAT_INVALID;
2951 
2952       continue;
2953     }
2954 
2955     if (id != libwebm::kMkvCluster) {  // not a Cluster ID
2956       if (size == unknown_size)
2957         return E_FILE_FORMAT_INVALID;
2958 
2959       pos += size;  // consume payload
2960       if (segment_stop >= 0 && pos > segment_stop)
2961         return E_FILE_FORMAT_INVALID;
2962 
2963       continue;
2964     }
2965 
2966     // We have a cluster.
2967     off_next = idoff;
2968 
2969     if (size != unknown_size)
2970       cluster_size = size;
2971 
2972     break;
2973   }
2974 
2975   assert(off_next > 0);  // have cluster
2976 
2977   // We have parsed the next cluster.
2978   // We have not created a cluster object yet.  What we need
2979   // to do now is determine whether it has already be preloaded
2980   //(in which case, an object for this cluster has already been
2981   // created), and if not, create a new cluster object.
2982 
2983   Cluster** const ii = m_clusters + m_clusterCount;
2984   Cluster** i = ii;
2985 
2986   Cluster** const jj = ii + m_clusterPreloadCount;
2987   Cluster** j = jj;
2988 
2989   while (i < j) {
2990     // INVARIANT:
2991     //[0, i) < pos_next
2992     //[i, j) ?
2993     //[j, jj)  > pos_next
2994 
2995     Cluster** const k = i + (j - i) / 2;
2996     assert(k < jj);
2997 
2998     const Cluster* const pNext = *k;
2999     assert(pNext);
3000     assert(pNext->m_index < 0);
3001 
3002     pos = pNext->GetPosition();
3003     assert(pos >= 0);
3004 
3005     if (pos < off_next)
3006       i = k + 1;
3007     else if (pos > off_next)
3008       j = k;
3009     else {
3010       pResult = pNext;
3011       return 0;  // success
3012     }
3013   }
3014 
3015   assert(i == j);
3016 
3017   long long pos_;
3018   long len_;
3019 
3020   status = Cluster::HasBlockEntries(this, off_next, pos_, len_);
3021 
3022   if (status < 0) {  // error or underflow
3023     pos = pos_;
3024     len = len_;
3025 
3026     return status;
3027   }
3028 
3029   if (status > 0) {  // means "found at least one block entry"
3030     Cluster* const pNext = Cluster::Create(this,
3031                                            -1,  // preloaded
3032                                            off_next);
3033     if (pNext == NULL)
3034       return -1;
3035 
3036     const ptrdiff_t idx_next = i - m_clusters;  // insertion position
3037 
3038     if (!PreloadCluster(pNext, idx_next)) {
3039       delete pNext;
3040       return -1;
3041     }
3042     assert(m_clusters);
3043     assert(idx_next < m_clusterSize);
3044     assert(m_clusters[idx_next] == pNext);
3045 
3046     pResult = pNext;
3047     return 0;  // success
3048   }
3049 
3050   // status == 0 means "no block entries found"
3051 
3052   if (cluster_size < 0) {  // unknown size
3053     const long long payload_pos = pos;  // absolute pos of cluster payload
3054 
3055     for (;;) {  // determine cluster size
3056       if ((total >= 0) && (pos >= total))
3057         break;
3058 
3059       if ((segment_stop >= 0) && (pos >= segment_stop))
3060         break;  // no more clusters
3061 
3062       // Read ID
3063 
3064       if ((pos + 1) > avail) {
3065         len = 1;
3066         return E_BUFFER_NOT_FULL;
3067       }
3068 
3069       long long result = GetUIntLength(m_pReader, pos, len);
3070 
3071       if (result < 0)  // error
3072         return static_cast<long>(result);
3073 
3074       if (result > 0)  // weird
3075         return E_BUFFER_NOT_FULL;
3076 
3077       if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3078         return E_FILE_FORMAT_INVALID;
3079 
3080       if ((pos + len) > avail)
3081         return E_BUFFER_NOT_FULL;
3082 
3083       const long long idpos = pos;
3084       const long long id = ReadID(m_pReader, idpos, len);
3085 
3086       if (id < 0)  // error (or underflow)
3087         return static_cast<long>(id);
3088 
3089       // This is the distinguished set of ID's we use to determine
3090       // that we have exhausted the sub-element's inside the cluster
3091       // whose ID we parsed earlier.
3092 
3093       if (id == libwebm::kMkvCluster || id == libwebm::kMkvCues)
3094         break;
3095 
3096       pos += len;  // consume ID (of sub-element)
3097 
3098       // Read Size
3099 
3100       if ((pos + 1) > avail) {
3101         len = 1;
3102         return E_BUFFER_NOT_FULL;
3103       }
3104 
3105       result = GetUIntLength(m_pReader, pos, len);
3106 
3107       if (result < 0)  // error
3108         return static_cast<long>(result);
3109 
3110       if (result > 0)  // weird
3111         return E_BUFFER_NOT_FULL;
3112 
3113       if ((segment_stop >= 0) && ((pos + len) > segment_stop))
3114         return E_FILE_FORMAT_INVALID;
3115 
3116       if ((pos + len) > avail)
3117         return E_BUFFER_NOT_FULL;
3118 
3119       const long long size = ReadUInt(m_pReader, pos, len);
3120 
3121       if (size < 0)  // error
3122         return static_cast<long>(size);
3123 
3124       pos += len;  // consume size field of element
3125 
3126       // pos now points to start of sub-element's payload
3127 
3128       if (size == 0)  // weird
3129         continue;
3130 
3131       const long long unknown_size = (1LL << (7 * len)) - 1;
3132 
3133       if (size == unknown_size)
3134         return E_FILE_FORMAT_INVALID;  // not allowed for sub-elements
3135 
3136       if ((segment_stop >= 0) && ((pos + size) > segment_stop))  // weird
3137         return E_FILE_FORMAT_INVALID;
3138 
3139       pos += size;  // consume payload of sub-element
3140       if (segment_stop >= 0 && pos > segment_stop)
3141         return E_FILE_FORMAT_INVALID;
3142     }  // determine cluster size
3143 
3144     cluster_size = pos - payload_pos;
3145     assert(cluster_size >= 0);  // TODO: handle cluster_size = 0
3146 
3147     pos = payload_pos;  // reset and re-parse original cluster
3148   }
3149 
3150   pos += cluster_size;  // consume payload
3151   if (segment_stop >= 0 && pos > segment_stop)
3152     return E_FILE_FORMAT_INVALID;
3153 
3154   return 2;  // try to find a cluster that follows next
3155 }
3156 
FindCluster(long long time_ns) const3157 const Cluster* Segment::FindCluster(long long time_ns) const {
3158   if ((m_clusters == NULL) || (m_clusterCount <= 0))
3159     return &m_eos;
3160 
3161   {
3162     Cluster* const pCluster = m_clusters[0];
3163     assert(pCluster);
3164     assert(pCluster->m_index == 0);
3165 
3166     if (time_ns <= pCluster->GetTime())
3167       return pCluster;
3168   }
3169 
3170   // Binary search of cluster array
3171 
3172   long i = 0;
3173   long j = m_clusterCount;
3174 
3175   while (i < j) {
3176     // INVARIANT:
3177     //[0, i) <= time_ns
3178     //[i, j) ?
3179     //[j, m_clusterCount)  > time_ns
3180 
3181     const long k = i + (j - i) / 2;
3182     assert(k < m_clusterCount);
3183 
3184     Cluster* const pCluster = m_clusters[k];
3185     assert(pCluster);
3186     assert(pCluster->m_index == k);
3187 
3188     const long long t = pCluster->GetTime();
3189 
3190     if (t <= time_ns)
3191       i = k + 1;
3192     else
3193       j = k;
3194 
3195     assert(i <= j);
3196   }
3197 
3198   assert(i == j);
3199   assert(i > 0);
3200   assert(i <= m_clusterCount);
3201 
3202   const long k = i - 1;
3203 
3204   Cluster* const pCluster = m_clusters[k];
3205   assert(pCluster);
3206   assert(pCluster->m_index == k);
3207   assert(pCluster->GetTime() <= time_ns);
3208 
3209   return pCluster;
3210 }
3211 
GetTracks() const3212 const Tracks* Segment::GetTracks() const { return m_pTracks; }
GetInfo() const3213 const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
GetCues() const3214 const Cues* Segment::GetCues() const { return m_pCues; }
GetChapters() const3215 const Chapters* Segment::GetChapters() const { return m_pChapters; }
GetTags() const3216 const Tags* Segment::GetTags() const { return m_pTags; }
GetSeekHead() const3217 const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
3218 
GetDuration() const3219 long long Segment::GetDuration() const {
3220   assert(m_pInfo);
3221   return m_pInfo->GetDuration();
3222 }
3223 
Chapters(Segment * pSegment,long long payload_start,long long payload_size,long long element_start,long long element_size)3224 Chapters::Chapters(Segment* pSegment, long long payload_start,
3225                    long long payload_size, long long element_start,
3226                    long long element_size)
3227     : m_pSegment(pSegment),
3228       m_start(payload_start),
3229       m_size(payload_size),
3230       m_element_start(element_start),
3231       m_element_size(element_size),
3232       m_editions(NULL),
3233       m_editions_size(0),
3234       m_editions_count(0) {}
3235 
~Chapters()3236 Chapters::~Chapters() {
3237   while (m_editions_count > 0) {
3238     Edition& e = m_editions[--m_editions_count];
3239     e.Clear();
3240   }
3241   delete[] m_editions;
3242 }
3243 
Parse()3244 long Chapters::Parse() {
3245   IMkvReader* const pReader = m_pSegment->m_pReader;
3246 
3247   long long pos = m_start;  // payload start
3248   const long long stop = pos + m_size;  // payload stop
3249 
3250   while (pos < stop) {
3251     long long id, size;
3252 
3253     long status = ParseElementHeader(pReader, pos, stop, id, size);
3254 
3255     if (status < 0)  // error
3256       return status;
3257 
3258     if (size == 0)  // weird
3259       continue;
3260 
3261     if (id == libwebm::kMkvEditionEntry) {
3262       status = ParseEdition(pos, size);
3263 
3264       if (status < 0)  // error
3265         return status;
3266     }
3267 
3268     pos += size;
3269     if (pos > stop)
3270       return E_FILE_FORMAT_INVALID;
3271   }
3272 
3273   if (pos != stop)
3274     return E_FILE_FORMAT_INVALID;
3275   return 0;
3276 }
3277 
GetEditionCount() const3278 int Chapters::GetEditionCount() const { return m_editions_count; }
3279 
GetEdition(int idx) const3280 const Chapters::Edition* Chapters::GetEdition(int idx) const {
3281   if (idx < 0)
3282     return NULL;
3283 
3284   if (idx >= m_editions_count)
3285     return NULL;
3286 
3287   return m_editions + idx;
3288 }
3289 
ExpandEditionsArray()3290 bool Chapters::ExpandEditionsArray() {
3291   if (m_editions_size > m_editions_count)
3292     return true;  // nothing else to do
3293 
3294   const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
3295 
3296   Edition* const editions = new (std::nothrow) Edition[size];
3297 
3298   if (editions == NULL)
3299     return false;
3300 
3301   for (int idx = 0; idx < m_editions_count; ++idx) {
3302     m_editions[idx].ShallowCopy(editions[idx]);
3303   }
3304 
3305   delete[] m_editions;
3306   m_editions = editions;
3307 
3308   m_editions_size = size;
3309   return true;
3310 }
3311 
ParseEdition(long long pos,long long size)3312 long Chapters::ParseEdition(long long pos, long long size) {
3313   if (!ExpandEditionsArray())
3314     return -1;
3315 
3316   Edition& e = m_editions[m_editions_count++];
3317   e.Init();
3318 
3319   return e.Parse(m_pSegment->m_pReader, pos, size);
3320 }
3321 
Edition()3322 Chapters::Edition::Edition() {}
3323 
~Edition()3324 Chapters::Edition::~Edition() {}
3325 
GetAtomCount() const3326 int Chapters::Edition::GetAtomCount() const { return m_atoms_count; }
3327 
GetAtom(int index) const3328 const Chapters::Atom* Chapters::Edition::GetAtom(int index) const {
3329   if (index < 0)
3330     return NULL;
3331 
3332   if (index >= m_atoms_count)
3333     return NULL;
3334 
3335   return m_atoms + index;
3336 }
3337 
Init()3338 void Chapters::Edition::Init() {
3339   m_atoms = NULL;
3340   m_atoms_size = 0;
3341   m_atoms_count = 0;
3342 }
3343 
ShallowCopy(Edition & rhs) const3344 void Chapters::Edition::ShallowCopy(Edition& rhs) const {
3345   rhs.m_atoms = m_atoms;
3346   rhs.m_atoms_size = m_atoms_size;
3347   rhs.m_atoms_count = m_atoms_count;
3348 }
3349 
Clear()3350 void Chapters::Edition::Clear() {
3351   while (m_atoms_count > 0) {
3352     Atom& a = m_atoms[--m_atoms_count];
3353     a.Clear();
3354   }
3355 
3356   delete[] m_atoms;
3357   m_atoms = NULL;
3358 
3359   m_atoms_size = 0;
3360 }
3361 
Parse(IMkvReader * pReader,long long pos,long long size)3362 long Chapters::Edition::Parse(IMkvReader* pReader, long long pos,
3363                               long long size) {
3364   const long long stop = pos + size;
3365 
3366   while (pos < stop) {
3367     long long id, size;
3368 
3369     long status = ParseElementHeader(pReader, pos, stop, id, size);
3370 
3371     if (status < 0)  // error
3372       return status;
3373 
3374     if (size == 0)
3375       continue;
3376 
3377     if (id == libwebm::kMkvChapterAtom) {
3378       status = ParseAtom(pReader, pos, size);
3379 
3380       if (status < 0)  // error
3381         return status;
3382     }
3383 
3384     pos += size;
3385     if (pos > stop)
3386       return E_FILE_FORMAT_INVALID;
3387   }
3388 
3389   if (pos != stop)
3390     return E_FILE_FORMAT_INVALID;
3391   return 0;
3392 }
3393 
ParseAtom(IMkvReader * pReader,long long pos,long long size)3394 long Chapters::Edition::ParseAtom(IMkvReader* pReader, long long pos,
3395                                   long long size) {
3396   if (!ExpandAtomsArray())
3397     return -1;
3398 
3399   Atom& a = m_atoms[m_atoms_count++];
3400   a.Init();
3401 
3402   return a.Parse(pReader, pos, size);
3403 }
3404 
ExpandAtomsArray()3405 bool Chapters::Edition::ExpandAtomsArray() {
3406   if (m_atoms_size > m_atoms_count)
3407     return true;  // nothing else to do
3408 
3409   const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
3410 
3411   Atom* const atoms = new (std::nothrow) Atom[size];
3412 
3413   if (atoms == NULL)
3414     return false;
3415 
3416   for (int idx = 0; idx < m_atoms_count; ++idx) {
3417     m_atoms[idx].ShallowCopy(atoms[idx]);
3418   }
3419 
3420   delete[] m_atoms;
3421   m_atoms = atoms;
3422 
3423   m_atoms_size = size;
3424   return true;
3425 }
3426 
Atom()3427 Chapters::Atom::Atom() {}
3428 
~Atom()3429 Chapters::Atom::~Atom() {}
3430 
GetUID() const3431 unsigned long long Chapters::Atom::GetUID() const { return m_uid; }
3432 
GetStringUID() const3433 const char* Chapters::Atom::GetStringUID() const { return m_string_uid; }
3434 
GetStartTimecode() const3435 long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; }
3436 
GetStopTimecode() const3437 long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; }
3438 
GetStartTime(const Chapters * pChapters) const3439 long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const {
3440   return GetTime(pChapters, m_start_timecode);
3441 }
3442 
GetStopTime(const Chapters * pChapters) const3443 long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const {
3444   return GetTime(pChapters, m_stop_timecode);
3445 }
3446 
GetDisplayCount() const3447 int Chapters::Atom::GetDisplayCount() const { return m_displays_count; }
3448 
GetDisplay(int index) const3449 const Chapters::Display* Chapters::Atom::GetDisplay(int index) const {
3450   if (index < 0)
3451     return NULL;
3452 
3453   if (index >= m_displays_count)
3454     return NULL;
3455 
3456   return m_displays + index;
3457 }
3458 
Init()3459 void Chapters::Atom::Init() {
3460   m_string_uid = NULL;
3461   m_uid = 0;
3462   m_start_timecode = -1;
3463   m_stop_timecode = -1;
3464 
3465   m_displays = NULL;
3466   m_displays_size = 0;
3467   m_displays_count = 0;
3468 }
3469 
ShallowCopy(Atom & rhs) const3470 void Chapters::Atom::ShallowCopy(Atom& rhs) const {
3471   rhs.m_string_uid = m_string_uid;
3472   rhs.m_uid = m_uid;
3473   rhs.m_start_timecode = m_start_timecode;
3474   rhs.m_stop_timecode = m_stop_timecode;
3475 
3476   rhs.m_displays = m_displays;
3477   rhs.m_displays_size = m_displays_size;
3478   rhs.m_displays_count = m_displays_count;
3479 }
3480 
Clear()3481 void Chapters::Atom::Clear() {
3482   delete[] m_string_uid;
3483   m_string_uid = NULL;
3484 
3485   while (m_displays_count > 0) {
3486     Display& d = m_displays[--m_displays_count];
3487     d.Clear();
3488   }
3489 
3490   delete[] m_displays;
3491   m_displays = NULL;
3492 
3493   m_displays_size = 0;
3494 }
3495 
Parse(IMkvReader * pReader,long long pos,long long size)3496 long Chapters::Atom::Parse(IMkvReader* pReader, long long pos, long long size) {
3497   const long long stop = pos + size;
3498 
3499   while (pos < stop) {
3500     long long id, size;
3501 
3502     long status = ParseElementHeader(pReader, pos, stop, id, size);
3503 
3504     if (status < 0)  // error
3505       return status;
3506 
3507     if (size == 0)  // 0 length payload, skip.
3508       continue;
3509 
3510     if (id == libwebm::kMkvChapterDisplay) {
3511       status = ParseDisplay(pReader, pos, size);
3512 
3513       if (status < 0)  // error
3514         return status;
3515     } else if (id == libwebm::kMkvChapterStringUID) {
3516       status = UnserializeString(pReader, pos, size, m_string_uid);
3517 
3518       if (status < 0)  // error
3519         return status;
3520     } else if (id == libwebm::kMkvChapterUID) {
3521       long long val;
3522       status = UnserializeInt(pReader, pos, size, val);
3523 
3524       if (status < 0)  // error
3525         return status;
3526 
3527       m_uid = static_cast<unsigned long long>(val);
3528     } else if (id == libwebm::kMkvChapterTimeStart) {
3529       const long long val = UnserializeUInt(pReader, pos, size);
3530 
3531       if (val < 0)  // error
3532         return static_cast<long>(val);
3533 
3534       m_start_timecode = val;
3535     } else if (id == libwebm::kMkvChapterTimeEnd) {
3536       const long long val = UnserializeUInt(pReader, pos, size);
3537 
3538       if (val < 0)  // error
3539         return static_cast<long>(val);
3540 
3541       m_stop_timecode = val;
3542     }
3543 
3544     pos += size;
3545     if (pos > stop)
3546       return E_FILE_FORMAT_INVALID;
3547   }
3548 
3549   if (pos != stop)
3550     return E_FILE_FORMAT_INVALID;
3551   return 0;
3552 }
3553 
GetTime(const Chapters * pChapters,long long timecode)3554 long long Chapters::Atom::GetTime(const Chapters* pChapters,
3555                                   long long timecode) {
3556   if (pChapters == NULL)
3557     return -1;
3558 
3559   Segment* const pSegment = pChapters->m_pSegment;
3560 
3561   if (pSegment == NULL)  // weird
3562     return -1;
3563 
3564   const SegmentInfo* const pInfo = pSegment->GetInfo();
3565 
3566   if (pInfo == NULL)
3567     return -1;
3568 
3569   const long long timecode_scale = pInfo->GetTimeCodeScale();
3570 
3571   if (timecode_scale < 1)  // weird
3572     return -1;
3573 
3574   if (timecode < 0)
3575     return -1;
3576 
3577   const long long result = timecode_scale * timecode;
3578 
3579   return result;
3580 }
3581 
ParseDisplay(IMkvReader * pReader,long long pos,long long size)3582 long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
3583                                   long long size) {
3584   if (!ExpandDisplaysArray())
3585     return -1;
3586 
3587   Display& d = m_displays[m_displays_count++];
3588   d.Init();
3589 
3590   return d.Parse(pReader, pos, size);
3591 }
3592 
ExpandDisplaysArray()3593 bool Chapters::Atom::ExpandDisplaysArray() {
3594   if (m_displays_size > m_displays_count)
3595     return true;  // nothing else to do
3596 
3597   const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
3598 
3599   Display* const displays = new (std::nothrow) Display[size];
3600 
3601   if (displays == NULL)
3602     return false;
3603 
3604   for (int idx = 0; idx < m_displays_count; ++idx) {
3605     m_displays[idx].ShallowCopy(displays[idx]);
3606   }
3607 
3608   delete[] m_displays;
3609   m_displays = displays;
3610 
3611   m_displays_size = size;
3612   return true;
3613 }
3614 
Display()3615 Chapters::Display::Display() {}
3616 
~Display()3617 Chapters::Display::~Display() {}
3618 
GetString() const3619 const char* Chapters::Display::GetString() const { return m_string; }
3620 
GetLanguage() const3621 const char* Chapters::Display::GetLanguage() const { return m_language; }
3622 
GetCountry() const3623 const char* Chapters::Display::GetCountry() const { return m_country; }
3624 
Init()3625 void Chapters::Display::Init() {
3626   m_string = NULL;
3627   m_language = NULL;
3628   m_country = NULL;
3629 }
3630 
ShallowCopy(Display & rhs) const3631 void Chapters::Display::ShallowCopy(Display& rhs) const {
3632   rhs.m_string = m_string;
3633   rhs.m_language = m_language;
3634   rhs.m_country = m_country;
3635 }
3636 
Clear()3637 void Chapters::Display::Clear() {
3638   delete[] m_string;
3639   m_string = NULL;
3640 
3641   delete[] m_language;
3642   m_language = NULL;
3643 
3644   delete[] m_country;
3645   m_country = NULL;
3646 }
3647 
Parse(IMkvReader * pReader,long long pos,long long size)3648 long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
3649                               long long size) {
3650   const long long stop = pos + size;
3651 
3652   while (pos < stop) {
3653     long long id, size;
3654 
3655     long status = ParseElementHeader(pReader, pos, stop, id, size);
3656 
3657     if (status < 0)  // error
3658       return status;
3659 
3660     if (size == 0)  // No payload.
3661       continue;
3662 
3663     if (id == libwebm::kMkvChapString) {
3664       status = UnserializeString(pReader, pos, size, m_string);
3665 
3666       if (status)
3667         return status;
3668     } else if (id == libwebm::kMkvChapLanguage) {
3669       status = UnserializeString(pReader, pos, size, m_language);
3670 
3671       if (status)
3672         return status;
3673     } else if (id == libwebm::kMkvChapCountry) {
3674       status = UnserializeString(pReader, pos, size, m_country);
3675 
3676       if (status)
3677         return status;
3678     }
3679 
3680     pos += size;
3681     if (pos > stop)
3682       return E_FILE_FORMAT_INVALID;
3683   }
3684 
3685   if (pos != stop)
3686     return E_FILE_FORMAT_INVALID;
3687   return 0;
3688 }
3689 
Tags(Segment * pSegment,long long payload_start,long long payload_size,long long element_start,long long element_size)3690 Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size,
3691            long long element_start, long long element_size)
3692     : m_pSegment(pSegment),
3693       m_start(payload_start),
3694       m_size(payload_size),
3695       m_element_start(element_start),
3696       m_element_size(element_size),
3697       m_tags(NULL),
3698       m_tags_size(0),
3699       m_tags_count(0) {}
3700 
~Tags()3701 Tags::~Tags() {
3702   while (m_tags_count > 0) {
3703     Tag& t = m_tags[--m_tags_count];
3704     t.Clear();
3705   }
3706   delete[] m_tags;
3707 }
3708 
Parse()3709 long Tags::Parse() {
3710   IMkvReader* const pReader = m_pSegment->m_pReader;
3711 
3712   long long pos = m_start;  // payload start
3713   const long long stop = pos + m_size;  // payload stop
3714 
3715   while (pos < stop) {
3716     long long id, size;
3717 
3718     long status = ParseElementHeader(pReader, pos, stop, id, size);
3719 
3720     if (status < 0)
3721       return status;
3722 
3723     if (size == 0)  // 0 length tag, read another
3724       continue;
3725 
3726     if (id == libwebm::kMkvTag) {
3727       status = ParseTag(pos, size);
3728 
3729       if (status < 0)
3730         return status;
3731     }
3732 
3733     pos += size;
3734     if (pos > stop)
3735       return E_FILE_FORMAT_INVALID;
3736   }
3737 
3738   if (pos != stop)
3739     return E_FILE_FORMAT_INVALID;
3740 
3741   return 0;
3742 }
3743 
GetTagCount() const3744 int Tags::GetTagCount() const { return m_tags_count; }
3745 
GetTag(int idx) const3746 const Tags::Tag* Tags::GetTag(int idx) const {
3747   if (idx < 0)
3748     return NULL;
3749 
3750   if (idx >= m_tags_count)
3751     return NULL;
3752 
3753   return m_tags + idx;
3754 }
3755 
ExpandTagsArray()3756 bool Tags::ExpandTagsArray() {
3757   if (m_tags_size > m_tags_count)
3758     return true;  // nothing else to do
3759 
3760   const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
3761 
3762   Tag* const tags = new (std::nothrow) Tag[size];
3763 
3764   if (tags == NULL)
3765     return false;
3766 
3767   for (int idx = 0; idx < m_tags_count; ++idx) {
3768     m_tags[idx].ShallowCopy(tags[idx]);
3769   }
3770 
3771   delete[] m_tags;
3772   m_tags = tags;
3773 
3774   m_tags_size = size;
3775   return true;
3776 }
3777 
ParseTag(long long pos,long long size)3778 long Tags::ParseTag(long long pos, long long size) {
3779   if (!ExpandTagsArray())
3780     return -1;
3781 
3782   Tag& t = m_tags[m_tags_count++];
3783   t.Init();
3784 
3785   return t.Parse(m_pSegment->m_pReader, pos, size);
3786 }
3787 
Tag()3788 Tags::Tag::Tag() {}
3789 
~Tag()3790 Tags::Tag::~Tag() {}
3791 
GetSimpleTagCount() const3792 int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
3793 
GetSimpleTag(int index) const3794 const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
3795   if (index < 0)
3796     return NULL;
3797 
3798   if (index >= m_simple_tags_count)
3799     return NULL;
3800 
3801   return m_simple_tags + index;
3802 }
3803 
Init()3804 void Tags::Tag::Init() {
3805   m_simple_tags = NULL;
3806   m_simple_tags_size = 0;
3807   m_simple_tags_count = 0;
3808 }
3809 
ShallowCopy(Tag & rhs) const3810 void Tags::Tag::ShallowCopy(Tag& rhs) const {
3811   rhs.m_simple_tags = m_simple_tags;
3812   rhs.m_simple_tags_size = m_simple_tags_size;
3813   rhs.m_simple_tags_count = m_simple_tags_count;
3814 }
3815 
Clear()3816 void Tags::Tag::Clear() {
3817   while (m_simple_tags_count > 0) {
3818     SimpleTag& d = m_simple_tags[--m_simple_tags_count];
3819     d.Clear();
3820   }
3821 
3822   delete[] m_simple_tags;
3823   m_simple_tags = NULL;
3824 
3825   m_simple_tags_size = 0;
3826 }
3827 
Parse(IMkvReader * pReader,long long pos,long long size)3828 long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) {
3829   const long long stop = pos + size;
3830 
3831   while (pos < stop) {
3832     long long id, size;
3833 
3834     long status = ParseElementHeader(pReader, pos, stop, id, size);
3835 
3836     if (status < 0)
3837       return status;
3838 
3839     if (size == 0)  // 0 length tag, read another
3840       continue;
3841 
3842     if (id == libwebm::kMkvSimpleTag) {
3843       status = ParseSimpleTag(pReader, pos, size);
3844 
3845       if (status < 0)
3846         return status;
3847     }
3848 
3849     pos += size;
3850     if (pos > stop)
3851       return E_FILE_FORMAT_INVALID;
3852   }
3853 
3854   if (pos != stop)
3855     return E_FILE_FORMAT_INVALID;
3856   return 0;
3857 }
3858 
ParseSimpleTag(IMkvReader * pReader,long long pos,long long size)3859 long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
3860                                long long size) {
3861   if (!ExpandSimpleTagsArray())
3862     return -1;
3863 
3864   SimpleTag& st = m_simple_tags[m_simple_tags_count++];
3865   st.Init();
3866 
3867   return st.Parse(pReader, pos, size);
3868 }
3869 
ExpandSimpleTagsArray()3870 bool Tags::Tag::ExpandSimpleTagsArray() {
3871   if (m_simple_tags_size > m_simple_tags_count)
3872     return true;  // nothing else to do
3873 
3874   const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
3875 
3876   SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
3877 
3878   if (displays == NULL)
3879     return false;
3880 
3881   for (int idx = 0; idx < m_simple_tags_count; ++idx) {
3882     m_simple_tags[idx].ShallowCopy(displays[idx]);
3883   }
3884 
3885   delete[] m_simple_tags;
3886   m_simple_tags = displays;
3887 
3888   m_simple_tags_size = size;
3889   return true;
3890 }
3891 
SimpleTag()3892 Tags::SimpleTag::SimpleTag() {}
3893 
~SimpleTag()3894 Tags::SimpleTag::~SimpleTag() {}
3895 
GetTagName() const3896 const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
3897 
GetTagString() const3898 const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
3899 
Init()3900 void Tags::SimpleTag::Init() {
3901   m_tag_name = NULL;
3902   m_tag_string = NULL;
3903 }
3904 
ShallowCopy(SimpleTag & rhs) const3905 void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
3906   rhs.m_tag_name = m_tag_name;
3907   rhs.m_tag_string = m_tag_string;
3908 }
3909 
Clear()3910 void Tags::SimpleTag::Clear() {
3911   delete[] m_tag_name;
3912   m_tag_name = NULL;
3913 
3914   delete[] m_tag_string;
3915   m_tag_string = NULL;
3916 }
3917 
Parse(IMkvReader * pReader,long long pos,long long size)3918 long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos,
3919                             long long size) {
3920   const long long stop = pos + size;
3921 
3922   while (pos < stop) {
3923     long long id, size;
3924 
3925     long status = ParseElementHeader(pReader, pos, stop, id, size);
3926 
3927     if (status < 0)  // error
3928       return status;
3929 
3930     if (size == 0)  // weird
3931       continue;
3932 
3933     if (id == libwebm::kMkvTagName) {
3934       status = UnserializeString(pReader, pos, size, m_tag_name);
3935 
3936       if (status)
3937         return status;
3938     } else if (id == libwebm::kMkvTagString) {
3939       status = UnserializeString(pReader, pos, size, m_tag_string);
3940 
3941       if (status)
3942         return status;
3943     }
3944 
3945     pos += size;
3946     if (pos > stop)
3947       return E_FILE_FORMAT_INVALID;
3948   }
3949 
3950   if (pos != stop)
3951     return E_FILE_FORMAT_INVALID;
3952   return 0;
3953 }
3954 
SegmentInfo(Segment * pSegment,long long start,long long size_,long long element_start,long long element_size)3955 SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
3956                          long long element_start, long long element_size)
3957     : m_pSegment(pSegment),
3958       m_start(start),
3959       m_size(size_),
3960       m_element_start(element_start),
3961       m_element_size(element_size),
3962       m_pMuxingAppAsUTF8(NULL),
3963       m_pWritingAppAsUTF8(NULL),
3964       m_pTitleAsUTF8(NULL) {}
3965 
~SegmentInfo()3966 SegmentInfo::~SegmentInfo() {
3967   delete[] m_pMuxingAppAsUTF8;
3968   m_pMuxingAppAsUTF8 = NULL;
3969 
3970   delete[] m_pWritingAppAsUTF8;
3971   m_pWritingAppAsUTF8 = NULL;
3972 
3973   delete[] m_pTitleAsUTF8;
3974   m_pTitleAsUTF8 = NULL;
3975 }
3976 
Parse()3977 long SegmentInfo::Parse() {
3978   assert(m_pMuxingAppAsUTF8 == NULL);
3979   assert(m_pWritingAppAsUTF8 == NULL);
3980   assert(m_pTitleAsUTF8 == NULL);
3981 
3982   IMkvReader* const pReader = m_pSegment->m_pReader;
3983 
3984   long long pos = m_start;
3985   const long long stop = m_start + m_size;
3986 
3987   m_timecodeScale = 1000000;
3988   m_duration = -1;
3989 
3990   while (pos < stop) {
3991     long long id, size;
3992 
3993     const long status = ParseElementHeader(pReader, pos, stop, id, size);
3994 
3995     if (status < 0)  // error
3996       return status;
3997 
3998     if (id == libwebm::kMkvTimecodeScale) {
3999       m_timecodeScale = UnserializeUInt(pReader, pos, size);
4000 
4001       if (m_timecodeScale <= 0)
4002         return E_FILE_FORMAT_INVALID;
4003     } else if (id == libwebm::kMkvDuration) {
4004       const long status = UnserializeFloat(pReader, pos, size, m_duration);
4005 
4006       if (status < 0)
4007         return status;
4008 
4009       if (m_duration < 0)
4010         return E_FILE_FORMAT_INVALID;
4011     } else if (id == libwebm::kMkvMuxingApp) {
4012       const long status =
4013           UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8);
4014 
4015       if (status)
4016         return status;
4017     } else if (id == libwebm::kMkvWritingApp) {
4018       const long status =
4019           UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8);
4020 
4021       if (status)
4022         return status;
4023     } else if (id == libwebm::kMkvTitle) {
4024       const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8);
4025 
4026       if (status)
4027         return status;
4028     }
4029 
4030     pos += size;
4031 
4032     if (pos > stop)
4033       return E_FILE_FORMAT_INVALID;
4034   }
4035 
4036   const double rollover_check = m_duration * m_timecodeScale;
4037   if (rollover_check > LLONG_MAX)
4038     return E_FILE_FORMAT_INVALID;
4039 
4040   if (pos != stop)
4041     return E_FILE_FORMAT_INVALID;
4042 
4043   return 0;
4044 }
4045 
GetTimeCodeScale() const4046 long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; }
4047 
GetDuration() const4048 long long SegmentInfo::GetDuration() const {
4049   if (m_duration < 0)
4050     return -1;
4051 
4052   assert(m_timecodeScale >= 1);
4053 
4054   const double dd = double(m_duration) * double(m_timecodeScale);
4055   const long long d = static_cast<long long>(dd);
4056 
4057   return d;
4058 }
4059 
GetMuxingAppAsUTF8() const4060 const char* SegmentInfo::GetMuxingAppAsUTF8() const {
4061   return m_pMuxingAppAsUTF8;
4062 }
4063 
GetWritingAppAsUTF8() const4064 const char* SegmentInfo::GetWritingAppAsUTF8() const {
4065   return m_pWritingAppAsUTF8;
4066 }
4067 
GetTitleAsUTF8() const4068 const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; }
4069 
4070 ///////////////////////////////////////////////////////////////
4071 // ContentEncoding element
ContentCompression()4072 ContentEncoding::ContentCompression::ContentCompression()
4073     : algo(0), settings(NULL), settings_len(0) {}
4074 
~ContentCompression()4075 ContentEncoding::ContentCompression::~ContentCompression() {
4076   delete[] settings;
4077 }
4078 
ContentEncryption()4079 ContentEncoding::ContentEncryption::ContentEncryption()
4080     : algo(0),
4081       key_id(NULL),
4082       key_id_len(0),
4083       signature(NULL),
4084       signature_len(0),
4085       sig_key_id(NULL),
4086       sig_key_id_len(0),
4087       sig_algo(0),
4088       sig_hash_algo(0) {}
4089 
~ContentEncryption()4090 ContentEncoding::ContentEncryption::~ContentEncryption() {
4091   delete[] key_id;
4092   delete[] signature;
4093   delete[] sig_key_id;
4094 }
4095 
ContentEncoding()4096 ContentEncoding::ContentEncoding()
4097     : compression_entries_(NULL),
4098       compression_entries_end_(NULL),
4099       encryption_entries_(NULL),
4100       encryption_entries_end_(NULL),
4101       encoding_order_(0),
4102       encoding_scope_(1),
4103       encoding_type_(0) {}
4104 
~ContentEncoding()4105 ContentEncoding::~ContentEncoding() {
4106   ContentCompression** comp_i = compression_entries_;
4107   ContentCompression** const comp_j = compression_entries_end_;
4108 
4109   while (comp_i != comp_j) {
4110     ContentCompression* const comp = *comp_i++;
4111     delete comp;
4112   }
4113 
4114   delete[] compression_entries_;
4115 
4116   ContentEncryption** enc_i = encryption_entries_;
4117   ContentEncryption** const enc_j = encryption_entries_end_;
4118 
4119   while (enc_i != enc_j) {
4120     ContentEncryption* const enc = *enc_i++;
4121     delete enc;
4122   }
4123 
4124   delete[] encryption_entries_;
4125 }
4126 
4127 const ContentEncoding::ContentCompression*
GetCompressionByIndex(unsigned long idx) const4128     ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
4129   const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4130   assert(count >= 0);
4131 
4132   if (idx >= static_cast<unsigned long>(count))
4133     return NULL;
4134 
4135   return compression_entries_[idx];
4136 }
4137 
GetCompressionCount() const4138 unsigned long ContentEncoding::GetCompressionCount() const {
4139   const ptrdiff_t count = compression_entries_end_ - compression_entries_;
4140   assert(count >= 0);
4141 
4142   return static_cast<unsigned long>(count);
4143 }
4144 
GetEncryptionByIndex(unsigned long idx) const4145 const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex(
4146     unsigned long idx) const {
4147   const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4148   assert(count >= 0);
4149 
4150   if (idx >= static_cast<unsigned long>(count))
4151     return NULL;
4152 
4153   return encryption_entries_[idx];
4154 }
4155 
GetEncryptionCount() const4156 unsigned long ContentEncoding::GetEncryptionCount() const {
4157   const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
4158   assert(count >= 0);
4159 
4160   return static_cast<unsigned long>(count);
4161 }
4162 
ParseContentEncAESSettingsEntry(long long start,long long size,IMkvReader * pReader,ContentEncAESSettings * aes)4163 long ContentEncoding::ParseContentEncAESSettingsEntry(
4164     long long start, long long size, IMkvReader* pReader,
4165     ContentEncAESSettings* aes) {
4166   assert(pReader);
4167   assert(aes);
4168 
4169   long long pos = start;
4170   const long long stop = start + size;
4171 
4172   while (pos < stop) {
4173     long long id, size;
4174     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4175     if (status < 0)  // error
4176       return status;
4177 
4178     if (id == libwebm::kMkvAESSettingsCipherMode) {
4179       aes->cipher_mode = UnserializeUInt(pReader, pos, size);
4180       if (aes->cipher_mode != 1)
4181         return E_FILE_FORMAT_INVALID;
4182     }
4183 
4184     pos += size;  // consume payload
4185     if (pos > stop)
4186       return E_FILE_FORMAT_INVALID;
4187   }
4188 
4189   return 0;
4190 }
4191 
ParseContentEncodingEntry(long long start,long long size,IMkvReader * pReader)4192 long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
4193                                                 IMkvReader* pReader) {
4194   assert(pReader);
4195 
4196   long long pos = start;
4197   const long long stop = start + size;
4198 
4199   // Count ContentCompression and ContentEncryption elements.
4200   int compression_count = 0;
4201   int encryption_count = 0;
4202 
4203   while (pos < stop) {
4204     long long id, size;
4205     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4206     if (status < 0)  // error
4207       return status;
4208 
4209     if (id == libwebm::kMkvContentCompression)
4210       ++compression_count;
4211 
4212     if (id == libwebm::kMkvContentEncryption)
4213       ++encryption_count;
4214 
4215     pos += size;  // consume payload
4216     if (pos > stop)
4217       return E_FILE_FORMAT_INVALID;
4218   }
4219 
4220   if (compression_count <= 0 && encryption_count <= 0)
4221     return -1;
4222 
4223   if (compression_count > 0) {
4224     compression_entries_ =
4225         new (std::nothrow) ContentCompression*[compression_count];
4226     if (!compression_entries_)
4227       return -1;
4228     compression_entries_end_ = compression_entries_;
4229   }
4230 
4231   if (encryption_count > 0) {
4232     encryption_entries_ =
4233         new (std::nothrow) ContentEncryption*[encryption_count];
4234     if (!encryption_entries_) {
4235       delete[] compression_entries_;
4236       compression_entries_ = NULL;
4237       return -1;
4238     }
4239     encryption_entries_end_ = encryption_entries_;
4240   }
4241 
4242   pos = start;
4243   while (pos < stop) {
4244     long long id, size;
4245     long status = ParseElementHeader(pReader, pos, stop, id, size);
4246     if (status < 0)  // error
4247       return status;
4248 
4249     if (id == libwebm::kMkvContentEncodingOrder) {
4250       encoding_order_ = UnserializeUInt(pReader, pos, size);
4251     } else if (id == libwebm::kMkvContentEncodingScope) {
4252       encoding_scope_ = UnserializeUInt(pReader, pos, size);
4253       if (encoding_scope_ < 1)
4254         return -1;
4255     } else if (id == libwebm::kMkvContentEncodingType) {
4256       encoding_type_ = UnserializeUInt(pReader, pos, size);
4257     } else if (id == libwebm::kMkvContentCompression) {
4258       ContentCompression* const compression =
4259           new (std::nothrow) ContentCompression();
4260       if (!compression)
4261         return -1;
4262 
4263       status = ParseCompressionEntry(pos, size, pReader, compression);
4264       if (status) {
4265         delete compression;
4266         return status;
4267       }
4268       *compression_entries_end_++ = compression;
4269     } else if (id == libwebm::kMkvContentEncryption) {
4270       ContentEncryption* const encryption =
4271           new (std::nothrow) ContentEncryption();
4272       if (!encryption)
4273         return -1;
4274 
4275       status = ParseEncryptionEntry(pos, size, pReader, encryption);
4276       if (status) {
4277         delete encryption;
4278         return status;
4279       }
4280       *encryption_entries_end_++ = encryption;
4281     }
4282 
4283     pos += size;  // consume payload
4284     if (pos > stop)
4285       return E_FILE_FORMAT_INVALID;
4286   }
4287 
4288   if (pos != stop)
4289     return E_FILE_FORMAT_INVALID;
4290   return 0;
4291 }
4292 
ParseCompressionEntry(long long start,long long size,IMkvReader * pReader,ContentCompression * compression)4293 long ContentEncoding::ParseCompressionEntry(long long start, long long size,
4294                                             IMkvReader* pReader,
4295                                             ContentCompression* compression) {
4296   assert(pReader);
4297   assert(compression);
4298 
4299   long long pos = start;
4300   const long long stop = start + size;
4301 
4302   bool valid = false;
4303 
4304   while (pos < stop) {
4305     long long id, size;
4306     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4307     if (status < 0)  // error
4308       return status;
4309 
4310     if (id == libwebm::kMkvContentCompAlgo) {
4311       long long algo = UnserializeUInt(pReader, pos, size);
4312       if (algo < 0)
4313         return E_FILE_FORMAT_INVALID;
4314       compression->algo = algo;
4315       valid = true;
4316     } else if (id == libwebm::kMkvContentCompSettings) {
4317       if (size <= 0)
4318         return E_FILE_FORMAT_INVALID;
4319 
4320       const size_t buflen = static_cast<size_t>(size);
4321       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4322       if (buf == NULL)
4323         return -1;
4324 
4325       const int read_status =
4326           pReader->Read(pos, static_cast<long>(buflen), buf);
4327       if (read_status) {
4328         delete[] buf;
4329         return status;
4330       }
4331 
4332       // There should be only one settings element per content compression.
4333       if (compression->settings != NULL) {
4334         delete[] buf;
4335         return E_FILE_FORMAT_INVALID;
4336       }
4337 
4338       compression->settings = buf;
4339       compression->settings_len = buflen;
4340     }
4341 
4342     pos += size;  // consume payload
4343     if (pos > stop)
4344       return E_FILE_FORMAT_INVALID;
4345   }
4346 
4347   // ContentCompAlgo is mandatory
4348   if (!valid)
4349     return E_FILE_FORMAT_INVALID;
4350 
4351   return 0;
4352 }
4353 
ParseEncryptionEntry(long long start,long long size,IMkvReader * pReader,ContentEncryption * encryption)4354 long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
4355                                            IMkvReader* pReader,
4356                                            ContentEncryption* encryption) {
4357   assert(pReader);
4358   assert(encryption);
4359 
4360   long long pos = start;
4361   const long long stop = start + size;
4362 
4363   while (pos < stop) {
4364     long long id, size;
4365     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4366     if (status < 0)  // error
4367       return status;
4368 
4369     if (id == libwebm::kMkvContentEncAlgo) {
4370       encryption->algo = UnserializeUInt(pReader, pos, size);
4371       if (encryption->algo != 5)
4372         return E_FILE_FORMAT_INVALID;
4373     } else if (id == libwebm::kMkvContentEncKeyID) {
4374       delete[] encryption->key_id;
4375       encryption->key_id = NULL;
4376       encryption->key_id_len = 0;
4377 
4378       if (size <= 0)
4379         return E_FILE_FORMAT_INVALID;
4380 
4381       const size_t buflen = static_cast<size_t>(size);
4382       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4383       if (buf == NULL)
4384         return -1;
4385 
4386       const int read_status =
4387           pReader->Read(pos, static_cast<long>(buflen), buf);
4388       if (read_status) {
4389         delete[] buf;
4390         return status;
4391       }
4392 
4393       encryption->key_id = buf;
4394       encryption->key_id_len = buflen;
4395     } else if (id == libwebm::kMkvContentSignature) {
4396       delete[] encryption->signature;
4397       encryption->signature = NULL;
4398       encryption->signature_len = 0;
4399 
4400       if (size <= 0)
4401         return E_FILE_FORMAT_INVALID;
4402 
4403       const size_t buflen = static_cast<size_t>(size);
4404       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4405       if (buf == NULL)
4406         return -1;
4407 
4408       const int read_status =
4409           pReader->Read(pos, static_cast<long>(buflen), buf);
4410       if (read_status) {
4411         delete[] buf;
4412         return status;
4413       }
4414 
4415       encryption->signature = buf;
4416       encryption->signature_len = buflen;
4417     } else if (id == libwebm::kMkvContentSigKeyID) {
4418       delete[] encryption->sig_key_id;
4419       encryption->sig_key_id = NULL;
4420       encryption->sig_key_id_len = 0;
4421 
4422       if (size <= 0)
4423         return E_FILE_FORMAT_INVALID;
4424 
4425       const size_t buflen = static_cast<size_t>(size);
4426       unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4427       if (buf == NULL)
4428         return -1;
4429 
4430       const int read_status =
4431           pReader->Read(pos, static_cast<long>(buflen), buf);
4432       if (read_status) {
4433         delete[] buf;
4434         return status;
4435       }
4436 
4437       encryption->sig_key_id = buf;
4438       encryption->sig_key_id_len = buflen;
4439     } else if (id == libwebm::kMkvContentSigAlgo) {
4440       encryption->sig_algo = UnserializeUInt(pReader, pos, size);
4441     } else if (id == libwebm::kMkvContentSigHashAlgo) {
4442       encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
4443     } else if (id == libwebm::kMkvContentEncAESSettings) {
4444       const long status = ParseContentEncAESSettingsEntry(
4445           pos, size, pReader, &encryption->aes_settings);
4446       if (status)
4447         return status;
4448     }
4449 
4450     pos += size;  // consume payload
4451     if (pos > stop)
4452       return E_FILE_FORMAT_INVALID;
4453   }
4454 
4455   return 0;
4456 }
4457 
Track(Segment * pSegment,long long element_start,long long element_size)4458 Track::Track(Segment* pSegment, long long element_start, long long element_size)
4459     : m_pSegment(pSegment),
4460       m_element_start(element_start),
4461       m_element_size(element_size),
4462       content_encoding_entries_(NULL),
4463       content_encoding_entries_end_(NULL) {}
4464 
~Track()4465 Track::~Track() {
4466   Info& info = const_cast<Info&>(m_info);
4467   info.Clear();
4468 
4469   ContentEncoding** i = content_encoding_entries_;
4470   ContentEncoding** const j = content_encoding_entries_end_;
4471 
4472   while (i != j) {
4473     ContentEncoding* const encoding = *i++;
4474     delete encoding;
4475   }
4476 
4477   delete[] content_encoding_entries_;
4478 }
4479 
Create(Segment * pSegment,const Info & info,long long element_start,long long element_size,Track * & pResult)4480 long Track::Create(Segment* pSegment, const Info& info, long long element_start,
4481                    long long element_size, Track*& pResult) {
4482   if (pResult)
4483     return -1;
4484 
4485   Track* const pTrack =
4486       new (std::nothrow) Track(pSegment, element_start, element_size);
4487 
4488   if (pTrack == NULL)
4489     return -1;  // generic error
4490 
4491   const int status = info.Copy(pTrack->m_info);
4492 
4493   if (status) {  // error
4494     delete pTrack;
4495     return status;
4496   }
4497 
4498   pResult = pTrack;
4499   return 0;  // success
4500 }
4501 
Info()4502 Track::Info::Info()
4503     : uid(0),
4504       defaultDuration(0),
4505       codecDelay(0),
4506       seekPreRoll(0),
4507       nameAsUTF8(NULL),
4508       language(NULL),
4509       codecId(NULL),
4510       codecNameAsUTF8(NULL),
4511       codecPrivate(NULL),
4512       codecPrivateSize(0),
4513       lacing(false) {}
4514 
~Info()4515 Track::Info::~Info() { Clear(); }
4516 
Clear()4517 void Track::Info::Clear() {
4518   delete[] nameAsUTF8;
4519   nameAsUTF8 = NULL;
4520 
4521   delete[] language;
4522   language = NULL;
4523 
4524   delete[] codecId;
4525   codecId = NULL;
4526 
4527   delete[] codecPrivate;
4528   codecPrivate = NULL;
4529   codecPrivateSize = 0;
4530 
4531   delete[] codecNameAsUTF8;
4532   codecNameAsUTF8 = NULL;
4533 }
4534 
CopyStr(char * Info::* str,Info & dst_) const4535 int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
4536   if (str == static_cast<char * Info::*>(NULL))
4537     return -1;
4538 
4539   char*& dst = dst_.*str;
4540 
4541   if (dst)  // should be NULL already
4542     return -1;
4543 
4544   const char* const src = this->*str;
4545 
4546   if (src == NULL)
4547     return 0;
4548 
4549   const size_t len = strlen(src);
4550 
4551   dst = SafeArrayAlloc<char>(1, len + 1);
4552 
4553   if (dst == NULL)
4554     return -1;
4555 
4556   strcpy(dst, src);
4557 
4558   return 0;
4559 }
4560 
Copy(Info & dst) const4561 int Track::Info::Copy(Info& dst) const {
4562   if (&dst == this)
4563     return 0;
4564 
4565   dst.type = type;
4566   dst.number = number;
4567   dst.defaultDuration = defaultDuration;
4568   dst.codecDelay = codecDelay;
4569   dst.seekPreRoll = seekPreRoll;
4570   dst.uid = uid;
4571   dst.lacing = lacing;
4572   dst.settings = settings;
4573 
4574   // We now copy the string member variables from src to dst.
4575   // This involves memory allocation so in principle the operation
4576   // can fail (indeed, that's why we have Info::Copy), so we must
4577   // report this to the caller.  An error return from this function
4578   // therefore implies that the copy was only partially successful.
4579 
4580   if (int status = CopyStr(&Info::nameAsUTF8, dst))
4581     return status;
4582 
4583   if (int status = CopyStr(&Info::language, dst))
4584     return status;
4585 
4586   if (int status = CopyStr(&Info::codecId, dst))
4587     return status;
4588 
4589   if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
4590     return status;
4591 
4592   if (codecPrivateSize > 0) {
4593     if (codecPrivate == NULL)
4594       return -1;
4595 
4596     if (dst.codecPrivate)
4597       return -1;
4598 
4599     if (dst.codecPrivateSize != 0)
4600       return -1;
4601 
4602     dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
4603 
4604     if (dst.codecPrivate == NULL)
4605       return -1;
4606 
4607     memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
4608     dst.codecPrivateSize = codecPrivateSize;
4609   }
4610 
4611   return 0;
4612 }
4613 
GetEOS() const4614 const BlockEntry* Track::GetEOS() const { return &m_eos; }
4615 
GetType() const4616 long Track::GetType() const { return m_info.type; }
4617 
GetNumber() const4618 long Track::GetNumber() const { return m_info.number; }
4619 
GetUid() const4620 unsigned long long Track::GetUid() const { return m_info.uid; }
4621 
GetNameAsUTF8() const4622 const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
4623 
GetLanguage() const4624 const char* Track::GetLanguage() const { return m_info.language; }
4625 
GetCodecNameAsUTF8() const4626 const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
4627 
GetCodecId() const4628 const char* Track::GetCodecId() const { return m_info.codecId; }
4629 
GetCodecPrivate(size_t & size) const4630 const unsigned char* Track::GetCodecPrivate(size_t& size) const {
4631   size = m_info.codecPrivateSize;
4632   return m_info.codecPrivate;
4633 }
4634 
GetLacing() const4635 bool Track::GetLacing() const { return m_info.lacing; }
4636 
GetDefaultDuration() const4637 unsigned long long Track::GetDefaultDuration() const {
4638   return m_info.defaultDuration;
4639 }
4640 
GetCodecDelay() const4641 unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
4642 
GetSeekPreRoll() const4643 unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
4644 
GetFirst(const BlockEntry * & pBlockEntry) const4645 long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
4646   const Cluster* pCluster = m_pSegment->GetFirst();
4647 
4648   for (int i = 0;;) {
4649     if (pCluster == NULL) {
4650       pBlockEntry = GetEOS();
4651       return 1;
4652     }
4653 
4654     if (pCluster->EOS()) {
4655       if (m_pSegment->DoneParsing()) {
4656         pBlockEntry = GetEOS();
4657         return 1;
4658       }
4659 
4660       pBlockEntry = 0;
4661       return E_BUFFER_NOT_FULL;
4662     }
4663 
4664     long status = pCluster->GetFirst(pBlockEntry);
4665 
4666     if (status < 0)  // error
4667       return status;
4668 
4669     if (pBlockEntry == 0) {  // empty cluster
4670       pCluster = m_pSegment->GetNext(pCluster);
4671       continue;
4672     }
4673 
4674     for (;;) {
4675       const Block* const pBlock = pBlockEntry->GetBlock();
4676       assert(pBlock);
4677 
4678       const long long tn = pBlock->GetTrackNumber();
4679 
4680       if ((tn == m_info.number) && VetEntry(pBlockEntry))
4681         return 0;
4682 
4683       const BlockEntry* pNextEntry;
4684 
4685       status = pCluster->GetNext(pBlockEntry, pNextEntry);
4686 
4687       if (status < 0)  // error
4688         return status;
4689 
4690       if (pNextEntry == 0)
4691         break;
4692 
4693       pBlockEntry = pNextEntry;
4694     }
4695 
4696     ++i;
4697 
4698     if (i >= 100)
4699       break;
4700 
4701     pCluster = m_pSegment->GetNext(pCluster);
4702   }
4703 
4704   // NOTE: if we get here, it means that we didn't find a block with
4705   // a matching track number.  We interpret that as an error (which
4706   // might be too conservative).
4707 
4708   pBlockEntry = GetEOS();  // so we can return a non-NULL value
4709   return 1;
4710 }
4711 
GetNext(const BlockEntry * pCurrEntry,const BlockEntry * & pNextEntry) const4712 long Track::GetNext(const BlockEntry* pCurrEntry,
4713                     const BlockEntry*& pNextEntry) const {
4714   assert(pCurrEntry);
4715   assert(!pCurrEntry->EOS());  //?
4716 
4717   const Block* const pCurrBlock = pCurrEntry->GetBlock();
4718   assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
4719   if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
4720     return -1;
4721 
4722   const Cluster* pCluster = pCurrEntry->GetCluster();
4723   assert(pCluster);
4724   assert(!pCluster->EOS());
4725 
4726   long status = pCluster->GetNext(pCurrEntry, pNextEntry);
4727 
4728   if (status < 0)  // error
4729     return status;
4730 
4731   for (int i = 0;;) {
4732     while (pNextEntry) {
4733       const Block* const pNextBlock = pNextEntry->GetBlock();
4734       assert(pNextBlock);
4735 
4736       if (pNextBlock->GetTrackNumber() == m_info.number)
4737         return 0;
4738 
4739       pCurrEntry = pNextEntry;
4740 
4741       status = pCluster->GetNext(pCurrEntry, pNextEntry);
4742 
4743       if (status < 0)  // error
4744         return status;
4745     }
4746 
4747     pCluster = m_pSegment->GetNext(pCluster);
4748 
4749     if (pCluster == NULL) {
4750       pNextEntry = GetEOS();
4751       return 1;
4752     }
4753 
4754     if (pCluster->EOS()) {
4755       if (m_pSegment->DoneParsing()) {
4756         pNextEntry = GetEOS();
4757         return 1;
4758       }
4759 
4760       // TODO: there is a potential O(n^2) problem here: we tell the
4761       // caller to (pre)load another cluster, which he does, but then he
4762       // calls GetNext again, which repeats the same search.  This is
4763       // a pathological case, since the only way it can happen is if
4764       // there exists a long sequence of clusters none of which contain a
4765       // block from this track.  One way around this problem is for the
4766       // caller to be smarter when he loads another cluster: don't call
4767       // us back until you have a cluster that contains a block from this
4768       // track. (Of course, that's not cheap either, since our caller
4769       // would have to scan the each cluster as it's loaded, so that
4770       // would just push back the problem.)
4771 
4772       pNextEntry = NULL;
4773       return E_BUFFER_NOT_FULL;
4774     }
4775 
4776     status = pCluster->GetFirst(pNextEntry);
4777 
4778     if (status < 0)  // error
4779       return status;
4780 
4781     if (pNextEntry == NULL)  // empty cluster
4782       continue;
4783 
4784     ++i;
4785 
4786     if (i >= 100)
4787       break;
4788   }
4789 
4790   // NOTE: if we get here, it means that we didn't find a block with
4791   // a matching track number after lots of searching, so we give
4792   // up trying.
4793 
4794   pNextEntry = GetEOS();  // so we can return a non-NULL value
4795   return 1;
4796 }
4797 
VetEntry(const BlockEntry * pBlockEntry) const4798 bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
4799   assert(pBlockEntry);
4800   const Block* const pBlock = pBlockEntry->GetBlock();
4801   assert(pBlock);
4802   assert(pBlock->GetTrackNumber() == m_info.number);
4803   if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
4804     return false;
4805 
4806   // This function is used during a seek to determine whether the
4807   // frame is a valid seek target.  This default function simply
4808   // returns true, which means all frames are valid seek targets.
4809   // It gets overridden by the VideoTrack class, because only video
4810   // keyframes can be used as seek target.
4811 
4812   return true;
4813 }
4814 
Seek(long long time_ns,const BlockEntry * & pResult) const4815 long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
4816   const long status = GetFirst(pResult);
4817 
4818   if (status < 0)  // buffer underflow, etc
4819     return status;
4820 
4821   assert(pResult);
4822 
4823   if (pResult->EOS())
4824     return 0;
4825 
4826   const Cluster* pCluster = pResult->GetCluster();
4827   assert(pCluster);
4828   assert(pCluster->GetIndex() >= 0);
4829 
4830   if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
4831     return 0;
4832 
4833   Cluster** const clusters = m_pSegment->m_clusters;
4834   assert(clusters);
4835 
4836   const long count = m_pSegment->GetCount();  // loaded only, not preloaded
4837   assert(count > 0);
4838 
4839   Cluster** const i = clusters + pCluster->GetIndex();
4840   assert(i);
4841   assert(*i == pCluster);
4842   assert(pCluster->GetTime() <= time_ns);
4843 
4844   Cluster** const j = clusters + count;
4845 
4846   Cluster** lo = i;
4847   Cluster** hi = j;
4848 
4849   while (lo < hi) {
4850     // INVARIANT:
4851     //[i, lo) <= time_ns
4852     //[lo, hi) ?
4853     //[hi, j)  > time_ns
4854 
4855     Cluster** const mid = lo + (hi - lo) / 2;
4856     assert(mid < hi);
4857 
4858     pCluster = *mid;
4859     assert(pCluster);
4860     assert(pCluster->GetIndex() >= 0);
4861     assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
4862 
4863     const long long t = pCluster->GetTime();
4864 
4865     if (t <= time_ns)
4866       lo = mid + 1;
4867     else
4868       hi = mid;
4869 
4870     assert(lo <= hi);
4871   }
4872 
4873   assert(lo == hi);
4874   assert(lo > i);
4875   assert(lo <= j);
4876 
4877   while (lo > i) {
4878     pCluster = *--lo;
4879     assert(pCluster);
4880     assert(pCluster->GetTime() <= time_ns);
4881 
4882     pResult = pCluster->GetEntry(this);
4883 
4884     if ((pResult != 0) && !pResult->EOS())
4885       return 0;
4886 
4887     // landed on empty cluster (no entries)
4888   }
4889 
4890   pResult = GetEOS();  // weird
4891   return 0;
4892 }
4893 
GetContentEncodingByIndex(unsigned long idx) const4894 const ContentEncoding* Track::GetContentEncodingByIndex(
4895     unsigned long idx) const {
4896   const ptrdiff_t count =
4897       content_encoding_entries_end_ - content_encoding_entries_;
4898   assert(count >= 0);
4899 
4900   if (idx >= static_cast<unsigned long>(count))
4901     return NULL;
4902 
4903   return content_encoding_entries_[idx];
4904 }
4905 
GetContentEncodingCount() const4906 unsigned long Track::GetContentEncodingCount() const {
4907   const ptrdiff_t count =
4908       content_encoding_entries_end_ - content_encoding_entries_;
4909   assert(count >= 0);
4910 
4911   return static_cast<unsigned long>(count);
4912 }
4913 
ParseContentEncodingsEntry(long long start,long long size)4914 long Track::ParseContentEncodingsEntry(long long start, long long size) {
4915   IMkvReader* const pReader = m_pSegment->m_pReader;
4916   assert(pReader);
4917 
4918   long long pos = start;
4919   const long long stop = start + size;
4920 
4921   // Count ContentEncoding elements.
4922   int count = 0;
4923   while (pos < stop) {
4924     long long id, size;
4925     const long status = ParseElementHeader(pReader, pos, stop, id, size);
4926     if (status < 0)  // error
4927       return status;
4928 
4929     // pos now designates start of element
4930     if (id == libwebm::kMkvContentEncoding)
4931       ++count;
4932 
4933     pos += size;  // consume payload
4934     if (pos > stop)
4935       return E_FILE_FORMAT_INVALID;
4936   }
4937 
4938   if (count <= 0)
4939     return -1;
4940 
4941   content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
4942   if (!content_encoding_entries_)
4943     return -1;
4944 
4945   content_encoding_entries_end_ = content_encoding_entries_;
4946 
4947   pos = start;
4948   while (pos < stop) {
4949     long long id, size;
4950     long status = ParseElementHeader(pReader, pos, stop, id, size);
4951     if (status < 0)  // error
4952       return status;
4953 
4954     // pos now designates start of element
4955     if (id == libwebm::kMkvContentEncoding) {
4956       ContentEncoding* const content_encoding =
4957           new (std::nothrow) ContentEncoding();
4958       if (!content_encoding)
4959         return -1;
4960 
4961       status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
4962       if (status) {
4963         delete content_encoding;
4964         return status;
4965       }
4966 
4967       *content_encoding_entries_end_++ = content_encoding;
4968     }
4969 
4970     pos += size;  // consume payload
4971     if (pos > stop)
4972       return E_FILE_FORMAT_INVALID;
4973   }
4974 
4975   if (pos != stop)
4976     return E_FILE_FORMAT_INVALID;
4977 
4978   return 0;
4979 }
4980 
EOSBlock()4981 Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
4982 
GetKind() const4983 BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
4984 
GetBlock() const4985 const Block* Track::EOSBlock::GetBlock() const { return NULL; }
4986 
Parse(IMkvReader * reader,long long read_pos,long long value_size,bool is_x,PrimaryChromaticity ** chromaticity)4987 bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos,
4988                                 long long value_size, bool is_x,
4989                                 PrimaryChromaticity** chromaticity) {
4990   if (!reader)
4991     return false;
4992 
4993   if (!*chromaticity)
4994     *chromaticity = new PrimaryChromaticity();
4995 
4996   if (!*chromaticity)
4997     return false;
4998 
4999   PrimaryChromaticity* pc = *chromaticity;
5000   float* value = is_x ? &pc->x : &pc->y;
5001 
5002   double parser_value = 0;
5003   const long long parse_status =
5004       UnserializeFloat(reader, read_pos, value_size, parser_value);
5005 
5006   if (parse_status < 0 || parser_value < FLT_MIN || parser_value > FLT_MAX)
5007     return false;
5008 
5009   *value = static_cast<float>(parser_value);
5010 
5011   if (*value < 0.0 || *value > 1.0)
5012     return false;
5013 
5014   return true;
5015 }
5016 
Parse(IMkvReader * reader,long long mm_start,long long mm_size,MasteringMetadata ** mm)5017 bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
5018                               long long mm_size, MasteringMetadata** mm) {
5019   if (!reader || *mm)
5020     return false;
5021 
5022   std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
5023   if (!mm_ptr.get())
5024     return false;
5025 
5026   const long long mm_end = mm_start + mm_size;
5027   long long read_pos = mm_start;
5028 
5029   while (read_pos < mm_end) {
5030     long long child_id = 0;
5031     long long child_size = 0;
5032 
5033     const long long status =
5034         ParseElementHeader(reader, read_pos, mm_end, child_id, child_size);
5035     if (status < 0)
5036       return false;
5037 
5038     if (child_id == libwebm::kMkvLuminanceMax) {
5039       double value = 0;
5040       const long long value_parse_status =
5041           UnserializeFloat(reader, read_pos, child_size, value);
5042       mm_ptr->luminance_max = static_cast<float>(value);
5043       if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 ||
5044           mm_ptr->luminance_max > 9999.99) {
5045         return false;
5046       }
5047     } else if (child_id == libwebm::kMkvLuminanceMin) {
5048       double value = 0;
5049       const long long value_parse_status =
5050           UnserializeFloat(reader, read_pos, child_size, value);
5051       mm_ptr->luminance_min = static_cast<float>(value);
5052       if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 ||
5053           mm_ptr->luminance_min > 999.9999) {
5054         return false;
5055       }
5056     } else {
5057       bool is_x = false;
5058       PrimaryChromaticity** chromaticity;
5059       switch (child_id) {
5060         case libwebm::kMkvPrimaryRChromaticityX:
5061         case libwebm::kMkvPrimaryRChromaticityY:
5062           is_x = child_id == libwebm::kMkvPrimaryRChromaticityX;
5063           chromaticity = &mm_ptr->r;
5064           break;
5065         case libwebm::kMkvPrimaryGChromaticityX:
5066         case libwebm::kMkvPrimaryGChromaticityY:
5067           is_x = child_id == libwebm::kMkvPrimaryGChromaticityX;
5068           chromaticity = &mm_ptr->g;
5069           break;
5070         case libwebm::kMkvPrimaryBChromaticityX:
5071         case libwebm::kMkvPrimaryBChromaticityY:
5072           is_x = child_id == libwebm::kMkvPrimaryBChromaticityX;
5073           chromaticity = &mm_ptr->b;
5074           break;
5075         case libwebm::kMkvWhitePointChromaticityX:
5076         case libwebm::kMkvWhitePointChromaticityY:
5077           is_x = child_id == libwebm::kMkvWhitePointChromaticityX;
5078           chromaticity = &mm_ptr->white_point;
5079           break;
5080         default:
5081           return false;
5082       }
5083       const bool value_parse_status = PrimaryChromaticity::Parse(
5084           reader, read_pos, child_size, is_x, chromaticity);
5085       if (!value_parse_status)
5086         return false;
5087     }
5088 
5089     read_pos += child_size;
5090     if (read_pos > mm_end)
5091       return false;
5092   }
5093 
5094   *mm = mm_ptr.release();
5095   return true;
5096 }
5097 
Parse(IMkvReader * reader,long long colour_start,long long colour_size,Colour ** colour)5098 bool Colour::Parse(IMkvReader* reader, long long colour_start,
5099                    long long colour_size, Colour** colour) {
5100   if (!reader || *colour)
5101     return false;
5102 
5103   std::auto_ptr<Colour> colour_ptr(new Colour());
5104   if (!colour_ptr.get())
5105     return false;
5106 
5107   const long long colour_end = colour_start + colour_size;
5108   long long read_pos = colour_start;
5109 
5110   while (read_pos < colour_end) {
5111     long long child_id = 0;
5112     long long child_size = 0;
5113 
5114     const long status =
5115         ParseElementHeader(reader, read_pos, colour_end, child_id, child_size);
5116     if (status < 0)
5117       return false;
5118 
5119     if (child_id == libwebm::kMkvMatrixCoefficients) {
5120       colour_ptr->matrix_coefficients =
5121           UnserializeUInt(reader, read_pos, child_size);
5122       if (colour_ptr->matrix_coefficients < 0)
5123         return false;
5124     } else if (child_id == libwebm::kMkvBitsPerChannel) {
5125       colour_ptr->bits_per_channel =
5126           UnserializeUInt(reader, read_pos, child_size);
5127       if (colour_ptr->bits_per_channel < 0)
5128         return false;
5129     } else if (child_id == libwebm::kMkvChromaSubsamplingHorz) {
5130       colour_ptr->chroma_subsampling_horz =
5131           UnserializeUInt(reader, read_pos, child_size);
5132       if (colour_ptr->chroma_subsampling_horz < 0)
5133         return false;
5134     } else if (child_id == libwebm::kMkvChromaSubsamplingVert) {
5135       colour_ptr->chroma_subsampling_vert =
5136           UnserializeUInt(reader, read_pos, child_size);
5137       if (colour_ptr->chroma_subsampling_vert < 0)
5138         return false;
5139     } else if (child_id == libwebm::kMkvCbSubsamplingHorz) {
5140       colour_ptr->cb_subsampling_horz =
5141           UnserializeUInt(reader, read_pos, child_size);
5142       if (colour_ptr->cb_subsampling_horz < 0)
5143         return false;
5144     } else if (child_id == libwebm::kMkvCbSubsamplingVert) {
5145       colour_ptr->cb_subsampling_vert =
5146           UnserializeUInt(reader, read_pos, child_size);
5147       if (colour_ptr->cb_subsampling_vert < 0)
5148         return false;
5149     } else if (child_id == libwebm::kMkvChromaSitingHorz) {
5150       colour_ptr->chroma_siting_horz =
5151           UnserializeUInt(reader, read_pos, child_size);
5152       if (colour_ptr->chroma_siting_horz < 0)
5153         return false;
5154     } else if (child_id == libwebm::kMkvChromaSitingVert) {
5155       colour_ptr->chroma_siting_vert =
5156           UnserializeUInt(reader, read_pos, child_size);
5157       if (colour_ptr->chroma_siting_vert < 0)
5158         return false;
5159     } else if (child_id == libwebm::kMkvRange) {
5160       colour_ptr->range = UnserializeUInt(reader, read_pos, child_size);
5161       if (colour_ptr->range < 0)
5162         return false;
5163     } else if (child_id == libwebm::kMkvTransferCharacteristics) {
5164       colour_ptr->transfer_characteristics =
5165           UnserializeUInt(reader, read_pos, child_size);
5166       if (colour_ptr->transfer_characteristics < 0)
5167         return false;
5168     } else if (child_id == libwebm::kMkvPrimaries) {
5169       colour_ptr->primaries = UnserializeUInt(reader, read_pos, child_size);
5170       if (colour_ptr->primaries < 0)
5171         return false;
5172     } else if (child_id == libwebm::kMkvMaxCLL) {
5173       colour_ptr->max_cll = UnserializeUInt(reader, read_pos, child_size);
5174       if (colour_ptr->max_cll < 0)
5175         return false;
5176     } else if (child_id == libwebm::kMkvMaxFALL) {
5177       colour_ptr->max_fall = UnserializeUInt(reader, read_pos, child_size);
5178       if (colour_ptr->max_fall < 0)
5179         return false;
5180     } else if (child_id == libwebm::kMkvMasteringMetadata) {
5181       if (!MasteringMetadata::Parse(reader, read_pos, child_size,
5182                                     &colour_ptr->mastering_metadata))
5183         return false;
5184     } else {
5185       return false;
5186     }
5187 
5188     read_pos += child_size;
5189     if (read_pos > colour_end)
5190       return false;
5191   }
5192   *colour = colour_ptr.release();
5193   return true;
5194 }
5195 
VideoTrack(Segment * pSegment,long long element_start,long long element_size)5196 VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
5197                        long long element_size)
5198     : Track(pSegment, element_start, element_size), m_colour(NULL) {}
5199 
~VideoTrack()5200 VideoTrack::~VideoTrack() { delete m_colour; }
5201 
Parse(Segment * pSegment,const Info & info,long long element_start,long long element_size,VideoTrack * & pResult)5202 long VideoTrack::Parse(Segment* pSegment, const Info& info,
5203                        long long element_start, long long element_size,
5204                        VideoTrack*& pResult) {
5205   if (pResult)
5206     return -1;
5207 
5208   if (info.type != Track::kVideo)
5209     return -1;
5210 
5211   long long width = 0;
5212   long long height = 0;
5213   long long display_width = 0;
5214   long long display_height = 0;
5215   long long display_unit = 0;
5216   long long stereo_mode = 0;
5217 
5218   double rate = 0.0;
5219 
5220   IMkvReader* const pReader = pSegment->m_pReader;
5221 
5222   const Settings& s = info.settings;
5223   assert(s.start >= 0);
5224   assert(s.size >= 0);
5225 
5226   long long pos = s.start;
5227   assert(pos >= 0);
5228 
5229   const long long stop = pos + s.size;
5230 
5231   Colour* colour = NULL;
5232 
5233   while (pos < stop) {
5234     long long id, size;
5235 
5236     const long status = ParseElementHeader(pReader, pos, stop, id, size);
5237 
5238     if (status < 0)  // error
5239       return status;
5240 
5241     if (id == libwebm::kMkvPixelWidth) {
5242       width = UnserializeUInt(pReader, pos, size);
5243 
5244       if (width <= 0)
5245         return E_FILE_FORMAT_INVALID;
5246     } else if (id == libwebm::kMkvPixelHeight) {
5247       height = UnserializeUInt(pReader, pos, size);
5248 
5249       if (height <= 0)
5250         return E_FILE_FORMAT_INVALID;
5251     } else if (id == libwebm::kMkvDisplayWidth) {
5252       display_width = UnserializeUInt(pReader, pos, size);
5253 
5254       if (display_width <= 0)
5255         return E_FILE_FORMAT_INVALID;
5256     } else if (id == libwebm::kMkvDisplayHeight) {
5257       display_height = UnserializeUInt(pReader, pos, size);
5258 
5259       if (display_height <= 0)
5260         return E_FILE_FORMAT_INVALID;
5261     } else if (id == libwebm::kMkvDisplayUnit) {
5262       display_unit = UnserializeUInt(pReader, pos, size);
5263 
5264       if (display_unit < 0)
5265         return E_FILE_FORMAT_INVALID;
5266     } else if (id == libwebm::kMkvStereoMode) {
5267       stereo_mode = UnserializeUInt(pReader, pos, size);
5268 
5269       if (stereo_mode < 0)
5270         return E_FILE_FORMAT_INVALID;
5271     } else if (id == libwebm::kMkvFrameRate) {
5272       const long status = UnserializeFloat(pReader, pos, size, rate);
5273 
5274       if (status < 0)
5275         return status;
5276 
5277       if (rate <= 0)
5278         return E_FILE_FORMAT_INVALID;
5279     } else if (id == libwebm::kMkvColour) {
5280       if (!Colour::Parse(pReader, pos, size, &colour))
5281         return E_FILE_FORMAT_INVALID;
5282     }
5283 
5284     pos += size;  // consume payload
5285     if (pos > stop)
5286       return E_FILE_FORMAT_INVALID;
5287   }
5288 
5289   if (pos != stop)
5290     return E_FILE_FORMAT_INVALID;
5291 
5292   VideoTrack* const pTrack =
5293       new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
5294 
5295   if (pTrack == NULL)
5296     return -1;  // generic error
5297 
5298   const int status = info.Copy(pTrack->m_info);
5299 
5300   if (status) {  // error
5301     delete pTrack;
5302     return status;
5303   }
5304 
5305   pTrack->m_width = width;
5306   pTrack->m_height = height;
5307   pTrack->m_display_width = display_width;
5308   pTrack->m_display_height = display_height;
5309   pTrack->m_display_unit = display_unit;
5310   pTrack->m_stereo_mode = stereo_mode;
5311   pTrack->m_rate = rate;
5312   pTrack->m_colour = colour;
5313 
5314   pResult = pTrack;
5315   return 0;  // success
5316 }
5317 
VetEntry(const BlockEntry * pBlockEntry) const5318 bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
5319   return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
5320 }
5321 
Seek(long long time_ns,const BlockEntry * & pResult) const5322 long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
5323   const long status = GetFirst(pResult);
5324 
5325   if (status < 0)  // buffer underflow, etc
5326     return status;
5327 
5328   assert(pResult);
5329 
5330   if (pResult->EOS())
5331     return 0;
5332 
5333   const Cluster* pCluster = pResult->GetCluster();
5334   assert(pCluster);
5335   assert(pCluster->GetIndex() >= 0);
5336 
5337   if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
5338     return 0;
5339 
5340   Cluster** const clusters = m_pSegment->m_clusters;
5341   assert(clusters);
5342 
5343   const long count = m_pSegment->GetCount();  // loaded only, not pre-loaded
5344   assert(count > 0);
5345 
5346   Cluster** const i = clusters + pCluster->GetIndex();
5347   assert(i);
5348   assert(*i == pCluster);
5349   assert(pCluster->GetTime() <= time_ns);
5350 
5351   Cluster** const j = clusters + count;
5352 
5353   Cluster** lo = i;
5354   Cluster** hi = j;
5355 
5356   while (lo < hi) {
5357     // INVARIANT:
5358     //[i, lo) <= time_ns
5359     //[lo, hi) ?
5360     //[hi, j)  > time_ns
5361 
5362     Cluster** const mid = lo + (hi - lo) / 2;
5363     assert(mid < hi);
5364 
5365     pCluster = *mid;
5366     assert(pCluster);
5367     assert(pCluster->GetIndex() >= 0);
5368     assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
5369 
5370     const long long t = pCluster->GetTime();
5371 
5372     if (t <= time_ns)
5373       lo = mid + 1;
5374     else
5375       hi = mid;
5376 
5377     assert(lo <= hi);
5378   }
5379 
5380   assert(lo == hi);
5381   assert(lo > i);
5382   assert(lo <= j);
5383 
5384   pCluster = *--lo;
5385   assert(pCluster);
5386   assert(pCluster->GetTime() <= time_ns);
5387 
5388   pResult = pCluster->GetEntry(this, time_ns);
5389 
5390   if ((pResult != 0) && !pResult->EOS())  // found a keyframe
5391     return 0;
5392 
5393   while (lo != i) {
5394     pCluster = *--lo;
5395     assert(pCluster);
5396     assert(pCluster->GetTime() <= time_ns);
5397 
5398     pResult = pCluster->GetEntry(this, time_ns);
5399 
5400     if ((pResult != 0) && !pResult->EOS())
5401       return 0;
5402   }
5403 
5404   // weird: we're on the first cluster, but no keyframe found
5405   // should never happen but we must return something anyway
5406 
5407   pResult = GetEOS();
5408   return 0;
5409 }
5410 
GetColour() const5411 Colour* VideoTrack::GetColour() const { return m_colour; }
5412 
GetWidth() const5413 long long VideoTrack::GetWidth() const { return m_width; }
5414 
GetHeight() const5415 long long VideoTrack::GetHeight() const { return m_height; }
5416 
GetDisplayWidth() const5417 long long VideoTrack::GetDisplayWidth() const {
5418   return m_display_width > 0 ? m_display_width : GetWidth();
5419 }
5420 
GetDisplayHeight() const5421 long long VideoTrack::GetDisplayHeight() const {
5422   return m_display_height > 0 ? m_display_height : GetHeight();
5423 }
5424 
GetDisplayUnit() const5425 long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
5426 
GetStereoMode() const5427 long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
5428 
GetFrameRate() const5429 double VideoTrack::GetFrameRate() const { return m_rate; }
5430 
AudioTrack(Segment * pSegment,long long element_start,long long element_size)5431 AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
5432                        long long element_size)
5433     : Track(pSegment, element_start, element_size) {}
5434 
Parse(Segment * pSegment,const Info & info,long long element_start,long long element_size,AudioTrack * & pResult)5435 long AudioTrack::Parse(Segment* pSegment, const Info& info,
5436                        long long element_start, long long element_size,
5437                        AudioTrack*& pResult) {
5438   if (pResult)
5439     return -1;
5440 
5441   if (info.type != Track::kAudio)
5442     return -1;
5443 
5444   IMkvReader* const pReader = pSegment->m_pReader;
5445 
5446   const Settings& s = info.settings;
5447   assert(s.start >= 0);
5448   assert(s.size >= 0);
5449 
5450   long long pos = s.start;
5451   assert(pos >= 0);
5452 
5453   const long long stop = pos + s.size;
5454 
5455   double rate = 8000.0;  // MKV default
5456   long long channels = 1;
5457   long long bit_depth = 0;
5458 
5459   while (pos < stop) {
5460     long long id, size;
5461 
5462     long status = ParseElementHeader(pReader, pos, stop, id, size);
5463 
5464     if (status < 0)  // error
5465       return status;
5466 
5467     if (id == libwebm::kMkvSamplingFrequency) {
5468       status = UnserializeFloat(pReader, pos, size, rate);
5469 
5470       if (status < 0)
5471         return status;
5472 
5473       if (rate <= 0)
5474         return E_FILE_FORMAT_INVALID;
5475     } else if (id == libwebm::kMkvChannels) {
5476       channels = UnserializeUInt(pReader, pos, size);
5477 
5478       if (channels <= 0)
5479         return E_FILE_FORMAT_INVALID;
5480     } else if (id == libwebm::kMkvBitDepth) {
5481       bit_depth = UnserializeUInt(pReader, pos, size);
5482 
5483       if (bit_depth <= 0)
5484         return E_FILE_FORMAT_INVALID;
5485     }
5486 
5487     pos += size;  // consume payload
5488     if (pos > stop)
5489       return E_FILE_FORMAT_INVALID;
5490   }
5491 
5492   if (pos != stop)
5493     return E_FILE_FORMAT_INVALID;
5494 
5495   AudioTrack* const pTrack =
5496       new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
5497 
5498   if (pTrack == NULL)
5499     return -1;  // generic error
5500 
5501   const int status = info.Copy(pTrack->m_info);
5502 
5503   if (status) {
5504     delete pTrack;
5505     return status;
5506   }
5507 
5508   pTrack->m_rate = rate;
5509   pTrack->m_channels = channels;
5510   pTrack->m_bitDepth = bit_depth;
5511 
5512   pResult = pTrack;
5513   return 0;  // success
5514 }
5515 
GetSamplingRate() const5516 double AudioTrack::GetSamplingRate() const { return m_rate; }
5517 
GetChannels() const5518 long long AudioTrack::GetChannels() const { return m_channels; }
5519 
GetBitDepth() const5520 long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
5521 
Tracks(Segment * pSegment,long long start,long long size_,long long element_start,long long element_size)5522 Tracks::Tracks(Segment* pSegment, long long start, long long size_,
5523                long long element_start, long long element_size)
5524     : m_pSegment(pSegment),
5525       m_start(start),
5526       m_size(size_),
5527       m_element_start(element_start),
5528       m_element_size(element_size),
5529       m_trackEntries(NULL),
5530       m_trackEntriesEnd(NULL) {}
5531 
Parse()5532 long Tracks::Parse() {
5533   assert(m_trackEntries == NULL);
5534   assert(m_trackEntriesEnd == NULL);
5535 
5536   const long long stop = m_start + m_size;
5537   IMkvReader* const pReader = m_pSegment->m_pReader;
5538 
5539   int count = 0;
5540   long long pos = m_start;
5541 
5542   while (pos < stop) {
5543     long long id, size;
5544 
5545     const long status = ParseElementHeader(pReader, pos, stop, id, size);
5546 
5547     if (status < 0)  // error
5548       return status;
5549 
5550     if (size == 0)  // weird
5551       continue;
5552 
5553     if (id == libwebm::kMkvTrackEntry)
5554       ++count;
5555 
5556     pos += size;  // consume payload
5557     if (pos > stop)
5558       return E_FILE_FORMAT_INVALID;
5559   }
5560 
5561   if (pos != stop)
5562     return E_FILE_FORMAT_INVALID;
5563 
5564   if (count <= 0)
5565     return 0;  // success
5566 
5567   m_trackEntries = new (std::nothrow) Track*[count];
5568 
5569   if (m_trackEntries == NULL)
5570     return -1;
5571 
5572   m_trackEntriesEnd = m_trackEntries;
5573 
5574   pos = m_start;
5575 
5576   while (pos < stop) {
5577     const long long element_start = pos;
5578 
5579     long long id, payload_size;
5580 
5581     const long status =
5582         ParseElementHeader(pReader, pos, stop, id, payload_size);
5583 
5584     if (status < 0)  // error
5585       return status;
5586 
5587     if (payload_size == 0)  // weird
5588       continue;
5589 
5590     const long long payload_stop = pos + payload_size;
5591     assert(payload_stop <= stop);  // checked in ParseElement
5592 
5593     const long long element_size = payload_stop - element_start;
5594 
5595     if (id == libwebm::kMkvTrackEntry) {
5596       Track*& pTrack = *m_trackEntriesEnd;
5597       pTrack = NULL;
5598 
5599       const long status = ParseTrackEntry(pos, payload_size, element_start,
5600                                           element_size, pTrack);
5601       if (status)
5602         return status;
5603 
5604       if (pTrack)
5605         ++m_trackEntriesEnd;
5606     }
5607 
5608     pos = payload_stop;
5609     if (pos > stop)
5610       return E_FILE_FORMAT_INVALID;
5611   }
5612 
5613   if (pos != stop)
5614     return E_FILE_FORMAT_INVALID;
5615 
5616   return 0;  // success
5617 }
5618 
GetTracksCount() const5619 unsigned long Tracks::GetTracksCount() const {
5620   const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
5621   assert(result >= 0);
5622 
5623   return static_cast<unsigned long>(result);
5624 }
5625 
ParseTrackEntry(long long track_start,long long track_size,long long element_start,long long element_size,Track * & pResult) const5626 long Tracks::ParseTrackEntry(long long track_start, long long track_size,
5627                              long long element_start, long long element_size,
5628                              Track*& pResult) const {
5629   if (pResult)
5630     return -1;
5631 
5632   IMkvReader* const pReader = m_pSegment->m_pReader;
5633 
5634   long long pos = track_start;
5635   const long long track_stop = track_start + track_size;
5636 
5637   Track::Info info;
5638 
5639   info.type = 0;
5640   info.number = 0;
5641   info.uid = 0;
5642   info.defaultDuration = 0;
5643 
5644   Track::Settings v;
5645   v.start = -1;
5646   v.size = -1;
5647 
5648   Track::Settings a;
5649   a.start = -1;
5650   a.size = -1;
5651 
5652   Track::Settings e;  // content_encodings_settings;
5653   e.start = -1;
5654   e.size = -1;
5655 
5656   long long lacing = 1;  // default is true
5657 
5658   while (pos < track_stop) {
5659     long long id, size;
5660 
5661     const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
5662 
5663     if (status < 0)  // error
5664       return status;
5665 
5666     if (size < 0)
5667       return E_FILE_FORMAT_INVALID;
5668 
5669     const long long start = pos;
5670 
5671     if (id == libwebm::kMkvVideo) {
5672       v.start = start;
5673       v.size = size;
5674     } else if (id == libwebm::kMkvAudio) {
5675       a.start = start;
5676       a.size = size;
5677     } else if (id == libwebm::kMkvContentEncodings) {
5678       e.start = start;
5679       e.size = size;
5680     } else if (id == libwebm::kMkvTrackUID) {
5681       if (size > 8)
5682         return E_FILE_FORMAT_INVALID;
5683 
5684       info.uid = 0;
5685 
5686       long long pos_ = start;
5687       const long long pos_end = start + size;
5688 
5689       while (pos_ != pos_end) {
5690         unsigned char b;
5691 
5692         const int status = pReader->Read(pos_, 1, &b);
5693 
5694         if (status)
5695           return status;
5696 
5697         info.uid <<= 8;
5698         info.uid |= b;
5699 
5700         ++pos_;
5701       }
5702     } else if (id == libwebm::kMkvTrackNumber) {
5703       const long long num = UnserializeUInt(pReader, pos, size);
5704 
5705       if ((num <= 0) || (num > 127))
5706         return E_FILE_FORMAT_INVALID;
5707 
5708       info.number = static_cast<long>(num);
5709     } else if (id == libwebm::kMkvTrackType) {
5710       const long long type = UnserializeUInt(pReader, pos, size);
5711 
5712       if ((type <= 0) || (type > 254))
5713         return E_FILE_FORMAT_INVALID;
5714 
5715       info.type = static_cast<long>(type);
5716     } else if (id == libwebm::kMkvName) {
5717       const long status =
5718           UnserializeString(pReader, pos, size, info.nameAsUTF8);
5719 
5720       if (status)
5721         return status;
5722     } else if (id == libwebm::kMkvLanguage) {
5723       const long status = UnserializeString(pReader, pos, size, info.language);
5724 
5725       if (status)
5726         return status;
5727     } else if (id == libwebm::kMkvDefaultDuration) {
5728       const long long duration = UnserializeUInt(pReader, pos, size);
5729 
5730       if (duration < 0)
5731         return E_FILE_FORMAT_INVALID;
5732 
5733       info.defaultDuration = static_cast<unsigned long long>(duration);
5734     } else if (id == libwebm::kMkvCodecID) {
5735       const long status = UnserializeString(pReader, pos, size, info.codecId);
5736 
5737       if (status)
5738         return status;
5739     } else if (id == libwebm::kMkvFlagLacing) {
5740       lacing = UnserializeUInt(pReader, pos, size);
5741 
5742       if ((lacing < 0) || (lacing > 1))
5743         return E_FILE_FORMAT_INVALID;
5744     } else if (id == libwebm::kMkvCodecPrivate) {
5745       delete[] info.codecPrivate;
5746       info.codecPrivate = NULL;
5747       info.codecPrivateSize = 0;
5748 
5749       const size_t buflen = static_cast<size_t>(size);
5750 
5751       if (buflen) {
5752         unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
5753 
5754         if (buf == NULL)
5755           return -1;
5756 
5757         const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
5758 
5759         if (status) {
5760           delete[] buf;
5761           return status;
5762         }
5763 
5764         info.codecPrivate = buf;
5765         info.codecPrivateSize = buflen;
5766       }
5767     } else if (id == libwebm::kMkvCodecName) {
5768       const long status =
5769           UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
5770 
5771       if (status)
5772         return status;
5773     } else if (id == libwebm::kMkvCodecDelay) {
5774       info.codecDelay = UnserializeUInt(pReader, pos, size);
5775     } else if (id == libwebm::kMkvSeekPreRoll) {
5776       info.seekPreRoll = UnserializeUInt(pReader, pos, size);
5777     }
5778 
5779     pos += size;  // consume payload
5780     if (pos > track_stop)
5781       return E_FILE_FORMAT_INVALID;
5782   }
5783 
5784   if (pos != track_stop)
5785     return E_FILE_FORMAT_INVALID;
5786 
5787   if (info.number <= 0)  // not specified
5788     return E_FILE_FORMAT_INVALID;
5789 
5790   if (GetTrackByNumber(info.number))
5791     return E_FILE_FORMAT_INVALID;
5792 
5793   if (info.type <= 0)  // not specified
5794     return E_FILE_FORMAT_INVALID;
5795 
5796   info.lacing = (lacing > 0) ? true : false;
5797 
5798   if (info.type == Track::kVideo) {
5799     if (v.start < 0)
5800       return E_FILE_FORMAT_INVALID;
5801 
5802     if (a.start >= 0)
5803       return E_FILE_FORMAT_INVALID;
5804 
5805     info.settings = v;
5806 
5807     VideoTrack* pTrack = NULL;
5808 
5809     const long status = VideoTrack::Parse(m_pSegment, info, element_start,
5810                                           element_size, pTrack);
5811 
5812     if (status)
5813       return status;
5814 
5815     pResult = pTrack;
5816     assert(pResult);
5817 
5818     if (e.start >= 0)
5819       pResult->ParseContentEncodingsEntry(e.start, e.size);
5820   } else if (info.type == Track::kAudio) {
5821     if (a.start < 0)
5822       return E_FILE_FORMAT_INVALID;
5823 
5824     if (v.start >= 0)
5825       return E_FILE_FORMAT_INVALID;
5826 
5827     info.settings = a;
5828 
5829     AudioTrack* pTrack = NULL;
5830 
5831     const long status = AudioTrack::Parse(m_pSegment, info, element_start,
5832                                           element_size, pTrack);
5833 
5834     if (status)
5835       return status;
5836 
5837     pResult = pTrack;
5838     assert(pResult);
5839 
5840     if (e.start >= 0)
5841       pResult->ParseContentEncodingsEntry(e.start, e.size);
5842   } else {
5843     // neither video nor audio - probably metadata or subtitles
5844 
5845     if (a.start >= 0)
5846       return E_FILE_FORMAT_INVALID;
5847 
5848     if (v.start >= 0)
5849       return E_FILE_FORMAT_INVALID;
5850 
5851     if (info.type == Track::kMetadata && e.start >= 0)
5852       return E_FILE_FORMAT_INVALID;
5853 
5854     info.settings.start = -1;
5855     info.settings.size = 0;
5856 
5857     Track* pTrack = NULL;
5858 
5859     const long status =
5860         Track::Create(m_pSegment, info, element_start, element_size, pTrack);
5861 
5862     if (status)
5863       return status;
5864 
5865     pResult = pTrack;
5866     assert(pResult);
5867   }
5868 
5869   return 0;  // success
5870 }
5871 
~Tracks()5872 Tracks::~Tracks() {
5873   Track** i = m_trackEntries;
5874   Track** const j = m_trackEntriesEnd;
5875 
5876   while (i != j) {
5877     Track* const pTrack = *i++;
5878     delete pTrack;
5879   }
5880 
5881   delete[] m_trackEntries;
5882 }
5883 
GetTrackByNumber(long tn) const5884 const Track* Tracks::GetTrackByNumber(long tn) const {
5885   if (tn < 0)
5886     return NULL;
5887 
5888   Track** i = m_trackEntries;
5889   Track** const j = m_trackEntriesEnd;
5890 
5891   while (i != j) {
5892     Track* const pTrack = *i++;
5893 
5894     if (pTrack == NULL)
5895       continue;
5896 
5897     if (tn == pTrack->GetNumber())
5898       return pTrack;
5899   }
5900 
5901   return NULL;  // not found
5902 }
5903 
GetTrackByIndex(unsigned long idx) const5904 const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
5905   const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
5906 
5907   if (idx >= static_cast<unsigned long>(count))
5908     return NULL;
5909 
5910   return m_trackEntries[idx];
5911 }
5912 
Load(long long & pos,long & len) const5913 long Cluster::Load(long long& pos, long& len) const {
5914   if (m_pSegment == NULL)
5915     return E_PARSE_FAILED;
5916 
5917   if (m_timecode >= 0)  // at least partially loaded
5918     return 0;
5919 
5920   if (m_pos != m_element_start || m_element_size >= 0)
5921     return E_PARSE_FAILED;
5922 
5923   IMkvReader* const pReader = m_pSegment->m_pReader;
5924   long long total, avail;
5925   const int status = pReader->Length(&total, &avail);
5926 
5927   if (status < 0)  // error
5928     return status;
5929 
5930   if (total >= 0 && (avail > total || m_pos > total))
5931     return E_FILE_FORMAT_INVALID;
5932 
5933   pos = m_pos;
5934 
5935   long long cluster_size = -1;
5936 
5937   if ((pos + 1) > avail) {
5938     len = 1;
5939     return E_BUFFER_NOT_FULL;
5940   }
5941 
5942   long long result = GetUIntLength(pReader, pos, len);
5943 
5944   if (result < 0)  // error or underflow
5945     return static_cast<long>(result);
5946 
5947   if (result > 0)
5948     return E_BUFFER_NOT_FULL;
5949 
5950   if ((pos + len) > avail)
5951     return E_BUFFER_NOT_FULL;
5952 
5953   const long long id_ = ReadID(pReader, pos, len);
5954 
5955   if (id_ < 0)  // error
5956     return static_cast<long>(id_);
5957 
5958   if (id_ != libwebm::kMkvCluster)
5959     return E_FILE_FORMAT_INVALID;
5960 
5961   pos += len;  // consume id
5962 
5963   // read cluster size
5964 
5965   if ((pos + 1) > avail) {
5966     len = 1;
5967     return E_BUFFER_NOT_FULL;
5968   }
5969 
5970   result = GetUIntLength(pReader, pos, len);
5971 
5972   if (result < 0)  // error
5973     return static_cast<long>(result);
5974 
5975   if (result > 0)
5976     return E_BUFFER_NOT_FULL;
5977 
5978   if ((pos + len) > avail)
5979     return E_BUFFER_NOT_FULL;
5980 
5981   const long long size = ReadUInt(pReader, pos, len);
5982 
5983   if (size < 0)  // error
5984     return static_cast<long>(cluster_size);
5985 
5986   if (size == 0)
5987     return E_FILE_FORMAT_INVALID;
5988 
5989   pos += len;  // consume length of size of element
5990 
5991   const long long unknown_size = (1LL << (7 * len)) - 1;
5992 
5993   if (size != unknown_size)
5994     cluster_size = size;
5995 
5996   // pos points to start of payload
5997   long long timecode = -1;
5998   long long new_pos = -1;
5999   bool bBlock = false;
6000 
6001   long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
6002 
6003   for (;;) {
6004     if ((cluster_stop >= 0) && (pos >= cluster_stop))
6005       break;
6006 
6007     // Parse ID
6008 
6009     if ((pos + 1) > avail) {
6010       len = 1;
6011       return E_BUFFER_NOT_FULL;
6012     }
6013 
6014     long long result = GetUIntLength(pReader, pos, len);
6015 
6016     if (result < 0)  // error
6017       return static_cast<long>(result);
6018 
6019     if (result > 0)
6020       return E_BUFFER_NOT_FULL;
6021 
6022     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6023       return E_FILE_FORMAT_INVALID;
6024 
6025     if ((pos + len) > avail)
6026       return E_BUFFER_NOT_FULL;
6027 
6028     const long long id = ReadID(pReader, pos, len);
6029 
6030     if (id < 0)  // error
6031       return static_cast<long>(id);
6032 
6033     if (id == 0)
6034       return E_FILE_FORMAT_INVALID;
6035 
6036     // This is the distinguished set of ID's we use to determine
6037     // that we have exhausted the sub-element's inside the cluster
6038     // whose ID we parsed earlier.
6039 
6040     if (id == libwebm::kMkvCluster)
6041       break;
6042 
6043     if (id == libwebm::kMkvCues)
6044       break;
6045 
6046     pos += len;  // consume ID field
6047 
6048     // Parse Size
6049 
6050     if ((pos + 1) > avail) {
6051       len = 1;
6052       return E_BUFFER_NOT_FULL;
6053     }
6054 
6055     result = GetUIntLength(pReader, pos, len);
6056 
6057     if (result < 0)  // error
6058       return static_cast<long>(result);
6059 
6060     if (result > 0)
6061       return E_BUFFER_NOT_FULL;
6062 
6063     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6064       return E_FILE_FORMAT_INVALID;
6065 
6066     if ((pos + len) > avail)
6067       return E_BUFFER_NOT_FULL;
6068 
6069     const long long size = ReadUInt(pReader, pos, len);
6070 
6071     if (size < 0)  // error
6072       return static_cast<long>(size);
6073 
6074     const long long unknown_size = (1LL << (7 * len)) - 1;
6075 
6076     if (size == unknown_size)
6077       return E_FILE_FORMAT_INVALID;
6078 
6079     pos += len;  // consume size field
6080 
6081     if ((cluster_stop >= 0) && (pos > cluster_stop))
6082       return E_FILE_FORMAT_INVALID;
6083 
6084     // pos now points to start of payload
6085 
6086     if (size == 0)
6087       continue;
6088 
6089     if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6090       return E_FILE_FORMAT_INVALID;
6091 
6092     if (id == libwebm::kMkvTimecode) {
6093       len = static_cast<long>(size);
6094 
6095       if ((pos + size) > avail)
6096         return E_BUFFER_NOT_FULL;
6097 
6098       timecode = UnserializeUInt(pReader, pos, size);
6099 
6100       if (timecode < 0)  // error (or underflow)
6101         return static_cast<long>(timecode);
6102 
6103       new_pos = pos + size;
6104 
6105       if (bBlock)
6106         break;
6107     } else if (id == libwebm::kMkvBlockGroup) {
6108       bBlock = true;
6109       break;
6110     } else if (id == libwebm::kMkvSimpleBlock) {
6111       bBlock = true;
6112       break;
6113     }
6114 
6115     pos += size;  // consume payload
6116     if (cluster_stop >= 0 && pos > cluster_stop)
6117       return E_FILE_FORMAT_INVALID;
6118   }
6119 
6120   if (cluster_stop >= 0 && pos > cluster_stop)
6121     return E_FILE_FORMAT_INVALID;
6122 
6123   if (timecode < 0)  // no timecode found
6124     return E_FILE_FORMAT_INVALID;
6125 
6126   if (!bBlock)
6127     return E_FILE_FORMAT_INVALID;
6128 
6129   m_pos = new_pos;  // designates position just beyond timecode payload
6130   m_timecode = timecode;  // m_timecode >= 0 means we're partially loaded
6131 
6132   if (cluster_size >= 0)
6133     m_element_size = cluster_stop - m_element_start;
6134 
6135   return 0;
6136 }
6137 
Parse(long long & pos,long & len) const6138 long Cluster::Parse(long long& pos, long& len) const {
6139   long status = Load(pos, len);
6140 
6141   if (status < 0)
6142     return status;
6143 
6144   if (m_pos < m_element_start || m_timecode < 0)
6145     return E_PARSE_FAILED;
6146 
6147   const long long cluster_stop =
6148       (m_element_size < 0) ? -1 : m_element_start + m_element_size;
6149 
6150   if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
6151     return 1;  // nothing else to do
6152 
6153   IMkvReader* const pReader = m_pSegment->m_pReader;
6154 
6155   long long total, avail;
6156 
6157   status = pReader->Length(&total, &avail);
6158 
6159   if (status < 0)  // error
6160     return status;
6161 
6162   if (total >= 0 && avail > total)
6163     return E_FILE_FORMAT_INVALID;
6164 
6165   pos = m_pos;
6166 
6167   for (;;) {
6168     if ((cluster_stop >= 0) && (pos >= cluster_stop))
6169       break;
6170 
6171     if ((total >= 0) && (pos >= total)) {
6172       if (m_element_size < 0)
6173         m_element_size = pos - m_element_start;
6174 
6175       break;
6176     }
6177 
6178     // Parse ID
6179 
6180     if ((pos + 1) > avail) {
6181       len = 1;
6182       return E_BUFFER_NOT_FULL;
6183     }
6184 
6185     long long result = GetUIntLength(pReader, pos, len);
6186 
6187     if (result < 0)  // error
6188       return static_cast<long>(result);
6189 
6190     if (result > 0)
6191       return E_BUFFER_NOT_FULL;
6192 
6193     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6194       return E_FILE_FORMAT_INVALID;
6195 
6196     if ((pos + len) > avail)
6197       return E_BUFFER_NOT_FULL;
6198 
6199     const long long id = ReadID(pReader, pos, len);
6200 
6201     if (id < 0)
6202       return E_FILE_FORMAT_INVALID;
6203 
6204     // This is the distinguished set of ID's we use to determine
6205     // that we have exhausted the sub-element's inside the cluster
6206     // whose ID we parsed earlier.
6207 
6208     if ((id == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) {
6209       if (m_element_size < 0)
6210         m_element_size = pos - m_element_start;
6211 
6212       break;
6213     }
6214 
6215     pos += len;  // consume ID field
6216 
6217     // Parse Size
6218 
6219     if ((pos + 1) > avail) {
6220       len = 1;
6221       return E_BUFFER_NOT_FULL;
6222     }
6223 
6224     result = GetUIntLength(pReader, pos, len);
6225 
6226     if (result < 0)  // error
6227       return static_cast<long>(result);
6228 
6229     if (result > 0)
6230       return E_BUFFER_NOT_FULL;
6231 
6232     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6233       return E_FILE_FORMAT_INVALID;
6234 
6235     if ((pos + len) > avail)
6236       return E_BUFFER_NOT_FULL;
6237 
6238     const long long size = ReadUInt(pReader, pos, len);
6239 
6240     if (size < 0)  // error
6241       return static_cast<long>(size);
6242 
6243     const long long unknown_size = (1LL << (7 * len)) - 1;
6244 
6245     if (size == unknown_size)
6246       return E_FILE_FORMAT_INVALID;
6247 
6248     pos += len;  // consume size field
6249 
6250     if ((cluster_stop >= 0) && (pos > cluster_stop))
6251       return E_FILE_FORMAT_INVALID;
6252 
6253     // pos now points to start of payload
6254 
6255     if (size == 0)
6256       continue;
6257 
6258     // const long long block_start = pos;
6259     const long long block_stop = pos + size;
6260 
6261     if (cluster_stop >= 0) {
6262       if (block_stop > cluster_stop) {
6263         if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) {
6264           return E_FILE_FORMAT_INVALID;
6265         }
6266 
6267         pos = cluster_stop;
6268         break;
6269       }
6270     } else if ((total >= 0) && (block_stop > total)) {
6271       m_element_size = total - m_element_start;
6272       pos = total;
6273       break;
6274     } else if (block_stop > avail) {
6275       len = static_cast<long>(size);
6276       return E_BUFFER_NOT_FULL;
6277     }
6278 
6279     Cluster* const this_ = const_cast<Cluster*>(this);
6280 
6281     if (id == libwebm::kMkvBlockGroup)
6282       return this_->ParseBlockGroup(size, pos, len);
6283 
6284     if (id == libwebm::kMkvSimpleBlock)
6285       return this_->ParseSimpleBlock(size, pos, len);
6286 
6287     pos += size;  // consume payload
6288     if (cluster_stop >= 0 && pos > cluster_stop)
6289       return E_FILE_FORMAT_INVALID;
6290   }
6291 
6292   if (m_element_size < 1)
6293     return E_FILE_FORMAT_INVALID;
6294 
6295   m_pos = pos;
6296   if (cluster_stop >= 0 && m_pos > cluster_stop)
6297     return E_FILE_FORMAT_INVALID;
6298 
6299   if (m_entries_count > 0) {
6300     const long idx = m_entries_count - 1;
6301 
6302     const BlockEntry* const pLast = m_entries[idx];
6303     if (pLast == NULL)
6304       return E_PARSE_FAILED;
6305 
6306     const Block* const pBlock = pLast->GetBlock();
6307     if (pBlock == NULL)
6308       return E_PARSE_FAILED;
6309 
6310     const long long start = pBlock->m_start;
6311 
6312     if ((total >= 0) && (start > total))
6313       return E_PARSE_FAILED;  // defend against trucated stream
6314 
6315     const long long size = pBlock->m_size;
6316 
6317     const long long stop = start + size;
6318     if (cluster_stop >= 0 && stop > cluster_stop)
6319       return E_FILE_FORMAT_INVALID;
6320 
6321     if ((total >= 0) && (stop > total))
6322       return E_PARSE_FAILED;  // defend against trucated stream
6323   }
6324 
6325   return 1;  // no more entries
6326 }
6327 
ParseSimpleBlock(long long block_size,long long & pos,long & len)6328 long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
6329                                long& len) {
6330   const long long block_start = pos;
6331   const long long block_stop = pos + block_size;
6332 
6333   IMkvReader* const pReader = m_pSegment->m_pReader;
6334 
6335   long long total, avail;
6336 
6337   long status = pReader->Length(&total, &avail);
6338 
6339   if (status < 0)  // error
6340     return status;
6341 
6342   assert((total < 0) || (avail <= total));
6343 
6344   // parse track number
6345 
6346   if ((pos + 1) > avail) {
6347     len = 1;
6348     return E_BUFFER_NOT_FULL;
6349   }
6350 
6351   long long result = GetUIntLength(pReader, pos, len);
6352 
6353   if (result < 0)  // error
6354     return static_cast<long>(result);
6355 
6356   if (result > 0)  // weird
6357     return E_BUFFER_NOT_FULL;
6358 
6359   if ((pos + len) > block_stop)
6360     return E_FILE_FORMAT_INVALID;
6361 
6362   if ((pos + len) > avail)
6363     return E_BUFFER_NOT_FULL;
6364 
6365   const long long track = ReadUInt(pReader, pos, len);
6366 
6367   if (track < 0)  // error
6368     return static_cast<long>(track);
6369 
6370   if (track == 0)
6371     return E_FILE_FORMAT_INVALID;
6372 
6373   pos += len;  // consume track number
6374 
6375   if ((pos + 2) > block_stop)
6376     return E_FILE_FORMAT_INVALID;
6377 
6378   if ((pos + 2) > avail) {
6379     len = 2;
6380     return E_BUFFER_NOT_FULL;
6381   }
6382 
6383   pos += 2;  // consume timecode
6384 
6385   if ((pos + 1) > block_stop)
6386     return E_FILE_FORMAT_INVALID;
6387 
6388   if ((pos + 1) > avail) {
6389     len = 1;
6390     return E_BUFFER_NOT_FULL;
6391   }
6392 
6393   unsigned char flags;
6394 
6395   status = pReader->Read(pos, 1, &flags);
6396 
6397   if (status < 0) {  // error or underflow
6398     len = 1;
6399     return status;
6400   }
6401 
6402   ++pos;  // consume flags byte
6403   assert(pos <= avail);
6404 
6405   if (pos >= block_stop)
6406     return E_FILE_FORMAT_INVALID;
6407 
6408   const int lacing = int(flags & 0x06) >> 1;
6409 
6410   if ((lacing != 0) && (block_stop > avail)) {
6411     len = static_cast<long>(block_stop - pos);
6412     return E_BUFFER_NOT_FULL;
6413   }
6414 
6415   status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size,
6416                        0);  // DiscardPadding
6417 
6418   if (status != 0)
6419     return status;
6420 
6421   m_pos = block_stop;
6422 
6423   return 0;  // success
6424 }
6425 
ParseBlockGroup(long long payload_size,long long & pos,long & len)6426 long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
6427                               long& len) {
6428   const long long payload_start = pos;
6429   const long long payload_stop = pos + payload_size;
6430 
6431   IMkvReader* const pReader = m_pSegment->m_pReader;
6432 
6433   long long total, avail;
6434 
6435   long status = pReader->Length(&total, &avail);
6436 
6437   if (status < 0)  // error
6438     return status;
6439 
6440   assert((total < 0) || (avail <= total));
6441 
6442   if ((total >= 0) && (payload_stop > total))
6443     return E_FILE_FORMAT_INVALID;
6444 
6445   if (payload_stop > avail) {
6446     len = static_cast<long>(payload_size);
6447     return E_BUFFER_NOT_FULL;
6448   }
6449 
6450   long long discard_padding = 0;
6451 
6452   while (pos < payload_stop) {
6453     // parse sub-block element ID
6454 
6455     if ((pos + 1) > avail) {
6456       len = 1;
6457       return E_BUFFER_NOT_FULL;
6458     }
6459 
6460     long long result = GetUIntLength(pReader, pos, len);
6461 
6462     if (result < 0)  // error
6463       return static_cast<long>(result);
6464 
6465     if (result > 0)  // weird
6466       return E_BUFFER_NOT_FULL;
6467 
6468     if ((pos + len) > payload_stop)
6469       return E_FILE_FORMAT_INVALID;
6470 
6471     if ((pos + len) > avail)
6472       return E_BUFFER_NOT_FULL;
6473 
6474     const long long id = ReadID(pReader, pos, len);
6475 
6476     if (id < 0)  // error
6477       return static_cast<long>(id);
6478 
6479     if (id == 0)  // not a valid ID
6480       return E_FILE_FORMAT_INVALID;
6481 
6482     pos += len;  // consume ID field
6483 
6484     // Parse Size
6485 
6486     if ((pos + 1) > avail) {
6487       len = 1;
6488       return E_BUFFER_NOT_FULL;
6489     }
6490 
6491     result = GetUIntLength(pReader, pos, len);
6492 
6493     if (result < 0)  // error
6494       return static_cast<long>(result);
6495 
6496     if (result > 0)  // weird
6497       return E_BUFFER_NOT_FULL;
6498 
6499     if ((pos + len) > payload_stop)
6500       return E_FILE_FORMAT_INVALID;
6501 
6502     if ((pos + len) > avail)
6503       return E_BUFFER_NOT_FULL;
6504 
6505     const long long size = ReadUInt(pReader, pos, len);
6506 
6507     if (size < 0)  // error
6508       return static_cast<long>(size);
6509 
6510     pos += len;  // consume size field
6511 
6512     // pos now points to start of sub-block group payload
6513 
6514     if (pos > payload_stop)
6515       return E_FILE_FORMAT_INVALID;
6516 
6517     if (size == 0)  // weird
6518       continue;
6519 
6520     const long long unknown_size = (1LL << (7 * len)) - 1;
6521 
6522     if (size == unknown_size)
6523       return E_FILE_FORMAT_INVALID;
6524 
6525     if (id == libwebm::kMkvDiscardPadding) {
6526       status = UnserializeInt(pReader, pos, size, discard_padding);
6527 
6528       if (status < 0)  // error
6529         return status;
6530     }
6531 
6532     if (id != libwebm::kMkvBlock) {
6533       pos += size;  // consume sub-part of block group
6534 
6535       if (pos > payload_stop)
6536         return E_FILE_FORMAT_INVALID;
6537 
6538       continue;
6539     }
6540 
6541     const long long block_stop = pos + size;
6542 
6543     if (block_stop > payload_stop)
6544       return E_FILE_FORMAT_INVALID;
6545 
6546     // parse track number
6547 
6548     if ((pos + 1) > avail) {
6549       len = 1;
6550       return E_BUFFER_NOT_FULL;
6551     }
6552 
6553     result = GetUIntLength(pReader, pos, len);
6554 
6555     if (result < 0)  // error
6556       return static_cast<long>(result);
6557 
6558     if (result > 0)  // weird
6559       return E_BUFFER_NOT_FULL;
6560 
6561     if ((pos + len) > block_stop)
6562       return E_FILE_FORMAT_INVALID;
6563 
6564     if ((pos + len) > avail)
6565       return E_BUFFER_NOT_FULL;
6566 
6567     const long long track = ReadUInt(pReader, pos, len);
6568 
6569     if (track < 0)  // error
6570       return static_cast<long>(track);
6571 
6572     if (track == 0)
6573       return E_FILE_FORMAT_INVALID;
6574 
6575     pos += len;  // consume track number
6576 
6577     if ((pos + 2) > block_stop)
6578       return E_FILE_FORMAT_INVALID;
6579 
6580     if ((pos + 2) > avail) {
6581       len = 2;
6582       return E_BUFFER_NOT_FULL;
6583     }
6584 
6585     pos += 2;  // consume timecode
6586 
6587     if ((pos + 1) > block_stop)
6588       return E_FILE_FORMAT_INVALID;
6589 
6590     if ((pos + 1) > avail) {
6591       len = 1;
6592       return E_BUFFER_NOT_FULL;
6593     }
6594 
6595     unsigned char flags;
6596 
6597     status = pReader->Read(pos, 1, &flags);
6598 
6599     if (status < 0) {  // error or underflow
6600       len = 1;
6601       return status;
6602     }
6603 
6604     ++pos;  // consume flags byte
6605     assert(pos <= avail);
6606 
6607     if (pos >= block_stop)
6608       return E_FILE_FORMAT_INVALID;
6609 
6610     const int lacing = int(flags & 0x06) >> 1;
6611 
6612     if ((lacing != 0) && (block_stop > avail)) {
6613       len = static_cast<long>(block_stop - pos);
6614       return E_BUFFER_NOT_FULL;
6615     }
6616 
6617     pos = block_stop;  // consume block-part of block group
6618     if (pos > payload_stop)
6619       return E_FILE_FORMAT_INVALID;
6620   }
6621 
6622   if (pos != payload_stop)
6623     return E_FILE_FORMAT_INVALID;
6624 
6625   status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size,
6626                        discard_padding);
6627   if (status != 0)
6628     return status;
6629 
6630   m_pos = payload_stop;
6631 
6632   return 0;  // success
6633 }
6634 
GetEntry(long index,const mkvparser::BlockEntry * & pEntry) const6635 long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
6636   assert(m_pos >= m_element_start);
6637 
6638   pEntry = NULL;
6639 
6640   if (index < 0)
6641     return -1;  // generic error
6642 
6643   if (m_entries_count < 0)
6644     return E_BUFFER_NOT_FULL;
6645 
6646   assert(m_entries);
6647   assert(m_entries_size > 0);
6648   assert(m_entries_count <= m_entries_size);
6649 
6650   if (index < m_entries_count) {
6651     pEntry = m_entries[index];
6652     assert(pEntry);
6653 
6654     return 1;  // found entry
6655   }
6656 
6657   if (m_element_size < 0)  // we don't know cluster end yet
6658     return E_BUFFER_NOT_FULL;  // underflow
6659 
6660   const long long element_stop = m_element_start + m_element_size;
6661 
6662   if (m_pos >= element_stop)
6663     return 0;  // nothing left to parse
6664 
6665   return E_BUFFER_NOT_FULL;  // underflow, since more remains to be parsed
6666 }
6667 
Create(Segment * pSegment,long idx,long long off)6668 Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
6669   if (!pSegment || off < 0)
6670     return NULL;
6671 
6672   const long long element_start = pSegment->m_start + off;
6673 
6674   Cluster* const pCluster =
6675       new (std::nothrow) Cluster(pSegment, idx, element_start);
6676 
6677   return pCluster;
6678 }
6679 
Cluster()6680 Cluster::Cluster()
6681     : m_pSegment(NULL),
6682       m_element_start(0),
6683       m_index(0),
6684       m_pos(0),
6685       m_element_size(0),
6686       m_timecode(0),
6687       m_entries(NULL),
6688       m_entries_size(0),
6689       m_entries_count(0)  // means "no entries"
6690 {}
6691 
Cluster(Segment * pSegment,long idx,long long element_start)6692 Cluster::Cluster(Segment* pSegment, long idx, long long element_start
6693                  /* long long element_size */)
6694     : m_pSegment(pSegment),
6695       m_element_start(element_start),
6696       m_index(idx),
6697       m_pos(element_start),
6698       m_element_size(-1 /* element_size */),
6699       m_timecode(-1),
6700       m_entries(NULL),
6701       m_entries_size(0),
6702       m_entries_count(-1)  // means "has not been parsed yet"
6703 {}
6704 
~Cluster()6705 Cluster::~Cluster() {
6706   if (m_entries_count <= 0)
6707     return;
6708 
6709   BlockEntry** i = m_entries;
6710   BlockEntry** const j = m_entries + m_entries_count;
6711 
6712   while (i != j) {
6713     BlockEntry* p = *i++;
6714     assert(p);
6715 
6716     delete p;
6717   }
6718 
6719   delete[] m_entries;
6720 }
6721 
EOS() const6722 bool Cluster::EOS() const { return (m_pSegment == NULL); }
6723 
GetIndex() const6724 long Cluster::GetIndex() const { return m_index; }
6725 
GetPosition() const6726 long long Cluster::GetPosition() const {
6727   const long long pos = m_element_start - m_pSegment->m_start;
6728   assert(pos >= 0);
6729 
6730   return pos;
6731 }
6732 
GetElementSize() const6733 long long Cluster::GetElementSize() const { return m_element_size; }
6734 
HasBlockEntries(const Segment * pSegment,long long off,long long & pos,long & len)6735 long Cluster::HasBlockEntries(
6736     const Segment* pSegment,
6737     long long off,  // relative to start of segment payload
6738     long long& pos, long& len) {
6739   assert(pSegment);
6740   assert(off >= 0);  // relative to segment
6741 
6742   IMkvReader* const pReader = pSegment->m_pReader;
6743 
6744   long long total, avail;
6745 
6746   long status = pReader->Length(&total, &avail);
6747 
6748   if (status < 0)  // error
6749     return status;
6750 
6751   assert((total < 0) || (avail <= total));
6752 
6753   pos = pSegment->m_start + off;  // absolute
6754 
6755   if ((total >= 0) && (pos >= total))
6756     return 0;  // we don't even have a complete cluster
6757 
6758   const long long segment_stop =
6759       (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
6760 
6761   long long cluster_stop = -1;  // interpreted later to mean "unknown size"
6762 
6763   {
6764     if ((pos + 1) > avail) {
6765       len = 1;
6766       return E_BUFFER_NOT_FULL;
6767     }
6768 
6769     long long result = GetUIntLength(pReader, pos, len);
6770 
6771     if (result < 0)  // error
6772       return static_cast<long>(result);
6773 
6774     if (result > 0)  // need more data
6775       return E_BUFFER_NOT_FULL;
6776 
6777     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6778       return E_FILE_FORMAT_INVALID;
6779 
6780     if ((total >= 0) && ((pos + len) > total))
6781       return 0;
6782 
6783     if ((pos + len) > avail)
6784       return E_BUFFER_NOT_FULL;
6785 
6786     const long long id = ReadID(pReader, pos, len);
6787 
6788     if (id < 0)  // error
6789       return static_cast<long>(id);
6790 
6791     if (id != libwebm::kMkvCluster)
6792       return E_PARSE_FAILED;
6793 
6794     pos += len;  // consume Cluster ID field
6795 
6796     // read size field
6797 
6798     if ((pos + 1) > avail) {
6799       len = 1;
6800       return E_BUFFER_NOT_FULL;
6801     }
6802 
6803     result = GetUIntLength(pReader, pos, len);
6804 
6805     if (result < 0)  // error
6806       return static_cast<long>(result);
6807 
6808     if (result > 0)  // weird
6809       return E_BUFFER_NOT_FULL;
6810 
6811     if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6812       return E_FILE_FORMAT_INVALID;
6813 
6814     if ((total >= 0) && ((pos + len) > total))
6815       return 0;
6816 
6817     if ((pos + len) > avail)
6818       return E_BUFFER_NOT_FULL;
6819 
6820     const long long size = ReadUInt(pReader, pos, len);
6821 
6822     if (size < 0)  // error
6823       return static_cast<long>(size);
6824 
6825     if (size == 0)
6826       return 0;  // cluster does not have entries
6827 
6828     pos += len;  // consume size field
6829 
6830     // pos now points to start of payload
6831 
6832     const long long unknown_size = (1LL << (7 * len)) - 1;
6833 
6834     if (size != unknown_size) {
6835       cluster_stop = pos + size;
6836       assert(cluster_stop >= 0);
6837 
6838       if ((segment_stop >= 0) && (cluster_stop > segment_stop))
6839         return E_FILE_FORMAT_INVALID;
6840 
6841       if ((total >= 0) && (cluster_stop > total))
6842         // return E_FILE_FORMAT_INVALID;  //too conservative
6843         return 0;  // cluster does not have any entries
6844     }
6845   }
6846 
6847   for (;;) {
6848     if ((cluster_stop >= 0) && (pos >= cluster_stop))
6849       return 0;  // no entries detected
6850 
6851     if ((pos + 1) > avail) {
6852       len = 1;
6853       return E_BUFFER_NOT_FULL;
6854     }
6855 
6856     long long result = GetUIntLength(pReader, pos, len);
6857 
6858     if (result < 0)  // error
6859       return static_cast<long>(result);
6860 
6861     if (result > 0)  // need more data
6862       return E_BUFFER_NOT_FULL;
6863 
6864     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6865       return E_FILE_FORMAT_INVALID;
6866 
6867     if ((pos + len) > avail)
6868       return E_BUFFER_NOT_FULL;
6869 
6870     const long long id = ReadID(pReader, pos, len);
6871 
6872     if (id < 0)  // error
6873       return static_cast<long>(id);
6874 
6875     // This is the distinguished set of ID's we use to determine
6876     // that we have exhausted the sub-element's inside the cluster
6877     // whose ID we parsed earlier.
6878 
6879     if (id == libwebm::kMkvCluster)
6880       return 0;  // no entries found
6881 
6882     if (id == libwebm::kMkvCues)
6883       return 0;  // no entries found
6884 
6885     pos += len;  // consume id field
6886 
6887     if ((cluster_stop >= 0) && (pos >= cluster_stop))
6888       return E_FILE_FORMAT_INVALID;
6889 
6890     // read size field
6891 
6892     if ((pos + 1) > avail) {
6893       len = 1;
6894       return E_BUFFER_NOT_FULL;
6895     }
6896 
6897     result = GetUIntLength(pReader, pos, len);
6898 
6899     if (result < 0)  // error
6900       return static_cast<long>(result);
6901 
6902     if (result > 0)  // underflow
6903       return E_BUFFER_NOT_FULL;
6904 
6905     if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6906       return E_FILE_FORMAT_INVALID;
6907 
6908     if ((pos + len) > avail)
6909       return E_BUFFER_NOT_FULL;
6910 
6911     const long long size = ReadUInt(pReader, pos, len);
6912 
6913     if (size < 0)  // error
6914       return static_cast<long>(size);
6915 
6916     pos += len;  // consume size field
6917 
6918     // pos now points to start of payload
6919 
6920     if ((cluster_stop >= 0) && (pos > cluster_stop))
6921       return E_FILE_FORMAT_INVALID;
6922 
6923     if (size == 0)  // weird
6924       continue;
6925 
6926     const long long unknown_size = (1LL << (7 * len)) - 1;
6927 
6928     if (size == unknown_size)
6929       return E_FILE_FORMAT_INVALID;  // not supported inside cluster
6930 
6931     if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6932       return E_FILE_FORMAT_INVALID;
6933 
6934     if (id == libwebm::kMkvBlockGroup)
6935       return 1;  // have at least one entry
6936 
6937     if (id == libwebm::kMkvSimpleBlock)
6938       return 1;  // have at least one entry
6939 
6940     pos += size;  // consume payload
6941     if (cluster_stop >= 0 && pos > cluster_stop)
6942       return E_FILE_FORMAT_INVALID;
6943   }
6944 }
6945 
GetTimeCode() const6946 long long Cluster::GetTimeCode() const {
6947   long long pos;
6948   long len;
6949 
6950   const long status = Load(pos, len);
6951 
6952   if (status < 0)  // error
6953     return status;
6954 
6955   return m_timecode;
6956 }
6957 
GetTime() const6958 long long Cluster::GetTime() const {
6959   const long long tc = GetTimeCode();
6960 
6961   if (tc < 0)
6962     return tc;
6963 
6964   const SegmentInfo* const pInfo = m_pSegment->GetInfo();
6965   assert(pInfo);
6966 
6967   const long long scale = pInfo->GetTimeCodeScale();
6968   assert(scale >= 1);
6969 
6970   const long long t = m_timecode * scale;
6971 
6972   return t;
6973 }
6974 
GetFirstTime() const6975 long long Cluster::GetFirstTime() const {
6976   const BlockEntry* pEntry;
6977 
6978   const long status = GetFirst(pEntry);
6979 
6980   if (status < 0)  // error
6981     return status;
6982 
6983   if (pEntry == NULL)  // empty cluster
6984     return GetTime();
6985 
6986   const Block* const pBlock = pEntry->GetBlock();
6987   assert(pBlock);
6988 
6989   return pBlock->GetTime(this);
6990 }
6991 
GetLastTime() const6992 long long Cluster::GetLastTime() const {
6993   const BlockEntry* pEntry;
6994 
6995   const long status = GetLast(pEntry);
6996 
6997   if (status < 0)  // error
6998     return status;
6999 
7000   if (pEntry == NULL)  // empty cluster
7001     return GetTime();
7002 
7003   const Block* const pBlock = pEntry->GetBlock();
7004   assert(pBlock);
7005 
7006   return pBlock->GetTime(this);
7007 }
7008 
CreateBlock(long long id,long long pos,long long size,long long discard_padding)7009 long Cluster::CreateBlock(long long id,
7010                           long long pos,  // absolute pos of payload
7011                           long long size, long long discard_padding) {
7012   if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock)
7013     return E_PARSE_FAILED;
7014 
7015   if (m_entries_count < 0) {  // haven't parsed anything yet
7016     assert(m_entries == NULL);
7017     assert(m_entries_size == 0);
7018 
7019     m_entries_size = 1024;
7020     m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
7021     if (m_entries == NULL)
7022       return -1;
7023 
7024     m_entries_count = 0;
7025   } else {
7026     assert(m_entries);
7027     assert(m_entries_size > 0);
7028     assert(m_entries_count <= m_entries_size);
7029 
7030     if (m_entries_count >= m_entries_size) {
7031       const long entries_size = 2 * m_entries_size;
7032 
7033       BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
7034       if (entries == NULL)
7035         return -1;
7036 
7037       BlockEntry** src = m_entries;
7038       BlockEntry** const src_end = src + m_entries_count;
7039 
7040       BlockEntry** dst = entries;
7041 
7042       while (src != src_end)
7043         *dst++ = *src++;
7044 
7045       delete[] m_entries;
7046 
7047       m_entries = entries;
7048       m_entries_size = entries_size;
7049     }
7050   }
7051 
7052   if (id == libwebm::kMkvBlockGroup)
7053     return CreateBlockGroup(pos, size, discard_padding);
7054   else
7055     return CreateSimpleBlock(pos, size);
7056 }
7057 
CreateBlockGroup(long long start_offset,long long size,long long discard_padding)7058 long Cluster::CreateBlockGroup(long long start_offset, long long size,
7059                                long long discard_padding) {
7060   assert(m_entries);
7061   assert(m_entries_size > 0);
7062   assert(m_entries_count >= 0);
7063   assert(m_entries_count < m_entries_size);
7064 
7065   IMkvReader* const pReader = m_pSegment->m_pReader;
7066 
7067   long long pos = start_offset;
7068   const long long stop = start_offset + size;
7069 
7070   // For WebM files, there is a bias towards previous reference times
7071   //(in order to support alt-ref frames, which refer back to the previous
7072   // keyframe).  Normally a 0 value is not possible, but here we tenatively
7073   // allow 0 as the value of a reference frame, with the interpretation
7074   // that this is a "previous" reference time.
7075 
7076   long long prev = 1;  // nonce
7077   long long next = 0;  // nonce
7078   long long duration = -1;  // really, this is unsigned
7079 
7080   long long bpos = -1;
7081   long long bsize = -1;
7082 
7083   while (pos < stop) {
7084     long len;
7085     const long long id = ReadID(pReader, pos, len);
7086     if (id < 0 || (pos + len) > stop)
7087       return E_FILE_FORMAT_INVALID;
7088 
7089     pos += len;  // consume ID
7090 
7091     const long long size = ReadUInt(pReader, pos, len);
7092     assert(size >= 0);  // TODO
7093     assert((pos + len) <= stop);
7094 
7095     pos += len;  // consume size
7096 
7097     if (id == libwebm::kMkvBlock) {
7098       if (bpos < 0) {  // Block ID
7099         bpos = pos;
7100         bsize = size;
7101       }
7102     } else if (id == libwebm::kMkvBlockDuration) {
7103       if (size > 8)
7104         return E_FILE_FORMAT_INVALID;
7105 
7106       duration = UnserializeUInt(pReader, pos, size);
7107 
7108       if (duration < 0)
7109         return E_FILE_FORMAT_INVALID;
7110     } else if (id == libwebm::kMkvReferenceBlock) {
7111       if (size > 8 || size <= 0)
7112         return E_FILE_FORMAT_INVALID;
7113       const long size_ = static_cast<long>(size);
7114 
7115       long long time;
7116 
7117       long status = UnserializeInt(pReader, pos, size_, time);
7118       assert(status == 0);
7119       if (status != 0)
7120         return -1;
7121 
7122       if (time <= 0)  // see note above
7123         prev = time;
7124       else
7125         next = time;
7126     }
7127 
7128     pos += size;  // consume payload
7129     if (pos > stop)
7130       return E_FILE_FORMAT_INVALID;
7131   }
7132   if (bpos < 0)
7133     return E_FILE_FORMAT_INVALID;
7134 
7135   if (pos != stop)
7136     return E_FILE_FORMAT_INVALID;
7137   assert(bsize >= 0);
7138 
7139   const long idx = m_entries_count;
7140 
7141   BlockEntry** const ppEntry = m_entries + idx;
7142   BlockEntry*& pEntry = *ppEntry;
7143 
7144   pEntry = new (std::nothrow)
7145       BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
7146 
7147   if (pEntry == NULL)
7148     return -1;  // generic error
7149 
7150   BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
7151 
7152   const long status = p->Parse();
7153 
7154   if (status == 0) {  // success
7155     ++m_entries_count;
7156     return 0;
7157   }
7158 
7159   delete pEntry;
7160   pEntry = 0;
7161 
7162   return status;
7163 }
7164 
CreateSimpleBlock(long long st,long long sz)7165 long Cluster::CreateSimpleBlock(long long st, long long sz) {
7166   assert(m_entries);
7167   assert(m_entries_size > 0);
7168   assert(m_entries_count >= 0);
7169   assert(m_entries_count < m_entries_size);
7170 
7171   const long idx = m_entries_count;
7172 
7173   BlockEntry** const ppEntry = m_entries + idx;
7174   BlockEntry*& pEntry = *ppEntry;
7175 
7176   pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
7177 
7178   if (pEntry == NULL)
7179     return -1;  // generic error
7180 
7181   SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
7182 
7183   const long status = p->Parse();
7184 
7185   if (status == 0) {
7186     ++m_entries_count;
7187     return 0;
7188   }
7189 
7190   delete pEntry;
7191   pEntry = 0;
7192 
7193   return status;
7194 }
7195 
GetFirst(const BlockEntry * & pFirst) const7196 long Cluster::GetFirst(const BlockEntry*& pFirst) const {
7197   if (m_entries_count <= 0) {
7198     long long pos;
7199     long len;
7200 
7201     const long status = Parse(pos, len);
7202 
7203     if (status < 0) {  // error
7204       pFirst = NULL;
7205       return status;
7206     }
7207 
7208     if (m_entries_count <= 0) {  // empty cluster
7209       pFirst = NULL;
7210       return 0;
7211     }
7212   }
7213 
7214   assert(m_entries);
7215 
7216   pFirst = m_entries[0];
7217   assert(pFirst);
7218 
7219   return 0;  // success
7220 }
7221 
GetLast(const BlockEntry * & pLast) const7222 long Cluster::GetLast(const BlockEntry*& pLast) const {
7223   for (;;) {
7224     long long pos;
7225     long len;
7226 
7227     const long status = Parse(pos, len);
7228 
7229     if (status < 0) {  // error
7230       pLast = NULL;
7231       return status;
7232     }
7233 
7234     if (status > 0)  // no new block
7235       break;
7236   }
7237 
7238   if (m_entries_count <= 0) {
7239     pLast = NULL;
7240     return 0;
7241   }
7242 
7243   assert(m_entries);
7244 
7245   const long idx = m_entries_count - 1;
7246 
7247   pLast = m_entries[idx];
7248   assert(pLast);
7249 
7250   return 0;
7251 }
7252 
GetNext(const BlockEntry * pCurr,const BlockEntry * & pNext) const7253 long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
7254   assert(pCurr);
7255   assert(m_entries);
7256   assert(m_entries_count > 0);
7257 
7258   size_t idx = pCurr->GetIndex();
7259   assert(idx < size_t(m_entries_count));
7260   assert(m_entries[idx] == pCurr);
7261 
7262   ++idx;
7263 
7264   if (idx >= size_t(m_entries_count)) {
7265     long long pos;
7266     long len;
7267 
7268     const long status = Parse(pos, len);
7269 
7270     if (status < 0) {  // error
7271       pNext = NULL;
7272       return status;
7273     }
7274 
7275     if (status > 0) {
7276       pNext = NULL;
7277       return 0;
7278     }
7279 
7280     assert(m_entries);
7281     assert(m_entries_count > 0);
7282     assert(idx < size_t(m_entries_count));
7283   }
7284 
7285   pNext = m_entries[idx];
7286   assert(pNext);
7287 
7288   return 0;
7289 }
7290 
GetEntryCount() const7291 long Cluster::GetEntryCount() const { return m_entries_count; }
7292 
GetEntry(const Track * pTrack,long long time_ns) const7293 const BlockEntry* Cluster::GetEntry(const Track* pTrack,
7294                                     long long time_ns) const {
7295   assert(pTrack);
7296 
7297   if (m_pSegment == NULL)  // this is the special EOS cluster
7298     return pTrack->GetEOS();
7299 
7300   const BlockEntry* pResult = pTrack->GetEOS();
7301 
7302   long index = 0;
7303 
7304   for (;;) {
7305     if (index >= m_entries_count) {
7306       long long pos;
7307       long len;
7308 
7309       const long status = Parse(pos, len);
7310       assert(status >= 0);
7311 
7312       if (status > 0)  // completely parsed, and no more entries
7313         return pResult;
7314 
7315       if (status < 0)  // should never happen
7316         return 0;
7317 
7318       assert(m_entries);
7319       assert(index < m_entries_count);
7320     }
7321 
7322     const BlockEntry* const pEntry = m_entries[index];
7323     assert(pEntry);
7324     assert(!pEntry->EOS());
7325 
7326     const Block* const pBlock = pEntry->GetBlock();
7327     assert(pBlock);
7328 
7329     if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
7330       ++index;
7331       continue;
7332     }
7333 
7334     if (pTrack->VetEntry(pEntry)) {
7335       if (time_ns < 0)  // just want first candidate block
7336         return pEntry;
7337 
7338       const long long ns = pBlock->GetTime(this);
7339 
7340       if (ns > time_ns)
7341         return pResult;
7342 
7343       pResult = pEntry;  // have a candidate
7344     } else if (time_ns >= 0) {
7345       const long long ns = pBlock->GetTime(this);
7346 
7347       if (ns > time_ns)
7348         return pResult;
7349     }
7350 
7351     ++index;
7352   }
7353 }
7354 
GetEntry(const CuePoint & cp,const CuePoint::TrackPosition & tp) const7355 const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
7356                                     const CuePoint::TrackPosition& tp) const {
7357   assert(m_pSegment);
7358   const long long tc = cp.GetTimeCode();
7359 
7360   if (tp.m_block > 0) {
7361     const long block = static_cast<long>(tp.m_block);
7362     const long index = block - 1;
7363 
7364     while (index >= m_entries_count) {
7365       long long pos;
7366       long len;
7367 
7368       const long status = Parse(pos, len);
7369 
7370       if (status < 0)  // TODO: can this happen?
7371         return NULL;
7372 
7373       if (status > 0)  // nothing remains to be parsed
7374         return NULL;
7375     }
7376 
7377     const BlockEntry* const pEntry = m_entries[index];
7378     assert(pEntry);
7379     assert(!pEntry->EOS());
7380 
7381     const Block* const pBlock = pEntry->GetBlock();
7382     assert(pBlock);
7383 
7384     if ((pBlock->GetTrackNumber() == tp.m_track) &&
7385         (pBlock->GetTimeCode(this) == tc)) {
7386       return pEntry;
7387     }
7388   }
7389 
7390   long index = 0;
7391 
7392   for (;;) {
7393     if (index >= m_entries_count) {
7394       long long pos;
7395       long len;
7396 
7397       const long status = Parse(pos, len);
7398 
7399       if (status < 0)  // TODO: can this happen?
7400         return NULL;
7401 
7402       if (status > 0)  // nothing remains to be parsed
7403         return NULL;
7404 
7405       assert(m_entries);
7406       assert(index < m_entries_count);
7407     }
7408 
7409     const BlockEntry* const pEntry = m_entries[index];
7410     assert(pEntry);
7411     assert(!pEntry->EOS());
7412 
7413     const Block* const pBlock = pEntry->GetBlock();
7414     assert(pBlock);
7415 
7416     if (pBlock->GetTrackNumber() != tp.m_track) {
7417       ++index;
7418       continue;
7419     }
7420 
7421     const long long tc_ = pBlock->GetTimeCode(this);
7422 
7423     if (tc_ < tc) {
7424       ++index;
7425       continue;
7426     }
7427 
7428     if (tc_ > tc)
7429       return NULL;
7430 
7431     const Tracks* const pTracks = m_pSegment->GetTracks();
7432     assert(pTracks);
7433 
7434     const long tn = static_cast<long>(tp.m_track);
7435     const Track* const pTrack = pTracks->GetTrackByNumber(tn);
7436 
7437     if (pTrack == NULL)
7438       return NULL;
7439 
7440     const long long type = pTrack->GetType();
7441 
7442     if (type == 2)  // audio
7443       return pEntry;
7444 
7445     if (type != 1)  // not video
7446       return NULL;
7447 
7448     if (!pBlock->IsKey())
7449       return NULL;
7450 
7451     return pEntry;
7452   }
7453 }
7454 
BlockEntry(Cluster * p,long idx)7455 BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
~BlockEntry()7456 BlockEntry::~BlockEntry() {}
GetCluster() const7457 const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
GetIndex() const7458 long BlockEntry::GetIndex() const { return m_index; }
7459 
SimpleBlock(Cluster * pCluster,long idx,long long start,long long size)7460 SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
7461                          long long size)
7462     : BlockEntry(pCluster, idx), m_block(start, size, 0) {}
7463 
Parse()7464 long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
GetKind() const7465 BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
GetBlock() const7466 const Block* SimpleBlock::GetBlock() const { return &m_block; }
7467 
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)7468 BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
7469                        long long block_size, long long prev, long long next,
7470                        long long duration, long long discard_padding)
7471     : BlockEntry(pCluster, idx),
7472       m_block(block_start, block_size, discard_padding),
7473       m_prev(prev),
7474       m_next(next),
7475       m_duration(duration) {}
7476 
Parse()7477 long BlockGroup::Parse() {
7478   const long status = m_block.Parse(m_pCluster);
7479 
7480   if (status)
7481     return status;
7482 
7483   m_block.SetKey((m_prev > 0) && (m_next <= 0));
7484 
7485   return 0;
7486 }
7487 
GetKind() const7488 BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
GetBlock() const7489 const Block* BlockGroup::GetBlock() const { return &m_block; }
GetPrevTimeCode() const7490 long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
GetNextTimeCode() const7491 long long BlockGroup::GetNextTimeCode() const { return m_next; }
GetDurationTimeCode() const7492 long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
7493 
Block(long long start,long long size_,long long discard_padding)7494 Block::Block(long long start, long long size_, long long discard_padding)
7495     : m_start(start),
7496       m_size(size_),
7497       m_track(0),
7498       m_timecode(-1),
7499       m_flags(0),
7500       m_frames(NULL),
7501       m_frame_count(-1),
7502       m_discard_padding(discard_padding) {}
7503 
~Block()7504 Block::~Block() { delete[] m_frames; }
7505 
Parse(const Cluster * pCluster)7506 long Block::Parse(const Cluster* pCluster) {
7507   if (pCluster == NULL)
7508     return -1;
7509 
7510   if (pCluster->m_pSegment == NULL)
7511     return -1;
7512 
7513   assert(m_start >= 0);
7514   assert(m_size >= 0);
7515   assert(m_track <= 0);
7516   assert(m_frames == NULL);
7517   assert(m_frame_count <= 0);
7518 
7519   long long pos = m_start;
7520   const long long stop = m_start + m_size;
7521 
7522   long len;
7523 
7524   IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
7525 
7526   m_track = ReadUInt(pReader, pos, len);
7527 
7528   if (m_track <= 0)
7529     return E_FILE_FORMAT_INVALID;
7530 
7531   if ((pos + len) > stop)
7532     return E_FILE_FORMAT_INVALID;
7533 
7534   pos += len;  // consume track number
7535 
7536   if ((stop - pos) < 2)
7537     return E_FILE_FORMAT_INVALID;
7538 
7539   long status;
7540   long long value;
7541 
7542   status = UnserializeInt(pReader, pos, 2, value);
7543 
7544   if (status)
7545     return E_FILE_FORMAT_INVALID;
7546 
7547   if (value < SHRT_MIN)
7548     return E_FILE_FORMAT_INVALID;
7549 
7550   if (value > SHRT_MAX)
7551     return E_FILE_FORMAT_INVALID;
7552 
7553   m_timecode = static_cast<short>(value);
7554 
7555   pos += 2;
7556 
7557   if ((stop - pos) <= 0)
7558     return E_FILE_FORMAT_INVALID;
7559 
7560   status = pReader->Read(pos, 1, &m_flags);
7561 
7562   if (status)
7563     return E_FILE_FORMAT_INVALID;
7564 
7565   const int lacing = int(m_flags & 0x06) >> 1;
7566 
7567   ++pos;  // consume flags byte
7568 
7569   if (lacing == 0) {  // no lacing
7570     if (pos > stop)
7571       return E_FILE_FORMAT_INVALID;
7572 
7573     m_frame_count = 1;
7574     m_frames = new (std::nothrow) Frame[m_frame_count];
7575     if (m_frames == NULL)
7576       return -1;
7577 
7578     Frame& f = m_frames[0];
7579     f.pos = pos;
7580 
7581     const long long frame_size = stop - pos;
7582 
7583     if (frame_size > LONG_MAX || frame_size <= 0)
7584       return E_FILE_FORMAT_INVALID;
7585 
7586     f.len = static_cast<long>(frame_size);
7587 
7588     return 0;  // success
7589   }
7590 
7591   if (pos >= stop)
7592     return E_FILE_FORMAT_INVALID;
7593 
7594   unsigned char biased_count;
7595 
7596   status = pReader->Read(pos, 1, &biased_count);
7597 
7598   if (status)
7599     return E_FILE_FORMAT_INVALID;
7600 
7601   ++pos;  // consume frame count
7602   if (pos > stop)
7603     return E_FILE_FORMAT_INVALID;
7604 
7605   m_frame_count = int(biased_count) + 1;
7606 
7607   m_frames = new (std::nothrow) Frame[m_frame_count];
7608   if (m_frames == NULL)
7609     return -1;
7610 
7611   if (!m_frames)
7612     return E_FILE_FORMAT_INVALID;
7613 
7614   if (lacing == 1) {  // Xiph
7615     Frame* pf = m_frames;
7616     Frame* const pf_end = pf + m_frame_count;
7617 
7618     long long size = 0;
7619     int frame_count = m_frame_count;
7620 
7621     while (frame_count > 1) {
7622       long frame_size = 0;
7623 
7624       for (;;) {
7625         unsigned char val;
7626 
7627         if (pos >= stop)
7628           return E_FILE_FORMAT_INVALID;
7629 
7630         status = pReader->Read(pos, 1, &val);
7631 
7632         if (status)
7633           return E_FILE_FORMAT_INVALID;
7634 
7635         ++pos;  // consume xiph size byte
7636 
7637         frame_size += val;
7638 
7639         if (val < 255)
7640           break;
7641       }
7642 
7643       Frame& f = *pf++;
7644       assert(pf < pf_end);
7645       if (pf >= pf_end)
7646         return E_FILE_FORMAT_INVALID;
7647 
7648       f.pos = 0;  // patch later
7649 
7650       if (frame_size <= 0)
7651         return E_FILE_FORMAT_INVALID;
7652 
7653       f.len = frame_size;
7654       size += frame_size;  // contribution of this frame
7655 
7656       --frame_count;
7657     }
7658 
7659     if (pf >= pf_end || pos > stop)
7660       return E_FILE_FORMAT_INVALID;
7661 
7662     {
7663       Frame& f = *pf++;
7664 
7665       if (pf != pf_end)
7666         return E_FILE_FORMAT_INVALID;
7667 
7668       f.pos = 0;  // patch later
7669 
7670       const long long total_size = stop - pos;
7671 
7672       if (total_size < size)
7673         return E_FILE_FORMAT_INVALID;
7674 
7675       const long long frame_size = total_size - size;
7676 
7677       if (frame_size > LONG_MAX || frame_size <= 0)
7678         return E_FILE_FORMAT_INVALID;
7679 
7680       f.len = static_cast<long>(frame_size);
7681     }
7682 
7683     pf = m_frames;
7684     while (pf != pf_end) {
7685       Frame& f = *pf++;
7686       assert((pos + f.len) <= stop);
7687 
7688       if ((pos + f.len) > stop)
7689         return E_FILE_FORMAT_INVALID;
7690 
7691       f.pos = pos;
7692       pos += f.len;
7693     }
7694 
7695     assert(pos == stop);
7696     if (pos != stop)
7697       return E_FILE_FORMAT_INVALID;
7698 
7699   } else if (lacing == 2) {  // fixed-size lacing
7700     if (pos >= stop)
7701       return E_FILE_FORMAT_INVALID;
7702 
7703     const long long total_size = stop - pos;
7704 
7705     if ((total_size % m_frame_count) != 0)
7706       return E_FILE_FORMAT_INVALID;
7707 
7708     const long long frame_size = total_size / m_frame_count;
7709 
7710     if (frame_size > LONG_MAX || frame_size <= 0)
7711       return E_FILE_FORMAT_INVALID;
7712 
7713     Frame* pf = m_frames;
7714     Frame* const pf_end = pf + m_frame_count;
7715 
7716     while (pf != pf_end) {
7717       assert((pos + frame_size) <= stop);
7718       if ((pos + frame_size) > stop)
7719         return E_FILE_FORMAT_INVALID;
7720 
7721       Frame& f = *pf++;
7722 
7723       f.pos = pos;
7724       f.len = static_cast<long>(frame_size);
7725 
7726       pos += frame_size;
7727     }
7728 
7729     assert(pos == stop);
7730     if (pos != stop)
7731       return E_FILE_FORMAT_INVALID;
7732 
7733   } else {
7734     assert(lacing == 3);  // EBML lacing
7735 
7736     if (pos >= stop)
7737       return E_FILE_FORMAT_INVALID;
7738 
7739     long long size = 0;
7740     int frame_count = m_frame_count;
7741 
7742     long long frame_size = ReadUInt(pReader, pos, len);
7743 
7744     if (frame_size <= 0)
7745       return E_FILE_FORMAT_INVALID;
7746 
7747     if (frame_size > LONG_MAX)
7748       return E_FILE_FORMAT_INVALID;
7749 
7750     if ((pos + len) > stop)
7751       return E_FILE_FORMAT_INVALID;
7752 
7753     pos += len;  // consume length of size of first frame
7754 
7755     if ((pos + frame_size) > stop)
7756       return E_FILE_FORMAT_INVALID;
7757 
7758     Frame* pf = m_frames;
7759     Frame* const pf_end = pf + m_frame_count;
7760 
7761     {
7762       Frame& curr = *pf;
7763 
7764       curr.pos = 0;  // patch later
7765 
7766       curr.len = static_cast<long>(frame_size);
7767       size += curr.len;  // contribution of this frame
7768     }
7769 
7770     --frame_count;
7771 
7772     while (frame_count > 1) {
7773       if (pos >= stop)
7774         return E_FILE_FORMAT_INVALID;
7775 
7776       assert(pf < pf_end);
7777       if (pf >= pf_end)
7778         return E_FILE_FORMAT_INVALID;
7779 
7780       const Frame& prev = *pf++;
7781       assert(prev.len == frame_size);
7782       if (prev.len != frame_size)
7783         return E_FILE_FORMAT_INVALID;
7784 
7785       assert(pf < pf_end);
7786       if (pf >= pf_end)
7787         return E_FILE_FORMAT_INVALID;
7788 
7789       Frame& curr = *pf;
7790 
7791       curr.pos = 0;  // patch later
7792 
7793       const long long delta_size_ = ReadUInt(pReader, pos, len);
7794 
7795       if (delta_size_ < 0)
7796         return E_FILE_FORMAT_INVALID;
7797 
7798       if ((pos + len) > stop)
7799         return E_FILE_FORMAT_INVALID;
7800 
7801       pos += len;  // consume length of (delta) size
7802       if (pos > stop)
7803         return E_FILE_FORMAT_INVALID;
7804 
7805       const int exp = 7 * len - 1;
7806       const long long bias = (1LL << exp) - 1LL;
7807       const long long delta_size = delta_size_ - bias;
7808 
7809       frame_size += delta_size;
7810 
7811       if (frame_size <= 0)
7812         return E_FILE_FORMAT_INVALID;
7813 
7814       if (frame_size > LONG_MAX)
7815         return E_FILE_FORMAT_INVALID;
7816 
7817       curr.len = static_cast<long>(frame_size);
7818       size += curr.len;  // contribution of this frame
7819 
7820       --frame_count;
7821     }
7822 
7823     // parse last frame
7824     if (frame_count > 0) {
7825       if (pos > stop || pf >= pf_end)
7826         return E_FILE_FORMAT_INVALID;
7827 
7828       const Frame& prev = *pf++;
7829       assert(prev.len == frame_size);
7830       if (prev.len != frame_size)
7831         return E_FILE_FORMAT_INVALID;
7832 
7833       if (pf >= pf_end)
7834         return E_FILE_FORMAT_INVALID;
7835 
7836       Frame& curr = *pf++;
7837       if (pf != pf_end)
7838         return E_FILE_FORMAT_INVALID;
7839 
7840       curr.pos = 0;  // patch later
7841 
7842       const long long total_size = stop - pos;
7843 
7844       if (total_size < size)
7845         return E_FILE_FORMAT_INVALID;
7846 
7847       frame_size = total_size - size;
7848 
7849       if (frame_size > LONG_MAX || frame_size <= 0)
7850         return E_FILE_FORMAT_INVALID;
7851 
7852       curr.len = static_cast<long>(frame_size);
7853     }
7854 
7855     pf = m_frames;
7856     while (pf != pf_end) {
7857       Frame& f = *pf++;
7858       assert((pos + f.len) <= stop);
7859       if ((pos + f.len) > stop)
7860         return E_FILE_FORMAT_INVALID;
7861 
7862       f.pos = pos;
7863       pos += f.len;
7864     }
7865 
7866     if (pos != stop)
7867       return E_FILE_FORMAT_INVALID;
7868   }
7869 
7870   return 0;  // success
7871 }
7872 
GetTimeCode(const Cluster * pCluster) const7873 long long Block::GetTimeCode(const Cluster* pCluster) const {
7874   if (pCluster == 0)
7875     return m_timecode;
7876 
7877   const long long tc0 = pCluster->GetTimeCode();
7878   assert(tc0 >= 0);
7879 
7880   const long long tc = tc0 + m_timecode;
7881 
7882   return tc;  // unscaled timecode units
7883 }
7884 
GetTime(const Cluster * pCluster) const7885 long long Block::GetTime(const Cluster* pCluster) const {
7886   assert(pCluster);
7887 
7888   const long long tc = GetTimeCode(pCluster);
7889 
7890   const Segment* const pSegment = pCluster->m_pSegment;
7891   const SegmentInfo* const pInfo = pSegment->GetInfo();
7892   assert(pInfo);
7893 
7894   const long long scale = pInfo->GetTimeCodeScale();
7895   assert(scale >= 1);
7896 
7897   const long long ns = tc * scale;
7898 
7899   return ns;
7900 }
7901 
GetTrackNumber() const7902 long long Block::GetTrackNumber() const { return m_track; }
7903 
IsKey() const7904 bool Block::IsKey() const {
7905   return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
7906 }
7907 
SetKey(bool bKey)7908 void Block::SetKey(bool bKey) {
7909   if (bKey)
7910     m_flags |= static_cast<unsigned char>(1 << 7);
7911   else
7912     m_flags &= 0x7F;
7913 }
7914 
IsInvisible() const7915 bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
7916 
GetLacing() const7917 Block::Lacing Block::GetLacing() const {
7918   const int value = int(m_flags & 0x06) >> 1;
7919   return static_cast<Lacing>(value);
7920 }
7921 
GetFrameCount() const7922 int Block::GetFrameCount() const { return m_frame_count; }
7923 
GetFrame(int idx) const7924 const Block::Frame& Block::GetFrame(int idx) const {
7925   assert(idx >= 0);
7926   assert(idx < m_frame_count);
7927 
7928   const Frame& f = m_frames[idx];
7929   assert(f.pos > 0);
7930   assert(f.len > 0);
7931 
7932   return f;
7933 }
7934 
Read(IMkvReader * pReader,unsigned char * buf) const7935 long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
7936   assert(pReader);
7937   assert(buf);
7938 
7939   const long status = pReader->Read(pos, len, buf);
7940   return status;
7941 }
7942 
GetDiscardPadding() const7943 long long Block::GetDiscardPadding() const { return m_discard_padding; }
7944 
7945 }  // namespace mkvparser
7946