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