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