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