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