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