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