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