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