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