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