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