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