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