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 return -1;
4237 }
4238 encryption_entries_end_ = encryption_entries_;
4239 }
4240
4241 pos = start;
4242 while (pos < stop) {
4243 long long id, size;
4244 long status = ParseElementHeader(pReader, pos, stop, id, size);
4245 if (status < 0) // error
4246 return status;
4247
4248 if (id == libwebm::kMkvContentEncodingOrder) {
4249 encoding_order_ = UnserializeUInt(pReader, pos, size);
4250 } else if (id == libwebm::kMkvContentEncodingScope) {
4251 encoding_scope_ = UnserializeUInt(pReader, pos, size);
4252 if (encoding_scope_ < 1)
4253 return -1;
4254 } else if (id == libwebm::kMkvContentEncodingType) {
4255 encoding_type_ = UnserializeUInt(pReader, pos, size);
4256 } else if (id == libwebm::kMkvContentCompression) {
4257 ContentCompression* const compression =
4258 new (std::nothrow) ContentCompression();
4259 if (!compression)
4260 return -1;
4261
4262 status = ParseCompressionEntry(pos, size, pReader, compression);
4263 if (status) {
4264 delete compression;
4265 return status;
4266 }
4267 *compression_entries_end_++ = compression;
4268 } else if (id == libwebm::kMkvContentEncryption) {
4269 ContentEncryption* const encryption =
4270 new (std::nothrow) ContentEncryption();
4271 if (!encryption)
4272 return -1;
4273
4274 status = ParseEncryptionEntry(pos, size, pReader, encryption);
4275 if (status) {
4276 delete encryption;
4277 return status;
4278 }
4279 *encryption_entries_end_++ = encryption;
4280 }
4281
4282 pos += size; // consume payload
4283 if (pos > stop)
4284 return E_FILE_FORMAT_INVALID;
4285 }
4286
4287 if (pos != stop)
4288 return E_FILE_FORMAT_INVALID;
4289 return 0;
4290 }
4291
ParseCompressionEntry(long long start,long long size,IMkvReader * pReader,ContentCompression * compression)4292 long ContentEncoding::ParseCompressionEntry(long long start, long long size,
4293 IMkvReader* pReader,
4294 ContentCompression* compression) {
4295 assert(pReader);
4296 assert(compression);
4297
4298 long long pos = start;
4299 const long long stop = start + size;
4300
4301 bool valid = false;
4302
4303 while (pos < stop) {
4304 long long id, size;
4305 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4306 if (status < 0) // error
4307 return status;
4308
4309 if (id == libwebm::kMkvContentCompAlgo) {
4310 long long algo = UnserializeUInt(pReader, pos, size);
4311 if (algo < 0)
4312 return E_FILE_FORMAT_INVALID;
4313 compression->algo = algo;
4314 valid = true;
4315 } else if (id == libwebm::kMkvContentCompSettings) {
4316 if (size <= 0)
4317 return E_FILE_FORMAT_INVALID;
4318
4319 const size_t buflen = static_cast<size_t>(size);
4320 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4321 if (buf == NULL)
4322 return -1;
4323
4324 const int read_status =
4325 pReader->Read(pos, static_cast<long>(buflen), buf);
4326 if (read_status) {
4327 delete[] buf;
4328 return status;
4329 }
4330
4331 compression->settings = buf;
4332 compression->settings_len = buflen;
4333 }
4334
4335 pos += size; // consume payload
4336 if (pos > stop)
4337 return E_FILE_FORMAT_INVALID;
4338 }
4339
4340 // ContentCompAlgo is mandatory
4341 if (!valid)
4342 return E_FILE_FORMAT_INVALID;
4343
4344 return 0;
4345 }
4346
ParseEncryptionEntry(long long start,long long size,IMkvReader * pReader,ContentEncryption * encryption)4347 long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
4348 IMkvReader* pReader,
4349 ContentEncryption* encryption) {
4350 assert(pReader);
4351 assert(encryption);
4352
4353 long long pos = start;
4354 const long long stop = start + size;
4355
4356 while (pos < stop) {
4357 long long id, size;
4358 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4359 if (status < 0) // error
4360 return status;
4361
4362 if (id == libwebm::kMkvContentEncAlgo) {
4363 encryption->algo = UnserializeUInt(pReader, pos, size);
4364 if (encryption->algo != 5)
4365 return E_FILE_FORMAT_INVALID;
4366 } else if (id == libwebm::kMkvContentEncKeyID) {
4367 delete[] encryption->key_id;
4368 encryption->key_id = NULL;
4369 encryption->key_id_len = 0;
4370
4371 if (size <= 0)
4372 return E_FILE_FORMAT_INVALID;
4373
4374 const size_t buflen = static_cast<size_t>(size);
4375 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4376 if (buf == NULL)
4377 return -1;
4378
4379 const int read_status =
4380 pReader->Read(pos, static_cast<long>(buflen), buf);
4381 if (read_status) {
4382 delete[] buf;
4383 return status;
4384 }
4385
4386 encryption->key_id = buf;
4387 encryption->key_id_len = buflen;
4388 } else if (id == libwebm::kMkvContentSignature) {
4389 delete[] encryption->signature;
4390 encryption->signature = NULL;
4391 encryption->signature_len = 0;
4392
4393 if (size <= 0)
4394 return E_FILE_FORMAT_INVALID;
4395
4396 const size_t buflen = static_cast<size_t>(size);
4397 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4398 if (buf == NULL)
4399 return -1;
4400
4401 const int read_status =
4402 pReader->Read(pos, static_cast<long>(buflen), buf);
4403 if (read_status) {
4404 delete[] buf;
4405 return status;
4406 }
4407
4408 encryption->signature = buf;
4409 encryption->signature_len = buflen;
4410 } else if (id == libwebm::kMkvContentSigKeyID) {
4411 delete[] encryption->sig_key_id;
4412 encryption->sig_key_id = NULL;
4413 encryption->sig_key_id_len = 0;
4414
4415 if (size <= 0)
4416 return E_FILE_FORMAT_INVALID;
4417
4418 const size_t buflen = static_cast<size_t>(size);
4419 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
4420 if (buf == NULL)
4421 return -1;
4422
4423 const int read_status =
4424 pReader->Read(pos, static_cast<long>(buflen), buf);
4425 if (read_status) {
4426 delete[] buf;
4427 return status;
4428 }
4429
4430 encryption->sig_key_id = buf;
4431 encryption->sig_key_id_len = buflen;
4432 } else if (id == libwebm::kMkvContentSigAlgo) {
4433 encryption->sig_algo = UnserializeUInt(pReader, pos, size);
4434 } else if (id == libwebm::kMkvContentSigHashAlgo) {
4435 encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
4436 } else if (id == libwebm::kMkvContentEncAESSettings) {
4437 const long status = ParseContentEncAESSettingsEntry(
4438 pos, size, pReader, &encryption->aes_settings);
4439 if (status)
4440 return status;
4441 }
4442
4443 pos += size; // consume payload
4444 if (pos > stop)
4445 return E_FILE_FORMAT_INVALID;
4446 }
4447
4448 return 0;
4449 }
4450
Track(Segment * pSegment,long long element_start,long long element_size)4451 Track::Track(Segment* pSegment, long long element_start, long long element_size)
4452 : m_pSegment(pSegment),
4453 m_element_start(element_start),
4454 m_element_size(element_size),
4455 content_encoding_entries_(NULL),
4456 content_encoding_entries_end_(NULL) {}
4457
~Track()4458 Track::~Track() {
4459 Info& info = const_cast<Info&>(m_info);
4460 info.Clear();
4461
4462 ContentEncoding** i = content_encoding_entries_;
4463 ContentEncoding** const j = content_encoding_entries_end_;
4464
4465 while (i != j) {
4466 ContentEncoding* const encoding = *i++;
4467 delete encoding;
4468 }
4469
4470 delete[] content_encoding_entries_;
4471 }
4472
Create(Segment * pSegment,const Info & info,long long element_start,long long element_size,Track * & pResult)4473 long Track::Create(Segment* pSegment, const Info& info, long long element_start,
4474 long long element_size, Track*& pResult) {
4475 if (pResult)
4476 return -1;
4477
4478 Track* const pTrack =
4479 new (std::nothrow) Track(pSegment, element_start, element_size);
4480
4481 if (pTrack == NULL)
4482 return -1; // generic error
4483
4484 const int status = info.Copy(pTrack->m_info);
4485
4486 if (status) { // error
4487 delete pTrack;
4488 return status;
4489 }
4490
4491 pResult = pTrack;
4492 return 0; // success
4493 }
4494
Info()4495 Track::Info::Info()
4496 : uid(0),
4497 defaultDuration(0),
4498 codecDelay(0),
4499 seekPreRoll(0),
4500 nameAsUTF8(NULL),
4501 language(NULL),
4502 codecId(NULL),
4503 codecNameAsUTF8(NULL),
4504 codecPrivate(NULL),
4505 codecPrivateSize(0),
4506 lacing(false) {}
4507
~Info()4508 Track::Info::~Info() { Clear(); }
4509
Clear()4510 void Track::Info::Clear() {
4511 delete[] nameAsUTF8;
4512 nameAsUTF8 = NULL;
4513
4514 delete[] language;
4515 language = NULL;
4516
4517 delete[] codecId;
4518 codecId = NULL;
4519
4520 delete[] codecPrivate;
4521 codecPrivate = NULL;
4522 codecPrivateSize = 0;
4523
4524 delete[] codecNameAsUTF8;
4525 codecNameAsUTF8 = NULL;
4526 }
4527
CopyStr(char * Info::* str,Info & dst_) const4528 int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
4529 if (str == static_cast<char * Info::*>(NULL))
4530 return -1;
4531
4532 char*& dst = dst_.*str;
4533
4534 if (dst) // should be NULL already
4535 return -1;
4536
4537 const char* const src = this->*str;
4538
4539 if (src == NULL)
4540 return 0;
4541
4542 const size_t len = strlen(src);
4543
4544 dst = SafeArrayAlloc<char>(1, len + 1);
4545
4546 if (dst == NULL)
4547 return -1;
4548
4549 strcpy(dst, src);
4550
4551 return 0;
4552 }
4553
Copy(Info & dst) const4554 int Track::Info::Copy(Info& dst) const {
4555 if (&dst == this)
4556 return 0;
4557
4558 dst.type = type;
4559 dst.number = number;
4560 dst.defaultDuration = defaultDuration;
4561 dst.codecDelay = codecDelay;
4562 dst.seekPreRoll = seekPreRoll;
4563 dst.uid = uid;
4564 dst.lacing = lacing;
4565 dst.settings = settings;
4566
4567 // We now copy the string member variables from src to dst.
4568 // This involves memory allocation so in principle the operation
4569 // can fail (indeed, that's why we have Info::Copy), so we must
4570 // report this to the caller. An error return from this function
4571 // therefore implies that the copy was only partially successful.
4572
4573 if (int status = CopyStr(&Info::nameAsUTF8, dst))
4574 return status;
4575
4576 if (int status = CopyStr(&Info::language, dst))
4577 return status;
4578
4579 if (int status = CopyStr(&Info::codecId, dst))
4580 return status;
4581
4582 if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
4583 return status;
4584
4585 if (codecPrivateSize > 0) {
4586 if (codecPrivate == NULL)
4587 return -1;
4588
4589 if (dst.codecPrivate)
4590 return -1;
4591
4592 if (dst.codecPrivateSize != 0)
4593 return -1;
4594
4595 dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
4596
4597 if (dst.codecPrivate == NULL)
4598 return -1;
4599
4600 memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
4601 dst.codecPrivateSize = codecPrivateSize;
4602 }
4603
4604 return 0;
4605 }
4606
GetEOS() const4607 const BlockEntry* Track::GetEOS() const { return &m_eos; }
4608
GetType() const4609 long Track::GetType() const { return m_info.type; }
4610
GetNumber() const4611 long Track::GetNumber() const { return m_info.number; }
4612
GetUid() const4613 unsigned long long Track::GetUid() const { return m_info.uid; }
4614
GetNameAsUTF8() const4615 const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
4616
GetLanguage() const4617 const char* Track::GetLanguage() const { return m_info.language; }
4618
GetCodecNameAsUTF8() const4619 const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
4620
GetCodecId() const4621 const char* Track::GetCodecId() const { return m_info.codecId; }
4622
GetCodecPrivate(size_t & size) const4623 const unsigned char* Track::GetCodecPrivate(size_t& size) const {
4624 size = m_info.codecPrivateSize;
4625 return m_info.codecPrivate;
4626 }
4627
GetLacing() const4628 bool Track::GetLacing() const { return m_info.lacing; }
4629
GetDefaultDuration() const4630 unsigned long long Track::GetDefaultDuration() const {
4631 return m_info.defaultDuration;
4632 }
4633
GetCodecDelay() const4634 unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
4635
GetSeekPreRoll() const4636 unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
4637
GetFirst(const BlockEntry * & pBlockEntry) const4638 long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
4639 const Cluster* pCluster = m_pSegment->GetFirst();
4640
4641 for (int i = 0;;) {
4642 if (pCluster == NULL) {
4643 pBlockEntry = GetEOS();
4644 return 1;
4645 }
4646
4647 if (pCluster->EOS()) {
4648 if (m_pSegment->DoneParsing()) {
4649 pBlockEntry = GetEOS();
4650 return 1;
4651 }
4652
4653 pBlockEntry = 0;
4654 return E_BUFFER_NOT_FULL;
4655 }
4656
4657 long status = pCluster->GetFirst(pBlockEntry);
4658
4659 if (status < 0) // error
4660 return status;
4661
4662 if (pBlockEntry == 0) { // empty cluster
4663 pCluster = m_pSegment->GetNext(pCluster);
4664 continue;
4665 }
4666
4667 for (;;) {
4668 const Block* const pBlock = pBlockEntry->GetBlock();
4669 assert(pBlock);
4670
4671 const long long tn = pBlock->GetTrackNumber();
4672
4673 if ((tn == m_info.number) && VetEntry(pBlockEntry))
4674 return 0;
4675
4676 const BlockEntry* pNextEntry;
4677
4678 status = pCluster->GetNext(pBlockEntry, pNextEntry);
4679
4680 if (status < 0) // error
4681 return status;
4682
4683 if (pNextEntry == 0)
4684 break;
4685
4686 pBlockEntry = pNextEntry;
4687 }
4688
4689 ++i;
4690
4691 if (i >= 100)
4692 break;
4693
4694 pCluster = m_pSegment->GetNext(pCluster);
4695 }
4696
4697 // NOTE: if we get here, it means that we didn't find a block with
4698 // a matching track number. We interpret that as an error (which
4699 // might be too conservative).
4700
4701 pBlockEntry = GetEOS(); // so we can return a non-NULL value
4702 return 1;
4703 }
4704
GetNext(const BlockEntry * pCurrEntry,const BlockEntry * & pNextEntry) const4705 long Track::GetNext(const BlockEntry* pCurrEntry,
4706 const BlockEntry*& pNextEntry) const {
4707 assert(pCurrEntry);
4708 assert(!pCurrEntry->EOS()); //?
4709
4710 const Block* const pCurrBlock = pCurrEntry->GetBlock();
4711 assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
4712 if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
4713 return -1;
4714
4715 const Cluster* pCluster = pCurrEntry->GetCluster();
4716 assert(pCluster);
4717 assert(!pCluster->EOS());
4718
4719 long status = pCluster->GetNext(pCurrEntry, pNextEntry);
4720
4721 if (status < 0) // error
4722 return status;
4723
4724 for (int i = 0;;) {
4725 while (pNextEntry) {
4726 const Block* const pNextBlock = pNextEntry->GetBlock();
4727 assert(pNextBlock);
4728
4729 if (pNextBlock->GetTrackNumber() == m_info.number)
4730 return 0;
4731
4732 pCurrEntry = pNextEntry;
4733
4734 status = pCluster->GetNext(pCurrEntry, pNextEntry);
4735
4736 if (status < 0) // error
4737 return status;
4738 }
4739
4740 pCluster = m_pSegment->GetNext(pCluster);
4741
4742 if (pCluster == NULL) {
4743 pNextEntry = GetEOS();
4744 return 1;
4745 }
4746
4747 if (pCluster->EOS()) {
4748 if (m_pSegment->DoneParsing()) {
4749 pNextEntry = GetEOS();
4750 return 1;
4751 }
4752
4753 // TODO: there is a potential O(n^2) problem here: we tell the
4754 // caller to (pre)load another cluster, which he does, but then he
4755 // calls GetNext again, which repeats the same search. This is
4756 // a pathological case, since the only way it can happen is if
4757 // there exists a long sequence of clusters none of which contain a
4758 // block from this track. One way around this problem is for the
4759 // caller to be smarter when he loads another cluster: don't call
4760 // us back until you have a cluster that contains a block from this
4761 // track. (Of course, that's not cheap either, since our caller
4762 // would have to scan the each cluster as it's loaded, so that
4763 // would just push back the problem.)
4764
4765 pNextEntry = NULL;
4766 return E_BUFFER_NOT_FULL;
4767 }
4768
4769 status = pCluster->GetFirst(pNextEntry);
4770
4771 if (status < 0) // error
4772 return status;
4773
4774 if (pNextEntry == NULL) // empty cluster
4775 continue;
4776
4777 ++i;
4778
4779 if (i >= 100)
4780 break;
4781 }
4782
4783 // NOTE: if we get here, it means that we didn't find a block with
4784 // a matching track number after lots of searching, so we give
4785 // up trying.
4786
4787 pNextEntry = GetEOS(); // so we can return a non-NULL value
4788 return 1;
4789 }
4790
VetEntry(const BlockEntry * pBlockEntry) const4791 bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
4792 assert(pBlockEntry);
4793 const Block* const pBlock = pBlockEntry->GetBlock();
4794 assert(pBlock);
4795 assert(pBlock->GetTrackNumber() == m_info.number);
4796 if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
4797 return false;
4798
4799 // This function is used during a seek to determine whether the
4800 // frame is a valid seek target. This default function simply
4801 // returns true, which means all frames are valid seek targets.
4802 // It gets overridden by the VideoTrack class, because only video
4803 // keyframes can be used as seek target.
4804
4805 return true;
4806 }
4807
Seek(long long time_ns,const BlockEntry * & pResult) const4808 long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
4809 const long status = GetFirst(pResult);
4810
4811 if (status < 0) // buffer underflow, etc
4812 return status;
4813
4814 assert(pResult);
4815
4816 if (pResult->EOS())
4817 return 0;
4818
4819 const Cluster* pCluster = pResult->GetCluster();
4820 assert(pCluster);
4821 assert(pCluster->GetIndex() >= 0);
4822
4823 if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
4824 return 0;
4825
4826 Cluster** const clusters = m_pSegment->m_clusters;
4827 assert(clusters);
4828
4829 const long count = m_pSegment->GetCount(); // loaded only, not preloaded
4830 assert(count > 0);
4831
4832 Cluster** const i = clusters + pCluster->GetIndex();
4833 assert(i);
4834 assert(*i == pCluster);
4835 assert(pCluster->GetTime() <= time_ns);
4836
4837 Cluster** const j = clusters + count;
4838
4839 Cluster** lo = i;
4840 Cluster** hi = j;
4841
4842 while (lo < hi) {
4843 // INVARIANT:
4844 //[i, lo) <= time_ns
4845 //[lo, hi) ?
4846 //[hi, j) > time_ns
4847
4848 Cluster** const mid = lo + (hi - lo) / 2;
4849 assert(mid < hi);
4850
4851 pCluster = *mid;
4852 assert(pCluster);
4853 assert(pCluster->GetIndex() >= 0);
4854 assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
4855
4856 const long long t = pCluster->GetTime();
4857
4858 if (t <= time_ns)
4859 lo = mid + 1;
4860 else
4861 hi = mid;
4862
4863 assert(lo <= hi);
4864 }
4865
4866 assert(lo == hi);
4867 assert(lo > i);
4868 assert(lo <= j);
4869
4870 while (lo > i) {
4871 pCluster = *--lo;
4872 assert(pCluster);
4873 assert(pCluster->GetTime() <= time_ns);
4874
4875 pResult = pCluster->GetEntry(this);
4876
4877 if ((pResult != 0) && !pResult->EOS())
4878 return 0;
4879
4880 // landed on empty cluster (no entries)
4881 }
4882
4883 pResult = GetEOS(); // weird
4884 return 0;
4885 }
4886
GetContentEncodingByIndex(unsigned long idx) const4887 const ContentEncoding* Track::GetContentEncodingByIndex(
4888 unsigned long idx) const {
4889 const ptrdiff_t count =
4890 content_encoding_entries_end_ - content_encoding_entries_;
4891 assert(count >= 0);
4892
4893 if (idx >= static_cast<unsigned long>(count))
4894 return NULL;
4895
4896 return content_encoding_entries_[idx];
4897 }
4898
GetContentEncodingCount() const4899 unsigned long Track::GetContentEncodingCount() const {
4900 const ptrdiff_t count =
4901 content_encoding_entries_end_ - content_encoding_entries_;
4902 assert(count >= 0);
4903
4904 return static_cast<unsigned long>(count);
4905 }
4906
ParseContentEncodingsEntry(long long start,long long size)4907 long Track::ParseContentEncodingsEntry(long long start, long long size) {
4908 IMkvReader* const pReader = m_pSegment->m_pReader;
4909 assert(pReader);
4910
4911 long long pos = start;
4912 const long long stop = start + size;
4913
4914 // Count ContentEncoding elements.
4915 int count = 0;
4916 while (pos < stop) {
4917 long long id, size;
4918 const long status = ParseElementHeader(pReader, pos, stop, id, size);
4919 if (status < 0) // error
4920 return status;
4921
4922 // pos now designates start of element
4923 if (id == libwebm::kMkvContentEncoding)
4924 ++count;
4925
4926 pos += size; // consume payload
4927 if (pos > stop)
4928 return E_FILE_FORMAT_INVALID;
4929 }
4930
4931 if (count <= 0)
4932 return -1;
4933
4934 content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
4935 if (!content_encoding_entries_)
4936 return -1;
4937
4938 content_encoding_entries_end_ = content_encoding_entries_;
4939
4940 pos = start;
4941 while (pos < stop) {
4942 long long id, size;
4943 long status = ParseElementHeader(pReader, pos, stop, id, size);
4944 if (status < 0) // error
4945 return status;
4946
4947 // pos now designates start of element
4948 if (id == libwebm::kMkvContentEncoding) {
4949 ContentEncoding* const content_encoding =
4950 new (std::nothrow) ContentEncoding();
4951 if (!content_encoding)
4952 return -1;
4953
4954 status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
4955 if (status) {
4956 delete content_encoding;
4957 return status;
4958 }
4959
4960 *content_encoding_entries_end_++ = content_encoding;
4961 }
4962
4963 pos += size; // consume payload
4964 if (pos > stop)
4965 return E_FILE_FORMAT_INVALID;
4966 }
4967
4968 if (pos != stop)
4969 return E_FILE_FORMAT_INVALID;
4970
4971 return 0;
4972 }
4973
EOSBlock()4974 Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
4975
GetKind() const4976 BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
4977
GetBlock() const4978 const Block* Track::EOSBlock::GetBlock() const { return NULL; }
4979
Parse(IMkvReader * reader,long long read_pos,long long value_size,bool is_x,PrimaryChromaticity ** chromaticity)4980 bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos,
4981 long long value_size, bool is_x,
4982 PrimaryChromaticity** chromaticity) {
4983 if (!reader)
4984 return false;
4985
4986 std::auto_ptr<PrimaryChromaticity> chromaticity_ptr;
4987
4988 if (!*chromaticity) {
4989 chromaticity_ptr.reset(new PrimaryChromaticity());
4990 } else {
4991 chromaticity_ptr.reset(*chromaticity);
4992 }
4993
4994 if (!chromaticity_ptr.get())
4995 return false;
4996
4997 float* value = is_x ? &chromaticity_ptr->x : &chromaticity_ptr->y;
4998
4999 double parser_value = 0;
5000 const long long value_parse_status =
5001 UnserializeFloat(reader, read_pos, value_size, parser_value);
5002
5003 *value = static_cast<float>(parser_value);
5004
5005 if (value_parse_status < 0 || *value < 0.0 || *value > 1.0)
5006 return false;
5007
5008 *chromaticity = chromaticity_ptr.release();
5009 return true;
5010 }
5011
Parse(IMkvReader * reader,long long mm_start,long long mm_size,MasteringMetadata ** mm)5012 bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
5013 long long mm_size, MasteringMetadata** mm) {
5014 if (!reader || *mm)
5015 return false;
5016
5017 std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
5018 if (!mm_ptr.get())
5019 return false;
5020
5021 const long long mm_end = mm_start + mm_size;
5022 long long read_pos = mm_start;
5023
5024 while (read_pos < mm_end) {
5025 long long child_id = 0;
5026 long long child_size = 0;
5027
5028 const long long status =
5029 ParseElementHeader(reader, read_pos, mm_end, child_id, child_size);
5030 if (status < 0)
5031 return false;
5032
5033 if (child_id == libwebm::kMkvLuminanceMax) {
5034 double value = 0;
5035 const long long value_parse_status =
5036 UnserializeFloat(reader, read_pos, child_size, value);
5037 mm_ptr->luminance_max = static_cast<float>(value);
5038 if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 ||
5039 mm_ptr->luminance_max > 9999.99) {
5040 return false;
5041 }
5042 } else if (child_id == libwebm::kMkvLuminanceMin) {
5043 double value = 0;
5044 const long long value_parse_status =
5045 UnserializeFloat(reader, read_pos, child_size, value);
5046 mm_ptr->luminance_min = static_cast<float>(value);
5047 if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 ||
5048 mm_ptr->luminance_min > 999.9999) {
5049 return false;
5050 }
5051 } else {
5052 bool is_x = false;
5053 PrimaryChromaticity** chromaticity;
5054 switch (child_id) {
5055 case libwebm::kMkvPrimaryRChromaticityX:
5056 case libwebm::kMkvPrimaryRChromaticityY:
5057 is_x = child_id == libwebm::kMkvPrimaryRChromaticityX;
5058 chromaticity = &mm_ptr->r;
5059 break;
5060 case libwebm::kMkvPrimaryGChromaticityX:
5061 case libwebm::kMkvPrimaryGChromaticityY:
5062 is_x = child_id == libwebm::kMkvPrimaryGChromaticityX;
5063 chromaticity = &mm_ptr->g;
5064 break;
5065 case libwebm::kMkvPrimaryBChromaticityX:
5066 case libwebm::kMkvPrimaryBChromaticityY:
5067 is_x = child_id == libwebm::kMkvPrimaryBChromaticityX;
5068 chromaticity = &mm_ptr->b;
5069 break;
5070 case libwebm::kMkvWhitePointChromaticityX:
5071 case libwebm::kMkvWhitePointChromaticityY:
5072 is_x = child_id == libwebm::kMkvWhitePointChromaticityX;
5073 chromaticity = &mm_ptr->white_point;
5074 break;
5075 default:
5076 return false;
5077 }
5078 const bool value_parse_status = PrimaryChromaticity::Parse(
5079 reader, read_pos, child_size, is_x, chromaticity);
5080 if (!value_parse_status)
5081 return false;
5082 }
5083
5084 read_pos += child_size;
5085 if (read_pos > mm_end)
5086 return false;
5087 }
5088
5089 *mm = mm_ptr.release();
5090 return true;
5091 }
5092
Parse(IMkvReader * reader,long long colour_start,long long colour_size,Colour ** colour)5093 bool Colour::Parse(IMkvReader* reader, long long colour_start,
5094 long long colour_size, Colour** colour) {
5095 if (!reader || *colour)
5096 return false;
5097
5098 std::auto_ptr<Colour> colour_ptr(new Colour());
5099 if (!colour_ptr.get())
5100 return false;
5101
5102 const long long colour_end = colour_start + colour_size;
5103 long long read_pos = colour_start;
5104
5105 while (read_pos < colour_end) {
5106 long long child_id = 0;
5107 long long child_size = 0;
5108
5109 const long status =
5110 ParseElementHeader(reader, read_pos, colour_end, child_id, child_size);
5111 if (status < 0)
5112 return false;
5113
5114 if (child_id == libwebm::kMkvMatrixCoefficients) {
5115 colour_ptr->matrix_coefficients =
5116 UnserializeUInt(reader, read_pos, child_size);
5117 if (colour_ptr->matrix_coefficients < 0)
5118 return false;
5119 } else if (child_id == libwebm::kMkvBitsPerChannel) {
5120 colour_ptr->bits_per_channel =
5121 UnserializeUInt(reader, read_pos, child_size);
5122 if (colour_ptr->bits_per_channel < 0)
5123 return false;
5124 } else if (child_id == libwebm::kMkvChromaSubsamplingHorz) {
5125 colour_ptr->chroma_subsampling_horz =
5126 UnserializeUInt(reader, read_pos, child_size);
5127 if (colour_ptr->chroma_subsampling_horz < 0)
5128 return false;
5129 } else if (child_id == libwebm::kMkvChromaSubsamplingVert) {
5130 colour_ptr->chroma_subsampling_vert =
5131 UnserializeUInt(reader, read_pos, child_size);
5132 if (colour_ptr->chroma_subsampling_vert < 0)
5133 return false;
5134 } else if (child_id == libwebm::kMkvCbSubsamplingHorz) {
5135 colour_ptr->cb_subsampling_horz =
5136 UnserializeUInt(reader, read_pos, child_size);
5137 if (colour_ptr->cb_subsampling_horz < 0)
5138 return false;
5139 } else if (child_id == libwebm::kMkvCbSubsamplingVert) {
5140 colour_ptr->cb_subsampling_vert =
5141 UnserializeUInt(reader, read_pos, child_size);
5142 if (colour_ptr->cb_subsampling_vert < 0)
5143 return false;
5144 } else if (child_id == libwebm::kMkvChromaSitingHorz) {
5145 colour_ptr->chroma_siting_horz =
5146 UnserializeUInt(reader, read_pos, child_size);
5147 if (colour_ptr->chroma_siting_horz < 0)
5148 return false;
5149 } else if (child_id == libwebm::kMkvChromaSitingVert) {
5150 colour_ptr->chroma_siting_vert =
5151 UnserializeUInt(reader, read_pos, child_size);
5152 if (colour_ptr->chroma_siting_vert < 0)
5153 return false;
5154 } else if (child_id == libwebm::kMkvRange) {
5155 colour_ptr->range = UnserializeUInt(reader, read_pos, child_size);
5156 if (colour_ptr->range < 0)
5157 return false;
5158 } else if (child_id == libwebm::kMkvTransferCharacteristics) {
5159 colour_ptr->transfer_characteristics =
5160 UnserializeUInt(reader, read_pos, child_size);
5161 if (colour_ptr->transfer_characteristics < 0)
5162 return false;
5163 } else if (child_id == libwebm::kMkvPrimaries) {
5164 colour_ptr->primaries = UnserializeUInt(reader, read_pos, child_size);
5165 if (colour_ptr->primaries < 0)
5166 return false;
5167 } else if (child_id == libwebm::kMkvMaxCLL) {
5168 colour_ptr->max_cll = UnserializeUInt(reader, read_pos, child_size);
5169 if (colour_ptr->max_cll < 0)
5170 return false;
5171 } else if (child_id == libwebm::kMkvMaxFALL) {
5172 colour_ptr->max_fall = UnserializeUInt(reader, read_pos, child_size);
5173 if (colour_ptr->max_fall < 0)
5174 return false;
5175 } else if (child_id == libwebm::kMkvMasteringMetadata) {
5176 if (!MasteringMetadata::Parse(reader, read_pos, child_size,
5177 &colour_ptr->mastering_metadata))
5178 return false;
5179 } else {
5180 return false;
5181 }
5182
5183 read_pos += child_size;
5184 if (read_pos > colour_end)
5185 return false;
5186 }
5187 *colour = colour_ptr.release();
5188 return true;
5189 }
5190
VideoTrack(Segment * pSegment,long long element_start,long long element_size)5191 VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
5192 long long element_size)
5193 : Track(pSegment, element_start, element_size), m_colour(NULL) {}
5194
~VideoTrack()5195 VideoTrack::~VideoTrack() { delete m_colour; }
5196
Parse(Segment * pSegment,const Info & info,long long element_start,long long element_size,VideoTrack * & pResult)5197 long VideoTrack::Parse(Segment* pSegment, const Info& info,
5198 long long element_start, long long element_size,
5199 VideoTrack*& pResult) {
5200 if (pResult)
5201 return -1;
5202
5203 if (info.type != Track::kVideo)
5204 return -1;
5205
5206 long long width = 0;
5207 long long height = 0;
5208 long long display_width = 0;
5209 long long display_height = 0;
5210 long long display_unit = 0;
5211 long long stereo_mode = 0;
5212
5213 double rate = 0.0;
5214
5215 IMkvReader* const pReader = pSegment->m_pReader;
5216
5217 const Settings& s = info.settings;
5218 assert(s.start >= 0);
5219 assert(s.size >= 0);
5220
5221 long long pos = s.start;
5222 assert(pos >= 0);
5223
5224 const long long stop = pos + s.size;
5225
5226 Colour* colour = NULL;
5227
5228 while (pos < stop) {
5229 long long id, size;
5230
5231 const long status = ParseElementHeader(pReader, pos, stop, id, size);
5232
5233 if (status < 0) // error
5234 return status;
5235
5236 if (id == libwebm::kMkvPixelWidth) {
5237 width = UnserializeUInt(pReader, pos, size);
5238
5239 if (width <= 0)
5240 return E_FILE_FORMAT_INVALID;
5241 } else if (id == libwebm::kMkvPixelHeight) {
5242 height = UnserializeUInt(pReader, pos, size);
5243
5244 if (height <= 0)
5245 return E_FILE_FORMAT_INVALID;
5246 } else if (id == libwebm::kMkvDisplayWidth) {
5247 display_width = UnserializeUInt(pReader, pos, size);
5248
5249 if (display_width <= 0)
5250 return E_FILE_FORMAT_INVALID;
5251 } else if (id == libwebm::kMkvDisplayHeight) {
5252 display_height = UnserializeUInt(pReader, pos, size);
5253
5254 if (display_height <= 0)
5255 return E_FILE_FORMAT_INVALID;
5256 } else if (id == libwebm::kMkvDisplayUnit) {
5257 display_unit = UnserializeUInt(pReader, pos, size);
5258
5259 if (display_unit < 0)
5260 return E_FILE_FORMAT_INVALID;
5261 } else if (id == libwebm::kMkvStereoMode) {
5262 stereo_mode = UnserializeUInt(pReader, pos, size);
5263
5264 if (stereo_mode < 0)
5265 return E_FILE_FORMAT_INVALID;
5266 } else if (id == libwebm::kMkvFrameRate) {
5267 const long status = UnserializeFloat(pReader, pos, size, rate);
5268
5269 if (status < 0)
5270 return status;
5271
5272 if (rate <= 0)
5273 return E_FILE_FORMAT_INVALID;
5274 } else if (id == libwebm::kMkvColour) {
5275 if (!Colour::Parse(pReader, pos, size, &colour))
5276 return E_FILE_FORMAT_INVALID;
5277 }
5278
5279 pos += size; // consume payload
5280 if (pos > stop)
5281 return E_FILE_FORMAT_INVALID;
5282 }
5283
5284 if (pos != stop)
5285 return E_FILE_FORMAT_INVALID;
5286
5287 VideoTrack* const pTrack =
5288 new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
5289
5290 if (pTrack == NULL)
5291 return -1; // generic error
5292
5293 const int status = info.Copy(pTrack->m_info);
5294
5295 if (status) { // error
5296 delete pTrack;
5297 return status;
5298 }
5299
5300 pTrack->m_width = width;
5301 pTrack->m_height = height;
5302 pTrack->m_display_width = display_width;
5303 pTrack->m_display_height = display_height;
5304 pTrack->m_display_unit = display_unit;
5305 pTrack->m_stereo_mode = stereo_mode;
5306 pTrack->m_rate = rate;
5307 pTrack->m_colour = colour;
5308
5309 pResult = pTrack;
5310 return 0; // success
5311 }
5312
VetEntry(const BlockEntry * pBlockEntry) const5313 bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
5314 return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
5315 }
5316
Seek(long long time_ns,const BlockEntry * & pResult) const5317 long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
5318 const long status = GetFirst(pResult);
5319
5320 if (status < 0) // buffer underflow, etc
5321 return status;
5322
5323 assert(pResult);
5324
5325 if (pResult->EOS())
5326 return 0;
5327
5328 const Cluster* pCluster = pResult->GetCluster();
5329 assert(pCluster);
5330 assert(pCluster->GetIndex() >= 0);
5331
5332 if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
5333 return 0;
5334
5335 Cluster** const clusters = m_pSegment->m_clusters;
5336 assert(clusters);
5337
5338 const long count = m_pSegment->GetCount(); // loaded only, not pre-loaded
5339 assert(count > 0);
5340
5341 Cluster** const i = clusters + pCluster->GetIndex();
5342 assert(i);
5343 assert(*i == pCluster);
5344 assert(pCluster->GetTime() <= time_ns);
5345
5346 Cluster** const j = clusters + count;
5347
5348 Cluster** lo = i;
5349 Cluster** hi = j;
5350
5351 while (lo < hi) {
5352 // INVARIANT:
5353 //[i, lo) <= time_ns
5354 //[lo, hi) ?
5355 //[hi, j) > time_ns
5356
5357 Cluster** const mid = lo + (hi - lo) / 2;
5358 assert(mid < hi);
5359
5360 pCluster = *mid;
5361 assert(pCluster);
5362 assert(pCluster->GetIndex() >= 0);
5363 assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
5364
5365 const long long t = pCluster->GetTime();
5366
5367 if (t <= time_ns)
5368 lo = mid + 1;
5369 else
5370 hi = mid;
5371
5372 assert(lo <= hi);
5373 }
5374
5375 assert(lo == hi);
5376 assert(lo > i);
5377 assert(lo <= j);
5378
5379 pCluster = *--lo;
5380 assert(pCluster);
5381 assert(pCluster->GetTime() <= time_ns);
5382
5383 pResult = pCluster->GetEntry(this, time_ns);
5384
5385 if ((pResult != 0) && !pResult->EOS()) // found a keyframe
5386 return 0;
5387
5388 while (lo != i) {
5389 pCluster = *--lo;
5390 assert(pCluster);
5391 assert(pCluster->GetTime() <= time_ns);
5392
5393 pResult = pCluster->GetEntry(this, time_ns);
5394
5395 if ((pResult != 0) && !pResult->EOS())
5396 return 0;
5397 }
5398
5399 // weird: we're on the first cluster, but no keyframe found
5400 // should never happen but we must return something anyway
5401
5402 pResult = GetEOS();
5403 return 0;
5404 }
5405
GetColour() const5406 Colour* VideoTrack::GetColour() const { return m_colour; }
5407
GetWidth() const5408 long long VideoTrack::GetWidth() const { return m_width; }
5409
GetHeight() const5410 long long VideoTrack::GetHeight() const { return m_height; }
5411
GetDisplayWidth() const5412 long long VideoTrack::GetDisplayWidth() const {
5413 return m_display_width > 0 ? m_display_width : GetWidth();
5414 }
5415
GetDisplayHeight() const5416 long long VideoTrack::GetDisplayHeight() const {
5417 return m_display_height > 0 ? m_display_height : GetHeight();
5418 }
5419
GetDisplayUnit() const5420 long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
5421
GetStereoMode() const5422 long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
5423
GetFrameRate() const5424 double VideoTrack::GetFrameRate() const { return m_rate; }
5425
AudioTrack(Segment * pSegment,long long element_start,long long element_size)5426 AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
5427 long long element_size)
5428 : Track(pSegment, element_start, element_size) {}
5429
Parse(Segment * pSegment,const Info & info,long long element_start,long long element_size,AudioTrack * & pResult)5430 long AudioTrack::Parse(Segment* pSegment, const Info& info,
5431 long long element_start, long long element_size,
5432 AudioTrack*& pResult) {
5433 if (pResult)
5434 return -1;
5435
5436 if (info.type != Track::kAudio)
5437 return -1;
5438
5439 IMkvReader* const pReader = pSegment->m_pReader;
5440
5441 const Settings& s = info.settings;
5442 assert(s.start >= 0);
5443 assert(s.size >= 0);
5444
5445 long long pos = s.start;
5446 assert(pos >= 0);
5447
5448 const long long stop = pos + s.size;
5449
5450 double rate = 8000.0; // MKV default
5451 long long channels = 1;
5452 long long bit_depth = 0;
5453
5454 while (pos < stop) {
5455 long long id, size;
5456
5457 long status = ParseElementHeader(pReader, pos, stop, id, size);
5458
5459 if (status < 0) // error
5460 return status;
5461
5462 if (id == libwebm::kMkvSamplingFrequency) {
5463 status = UnserializeFloat(pReader, pos, size, rate);
5464
5465 if (status < 0)
5466 return status;
5467
5468 if (rate <= 0)
5469 return E_FILE_FORMAT_INVALID;
5470 } else if (id == libwebm::kMkvChannels) {
5471 channels = UnserializeUInt(pReader, pos, size);
5472
5473 if (channels <= 0)
5474 return E_FILE_FORMAT_INVALID;
5475 } else if (id == libwebm::kMkvBitDepth) {
5476 bit_depth = UnserializeUInt(pReader, pos, size);
5477
5478 if (bit_depth <= 0)
5479 return E_FILE_FORMAT_INVALID;
5480 }
5481
5482 pos += size; // consume payload
5483 if (pos > stop)
5484 return E_FILE_FORMAT_INVALID;
5485 }
5486
5487 if (pos != stop)
5488 return E_FILE_FORMAT_INVALID;
5489
5490 AudioTrack* const pTrack =
5491 new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
5492
5493 if (pTrack == NULL)
5494 return -1; // generic error
5495
5496 const int status = info.Copy(pTrack->m_info);
5497
5498 if (status) {
5499 delete pTrack;
5500 return status;
5501 }
5502
5503 pTrack->m_rate = rate;
5504 pTrack->m_channels = channels;
5505 pTrack->m_bitDepth = bit_depth;
5506
5507 pResult = pTrack;
5508 return 0; // success
5509 }
5510
GetSamplingRate() const5511 double AudioTrack::GetSamplingRate() const { return m_rate; }
5512
GetChannels() const5513 long long AudioTrack::GetChannels() const { return m_channels; }
5514
GetBitDepth() const5515 long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
5516
Tracks(Segment * pSegment,long long start,long long size_,long long element_start,long long element_size)5517 Tracks::Tracks(Segment* pSegment, long long start, long long size_,
5518 long long element_start, long long element_size)
5519 : m_pSegment(pSegment),
5520 m_start(start),
5521 m_size(size_),
5522 m_element_start(element_start),
5523 m_element_size(element_size),
5524 m_trackEntries(NULL),
5525 m_trackEntriesEnd(NULL) {}
5526
Parse()5527 long Tracks::Parse() {
5528 assert(m_trackEntries == NULL);
5529 assert(m_trackEntriesEnd == NULL);
5530
5531 const long long stop = m_start + m_size;
5532 IMkvReader* const pReader = m_pSegment->m_pReader;
5533
5534 int count = 0;
5535 long long pos = m_start;
5536
5537 while (pos < stop) {
5538 long long id, size;
5539
5540 const long status = ParseElementHeader(pReader, pos, stop, id, size);
5541
5542 if (status < 0) // error
5543 return status;
5544
5545 if (size == 0) // weird
5546 continue;
5547
5548 if (id == libwebm::kMkvTrackEntry)
5549 ++count;
5550
5551 pos += size; // consume payload
5552 if (pos > stop)
5553 return E_FILE_FORMAT_INVALID;
5554 }
5555
5556 if (pos != stop)
5557 return E_FILE_FORMAT_INVALID;
5558
5559 if (count <= 0)
5560 return 0; // success
5561
5562 m_trackEntries = new (std::nothrow) Track*[count];
5563
5564 if (m_trackEntries == NULL)
5565 return -1;
5566
5567 m_trackEntriesEnd = m_trackEntries;
5568
5569 pos = m_start;
5570
5571 while (pos < stop) {
5572 const long long element_start = pos;
5573
5574 long long id, payload_size;
5575
5576 const long status =
5577 ParseElementHeader(pReader, pos, stop, id, payload_size);
5578
5579 if (status < 0) // error
5580 return status;
5581
5582 if (payload_size == 0) // weird
5583 continue;
5584
5585 const long long payload_stop = pos + payload_size;
5586 assert(payload_stop <= stop); // checked in ParseElement
5587
5588 const long long element_size = payload_stop - element_start;
5589
5590 if (id == libwebm::kMkvTrackEntry) {
5591 Track*& pTrack = *m_trackEntriesEnd;
5592 pTrack = NULL;
5593
5594 const long status = ParseTrackEntry(pos, payload_size, element_start,
5595 element_size, pTrack);
5596 if (status)
5597 return status;
5598
5599 if (pTrack)
5600 ++m_trackEntriesEnd;
5601 }
5602
5603 pos = payload_stop;
5604 if (pos > stop)
5605 return E_FILE_FORMAT_INVALID;
5606 }
5607
5608 if (pos != stop)
5609 return E_FILE_FORMAT_INVALID;
5610
5611 return 0; // success
5612 }
5613
GetTracksCount() const5614 unsigned long Tracks::GetTracksCount() const {
5615 const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
5616 assert(result >= 0);
5617
5618 return static_cast<unsigned long>(result);
5619 }
5620
ParseTrackEntry(long long track_start,long long track_size,long long element_start,long long element_size,Track * & pResult) const5621 long Tracks::ParseTrackEntry(long long track_start, long long track_size,
5622 long long element_start, long long element_size,
5623 Track*& pResult) const {
5624 if (pResult)
5625 return -1;
5626
5627 IMkvReader* const pReader = m_pSegment->m_pReader;
5628
5629 long long pos = track_start;
5630 const long long track_stop = track_start + track_size;
5631
5632 Track::Info info;
5633
5634 info.type = 0;
5635 info.number = 0;
5636 info.uid = 0;
5637 info.defaultDuration = 0;
5638
5639 Track::Settings v;
5640 v.start = -1;
5641 v.size = -1;
5642
5643 Track::Settings a;
5644 a.start = -1;
5645 a.size = -1;
5646
5647 Track::Settings e; // content_encodings_settings;
5648 e.start = -1;
5649 e.size = -1;
5650
5651 long long lacing = 1; // default is true
5652
5653 while (pos < track_stop) {
5654 long long id, size;
5655
5656 const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
5657
5658 if (status < 0) // error
5659 return status;
5660
5661 if (size < 0)
5662 return E_FILE_FORMAT_INVALID;
5663
5664 const long long start = pos;
5665
5666 if (id == libwebm::kMkvVideo) {
5667 v.start = start;
5668 v.size = size;
5669 } else if (id == libwebm::kMkvAudio) {
5670 a.start = start;
5671 a.size = size;
5672 } else if (id == libwebm::kMkvContentEncodings) {
5673 e.start = start;
5674 e.size = size;
5675 } else if (id == libwebm::kMkvTrackUID) {
5676 if (size > 8)
5677 return E_FILE_FORMAT_INVALID;
5678
5679 info.uid = 0;
5680
5681 long long pos_ = start;
5682 const long long pos_end = start + size;
5683
5684 while (pos_ != pos_end) {
5685 unsigned char b;
5686
5687 const int status = pReader->Read(pos_, 1, &b);
5688
5689 if (status)
5690 return status;
5691
5692 info.uid <<= 8;
5693 info.uid |= b;
5694
5695 ++pos_;
5696 }
5697 } else if (id == libwebm::kMkvTrackNumber) {
5698 const long long num = UnserializeUInt(pReader, pos, size);
5699
5700 if ((num <= 0) || (num > 127))
5701 return E_FILE_FORMAT_INVALID;
5702
5703 info.number = static_cast<long>(num);
5704 } else if (id == libwebm::kMkvTrackType) {
5705 const long long type = UnserializeUInt(pReader, pos, size);
5706
5707 if ((type <= 0) || (type > 254))
5708 return E_FILE_FORMAT_INVALID;
5709
5710 info.type = static_cast<long>(type);
5711 } else if (id == libwebm::kMkvName) {
5712 const long status =
5713 UnserializeString(pReader, pos, size, info.nameAsUTF8);
5714
5715 if (status)
5716 return status;
5717 } else if (id == libwebm::kMkvLanguage) {
5718 const long status = UnserializeString(pReader, pos, size, info.language);
5719
5720 if (status)
5721 return status;
5722 } else if (id == libwebm::kMkvDefaultDuration) {
5723 const long long duration = UnserializeUInt(pReader, pos, size);
5724
5725 if (duration < 0)
5726 return E_FILE_FORMAT_INVALID;
5727
5728 info.defaultDuration = static_cast<unsigned long long>(duration);
5729 } else if (id == libwebm::kMkvCodecID) {
5730 const long status = UnserializeString(pReader, pos, size, info.codecId);
5731
5732 if (status)
5733 return status;
5734 } else if (id == libwebm::kMkvFlagLacing) {
5735 lacing = UnserializeUInt(pReader, pos, size);
5736
5737 if ((lacing < 0) || (lacing > 1))
5738 return E_FILE_FORMAT_INVALID;
5739 } else if (id == libwebm::kMkvCodecPrivate) {
5740 delete[] info.codecPrivate;
5741 info.codecPrivate = NULL;
5742 info.codecPrivateSize = 0;
5743
5744 const size_t buflen = static_cast<size_t>(size);
5745
5746 if (buflen) {
5747 unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
5748
5749 if (buf == NULL)
5750 return -1;
5751
5752 const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
5753
5754 if (status) {
5755 delete[] buf;
5756 return status;
5757 }
5758
5759 info.codecPrivate = buf;
5760 info.codecPrivateSize = buflen;
5761 }
5762 } else if (id == libwebm::kMkvCodecName) {
5763 const long status =
5764 UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
5765
5766 if (status)
5767 return status;
5768 } else if (id == libwebm::kMkvCodecDelay) {
5769 info.codecDelay = UnserializeUInt(pReader, pos, size);
5770 } else if (id == libwebm::kMkvSeekPreRoll) {
5771 info.seekPreRoll = UnserializeUInt(pReader, pos, size);
5772 }
5773
5774 pos += size; // consume payload
5775 if (pos > track_stop)
5776 return E_FILE_FORMAT_INVALID;
5777 }
5778
5779 if (pos != track_stop)
5780 return E_FILE_FORMAT_INVALID;
5781
5782 if (info.number <= 0) // not specified
5783 return E_FILE_FORMAT_INVALID;
5784
5785 if (GetTrackByNumber(info.number))
5786 return E_FILE_FORMAT_INVALID;
5787
5788 if (info.type <= 0) // not specified
5789 return E_FILE_FORMAT_INVALID;
5790
5791 info.lacing = (lacing > 0) ? true : false;
5792
5793 if (info.type == Track::kVideo) {
5794 if (v.start < 0)
5795 return E_FILE_FORMAT_INVALID;
5796
5797 if (a.start >= 0)
5798 return E_FILE_FORMAT_INVALID;
5799
5800 info.settings = v;
5801
5802 VideoTrack* pTrack = NULL;
5803
5804 const long status = VideoTrack::Parse(m_pSegment, info, element_start,
5805 element_size, pTrack);
5806
5807 if (status)
5808 return status;
5809
5810 pResult = pTrack;
5811 assert(pResult);
5812
5813 if (e.start >= 0)
5814 pResult->ParseContentEncodingsEntry(e.start, e.size);
5815 } else if (info.type == Track::kAudio) {
5816 if (a.start < 0)
5817 return E_FILE_FORMAT_INVALID;
5818
5819 if (v.start >= 0)
5820 return E_FILE_FORMAT_INVALID;
5821
5822 info.settings = a;
5823
5824 AudioTrack* pTrack = NULL;
5825
5826 const long status = AudioTrack::Parse(m_pSegment, info, element_start,
5827 element_size, pTrack);
5828
5829 if (status)
5830 return status;
5831
5832 pResult = pTrack;
5833 assert(pResult);
5834
5835 if (e.start >= 0)
5836 pResult->ParseContentEncodingsEntry(e.start, e.size);
5837 } else {
5838 // neither video nor audio - probably metadata or subtitles
5839
5840 if (a.start >= 0)
5841 return E_FILE_FORMAT_INVALID;
5842
5843 if (v.start >= 0)
5844 return E_FILE_FORMAT_INVALID;
5845
5846 if (info.type == Track::kMetadata && e.start >= 0)
5847 return E_FILE_FORMAT_INVALID;
5848
5849 info.settings.start = -1;
5850 info.settings.size = 0;
5851
5852 Track* pTrack = NULL;
5853
5854 const long status =
5855 Track::Create(m_pSegment, info, element_start, element_size, pTrack);
5856
5857 if (status)
5858 return status;
5859
5860 pResult = pTrack;
5861 assert(pResult);
5862 }
5863
5864 return 0; // success
5865 }
5866
~Tracks()5867 Tracks::~Tracks() {
5868 Track** i = m_trackEntries;
5869 Track** const j = m_trackEntriesEnd;
5870
5871 while (i != j) {
5872 Track* const pTrack = *i++;
5873 delete pTrack;
5874 }
5875
5876 delete[] m_trackEntries;
5877 }
5878
GetTrackByNumber(long tn) const5879 const Track* Tracks::GetTrackByNumber(long tn) const {
5880 if (tn < 0)
5881 return NULL;
5882
5883 Track** i = m_trackEntries;
5884 Track** const j = m_trackEntriesEnd;
5885
5886 while (i != j) {
5887 Track* const pTrack = *i++;
5888
5889 if (pTrack == NULL)
5890 continue;
5891
5892 if (tn == pTrack->GetNumber())
5893 return pTrack;
5894 }
5895
5896 return NULL; // not found
5897 }
5898
GetTrackByIndex(unsigned long idx) const5899 const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
5900 const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
5901
5902 if (idx >= static_cast<unsigned long>(count))
5903 return NULL;
5904
5905 return m_trackEntries[idx];
5906 }
5907
Load(long long & pos,long & len) const5908 long Cluster::Load(long long& pos, long& len) const {
5909 if (m_pSegment == NULL)
5910 return E_PARSE_FAILED;
5911
5912 if (m_timecode >= 0) // at least partially loaded
5913 return 0;
5914
5915 if (m_pos != m_element_start || m_element_size >= 0)
5916 return E_PARSE_FAILED;
5917
5918 IMkvReader* const pReader = m_pSegment->m_pReader;
5919 long long total, avail;
5920 const int status = pReader->Length(&total, &avail);
5921
5922 if (status < 0) // error
5923 return status;
5924
5925 if (total >= 0 && (avail > total || m_pos > total))
5926 return E_FILE_FORMAT_INVALID;
5927
5928 pos = m_pos;
5929
5930 long long cluster_size = -1;
5931
5932 if ((pos + 1) > avail) {
5933 len = 1;
5934 return E_BUFFER_NOT_FULL;
5935 }
5936
5937 long long result = GetUIntLength(pReader, pos, len);
5938
5939 if (result < 0) // error or underflow
5940 return static_cast<long>(result);
5941
5942 if (result > 0)
5943 return E_BUFFER_NOT_FULL;
5944
5945 if ((pos + len) > avail)
5946 return E_BUFFER_NOT_FULL;
5947
5948 const long long id_ = ReadID(pReader, pos, len);
5949
5950 if (id_ < 0) // error
5951 return static_cast<long>(id_);
5952
5953 if (id_ != libwebm::kMkvCluster)
5954 return E_FILE_FORMAT_INVALID;
5955
5956 pos += len; // consume id
5957
5958 // read cluster size
5959
5960 if ((pos + 1) > avail) {
5961 len = 1;
5962 return E_BUFFER_NOT_FULL;
5963 }
5964
5965 result = GetUIntLength(pReader, pos, len);
5966
5967 if (result < 0) // error
5968 return static_cast<long>(result);
5969
5970 if (result > 0)
5971 return E_BUFFER_NOT_FULL;
5972
5973 if ((pos + len) > avail)
5974 return E_BUFFER_NOT_FULL;
5975
5976 const long long size = ReadUInt(pReader, pos, len);
5977
5978 if (size < 0) // error
5979 return static_cast<long>(cluster_size);
5980
5981 if (size == 0)
5982 return E_FILE_FORMAT_INVALID;
5983
5984 pos += len; // consume length of size of element
5985
5986 const long long unknown_size = (1LL << (7 * len)) - 1;
5987
5988 if (size != unknown_size)
5989 cluster_size = size;
5990
5991 // pos points to start of payload
5992 long long timecode = -1;
5993 long long new_pos = -1;
5994 bool bBlock = false;
5995
5996 long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
5997
5998 for (;;) {
5999 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6000 break;
6001
6002 // Parse ID
6003
6004 if ((pos + 1) > avail) {
6005 len = 1;
6006 return E_BUFFER_NOT_FULL;
6007 }
6008
6009 long long result = GetUIntLength(pReader, pos, len);
6010
6011 if (result < 0) // error
6012 return static_cast<long>(result);
6013
6014 if (result > 0)
6015 return E_BUFFER_NOT_FULL;
6016
6017 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6018 return E_FILE_FORMAT_INVALID;
6019
6020 if ((pos + len) > avail)
6021 return E_BUFFER_NOT_FULL;
6022
6023 const long long id = ReadID(pReader, pos, len);
6024
6025 if (id < 0) // error
6026 return static_cast<long>(id);
6027
6028 if (id == 0)
6029 return E_FILE_FORMAT_INVALID;
6030
6031 // This is the distinguished set of ID's we use to determine
6032 // that we have exhausted the sub-element's inside the cluster
6033 // whose ID we parsed earlier.
6034
6035 if (id == libwebm::kMkvCluster)
6036 break;
6037
6038 if (id == libwebm::kMkvCues)
6039 break;
6040
6041 pos += len; // consume ID field
6042
6043 // Parse Size
6044
6045 if ((pos + 1) > avail) {
6046 len = 1;
6047 return E_BUFFER_NOT_FULL;
6048 }
6049
6050 result = GetUIntLength(pReader, pos, len);
6051
6052 if (result < 0) // error
6053 return static_cast<long>(result);
6054
6055 if (result > 0)
6056 return E_BUFFER_NOT_FULL;
6057
6058 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6059 return E_FILE_FORMAT_INVALID;
6060
6061 if ((pos + len) > avail)
6062 return E_BUFFER_NOT_FULL;
6063
6064 const long long size = ReadUInt(pReader, pos, len);
6065
6066 if (size < 0) // error
6067 return static_cast<long>(size);
6068
6069 const long long unknown_size = (1LL << (7 * len)) - 1;
6070
6071 if (size == unknown_size)
6072 return E_FILE_FORMAT_INVALID;
6073
6074 pos += len; // consume size field
6075
6076 if ((cluster_stop >= 0) && (pos > cluster_stop))
6077 return E_FILE_FORMAT_INVALID;
6078
6079 // pos now points to start of payload
6080
6081 if (size == 0)
6082 continue;
6083
6084 if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6085 return E_FILE_FORMAT_INVALID;
6086
6087 if (id == libwebm::kMkvTimecode) {
6088 len = static_cast<long>(size);
6089
6090 if ((pos + size) > avail)
6091 return E_BUFFER_NOT_FULL;
6092
6093 timecode = UnserializeUInt(pReader, pos, size);
6094
6095 if (timecode < 0) // error (or underflow)
6096 return static_cast<long>(timecode);
6097
6098 new_pos = pos + size;
6099
6100 if (bBlock)
6101 break;
6102 } else if (id == libwebm::kMkvBlockGroup) {
6103 bBlock = true;
6104 break;
6105 } else if (id == libwebm::kMkvSimpleBlock) {
6106 bBlock = true;
6107 break;
6108 }
6109
6110 pos += size; // consume payload
6111 if (cluster_stop >= 0 && pos > cluster_stop)
6112 return E_FILE_FORMAT_INVALID;
6113 }
6114
6115 if (cluster_stop >= 0 && pos > cluster_stop)
6116 return E_FILE_FORMAT_INVALID;
6117
6118 if (timecode < 0) // no timecode found
6119 return E_FILE_FORMAT_INVALID;
6120
6121 if (!bBlock)
6122 return E_FILE_FORMAT_INVALID;
6123
6124 m_pos = new_pos; // designates position just beyond timecode payload
6125 m_timecode = timecode; // m_timecode >= 0 means we're partially loaded
6126
6127 if (cluster_size >= 0)
6128 m_element_size = cluster_stop - m_element_start;
6129
6130 return 0;
6131 }
6132
Parse(long long & pos,long & len) const6133 long Cluster::Parse(long long& pos, long& len) const {
6134 long status = Load(pos, len);
6135
6136 if (status < 0)
6137 return status;
6138
6139 if (m_pos < m_element_start || m_timecode < 0)
6140 return E_PARSE_FAILED;
6141
6142 const long long cluster_stop =
6143 (m_element_size < 0) ? -1 : m_element_start + m_element_size;
6144
6145 if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
6146 return 1; // nothing else to do
6147
6148 IMkvReader* const pReader = m_pSegment->m_pReader;
6149
6150 long long total, avail;
6151
6152 status = pReader->Length(&total, &avail);
6153
6154 if (status < 0) // error
6155 return status;
6156
6157 if (total >= 0 && avail > total)
6158 return E_FILE_FORMAT_INVALID;
6159
6160 pos = m_pos;
6161
6162 for (;;) {
6163 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6164 break;
6165
6166 if ((total >= 0) && (pos >= total)) {
6167 if (m_element_size < 0)
6168 m_element_size = pos - m_element_start;
6169
6170 break;
6171 }
6172
6173 // Parse ID
6174
6175 if ((pos + 1) > avail) {
6176 len = 1;
6177 return E_BUFFER_NOT_FULL;
6178 }
6179
6180 long long result = GetUIntLength(pReader, pos, len);
6181
6182 if (result < 0) // error
6183 return static_cast<long>(result);
6184
6185 if (result > 0)
6186 return E_BUFFER_NOT_FULL;
6187
6188 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6189 return E_FILE_FORMAT_INVALID;
6190
6191 if ((pos + len) > avail)
6192 return E_BUFFER_NOT_FULL;
6193
6194 const long long id = ReadID(pReader, pos, len);
6195
6196 if (id < 0)
6197 return E_FILE_FORMAT_INVALID;
6198
6199 // This is the distinguished set of ID's we use to determine
6200 // that we have exhausted the sub-element's inside the cluster
6201 // whose ID we parsed earlier.
6202
6203 if ((id == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) {
6204 if (m_element_size < 0)
6205 m_element_size = pos - m_element_start;
6206
6207 break;
6208 }
6209
6210 pos += len; // consume ID field
6211
6212 // Parse Size
6213
6214 if ((pos + 1) > avail) {
6215 len = 1;
6216 return E_BUFFER_NOT_FULL;
6217 }
6218
6219 result = GetUIntLength(pReader, pos, len);
6220
6221 if (result < 0) // error
6222 return static_cast<long>(result);
6223
6224 if (result > 0)
6225 return E_BUFFER_NOT_FULL;
6226
6227 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6228 return E_FILE_FORMAT_INVALID;
6229
6230 if ((pos + len) > avail)
6231 return E_BUFFER_NOT_FULL;
6232
6233 const long long size = ReadUInt(pReader, pos, len);
6234
6235 if (size < 0) // error
6236 return static_cast<long>(size);
6237
6238 const long long unknown_size = (1LL << (7 * len)) - 1;
6239
6240 if (size == unknown_size)
6241 return E_FILE_FORMAT_INVALID;
6242
6243 pos += len; // consume size field
6244
6245 if ((cluster_stop >= 0) && (pos > cluster_stop))
6246 return E_FILE_FORMAT_INVALID;
6247
6248 // pos now points to start of payload
6249
6250 if (size == 0)
6251 continue;
6252
6253 // const long long block_start = pos;
6254 const long long block_stop = pos + size;
6255
6256 if (cluster_stop >= 0) {
6257 if (block_stop > cluster_stop) {
6258 if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) {
6259 return E_FILE_FORMAT_INVALID;
6260 }
6261
6262 pos = cluster_stop;
6263 break;
6264 }
6265 } else if ((total >= 0) && (block_stop > total)) {
6266 m_element_size = total - m_element_start;
6267 pos = total;
6268 break;
6269 } else if (block_stop > avail) {
6270 len = static_cast<long>(size);
6271 return E_BUFFER_NOT_FULL;
6272 }
6273
6274 Cluster* const this_ = const_cast<Cluster*>(this);
6275
6276 if (id == libwebm::kMkvBlockGroup)
6277 return this_->ParseBlockGroup(size, pos, len);
6278
6279 if (id == libwebm::kMkvSimpleBlock)
6280 return this_->ParseSimpleBlock(size, pos, len);
6281
6282 pos += size; // consume payload
6283 if (cluster_stop >= 0 && pos > cluster_stop)
6284 return E_FILE_FORMAT_INVALID;
6285 }
6286
6287 if (m_element_size < 1)
6288 return E_FILE_FORMAT_INVALID;
6289
6290 m_pos = pos;
6291 if (cluster_stop >= 0 && m_pos > cluster_stop)
6292 return E_FILE_FORMAT_INVALID;
6293
6294 if (m_entries_count > 0) {
6295 const long idx = m_entries_count - 1;
6296
6297 const BlockEntry* const pLast = m_entries[idx];
6298 if (pLast == NULL)
6299 return E_PARSE_FAILED;
6300
6301 const Block* const pBlock = pLast->GetBlock();
6302 if (pBlock == NULL)
6303 return E_PARSE_FAILED;
6304
6305 const long long start = pBlock->m_start;
6306
6307 if ((total >= 0) && (start > total))
6308 return E_PARSE_FAILED; // defend against trucated stream
6309
6310 const long long size = pBlock->m_size;
6311
6312 const long long stop = start + size;
6313 if (cluster_stop >= 0 && stop > cluster_stop)
6314 return E_FILE_FORMAT_INVALID;
6315
6316 if ((total >= 0) && (stop > total))
6317 return E_PARSE_FAILED; // defend against trucated stream
6318 }
6319
6320 return 1; // no more entries
6321 }
6322
ParseSimpleBlock(long long block_size,long long & pos,long & len)6323 long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
6324 long& len) {
6325 const long long block_start = pos;
6326 const long long block_stop = pos + block_size;
6327
6328 IMkvReader* const pReader = m_pSegment->m_pReader;
6329
6330 long long total, avail;
6331
6332 long status = pReader->Length(&total, &avail);
6333
6334 if (status < 0) // error
6335 return status;
6336
6337 assert((total < 0) || (avail <= total));
6338
6339 // parse track number
6340
6341 if ((pos + 1) > avail) {
6342 len = 1;
6343 return E_BUFFER_NOT_FULL;
6344 }
6345
6346 long long result = GetUIntLength(pReader, pos, len);
6347
6348 if (result < 0) // error
6349 return static_cast<long>(result);
6350
6351 if (result > 0) // weird
6352 return E_BUFFER_NOT_FULL;
6353
6354 if ((pos + len) > block_stop)
6355 return E_FILE_FORMAT_INVALID;
6356
6357 if ((pos + len) > avail)
6358 return E_BUFFER_NOT_FULL;
6359
6360 const long long track = ReadUInt(pReader, pos, len);
6361
6362 if (track < 0) // error
6363 return static_cast<long>(track);
6364
6365 if (track == 0)
6366 return E_FILE_FORMAT_INVALID;
6367
6368 pos += len; // consume track number
6369
6370 if ((pos + 2) > block_stop)
6371 return E_FILE_FORMAT_INVALID;
6372
6373 if ((pos + 2) > avail) {
6374 len = 2;
6375 return E_BUFFER_NOT_FULL;
6376 }
6377
6378 pos += 2; // consume timecode
6379
6380 if ((pos + 1) > block_stop)
6381 return E_FILE_FORMAT_INVALID;
6382
6383 if ((pos + 1) > avail) {
6384 len = 1;
6385 return E_BUFFER_NOT_FULL;
6386 }
6387
6388 unsigned char flags;
6389
6390 status = pReader->Read(pos, 1, &flags);
6391
6392 if (status < 0) { // error or underflow
6393 len = 1;
6394 return status;
6395 }
6396
6397 ++pos; // consume flags byte
6398 assert(pos <= avail);
6399
6400 if (pos >= block_stop)
6401 return E_FILE_FORMAT_INVALID;
6402
6403 const int lacing = int(flags & 0x06) >> 1;
6404
6405 if ((lacing != 0) && (block_stop > avail)) {
6406 len = static_cast<long>(block_stop - pos);
6407 return E_BUFFER_NOT_FULL;
6408 }
6409
6410 status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size,
6411 0); // DiscardPadding
6412
6413 if (status != 0)
6414 return status;
6415
6416 m_pos = block_stop;
6417
6418 return 0; // success
6419 }
6420
ParseBlockGroup(long long payload_size,long long & pos,long & len)6421 long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
6422 long& len) {
6423 const long long payload_start = pos;
6424 const long long payload_stop = pos + payload_size;
6425
6426 IMkvReader* const pReader = m_pSegment->m_pReader;
6427
6428 long long total, avail;
6429
6430 long status = pReader->Length(&total, &avail);
6431
6432 if (status < 0) // error
6433 return status;
6434
6435 assert((total < 0) || (avail <= total));
6436
6437 if ((total >= 0) && (payload_stop > total))
6438 return E_FILE_FORMAT_INVALID;
6439
6440 if (payload_stop > avail) {
6441 len = static_cast<long>(payload_size);
6442 return E_BUFFER_NOT_FULL;
6443 }
6444
6445 long long discard_padding = 0;
6446
6447 while (pos < payload_stop) {
6448 // parse sub-block element ID
6449
6450 if ((pos + 1) > avail) {
6451 len = 1;
6452 return E_BUFFER_NOT_FULL;
6453 }
6454
6455 long long result = GetUIntLength(pReader, pos, len);
6456
6457 if (result < 0) // error
6458 return static_cast<long>(result);
6459
6460 if (result > 0) // weird
6461 return E_BUFFER_NOT_FULL;
6462
6463 if ((pos + len) > payload_stop)
6464 return E_FILE_FORMAT_INVALID;
6465
6466 if ((pos + len) > avail)
6467 return E_BUFFER_NOT_FULL;
6468
6469 const long long id = ReadID(pReader, pos, len);
6470
6471 if (id < 0) // error
6472 return static_cast<long>(id);
6473
6474 if (id == 0) // not a valid ID
6475 return E_FILE_FORMAT_INVALID;
6476
6477 pos += len; // consume ID field
6478
6479 // Parse Size
6480
6481 if ((pos + 1) > avail) {
6482 len = 1;
6483 return E_BUFFER_NOT_FULL;
6484 }
6485
6486 result = GetUIntLength(pReader, pos, len);
6487
6488 if (result < 0) // error
6489 return static_cast<long>(result);
6490
6491 if (result > 0) // weird
6492 return E_BUFFER_NOT_FULL;
6493
6494 if ((pos + len) > payload_stop)
6495 return E_FILE_FORMAT_INVALID;
6496
6497 if ((pos + len) > avail)
6498 return E_BUFFER_NOT_FULL;
6499
6500 const long long size = ReadUInt(pReader, pos, len);
6501
6502 if (size < 0) // error
6503 return static_cast<long>(size);
6504
6505 pos += len; // consume size field
6506
6507 // pos now points to start of sub-block group payload
6508
6509 if (pos > payload_stop)
6510 return E_FILE_FORMAT_INVALID;
6511
6512 if (size == 0) // weird
6513 continue;
6514
6515 const long long unknown_size = (1LL << (7 * len)) - 1;
6516
6517 if (size == unknown_size)
6518 return E_FILE_FORMAT_INVALID;
6519
6520 if (id == libwebm::kMkvDiscardPadding) {
6521 status = UnserializeInt(pReader, pos, size, discard_padding);
6522
6523 if (status < 0) // error
6524 return status;
6525 }
6526
6527 if (id != libwebm::kMkvBlock) {
6528 pos += size; // consume sub-part of block group
6529
6530 if (pos > payload_stop)
6531 return E_FILE_FORMAT_INVALID;
6532
6533 continue;
6534 }
6535
6536 const long long block_stop = pos + size;
6537
6538 if (block_stop > payload_stop)
6539 return E_FILE_FORMAT_INVALID;
6540
6541 // parse track number
6542
6543 if ((pos + 1) > avail) {
6544 len = 1;
6545 return E_BUFFER_NOT_FULL;
6546 }
6547
6548 result = GetUIntLength(pReader, pos, len);
6549
6550 if (result < 0) // error
6551 return static_cast<long>(result);
6552
6553 if (result > 0) // weird
6554 return E_BUFFER_NOT_FULL;
6555
6556 if ((pos + len) > block_stop)
6557 return E_FILE_FORMAT_INVALID;
6558
6559 if ((pos + len) > avail)
6560 return E_BUFFER_NOT_FULL;
6561
6562 const long long track = ReadUInt(pReader, pos, len);
6563
6564 if (track < 0) // error
6565 return static_cast<long>(track);
6566
6567 if (track == 0)
6568 return E_FILE_FORMAT_INVALID;
6569
6570 pos += len; // consume track number
6571
6572 if ((pos + 2) > block_stop)
6573 return E_FILE_FORMAT_INVALID;
6574
6575 if ((pos + 2) > avail) {
6576 len = 2;
6577 return E_BUFFER_NOT_FULL;
6578 }
6579
6580 pos += 2; // consume timecode
6581
6582 if ((pos + 1) > block_stop)
6583 return E_FILE_FORMAT_INVALID;
6584
6585 if ((pos + 1) > avail) {
6586 len = 1;
6587 return E_BUFFER_NOT_FULL;
6588 }
6589
6590 unsigned char flags;
6591
6592 status = pReader->Read(pos, 1, &flags);
6593
6594 if (status < 0) { // error or underflow
6595 len = 1;
6596 return status;
6597 }
6598
6599 ++pos; // consume flags byte
6600 assert(pos <= avail);
6601
6602 if (pos >= block_stop)
6603 return E_FILE_FORMAT_INVALID;
6604
6605 const int lacing = int(flags & 0x06) >> 1;
6606
6607 if ((lacing != 0) && (block_stop > avail)) {
6608 len = static_cast<long>(block_stop - pos);
6609 return E_BUFFER_NOT_FULL;
6610 }
6611
6612 pos = block_stop; // consume block-part of block group
6613 if (pos > payload_stop)
6614 return E_FILE_FORMAT_INVALID;
6615 }
6616
6617 if (pos != payload_stop)
6618 return E_FILE_FORMAT_INVALID;
6619
6620 status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size,
6621 discard_padding);
6622 if (status != 0)
6623 return status;
6624
6625 m_pos = payload_stop;
6626
6627 return 0; // success
6628 }
6629
GetEntry(long index,const mkvparser::BlockEntry * & pEntry) const6630 long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
6631 assert(m_pos >= m_element_start);
6632
6633 pEntry = NULL;
6634
6635 if (index < 0)
6636 return -1; // generic error
6637
6638 if (m_entries_count < 0)
6639 return E_BUFFER_NOT_FULL;
6640
6641 assert(m_entries);
6642 assert(m_entries_size > 0);
6643 assert(m_entries_count <= m_entries_size);
6644
6645 if (index < m_entries_count) {
6646 pEntry = m_entries[index];
6647 assert(pEntry);
6648
6649 return 1; // found entry
6650 }
6651
6652 if (m_element_size < 0) // we don't know cluster end yet
6653 return E_BUFFER_NOT_FULL; // underflow
6654
6655 const long long element_stop = m_element_start + m_element_size;
6656
6657 if (m_pos >= element_stop)
6658 return 0; // nothing left to parse
6659
6660 return E_BUFFER_NOT_FULL; // underflow, since more remains to be parsed
6661 }
6662
Create(Segment * pSegment,long idx,long long off)6663 Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
6664 if (!pSegment || off < 0)
6665 return NULL;
6666
6667 const long long element_start = pSegment->m_start + off;
6668
6669 Cluster* const pCluster =
6670 new (std::nothrow) Cluster(pSegment, idx, element_start);
6671
6672 return pCluster;
6673 }
6674
Cluster()6675 Cluster::Cluster()
6676 : m_pSegment(NULL),
6677 m_element_start(0),
6678 m_index(0),
6679 m_pos(0),
6680 m_element_size(0),
6681 m_timecode(0),
6682 m_entries(NULL),
6683 m_entries_size(0),
6684 m_entries_count(0) // means "no entries"
6685 {}
6686
Cluster(Segment * pSegment,long idx,long long element_start)6687 Cluster::Cluster(Segment* pSegment, long idx, long long element_start
6688 /* long long element_size */)
6689 : m_pSegment(pSegment),
6690 m_element_start(element_start),
6691 m_index(idx),
6692 m_pos(element_start),
6693 m_element_size(-1 /* element_size */),
6694 m_timecode(-1),
6695 m_entries(NULL),
6696 m_entries_size(0),
6697 m_entries_count(-1) // means "has not been parsed yet"
6698 {}
6699
~Cluster()6700 Cluster::~Cluster() {
6701 if (m_entries_count <= 0)
6702 return;
6703
6704 BlockEntry** i = m_entries;
6705 BlockEntry** const j = m_entries + m_entries_count;
6706
6707 while (i != j) {
6708 BlockEntry* p = *i++;
6709 assert(p);
6710
6711 delete p;
6712 }
6713
6714 delete[] m_entries;
6715 }
6716
EOS() const6717 bool Cluster::EOS() const { return (m_pSegment == NULL); }
6718
GetIndex() const6719 long Cluster::GetIndex() const { return m_index; }
6720
GetPosition() const6721 long long Cluster::GetPosition() const {
6722 const long long pos = m_element_start - m_pSegment->m_start;
6723 assert(pos >= 0);
6724
6725 return pos;
6726 }
6727
GetElementSize() const6728 long long Cluster::GetElementSize() const { return m_element_size; }
6729
HasBlockEntries(const Segment * pSegment,long long off,long long & pos,long & len)6730 long Cluster::HasBlockEntries(
6731 const Segment* pSegment,
6732 long long off, // relative to start of segment payload
6733 long long& pos, long& len) {
6734 assert(pSegment);
6735 assert(off >= 0); // relative to segment
6736
6737 IMkvReader* const pReader = pSegment->m_pReader;
6738
6739 long long total, avail;
6740
6741 long status = pReader->Length(&total, &avail);
6742
6743 if (status < 0) // error
6744 return status;
6745
6746 assert((total < 0) || (avail <= total));
6747
6748 pos = pSegment->m_start + off; // absolute
6749
6750 if ((total >= 0) && (pos >= total))
6751 return 0; // we don't even have a complete cluster
6752
6753 const long long segment_stop =
6754 (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
6755
6756 long long cluster_stop = -1; // interpreted later to mean "unknown size"
6757
6758 {
6759 if ((pos + 1) > avail) {
6760 len = 1;
6761 return E_BUFFER_NOT_FULL;
6762 }
6763
6764 long long result = GetUIntLength(pReader, pos, len);
6765
6766 if (result < 0) // error
6767 return static_cast<long>(result);
6768
6769 if (result > 0) // need more data
6770 return E_BUFFER_NOT_FULL;
6771
6772 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6773 return E_FILE_FORMAT_INVALID;
6774
6775 if ((total >= 0) && ((pos + len) > total))
6776 return 0;
6777
6778 if ((pos + len) > avail)
6779 return E_BUFFER_NOT_FULL;
6780
6781 const long long id = ReadID(pReader, pos, len);
6782
6783 if (id < 0) // error
6784 return static_cast<long>(id);
6785
6786 if (id != libwebm::kMkvCluster)
6787 return E_PARSE_FAILED;
6788
6789 pos += len; // consume Cluster ID field
6790
6791 // read size field
6792
6793 if ((pos + 1) > avail) {
6794 len = 1;
6795 return E_BUFFER_NOT_FULL;
6796 }
6797
6798 result = GetUIntLength(pReader, pos, len);
6799
6800 if (result < 0) // error
6801 return static_cast<long>(result);
6802
6803 if (result > 0) // weird
6804 return E_BUFFER_NOT_FULL;
6805
6806 if ((segment_stop >= 0) && ((pos + len) > segment_stop))
6807 return E_FILE_FORMAT_INVALID;
6808
6809 if ((total >= 0) && ((pos + len) > total))
6810 return 0;
6811
6812 if ((pos + len) > avail)
6813 return E_BUFFER_NOT_FULL;
6814
6815 const long long size = ReadUInt(pReader, pos, len);
6816
6817 if (size < 0) // error
6818 return static_cast<long>(size);
6819
6820 if (size == 0)
6821 return 0; // cluster does not have entries
6822
6823 pos += len; // consume size field
6824
6825 // pos now points to start of payload
6826
6827 const long long unknown_size = (1LL << (7 * len)) - 1;
6828
6829 if (size != unknown_size) {
6830 cluster_stop = pos + size;
6831 assert(cluster_stop >= 0);
6832
6833 if ((segment_stop >= 0) && (cluster_stop > segment_stop))
6834 return E_FILE_FORMAT_INVALID;
6835
6836 if ((total >= 0) && (cluster_stop > total))
6837 // return E_FILE_FORMAT_INVALID; //too conservative
6838 return 0; // cluster does not have any entries
6839 }
6840 }
6841
6842 for (;;) {
6843 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6844 return 0; // no entries detected
6845
6846 if ((pos + 1) > avail) {
6847 len = 1;
6848 return E_BUFFER_NOT_FULL;
6849 }
6850
6851 long long result = GetUIntLength(pReader, pos, len);
6852
6853 if (result < 0) // error
6854 return static_cast<long>(result);
6855
6856 if (result > 0) // need more data
6857 return E_BUFFER_NOT_FULL;
6858
6859 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6860 return E_FILE_FORMAT_INVALID;
6861
6862 if ((pos + len) > avail)
6863 return E_BUFFER_NOT_FULL;
6864
6865 const long long id = ReadID(pReader, pos, len);
6866
6867 if (id < 0) // error
6868 return static_cast<long>(id);
6869
6870 // This is the distinguished set of ID's we use to determine
6871 // that we have exhausted the sub-element's inside the cluster
6872 // whose ID we parsed earlier.
6873
6874 if (id == libwebm::kMkvCluster)
6875 return 0; // no entries found
6876
6877 if (id == libwebm::kMkvCues)
6878 return 0; // no entries found
6879
6880 pos += len; // consume id field
6881
6882 if ((cluster_stop >= 0) && (pos >= cluster_stop))
6883 return E_FILE_FORMAT_INVALID;
6884
6885 // read size field
6886
6887 if ((pos + 1) > avail) {
6888 len = 1;
6889 return E_BUFFER_NOT_FULL;
6890 }
6891
6892 result = GetUIntLength(pReader, pos, len);
6893
6894 if (result < 0) // error
6895 return static_cast<long>(result);
6896
6897 if (result > 0) // underflow
6898 return E_BUFFER_NOT_FULL;
6899
6900 if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
6901 return E_FILE_FORMAT_INVALID;
6902
6903 if ((pos + len) > avail)
6904 return E_BUFFER_NOT_FULL;
6905
6906 const long long size = ReadUInt(pReader, pos, len);
6907
6908 if (size < 0) // error
6909 return static_cast<long>(size);
6910
6911 pos += len; // consume size field
6912
6913 // pos now points to start of payload
6914
6915 if ((cluster_stop >= 0) && (pos > cluster_stop))
6916 return E_FILE_FORMAT_INVALID;
6917
6918 if (size == 0) // weird
6919 continue;
6920
6921 const long long unknown_size = (1LL << (7 * len)) - 1;
6922
6923 if (size == unknown_size)
6924 return E_FILE_FORMAT_INVALID; // not supported inside cluster
6925
6926 if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
6927 return E_FILE_FORMAT_INVALID;
6928
6929 if (id == libwebm::kMkvBlockGroup)
6930 return 1; // have at least one entry
6931
6932 if (id == libwebm::kMkvSimpleBlock)
6933 return 1; // have at least one entry
6934
6935 pos += size; // consume payload
6936 if (cluster_stop >= 0 && pos > cluster_stop)
6937 return E_FILE_FORMAT_INVALID;
6938 }
6939 }
6940
GetTimeCode() const6941 long long Cluster::GetTimeCode() const {
6942 long long pos;
6943 long len;
6944
6945 const long status = Load(pos, len);
6946
6947 if (status < 0) // error
6948 return status;
6949
6950 return m_timecode;
6951 }
6952
GetTime() const6953 long long Cluster::GetTime() const {
6954 const long long tc = GetTimeCode();
6955
6956 if (tc < 0)
6957 return tc;
6958
6959 const SegmentInfo* const pInfo = m_pSegment->GetInfo();
6960 assert(pInfo);
6961
6962 const long long scale = pInfo->GetTimeCodeScale();
6963 assert(scale >= 1);
6964
6965 const long long t = m_timecode * scale;
6966
6967 return t;
6968 }
6969
GetFirstTime() const6970 long long Cluster::GetFirstTime() const {
6971 const BlockEntry* pEntry;
6972
6973 const long status = GetFirst(pEntry);
6974
6975 if (status < 0) // error
6976 return status;
6977
6978 if (pEntry == NULL) // empty cluster
6979 return GetTime();
6980
6981 const Block* const pBlock = pEntry->GetBlock();
6982 assert(pBlock);
6983
6984 return pBlock->GetTime(this);
6985 }
6986
GetLastTime() const6987 long long Cluster::GetLastTime() const {
6988 const BlockEntry* pEntry;
6989
6990 const long status = GetLast(pEntry);
6991
6992 if (status < 0) // error
6993 return status;
6994
6995 if (pEntry == NULL) // empty cluster
6996 return GetTime();
6997
6998 const Block* const pBlock = pEntry->GetBlock();
6999 assert(pBlock);
7000
7001 return pBlock->GetTime(this);
7002 }
7003
CreateBlock(long long id,long long pos,long long size,long long discard_padding)7004 long Cluster::CreateBlock(long long id,
7005 long long pos, // absolute pos of payload
7006 long long size, long long discard_padding) {
7007 if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock)
7008 return E_PARSE_FAILED;
7009
7010 if (m_entries_count < 0) { // haven't parsed anything yet
7011 assert(m_entries == NULL);
7012 assert(m_entries_size == 0);
7013
7014 m_entries_size = 1024;
7015 m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
7016 if (m_entries == NULL)
7017 return -1;
7018
7019 m_entries_count = 0;
7020 } else {
7021 assert(m_entries);
7022 assert(m_entries_size > 0);
7023 assert(m_entries_count <= m_entries_size);
7024
7025 if (m_entries_count >= m_entries_size) {
7026 const long entries_size = 2 * m_entries_size;
7027
7028 BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
7029 if (entries == NULL)
7030 return -1;
7031
7032 BlockEntry** src = m_entries;
7033 BlockEntry** const src_end = src + m_entries_count;
7034
7035 BlockEntry** dst = entries;
7036
7037 while (src != src_end)
7038 *dst++ = *src++;
7039
7040 delete[] m_entries;
7041
7042 m_entries = entries;
7043 m_entries_size = entries_size;
7044 }
7045 }
7046
7047 if (id == libwebm::kMkvBlockGroup)
7048 return CreateBlockGroup(pos, size, discard_padding);
7049 else
7050 return CreateSimpleBlock(pos, size);
7051 }
7052
CreateBlockGroup(long long start_offset,long long size,long long discard_padding)7053 long Cluster::CreateBlockGroup(long long start_offset, long long size,
7054 long long discard_padding) {
7055 assert(m_entries);
7056 assert(m_entries_size > 0);
7057 assert(m_entries_count >= 0);
7058 assert(m_entries_count < m_entries_size);
7059
7060 IMkvReader* const pReader = m_pSegment->m_pReader;
7061
7062 long long pos = start_offset;
7063 const long long stop = start_offset + size;
7064
7065 // For WebM files, there is a bias towards previous reference times
7066 //(in order to support alt-ref frames, which refer back to the previous
7067 // keyframe). Normally a 0 value is not possible, but here we tenatively
7068 // allow 0 as the value of a reference frame, with the interpretation
7069 // that this is a "previous" reference time.
7070
7071 long long prev = 1; // nonce
7072 long long next = 0; // nonce
7073 long long duration = -1; // really, this is unsigned
7074
7075 long long bpos = -1;
7076 long long bsize = -1;
7077
7078 while (pos < stop) {
7079 long len;
7080 const long long id = ReadID(pReader, pos, len);
7081 if (id < 0 || (pos + len) > stop)
7082 return E_FILE_FORMAT_INVALID;
7083
7084 pos += len; // consume ID
7085
7086 const long long size = ReadUInt(pReader, pos, len);
7087 assert(size >= 0); // TODO
7088 assert((pos + len) <= stop);
7089
7090 pos += len; // consume size
7091
7092 if (id == libwebm::kMkvBlock) {
7093 if (bpos < 0) { // Block ID
7094 bpos = pos;
7095 bsize = size;
7096 }
7097 } else if (id == libwebm::kMkvBlockDuration) {
7098 if (size > 8)
7099 return E_FILE_FORMAT_INVALID;
7100
7101 duration = UnserializeUInt(pReader, pos, size);
7102
7103 if (duration < 0)
7104 return E_FILE_FORMAT_INVALID;
7105 } else if (id == libwebm::kMkvReferenceBlock) {
7106 if (size > 8 || size <= 0)
7107 return E_FILE_FORMAT_INVALID;
7108 const long size_ = static_cast<long>(size);
7109
7110 long long time;
7111
7112 long status = UnserializeInt(pReader, pos, size_, time);
7113 assert(status == 0);
7114 if (status != 0)
7115 return -1;
7116
7117 if (time <= 0) // see note above
7118 prev = time;
7119 else
7120 next = time;
7121 }
7122
7123 pos += size; // consume payload
7124 if (pos > stop)
7125 return E_FILE_FORMAT_INVALID;
7126 }
7127 if (bpos < 0)
7128 return E_FILE_FORMAT_INVALID;
7129
7130 if (pos != stop)
7131 return E_FILE_FORMAT_INVALID;
7132 assert(bsize >= 0);
7133
7134 const long idx = m_entries_count;
7135
7136 BlockEntry** const ppEntry = m_entries + idx;
7137 BlockEntry*& pEntry = *ppEntry;
7138
7139 pEntry = new (std::nothrow)
7140 BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
7141
7142 if (pEntry == NULL)
7143 return -1; // generic error
7144
7145 BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
7146
7147 const long status = p->Parse();
7148
7149 if (status == 0) { // success
7150 ++m_entries_count;
7151 return 0;
7152 }
7153
7154 delete pEntry;
7155 pEntry = 0;
7156
7157 return status;
7158 }
7159
CreateSimpleBlock(long long st,long long sz)7160 long Cluster::CreateSimpleBlock(long long st, long long sz) {
7161 assert(m_entries);
7162 assert(m_entries_size > 0);
7163 assert(m_entries_count >= 0);
7164 assert(m_entries_count < m_entries_size);
7165
7166 const long idx = m_entries_count;
7167
7168 BlockEntry** const ppEntry = m_entries + idx;
7169 BlockEntry*& pEntry = *ppEntry;
7170
7171 pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
7172
7173 if (pEntry == NULL)
7174 return -1; // generic error
7175
7176 SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
7177
7178 const long status = p->Parse();
7179
7180 if (status == 0) {
7181 ++m_entries_count;
7182 return 0;
7183 }
7184
7185 delete pEntry;
7186 pEntry = 0;
7187
7188 return status;
7189 }
7190
GetFirst(const BlockEntry * & pFirst) const7191 long Cluster::GetFirst(const BlockEntry*& pFirst) const {
7192 if (m_entries_count <= 0) {
7193 long long pos;
7194 long len;
7195
7196 const long status = Parse(pos, len);
7197
7198 if (status < 0) { // error
7199 pFirst = NULL;
7200 return status;
7201 }
7202
7203 if (m_entries_count <= 0) { // empty cluster
7204 pFirst = NULL;
7205 return 0;
7206 }
7207 }
7208
7209 assert(m_entries);
7210
7211 pFirst = m_entries[0];
7212 assert(pFirst);
7213
7214 return 0; // success
7215 }
7216
GetLast(const BlockEntry * & pLast) const7217 long Cluster::GetLast(const BlockEntry*& pLast) const {
7218 for (;;) {
7219 long long pos;
7220 long len;
7221
7222 const long status = Parse(pos, len);
7223
7224 if (status < 0) { // error
7225 pLast = NULL;
7226 return status;
7227 }
7228
7229 if (status > 0) // no new block
7230 break;
7231 }
7232
7233 if (m_entries_count <= 0) {
7234 pLast = NULL;
7235 return 0;
7236 }
7237
7238 assert(m_entries);
7239
7240 const long idx = m_entries_count - 1;
7241
7242 pLast = m_entries[idx];
7243 assert(pLast);
7244
7245 return 0;
7246 }
7247
GetNext(const BlockEntry * pCurr,const BlockEntry * & pNext) const7248 long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
7249 assert(pCurr);
7250 assert(m_entries);
7251 assert(m_entries_count > 0);
7252
7253 size_t idx = pCurr->GetIndex();
7254 assert(idx < size_t(m_entries_count));
7255 assert(m_entries[idx] == pCurr);
7256
7257 ++idx;
7258
7259 if (idx >= size_t(m_entries_count)) {
7260 long long pos;
7261 long len;
7262
7263 const long status = Parse(pos, len);
7264
7265 if (status < 0) { // error
7266 pNext = NULL;
7267 return status;
7268 }
7269
7270 if (status > 0) {
7271 pNext = NULL;
7272 return 0;
7273 }
7274
7275 assert(m_entries);
7276 assert(m_entries_count > 0);
7277 assert(idx < size_t(m_entries_count));
7278 }
7279
7280 pNext = m_entries[idx];
7281 assert(pNext);
7282
7283 return 0;
7284 }
7285
GetEntryCount() const7286 long Cluster::GetEntryCount() const { return m_entries_count; }
7287
GetEntry(const Track * pTrack,long long time_ns) const7288 const BlockEntry* Cluster::GetEntry(const Track* pTrack,
7289 long long time_ns) const {
7290 assert(pTrack);
7291
7292 if (m_pSegment == NULL) // this is the special EOS cluster
7293 return pTrack->GetEOS();
7294
7295 const BlockEntry* pResult = pTrack->GetEOS();
7296
7297 long index = 0;
7298
7299 for (;;) {
7300 if (index >= m_entries_count) {
7301 long long pos;
7302 long len;
7303
7304 const long status = Parse(pos, len);
7305 assert(status >= 0);
7306
7307 if (status > 0) // completely parsed, and no more entries
7308 return pResult;
7309
7310 if (status < 0) // should never happen
7311 return 0;
7312
7313 assert(m_entries);
7314 assert(index < m_entries_count);
7315 }
7316
7317 const BlockEntry* const pEntry = m_entries[index];
7318 assert(pEntry);
7319 assert(!pEntry->EOS());
7320
7321 const Block* const pBlock = pEntry->GetBlock();
7322 assert(pBlock);
7323
7324 if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
7325 ++index;
7326 continue;
7327 }
7328
7329 if (pTrack->VetEntry(pEntry)) {
7330 if (time_ns < 0) // just want first candidate block
7331 return pEntry;
7332
7333 const long long ns = pBlock->GetTime(this);
7334
7335 if (ns > time_ns)
7336 return pResult;
7337
7338 pResult = pEntry; // have a candidate
7339 } else if (time_ns >= 0) {
7340 const long long ns = pBlock->GetTime(this);
7341
7342 if (ns > time_ns)
7343 return pResult;
7344 }
7345
7346 ++index;
7347 }
7348 }
7349
GetEntry(const CuePoint & cp,const CuePoint::TrackPosition & tp) const7350 const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
7351 const CuePoint::TrackPosition& tp) const {
7352 assert(m_pSegment);
7353 const long long tc = cp.GetTimeCode();
7354
7355 if (tp.m_block > 0) {
7356 const long block = static_cast<long>(tp.m_block);
7357 const long index = block - 1;
7358
7359 while (index >= m_entries_count) {
7360 long long pos;
7361 long len;
7362
7363 const long status = Parse(pos, len);
7364
7365 if (status < 0) // TODO: can this happen?
7366 return NULL;
7367
7368 if (status > 0) // nothing remains to be parsed
7369 return NULL;
7370 }
7371
7372 const BlockEntry* const pEntry = m_entries[index];
7373 assert(pEntry);
7374 assert(!pEntry->EOS());
7375
7376 const Block* const pBlock = pEntry->GetBlock();
7377 assert(pBlock);
7378
7379 if ((pBlock->GetTrackNumber() == tp.m_track) &&
7380 (pBlock->GetTimeCode(this) == tc)) {
7381 return pEntry;
7382 }
7383 }
7384
7385 long index = 0;
7386
7387 for (;;) {
7388 if (index >= m_entries_count) {
7389 long long pos;
7390 long len;
7391
7392 const long status = Parse(pos, len);
7393
7394 if (status < 0) // TODO: can this happen?
7395 return NULL;
7396
7397 if (status > 0) // nothing remains to be parsed
7398 return NULL;
7399
7400 assert(m_entries);
7401 assert(index < m_entries_count);
7402 }
7403
7404 const BlockEntry* const pEntry = m_entries[index];
7405 assert(pEntry);
7406 assert(!pEntry->EOS());
7407
7408 const Block* const pBlock = pEntry->GetBlock();
7409 assert(pBlock);
7410
7411 if (pBlock->GetTrackNumber() != tp.m_track) {
7412 ++index;
7413 continue;
7414 }
7415
7416 const long long tc_ = pBlock->GetTimeCode(this);
7417
7418 if (tc_ < tc) {
7419 ++index;
7420 continue;
7421 }
7422
7423 if (tc_ > tc)
7424 return NULL;
7425
7426 const Tracks* const pTracks = m_pSegment->GetTracks();
7427 assert(pTracks);
7428
7429 const long tn = static_cast<long>(tp.m_track);
7430 const Track* const pTrack = pTracks->GetTrackByNumber(tn);
7431
7432 if (pTrack == NULL)
7433 return NULL;
7434
7435 const long long type = pTrack->GetType();
7436
7437 if (type == 2) // audio
7438 return pEntry;
7439
7440 if (type != 1) // not video
7441 return NULL;
7442
7443 if (!pBlock->IsKey())
7444 return NULL;
7445
7446 return pEntry;
7447 }
7448 }
7449
BlockEntry(Cluster * p,long idx)7450 BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
~BlockEntry()7451 BlockEntry::~BlockEntry() {}
GetCluster() const7452 const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
GetIndex() const7453 long BlockEntry::GetIndex() const { return m_index; }
7454
SimpleBlock(Cluster * pCluster,long idx,long long start,long long size)7455 SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
7456 long long size)
7457 : BlockEntry(pCluster, idx), m_block(start, size, 0) {}
7458
Parse()7459 long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
GetKind() const7460 BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
GetBlock() const7461 const Block* SimpleBlock::GetBlock() const { return &m_block; }
7462
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)7463 BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
7464 long long block_size, long long prev, long long next,
7465 long long duration, long long discard_padding)
7466 : BlockEntry(pCluster, idx),
7467 m_block(block_start, block_size, discard_padding),
7468 m_prev(prev),
7469 m_next(next),
7470 m_duration(duration) {}
7471
Parse()7472 long BlockGroup::Parse() {
7473 const long status = m_block.Parse(m_pCluster);
7474
7475 if (status)
7476 return status;
7477
7478 m_block.SetKey((m_prev > 0) && (m_next <= 0));
7479
7480 return 0;
7481 }
7482
GetKind() const7483 BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
GetBlock() const7484 const Block* BlockGroup::GetBlock() const { return &m_block; }
GetPrevTimeCode() const7485 long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
GetNextTimeCode() const7486 long long BlockGroup::GetNextTimeCode() const { return m_next; }
GetDurationTimeCode() const7487 long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
7488
Block(long long start,long long size_,long long discard_padding)7489 Block::Block(long long start, long long size_, long long discard_padding)
7490 : m_start(start),
7491 m_size(size_),
7492 m_track(0),
7493 m_timecode(-1),
7494 m_flags(0),
7495 m_frames(NULL),
7496 m_frame_count(-1),
7497 m_discard_padding(discard_padding) {}
7498
~Block()7499 Block::~Block() { delete[] m_frames; }
7500
Parse(const Cluster * pCluster)7501 long Block::Parse(const Cluster* pCluster) {
7502 if (pCluster == NULL)
7503 return -1;
7504
7505 if (pCluster->m_pSegment == NULL)
7506 return -1;
7507
7508 assert(m_start >= 0);
7509 assert(m_size >= 0);
7510 assert(m_track <= 0);
7511 assert(m_frames == NULL);
7512 assert(m_frame_count <= 0);
7513
7514 long long pos = m_start;
7515 const long long stop = m_start + m_size;
7516
7517 long len;
7518
7519 IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
7520
7521 m_track = ReadUInt(pReader, pos, len);
7522
7523 if (m_track <= 0)
7524 return E_FILE_FORMAT_INVALID;
7525
7526 if ((pos + len) > stop)
7527 return E_FILE_FORMAT_INVALID;
7528
7529 pos += len; // consume track number
7530
7531 if ((stop - pos) < 2)
7532 return E_FILE_FORMAT_INVALID;
7533
7534 long status;
7535 long long value;
7536
7537 status = UnserializeInt(pReader, pos, 2, value);
7538
7539 if (status)
7540 return E_FILE_FORMAT_INVALID;
7541
7542 if (value < SHRT_MIN)
7543 return E_FILE_FORMAT_INVALID;
7544
7545 if (value > SHRT_MAX)
7546 return E_FILE_FORMAT_INVALID;
7547
7548 m_timecode = static_cast<short>(value);
7549
7550 pos += 2;
7551
7552 if ((stop - pos) <= 0)
7553 return E_FILE_FORMAT_INVALID;
7554
7555 status = pReader->Read(pos, 1, &m_flags);
7556
7557 if (status)
7558 return E_FILE_FORMAT_INVALID;
7559
7560 const int lacing = int(m_flags & 0x06) >> 1;
7561
7562 ++pos; // consume flags byte
7563
7564 if (lacing == 0) { // no lacing
7565 if (pos > stop)
7566 return E_FILE_FORMAT_INVALID;
7567
7568 m_frame_count = 1;
7569 m_frames = new (std::nothrow) Frame[m_frame_count];
7570 if (m_frames == NULL)
7571 return -1;
7572
7573 Frame& f = m_frames[0];
7574 f.pos = pos;
7575
7576 const long long frame_size = stop - pos;
7577
7578 if (frame_size > LONG_MAX || frame_size <= 0)
7579 return E_FILE_FORMAT_INVALID;
7580
7581 f.len = static_cast<long>(frame_size);
7582
7583 return 0; // success
7584 }
7585
7586 if (pos >= stop)
7587 return E_FILE_FORMAT_INVALID;
7588
7589 unsigned char biased_count;
7590
7591 status = pReader->Read(pos, 1, &biased_count);
7592
7593 if (status)
7594 return E_FILE_FORMAT_INVALID;
7595
7596 ++pos; // consume frame count
7597 if (pos > stop)
7598 return E_FILE_FORMAT_INVALID;
7599
7600 m_frame_count = int(biased_count) + 1;
7601
7602 m_frames = new (std::nothrow) Frame[m_frame_count];
7603 if (m_frames == NULL)
7604 return -1;
7605
7606 if (!m_frames)
7607 return E_FILE_FORMAT_INVALID;
7608
7609 if (lacing == 1) { // Xiph
7610 Frame* pf = m_frames;
7611 Frame* const pf_end = pf + m_frame_count;
7612
7613 long long size = 0;
7614 int frame_count = m_frame_count;
7615
7616 while (frame_count > 1) {
7617 long frame_size = 0;
7618
7619 for (;;) {
7620 unsigned char val;
7621
7622 if (pos >= stop)
7623 return E_FILE_FORMAT_INVALID;
7624
7625 status = pReader->Read(pos, 1, &val);
7626
7627 if (status)
7628 return E_FILE_FORMAT_INVALID;
7629
7630 ++pos; // consume xiph size byte
7631
7632 frame_size += val;
7633
7634 if (val < 255)
7635 break;
7636 }
7637
7638 Frame& f = *pf++;
7639 assert(pf < pf_end);
7640 if (pf >= pf_end)
7641 return E_FILE_FORMAT_INVALID;
7642
7643 f.pos = 0; // patch later
7644
7645 if (frame_size <= 0)
7646 return E_FILE_FORMAT_INVALID;
7647
7648 f.len = frame_size;
7649 size += frame_size; // contribution of this frame
7650
7651 --frame_count;
7652 }
7653
7654 if (pf >= pf_end || pos > stop)
7655 return E_FILE_FORMAT_INVALID;
7656
7657 {
7658 Frame& f = *pf++;
7659
7660 if (pf != pf_end)
7661 return E_FILE_FORMAT_INVALID;
7662
7663 f.pos = 0; // patch later
7664
7665 const long long total_size = stop - pos;
7666
7667 if (total_size < size)
7668 return E_FILE_FORMAT_INVALID;
7669
7670 const long long frame_size = total_size - size;
7671
7672 if (frame_size > LONG_MAX || frame_size <= 0)
7673 return E_FILE_FORMAT_INVALID;
7674
7675 f.len = static_cast<long>(frame_size);
7676 }
7677
7678 pf = m_frames;
7679 while (pf != pf_end) {
7680 Frame& f = *pf++;
7681 assert((pos + f.len) <= stop);
7682
7683 if ((pos + f.len) > stop)
7684 return E_FILE_FORMAT_INVALID;
7685
7686 f.pos = pos;
7687 pos += f.len;
7688 }
7689
7690 assert(pos == stop);
7691 if (pos != stop)
7692 return E_FILE_FORMAT_INVALID;
7693
7694 } else if (lacing == 2) { // fixed-size lacing
7695 if (pos >= stop)
7696 return E_FILE_FORMAT_INVALID;
7697
7698 const long long total_size = stop - pos;
7699
7700 if ((total_size % m_frame_count) != 0)
7701 return E_FILE_FORMAT_INVALID;
7702
7703 const long long frame_size = total_size / m_frame_count;
7704
7705 if (frame_size > LONG_MAX || frame_size <= 0)
7706 return E_FILE_FORMAT_INVALID;
7707
7708 Frame* pf = m_frames;
7709 Frame* const pf_end = pf + m_frame_count;
7710
7711 while (pf != pf_end) {
7712 assert((pos + frame_size) <= stop);
7713 if ((pos + frame_size) > stop)
7714 return E_FILE_FORMAT_INVALID;
7715
7716 Frame& f = *pf++;
7717
7718 f.pos = pos;
7719 f.len = static_cast<long>(frame_size);
7720
7721 pos += frame_size;
7722 }
7723
7724 assert(pos == stop);
7725 if (pos != stop)
7726 return E_FILE_FORMAT_INVALID;
7727
7728 } else {
7729 assert(lacing == 3); // EBML lacing
7730
7731 if (pos >= stop)
7732 return E_FILE_FORMAT_INVALID;
7733
7734 long long size = 0;
7735 int frame_count = m_frame_count;
7736
7737 long long frame_size = ReadUInt(pReader, pos, len);
7738
7739 if (frame_size <= 0)
7740 return E_FILE_FORMAT_INVALID;
7741
7742 if (frame_size > LONG_MAX)
7743 return E_FILE_FORMAT_INVALID;
7744
7745 if ((pos + len) > stop)
7746 return E_FILE_FORMAT_INVALID;
7747
7748 pos += len; // consume length of size of first frame
7749
7750 if ((pos + frame_size) > stop)
7751 return E_FILE_FORMAT_INVALID;
7752
7753 Frame* pf = m_frames;
7754 Frame* const pf_end = pf + m_frame_count;
7755
7756 {
7757 Frame& curr = *pf;
7758
7759 curr.pos = 0; // patch later
7760
7761 curr.len = static_cast<long>(frame_size);
7762 size += curr.len; // contribution of this frame
7763 }
7764
7765 --frame_count;
7766
7767 while (frame_count > 1) {
7768 if (pos >= stop)
7769 return E_FILE_FORMAT_INVALID;
7770
7771 assert(pf < pf_end);
7772 if (pf >= pf_end)
7773 return E_FILE_FORMAT_INVALID;
7774
7775 const Frame& prev = *pf++;
7776 assert(prev.len == frame_size);
7777 if (prev.len != frame_size)
7778 return E_FILE_FORMAT_INVALID;
7779
7780 assert(pf < pf_end);
7781 if (pf >= pf_end)
7782 return E_FILE_FORMAT_INVALID;
7783
7784 Frame& curr = *pf;
7785
7786 curr.pos = 0; // patch later
7787
7788 const long long delta_size_ = ReadUInt(pReader, pos, len);
7789
7790 if (delta_size_ < 0)
7791 return E_FILE_FORMAT_INVALID;
7792
7793 if ((pos + len) > stop)
7794 return E_FILE_FORMAT_INVALID;
7795
7796 pos += len; // consume length of (delta) size
7797 if (pos > stop)
7798 return E_FILE_FORMAT_INVALID;
7799
7800 const int exp = 7 * len - 1;
7801 const long long bias = (1LL << exp) - 1LL;
7802 const long long delta_size = delta_size_ - bias;
7803
7804 frame_size += delta_size;
7805
7806 if (frame_size <= 0)
7807 return E_FILE_FORMAT_INVALID;
7808
7809 if (frame_size > LONG_MAX)
7810 return E_FILE_FORMAT_INVALID;
7811
7812 curr.len = static_cast<long>(frame_size);
7813 size += curr.len; // contribution of this frame
7814
7815 --frame_count;
7816 }
7817
7818 // parse last frame
7819 if (frame_count > 0) {
7820 if (pos > stop || pf >= pf_end)
7821 return E_FILE_FORMAT_INVALID;
7822
7823 const Frame& prev = *pf++;
7824 assert(prev.len == frame_size);
7825 if (prev.len != frame_size)
7826 return E_FILE_FORMAT_INVALID;
7827
7828 if (pf >= pf_end)
7829 return E_FILE_FORMAT_INVALID;
7830
7831 Frame& curr = *pf++;
7832 if (pf != pf_end)
7833 return E_FILE_FORMAT_INVALID;
7834
7835 curr.pos = 0; // patch later
7836
7837 const long long total_size = stop - pos;
7838
7839 if (total_size < size)
7840 return E_FILE_FORMAT_INVALID;
7841
7842 frame_size = total_size - size;
7843
7844 if (frame_size > LONG_MAX || frame_size <= 0)
7845 return E_FILE_FORMAT_INVALID;
7846
7847 curr.len = static_cast<long>(frame_size);
7848 }
7849
7850 pf = m_frames;
7851 while (pf != pf_end) {
7852 Frame& f = *pf++;
7853 assert((pos + f.len) <= stop);
7854 if ((pos + f.len) > stop)
7855 return E_FILE_FORMAT_INVALID;
7856
7857 f.pos = pos;
7858 pos += f.len;
7859 }
7860
7861 if (pos != stop)
7862 return E_FILE_FORMAT_INVALID;
7863 }
7864
7865 return 0; // success
7866 }
7867
GetTimeCode(const Cluster * pCluster) const7868 long long Block::GetTimeCode(const Cluster* pCluster) const {
7869 if (pCluster == 0)
7870 return m_timecode;
7871
7872 const long long tc0 = pCluster->GetTimeCode();
7873 assert(tc0 >= 0);
7874
7875 const long long tc = tc0 + m_timecode;
7876
7877 return tc; // unscaled timecode units
7878 }
7879
GetTime(const Cluster * pCluster) const7880 long long Block::GetTime(const Cluster* pCluster) const {
7881 assert(pCluster);
7882
7883 const long long tc = GetTimeCode(pCluster);
7884
7885 const Segment* const pSegment = pCluster->m_pSegment;
7886 const SegmentInfo* const pInfo = pSegment->GetInfo();
7887 assert(pInfo);
7888
7889 const long long scale = pInfo->GetTimeCodeScale();
7890 assert(scale >= 1);
7891
7892 const long long ns = tc * scale;
7893
7894 return ns;
7895 }
7896
GetTrackNumber() const7897 long long Block::GetTrackNumber() const { return m_track; }
7898
IsKey() const7899 bool Block::IsKey() const {
7900 return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
7901 }
7902
SetKey(bool bKey)7903 void Block::SetKey(bool bKey) {
7904 if (bKey)
7905 m_flags |= static_cast<unsigned char>(1 << 7);
7906 else
7907 m_flags &= 0x7F;
7908 }
7909
IsInvisible() const7910 bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
7911
GetLacing() const7912 Block::Lacing Block::GetLacing() const {
7913 const int value = int(m_flags & 0x06) >> 1;
7914 return static_cast<Lacing>(value);
7915 }
7916
GetFrameCount() const7917 int Block::GetFrameCount() const { return m_frame_count; }
7918
GetFrame(int idx) const7919 const Block::Frame& Block::GetFrame(int idx) const {
7920 assert(idx >= 0);
7921 assert(idx < m_frame_count);
7922
7923 const Frame& f = m_frames[idx];
7924 assert(f.pos > 0);
7925 assert(f.len > 0);
7926
7927 return f;
7928 }
7929
Read(IMkvReader * pReader,unsigned char * buf) const7930 long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
7931 assert(pReader);
7932 assert(buf);
7933
7934 const long status = pReader->Read(pos, len, buf);
7935 return status;
7936 }
7937
GetDiscardPadding() const7938 long long Block::GetDiscardPadding() const { return m_discard_padding; }
7939
7940 } // namespace mkvparser
7941