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