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