]> granicus.if.org Git - libvpx/blob - third_party/libwebm/mkvmuxer/mkvmuxer.cc
configure: Disable webm_io when target is VS 7/8/9.
[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, bool fixed_size_timecode)
2116     : blocks_added_(0),
2117       finalized_(false),
2118       fixed_size_timecode_(fixed_size_timecode),
2119       header_written_(false),
2120       payload_size_(0),
2121       position_for_cues_(cues_pos),
2122       size_position_(-1),
2123       timecode_(timecode),
2124       timecode_scale_(timecode_scale),
2125       write_last_frame_with_duration_(write_last_frame_with_duration),
2126       writer_(NULL) {}
2127
2128 Cluster::~Cluster() {}
2129
2130 bool Cluster::Init(IMkvWriter* ptr_writer) {
2131   if (!ptr_writer) {
2132     return false;
2133   }
2134   writer_ = ptr_writer;
2135   return true;
2136 }
2137
2138 bool Cluster::AddFrame(const Frame* const frame) {
2139   return QueueOrWriteFrame(frame);
2140 }
2141
2142 bool Cluster::AddFrame(const uint8_t* data, uint64_t length,
2143                        uint64_t track_number, uint64_t abs_timecode,
2144                        bool is_key) {
2145   Frame frame;
2146   if (!frame.Init(data, length))
2147     return false;
2148   frame.set_track_number(track_number);
2149   frame.set_timestamp(abs_timecode);
2150   frame.set_is_key(is_key);
2151   return QueueOrWriteFrame(&frame);
2152 }
2153
2154 bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
2155                                      const uint8_t* additional,
2156                                      uint64_t additional_length,
2157                                      uint64_t add_id, uint64_t track_number,
2158                                      uint64_t abs_timecode, bool is_key) {
2159   if (!additional || additional_length == 0) {
2160     return false;
2161   }
2162   Frame frame;
2163   if (!frame.Init(data, length) ||
2164       !frame.AddAdditionalData(additional, additional_length, add_id)) {
2165     return false;
2166   }
2167   frame.set_track_number(track_number);
2168   frame.set_timestamp(abs_timecode);
2169   frame.set_is_key(is_key);
2170   return QueueOrWriteFrame(&frame);
2171 }
2172
2173 bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
2174                                          int64_t discard_padding,
2175                                          uint64_t track_number,
2176                                          uint64_t abs_timecode, bool is_key) {
2177   Frame frame;
2178   if (!frame.Init(data, length))
2179     return false;
2180   frame.set_discard_padding(discard_padding);
2181   frame.set_track_number(track_number);
2182   frame.set_timestamp(abs_timecode);
2183   frame.set_is_key(is_key);
2184   return QueueOrWriteFrame(&frame);
2185 }
2186
2187 bool Cluster::AddMetadata(const uint8_t* data, uint64_t length,
2188                           uint64_t track_number, uint64_t abs_timecode,
2189                           uint64_t duration_timecode) {
2190   Frame frame;
2191   if (!frame.Init(data, length))
2192     return false;
2193   frame.set_track_number(track_number);
2194   frame.set_timestamp(abs_timecode);
2195   frame.set_duration(duration_timecode);
2196   frame.set_is_key(true);  // All metadata blocks are keyframes.
2197   return QueueOrWriteFrame(&frame);
2198 }
2199
2200 void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; }
2201
2202 bool Cluster::Finalize() {
2203   return !write_last_frame_with_duration_ && Finalize(false, 0);
2204 }
2205
2206 bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) {
2207   if (!writer_ || finalized_)
2208     return false;
2209
2210   if (write_last_frame_with_duration_) {
2211     // Write out held back Frames. This essentially performs a k-way merge
2212     // across all tracks in the increasing order of timestamps.
2213     while (!stored_frames_.empty()) {
2214       Frame* frame = stored_frames_.begin()->second.front();
2215
2216       // Get the next frame to write (frame with least timestamp across all
2217       // tracks).
2218       for (FrameMapIterator frames_iterator = ++stored_frames_.begin();
2219            frames_iterator != stored_frames_.end(); ++frames_iterator) {
2220         if (frames_iterator->second.front()->timestamp() < frame->timestamp()) {
2221           frame = frames_iterator->second.front();
2222         }
2223       }
2224
2225       // Set the duration if it's the last frame for the track.
2226       if (set_last_frame_duration &&
2227           stored_frames_[frame->track_number()].size() == 1 &&
2228           !frame->duration_set()) {
2229         frame->set_duration(duration - frame->timestamp());
2230         if (!frame->is_key() && !frame->reference_block_timestamp_set()) {
2231           frame->set_reference_block_timestamp(
2232               last_block_timestamp_[frame->track_number()]);
2233         }
2234       }
2235
2236       // Write the frame and remove it from |stored_frames_|.
2237       const bool wrote_frame = DoWriteFrame(frame);
2238       stored_frames_[frame->track_number()].pop_front();
2239       if (stored_frames_[frame->track_number()].empty()) {
2240         stored_frames_.erase(frame->track_number());
2241       }
2242       delete frame;
2243       if (!wrote_frame)
2244         return false;
2245     }
2246   }
2247
2248   if (size_position_ == -1)
2249     return false;
2250
2251   if (writer_->Seekable()) {
2252     const int64_t pos = writer_->Position();
2253
2254     if (writer_->Position(size_position_))
2255       return false;
2256
2257     if (WriteUIntSize(writer_, payload_size(), 8))
2258       return false;
2259
2260     if (writer_->Position(pos))
2261       return false;
2262   }
2263
2264   finalized_ = true;
2265
2266   return true;
2267 }
2268
2269 uint64_t Cluster::Size() const {
2270   const uint64_t element_size =
2271       EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) +
2272       payload_size_;
2273   return element_size;
2274 }
2275
2276 bool Cluster::PreWriteBlock() {
2277   if (finalized_)
2278     return false;
2279
2280   if (!header_written_) {
2281     if (!WriteClusterHeader())
2282       return false;
2283   }
2284
2285   return true;
2286 }
2287
2288 void Cluster::PostWriteBlock(uint64_t element_size) {
2289   AddPayloadSize(element_size);
2290   ++blocks_added_;
2291 }
2292
2293 int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const {
2294   const int64_t cluster_timecode = this->Cluster::timecode();
2295   const int64_t rel_timecode =
2296       static_cast<int64_t>(abs_timecode) - cluster_timecode;
2297
2298   if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
2299     return -1;
2300
2301   return rel_timecode;
2302 }
2303
2304 bool Cluster::DoWriteFrame(const Frame* const frame) {
2305   if (!frame || !frame->IsValid())
2306     return false;
2307
2308   if (!PreWriteBlock())
2309     return false;
2310
2311   const uint64_t element_size = WriteFrame(writer_, frame, this);
2312   if (element_size == 0)
2313     return false;
2314
2315   PostWriteBlock(element_size);
2316   last_block_timestamp_[frame->track_number()] = frame->timestamp();
2317   return true;
2318 }
2319
2320 bool Cluster::QueueOrWriteFrame(const Frame* const frame) {
2321   if (!frame || !frame->IsValid())
2322     return false;
2323
2324   // If |write_last_frame_with_duration_| is not set, then write the frame right
2325   // away.
2326   if (!write_last_frame_with_duration_) {
2327     return DoWriteFrame(frame);
2328   }
2329
2330   // Queue the current frame.
2331   uint64_t track_number = frame->track_number();
2332   Frame* const frame_to_store = new Frame();
2333   frame_to_store->CopyFrom(*frame);
2334   stored_frames_[track_number].push_back(frame_to_store);
2335
2336   // Iterate through all queued frames in the current track except the last one
2337   // and write it if it is okay to do so (i.e.) no other track has an held back
2338   // frame with timestamp <= the timestamp of the frame in question.
2339   std::vector<std::list<Frame*>::iterator> frames_to_erase;
2340   for (std::list<Frame *>::iterator
2341            current_track_iterator = stored_frames_[track_number].begin(),
2342            end = --stored_frames_[track_number].end();
2343        current_track_iterator != end; ++current_track_iterator) {
2344     const Frame* const frame_to_write = *current_track_iterator;
2345     bool okay_to_write = true;
2346     for (FrameMapIterator track_iterator = stored_frames_.begin();
2347          track_iterator != stored_frames_.end(); ++track_iterator) {
2348       if (track_iterator->first == track_number) {
2349         continue;
2350       }
2351       if (track_iterator->second.front()->timestamp() <
2352           frame_to_write->timestamp()) {
2353         okay_to_write = false;
2354         break;
2355       }
2356     }
2357     if (okay_to_write) {
2358       const bool wrote_frame = DoWriteFrame(frame_to_write);
2359       delete frame_to_write;
2360       if (!wrote_frame)
2361         return false;
2362       frames_to_erase.push_back(current_track_iterator);
2363     } else {
2364       break;
2365     }
2366   }
2367   for (std::vector<std::list<Frame*>::iterator>::iterator iterator =
2368            frames_to_erase.begin();
2369        iterator != frames_to_erase.end(); ++iterator) {
2370     stored_frames_[track_number].erase(*iterator);
2371   }
2372   return true;
2373 }
2374
2375 bool Cluster::WriteClusterHeader() {
2376   if (finalized_)
2377     return false;
2378
2379   if (WriteID(writer_, libwebm::kMkvCluster))
2380     return false;
2381
2382   // Save for later.
2383   size_position_ = writer_->Position();
2384
2385   // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
2386   // bytes because we do not know how big our cluster will be.
2387   if (SerializeInt(writer_, kEbmlUnknownValue, 8))
2388     return false;
2389
2390   if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode(),
2391                         fixed_size_timecode_ ? 8 : 0)) {
2392     return false;
2393   }
2394   AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode(),
2395                                  fixed_size_timecode_ ? 8 : 0));
2396   header_written_ = true;
2397
2398   return true;
2399 }
2400
2401 ///////////////////////////////////////////////////////////////
2402 //
2403 // SeekHead Class
2404
2405 SeekHead::SeekHead() : start_pos_(0ULL) {
2406   for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2407     seek_entry_id_[i] = 0;
2408     seek_entry_pos_[i] = 0;
2409   }
2410 }
2411
2412 SeekHead::~SeekHead() {}
2413
2414 bool SeekHead::Finalize(IMkvWriter* writer) const {
2415   if (writer->Seekable()) {
2416     if (start_pos_ == -1)
2417       return false;
2418
2419     uint64_t payload_size = 0;
2420     uint64_t entry_size[kSeekEntryCount];
2421
2422     for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2423       if (seek_entry_id_[i] != 0) {
2424         entry_size[i] = EbmlElementSize(
2425             libwebm::kMkvSeekID, static_cast<uint64_t>(seek_entry_id_[i]));
2426         entry_size[i] +=
2427             EbmlElementSize(libwebm::kMkvSeekPosition, seek_entry_pos_[i]);
2428
2429         payload_size +=
2430             EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) +
2431             entry_size[i];
2432       }
2433     }
2434
2435     // No SeekHead elements
2436     if (payload_size == 0)
2437       return true;
2438
2439     const int64_t pos = writer->Position();
2440     if (writer->Position(start_pos_))
2441       return false;
2442
2443     if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size))
2444       return false;
2445
2446     for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2447       if (seek_entry_id_[i] != 0) {
2448         if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i]))
2449           return false;
2450
2451         if (!WriteEbmlElement(writer, libwebm::kMkvSeekID,
2452                               static_cast<uint64_t>(seek_entry_id_[i])))
2453           return false;
2454
2455         if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition,
2456                               seek_entry_pos_[i]))
2457           return false;
2458       }
2459     }
2460
2461     const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize();
2462     const uint64_t total_size =
2463         EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) +
2464         total_entry_size;
2465     const int64_t size_left = total_size - (writer->Position() - start_pos_);
2466
2467     const uint64_t bytes_written = WriteVoidElement(writer, size_left);
2468     if (!bytes_written)
2469       return false;
2470
2471     if (writer->Position(pos))
2472       return false;
2473   }
2474
2475   return true;
2476 }
2477
2478 bool SeekHead::Write(IMkvWriter* writer) {
2479   const uint64_t entry_size = kSeekEntryCount * MaxEntrySize();
2480   const uint64_t size =
2481       EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size);
2482
2483   start_pos_ = writer->Position();
2484
2485   const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size);
2486   if (!bytes_written)
2487     return false;
2488
2489   return true;
2490 }
2491
2492 bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) {
2493   for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2494     if (seek_entry_id_[i] == 0) {
2495       seek_entry_id_[i] = id;
2496       seek_entry_pos_[i] = pos;
2497       return true;
2498     }
2499   }
2500   return false;
2501 }
2502
2503 uint32_t SeekHead::GetId(int index) const {
2504   if (index < 0 || index >= kSeekEntryCount)
2505     return UINT_MAX;
2506   return seek_entry_id_[index];
2507 }
2508
2509 uint64_t SeekHead::GetPosition(int index) const {
2510   if (index < 0 || index >= kSeekEntryCount)
2511     return ULLONG_MAX;
2512   return seek_entry_pos_[index];
2513 }
2514
2515 bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) {
2516   if (index < 0 || index >= kSeekEntryCount)
2517     return false;
2518   seek_entry_id_[index] = id;
2519   seek_entry_pos_[index] = position;
2520   return true;
2521 }
2522
2523 uint64_t SeekHead::MaxEntrySize() const {
2524   const uint64_t max_entry_payload_size =
2525       EbmlElementSize(libwebm::kMkvSeekID, UINT64_C(0xffffffff)) +
2526       EbmlElementSize(libwebm::kMkvSeekPosition, UINT64_C(0xffffffffffffffff));
2527   const uint64_t max_entry_size =
2528       EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) +
2529       max_entry_payload_size;
2530
2531   return max_entry_size;
2532 }
2533
2534 ///////////////////////////////////////////////////////////////
2535 //
2536 // SegmentInfo Class
2537
2538 SegmentInfo::SegmentInfo()
2539     : duration_(-1.0),
2540       muxing_app_(NULL),
2541       timecode_scale_(1000000ULL),
2542       writing_app_(NULL),
2543       date_utc_(LLONG_MIN),
2544       duration_pos_(-1) {}
2545
2546 SegmentInfo::~SegmentInfo() {
2547   delete[] muxing_app_;
2548   delete[] writing_app_;
2549 }
2550
2551 bool SegmentInfo::Init() {
2552   int32_t major;
2553   int32_t minor;
2554   int32_t build;
2555   int32_t revision;
2556   GetVersion(&major, &minor, &build, &revision);
2557   char temp[256];
2558 #ifdef _MSC_VER
2559   sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2560             minor, build, revision);
2561 #else
2562   snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2563            minor, build, revision);
2564 #endif
2565
2566   const size_t app_len = strlen(temp) + 1;
2567
2568   delete[] muxing_app_;
2569
2570   muxing_app_ = new (std::nothrow) char[app_len];  // NOLINT
2571   if (!muxing_app_)
2572     return false;
2573
2574 #ifdef _MSC_VER
2575   strcpy_s(muxing_app_, app_len, temp);
2576 #else
2577   strcpy(muxing_app_, temp);
2578 #endif
2579
2580   set_writing_app(temp);
2581   if (!writing_app_)
2582     return false;
2583   return true;
2584 }
2585
2586 bool SegmentInfo::Finalize(IMkvWriter* writer) const {
2587   if (!writer)
2588     return false;
2589
2590   if (duration_ > 0.0) {
2591     if (writer->Seekable()) {
2592       if (duration_pos_ == -1)
2593         return false;
2594
2595       const int64_t pos = writer->Position();
2596
2597       if (writer->Position(duration_pos_))
2598         return false;
2599
2600       if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2601                             static_cast<float>(duration_)))
2602         return false;
2603
2604       if (writer->Position(pos))
2605         return false;
2606     }
2607   }
2608
2609   return true;
2610 }
2611
2612 bool SegmentInfo::Write(IMkvWriter* writer) {
2613   if (!writer || !muxing_app_ || !writing_app_)
2614     return false;
2615
2616   uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale, timecode_scale_);
2617   if (duration_ > 0.0)
2618     size +=
2619         EbmlElementSize(libwebm::kMkvDuration, static_cast<float>(duration_));
2620   if (date_utc_ != LLONG_MIN)
2621     size += EbmlDateElementSize(libwebm::kMkvDateUTC);
2622   size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_);
2623   size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_);
2624
2625   if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size))
2626     return false;
2627
2628   const int64_t payload_position = writer->Position();
2629   if (payload_position < 0)
2630     return false;
2631
2632   if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale, timecode_scale_))
2633     return false;
2634
2635   if (duration_ > 0.0) {
2636     // Save for later
2637     duration_pos_ = writer->Position();
2638
2639     if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2640                           static_cast<float>(duration_)))
2641       return false;
2642   }
2643
2644   if (date_utc_ != LLONG_MIN)
2645     WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_);
2646
2647   if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_))
2648     return false;
2649   if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_))
2650     return false;
2651
2652   const int64_t stop_position = writer->Position();
2653   if (stop_position < 0 ||
2654       stop_position - payload_position != static_cast<int64_t>(size))
2655     return false;
2656
2657   return true;
2658 }
2659
2660 void SegmentInfo::set_muxing_app(const char* app) {
2661   if (app) {
2662     const size_t length = strlen(app) + 1;
2663     char* temp_str = new (std::nothrow) char[length];  // NOLINT
2664     if (!temp_str)
2665       return;
2666
2667 #ifdef _MSC_VER
2668     strcpy_s(temp_str, length, app);
2669 #else
2670     strcpy(temp_str, app);
2671 #endif
2672
2673     delete[] muxing_app_;
2674     muxing_app_ = temp_str;
2675   }
2676 }
2677
2678 void SegmentInfo::set_writing_app(const char* app) {
2679   if (app) {
2680     const size_t length = strlen(app) + 1;
2681     char* temp_str = new (std::nothrow) char[length];  // NOLINT
2682     if (!temp_str)
2683       return;
2684
2685 #ifdef _MSC_VER
2686     strcpy_s(temp_str, length, app);
2687 #else
2688     strcpy(temp_str, app);
2689 #endif
2690
2691     delete[] writing_app_;
2692     writing_app_ = temp_str;
2693   }
2694 }
2695
2696 ///////////////////////////////////////////////////////////////
2697 //
2698 // Segment Class
2699
2700 Segment::Segment()
2701     : chunk_count_(0),
2702       chunk_name_(NULL),
2703       chunk_writer_cluster_(NULL),
2704       chunk_writer_cues_(NULL),
2705       chunk_writer_header_(NULL),
2706       chunking_(false),
2707       chunking_base_name_(NULL),
2708       cluster_list_(NULL),
2709       cluster_list_capacity_(0),
2710       cluster_list_size_(0),
2711       cues_position_(kAfterClusters),
2712       cues_track_(0),
2713       force_new_cluster_(false),
2714       frames_(NULL),
2715       frames_capacity_(0),
2716       frames_size_(0),
2717       has_video_(false),
2718       header_written_(false),
2719       last_block_duration_(0),
2720       last_timestamp_(0),
2721       max_cluster_duration_(kDefaultMaxClusterDuration),
2722       max_cluster_size_(0),
2723       mode_(kFile),
2724       new_cuepoint_(false),
2725       output_cues_(true),
2726       accurate_cluster_duration_(false),
2727       fixed_size_cluster_timecode_(false),
2728       payload_pos_(0),
2729       size_position_(0),
2730       doc_type_version_(kDefaultDocTypeVersion),
2731       doc_type_version_written_(0),
2732       writer_cluster_(NULL),
2733       writer_cues_(NULL),
2734       writer_header_(NULL) {
2735   const time_t curr_time = time(NULL);
2736   seed_ = static_cast<unsigned int>(curr_time);
2737 #ifdef _WIN32
2738   srand(seed_);
2739 #endif
2740 }
2741
2742 Segment::~Segment() {
2743   if (cluster_list_) {
2744     for (int32_t i = 0; i < cluster_list_size_; ++i) {
2745       Cluster* const cluster = cluster_list_[i];
2746       delete cluster;
2747     }
2748     delete[] cluster_list_;
2749   }
2750
2751   if (frames_) {
2752     for (int32_t i = 0; i < frames_size_; ++i) {
2753       Frame* const frame = frames_[i];
2754       delete frame;
2755     }
2756     delete[] frames_;
2757   }
2758
2759   delete[] chunk_name_;
2760   delete[] chunking_base_name_;
2761
2762   if (chunk_writer_cluster_) {
2763     chunk_writer_cluster_->Close();
2764     delete chunk_writer_cluster_;
2765   }
2766   if (chunk_writer_cues_) {
2767     chunk_writer_cues_->Close();
2768     delete chunk_writer_cues_;
2769   }
2770   if (chunk_writer_header_) {
2771     chunk_writer_header_->Close();
2772     delete chunk_writer_header_;
2773   }
2774 }
2775
2776 void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index,
2777                                            uint64_t* cues_size) {
2778   CuePoint* const cue_point = cues_.GetCueByIndex(index);
2779   if (cue_point == NULL)
2780     return;
2781   const uint64_t old_cue_point_size = cue_point->Size();
2782   const uint64_t cluster_pos = cue_point->cluster_pos() + diff;
2783   cue_point->set_cluster_pos(cluster_pos);  // update the new cluster position
2784   // New size of the cue is computed as follows
2785   //    Let a = current sum of size of all CuePoints
2786   //    Let b = Increase in Cue Point's size due to this iteration
2787   //    Let c = Increase in size of Cues Element's length due to this iteration
2788   //            (This is computed as CodedSize(a + b) - CodedSize(a))
2789   //    Let d = b + c. Now d is the |diff| passed to the next recursive call.
2790   //    Let e = a + b. Now e is the |cues_size| passed to the next recursive
2791   //                   call.
2792   const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size;
2793   const uint64_t cue_size_diff =
2794       GetCodedUIntSize(*cues_size + cue_point_size_diff) -
2795       GetCodedUIntSize(*cues_size);
2796   *cues_size += cue_point_size_diff;
2797   diff = cue_size_diff + cue_point_size_diff;
2798   if (diff > 0) {
2799     for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) {
2800       MoveCuesBeforeClustersHelper(diff, i, cues_size);
2801     }
2802   }
2803 }
2804
2805 void Segment::MoveCuesBeforeClusters() {
2806   const uint64_t current_cue_size = cues_.Size();
2807   uint64_t cue_size = 0;
2808   for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2809     cue_size += cues_.GetCueByIndex(i)->Size();
2810   for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2811     MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
2812
2813   // Adjust the Seek Entry to reflect the change in position
2814   // of Cluster and Cues
2815   int32_t cluster_index = 0;
2816   int32_t cues_index = 0;
2817   for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) {
2818     if (seek_head_.GetId(i) == libwebm::kMkvCluster)
2819       cluster_index = i;
2820     if (seek_head_.GetId(i) == libwebm::kMkvCues)
2821       cues_index = i;
2822   }
2823   seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues,
2824                           seek_head_.GetPosition(cluster_index));
2825   seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster,
2826                           cues_.Size() + seek_head_.GetPosition(cues_index));
2827 }
2828
2829 bool Segment::Init(IMkvWriter* ptr_writer) {
2830   if (!ptr_writer) {
2831     return false;
2832   }
2833   writer_cluster_ = ptr_writer;
2834   writer_cues_ = ptr_writer;
2835   writer_header_ = ptr_writer;
2836   return segment_info_.Init();
2837 }
2838
2839 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
2840                                             IMkvWriter* writer) {
2841   if (!writer->Seekable() || chunking_)
2842     return false;
2843   const int64_t cluster_offset =
2844       cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster);
2845
2846   // Copy the headers.
2847   if (!ChunkedCopy(reader, writer, 0, cluster_offset))
2848     return false;
2849
2850   // Recompute cue positions and seek entries.
2851   MoveCuesBeforeClusters();
2852
2853   // Write cues and seek entries.
2854   // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
2855   // second time with a different writer object. But the name Finalize() doesn't
2856   // indicate something we want to call more than once. So consider renaming it
2857   // to write() or some such.
2858   if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
2859     return false;
2860
2861   // Copy the Clusters.
2862   if (!ChunkedCopy(reader, writer, cluster_offset,
2863                    cluster_end_offset_ - cluster_offset))
2864     return false;
2865
2866   // Update the Segment size in case the Cues size has changed.
2867   const int64_t pos = writer->Position();
2868   const int64_t segment_size = writer->Position() - payload_pos_;
2869   if (writer->Position(size_position_) ||
2870       WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
2871     return false;
2872   return true;
2873 }
2874
2875 bool Segment::Finalize() {
2876   if (WriteFramesAll() < 0)
2877     return false;
2878
2879   if (cluster_list_size_ > 0) {
2880     // Update last cluster's size
2881     Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2882
2883     // For the last frame of the last Cluster, we don't write it as a BlockGroup
2884     // with Duration unless the frame itself has duration set explicitly.
2885     if (!old_cluster || !old_cluster->Finalize(false, 0))
2886       return false;
2887   }
2888
2889   if (mode_ == kFile) {
2890     if (chunking_ && chunk_writer_cluster_) {
2891       chunk_writer_cluster_->Close();
2892       chunk_count_++;
2893     }
2894
2895     const double duration =
2896         (static_cast<double>(last_timestamp_) + last_block_duration_) /
2897         segment_info_.timecode_scale();
2898     segment_info_.set_duration(duration);
2899     if (!segment_info_.Finalize(writer_header_))
2900       return false;
2901
2902     if (output_cues_)
2903       if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset()))
2904         return false;
2905
2906     if (chunking_) {
2907       if (!chunk_writer_cues_)
2908         return false;
2909
2910       char* name = NULL;
2911       if (!UpdateChunkName("cues", &name))
2912         return false;
2913
2914       const bool cues_open = chunk_writer_cues_->Open(name);
2915       delete[] name;
2916       if (!cues_open)
2917         return false;
2918     }
2919
2920     cluster_end_offset_ = writer_cluster_->Position();
2921
2922     // Write the seek headers and cues
2923     if (output_cues_)
2924       if (!cues_.Write(writer_cues_))
2925         return false;
2926
2927     if (!seek_head_.Finalize(writer_header_))
2928       return false;
2929
2930     if (writer_header_->Seekable()) {
2931       if (size_position_ == -1)
2932         return false;
2933
2934       const int64_t segment_size = MaxOffset();
2935       if (segment_size < 1)
2936         return false;
2937
2938       const int64_t pos = writer_header_->Position();
2939       UpdateDocTypeVersion();
2940       if (doc_type_version_ != doc_type_version_written_) {
2941         if (writer_header_->Position(0))
2942           return false;
2943
2944         if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2945           return false;
2946         if (writer_header_->Position() != ebml_header_size_)
2947           return false;
2948
2949         doc_type_version_written_ = doc_type_version_;
2950       }
2951
2952       if (writer_header_->Position(size_position_))
2953         return false;
2954
2955       if (WriteUIntSize(writer_header_, segment_size, 8))
2956         return false;
2957
2958       if (writer_header_->Position(pos))
2959         return false;
2960     }
2961
2962     if (chunking_) {
2963       // Do not close any writers until the segment size has been written,
2964       // otherwise the size may be off.
2965       if (!chunk_writer_cues_ || !chunk_writer_header_)
2966         return false;
2967
2968       chunk_writer_cues_->Close();
2969       chunk_writer_header_->Close();
2970     }
2971   }
2972
2973   return true;
2974 }
2975
2976 Track* Segment::AddTrack(int32_t number) {
2977   Track* const track = new (std::nothrow) Track(&seed_);  // NOLINT
2978
2979   if (!track)
2980     return NULL;
2981
2982   if (!tracks_.AddTrack(track, number)) {
2983     delete track;
2984     return NULL;
2985   }
2986
2987   return track;
2988 }
2989
2990 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
2991
2992 Tag* Segment::AddTag() { return tags_.AddTag(); }
2993
2994 uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) {
2995   VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_);  // NOLINT
2996   if (!track)
2997     return 0;
2998
2999   track->set_type(Tracks::kVideo);
3000   track->set_codec_id(Tracks::kVp8CodecId);
3001   track->set_width(width);
3002   track->set_height(height);
3003
3004   tracks_.AddTrack(track, number);
3005   has_video_ = true;
3006
3007   return track->number();
3008 }
3009
3010 bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) {
3011   if (cluster_list_size_ < 1)
3012     return false;
3013
3014   const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3015   if (!cluster)
3016     return false;
3017
3018   CuePoint* const cue = new (std::nothrow) CuePoint();  // NOLINT
3019   if (!cue)
3020     return false;
3021
3022   cue->set_time(timestamp / segment_info_.timecode_scale());
3023   cue->set_block_number(cluster->blocks_added());
3024   cue->set_cluster_pos(cluster->position_for_cues());
3025   cue->set_track(track);
3026   if (!cues_.AddCue(cue))
3027     return false;
3028
3029   new_cuepoint_ = false;
3030   return true;
3031 }
3032
3033 uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels,
3034                                 int32_t number) {
3035   AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_);  // NOLINT
3036   if (!track)
3037     return 0;
3038
3039   track->set_type(Tracks::kAudio);
3040   track->set_codec_id(Tracks::kVorbisCodecId);
3041   track->set_sample_rate(sample_rate);
3042   track->set_channels(channels);
3043
3044   tracks_.AddTrack(track, number);
3045
3046   return track->number();
3047 }
3048
3049 bool Segment::AddFrame(const uint8_t* data, uint64_t length,
3050                        uint64_t track_number, uint64_t timestamp, bool is_key) {
3051   if (!data)
3052     return false;
3053
3054   Frame frame;
3055   if (!frame.Init(data, length))
3056     return false;
3057   frame.set_track_number(track_number);
3058   frame.set_timestamp(timestamp);
3059   frame.set_is_key(is_key);
3060   return AddGenericFrame(&frame);
3061 }
3062
3063 bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
3064                                      const uint8_t* additional,
3065                                      uint64_t additional_length,
3066                                      uint64_t add_id, uint64_t track_number,
3067                                      uint64_t timestamp, bool is_key) {
3068   if (!data || !additional)
3069     return false;
3070
3071   Frame frame;
3072   if (!frame.Init(data, length) ||
3073       !frame.AddAdditionalData(additional, additional_length, add_id)) {
3074     return false;
3075   }
3076   frame.set_track_number(track_number);
3077   frame.set_timestamp(timestamp);
3078   frame.set_is_key(is_key);
3079   return AddGenericFrame(&frame);
3080 }
3081
3082 bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
3083                                          int64_t discard_padding,
3084                                          uint64_t track_number,
3085                                          uint64_t timestamp, bool is_key) {
3086   if (!data)
3087     return false;
3088
3089   Frame frame;
3090   if (!frame.Init(data, length))
3091     return false;
3092   frame.set_discard_padding(discard_padding);
3093   frame.set_track_number(track_number);
3094   frame.set_timestamp(timestamp);
3095   frame.set_is_key(is_key);
3096   return AddGenericFrame(&frame);
3097 }
3098
3099 bool Segment::AddMetadata(const uint8_t* data, uint64_t length,
3100                           uint64_t track_number, uint64_t timestamp_ns,
3101                           uint64_t duration_ns) {
3102   if (!data)
3103     return false;
3104
3105   Frame frame;
3106   if (!frame.Init(data, length))
3107     return false;
3108   frame.set_track_number(track_number);
3109   frame.set_timestamp(timestamp_ns);
3110   frame.set_duration(duration_ns);
3111   frame.set_is_key(true);  // All metadata blocks are keyframes.
3112   return AddGenericFrame(&frame);
3113 }
3114
3115 bool Segment::AddGenericFrame(const Frame* frame) {
3116   if (!frame)
3117     return false;
3118
3119   if (!CheckHeaderInfo())
3120     return false;
3121
3122   // Check for non-monotonically increasing timestamps.
3123   if (frame->timestamp() < last_timestamp_)
3124     return false;
3125
3126   // Check if the track number is valid.
3127   if (!tracks_.GetTrackByNumber(frame->track_number()))
3128     return false;
3129
3130   if (frame->discard_padding() != 0)
3131     doc_type_version_ = 4;
3132
3133   // If the segment has a video track hold onto audio frames to make sure the
3134   // audio that is associated with the start time of a video key-frame is
3135   // muxed into the same cluster.
3136   if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
3137       !force_new_cluster_) {
3138     Frame* const new_frame = new (std::nothrow) Frame();
3139     if (!new_frame || !new_frame->CopyFrom(*frame))
3140       return false;
3141     return QueueFrame(new_frame);
3142   }
3143
3144   if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
3145                               frame->is_key())) {
3146     return false;
3147   }
3148
3149   if (cluster_list_size_ < 1)
3150     return false;
3151
3152   Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3153   if (!cluster)
3154     return false;
3155
3156   // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
3157   // if it is not set already.
3158   bool frame_created = false;
3159   if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
3160       !frame->reference_block_timestamp_set()) {
3161     Frame* const new_frame = new (std::nothrow) Frame();
3162     if (!new_frame->CopyFrom(*frame))
3163       return false;
3164     new_frame->set_reference_block_timestamp(
3165         last_track_timestamp_[frame->track_number() - 1]);
3166     frame = new_frame;
3167     frame_created = true;
3168   }
3169
3170   if (!cluster->AddFrame(frame))
3171     return false;
3172
3173   if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3174     if (!AddCuePoint(frame->timestamp(), cues_track_))
3175       return false;
3176   }
3177
3178   last_timestamp_ = frame->timestamp();
3179   last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3180   last_block_duration_ = frame->duration();
3181
3182   if (frame_created)
3183     delete frame;
3184
3185   return true;
3186 }
3187
3188 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
3189
3190 void Segment::AccurateClusterDuration(bool accurate_cluster_duration) {
3191   accurate_cluster_duration_ = accurate_cluster_duration;
3192 }
3193
3194 void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) {
3195   fixed_size_cluster_timecode_ = fixed_size_cluster_timecode;
3196 }
3197
3198 bool Segment::SetChunking(bool chunking, const char* filename) {
3199   if (chunk_count_ > 0)
3200     return false;
3201
3202   if (chunking) {
3203     if (!filename)
3204       return false;
3205
3206     // Check if we are being set to what is already set.
3207     if (chunking_ && !strcmp(filename, chunking_base_name_))
3208       return true;
3209
3210     const size_t name_length = strlen(filename) + 1;
3211     char* const temp = new (std::nothrow) char[name_length];  // NOLINT
3212     if (!temp)
3213       return false;
3214
3215 #ifdef _MSC_VER
3216     strcpy_s(temp, name_length, filename);
3217 #else
3218     strcpy(temp, filename);
3219 #endif
3220
3221     delete[] chunking_base_name_;
3222     chunking_base_name_ = temp;
3223
3224     if (!UpdateChunkName("chk", &chunk_name_))
3225       return false;
3226
3227     if (!chunk_writer_cluster_) {
3228       chunk_writer_cluster_ = new (std::nothrow) MkvWriter();  // NOLINT
3229       if (!chunk_writer_cluster_)
3230         return false;
3231     }
3232
3233     if (!chunk_writer_cues_) {
3234       chunk_writer_cues_ = new (std::nothrow) MkvWriter();  // NOLINT
3235       if (!chunk_writer_cues_)
3236         return false;
3237     }
3238
3239     if (!chunk_writer_header_) {
3240       chunk_writer_header_ = new (std::nothrow) MkvWriter();  // NOLINT
3241       if (!chunk_writer_header_)
3242         return false;
3243     }
3244
3245     if (!chunk_writer_cluster_->Open(chunk_name_))
3246       return false;
3247
3248     const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
3249     char* const header = new (std::nothrow) char[header_length];  // NOLINT
3250     if (!header)
3251       return false;
3252
3253 #ifdef _MSC_VER
3254     strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
3255     strcat_s(header, header_length, ".hdr");
3256 #else
3257     strcpy(header, chunking_base_name_);
3258     strcat(header, ".hdr");
3259 #endif
3260     if (!chunk_writer_header_->Open(header)) {
3261       delete[] header;
3262       return false;
3263     }
3264
3265     writer_cluster_ = chunk_writer_cluster_;
3266     writer_cues_ = chunk_writer_cues_;
3267     writer_header_ = chunk_writer_header_;
3268
3269     delete[] header;
3270   }
3271
3272   chunking_ = chunking;
3273
3274   return true;
3275 }
3276
3277 bool Segment::CuesTrack(uint64_t track_number) {
3278   const Track* const track = GetTrackByNumber(track_number);
3279   if (!track)
3280     return false;
3281
3282   cues_track_ = track_number;
3283   return true;
3284 }
3285
3286 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
3287
3288 Track* Segment::GetTrackByNumber(uint64_t track_number) const {
3289   return tracks_.GetTrackByNumber(track_number);
3290 }
3291
3292 bool Segment::WriteSegmentHeader() {
3293   UpdateDocTypeVersion();
3294
3295   // TODO(fgalligan): Support more than one segment.
3296   if (!WriteEbmlHeader(writer_header_, doc_type_version_))
3297     return false;
3298   doc_type_version_written_ = doc_type_version_;
3299   ebml_header_size_ = static_cast<int32_t>(writer_header_->Position());
3300
3301   // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
3302   // will write over duration when the file is finalized.
3303   if (WriteID(writer_header_, libwebm::kMkvSegment))
3304     return false;
3305
3306   // Save for later.
3307   size_position_ = writer_header_->Position();
3308
3309   // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
3310   // bytes because if we are going to overwrite the segment size later we do
3311   // not know how big our segment will be.
3312   if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
3313     return false;
3314
3315   payload_pos_ = writer_header_->Position();
3316
3317   if (mode_ == kFile && writer_header_->Seekable()) {
3318     // Set the duration > 0.0 so SegmentInfo will write out the duration. When
3319     // the muxer is done writing we will set the correct duration and have
3320     // SegmentInfo upadte it.
3321     segment_info_.set_duration(1.0);
3322
3323     if (!seek_head_.Write(writer_header_))
3324       return false;
3325   }
3326
3327   if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset()))
3328     return false;
3329   if (!segment_info_.Write(writer_header_))
3330     return false;
3331
3332   if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset()))
3333     return false;
3334   if (!tracks_.Write(writer_header_))
3335     return false;
3336
3337   if (chapters_.Count() > 0) {
3338     if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset()))
3339       return false;
3340     if (!chapters_.Write(writer_header_))
3341       return false;
3342   }
3343
3344   if (tags_.Count() > 0) {
3345     if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset()))
3346       return false;
3347     if (!tags_.Write(writer_header_))
3348       return false;
3349   }
3350
3351   if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
3352     if (!chunk_writer_header_)
3353       return false;
3354
3355     chunk_writer_header_->Close();
3356   }
3357
3358   header_written_ = true;
3359
3360   return true;
3361 }
3362
3363 // Here we are testing whether to create a new cluster, given a frame
3364 // having time frame_timestamp_ns.
3365 //
3366 int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns,
3367                        bool is_key) const {
3368   if (force_new_cluster_)
3369     return 1;
3370
3371   // If no clusters have been created yet, then create a new cluster
3372   // and write this frame immediately, in the new cluster.  This path
3373   // should only be followed once, the first time we attempt to write
3374   // a frame.
3375
3376   if (cluster_list_size_ <= 0)
3377     return 1;
3378
3379   // There exists at least one cluster. We must compare the frame to
3380   // the last cluster, in order to determine whether the frame is
3381   // written to the existing cluster, or that a new cluster should be
3382   // created.
3383
3384   const uint64_t timecode_scale = segment_info_.timecode_scale();
3385   const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3386
3387   const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
3388   const uint64_t last_cluster_timecode = last_cluster->timecode();
3389
3390   // For completeness we test for the case when the frame's timecode
3391   // is less than the cluster's timecode.  Although in principle that
3392   // is allowed, this muxer doesn't actually write clusters like that,
3393   // so this indicates a bug somewhere in our algorithm.
3394
3395   if (frame_timecode < last_cluster_timecode)  // should never happen
3396     return -1;
3397
3398   // If the frame has a timestamp significantly larger than the last
3399   // cluster (in Matroska, cluster-relative timestamps are serialized
3400   // using a 16-bit signed integer), then we cannot write this frame
3401   // to that cluster, and so we must create a new cluster.
3402
3403   const int64_t delta_timecode = frame_timecode - last_cluster_timecode;
3404
3405   if (delta_timecode > kMaxBlockTimecode)
3406     return 2;
3407
3408   // We decide to create a new cluster when we have a video keyframe.
3409   // This will flush queued (audio) frames, and write the keyframe
3410   // immediately, in the newly-created cluster.
3411
3412   if (is_key && tracks_.TrackIsVideo(track_number))
3413     return 1;
3414
3415   // Create a new cluster if we have accumulated too many frames
3416   // already, where "too many" is defined as "the total time of frames
3417   // in the cluster exceeds a threshold".
3418
3419   const uint64_t delta_ns = delta_timecode * timecode_scale;
3420
3421   if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
3422     return 1;
3423
3424   // This is similar to the case above, with the difference that a new
3425   // cluster is created when the size of the current cluster exceeds a
3426   // threshold.
3427
3428   const uint64_t cluster_size = last_cluster->payload_size();
3429
3430   if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
3431     return 1;
3432
3433   // There's no need to create a new cluster, so emit this frame now.
3434
3435   return 0;
3436 }
3437
3438 bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) {
3439   const int32_t new_size = cluster_list_size_ + 1;
3440
3441   if (new_size > cluster_list_capacity_) {
3442     // Add more clusters.
3443     const int32_t new_capacity =
3444         (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
3445     Cluster** const clusters =
3446         new (std::nothrow) Cluster*[new_capacity];  // NOLINT
3447     if (!clusters)
3448       return false;
3449
3450     for (int32_t i = 0; i < cluster_list_size_; ++i) {
3451       clusters[i] = cluster_list_[i];
3452     }
3453
3454     delete[] cluster_list_;
3455
3456     cluster_list_ = clusters;
3457     cluster_list_capacity_ = new_capacity;
3458   }
3459
3460   if (!WriteFramesLessThan(frame_timestamp_ns))
3461     return false;
3462
3463   if (cluster_list_size_ > 0) {
3464     // Update old cluster's size
3465     Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
3466
3467     if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns))
3468       return false;
3469   }
3470
3471   if (output_cues_)
3472     new_cuepoint_ = true;
3473
3474   if (chunking_ && cluster_list_size_ > 0) {
3475     chunk_writer_cluster_->Close();
3476     chunk_count_++;
3477
3478     if (!UpdateChunkName("chk", &chunk_name_))
3479       return false;
3480     if (!chunk_writer_cluster_->Open(chunk_name_))
3481       return false;
3482   }
3483
3484   const uint64_t timecode_scale = segment_info_.timecode_scale();
3485   const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3486
3487   uint64_t cluster_timecode = frame_timecode;
3488
3489   if (frames_size_ > 0) {
3490     const Frame* const f = frames_[0];  // earliest queued frame
3491     const uint64_t ns = f->timestamp();
3492     const uint64_t tc = ns / timecode_scale;
3493
3494     if (tc < cluster_timecode)
3495       cluster_timecode = tc;
3496   }
3497
3498   Cluster*& cluster = cluster_list_[cluster_list_size_];
3499   const int64_t offset = MaxOffset();
3500   cluster = new (std::nothrow)
3501       Cluster(cluster_timecode, offset, segment_info_.timecode_scale(),
3502               accurate_cluster_duration_, fixed_size_cluster_timecode_);
3503   if (!cluster)
3504     return false;
3505
3506   if (!cluster->Init(writer_cluster_))
3507     return false;
3508
3509   cluster_list_size_ = new_size;
3510   return true;
3511 }
3512
3513 bool Segment::DoNewClusterProcessing(uint64_t track_number,
3514                                      uint64_t frame_timestamp_ns, bool is_key) {
3515   for (;;) {
3516     // Based on the characteristics of the current frame and current
3517     // cluster, decide whether to create a new cluster.
3518     const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
3519     if (result < 0)  // error
3520       return false;
3521
3522     // Always set force_new_cluster_ to false after TestFrame.
3523     force_new_cluster_ = false;
3524
3525     // A non-zero result means create a new cluster.
3526     if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
3527       return false;
3528
3529     // Write queued (audio) frames.
3530     const int frame_count = WriteFramesAll();
3531     if (frame_count < 0)  // error
3532       return false;
3533
3534     // Write the current frame to the current cluster (if TestFrame
3535     // returns 0) or to a newly created cluster (TestFrame returns 1).
3536     if (result <= 1)
3537       return true;
3538
3539     // TestFrame returned 2, which means there was a large time
3540     // difference between the cluster and the frame itself.  Do the
3541     // test again, comparing the frame to the new cluster.
3542   }
3543 }
3544
3545 bool Segment::CheckHeaderInfo() {
3546   if (!header_written_) {
3547     if (!WriteSegmentHeader())
3548       return false;
3549
3550     if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset()))
3551       return false;
3552
3553     if (output_cues_ && cues_track_ == 0) {
3554       // Check for a video track
3555       for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) {
3556         const Track* const track = tracks_.GetTrackByIndex(i);
3557         if (!track)
3558           return false;
3559
3560         if (tracks_.TrackIsVideo(track->number())) {
3561           cues_track_ = track->number();
3562           break;
3563         }
3564       }
3565
3566       // Set first track found
3567       if (cues_track_ == 0) {
3568         const Track* const track = tracks_.GetTrackByIndex(0);
3569         if (!track)
3570           return false;
3571
3572         cues_track_ = track->number();
3573       }
3574     }
3575   }
3576   return true;
3577 }
3578
3579 void Segment::UpdateDocTypeVersion() {
3580   for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) {
3581     const Track* track = tracks_.GetTrackByIndex(index);
3582     if (track == NULL)
3583       break;
3584     if ((track->codec_delay() || track->seek_pre_roll()) &&
3585         doc_type_version_ < 4) {
3586       doc_type_version_ = 4;
3587       break;
3588     }
3589   }
3590 }
3591
3592 bool Segment::UpdateChunkName(const char* ext, char** name) const {
3593   if (!name || !ext)
3594     return false;
3595
3596   char ext_chk[64];
3597 #ifdef _MSC_VER
3598   sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3599 #else
3600   snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3601 #endif
3602
3603   const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
3604   char* const str = new (std::nothrow) char[length];  // NOLINT
3605   if (!str)
3606     return false;
3607
3608 #ifdef _MSC_VER
3609   strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
3610   strcat_s(str, length, ext_chk);
3611 #else
3612   strcpy(str, chunking_base_name_);
3613   strcat(str, ext_chk);
3614 #endif
3615
3616   delete[] * name;
3617   *name = str;
3618
3619   return true;
3620 }
3621
3622 int64_t Segment::MaxOffset() {
3623   if (!writer_header_)
3624     return -1;
3625
3626   int64_t offset = writer_header_->Position() - payload_pos_;
3627
3628   if (chunking_) {
3629     for (int32_t i = 0; i < cluster_list_size_; ++i) {
3630       Cluster* const cluster = cluster_list_[i];
3631       offset += cluster->Size();
3632     }
3633
3634     if (writer_cues_)
3635       offset += writer_cues_->Position();
3636   }
3637
3638   return offset;
3639 }
3640
3641 bool Segment::QueueFrame(Frame* frame) {
3642   const int32_t new_size = frames_size_ + 1;
3643
3644   if (new_size > frames_capacity_) {
3645     // Add more frames.
3646     const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
3647
3648     if (new_capacity < 1)
3649       return false;
3650
3651     Frame** const frames = new (std::nothrow) Frame*[new_capacity];  // NOLINT
3652     if (!frames)
3653       return false;
3654
3655     for (int32_t i = 0; i < frames_size_; ++i) {
3656       frames[i] = frames_[i];
3657     }
3658
3659     delete[] frames_;
3660     frames_ = frames;
3661     frames_capacity_ = new_capacity;
3662   }
3663
3664   frames_[frames_size_++] = frame;
3665
3666   return true;
3667 }
3668
3669 int Segment::WriteFramesAll() {
3670   if (frames_ == NULL)
3671     return 0;
3672
3673   if (cluster_list_size_ < 1)
3674     return -1;
3675
3676   Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3677
3678   if (!cluster)
3679     return -1;
3680
3681   for (int32_t i = 0; i < frames_size_; ++i) {
3682     Frame*& frame = frames_[i];
3683     // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
3684     // places where |doc_type_version_| needs to be updated.
3685     if (frame->discard_padding() != 0)
3686       doc_type_version_ = 4;
3687     if (!cluster->AddFrame(frame))
3688       return -1;
3689
3690     if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3691       if (!AddCuePoint(frame->timestamp(), cues_track_))
3692         return -1;
3693     }
3694
3695     if (frame->timestamp() > last_timestamp_) {
3696       last_timestamp_ = frame->timestamp();
3697       last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3698     }
3699
3700     delete frame;
3701     frame = NULL;
3702   }
3703
3704   const int result = frames_size_;
3705   frames_size_ = 0;
3706
3707   return result;
3708 }
3709
3710 bool Segment::WriteFramesLessThan(uint64_t timestamp) {
3711   // Check |cluster_list_size_| to see if this is the first cluster. If it is
3712   // the first cluster the audio frames that are less than the first video
3713   // timesatmp will be written in a later step.
3714   if (frames_size_ > 0 && cluster_list_size_ > 0) {
3715     if (!frames_)
3716       return false;
3717
3718     Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3719     if (!cluster)
3720       return false;
3721
3722     int32_t shift_left = 0;
3723
3724     // TODO(fgalligan): Change this to use the durations of frames instead of
3725     // the next frame's start time if the duration is accurate.
3726     for (int32_t i = 1; i < frames_size_; ++i) {
3727       const Frame* const frame_curr = frames_[i];
3728
3729       if (frame_curr->timestamp() > timestamp)
3730         break;
3731
3732       const Frame* const frame_prev = frames_[i - 1];
3733       if (frame_prev->discard_padding() != 0)
3734         doc_type_version_ = 4;
3735       if (!cluster->AddFrame(frame_prev))
3736         return false;
3737
3738       if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
3739         if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
3740           return false;
3741       }
3742
3743       ++shift_left;
3744       if (frame_prev->timestamp() > last_timestamp_) {
3745         last_timestamp_ = frame_prev->timestamp();
3746         last_track_timestamp_[frame_prev->track_number() - 1] =
3747             frame_prev->timestamp();
3748       }
3749
3750       delete frame_prev;
3751     }
3752
3753     if (shift_left > 0) {
3754       if (shift_left >= frames_size_)
3755         return false;
3756
3757       const int32_t new_frames_size = frames_size_ - shift_left;
3758       for (int32_t i = 0; i < new_frames_size; ++i) {
3759         frames_[i] = frames_[i + shift_left];
3760       }
3761
3762       frames_size_ = new_frames_size;
3763     }
3764   }
3765
3766   return true;
3767 }
3768
3769 }  // namespace mkvmuxer