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