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