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