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
9 #include "mkvmuxer/mkvmuxer.h"
10
11 #include <stdint.h>
12
13 #include <cfloat>
14 #include <climits>
15 #include <cstdio>
16 #include <cstdlib>
17 #include <cstring>
18 #include <ctime>
19 #include <memory>
20 #include <new>
21 #include <string>
22 #include <vector>
23
24 #include "common/webmids.h"
25 #include "mkvmuxer/mkvmuxerutil.h"
26 #include "mkvmuxer/mkvwriter.h"
27 #include "mkvparser/mkvparser.h"
28
29 // disable deprecation warnings for auto_ptr
30 #if defined(__GNUC__) && __GNUC__ >= 5
31 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
32 #endif
33
34 namespace mkvmuxer {
35
36 const float PrimaryChromaticity::kChromaticityMin = 0.0f;
37 const float PrimaryChromaticity::kChromaticityMax = 1.0f;
38 const float MasteringMetadata::kMinLuminance = 0.0f;
39 const float MasteringMetadata::kMinLuminanceMax = 999.99f;
40 const float MasteringMetadata::kMaxLuminanceMax = 9999.99f;
41 const float MasteringMetadata::kValueNotPresent = FLT_MAX;
42 const uint64_t Colour::kValueNotPresent = UINT64_MAX;
43
44 namespace {
45
46 const char kDocTypeWebm[] = "webm";
47 const char kDocTypeMatroska[] = "matroska";
48
49 // Deallocate the string designated by |dst|, and then copy the |src|
50 // string to |dst|. The caller owns both the |src| string and the
51 // |dst| copy (hence the caller is responsible for eventually
52 // deallocating the strings, either directly, or indirectly via
53 // StrCpy). Returns true if the source string was successfully copied
54 // to the destination.
StrCpy(const char * src,char ** dst_ptr)55 bool StrCpy(const char* src, char** dst_ptr) {
56 if (dst_ptr == NULL)
57 return false;
58
59 char*& dst = *dst_ptr;
60
61 delete[] dst;
62 dst = NULL;
63
64 if (src == NULL)
65 return true;
66
67 const size_t size = strlen(src) + 1;
68
69 dst = new (std::nothrow) char[size]; // NOLINT
70 if (dst == NULL)
71 return false;
72
73 strcpy(dst, src); // NOLINT
74 return true;
75 }
76
77 typedef std::auto_ptr<PrimaryChromaticity> PrimaryChromaticityPtr;
CopyChromaticity(const PrimaryChromaticity * src,PrimaryChromaticityPtr * dst)78 bool CopyChromaticity(const PrimaryChromaticity* src,
79 PrimaryChromaticityPtr* dst) {
80 if (!dst)
81 return false;
82
83 dst->reset(new (std::nothrow) PrimaryChromaticity(src->x(), src->y()));
84 if (!dst->get())
85 return false;
86
87 return true;
88 }
89
90 } // namespace
91
92 ///////////////////////////////////////////////////////////////
93 //
94 // IMkvWriter Class
95
IMkvWriter()96 IMkvWriter::IMkvWriter() {}
97
~IMkvWriter()98 IMkvWriter::~IMkvWriter() {}
99
WriteEbmlHeader(IMkvWriter * writer,uint64_t doc_type_version,const char * const doc_type)100 bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version,
101 const char* const doc_type) {
102 // Level 0
103 uint64_t size =
104 EbmlElementSize(libwebm::kMkvEBMLVersion, static_cast<uint64>(1));
105 size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, static_cast<uint64>(1));
106 size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, static_cast<uint64>(4));
107 size +=
108 EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, static_cast<uint64>(8));
109 size += EbmlElementSize(libwebm::kMkvDocType, doc_type);
110 size += EbmlElementSize(libwebm::kMkvDocTypeVersion,
111 static_cast<uint64>(doc_type_version));
112 size +=
113 EbmlElementSize(libwebm::kMkvDocTypeReadVersion, static_cast<uint64>(2));
114
115 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size))
116 return false;
117 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion,
118 static_cast<uint64>(1))) {
119 return false;
120 }
121 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion,
122 static_cast<uint64>(1))) {
123 return false;
124 }
125 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength,
126 static_cast<uint64>(4))) {
127 return false;
128 }
129 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength,
130 static_cast<uint64>(8))) {
131 return false;
132 }
133 if (!WriteEbmlElement(writer, libwebm::kMkvDocType, doc_type))
134 return false;
135 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion,
136 static_cast<uint64>(doc_type_version))) {
137 return false;
138 }
139 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion,
140 static_cast<uint64>(2))) {
141 return false;
142 }
143
144 return true;
145 }
146
WriteEbmlHeader(IMkvWriter * writer,uint64_t doc_type_version)147 bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) {
148 return WriteEbmlHeader(writer, doc_type_version, kDocTypeWebm);
149 }
150
WriteEbmlHeader(IMkvWriter * writer)151 bool WriteEbmlHeader(IMkvWriter* writer) {
152 return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion);
153 }
154
ChunkedCopy(mkvparser::IMkvReader * source,mkvmuxer::IMkvWriter * dst,int64_t start,int64_t size)155 bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst,
156 int64_t start, int64_t size) {
157 // TODO(vigneshv): Check if this is a reasonable value.
158 const uint32_t kBufSize = 2048;
159 uint8_t* buf = new uint8_t[kBufSize];
160 int64_t offset = start;
161 while (size > 0) {
162 const int64_t read_len = (size > kBufSize) ? kBufSize : size;
163 if (source->Read(offset, static_cast<long>(read_len), buf))
164 return false;
165 dst->Write(buf, static_cast<uint32_t>(read_len));
166 offset += read_len;
167 size -= read_len;
168 }
169 delete[] buf;
170 return true;
171 }
172
173 ///////////////////////////////////////////////////////////////
174 //
175 // Frame Class
176
Frame()177 Frame::Frame()
178 : add_id_(0),
179 additional_(NULL),
180 additional_length_(0),
181 duration_(0),
182 duration_set_(false),
183 frame_(NULL),
184 is_key_(false),
185 length_(0),
186 track_number_(0),
187 timestamp_(0),
188 discard_padding_(0),
189 reference_block_timestamp_(0),
190 reference_block_timestamp_set_(false) {}
191
~Frame()192 Frame::~Frame() {
193 delete[] frame_;
194 delete[] additional_;
195 }
196
CopyFrom(const Frame & frame)197 bool Frame::CopyFrom(const Frame& frame) {
198 delete[] frame_;
199 frame_ = NULL;
200 length_ = 0;
201 if (frame.length() > 0 && frame.frame() != NULL &&
202 !Init(frame.frame(), frame.length())) {
203 return false;
204 }
205 add_id_ = 0;
206 delete[] additional_;
207 additional_ = NULL;
208 additional_length_ = 0;
209 if (frame.additional_length() > 0 && frame.additional() != NULL &&
210 !AddAdditionalData(frame.additional(), frame.additional_length(),
211 frame.add_id())) {
212 return false;
213 }
214 duration_ = frame.duration();
215 duration_set_ = frame.duration_set();
216 is_key_ = frame.is_key();
217 track_number_ = frame.track_number();
218 timestamp_ = frame.timestamp();
219 discard_padding_ = frame.discard_padding();
220 reference_block_timestamp_ = frame.reference_block_timestamp();
221 reference_block_timestamp_set_ = frame.reference_block_timestamp_set();
222 return true;
223 }
224
Init(const uint8_t * frame,uint64_t length)225 bool Frame::Init(const uint8_t* frame, uint64_t length) {
226 uint8_t* const data =
227 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
228 if (!data)
229 return false;
230
231 delete[] frame_;
232 frame_ = data;
233 length_ = length;
234
235 memcpy(frame_, frame, static_cast<size_t>(length_));
236 return true;
237 }
238
AddAdditionalData(const uint8_t * additional,uint64_t length,uint64_t add_id)239 bool Frame::AddAdditionalData(const uint8_t* additional, uint64_t length,
240 uint64_t add_id) {
241 uint8_t* const data =
242 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
243 if (!data)
244 return false;
245
246 delete[] additional_;
247 additional_ = data;
248 additional_length_ = length;
249 add_id_ = add_id;
250
251 memcpy(additional_, additional, static_cast<size_t>(additional_length_));
252 return true;
253 }
254
IsValid() const255 bool Frame::IsValid() const {
256 if (length_ == 0 || !frame_) {
257 return false;
258 }
259 if ((additional_length_ != 0 && !additional_) ||
260 (additional_ != NULL && additional_length_ == 0)) {
261 return false;
262 }
263 if (track_number_ == 0 || track_number_ > kMaxTrackNumber) {
264 return false;
265 }
266 if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) {
267 return false;
268 }
269 return true;
270 }
271
CanBeSimpleBlock() const272 bool Frame::CanBeSimpleBlock() const {
273 return additional_ == NULL && discard_padding_ == 0 && duration_ == 0;
274 }
275
set_duration(uint64_t duration)276 void Frame::set_duration(uint64_t duration) {
277 duration_ = duration;
278 duration_set_ = true;
279 }
280
set_reference_block_timestamp(int64_t reference_block_timestamp)281 void Frame::set_reference_block_timestamp(int64_t reference_block_timestamp) {
282 reference_block_timestamp_ = reference_block_timestamp;
283 reference_block_timestamp_set_ = true;
284 }
285
286 ///////////////////////////////////////////////////////////////
287 //
288 // CuePoint Class
289
CuePoint()290 CuePoint::CuePoint()
291 : time_(0),
292 track_(0),
293 cluster_pos_(0),
294 block_number_(1),
295 output_block_number_(true) {}
296
~CuePoint()297 CuePoint::~CuePoint() {}
298
Write(IMkvWriter * writer) const299 bool CuePoint::Write(IMkvWriter* writer) const {
300 if (!writer || track_ < 1 || cluster_pos_ < 1)
301 return false;
302
303 uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition,
304 static_cast<uint64>(cluster_pos_));
305 size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast<uint64>(track_));
306 if (output_block_number_ && block_number_ > 1)
307 size += EbmlElementSize(libwebm::kMkvCueBlockNumber,
308 static_cast<uint64>(block_number_));
309 const uint64_t track_pos_size =
310 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
311 const uint64_t payload_size =
312 EbmlElementSize(libwebm::kMkvCueTime, static_cast<uint64>(time_)) +
313 track_pos_size;
314
315 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCuePoint, payload_size))
316 return false;
317
318 const int64_t payload_position = writer->Position();
319 if (payload_position < 0)
320 return false;
321
322 if (!WriteEbmlElement(writer, libwebm::kMkvCueTime,
323 static_cast<uint64>(time_))) {
324 return false;
325 }
326
327 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size))
328 return false;
329 if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack,
330 static_cast<uint64>(track_))) {
331 return false;
332 }
333 if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition,
334 static_cast<uint64>(cluster_pos_))) {
335 return false;
336 }
337 if (output_block_number_ && block_number_ > 1) {
338 if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber,
339 static_cast<uint64>(block_number_))) {
340 return false;
341 }
342 }
343
344 const int64_t stop_position = writer->Position();
345 if (stop_position < 0)
346 return false;
347
348 if (stop_position - payload_position != static_cast<int64_t>(payload_size))
349 return false;
350
351 return true;
352 }
353
PayloadSize() const354 uint64_t CuePoint::PayloadSize() const {
355 uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition,
356 static_cast<uint64>(cluster_pos_));
357 size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast<uint64>(track_));
358 if (output_block_number_ && block_number_ > 1)
359 size += EbmlElementSize(libwebm::kMkvCueBlockNumber,
360 static_cast<uint64>(block_number_));
361 const uint64_t track_pos_size =
362 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
363 const uint64_t payload_size =
364 EbmlElementSize(libwebm::kMkvCueTime, static_cast<uint64>(time_)) +
365 track_pos_size;
366
367 return payload_size;
368 }
369
Size() const370 uint64_t CuePoint::Size() const {
371 const uint64_t payload_size = PayloadSize();
372 return EbmlMasterElementSize(libwebm::kMkvCuePoint, payload_size) +
373 payload_size;
374 }
375
376 ///////////////////////////////////////////////////////////////
377 //
378 // Cues Class
379
Cues()380 Cues::Cues()
381 : cue_entries_capacity_(0),
382 cue_entries_size_(0),
383 cue_entries_(NULL),
384 output_block_number_(true) {}
385
~Cues()386 Cues::~Cues() {
387 if (cue_entries_) {
388 for (int32_t i = 0; i < cue_entries_size_; ++i) {
389 CuePoint* const cue = cue_entries_[i];
390 delete cue;
391 }
392 delete[] cue_entries_;
393 }
394 }
395
AddCue(CuePoint * cue)396 bool Cues::AddCue(CuePoint* cue) {
397 if (!cue)
398 return false;
399
400 if ((cue_entries_size_ + 1) > cue_entries_capacity_) {
401 // Add more CuePoints.
402 const int32_t new_capacity =
403 (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2;
404
405 if (new_capacity < 1)
406 return false;
407
408 CuePoint** const cues =
409 new (std::nothrow) CuePoint*[new_capacity]; // NOLINT
410 if (!cues)
411 return false;
412
413 for (int32_t i = 0; i < cue_entries_size_; ++i) {
414 cues[i] = cue_entries_[i];
415 }
416
417 delete[] cue_entries_;
418
419 cue_entries_ = cues;
420 cue_entries_capacity_ = new_capacity;
421 }
422
423 cue->set_output_block_number(output_block_number_);
424 cue_entries_[cue_entries_size_++] = cue;
425 return true;
426 }
427
GetCueByIndex(int32_t index) const428 CuePoint* Cues::GetCueByIndex(int32_t index) const {
429 if (cue_entries_ == NULL)
430 return NULL;
431
432 if (index >= cue_entries_size_)
433 return NULL;
434
435 return cue_entries_[index];
436 }
437
Size()438 uint64_t Cues::Size() {
439 uint64_t size = 0;
440 for (int32_t i = 0; i < cue_entries_size_; ++i)
441 size += GetCueByIndex(i)->Size();
442 size += EbmlMasterElementSize(libwebm::kMkvCues, size);
443 return size;
444 }
445
Write(IMkvWriter * writer) const446 bool Cues::Write(IMkvWriter* writer) const {
447 if (!writer)
448 return false;
449
450 uint64_t size = 0;
451 for (int32_t i = 0; i < cue_entries_size_; ++i) {
452 const CuePoint* const cue = GetCueByIndex(i);
453
454 if (!cue)
455 return false;
456
457 size += cue->Size();
458 }
459
460 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCues, size))
461 return false;
462
463 const int64_t payload_position = writer->Position();
464 if (payload_position < 0)
465 return false;
466
467 for (int32_t i = 0; i < cue_entries_size_; ++i) {
468 const CuePoint* const cue = GetCueByIndex(i);
469
470 if (!cue->Write(writer))
471 return false;
472 }
473
474 const int64_t stop_position = writer->Position();
475 if (stop_position < 0)
476 return false;
477
478 if (stop_position - payload_position != static_cast<int64_t>(size))
479 return false;
480
481 return true;
482 }
483
484 ///////////////////////////////////////////////////////////////
485 //
486 // ContentEncAESSettings Class
487
ContentEncAESSettings()488 ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
489
Size() const490 uint64_t ContentEncAESSettings::Size() const {
491 const uint64_t payload = PayloadSize();
492 const uint64_t size =
493 EbmlMasterElementSize(libwebm::kMkvContentEncAESSettings, payload) +
494 payload;
495 return size;
496 }
497
Write(IMkvWriter * writer) const498 bool ContentEncAESSettings::Write(IMkvWriter* writer) const {
499 const uint64_t payload = PayloadSize();
500
501 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncAESSettings,
502 payload))
503 return false;
504
505 const int64_t payload_position = writer->Position();
506 if (payload_position < 0)
507 return false;
508
509 if (!WriteEbmlElement(writer, libwebm::kMkvAESSettingsCipherMode,
510 static_cast<uint64>(cipher_mode_))) {
511 return false;
512 }
513
514 const int64_t stop_position = writer->Position();
515 if (stop_position < 0 ||
516 stop_position - payload_position != static_cast<int64_t>(payload))
517 return false;
518
519 return true;
520 }
521
PayloadSize() const522 uint64_t ContentEncAESSettings::PayloadSize() const {
523 uint64_t size = EbmlElementSize(libwebm::kMkvAESSettingsCipherMode,
524 static_cast<uint64>(cipher_mode_));
525 return size;
526 }
527
528 ///////////////////////////////////////////////////////////////
529 //
530 // ContentEncoding Class
531
ContentEncoding()532 ContentEncoding::ContentEncoding()
533 : enc_algo_(5),
534 enc_key_id_(NULL),
535 encoding_order_(0),
536 encoding_scope_(1),
537 encoding_type_(1),
538 enc_key_id_length_(0) {}
539
~ContentEncoding()540 ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
541
SetEncryptionID(const uint8_t * id,uint64_t length)542 bool ContentEncoding::SetEncryptionID(const uint8_t* id, uint64_t length) {
543 if (!id || length < 1)
544 return false;
545
546 delete[] enc_key_id_;
547
548 enc_key_id_ =
549 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
550 if (!enc_key_id_)
551 return false;
552
553 memcpy(enc_key_id_, id, static_cast<size_t>(length));
554 enc_key_id_length_ = length;
555
556 return true;
557 }
558
Size() const559 uint64_t ContentEncoding::Size() const {
560 const uint64_t encryption_size = EncryptionSize();
561 const uint64_t encoding_size = EncodingSize(0, encryption_size);
562 const uint64_t encodings_size =
563 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
564 encoding_size;
565
566 return encodings_size;
567 }
568
Write(IMkvWriter * writer) const569 bool ContentEncoding::Write(IMkvWriter* writer) const {
570 const uint64_t encryption_size = EncryptionSize();
571 const uint64_t encoding_size = EncodingSize(0, encryption_size);
572 const uint64_t size =
573 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
574 encoding_size;
575
576 const int64_t payload_position = writer->Position();
577 if (payload_position < 0)
578 return false;
579
580 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncoding,
581 encoding_size))
582 return false;
583 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingOrder,
584 static_cast<uint64>(encoding_order_)))
585 return false;
586 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope,
587 static_cast<uint64>(encoding_scope_)))
588 return false;
589 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType,
590 static_cast<uint64>(encoding_type_)))
591 return false;
592
593 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption,
594 encryption_size))
595 return false;
596 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo,
597 static_cast<uint64>(enc_algo_))) {
598 return false;
599 }
600 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncKeyID, enc_key_id_,
601 enc_key_id_length_))
602 return false;
603
604 if (!enc_aes_settings_.Write(writer))
605 return false;
606
607 const int64_t stop_position = writer->Position();
608 if (stop_position < 0 ||
609 stop_position - payload_position != static_cast<int64_t>(size))
610 return false;
611
612 return true;
613 }
614
EncodingSize(uint64_t compresion_size,uint64_t encryption_size) const615 uint64_t ContentEncoding::EncodingSize(uint64_t compresion_size,
616 uint64_t encryption_size) const {
617 // TODO(fgalligan): Add support for compression settings.
618 if (compresion_size != 0)
619 return 0;
620
621 uint64_t encoding_size = 0;
622
623 if (encryption_size > 0) {
624 encoding_size +=
625 EbmlMasterElementSize(libwebm::kMkvContentEncryption, encryption_size) +
626 encryption_size;
627 }
628 encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingType,
629 static_cast<uint64>(encoding_type_));
630 encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingScope,
631 static_cast<uint64>(encoding_scope_));
632 encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingOrder,
633 static_cast<uint64>(encoding_order_));
634
635 return encoding_size;
636 }
637
EncryptionSize() const638 uint64_t ContentEncoding::EncryptionSize() const {
639 const uint64_t aes_size = enc_aes_settings_.Size();
640
641 uint64_t encryption_size = EbmlElementSize(libwebm::kMkvContentEncKeyID,
642 enc_key_id_, enc_key_id_length_);
643 encryption_size += EbmlElementSize(libwebm::kMkvContentEncAlgo,
644 static_cast<uint64>(enc_algo_));
645
646 return encryption_size + aes_size;
647 }
648
649 ///////////////////////////////////////////////////////////////
650 //
651 // Track Class
652
Track(unsigned int * seed)653 Track::Track(unsigned int* seed)
654 : codec_id_(NULL),
655 codec_private_(NULL),
656 language_(NULL),
657 max_block_additional_id_(0),
658 name_(NULL),
659 number_(0),
660 type_(0),
661 uid_(MakeUID(seed)),
662 codec_delay_(0),
663 seek_pre_roll_(0),
664 default_duration_(0),
665 codec_private_length_(0),
666 content_encoding_entries_(NULL),
667 content_encoding_entries_size_(0) {}
668
~Track()669 Track::~Track() {
670 delete[] codec_id_;
671 delete[] codec_private_;
672 delete[] language_;
673 delete[] name_;
674
675 if (content_encoding_entries_) {
676 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
677 ContentEncoding* const encoding = content_encoding_entries_[i];
678 delete encoding;
679 }
680 delete[] content_encoding_entries_;
681 }
682 }
683
AddContentEncoding()684 bool Track::AddContentEncoding() {
685 const uint32_t count = content_encoding_entries_size_ + 1;
686
687 ContentEncoding** const content_encoding_entries =
688 new (std::nothrow) ContentEncoding*[count]; // NOLINT
689 if (!content_encoding_entries)
690 return false;
691
692 ContentEncoding* const content_encoding =
693 new (std::nothrow) ContentEncoding(); // NOLINT
694 if (!content_encoding) {
695 delete[] content_encoding_entries;
696 return false;
697 }
698
699 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
700 content_encoding_entries[i] = content_encoding_entries_[i];
701 }
702
703 delete[] content_encoding_entries_;
704
705 content_encoding_entries_ = content_encoding_entries;
706 content_encoding_entries_[content_encoding_entries_size_] = content_encoding;
707 content_encoding_entries_size_ = count;
708 return true;
709 }
710
GetContentEncodingByIndex(uint32_t index) const711 ContentEncoding* Track::GetContentEncodingByIndex(uint32_t index) const {
712 if (content_encoding_entries_ == NULL)
713 return NULL;
714
715 if (index >= content_encoding_entries_size_)
716 return NULL;
717
718 return content_encoding_entries_[index];
719 }
720
PayloadSize() const721 uint64_t Track::PayloadSize() const {
722 uint64_t size =
723 EbmlElementSize(libwebm::kMkvTrackNumber, static_cast<uint64>(number_));
724 size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast<uint64>(uid_));
725 size += EbmlElementSize(libwebm::kMkvTrackType, static_cast<uint64>(type_));
726 if (codec_id_)
727 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
728 if (codec_private_)
729 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
730 codec_private_length_);
731 if (language_)
732 size += EbmlElementSize(libwebm::kMkvLanguage, language_);
733 if (name_)
734 size += EbmlElementSize(libwebm::kMkvName, name_);
735 if (max_block_additional_id_) {
736 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
737 static_cast<uint64>(max_block_additional_id_));
738 }
739 if (codec_delay_) {
740 size += EbmlElementSize(libwebm::kMkvCodecDelay,
741 static_cast<uint64>(codec_delay_));
742 }
743 if (seek_pre_roll_) {
744 size += EbmlElementSize(libwebm::kMkvSeekPreRoll,
745 static_cast<uint64>(seek_pre_roll_));
746 }
747 if (default_duration_) {
748 size += EbmlElementSize(libwebm::kMkvDefaultDuration,
749 static_cast<uint64>(default_duration_));
750 }
751
752 if (content_encoding_entries_size_ > 0) {
753 uint64_t content_encodings_size = 0;
754 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
755 ContentEncoding* const encoding = content_encoding_entries_[i];
756 content_encodings_size += encoding->Size();
757 }
758
759 size += EbmlMasterElementSize(libwebm::kMkvContentEncodings,
760 content_encodings_size) +
761 content_encodings_size;
762 }
763
764 return size;
765 }
766
Size() const767 uint64_t Track::Size() const {
768 uint64_t size = PayloadSize();
769 size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size);
770 return size;
771 }
772
Write(IMkvWriter * writer) const773 bool Track::Write(IMkvWriter* writer) const {
774 if (!writer)
775 return false;
776
777 // mandatory elements without a default value.
778 if (!type_ || !codec_id_)
779 return false;
780
781 // |size| may be bigger than what is written out in this function because
782 // derived classes may write out more data in the Track element.
783 const uint64_t payload_size = PayloadSize();
784
785 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTrackEntry, payload_size))
786 return false;
787
788 uint64_t size =
789 EbmlElementSize(libwebm::kMkvTrackNumber, static_cast<uint64>(number_));
790 size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast<uint64>(uid_));
791 size += EbmlElementSize(libwebm::kMkvTrackType, static_cast<uint64>(type_));
792 if (codec_id_)
793 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
794 if (codec_private_)
795 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
796 static_cast<uint64>(codec_private_length_));
797 if (language_)
798 size += EbmlElementSize(libwebm::kMkvLanguage, language_);
799 if (name_)
800 size += EbmlElementSize(libwebm::kMkvName, name_);
801 if (max_block_additional_id_)
802 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
803 static_cast<uint64>(max_block_additional_id_));
804 if (codec_delay_)
805 size += EbmlElementSize(libwebm::kMkvCodecDelay,
806 static_cast<uint64>(codec_delay_));
807 if (seek_pre_roll_)
808 size += EbmlElementSize(libwebm::kMkvSeekPreRoll,
809 static_cast<uint64>(seek_pre_roll_));
810 if (default_duration_)
811 size += EbmlElementSize(libwebm::kMkvDefaultDuration,
812 static_cast<uint64>(default_duration_));
813
814 const int64_t payload_position = writer->Position();
815 if (payload_position < 0)
816 return false;
817
818 if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber,
819 static_cast<uint64>(number_)))
820 return false;
821 if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID,
822 static_cast<uint64>(uid_)))
823 return false;
824 if (!WriteEbmlElement(writer, libwebm::kMkvTrackType,
825 static_cast<uint64>(type_)))
826 return false;
827 if (max_block_additional_id_) {
828 if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID,
829 static_cast<uint64>(max_block_additional_id_))) {
830 return false;
831 }
832 }
833 if (codec_delay_) {
834 if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay,
835 static_cast<uint64>(codec_delay_)))
836 return false;
837 }
838 if (seek_pre_roll_) {
839 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll,
840 static_cast<uint64>(seek_pre_roll_)))
841 return false;
842 }
843 if (default_duration_) {
844 if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration,
845 static_cast<uint64>(default_duration_)))
846 return false;
847 }
848 if (codec_id_) {
849 if (!WriteEbmlElement(writer, libwebm::kMkvCodecID, codec_id_))
850 return false;
851 }
852 if (codec_private_) {
853 if (!WriteEbmlElement(writer, libwebm::kMkvCodecPrivate, codec_private_,
854 static_cast<uint64>(codec_private_length_)))
855 return false;
856 }
857 if (language_) {
858 if (!WriteEbmlElement(writer, libwebm::kMkvLanguage, language_))
859 return false;
860 }
861 if (name_) {
862 if (!WriteEbmlElement(writer, libwebm::kMkvName, name_))
863 return false;
864 }
865
866 int64_t stop_position = writer->Position();
867 if (stop_position < 0 ||
868 stop_position - payload_position != static_cast<int64_t>(size))
869 return false;
870
871 if (content_encoding_entries_size_ > 0) {
872 uint64_t content_encodings_size = 0;
873 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
874 ContentEncoding* const encoding = content_encoding_entries_[i];
875 content_encodings_size += encoding->Size();
876 }
877
878 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncodings,
879 content_encodings_size))
880 return false;
881
882 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
883 ContentEncoding* const encoding = content_encoding_entries_[i];
884 if (!encoding->Write(writer))
885 return false;
886 }
887 }
888
889 stop_position = writer->Position();
890 if (stop_position < 0)
891 return false;
892 return true;
893 }
894
SetCodecPrivate(const uint8_t * codec_private,uint64_t length)895 bool Track::SetCodecPrivate(const uint8_t* codec_private, uint64_t length) {
896 if (!codec_private || length < 1)
897 return false;
898
899 delete[] codec_private_;
900
901 codec_private_ =
902 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
903 if (!codec_private_)
904 return false;
905
906 memcpy(codec_private_, codec_private, static_cast<size_t>(length));
907 codec_private_length_ = length;
908
909 return true;
910 }
911
set_codec_id(const char * codec_id)912 void Track::set_codec_id(const char* codec_id) {
913 if (codec_id) {
914 delete[] codec_id_;
915
916 const size_t length = strlen(codec_id) + 1;
917 codec_id_ = new (std::nothrow) char[length]; // NOLINT
918 if (codec_id_) {
919 #ifdef _MSC_VER
920 strcpy_s(codec_id_, length, codec_id);
921 #else
922 strcpy(codec_id_, codec_id);
923 #endif
924 }
925 }
926 }
927
928 // TODO(fgalligan): Vet the language parameter.
set_language(const char * language)929 void Track::set_language(const char* language) {
930 if (language) {
931 delete[] language_;
932
933 const size_t length = strlen(language) + 1;
934 language_ = new (std::nothrow) char[length]; // NOLINT
935 if (language_) {
936 #ifdef _MSC_VER
937 strcpy_s(language_, length, language);
938 #else
939 strcpy(language_, language);
940 #endif
941 }
942 }
943 }
944
set_name(const char * name)945 void Track::set_name(const char* name) {
946 if (name) {
947 delete[] name_;
948
949 const size_t length = strlen(name) + 1;
950 name_ = new (std::nothrow) char[length]; // NOLINT
951 if (name_) {
952 #ifdef _MSC_VER
953 strcpy_s(name_, length, name);
954 #else
955 strcpy(name_, name);
956 #endif
957 }
958 }
959 }
960
961 ///////////////////////////////////////////////////////////////
962 //
963 // Colour and its child elements
964
PrimaryChromaticitySize(libwebm::MkvId x_id,libwebm::MkvId y_id) const965 uint64_t PrimaryChromaticity::PrimaryChromaticitySize(
966 libwebm::MkvId x_id, libwebm::MkvId y_id) const {
967 return EbmlElementSize(x_id, x_) + EbmlElementSize(y_id, y_);
968 }
969
Write(IMkvWriter * writer,libwebm::MkvId x_id,libwebm::MkvId y_id) const970 bool PrimaryChromaticity::Write(IMkvWriter* writer, libwebm::MkvId x_id,
971 libwebm::MkvId y_id) const {
972 if (!Valid()) {
973 return false;
974 }
975 return WriteEbmlElement(writer, x_id, x_) &&
976 WriteEbmlElement(writer, y_id, y_);
977 }
978
Valid() const979 bool PrimaryChromaticity::Valid() const {
980 return (x_ >= kChromaticityMin && x_ <= kChromaticityMax &&
981 y_ >= kChromaticityMin && y_ <= kChromaticityMax);
982 }
983
MasteringMetadataSize() const984 uint64_t MasteringMetadata::MasteringMetadataSize() const {
985 uint64_t size = PayloadSize();
986
987 if (size > 0)
988 size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size);
989
990 return size;
991 }
992
Valid() const993 bool MasteringMetadata::Valid() const {
994 if (luminance_min_ != kValueNotPresent) {
995 if (luminance_min_ < kMinLuminance || luminance_min_ > kMinLuminanceMax ||
996 luminance_min_ > luminance_max_) {
997 return false;
998 }
999 }
1000 if (luminance_max_ != kValueNotPresent) {
1001 if (luminance_max_ < kMinLuminance || luminance_max_ > kMaxLuminanceMax ||
1002 luminance_max_ < luminance_min_) {
1003 return false;
1004 }
1005 }
1006 if (r_ && !r_->Valid())
1007 return false;
1008 if (g_ && !g_->Valid())
1009 return false;
1010 if (b_ && !b_->Valid())
1011 return false;
1012 if (white_point_ && !white_point_->Valid())
1013 return false;
1014
1015 return true;
1016 }
1017
Write(IMkvWriter * writer) const1018 bool MasteringMetadata::Write(IMkvWriter* writer) const {
1019 const uint64_t size = PayloadSize();
1020
1021 // Don't write an empty element.
1022 if (size == 0)
1023 return true;
1024
1025 if (!WriteEbmlMasterElement(writer, libwebm::kMkvMasteringMetadata, size))
1026 return false;
1027 if (luminance_max_ != kValueNotPresent &&
1028 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMax, luminance_max_)) {
1029 return false;
1030 }
1031 if (luminance_min_ != kValueNotPresent &&
1032 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMin, luminance_min_)) {
1033 return false;
1034 }
1035 if (r_ &&
1036 !r_->Write(writer, libwebm::kMkvPrimaryRChromaticityX,
1037 libwebm::kMkvPrimaryRChromaticityY)) {
1038 return false;
1039 }
1040 if (g_ &&
1041 !g_->Write(writer, libwebm::kMkvPrimaryGChromaticityX,
1042 libwebm::kMkvPrimaryGChromaticityY)) {
1043 return false;
1044 }
1045 if (b_ &&
1046 !b_->Write(writer, libwebm::kMkvPrimaryBChromaticityX,
1047 libwebm::kMkvPrimaryBChromaticityY)) {
1048 return false;
1049 }
1050 if (white_point_ &&
1051 !white_point_->Write(writer, libwebm::kMkvWhitePointChromaticityX,
1052 libwebm::kMkvWhitePointChromaticityY)) {
1053 return false;
1054 }
1055
1056 return true;
1057 }
1058
SetChromaticity(const PrimaryChromaticity * r,const PrimaryChromaticity * g,const PrimaryChromaticity * b,const PrimaryChromaticity * white_point)1059 bool MasteringMetadata::SetChromaticity(
1060 const PrimaryChromaticity* r, const PrimaryChromaticity* g,
1061 const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) {
1062 PrimaryChromaticityPtr r_ptr(NULL);
1063 if (r) {
1064 if (!CopyChromaticity(r, &r_ptr))
1065 return false;
1066 }
1067 PrimaryChromaticityPtr g_ptr(NULL);
1068 if (g) {
1069 if (!CopyChromaticity(g, &g_ptr))
1070 return false;
1071 }
1072 PrimaryChromaticityPtr b_ptr(NULL);
1073 if (b) {
1074 if (!CopyChromaticity(b, &b_ptr))
1075 return false;
1076 }
1077 PrimaryChromaticityPtr wp_ptr(NULL);
1078 if (white_point) {
1079 if (!CopyChromaticity(white_point, &wp_ptr))
1080 return false;
1081 }
1082
1083 r_ = r_ptr.release();
1084 g_ = g_ptr.release();
1085 b_ = b_ptr.release();
1086 white_point_ = wp_ptr.release();
1087 return true;
1088 }
1089
PayloadSize() const1090 uint64_t MasteringMetadata::PayloadSize() const {
1091 uint64_t size = 0;
1092
1093 if (luminance_max_ != kValueNotPresent)
1094 size += EbmlElementSize(libwebm::kMkvLuminanceMax, luminance_max_);
1095 if (luminance_min_ != kValueNotPresent)
1096 size += EbmlElementSize(libwebm::kMkvLuminanceMin, luminance_min_);
1097
1098 if (r_) {
1099 size += r_->PrimaryChromaticitySize(libwebm::kMkvPrimaryRChromaticityX,
1100 libwebm::kMkvPrimaryRChromaticityY);
1101 }
1102 if (g_) {
1103 size += g_->PrimaryChromaticitySize(libwebm::kMkvPrimaryGChromaticityX,
1104 libwebm::kMkvPrimaryGChromaticityY);
1105 }
1106 if (b_) {
1107 size += b_->PrimaryChromaticitySize(libwebm::kMkvPrimaryBChromaticityX,
1108 libwebm::kMkvPrimaryBChromaticityY);
1109 }
1110 if (white_point_) {
1111 size += white_point_->PrimaryChromaticitySize(
1112 libwebm::kMkvWhitePointChromaticityX,
1113 libwebm::kMkvWhitePointChromaticityY);
1114 }
1115
1116 return size;
1117 }
1118
ColourSize() const1119 uint64_t Colour::ColourSize() const {
1120 uint64_t size = PayloadSize();
1121
1122 if (size > 0)
1123 size += EbmlMasterElementSize(libwebm::kMkvColour, size);
1124
1125 return size;
1126 }
1127
Valid() const1128 bool Colour::Valid() const {
1129 if (mastering_metadata_ && !mastering_metadata_->Valid())
1130 return false;
1131 if (matrix_coefficients_ != kValueNotPresent &&
1132 !IsMatrixCoefficientsValueValid(matrix_coefficients_)) {
1133 return false;
1134 }
1135 if (chroma_siting_horz_ != kValueNotPresent &&
1136 !IsChromaSitingHorzValueValid(chroma_siting_horz_)) {
1137 return false;
1138 }
1139 if (chroma_siting_vert_ != kValueNotPresent &&
1140 !IsChromaSitingVertValueValid(chroma_siting_vert_)) {
1141 return false;
1142 }
1143 if (range_ != kValueNotPresent && !IsColourRangeValueValid(range_))
1144 return false;
1145 if (transfer_characteristics_ != kValueNotPresent &&
1146 !IsTransferCharacteristicsValueValid(transfer_characteristics_)) {
1147 return false;
1148 }
1149 if (primaries_ != kValueNotPresent && !IsPrimariesValueValid(primaries_))
1150 return false;
1151
1152 return true;
1153 }
1154
Write(IMkvWriter * writer) const1155 bool Colour::Write(IMkvWriter* writer) const {
1156 const uint64_t size = PayloadSize();
1157
1158 // Don't write an empty element.
1159 if (size == 0)
1160 return true;
1161
1162 // Don't write an invalid element.
1163 if (!Valid())
1164 return false;
1165
1166 if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size))
1167 return false;
1168
1169 if (matrix_coefficients_ != kValueNotPresent &&
1170 !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients,
1171 static_cast<uint64>(matrix_coefficients_))) {
1172 return false;
1173 }
1174 if (bits_per_channel_ != kValueNotPresent &&
1175 !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel,
1176 static_cast<uint64>(bits_per_channel_))) {
1177 return false;
1178 }
1179 if (chroma_subsampling_horz_ != kValueNotPresent &&
1180 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz,
1181 static_cast<uint64>(chroma_subsampling_horz_))) {
1182 return false;
1183 }
1184 if (chroma_subsampling_vert_ != kValueNotPresent &&
1185 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert,
1186 static_cast<uint64>(chroma_subsampling_vert_))) {
1187 return false;
1188 }
1189
1190 if (cb_subsampling_horz_ != kValueNotPresent &&
1191 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz,
1192 static_cast<uint64>(cb_subsampling_horz_))) {
1193 return false;
1194 }
1195 if (cb_subsampling_vert_ != kValueNotPresent &&
1196 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert,
1197 static_cast<uint64>(cb_subsampling_vert_))) {
1198 return false;
1199 }
1200 if (chroma_siting_horz_ != kValueNotPresent &&
1201 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz,
1202 static_cast<uint64>(chroma_siting_horz_))) {
1203 return false;
1204 }
1205 if (chroma_siting_vert_ != kValueNotPresent &&
1206 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert,
1207 static_cast<uint64>(chroma_siting_vert_))) {
1208 return false;
1209 }
1210 if (range_ != kValueNotPresent &&
1211 !WriteEbmlElement(writer, libwebm::kMkvRange,
1212 static_cast<uint64>(range_))) {
1213 return false;
1214 }
1215 if (transfer_characteristics_ != kValueNotPresent &&
1216 !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics,
1217 static_cast<uint64>(transfer_characteristics_))) {
1218 return false;
1219 }
1220 if (primaries_ != kValueNotPresent &&
1221 !WriteEbmlElement(writer, libwebm::kMkvPrimaries,
1222 static_cast<uint64>(primaries_))) {
1223 return false;
1224 }
1225 if (max_cll_ != kValueNotPresent &&
1226 !WriteEbmlElement(writer, libwebm::kMkvMaxCLL,
1227 static_cast<uint64>(max_cll_))) {
1228 return false;
1229 }
1230 if (max_fall_ != kValueNotPresent &&
1231 !WriteEbmlElement(writer, libwebm::kMkvMaxFALL,
1232 static_cast<uint64>(max_fall_))) {
1233 return false;
1234 }
1235
1236 if (mastering_metadata_ && !mastering_metadata_->Write(writer))
1237 return false;
1238
1239 return true;
1240 }
1241
SetMasteringMetadata(const MasteringMetadata & mastering_metadata)1242 bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) {
1243 std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
1244 if (!mm_ptr.get())
1245 return false;
1246
1247 mm_ptr->set_luminance_max(mastering_metadata.luminance_max());
1248 mm_ptr->set_luminance_min(mastering_metadata.luminance_min());
1249
1250 if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(),
1251 mastering_metadata.b(),
1252 mastering_metadata.white_point())) {
1253 return false;
1254 }
1255
1256 delete mastering_metadata_;
1257 mastering_metadata_ = mm_ptr.release();
1258 return true;
1259 }
1260
PayloadSize() const1261 uint64_t Colour::PayloadSize() const {
1262 uint64_t size = 0;
1263
1264 if (matrix_coefficients_ != kValueNotPresent) {
1265 size += EbmlElementSize(libwebm::kMkvMatrixCoefficients,
1266 static_cast<uint64>(matrix_coefficients_));
1267 }
1268 if (bits_per_channel_ != kValueNotPresent) {
1269 size += EbmlElementSize(libwebm::kMkvBitsPerChannel,
1270 static_cast<uint64>(bits_per_channel_));
1271 }
1272 if (chroma_subsampling_horz_ != kValueNotPresent) {
1273 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz,
1274 static_cast<uint64>(chroma_subsampling_horz_));
1275 }
1276 if (chroma_subsampling_vert_ != kValueNotPresent) {
1277 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert,
1278 static_cast<uint64>(chroma_subsampling_vert_));
1279 }
1280 if (cb_subsampling_horz_ != kValueNotPresent) {
1281 size += EbmlElementSize(libwebm::kMkvCbSubsamplingHorz,
1282 static_cast<uint64>(cb_subsampling_horz_));
1283 }
1284 if (cb_subsampling_vert_ != kValueNotPresent) {
1285 size += EbmlElementSize(libwebm::kMkvCbSubsamplingVert,
1286 static_cast<uint64>(cb_subsampling_vert_));
1287 }
1288 if (chroma_siting_horz_ != kValueNotPresent) {
1289 size += EbmlElementSize(libwebm::kMkvChromaSitingHorz,
1290 static_cast<uint64>(chroma_siting_horz_));
1291 }
1292 if (chroma_siting_vert_ != kValueNotPresent) {
1293 size += EbmlElementSize(libwebm::kMkvChromaSitingVert,
1294 static_cast<uint64>(chroma_siting_vert_));
1295 }
1296 if (range_ != kValueNotPresent) {
1297 size += EbmlElementSize(libwebm::kMkvRange, static_cast<uint64>(range_));
1298 }
1299 if (transfer_characteristics_ != kValueNotPresent) {
1300 size += EbmlElementSize(libwebm::kMkvTransferCharacteristics,
1301 static_cast<uint64>(transfer_characteristics_));
1302 }
1303 if (primaries_ != kValueNotPresent) {
1304 size += EbmlElementSize(libwebm::kMkvPrimaries,
1305 static_cast<uint64>(primaries_));
1306 }
1307 if (max_cll_ != kValueNotPresent) {
1308 size += EbmlElementSize(libwebm::kMkvMaxCLL, static_cast<uint64>(max_cll_));
1309 }
1310 if (max_fall_ != kValueNotPresent) {
1311 size +=
1312 EbmlElementSize(libwebm::kMkvMaxFALL, static_cast<uint64>(max_fall_));
1313 }
1314
1315 if (mastering_metadata_)
1316 size += mastering_metadata_->MasteringMetadataSize();
1317
1318 return size;
1319 }
1320
1321 ///////////////////////////////////////////////////////////////
1322 //
1323 // Projection element
1324
ProjectionSize() const1325 uint64_t Projection::ProjectionSize() const {
1326 uint64_t size = PayloadSize();
1327
1328 if (size > 0)
1329 size += EbmlMasterElementSize(libwebm::kMkvProjection, size);
1330
1331 return size;
1332 }
1333
Write(IMkvWriter * writer) const1334 bool Projection::Write(IMkvWriter* writer) const {
1335 const uint64_t size = PayloadSize();
1336
1337 // Don't write an empty element.
1338 if (size == 0)
1339 return true;
1340
1341 if (!WriteEbmlMasterElement(writer, libwebm::kMkvProjection, size))
1342 return false;
1343
1344 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionType,
1345 static_cast<uint64>(type_))) {
1346 return false;
1347 }
1348
1349 if (private_data_length_ > 0 && private_data_ != NULL &&
1350 !WriteEbmlElement(writer, libwebm::kMkvProjectionPrivate, private_data_,
1351 private_data_length_)) {
1352 return false;
1353 }
1354
1355 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseYaw, pose_yaw_))
1356 return false;
1357
1358 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPosePitch,
1359 pose_pitch_)) {
1360 return false;
1361 }
1362
1363 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseRoll, pose_roll_)) {
1364 return false;
1365 }
1366
1367 return true;
1368 }
1369
SetProjectionPrivate(const uint8_t * data,uint64_t data_length)1370 bool Projection::SetProjectionPrivate(const uint8_t* data,
1371 uint64_t data_length) {
1372 if (data == NULL || data_length == 0) {
1373 return false;
1374 }
1375
1376 if (data_length != static_cast<size_t>(data_length)) {
1377 return false;
1378 }
1379
1380 uint8_t* new_private_data =
1381 new (std::nothrow) uint8_t[static_cast<size_t>(data_length)];
1382 if (new_private_data == NULL) {
1383 return false;
1384 }
1385
1386 delete[] private_data_;
1387 private_data_ = new_private_data;
1388 private_data_length_ = data_length;
1389 memcpy(private_data_, data, static_cast<size_t>(data_length));
1390
1391 return true;
1392 }
1393
PayloadSize() const1394 uint64_t Projection::PayloadSize() const {
1395 uint64_t size =
1396 EbmlElementSize(libwebm::kMkvProjection, static_cast<uint64>(type_));
1397
1398 if (private_data_length_ > 0 && private_data_ != NULL) {
1399 size += EbmlElementSize(libwebm::kMkvProjectionPrivate, private_data_,
1400 private_data_length_);
1401 }
1402
1403 size += EbmlElementSize(libwebm::kMkvProjectionPoseYaw, pose_yaw_);
1404 size += EbmlElementSize(libwebm::kMkvProjectionPosePitch, pose_pitch_);
1405 size += EbmlElementSize(libwebm::kMkvProjectionPoseRoll, pose_roll_);
1406
1407 return size;
1408 }
1409
1410 ///////////////////////////////////////////////////////////////
1411 //
1412 // VideoTrack Class
1413
VideoTrack(unsigned int * seed)1414 VideoTrack::VideoTrack(unsigned int* seed)
1415 : Track(seed),
1416 display_height_(0),
1417 display_width_(0),
1418 pixel_height_(0),
1419 pixel_width_(0),
1420 crop_left_(0),
1421 crop_right_(0),
1422 crop_top_(0),
1423 crop_bottom_(0),
1424 frame_rate_(0.0),
1425 height_(0),
1426 stereo_mode_(0),
1427 alpha_mode_(0),
1428 width_(0),
1429 colour_(NULL),
1430 projection_(NULL) {}
1431
~VideoTrack()1432 VideoTrack::~VideoTrack() {
1433 delete colour_;
1434 delete projection_;
1435 }
1436
SetStereoMode(uint64_t stereo_mode)1437 bool VideoTrack::SetStereoMode(uint64_t stereo_mode) {
1438 if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst &&
1439 stereo_mode != kTopBottomRightIsFirst &&
1440 stereo_mode != kTopBottomLeftIsFirst &&
1441 stereo_mode != kSideBySideRightIsFirst)
1442 return false;
1443
1444 stereo_mode_ = stereo_mode;
1445 return true;
1446 }
1447
SetAlphaMode(uint64_t alpha_mode)1448 bool VideoTrack::SetAlphaMode(uint64_t alpha_mode) {
1449 if (alpha_mode != kNoAlpha && alpha_mode != kAlpha)
1450 return false;
1451
1452 alpha_mode_ = alpha_mode;
1453 return true;
1454 }
1455
PayloadSize() const1456 uint64_t VideoTrack::PayloadSize() const {
1457 const uint64_t parent_size = Track::PayloadSize();
1458
1459 uint64_t size = VideoPayloadSize();
1460 size += EbmlMasterElementSize(libwebm::kMkvVideo, size);
1461
1462 return parent_size + size;
1463 }
1464
Write(IMkvWriter * writer) const1465 bool VideoTrack::Write(IMkvWriter* writer) const {
1466 if (!Track::Write(writer))
1467 return false;
1468
1469 const uint64_t size = VideoPayloadSize();
1470
1471 if (!WriteEbmlMasterElement(writer, libwebm::kMkvVideo, size))
1472 return false;
1473
1474 const int64_t payload_position = writer->Position();
1475 if (payload_position < 0)
1476 return false;
1477
1478 if (!WriteEbmlElement(
1479 writer, libwebm::kMkvPixelWidth,
1480 static_cast<uint64>((pixel_width_ > 0) ? pixel_width_ : width_)))
1481 return false;
1482 if (!WriteEbmlElement(
1483 writer, libwebm::kMkvPixelHeight,
1484 static_cast<uint64>((pixel_height_ > 0) ? pixel_height_ : height_)))
1485 return false;
1486 if (display_width_ > 0) {
1487 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth,
1488 static_cast<uint64>(display_width_)))
1489 return false;
1490 }
1491 if (display_height_ > 0) {
1492 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight,
1493 static_cast<uint64>(display_height_)))
1494 return false;
1495 }
1496 if (crop_left_ > 0) {
1497 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft,
1498 static_cast<uint64>(crop_left_)))
1499 return false;
1500 }
1501 if (crop_right_ > 0) {
1502 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight,
1503 static_cast<uint64>(crop_right_)))
1504 return false;
1505 }
1506 if (crop_top_ > 0) {
1507 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop,
1508 static_cast<uint64>(crop_top_)))
1509 return false;
1510 }
1511 if (crop_bottom_ > 0) {
1512 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom,
1513 static_cast<uint64>(crop_bottom_)))
1514 return false;
1515 }
1516 if (stereo_mode_ > kMono) {
1517 if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode,
1518 static_cast<uint64>(stereo_mode_)))
1519 return false;
1520 }
1521 if (alpha_mode_ > kNoAlpha) {
1522 if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode,
1523 static_cast<uint64>(alpha_mode_)))
1524 return false;
1525 }
1526 if (frame_rate_ > 0.0) {
1527 if (!WriteEbmlElement(writer, libwebm::kMkvFrameRate,
1528 static_cast<float>(frame_rate_))) {
1529 return false;
1530 }
1531 }
1532 if (colour_) {
1533 if (!colour_->Write(writer))
1534 return false;
1535 }
1536 if (projection_) {
1537 if (!projection_->Write(writer))
1538 return false;
1539 }
1540
1541 const int64_t stop_position = writer->Position();
1542 if (stop_position < 0 ||
1543 stop_position - payload_position != static_cast<int64_t>(size)) {
1544 return false;
1545 }
1546
1547 return true;
1548 }
1549
SetColour(const Colour & colour)1550 bool VideoTrack::SetColour(const Colour& colour) {
1551 std::auto_ptr<Colour> colour_ptr(new Colour());
1552 if (!colour_ptr.get())
1553 return false;
1554
1555 if (colour.mastering_metadata()) {
1556 if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata()))
1557 return false;
1558 }
1559
1560 colour_ptr->set_matrix_coefficients(colour.matrix_coefficients());
1561 colour_ptr->set_bits_per_channel(colour.bits_per_channel());
1562 colour_ptr->set_chroma_subsampling_horz(colour.chroma_subsampling_horz());
1563 colour_ptr->set_chroma_subsampling_vert(colour.chroma_subsampling_vert());
1564 colour_ptr->set_cb_subsampling_horz(colour.cb_subsampling_horz());
1565 colour_ptr->set_cb_subsampling_vert(colour.cb_subsampling_vert());
1566 colour_ptr->set_chroma_siting_horz(colour.chroma_siting_horz());
1567 colour_ptr->set_chroma_siting_vert(colour.chroma_siting_vert());
1568 colour_ptr->set_range(colour.range());
1569 colour_ptr->set_transfer_characteristics(colour.transfer_characteristics());
1570 colour_ptr->set_primaries(colour.primaries());
1571 colour_ptr->set_max_cll(colour.max_cll());
1572 colour_ptr->set_max_fall(colour.max_fall());
1573 delete colour_;
1574 colour_ = colour_ptr.release();
1575 return true;
1576 }
1577
SetProjection(const Projection & projection)1578 bool VideoTrack::SetProjection(const Projection& projection) {
1579 std::auto_ptr<Projection> projection_ptr(new Projection());
1580 if (!projection_ptr.get())
1581 return false;
1582
1583 if (projection.private_data()) {
1584 if (!projection_ptr->SetProjectionPrivate(
1585 projection.private_data(), projection.private_data_length())) {
1586 return false;
1587 }
1588 }
1589
1590 projection_ptr->set_type(projection.type());
1591 projection_ptr->set_pose_yaw(projection.pose_yaw());
1592 projection_ptr->set_pose_pitch(projection.pose_pitch());
1593 projection_ptr->set_pose_roll(projection.pose_roll());
1594 delete projection_;
1595 projection_ = projection_ptr.release();
1596 return true;
1597 }
1598
VideoPayloadSize() const1599 uint64_t VideoTrack::VideoPayloadSize() const {
1600 uint64_t size = EbmlElementSize(
1601 libwebm::kMkvPixelWidth,
1602 static_cast<uint64>((pixel_width_ > 0) ? pixel_width_ : width_));
1603 size += EbmlElementSize(
1604 libwebm::kMkvPixelHeight,
1605 static_cast<uint64>((pixel_height_ > 0) ? pixel_height_ : height_));
1606 if (display_width_ > 0)
1607 size += EbmlElementSize(libwebm::kMkvDisplayWidth,
1608 static_cast<uint64>(display_width_));
1609 if (display_height_ > 0)
1610 size += EbmlElementSize(libwebm::kMkvDisplayHeight,
1611 static_cast<uint64>(display_height_));
1612 if (crop_left_ > 0)
1613 size += EbmlElementSize(libwebm::kMkvPixelCropLeft,
1614 static_cast<uint64>(crop_left_));
1615 if (crop_right_ > 0)
1616 size += EbmlElementSize(libwebm::kMkvPixelCropRight,
1617 static_cast<uint64>(crop_right_));
1618 if (crop_top_ > 0)
1619 size += EbmlElementSize(libwebm::kMkvPixelCropTop,
1620 static_cast<uint64>(crop_top_));
1621 if (crop_bottom_ > 0)
1622 size += EbmlElementSize(libwebm::kMkvPixelCropBottom,
1623 static_cast<uint64>(crop_bottom_));
1624 if (stereo_mode_ > kMono)
1625 size += EbmlElementSize(libwebm::kMkvStereoMode,
1626 static_cast<uint64>(stereo_mode_));
1627 if (alpha_mode_ > kNoAlpha)
1628 size += EbmlElementSize(libwebm::kMkvAlphaMode,
1629 static_cast<uint64>(alpha_mode_));
1630 if (frame_rate_ > 0.0)
1631 size += EbmlElementSize(libwebm::kMkvFrameRate,
1632 static_cast<float>(frame_rate_));
1633 if (colour_)
1634 size += colour_->ColourSize();
1635 if (projection_)
1636 size += projection_->ProjectionSize();
1637
1638 return size;
1639 }
1640
1641 ///////////////////////////////////////////////////////////////
1642 //
1643 // AudioTrack Class
1644
AudioTrack(unsigned int * seed)1645 AudioTrack::AudioTrack(unsigned int* seed)
1646 : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
1647
~AudioTrack()1648 AudioTrack::~AudioTrack() {}
1649
PayloadSize() const1650 uint64_t AudioTrack::PayloadSize() const {
1651 const uint64_t parent_size = Track::PayloadSize();
1652
1653 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
1654 static_cast<float>(sample_rate_));
1655 size +=
1656 EbmlElementSize(libwebm::kMkvChannels, static_cast<uint64>(channels_));
1657 if (bit_depth_ > 0)
1658 size +=
1659 EbmlElementSize(libwebm::kMkvBitDepth, static_cast<uint64>(bit_depth_));
1660 size += EbmlMasterElementSize(libwebm::kMkvAudio, size);
1661
1662 return parent_size + size;
1663 }
1664
Write(IMkvWriter * writer) const1665 bool AudioTrack::Write(IMkvWriter* writer) const {
1666 if (!Track::Write(writer))
1667 return false;
1668
1669 // Calculate AudioSettings size.
1670 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
1671 static_cast<float>(sample_rate_));
1672 size +=
1673 EbmlElementSize(libwebm::kMkvChannels, static_cast<uint64>(channels_));
1674 if (bit_depth_ > 0)
1675 size +=
1676 EbmlElementSize(libwebm::kMkvBitDepth, static_cast<uint64>(bit_depth_));
1677
1678 if (!WriteEbmlMasterElement(writer, libwebm::kMkvAudio, size))
1679 return false;
1680
1681 const int64_t payload_position = writer->Position();
1682 if (payload_position < 0)
1683 return false;
1684
1685 if (!WriteEbmlElement(writer, libwebm::kMkvSamplingFrequency,
1686 static_cast<float>(sample_rate_)))
1687 return false;
1688 if (!WriteEbmlElement(writer, libwebm::kMkvChannels,
1689 static_cast<uint64>(channels_)))
1690 return false;
1691 if (bit_depth_ > 0)
1692 if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth,
1693 static_cast<uint64>(bit_depth_)))
1694 return false;
1695
1696 const int64_t stop_position = writer->Position();
1697 if (stop_position < 0 ||
1698 stop_position - payload_position != static_cast<int64_t>(size))
1699 return false;
1700
1701 return true;
1702 }
1703
1704 ///////////////////////////////////////////////////////////////
1705 //
1706 // Tracks Class
1707
1708 const char Tracks::kOpusCodecId[] = "A_OPUS";
1709 const char Tracks::kVorbisCodecId[] = "A_VORBIS";
1710 const char Tracks::kVp8CodecId[] = "V_VP8";
1711 const char Tracks::kVp9CodecId[] = "V_VP9";
1712 const char Tracks::kVp10CodecId[] = "V_VP10";
1713 const char Tracks::kAV1CodecId[] = "V_AV1";
1714 const char Tracks::kWebVttCaptionsId[] = "D_WEBVTT/CAPTIONS";
1715 const char Tracks::kWebVttDescriptionsId[] = "D_WEBVTT/DESCRIPTIONS";
1716 const char Tracks::kWebVttMetadataId[] = "D_WEBVTT/METADATA";
1717 const char Tracks::kWebVttSubtitlesId[] = "D_WEBVTT/SUBTITLES";
1718
Tracks()1719 Tracks::Tracks()
1720 : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {}
1721
~Tracks()1722 Tracks::~Tracks() {
1723 if (track_entries_) {
1724 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1725 Track* const track = track_entries_[i];
1726 delete track;
1727 }
1728 delete[] track_entries_;
1729 }
1730 }
1731
AddTrack(Track * track,int32_t number)1732 bool Tracks::AddTrack(Track* track, int32_t number) {
1733 if (number < 0 || wrote_tracks_)
1734 return false;
1735
1736 // This muxer only supports track numbers in the range [1, 126], in
1737 // order to be able (to use Matroska integer representation) to
1738 // serialize the block header (of which the track number is a part)
1739 // for a frame using exactly 4 bytes.
1740
1741 if (number > 0x7E)
1742 return false;
1743
1744 uint32_t track_num = number;
1745
1746 if (track_num > 0) {
1747 // Check to make sure a track does not already have |track_num|.
1748 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1749 if (track_entries_[i]->number() == track_num)
1750 return false;
1751 }
1752 }
1753
1754 const uint32_t count = track_entries_size_ + 1;
1755
1756 Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
1757 if (!track_entries)
1758 return false;
1759
1760 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1761 track_entries[i] = track_entries_[i];
1762 }
1763
1764 delete[] track_entries_;
1765
1766 // Find the lowest availible track number > 0.
1767 if (track_num == 0) {
1768 track_num = count;
1769
1770 // Check to make sure a track does not already have |track_num|.
1771 bool exit = false;
1772 do {
1773 exit = true;
1774 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1775 if (track_entries[i]->number() == track_num) {
1776 track_num++;
1777 exit = false;
1778 break;
1779 }
1780 }
1781 } while (!exit);
1782 }
1783 track->set_number(track_num);
1784
1785 track_entries_ = track_entries;
1786 track_entries_[track_entries_size_] = track;
1787 track_entries_size_ = count;
1788 return true;
1789 }
1790
GetTrackByIndex(uint32_t index) const1791 const Track* Tracks::GetTrackByIndex(uint32_t index) const {
1792 if (track_entries_ == NULL)
1793 return NULL;
1794
1795 if (index >= track_entries_size_)
1796 return NULL;
1797
1798 return track_entries_[index];
1799 }
1800
GetTrackByNumber(uint64_t track_number) const1801 Track* Tracks::GetTrackByNumber(uint64_t track_number) const {
1802 const int32_t count = track_entries_size();
1803 for (int32_t i = 0; i < count; ++i) {
1804 if (track_entries_[i]->number() == track_number)
1805 return track_entries_[i];
1806 }
1807
1808 return NULL;
1809 }
1810
TrackIsAudio(uint64_t track_number) const1811 bool Tracks::TrackIsAudio(uint64_t track_number) const {
1812 const Track* const track = GetTrackByNumber(track_number);
1813
1814 if (track->type() == kAudio)
1815 return true;
1816
1817 return false;
1818 }
1819
TrackIsVideo(uint64_t track_number) const1820 bool Tracks::TrackIsVideo(uint64_t track_number) const {
1821 const Track* const track = GetTrackByNumber(track_number);
1822
1823 if (track->type() == kVideo)
1824 return true;
1825
1826 return false;
1827 }
1828
Write(IMkvWriter * writer) const1829 bool Tracks::Write(IMkvWriter* writer) const {
1830 uint64_t size = 0;
1831 const int32_t count = track_entries_size();
1832 for (int32_t i = 0; i < count; ++i) {
1833 const Track* const track = GetTrackByIndex(i);
1834
1835 if (!track)
1836 return false;
1837
1838 size += track->Size();
1839 }
1840
1841 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTracks, size))
1842 return false;
1843
1844 const int64_t payload_position = writer->Position();
1845 if (payload_position < 0)
1846 return false;
1847
1848 for (int32_t i = 0; i < count; ++i) {
1849 const Track* const track = GetTrackByIndex(i);
1850 if (!track->Write(writer))
1851 return false;
1852 }
1853
1854 const int64_t stop_position = writer->Position();
1855 if (stop_position < 0 ||
1856 stop_position - payload_position != static_cast<int64_t>(size))
1857 return false;
1858
1859 wrote_tracks_ = true;
1860 return true;
1861 }
1862
1863 ///////////////////////////////////////////////////////////////
1864 //
1865 // Chapter Class
1866
set_id(const char * id)1867 bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
1868
set_time(const Segment & segment,uint64_t start_ns,uint64_t end_ns)1869 void Chapter::set_time(const Segment& segment, uint64_t start_ns,
1870 uint64_t end_ns) {
1871 const SegmentInfo* const info = segment.GetSegmentInfo();
1872 const uint64_t timecode_scale = info->timecode_scale();
1873 start_timecode_ = start_ns / timecode_scale;
1874 end_timecode_ = end_ns / timecode_scale;
1875 }
1876
add_string(const char * title,const char * language,const char * country)1877 bool Chapter::add_string(const char* title, const char* language,
1878 const char* country) {
1879 if (!ExpandDisplaysArray())
1880 return false;
1881
1882 Display& d = displays_[displays_count_++];
1883 d.Init();
1884
1885 if (!d.set_title(title))
1886 return false;
1887
1888 if (!d.set_language(language))
1889 return false;
1890
1891 if (!d.set_country(country))
1892 return false;
1893
1894 return true;
1895 }
1896
Chapter()1897 Chapter::Chapter() {
1898 // This ctor only constructs the object. Proper initialization is
1899 // done in Init() (called in Chapters::AddChapter()). The only
1900 // reason we bother implementing this ctor is because we had to
1901 // declare it as private (along with the dtor), in order to prevent
1902 // clients from creating Chapter instances (a privelege we grant
1903 // only to the Chapters class). Doing no initialization here also
1904 // means that creating arrays of chapter objects is more efficient,
1905 // because we only initialize each new chapter object as it becomes
1906 // active on the array.
1907 }
1908
~Chapter()1909 Chapter::~Chapter() {}
1910
Init(unsigned int * seed)1911 void Chapter::Init(unsigned int* seed) {
1912 id_ = NULL;
1913 start_timecode_ = 0;
1914 end_timecode_ = 0;
1915 displays_ = NULL;
1916 displays_size_ = 0;
1917 displays_count_ = 0;
1918 uid_ = MakeUID(seed);
1919 }
1920
ShallowCopy(Chapter * dst) const1921 void Chapter::ShallowCopy(Chapter* dst) const {
1922 dst->id_ = id_;
1923 dst->start_timecode_ = start_timecode_;
1924 dst->end_timecode_ = end_timecode_;
1925 dst->uid_ = uid_;
1926 dst->displays_ = displays_;
1927 dst->displays_size_ = displays_size_;
1928 dst->displays_count_ = displays_count_;
1929 }
1930
Clear()1931 void Chapter::Clear() {
1932 StrCpy(NULL, &id_);
1933
1934 while (displays_count_ > 0) {
1935 Display& d = displays_[--displays_count_];
1936 d.Clear();
1937 }
1938
1939 delete[] displays_;
1940 displays_ = NULL;
1941
1942 displays_size_ = 0;
1943 }
1944
ExpandDisplaysArray()1945 bool Chapter::ExpandDisplaysArray() {
1946 if (displays_size_ > displays_count_)
1947 return true; // nothing to do yet
1948
1949 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
1950
1951 Display* const displays = new (std::nothrow) Display[size]; // NOLINT
1952 if (displays == NULL)
1953 return false;
1954
1955 for (int idx = 0; idx < displays_count_; ++idx) {
1956 displays[idx] = displays_[idx]; // shallow copy
1957 }
1958
1959 delete[] displays_;
1960
1961 displays_ = displays;
1962 displays_size_ = size;
1963
1964 return true;
1965 }
1966
WriteAtom(IMkvWriter * writer) const1967 uint64_t Chapter::WriteAtom(IMkvWriter* writer) const {
1968 uint64_t payload_size =
1969 EbmlElementSize(libwebm::kMkvChapterStringUID, id_) +
1970 EbmlElementSize(libwebm::kMkvChapterUID, static_cast<uint64>(uid_)) +
1971 EbmlElementSize(libwebm::kMkvChapterTimeStart,
1972 static_cast<uint64>(start_timecode_)) +
1973 EbmlElementSize(libwebm::kMkvChapterTimeEnd,
1974 static_cast<uint64>(end_timecode_));
1975
1976 for (int idx = 0; idx < displays_count_; ++idx) {
1977 const Display& d = displays_[idx];
1978 payload_size += d.WriteDisplay(NULL);
1979 }
1980
1981 const uint64_t atom_size =
1982 EbmlMasterElementSize(libwebm::kMkvChapterAtom, payload_size) +
1983 payload_size;
1984
1985 if (writer == NULL)
1986 return atom_size;
1987
1988 const int64_t start = writer->Position();
1989
1990 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterAtom, payload_size))
1991 return 0;
1992
1993 if (!WriteEbmlElement(writer, libwebm::kMkvChapterStringUID, id_))
1994 return 0;
1995
1996 if (!WriteEbmlElement(writer, libwebm::kMkvChapterUID,
1997 static_cast<uint64>(uid_)))
1998 return 0;
1999
2000 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart,
2001 static_cast<uint64>(start_timecode_)))
2002 return 0;
2003
2004 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd,
2005 static_cast<uint64>(end_timecode_)))
2006 return 0;
2007
2008 for (int idx = 0; idx < displays_count_; ++idx) {
2009 const Display& d = displays_[idx];
2010
2011 if (!d.WriteDisplay(writer))
2012 return 0;
2013 }
2014
2015 const int64_t stop = writer->Position();
2016
2017 if (stop >= start && uint64_t(stop - start) != atom_size)
2018 return 0;
2019
2020 return atom_size;
2021 }
2022
Init()2023 void Chapter::Display::Init() {
2024 title_ = NULL;
2025 language_ = NULL;
2026 country_ = NULL;
2027 }
2028
Clear()2029 void Chapter::Display::Clear() {
2030 StrCpy(NULL, &title_);
2031 StrCpy(NULL, &language_);
2032 StrCpy(NULL, &country_);
2033 }
2034
set_title(const char * title)2035 bool Chapter::Display::set_title(const char* title) {
2036 return StrCpy(title, &title_);
2037 }
2038
set_language(const char * language)2039 bool Chapter::Display::set_language(const char* language) {
2040 return StrCpy(language, &language_);
2041 }
2042
set_country(const char * country)2043 bool Chapter::Display::set_country(const char* country) {
2044 return StrCpy(country, &country_);
2045 }
2046
WriteDisplay(IMkvWriter * writer) const2047 uint64_t Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
2048 uint64_t payload_size = EbmlElementSize(libwebm::kMkvChapString, title_);
2049
2050 if (language_)
2051 payload_size += EbmlElementSize(libwebm::kMkvChapLanguage, language_);
2052
2053 if (country_)
2054 payload_size += EbmlElementSize(libwebm::kMkvChapCountry, country_);
2055
2056 const uint64_t display_size =
2057 EbmlMasterElementSize(libwebm::kMkvChapterDisplay, payload_size) +
2058 payload_size;
2059
2060 if (writer == NULL)
2061 return display_size;
2062
2063 const int64_t start = writer->Position();
2064
2065 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterDisplay,
2066 payload_size))
2067 return 0;
2068
2069 if (!WriteEbmlElement(writer, libwebm::kMkvChapString, title_))
2070 return 0;
2071
2072 if (language_) {
2073 if (!WriteEbmlElement(writer, libwebm::kMkvChapLanguage, language_))
2074 return 0;
2075 }
2076
2077 if (country_) {
2078 if (!WriteEbmlElement(writer, libwebm::kMkvChapCountry, country_))
2079 return 0;
2080 }
2081
2082 const int64_t stop = writer->Position();
2083
2084 if (stop >= start && uint64_t(stop - start) != display_size)
2085 return 0;
2086
2087 return display_size;
2088 }
2089
2090 ///////////////////////////////////////////////////////////////
2091 //
2092 // Chapters Class
2093
Chapters()2094 Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
2095
~Chapters()2096 Chapters::~Chapters() {
2097 while (chapters_count_ > 0) {
2098 Chapter& chapter = chapters_[--chapters_count_];
2099 chapter.Clear();
2100 }
2101
2102 delete[] chapters_;
2103 chapters_ = NULL;
2104 }
2105
Count() const2106 int Chapters::Count() const { return chapters_count_; }
2107
AddChapter(unsigned int * seed)2108 Chapter* Chapters::AddChapter(unsigned int* seed) {
2109 if (!ExpandChaptersArray())
2110 return NULL;
2111
2112 Chapter& chapter = chapters_[chapters_count_++];
2113 chapter.Init(seed);
2114
2115 return &chapter;
2116 }
2117
Write(IMkvWriter * writer) const2118 bool Chapters::Write(IMkvWriter* writer) const {
2119 if (writer == NULL)
2120 return false;
2121
2122 const uint64_t payload_size = WriteEdition(NULL); // return size only
2123
2124 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapters, payload_size))
2125 return false;
2126
2127 const int64_t start = writer->Position();
2128
2129 if (WriteEdition(writer) == 0) // error
2130 return false;
2131
2132 const int64_t stop = writer->Position();
2133
2134 if (stop >= start && uint64_t(stop - start) != payload_size)
2135 return false;
2136
2137 return true;
2138 }
2139
ExpandChaptersArray()2140 bool Chapters::ExpandChaptersArray() {
2141 if (chapters_size_ > chapters_count_)
2142 return true; // nothing to do yet
2143
2144 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
2145
2146 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
2147 if (chapters == NULL)
2148 return false;
2149
2150 for (int idx = 0; idx < chapters_count_; ++idx) {
2151 const Chapter& src = chapters_[idx];
2152 Chapter* const dst = chapters + idx;
2153 src.ShallowCopy(dst);
2154 }
2155
2156 delete[] chapters_;
2157
2158 chapters_ = chapters;
2159 chapters_size_ = size;
2160
2161 return true;
2162 }
2163
WriteEdition(IMkvWriter * writer) const2164 uint64_t Chapters::WriteEdition(IMkvWriter* writer) const {
2165 uint64_t payload_size = 0;
2166
2167 for (int idx = 0; idx < chapters_count_; ++idx) {
2168 const Chapter& chapter = chapters_[idx];
2169 payload_size += chapter.WriteAtom(NULL);
2170 }
2171
2172 const uint64_t edition_size =
2173 EbmlMasterElementSize(libwebm::kMkvEditionEntry, payload_size) +
2174 payload_size;
2175
2176 if (writer == NULL) // return size only
2177 return edition_size;
2178
2179 const int64_t start = writer->Position();
2180
2181 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEditionEntry, payload_size))
2182 return 0; // error
2183
2184 for (int idx = 0; idx < chapters_count_; ++idx) {
2185 const Chapter& chapter = chapters_[idx];
2186
2187 const uint64_t chapter_size = chapter.WriteAtom(writer);
2188 if (chapter_size == 0) // error
2189 return 0;
2190 }
2191
2192 const int64_t stop = writer->Position();
2193
2194 if (stop >= start && uint64_t(stop - start) != edition_size)
2195 return 0;
2196
2197 return edition_size;
2198 }
2199
2200 // Tag Class
2201
add_simple_tag(const char * tag_name,const char * tag_string)2202 bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) {
2203 if (!ExpandSimpleTagsArray())
2204 return false;
2205
2206 SimpleTag& st = simple_tags_[simple_tags_count_++];
2207 st.Init();
2208
2209 if (!st.set_tag_name(tag_name))
2210 return false;
2211
2212 if (!st.set_tag_string(tag_string))
2213 return false;
2214
2215 return true;
2216 }
2217
Tag()2218 Tag::Tag() {
2219 simple_tags_ = NULL;
2220 simple_tags_size_ = 0;
2221 simple_tags_count_ = 0;
2222 }
2223
~Tag()2224 Tag::~Tag() {}
2225
ShallowCopy(Tag * dst) const2226 void Tag::ShallowCopy(Tag* dst) const {
2227 dst->simple_tags_ = simple_tags_;
2228 dst->simple_tags_size_ = simple_tags_size_;
2229 dst->simple_tags_count_ = simple_tags_count_;
2230 }
2231
Clear()2232 void Tag::Clear() {
2233 while (simple_tags_count_ > 0) {
2234 SimpleTag& st = simple_tags_[--simple_tags_count_];
2235 st.Clear();
2236 }
2237
2238 delete[] simple_tags_;
2239 simple_tags_ = NULL;
2240
2241 simple_tags_size_ = 0;
2242 }
2243
ExpandSimpleTagsArray()2244 bool Tag::ExpandSimpleTagsArray() {
2245 if (simple_tags_size_ > simple_tags_count_)
2246 return true; // nothing to do yet
2247
2248 const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_;
2249
2250 SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT
2251 if (simple_tags == NULL)
2252 return false;
2253
2254 for (int idx = 0; idx < simple_tags_count_; ++idx) {
2255 simple_tags[idx] = simple_tags_[idx]; // shallow copy
2256 }
2257
2258 delete[] simple_tags_;
2259
2260 simple_tags_ = simple_tags;
2261 simple_tags_size_ = size;
2262
2263 return true;
2264 }
2265
Write(IMkvWriter * writer) const2266 uint64_t Tag::Write(IMkvWriter* writer) const {
2267 uint64_t payload_size = 0;
2268
2269 for (int idx = 0; idx < simple_tags_count_; ++idx) {
2270 const SimpleTag& st = simple_tags_[idx];
2271 payload_size += st.Write(NULL);
2272 }
2273
2274 const uint64_t tag_size =
2275 EbmlMasterElementSize(libwebm::kMkvTag, payload_size) + payload_size;
2276
2277 if (writer == NULL)
2278 return tag_size;
2279
2280 const int64_t start = writer->Position();
2281
2282 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTag, payload_size))
2283 return 0;
2284
2285 for (int idx = 0; idx < simple_tags_count_; ++idx) {
2286 const SimpleTag& st = simple_tags_[idx];
2287
2288 if (!st.Write(writer))
2289 return 0;
2290 }
2291
2292 const int64_t stop = writer->Position();
2293
2294 if (stop >= start && uint64_t(stop - start) != tag_size)
2295 return 0;
2296
2297 return tag_size;
2298 }
2299
2300 // Tag::SimpleTag
2301
Init()2302 void Tag::SimpleTag::Init() {
2303 tag_name_ = NULL;
2304 tag_string_ = NULL;
2305 }
2306
Clear()2307 void Tag::SimpleTag::Clear() {
2308 StrCpy(NULL, &tag_name_);
2309 StrCpy(NULL, &tag_string_);
2310 }
2311
set_tag_name(const char * tag_name)2312 bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
2313 return StrCpy(tag_name, &tag_name_);
2314 }
2315
set_tag_string(const char * tag_string)2316 bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
2317 return StrCpy(tag_string, &tag_string_);
2318 }
2319
Write(IMkvWriter * writer) const2320 uint64_t Tag::SimpleTag::Write(IMkvWriter* writer) const {
2321 uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_);
2322
2323 payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_);
2324
2325 const uint64_t simple_tag_size =
2326 EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) +
2327 payload_size;
2328
2329 if (writer == NULL)
2330 return simple_tag_size;
2331
2332 const int64_t start = writer->Position();
2333
2334 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size))
2335 return 0;
2336
2337 if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_))
2338 return 0;
2339
2340 if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_))
2341 return 0;
2342
2343 const int64_t stop = writer->Position();
2344
2345 if (stop >= start && uint64_t(stop - start) != simple_tag_size)
2346 return 0;
2347
2348 return simple_tag_size;
2349 }
2350
2351 // Tags Class
2352
Tags()2353 Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
2354
~Tags()2355 Tags::~Tags() {
2356 while (tags_count_ > 0) {
2357 Tag& tag = tags_[--tags_count_];
2358 tag.Clear();
2359 }
2360
2361 delete[] tags_;
2362 tags_ = NULL;
2363 }
2364
Count() const2365 int Tags::Count() const { return tags_count_; }
2366
AddTag()2367 Tag* Tags::AddTag() {
2368 if (!ExpandTagsArray())
2369 return NULL;
2370
2371 Tag& tag = tags_[tags_count_++];
2372
2373 return &tag;
2374 }
2375
Write(IMkvWriter * writer) const2376 bool Tags::Write(IMkvWriter* writer) const {
2377 if (writer == NULL)
2378 return false;
2379
2380 uint64_t payload_size = 0;
2381
2382 for (int idx = 0; idx < tags_count_; ++idx) {
2383 const Tag& tag = tags_[idx];
2384 payload_size += tag.Write(NULL);
2385 }
2386
2387 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTags, payload_size))
2388 return false;
2389
2390 const int64_t start = writer->Position();
2391
2392 for (int idx = 0; idx < tags_count_; ++idx) {
2393 const Tag& tag = tags_[idx];
2394
2395 const uint64_t tag_size = tag.Write(writer);
2396 if (tag_size == 0) // error
2397 return 0;
2398 }
2399
2400 const int64_t stop = writer->Position();
2401
2402 if (stop >= start && uint64_t(stop - start) != payload_size)
2403 return false;
2404
2405 return true;
2406 }
2407
ExpandTagsArray()2408 bool Tags::ExpandTagsArray() {
2409 if (tags_size_ > tags_count_)
2410 return true; // nothing to do yet
2411
2412 const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_;
2413
2414 Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT
2415 if (tags == NULL)
2416 return false;
2417
2418 for (int idx = 0; idx < tags_count_; ++idx) {
2419 const Tag& src = tags_[idx];
2420 Tag* const dst = tags + idx;
2421 src.ShallowCopy(dst);
2422 }
2423
2424 delete[] tags_;
2425
2426 tags_ = tags;
2427 tags_size_ = size;
2428
2429 return true;
2430 }
2431
2432 ///////////////////////////////////////////////////////////////
2433 //
2434 // Cluster class
2435
Cluster(uint64_t timecode,int64_t cues_pos,uint64_t timecode_scale,bool write_last_frame_with_duration,bool fixed_size_timecode)2436 Cluster::Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale,
2437 bool write_last_frame_with_duration, bool fixed_size_timecode)
2438 : blocks_added_(0),
2439 finalized_(false),
2440 fixed_size_timecode_(fixed_size_timecode),
2441 header_written_(false),
2442 payload_size_(0),
2443 position_for_cues_(cues_pos),
2444 size_position_(-1),
2445 timecode_(timecode),
2446 timecode_scale_(timecode_scale),
2447 write_last_frame_with_duration_(write_last_frame_with_duration),
2448 writer_(NULL) {}
2449
~Cluster()2450 Cluster::~Cluster() {
2451 // Delete any stored frames that are left behind. This will happen if the
2452 // Cluster was not Finalized for whatever reason.
2453 while (!stored_frames_.empty()) {
2454 while (!stored_frames_.begin()->second.empty()) {
2455 delete stored_frames_.begin()->second.front();
2456 stored_frames_.begin()->second.pop_front();
2457 }
2458 stored_frames_.erase(stored_frames_.begin()->first);
2459 }
2460 }
2461
Init(IMkvWriter * ptr_writer)2462 bool Cluster::Init(IMkvWriter* ptr_writer) {
2463 if (!ptr_writer) {
2464 return false;
2465 }
2466 writer_ = ptr_writer;
2467 return true;
2468 }
2469
AddFrame(const Frame * const frame)2470 bool Cluster::AddFrame(const Frame* const frame) {
2471 return QueueOrWriteFrame(frame);
2472 }
2473
AddFrame(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t abs_timecode,bool is_key)2474 bool Cluster::AddFrame(const uint8_t* data, uint64_t length,
2475 uint64_t track_number, uint64_t abs_timecode,
2476 bool is_key) {
2477 Frame frame;
2478 if (!frame.Init(data, length))
2479 return false;
2480 frame.set_track_number(track_number);
2481 frame.set_timestamp(abs_timecode);
2482 frame.set_is_key(is_key);
2483 return QueueOrWriteFrame(&frame);
2484 }
2485
AddFrameWithAdditional(const uint8_t * data,uint64_t length,const uint8_t * additional,uint64_t additional_length,uint64_t add_id,uint64_t track_number,uint64_t abs_timecode,bool is_key)2486 bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
2487 const uint8_t* additional,
2488 uint64_t additional_length,
2489 uint64_t add_id, uint64_t track_number,
2490 uint64_t abs_timecode, bool is_key) {
2491 if (!additional || additional_length == 0) {
2492 return false;
2493 }
2494 Frame frame;
2495 if (!frame.Init(data, length) ||
2496 !frame.AddAdditionalData(additional, additional_length, add_id)) {
2497 return false;
2498 }
2499 frame.set_track_number(track_number);
2500 frame.set_timestamp(abs_timecode);
2501 frame.set_is_key(is_key);
2502 return QueueOrWriteFrame(&frame);
2503 }
2504
AddFrameWithDiscardPadding(const uint8_t * data,uint64_t length,int64_t discard_padding,uint64_t track_number,uint64_t abs_timecode,bool is_key)2505 bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
2506 int64_t discard_padding,
2507 uint64_t track_number,
2508 uint64_t abs_timecode, bool is_key) {
2509 Frame frame;
2510 if (!frame.Init(data, length))
2511 return false;
2512 frame.set_discard_padding(discard_padding);
2513 frame.set_track_number(track_number);
2514 frame.set_timestamp(abs_timecode);
2515 frame.set_is_key(is_key);
2516 return QueueOrWriteFrame(&frame);
2517 }
2518
AddMetadata(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t abs_timecode,uint64_t duration_timecode)2519 bool Cluster::AddMetadata(const uint8_t* data, uint64_t length,
2520 uint64_t track_number, uint64_t abs_timecode,
2521 uint64_t duration_timecode) {
2522 Frame frame;
2523 if (!frame.Init(data, length))
2524 return false;
2525 frame.set_track_number(track_number);
2526 frame.set_timestamp(abs_timecode);
2527 frame.set_duration(duration_timecode);
2528 frame.set_is_key(true); // All metadata blocks are keyframes.
2529 return QueueOrWriteFrame(&frame);
2530 }
2531
AddPayloadSize(uint64_t size)2532 void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; }
2533
Finalize()2534 bool Cluster::Finalize() {
2535 return !write_last_frame_with_duration_ && Finalize(false, 0);
2536 }
2537
Finalize(bool set_last_frame_duration,uint64_t duration)2538 bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) {
2539 if (!writer_ || finalized_)
2540 return false;
2541
2542 if (write_last_frame_with_duration_) {
2543 // Write out held back Frames. This essentially performs a k-way merge
2544 // across all tracks in the increasing order of timestamps.
2545 while (!stored_frames_.empty()) {
2546 Frame* frame = stored_frames_.begin()->second.front();
2547
2548 // Get the next frame to write (frame with least timestamp across all
2549 // tracks).
2550 for (FrameMapIterator frames_iterator = ++stored_frames_.begin();
2551 frames_iterator != stored_frames_.end(); ++frames_iterator) {
2552 if (frames_iterator->second.front()->timestamp() < frame->timestamp()) {
2553 frame = frames_iterator->second.front();
2554 }
2555 }
2556
2557 // Set the duration if it's the last frame for the track.
2558 if (set_last_frame_duration &&
2559 stored_frames_[frame->track_number()].size() == 1 &&
2560 !frame->duration_set()) {
2561 frame->set_duration(duration - frame->timestamp());
2562 if (!frame->is_key() && !frame->reference_block_timestamp_set()) {
2563 frame->set_reference_block_timestamp(
2564 last_block_timestamp_[frame->track_number()]);
2565 }
2566 }
2567
2568 // Write the frame and remove it from |stored_frames_|.
2569 const bool wrote_frame = DoWriteFrame(frame);
2570 stored_frames_[frame->track_number()].pop_front();
2571 if (stored_frames_[frame->track_number()].empty()) {
2572 stored_frames_.erase(frame->track_number());
2573 }
2574 delete frame;
2575 if (!wrote_frame)
2576 return false;
2577 }
2578 }
2579
2580 if (size_position_ == -1)
2581 return false;
2582
2583 if (writer_->Seekable()) {
2584 const int64_t pos = writer_->Position();
2585
2586 if (writer_->Position(size_position_))
2587 return false;
2588
2589 if (WriteUIntSize(writer_, payload_size(), 8))
2590 return false;
2591
2592 if (writer_->Position(pos))
2593 return false;
2594 }
2595
2596 finalized_ = true;
2597
2598 return true;
2599 }
2600
Size() const2601 uint64_t Cluster::Size() const {
2602 const uint64_t element_size =
2603 EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) +
2604 payload_size_;
2605 return element_size;
2606 }
2607
PreWriteBlock()2608 bool Cluster::PreWriteBlock() {
2609 if (finalized_)
2610 return false;
2611
2612 if (!header_written_) {
2613 if (!WriteClusterHeader())
2614 return false;
2615 }
2616
2617 return true;
2618 }
2619
PostWriteBlock(uint64_t element_size)2620 void Cluster::PostWriteBlock(uint64_t element_size) {
2621 AddPayloadSize(element_size);
2622 ++blocks_added_;
2623 }
2624
GetRelativeTimecode(int64_t abs_timecode) const2625 int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const {
2626 const int64_t cluster_timecode = this->Cluster::timecode();
2627 const int64_t rel_timecode =
2628 static_cast<int64_t>(abs_timecode) - cluster_timecode;
2629
2630 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
2631 return -1;
2632
2633 return rel_timecode;
2634 }
2635
DoWriteFrame(const Frame * const frame)2636 bool Cluster::DoWriteFrame(const Frame* const frame) {
2637 if (!frame || !frame->IsValid())
2638 return false;
2639
2640 if (!PreWriteBlock())
2641 return false;
2642
2643 const uint64_t element_size = WriteFrame(writer_, frame, this);
2644 if (element_size == 0)
2645 return false;
2646
2647 PostWriteBlock(element_size);
2648 last_block_timestamp_[frame->track_number()] = frame->timestamp();
2649 return true;
2650 }
2651
QueueOrWriteFrame(const Frame * const frame)2652 bool Cluster::QueueOrWriteFrame(const Frame* const frame) {
2653 if (!frame || !frame->IsValid())
2654 return false;
2655
2656 // If |write_last_frame_with_duration_| is not set, then write the frame right
2657 // away.
2658 if (!write_last_frame_with_duration_) {
2659 return DoWriteFrame(frame);
2660 }
2661
2662 // Queue the current frame.
2663 uint64_t track_number = frame->track_number();
2664 Frame* const frame_to_store = new Frame();
2665 frame_to_store->CopyFrom(*frame);
2666 stored_frames_[track_number].push_back(frame_to_store);
2667
2668 // Iterate through all queued frames in the current track except the last one
2669 // and write it if it is okay to do so (i.e.) no other track has an held back
2670 // frame with timestamp <= the timestamp of the frame in question.
2671 std::vector<std::list<Frame*>::iterator> frames_to_erase;
2672 for (std::list<Frame*>::iterator
2673 current_track_iterator = stored_frames_[track_number].begin(),
2674 end = --stored_frames_[track_number].end();
2675 current_track_iterator != end; ++current_track_iterator) {
2676 const Frame* const frame_to_write = *current_track_iterator;
2677 bool okay_to_write = true;
2678 for (FrameMapIterator track_iterator = stored_frames_.begin();
2679 track_iterator != stored_frames_.end(); ++track_iterator) {
2680 if (track_iterator->first == track_number) {
2681 continue;
2682 }
2683 if (track_iterator->second.front()->timestamp() <
2684 frame_to_write->timestamp()) {
2685 okay_to_write = false;
2686 break;
2687 }
2688 }
2689 if (okay_to_write) {
2690 const bool wrote_frame = DoWriteFrame(frame_to_write);
2691 delete frame_to_write;
2692 if (!wrote_frame)
2693 return false;
2694 frames_to_erase.push_back(current_track_iterator);
2695 } else {
2696 break;
2697 }
2698 }
2699 for (std::vector<std::list<Frame*>::iterator>::iterator iterator =
2700 frames_to_erase.begin();
2701 iterator != frames_to_erase.end(); ++iterator) {
2702 stored_frames_[track_number].erase(*iterator);
2703 }
2704 return true;
2705 }
2706
WriteClusterHeader()2707 bool Cluster::WriteClusterHeader() {
2708 if (finalized_)
2709 return false;
2710
2711 if (WriteID(writer_, libwebm::kMkvCluster))
2712 return false;
2713
2714 // Save for later.
2715 size_position_ = writer_->Position();
2716
2717 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
2718 // bytes because we do not know how big our cluster will be.
2719 if (SerializeInt(writer_, kEbmlUnknownValue, 8))
2720 return false;
2721
2722 if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode(),
2723 fixed_size_timecode_ ? 8 : 0)) {
2724 return false;
2725 }
2726 AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode(),
2727 fixed_size_timecode_ ? 8 : 0));
2728 header_written_ = true;
2729
2730 return true;
2731 }
2732
2733 ///////////////////////////////////////////////////////////////
2734 //
2735 // SeekHead Class
2736
SeekHead()2737 SeekHead::SeekHead() : start_pos_(0ULL) {
2738 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2739 seek_entry_id_[i] = 0;
2740 seek_entry_pos_[i] = 0;
2741 }
2742 }
2743
~SeekHead()2744 SeekHead::~SeekHead() {}
2745
Finalize(IMkvWriter * writer) const2746 bool SeekHead::Finalize(IMkvWriter* writer) const {
2747 if (writer->Seekable()) {
2748 if (start_pos_ == -1)
2749 return false;
2750
2751 uint64_t payload_size = 0;
2752 uint64_t entry_size[kSeekEntryCount];
2753
2754 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2755 if (seek_entry_id_[i] != 0) {
2756 entry_size[i] = EbmlElementSize(libwebm::kMkvSeekID,
2757 static_cast<uint64>(seek_entry_id_[i]));
2758 entry_size[i] += EbmlElementSize(
2759 libwebm::kMkvSeekPosition, static_cast<uint64>(seek_entry_pos_[i]));
2760
2761 payload_size +=
2762 EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) +
2763 entry_size[i];
2764 }
2765 }
2766
2767 // No SeekHead elements
2768 if (payload_size == 0)
2769 return true;
2770
2771 const int64_t pos = writer->Position();
2772 if (writer->Position(start_pos_))
2773 return false;
2774
2775 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size))
2776 return false;
2777
2778 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2779 if (seek_entry_id_[i] != 0) {
2780 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i]))
2781 return false;
2782
2783 if (!WriteEbmlElement(writer, libwebm::kMkvSeekID,
2784 static_cast<uint64>(seek_entry_id_[i])))
2785 return false;
2786
2787 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition,
2788 static_cast<uint64>(seek_entry_pos_[i])))
2789 return false;
2790 }
2791 }
2792
2793 const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize();
2794 const uint64_t total_size =
2795 EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) +
2796 total_entry_size;
2797 const int64_t size_left = total_size - (writer->Position() - start_pos_);
2798
2799 const uint64_t bytes_written = WriteVoidElement(writer, size_left);
2800 if (!bytes_written)
2801 return false;
2802
2803 if (writer->Position(pos))
2804 return false;
2805 }
2806
2807 return true;
2808 }
2809
Write(IMkvWriter * writer)2810 bool SeekHead::Write(IMkvWriter* writer) {
2811 const uint64_t entry_size = kSeekEntryCount * MaxEntrySize();
2812 const uint64_t size =
2813 EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size);
2814
2815 start_pos_ = writer->Position();
2816
2817 const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size);
2818 if (!bytes_written)
2819 return false;
2820
2821 return true;
2822 }
2823
AddSeekEntry(uint32_t id,uint64_t pos)2824 bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) {
2825 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2826 if (seek_entry_id_[i] == 0) {
2827 seek_entry_id_[i] = id;
2828 seek_entry_pos_[i] = pos;
2829 return true;
2830 }
2831 }
2832 return false;
2833 }
2834
GetId(int index) const2835 uint32_t SeekHead::GetId(int index) const {
2836 if (index < 0 || index >= kSeekEntryCount)
2837 return UINT_MAX;
2838 return seek_entry_id_[index];
2839 }
2840
GetPosition(int index) const2841 uint64_t SeekHead::GetPosition(int index) const {
2842 if (index < 0 || index >= kSeekEntryCount)
2843 return ULLONG_MAX;
2844 return seek_entry_pos_[index];
2845 }
2846
SetSeekEntry(int index,uint32_t id,uint64_t position)2847 bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) {
2848 if (index < 0 || index >= kSeekEntryCount)
2849 return false;
2850 seek_entry_id_[index] = id;
2851 seek_entry_pos_[index] = position;
2852 return true;
2853 }
2854
MaxEntrySize() const2855 uint64_t SeekHead::MaxEntrySize() const {
2856 const uint64_t max_entry_payload_size =
2857 EbmlElementSize(libwebm::kMkvSeekID,
2858 static_cast<uint64>(UINT64_C(0xffffffff))) +
2859 EbmlElementSize(libwebm::kMkvSeekPosition,
2860 static_cast<uint64>(UINT64_C(0xffffffffffffffff)));
2861 const uint64_t max_entry_size =
2862 EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) +
2863 max_entry_payload_size;
2864
2865 return max_entry_size;
2866 }
2867
2868 ///////////////////////////////////////////////////////////////
2869 //
2870 // SegmentInfo Class
2871
SegmentInfo()2872 SegmentInfo::SegmentInfo()
2873 : duration_(-1.0),
2874 muxing_app_(NULL),
2875 timecode_scale_(1000000ULL),
2876 writing_app_(NULL),
2877 date_utc_(LLONG_MIN),
2878 duration_pos_(-1) {}
2879
~SegmentInfo()2880 SegmentInfo::~SegmentInfo() {
2881 delete[] muxing_app_;
2882 delete[] writing_app_;
2883 }
2884
Init()2885 bool SegmentInfo::Init() {
2886 int32_t major;
2887 int32_t minor;
2888 int32_t build;
2889 int32_t revision;
2890 GetVersion(&major, &minor, &build, &revision);
2891 char temp[256];
2892 #ifdef _MSC_VER
2893 sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2894 minor, build, revision);
2895 #else
2896 snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2897 minor, build, revision);
2898 #endif
2899
2900 const size_t app_len = strlen(temp) + 1;
2901
2902 delete[] muxing_app_;
2903
2904 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
2905 if (!muxing_app_)
2906 return false;
2907
2908 #ifdef _MSC_VER
2909 strcpy_s(muxing_app_, app_len, temp);
2910 #else
2911 strcpy(muxing_app_, temp);
2912 #endif
2913
2914 set_writing_app(temp);
2915 if (!writing_app_)
2916 return false;
2917 return true;
2918 }
2919
Finalize(IMkvWriter * writer) const2920 bool SegmentInfo::Finalize(IMkvWriter* writer) const {
2921 if (!writer)
2922 return false;
2923
2924 if (duration_ > 0.0) {
2925 if (writer->Seekable()) {
2926 if (duration_pos_ == -1)
2927 return false;
2928
2929 const int64_t pos = writer->Position();
2930
2931 if (writer->Position(duration_pos_))
2932 return false;
2933
2934 if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2935 static_cast<float>(duration_)))
2936 return false;
2937
2938 if (writer->Position(pos))
2939 return false;
2940 }
2941 }
2942
2943 return true;
2944 }
2945
Write(IMkvWriter * writer)2946 bool SegmentInfo::Write(IMkvWriter* writer) {
2947 if (!writer || !muxing_app_ || !writing_app_)
2948 return false;
2949
2950 uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale,
2951 static_cast<uint64>(timecode_scale_));
2952 if (duration_ > 0.0)
2953 size +=
2954 EbmlElementSize(libwebm::kMkvDuration, static_cast<float>(duration_));
2955 if (date_utc_ != LLONG_MIN)
2956 size += EbmlDateElementSize(libwebm::kMkvDateUTC);
2957 size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_);
2958 size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_);
2959
2960 if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size))
2961 return false;
2962
2963 const int64_t payload_position = writer->Position();
2964 if (payload_position < 0)
2965 return false;
2966
2967 if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale,
2968 static_cast<uint64>(timecode_scale_)))
2969 return false;
2970
2971 if (duration_ > 0.0) {
2972 // Save for later
2973 duration_pos_ = writer->Position();
2974
2975 if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2976 static_cast<float>(duration_)))
2977 return false;
2978 }
2979
2980 if (date_utc_ != LLONG_MIN)
2981 WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_);
2982
2983 if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_))
2984 return false;
2985 if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_))
2986 return false;
2987
2988 const int64_t stop_position = writer->Position();
2989 if (stop_position < 0 ||
2990 stop_position - payload_position != static_cast<int64_t>(size))
2991 return false;
2992
2993 return true;
2994 }
2995
set_muxing_app(const char * app)2996 void SegmentInfo::set_muxing_app(const char* app) {
2997 if (app) {
2998 const size_t length = strlen(app) + 1;
2999 char* temp_str = new (std::nothrow) char[length]; // NOLINT
3000 if (!temp_str)
3001 return;
3002
3003 #ifdef _MSC_VER
3004 strcpy_s(temp_str, length, app);
3005 #else
3006 strcpy(temp_str, app);
3007 #endif
3008
3009 delete[] muxing_app_;
3010 muxing_app_ = temp_str;
3011 }
3012 }
3013
set_writing_app(const char * app)3014 void SegmentInfo::set_writing_app(const char* app) {
3015 if (app) {
3016 const size_t length = strlen(app) + 1;
3017 char* temp_str = new (std::nothrow) char[length]; // NOLINT
3018 if (!temp_str)
3019 return;
3020
3021 #ifdef _MSC_VER
3022 strcpy_s(temp_str, length, app);
3023 #else
3024 strcpy(temp_str, app);
3025 #endif
3026
3027 delete[] writing_app_;
3028 writing_app_ = temp_str;
3029 }
3030 }
3031
3032 ///////////////////////////////////////////////////////////////
3033 //
3034 // Segment Class
3035
Segment()3036 Segment::Segment()
3037 : chunk_count_(0),
3038 chunk_name_(NULL),
3039 chunk_writer_cluster_(NULL),
3040 chunk_writer_cues_(NULL),
3041 chunk_writer_header_(NULL),
3042 chunking_(false),
3043 chunking_base_name_(NULL),
3044 cluster_list_(NULL),
3045 cluster_list_capacity_(0),
3046 cluster_list_size_(0),
3047 cues_position_(kAfterClusters),
3048 cues_track_(0),
3049 force_new_cluster_(false),
3050 frames_(NULL),
3051 frames_capacity_(0),
3052 frames_size_(0),
3053 has_video_(false),
3054 header_written_(false),
3055 last_block_duration_(0),
3056 last_timestamp_(0),
3057 max_cluster_duration_(kDefaultMaxClusterDuration),
3058 max_cluster_size_(0),
3059 mode_(kFile),
3060 new_cuepoint_(false),
3061 output_cues_(true),
3062 accurate_cluster_duration_(false),
3063 fixed_size_cluster_timecode_(false),
3064 estimate_file_duration_(false),
3065 payload_pos_(0),
3066 size_position_(0),
3067 doc_type_version_(kDefaultDocTypeVersion),
3068 doc_type_version_written_(0),
3069 duration_(0.0),
3070 writer_cluster_(NULL),
3071 writer_cues_(NULL),
3072 writer_header_(NULL) {
3073 const time_t curr_time = time(NULL);
3074 seed_ = static_cast<unsigned int>(curr_time);
3075 #ifdef _WIN32
3076 srand(seed_);
3077 #endif
3078 }
3079
~Segment()3080 Segment::~Segment() {
3081 if (cluster_list_) {
3082 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3083 Cluster* const cluster = cluster_list_[i];
3084 delete cluster;
3085 }
3086 delete[] cluster_list_;
3087 }
3088
3089 if (frames_) {
3090 for (int32_t i = 0; i < frames_size_; ++i) {
3091 Frame* const frame = frames_[i];
3092 delete frame;
3093 }
3094 delete[] frames_;
3095 }
3096
3097 delete[] chunk_name_;
3098 delete[] chunking_base_name_;
3099
3100 if (chunk_writer_cluster_) {
3101 chunk_writer_cluster_->Close();
3102 delete chunk_writer_cluster_;
3103 }
3104 if (chunk_writer_cues_) {
3105 chunk_writer_cues_->Close();
3106 delete chunk_writer_cues_;
3107 }
3108 if (chunk_writer_header_) {
3109 chunk_writer_header_->Close();
3110 delete chunk_writer_header_;
3111 }
3112 }
3113
MoveCuesBeforeClustersHelper(uint64_t diff,int32_t index,uint64_t * cues_size)3114 void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index,
3115 uint64_t* cues_size) {
3116 CuePoint* const cue_point = cues_.GetCueByIndex(index);
3117 if (cue_point == NULL)
3118 return;
3119 const uint64_t old_cue_point_size = cue_point->Size();
3120 const uint64_t cluster_pos = cue_point->cluster_pos() + diff;
3121 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
3122 // New size of the cue is computed as follows
3123 // Let a = current sum of size of all CuePoints
3124 // Let b = Increase in Cue Point's size due to this iteration
3125 // Let c = Increase in size of Cues Element's length due to this iteration
3126 // (This is computed as CodedSize(a + b) - CodedSize(a))
3127 // Let d = b + c. Now d is the |diff| passed to the next recursive call.
3128 // Let e = a + b. Now e is the |cues_size| passed to the next recursive
3129 // call.
3130 const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size;
3131 const uint64_t cue_size_diff =
3132 GetCodedUIntSize(*cues_size + cue_point_size_diff) -
3133 GetCodedUIntSize(*cues_size);
3134 *cues_size += cue_point_size_diff;
3135 diff = cue_size_diff + cue_point_size_diff;
3136 if (diff > 0) {
3137 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) {
3138 MoveCuesBeforeClustersHelper(diff, i, cues_size);
3139 }
3140 }
3141 }
3142
MoveCuesBeforeClusters()3143 void Segment::MoveCuesBeforeClusters() {
3144 const uint64_t current_cue_size = cues_.Size();
3145 uint64_t cue_size = 0;
3146 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
3147 cue_size += cues_.GetCueByIndex(i)->Size();
3148 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
3149 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
3150
3151 // Adjust the Seek Entry to reflect the change in position
3152 // of Cluster and Cues
3153 int32_t cluster_index = 0;
3154 int32_t cues_index = 0;
3155 for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) {
3156 if (seek_head_.GetId(i) == libwebm::kMkvCluster)
3157 cluster_index = i;
3158 if (seek_head_.GetId(i) == libwebm::kMkvCues)
3159 cues_index = i;
3160 }
3161 seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues,
3162 seek_head_.GetPosition(cluster_index));
3163 seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster,
3164 cues_.Size() + seek_head_.GetPosition(cues_index));
3165 }
3166
Init(IMkvWriter * ptr_writer)3167 bool Segment::Init(IMkvWriter* ptr_writer) {
3168 if (!ptr_writer) {
3169 return false;
3170 }
3171 writer_cluster_ = ptr_writer;
3172 writer_cues_ = ptr_writer;
3173 writer_header_ = ptr_writer;
3174 memset(&track_frames_written_, 0,
3175 sizeof(track_frames_written_[0]) * kMaxTrackNumber);
3176 memset(&last_track_timestamp_, 0,
3177 sizeof(last_track_timestamp_[0]) * kMaxTrackNumber);
3178 return segment_info_.Init();
3179 }
3180
CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader * reader,IMkvWriter * writer)3181 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
3182 IMkvWriter* writer) {
3183 if (!writer->Seekable() || chunking_)
3184 return false;
3185 const int64_t cluster_offset =
3186 cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster);
3187
3188 // Copy the headers.
3189 if (!ChunkedCopy(reader, writer, 0, cluster_offset))
3190 return false;
3191
3192 // Recompute cue positions and seek entries.
3193 MoveCuesBeforeClusters();
3194
3195 // Write cues and seek entries.
3196 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
3197 // second time with a different writer object. But the name Finalize() doesn't
3198 // indicate something we want to call more than once. So consider renaming it
3199 // to write() or some such.
3200 if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
3201 return false;
3202
3203 // Copy the Clusters.
3204 if (!ChunkedCopy(reader, writer, cluster_offset,
3205 cluster_end_offset_ - cluster_offset))
3206 return false;
3207
3208 // Update the Segment size in case the Cues size has changed.
3209 const int64_t pos = writer->Position();
3210 const int64_t segment_size = writer->Position() - payload_pos_;
3211 if (writer->Position(size_position_) ||
3212 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
3213 return false;
3214 return true;
3215 }
3216
Finalize()3217 bool Segment::Finalize() {
3218 if (WriteFramesAll() < 0)
3219 return false;
3220
3221 // In kLive mode, call Cluster::Finalize only if |accurate_cluster_duration_|
3222 // is set. In all other modes, always call Cluster::Finalize.
3223 if ((mode_ == kLive ? accurate_cluster_duration_ : true) &&
3224 cluster_list_size_ > 0) {
3225 // Update last cluster's size
3226 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
3227
3228 // For the last frame of the last Cluster, we don't write it as a BlockGroup
3229 // with Duration unless the frame itself has duration set explicitly.
3230 if (!old_cluster || !old_cluster->Finalize(false, 0))
3231 return false;
3232 }
3233
3234 if (mode_ == kFile) {
3235 if (chunking_ && chunk_writer_cluster_) {
3236 chunk_writer_cluster_->Close();
3237 chunk_count_++;
3238 }
3239
3240 double duration =
3241 (static_cast<double>(last_timestamp_) + last_block_duration_) /
3242 segment_info_.timecode_scale();
3243 if (duration_ > 0.0) {
3244 duration = duration_;
3245 } else {
3246 if (last_block_duration_ == 0 && estimate_file_duration_) {
3247 const int num_tracks = static_cast<int>(tracks_.track_entries_size());
3248 for (int i = 0; i < num_tracks; ++i) {
3249 if (track_frames_written_[i] < 2)
3250 continue;
3251
3252 // Estimate the duration for the last block of a Track.
3253 const double nano_per_frame =
3254 static_cast<double>(last_track_timestamp_[i]) /
3255 (track_frames_written_[i] - 1);
3256 const double track_duration =
3257 (last_track_timestamp_[i] + nano_per_frame) /
3258 segment_info_.timecode_scale();
3259 if (track_duration > duration)
3260 duration = track_duration;
3261 }
3262 }
3263 }
3264 segment_info_.set_duration(duration);
3265 if (!segment_info_.Finalize(writer_header_))
3266 return false;
3267
3268 if (output_cues_)
3269 if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset()))
3270 return false;
3271
3272 if (chunking_) {
3273 if (!chunk_writer_cues_)
3274 return false;
3275
3276 char* name = NULL;
3277 if (!UpdateChunkName("cues", &name))
3278 return false;
3279
3280 const bool cues_open = chunk_writer_cues_->Open(name);
3281 delete[] name;
3282 if (!cues_open)
3283 return false;
3284 }
3285
3286 cluster_end_offset_ = writer_cluster_->Position();
3287
3288 // Write the seek headers and cues
3289 if (output_cues_)
3290 if (!cues_.Write(writer_cues_))
3291 return false;
3292
3293 if (!seek_head_.Finalize(writer_header_))
3294 return false;
3295
3296 if (writer_header_->Seekable()) {
3297 if (size_position_ == -1)
3298 return false;
3299
3300 const int64_t segment_size = MaxOffset();
3301 if (segment_size < 1)
3302 return false;
3303
3304 const int64_t pos = writer_header_->Position();
3305 UpdateDocTypeVersion();
3306 if (doc_type_version_ != doc_type_version_written_) {
3307 if (writer_header_->Position(0))
3308 return false;
3309
3310 const char* const doc_type =
3311 DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska;
3312 if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type))
3313 return false;
3314 if (writer_header_->Position() != ebml_header_size_)
3315 return false;
3316
3317 doc_type_version_written_ = doc_type_version_;
3318 }
3319
3320 if (writer_header_->Position(size_position_))
3321 return false;
3322
3323 if (WriteUIntSize(writer_header_, segment_size, 8))
3324 return false;
3325
3326 if (writer_header_->Position(pos))
3327 return false;
3328 }
3329
3330 if (chunking_) {
3331 // Do not close any writers until the segment size has been written,
3332 // otherwise the size may be off.
3333 if (!chunk_writer_cues_ || !chunk_writer_header_)
3334 return false;
3335
3336 chunk_writer_cues_->Close();
3337 chunk_writer_header_->Close();
3338 }
3339 }
3340
3341 return true;
3342 }
3343
AddTrack(int32_t number)3344 Track* Segment::AddTrack(int32_t number) {
3345 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
3346
3347 if (!track)
3348 return NULL;
3349
3350 if (!tracks_.AddTrack(track, number)) {
3351 delete track;
3352 return NULL;
3353 }
3354
3355 return track;
3356 }
3357
AddChapter()3358 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
3359
AddTag()3360 Tag* Segment::AddTag() { return tags_.AddTag(); }
3361
AddVideoTrack(int32_t width,int32_t height,int32_t number)3362 uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) {
3363 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
3364 if (!track)
3365 return 0;
3366
3367 track->set_type(Tracks::kVideo);
3368 track->set_codec_id(Tracks::kVp8CodecId);
3369 track->set_width(width);
3370 track->set_height(height);
3371
3372 if (!tracks_.AddTrack(track, number)) {
3373 delete track;
3374 return 0;
3375 }
3376 has_video_ = true;
3377
3378 return track->number();
3379 }
3380
AddCuePoint(uint64_t timestamp,uint64_t track)3381 bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) {
3382 if (cluster_list_size_ < 1)
3383 return false;
3384
3385 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3386 if (!cluster)
3387 return false;
3388
3389 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
3390 if (!cue)
3391 return false;
3392
3393 cue->set_time(timestamp / segment_info_.timecode_scale());
3394 cue->set_block_number(cluster->blocks_added());
3395 cue->set_cluster_pos(cluster->position_for_cues());
3396 cue->set_track(track);
3397 if (!cues_.AddCue(cue)) {
3398 delete cue;
3399 return false;
3400 }
3401
3402 new_cuepoint_ = false;
3403 return true;
3404 }
3405
AddAudioTrack(int32_t sample_rate,int32_t channels,int32_t number)3406 uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels,
3407 int32_t number) {
3408 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
3409 if (!track)
3410 return 0;
3411
3412 track->set_type(Tracks::kAudio);
3413 track->set_codec_id(Tracks::kVorbisCodecId);
3414 track->set_sample_rate(sample_rate);
3415 track->set_channels(channels);
3416
3417 if (!tracks_.AddTrack(track, number)) {
3418 delete track;
3419 return 0;
3420 }
3421
3422 return track->number();
3423 }
3424
AddFrame(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t timestamp,bool is_key)3425 bool Segment::AddFrame(const uint8_t* data, uint64_t length,
3426 uint64_t track_number, uint64_t timestamp, bool is_key) {
3427 if (!data)
3428 return false;
3429
3430 Frame frame;
3431 if (!frame.Init(data, length))
3432 return false;
3433 frame.set_track_number(track_number);
3434 frame.set_timestamp(timestamp);
3435 frame.set_is_key(is_key);
3436 return AddGenericFrame(&frame);
3437 }
3438
AddFrameWithAdditional(const uint8_t * data,uint64_t length,const uint8_t * additional,uint64_t additional_length,uint64_t add_id,uint64_t track_number,uint64_t timestamp,bool is_key)3439 bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
3440 const uint8_t* additional,
3441 uint64_t additional_length,
3442 uint64_t add_id, uint64_t track_number,
3443 uint64_t timestamp, bool is_key) {
3444 if (!data || !additional)
3445 return false;
3446
3447 Frame frame;
3448 if (!frame.Init(data, length) ||
3449 !frame.AddAdditionalData(additional, additional_length, add_id)) {
3450 return false;
3451 }
3452 frame.set_track_number(track_number);
3453 frame.set_timestamp(timestamp);
3454 frame.set_is_key(is_key);
3455 return AddGenericFrame(&frame);
3456 }
3457
AddFrameWithDiscardPadding(const uint8_t * data,uint64_t length,int64_t discard_padding,uint64_t track_number,uint64_t timestamp,bool is_key)3458 bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
3459 int64_t discard_padding,
3460 uint64_t track_number,
3461 uint64_t timestamp, bool is_key) {
3462 if (!data)
3463 return false;
3464
3465 Frame frame;
3466 if (!frame.Init(data, length))
3467 return false;
3468 frame.set_discard_padding(discard_padding);
3469 frame.set_track_number(track_number);
3470 frame.set_timestamp(timestamp);
3471 frame.set_is_key(is_key);
3472 return AddGenericFrame(&frame);
3473 }
3474
AddMetadata(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t timestamp_ns,uint64_t duration_ns)3475 bool Segment::AddMetadata(const uint8_t* data, uint64_t length,
3476 uint64_t track_number, uint64_t timestamp_ns,
3477 uint64_t duration_ns) {
3478 if (!data)
3479 return false;
3480
3481 Frame frame;
3482 if (!frame.Init(data, length))
3483 return false;
3484 frame.set_track_number(track_number);
3485 frame.set_timestamp(timestamp_ns);
3486 frame.set_duration(duration_ns);
3487 frame.set_is_key(true); // All metadata blocks are keyframes.
3488 return AddGenericFrame(&frame);
3489 }
3490
AddGenericFrame(const Frame * frame)3491 bool Segment::AddGenericFrame(const Frame* frame) {
3492 if (!frame)
3493 return false;
3494
3495 if (!CheckHeaderInfo())
3496 return false;
3497
3498 // Check for non-monotonically increasing timestamps.
3499 if (frame->timestamp() < last_timestamp_)
3500 return false;
3501
3502 // Check if the track number is valid.
3503 if (!tracks_.GetTrackByNumber(frame->track_number()))
3504 return false;
3505
3506 if (frame->discard_padding() != 0)
3507 doc_type_version_ = 4;
3508
3509 if (cluster_list_size_ > 0) {
3510 const uint64_t timecode_scale = segment_info_.timecode_scale();
3511 const uint64_t frame_timecode = frame->timestamp() / timecode_scale;
3512
3513 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
3514 const uint64_t last_cluster_timecode = last_cluster->timecode();
3515
3516 const uint64_t rel_timecode = frame_timecode - last_cluster_timecode;
3517 if (rel_timecode > kMaxBlockTimecode) {
3518 force_new_cluster_ = true;
3519 }
3520 }
3521
3522 // If the segment has a video track hold onto audio frames to make sure the
3523 // audio that is associated with the start time of a video key-frame is
3524 // muxed into the same cluster.
3525 if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
3526 !force_new_cluster_) {
3527 Frame* const new_frame = new (std::nothrow) Frame();
3528 if (!new_frame || !new_frame->CopyFrom(*frame)) {
3529 delete new_frame;
3530 return false;
3531 }
3532 if (!QueueFrame(new_frame)) {
3533 delete new_frame;
3534 return false;
3535 }
3536 track_frames_written_[frame->track_number() - 1]++;
3537 return true;
3538 }
3539
3540 if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
3541 frame->is_key())) {
3542 return false;
3543 }
3544
3545 if (cluster_list_size_ < 1)
3546 return false;
3547
3548 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3549 if (!cluster)
3550 return false;
3551
3552 // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
3553 // if it is not set already.
3554 bool frame_created = false;
3555 if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
3556 !frame->reference_block_timestamp_set()) {
3557 Frame* const new_frame = new (std::nothrow) Frame();
3558 if (!new_frame || !new_frame->CopyFrom(*frame)) {
3559 delete new_frame;
3560 return false;
3561 }
3562 new_frame->set_reference_block_timestamp(
3563 last_track_timestamp_[frame->track_number() - 1]);
3564 frame = new_frame;
3565 frame_created = true;
3566 }
3567
3568 if (!cluster->AddFrame(frame))
3569 return false;
3570
3571 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3572 if (!AddCuePoint(frame->timestamp(), cues_track_))
3573 return false;
3574 }
3575
3576 last_timestamp_ = frame->timestamp();
3577 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3578 last_block_duration_ = frame->duration();
3579 track_frames_written_[frame->track_number() - 1]++;
3580
3581 if (frame_created)
3582 delete frame;
3583 return true;
3584 }
3585
OutputCues(bool output_cues)3586 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
3587
AccurateClusterDuration(bool accurate_cluster_duration)3588 void Segment::AccurateClusterDuration(bool accurate_cluster_duration) {
3589 accurate_cluster_duration_ = accurate_cluster_duration;
3590 }
3591
UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode)3592 void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) {
3593 fixed_size_cluster_timecode_ = fixed_size_cluster_timecode;
3594 }
3595
SetChunking(bool chunking,const char * filename)3596 bool Segment::SetChunking(bool chunking, const char* filename) {
3597 if (chunk_count_ > 0)
3598 return false;
3599
3600 if (chunking) {
3601 if (!filename)
3602 return false;
3603
3604 // Check if we are being set to what is already set.
3605 if (chunking_ && !strcmp(filename, chunking_base_name_))
3606 return true;
3607
3608 const size_t name_length = strlen(filename) + 1;
3609 char* const temp = new (std::nothrow) char[name_length]; // NOLINT
3610 if (!temp)
3611 return false;
3612
3613 #ifdef _MSC_VER
3614 strcpy_s(temp, name_length, filename);
3615 #else
3616 strcpy(temp, filename);
3617 #endif
3618
3619 delete[] chunking_base_name_;
3620 chunking_base_name_ = temp;
3621
3622 if (!UpdateChunkName("chk", &chunk_name_))
3623 return false;
3624
3625 if (!chunk_writer_cluster_) {
3626 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
3627 if (!chunk_writer_cluster_)
3628 return false;
3629 }
3630
3631 if (!chunk_writer_cues_) {
3632 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
3633 if (!chunk_writer_cues_)
3634 return false;
3635 }
3636
3637 if (!chunk_writer_header_) {
3638 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
3639 if (!chunk_writer_header_)
3640 return false;
3641 }
3642
3643 if (!chunk_writer_cluster_->Open(chunk_name_))
3644 return false;
3645
3646 const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
3647 char* const header = new (std::nothrow) char[header_length]; // NOLINT
3648 if (!header)
3649 return false;
3650
3651 #ifdef _MSC_VER
3652 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
3653 strcat_s(header, header_length, ".hdr");
3654 #else
3655 strcpy(header, chunking_base_name_);
3656 strcat(header, ".hdr");
3657 #endif
3658 if (!chunk_writer_header_->Open(header)) {
3659 delete[] header;
3660 return false;
3661 }
3662
3663 writer_cluster_ = chunk_writer_cluster_;
3664 writer_cues_ = chunk_writer_cues_;
3665 writer_header_ = chunk_writer_header_;
3666
3667 delete[] header;
3668 }
3669
3670 chunking_ = chunking;
3671
3672 return true;
3673 }
3674
CuesTrack(uint64_t track_number)3675 bool Segment::CuesTrack(uint64_t track_number) {
3676 const Track* const track = GetTrackByNumber(track_number);
3677 if (!track)
3678 return false;
3679
3680 cues_track_ = track_number;
3681 return true;
3682 }
3683
ForceNewClusterOnNextFrame()3684 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
3685
GetTrackByNumber(uint64_t track_number) const3686 Track* Segment::GetTrackByNumber(uint64_t track_number) const {
3687 return tracks_.GetTrackByNumber(track_number);
3688 }
3689
WriteSegmentHeader()3690 bool Segment::WriteSegmentHeader() {
3691 UpdateDocTypeVersion();
3692
3693 const char* const doc_type =
3694 DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska;
3695 if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type))
3696 return false;
3697 doc_type_version_written_ = doc_type_version_;
3698 ebml_header_size_ = static_cast<int32_t>(writer_header_->Position());
3699
3700 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
3701 // will write over duration when the file is finalized.
3702 if (WriteID(writer_header_, libwebm::kMkvSegment))
3703 return false;
3704
3705 // Save for later.
3706 size_position_ = writer_header_->Position();
3707
3708 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
3709 // bytes because if we are going to overwrite the segment size later we do
3710 // not know how big our segment will be.
3711 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
3712 return false;
3713
3714 payload_pos_ = writer_header_->Position();
3715
3716 if (mode_ == kFile && writer_header_->Seekable()) {
3717 // Set the duration > 0.0 so SegmentInfo will write out the duration. When
3718 // the muxer is done writing we will set the correct duration and have
3719 // SegmentInfo upadte it.
3720 segment_info_.set_duration(1.0);
3721
3722 if (!seek_head_.Write(writer_header_))
3723 return false;
3724 }
3725
3726 if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset()))
3727 return false;
3728 if (!segment_info_.Write(writer_header_))
3729 return false;
3730
3731 if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset()))
3732 return false;
3733 if (!tracks_.Write(writer_header_))
3734 return false;
3735
3736 if (chapters_.Count() > 0) {
3737 if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset()))
3738 return false;
3739 if (!chapters_.Write(writer_header_))
3740 return false;
3741 }
3742
3743 if (tags_.Count() > 0) {
3744 if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset()))
3745 return false;
3746 if (!tags_.Write(writer_header_))
3747 return false;
3748 }
3749
3750 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
3751 if (!chunk_writer_header_)
3752 return false;
3753
3754 chunk_writer_header_->Close();
3755 }
3756
3757 header_written_ = true;
3758
3759 return true;
3760 }
3761
3762 // Here we are testing whether to create a new cluster, given a frame
3763 // having time frame_timestamp_ns.
3764 //
TestFrame(uint64_t track_number,uint64_t frame_timestamp_ns,bool is_key) const3765 int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns,
3766 bool is_key) const {
3767 if (force_new_cluster_)
3768 return 1;
3769
3770 // If no clusters have been created yet, then create a new cluster
3771 // and write this frame immediately, in the new cluster. This path
3772 // should only be followed once, the first time we attempt to write
3773 // a frame.
3774
3775 if (cluster_list_size_ <= 0)
3776 return 1;
3777
3778 // There exists at least one cluster. We must compare the frame to
3779 // the last cluster, in order to determine whether the frame is
3780 // written to the existing cluster, or that a new cluster should be
3781 // created.
3782
3783 const uint64_t timecode_scale = segment_info_.timecode_scale();
3784 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3785
3786 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
3787 const uint64_t last_cluster_timecode = last_cluster->timecode();
3788
3789 // For completeness we test for the case when the frame's timecode
3790 // is less than the cluster's timecode. Although in principle that
3791 // is allowed, this muxer doesn't actually write clusters like that,
3792 // so this indicates a bug somewhere in our algorithm.
3793
3794 if (frame_timecode < last_cluster_timecode) // should never happen
3795 return -1;
3796
3797 // If the frame has a timestamp significantly larger than the last
3798 // cluster (in Matroska, cluster-relative timestamps are serialized
3799 // using a 16-bit signed integer), then we cannot write this frame
3800 // to that cluster, and so we must create a new cluster.
3801
3802 const int64_t delta_timecode = frame_timecode - last_cluster_timecode;
3803
3804 if (delta_timecode > kMaxBlockTimecode)
3805 return 2;
3806
3807 // We decide to create a new cluster when we have a video keyframe.
3808 // This will flush queued (audio) frames, and write the keyframe
3809 // immediately, in the newly-created cluster.
3810
3811 if (is_key && tracks_.TrackIsVideo(track_number))
3812 return 1;
3813
3814 // Create a new cluster if we have accumulated too many frames
3815 // already, where "too many" is defined as "the total time of frames
3816 // in the cluster exceeds a threshold".
3817
3818 const uint64_t delta_ns = delta_timecode * timecode_scale;
3819
3820 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
3821 return 1;
3822
3823 // This is similar to the case above, with the difference that a new
3824 // cluster is created when the size of the current cluster exceeds a
3825 // threshold.
3826
3827 const uint64_t cluster_size = last_cluster->payload_size();
3828
3829 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
3830 return 1;
3831
3832 // There's no need to create a new cluster, so emit this frame now.
3833
3834 return 0;
3835 }
3836
MakeNewCluster(uint64_t frame_timestamp_ns)3837 bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) {
3838 const int32_t new_size = cluster_list_size_ + 1;
3839
3840 if (new_size > cluster_list_capacity_) {
3841 // Add more clusters.
3842 const int32_t new_capacity =
3843 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
3844 Cluster** const clusters =
3845 new (std::nothrow) Cluster*[new_capacity]; // NOLINT
3846 if (!clusters)
3847 return false;
3848
3849 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3850 clusters[i] = cluster_list_[i];
3851 }
3852
3853 delete[] cluster_list_;
3854
3855 cluster_list_ = clusters;
3856 cluster_list_capacity_ = new_capacity;
3857 }
3858
3859 if (!WriteFramesLessThan(frame_timestamp_ns))
3860 return false;
3861
3862 if (cluster_list_size_ > 0) {
3863 // Update old cluster's size
3864 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
3865
3866 if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns))
3867 return false;
3868 }
3869
3870 if (output_cues_)
3871 new_cuepoint_ = true;
3872
3873 if (chunking_ && cluster_list_size_ > 0) {
3874 chunk_writer_cluster_->Close();
3875 chunk_count_++;
3876
3877 if (!UpdateChunkName("chk", &chunk_name_))
3878 return false;
3879 if (!chunk_writer_cluster_->Open(chunk_name_))
3880 return false;
3881 }
3882
3883 const uint64_t timecode_scale = segment_info_.timecode_scale();
3884 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3885
3886 uint64_t cluster_timecode = frame_timecode;
3887
3888 if (frames_size_ > 0) {
3889 const Frame* const f = frames_[0]; // earliest queued frame
3890 const uint64_t ns = f->timestamp();
3891 const uint64_t tc = ns / timecode_scale;
3892
3893 if (tc < cluster_timecode)
3894 cluster_timecode = tc;
3895 }
3896
3897 Cluster*& cluster = cluster_list_[cluster_list_size_];
3898 const int64_t offset = MaxOffset();
3899 cluster = new (std::nothrow)
3900 Cluster(cluster_timecode, offset, segment_info_.timecode_scale(),
3901 accurate_cluster_duration_, fixed_size_cluster_timecode_);
3902 if (!cluster)
3903 return false;
3904
3905 if (!cluster->Init(writer_cluster_))
3906 return false;
3907
3908 cluster_list_size_ = new_size;
3909 return true;
3910 }
3911
DoNewClusterProcessing(uint64_t track_number,uint64_t frame_timestamp_ns,bool is_key)3912 bool Segment::DoNewClusterProcessing(uint64_t track_number,
3913 uint64_t frame_timestamp_ns, bool is_key) {
3914 for (;;) {
3915 // Based on the characteristics of the current frame and current
3916 // cluster, decide whether to create a new cluster.
3917 const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
3918 if (result < 0) // error
3919 return false;
3920
3921 // Always set force_new_cluster_ to false after TestFrame.
3922 force_new_cluster_ = false;
3923
3924 // A non-zero result means create a new cluster.
3925 if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
3926 return false;
3927
3928 // Write queued (audio) frames.
3929 const int frame_count = WriteFramesAll();
3930 if (frame_count < 0) // error
3931 return false;
3932
3933 // Write the current frame to the current cluster (if TestFrame
3934 // returns 0) or to a newly created cluster (TestFrame returns 1).
3935 if (result <= 1)
3936 return true;
3937
3938 // TestFrame returned 2, which means there was a large time
3939 // difference between the cluster and the frame itself. Do the
3940 // test again, comparing the frame to the new cluster.
3941 }
3942 }
3943
CheckHeaderInfo()3944 bool Segment::CheckHeaderInfo() {
3945 if (!header_written_) {
3946 if (!WriteSegmentHeader())
3947 return false;
3948
3949 if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset()))
3950 return false;
3951
3952 if (output_cues_ && cues_track_ == 0) {
3953 // Check for a video track
3954 for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) {
3955 const Track* const track = tracks_.GetTrackByIndex(i);
3956 if (!track)
3957 return false;
3958
3959 if (tracks_.TrackIsVideo(track->number())) {
3960 cues_track_ = track->number();
3961 break;
3962 }
3963 }
3964
3965 // Set first track found
3966 if (cues_track_ == 0) {
3967 const Track* const track = tracks_.GetTrackByIndex(0);
3968 if (!track)
3969 return false;
3970
3971 cues_track_ = track->number();
3972 }
3973 }
3974 }
3975 return true;
3976 }
3977
UpdateDocTypeVersion()3978 void Segment::UpdateDocTypeVersion() {
3979 for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) {
3980 const Track* track = tracks_.GetTrackByIndex(index);
3981 if (track == NULL)
3982 break;
3983 if ((track->codec_delay() || track->seek_pre_roll()) &&
3984 doc_type_version_ < 4) {
3985 doc_type_version_ = 4;
3986 break;
3987 }
3988 }
3989 }
3990
UpdateChunkName(const char * ext,char ** name) const3991 bool Segment::UpdateChunkName(const char* ext, char** name) const {
3992 if (!name || !ext)
3993 return false;
3994
3995 char ext_chk[64];
3996 #ifdef _MSC_VER
3997 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3998 #else
3999 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
4000 #endif
4001
4002 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
4003 char* const str = new (std::nothrow) char[length]; // NOLINT
4004 if (!str)
4005 return false;
4006
4007 #ifdef _MSC_VER
4008 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
4009 strcat_s(str, length, ext_chk);
4010 #else
4011 strcpy(str, chunking_base_name_);
4012 strcat(str, ext_chk);
4013 #endif
4014
4015 delete[] * name;
4016 *name = str;
4017
4018 return true;
4019 }
4020
MaxOffset()4021 int64_t Segment::MaxOffset() {
4022 if (!writer_header_)
4023 return -1;
4024
4025 int64_t offset = writer_header_->Position() - payload_pos_;
4026
4027 if (chunking_) {
4028 for (int32_t i = 0; i < cluster_list_size_; ++i) {
4029 Cluster* const cluster = cluster_list_[i];
4030 offset += cluster->Size();
4031 }
4032
4033 if (writer_cues_)
4034 offset += writer_cues_->Position();
4035 }
4036
4037 return offset;
4038 }
4039
QueueFrame(Frame * frame)4040 bool Segment::QueueFrame(Frame* frame) {
4041 const int32_t new_size = frames_size_ + 1;
4042
4043 if (new_size > frames_capacity_) {
4044 // Add more frames.
4045 const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
4046
4047 if (new_capacity < 1)
4048 return false;
4049
4050 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
4051 if (!frames)
4052 return false;
4053
4054 for (int32_t i = 0; i < frames_size_; ++i) {
4055 frames[i] = frames_[i];
4056 }
4057
4058 delete[] frames_;
4059 frames_ = frames;
4060 frames_capacity_ = new_capacity;
4061 }
4062
4063 frames_[frames_size_++] = frame;
4064
4065 return true;
4066 }
4067
WriteFramesAll()4068 int Segment::WriteFramesAll() {
4069 if (frames_ == NULL)
4070 return 0;
4071
4072 if (cluster_list_size_ < 1)
4073 return -1;
4074
4075 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
4076
4077 if (!cluster)
4078 return -1;
4079
4080 for (int32_t i = 0; i < frames_size_; ++i) {
4081 Frame*& frame = frames_[i];
4082 // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
4083 // places where |doc_type_version_| needs to be updated.
4084 if (frame->discard_padding() != 0)
4085 doc_type_version_ = 4;
4086 if (!cluster->AddFrame(frame))
4087 return -1;
4088
4089 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
4090 if (!AddCuePoint(frame->timestamp(), cues_track_))
4091 return -1;
4092 }
4093
4094 if (frame->timestamp() > last_timestamp_) {
4095 last_timestamp_ = frame->timestamp();
4096 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
4097 }
4098
4099 delete frame;
4100 frame = NULL;
4101 }
4102
4103 const int result = frames_size_;
4104 frames_size_ = 0;
4105
4106 return result;
4107 }
4108
WriteFramesLessThan(uint64_t timestamp)4109 bool Segment::WriteFramesLessThan(uint64_t timestamp) {
4110 // Check |cluster_list_size_| to see if this is the first cluster. If it is
4111 // the first cluster the audio frames that are less than the first video
4112 // timesatmp will be written in a later step.
4113 if (frames_size_ > 0 && cluster_list_size_ > 0) {
4114 if (!frames_)
4115 return false;
4116
4117 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
4118 if (!cluster)
4119 return false;
4120
4121 int32_t shift_left = 0;
4122
4123 // TODO(fgalligan): Change this to use the durations of frames instead of
4124 // the next frame's start time if the duration is accurate.
4125 for (int32_t i = 1; i < frames_size_; ++i) {
4126 const Frame* const frame_curr = frames_[i];
4127
4128 if (frame_curr->timestamp() > timestamp)
4129 break;
4130
4131 const Frame* const frame_prev = frames_[i - 1];
4132 if (frame_prev->discard_padding() != 0)
4133 doc_type_version_ = 4;
4134 if (!cluster->AddFrame(frame_prev))
4135 return false;
4136
4137 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
4138 if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
4139 return false;
4140 }
4141
4142 ++shift_left;
4143 if (frame_prev->timestamp() > last_timestamp_) {
4144 last_timestamp_ = frame_prev->timestamp();
4145 last_track_timestamp_[frame_prev->track_number() - 1] =
4146 frame_prev->timestamp();
4147 }
4148
4149 delete frame_prev;
4150 }
4151
4152 if (shift_left > 0) {
4153 if (shift_left >= frames_size_)
4154 return false;
4155
4156 const int32_t new_frames_size = frames_size_ - shift_left;
4157 for (int32_t i = 0; i < new_frames_size; ++i) {
4158 frames_[i] = frames_[i + shift_left];
4159 }
4160
4161 frames_size_ = new_frames_size;
4162 }
4163 }
4164
4165 return true;
4166 }
4167
DocTypeIsWebm() const4168 bool Segment::DocTypeIsWebm() const {
4169 const int kNumCodecIds = 10;
4170
4171 // TODO(vigneshv): Tweak .clang-format.
4172 const char* kWebmCodecIds[kNumCodecIds] = {
4173 Tracks::kOpusCodecId, Tracks::kVorbisCodecId,
4174 Tracks::kVp8CodecId, Tracks::kVp9CodecId,
4175 Tracks::kVp10CodecId, Tracks::kAV1CodecId,
4176 Tracks::kWebVttCaptionsId, Tracks::kWebVttDescriptionsId,
4177 Tracks::kWebVttMetadataId, Tracks::kWebVttSubtitlesId};
4178
4179 const int num_tracks = static_cast<int>(tracks_.track_entries_size());
4180 for (int track_index = 0; track_index < num_tracks; ++track_index) {
4181 const Track* const track = tracks_.GetTrackByIndex(track_index);
4182 const std::string codec_id = track->codec_id();
4183
4184 bool id_is_webm = false;
4185 for (int id_index = 0; id_index < kNumCodecIds; ++id_index) {
4186 if (codec_id == kWebmCodecIds[id_index]) {
4187 id_is_webm = true;
4188 break;
4189 }
4190 }
4191
4192 if (!id_is_webm)
4193 return false;
4194 }
4195
4196 return true;
4197 }
4198
4199 } // namespace mkvmuxer
4200