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