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