• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
27 namespace mkvmuxer {
28 
29 const float PrimaryChromaticity::kChromaticityMin = 0.0f;
30 const float PrimaryChromaticity::kChromaticityMax = 1.0f;
31 const float MasteringMetadata::kMinLuminance = 0.0f;
32 const float MasteringMetadata::kMinLuminanceMax = 999.99f;
33 const float MasteringMetadata::kMaxLuminanceMax = 9999.99f;
34 const float MasteringMetadata::kValueNotPresent = FLT_MAX;
35 const uint64_t Colour::kValueNotPresent = UINT64_MAX;
36 
37 namespace {
38 
39 const char kDocTypeWebm[] = "webm";
40 const 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.
StrCpy(const char * src,char ** dst_ptr)48 bool 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 
70 typedef std::auto_ptr<PrimaryChromaticity> PrimaryChromaticityPtr;
CopyChromaticity(const PrimaryChromaticity * src,PrimaryChromaticityPtr * dst)71 bool 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 
IMkvWriter()89 IMkvWriter::IMkvWriter() {}
90 
~IMkvWriter()91 IMkvWriter::~IMkvWriter() {}
92 
WriteEbmlHeader(IMkvWriter * writer,uint64_t doc_type_version,const char * const doc_type)93 bool 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 
WriteEbmlHeader(IMkvWriter * writer,uint64_t doc_type_version)140 bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) {
141   return WriteEbmlHeader(writer, doc_type_version, kDocTypeWebm);
142 }
143 
WriteEbmlHeader(IMkvWriter * writer)144 bool WriteEbmlHeader(IMkvWriter* writer) {
145   return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion);
146 }
147 
ChunkedCopy(mkvparser::IMkvReader * source,mkvmuxer::IMkvWriter * dst,int64_t start,int64_t size)148 bool 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 
Frame()170 Frame::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 
~Frame()185 Frame::~Frame() {
186   delete[] frame_;
187   delete[] additional_;
188 }
189 
CopyFrom(const Frame & frame)190 bool 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 
Init(const uint8_t * frame,uint64_t length)218 bool 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 
AddAdditionalData(const uint8_t * additional,uint64_t length,uint64_t add_id)232 bool 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 
IsValid() const248 bool 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 
CanBeSimpleBlock() const265 bool Frame::CanBeSimpleBlock() const {
266   return additional_ == NULL && discard_padding_ == 0 && duration_ == 0;
267 }
268 
set_duration(uint64_t duration)269 void Frame::set_duration(uint64_t duration) {
270   duration_ = duration;
271   duration_set_ = true;
272 }
273 
set_reference_block_timestamp(int64_t reference_block_timestamp)274 void 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 
CuePoint()283 CuePoint::CuePoint()
284     : time_(0),
285       track_(0),
286       cluster_pos_(0),
287       block_number_(1),
288       output_block_number_(true) {}
289 
~CuePoint()290 CuePoint::~CuePoint() {}
291 
Write(IMkvWriter * writer) const292 bool 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 
PayloadSize() const347 uint64_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 
Size() const363 uint64_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 
Cues()373 Cues::Cues()
374     : cue_entries_capacity_(0),
375       cue_entries_size_(0),
376       cue_entries_(NULL),
377       output_block_number_(true) {}
378 
~Cues()379 Cues::~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 
AddCue(CuePoint * cue)389 bool 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 
GetCueByIndex(int32_t index) const421 CuePoint* 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 
Size()431 uint64_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 
Write(IMkvWriter * writer) const439 bool 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 
ContentEncAESSettings()481 ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
482 
Size() const483 uint64_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 
Write(IMkvWriter * writer) const491 bool 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 
PayloadSize() const515 uint64_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 
ContentEncoding()525 ContentEncoding::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 
~ContentEncoding()533 ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
534 
SetEncryptionID(const uint8_t * id,uint64_t length)535 bool 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 
Size() const552 uint64_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 
Write(IMkvWriter * writer) const562 bool 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 
EncodingSize(uint64_t compresion_size,uint64_t encryption_size) const608 uint64_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 
EncryptionSize() const631 uint64_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 
Track(unsigned int * seed)646 Track::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 
~Track()662 Track::~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 
AddContentEncoding()677 bool 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 
GetContentEncodingByIndex(uint32_t index) const704 ContentEncoding* 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 
PayloadSize() const714 uint64_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 
Size() const760 uint64_t Track::Size() const {
761   uint64_t size = PayloadSize();
762   size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size);
763   return size;
764 }
765 
Write(IMkvWriter * writer) const766 bool 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 
SetCodecPrivate(const uint8_t * codec_private,uint64_t length)888 bool 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 
set_codec_id(const char * codec_id)905 void 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.
set_language(const char * language)922 void 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 
set_name(const char * name)938 void 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 
PrimaryChromaticitySize(libwebm::MkvId x_id,libwebm::MkvId y_id) const958 uint64_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 
Write(IMkvWriter * writer,libwebm::MkvId x_id,libwebm::MkvId y_id) const963 bool 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 
Valid() const972 bool PrimaryChromaticity::Valid() const {
973   return (x_ >= kChromaticityMin && x_ <= kChromaticityMax &&
974           y_ >= kChromaticityMin && y_ <= kChromaticityMax);
975 }
976 
MasteringMetadataSize() const977 uint64_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 
Valid() const986 bool 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 
Write(IMkvWriter * writer) const1011 bool 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 
SetChromaticity(const PrimaryChromaticity * r,const PrimaryChromaticity * g,const PrimaryChromaticity * b,const PrimaryChromaticity * white_point)1052 bool 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 
PayloadSize() const1083 uint64_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 
ColourSize() const1112 uint64_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 
Valid() const1121 bool 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 
Write(IMkvWriter * writer) const1148 bool 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 
SetMasteringMetadata(const MasteringMetadata & mastering_metadata)1235 bool 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 
PayloadSize() const1254 uint64_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 
ProjectionSize() const1318 uint64_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 
Write(IMkvWriter * writer) const1327 bool 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 
SetProjectionPrivate(const uint8_t * data,uint64_t data_length)1363 bool 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 
PayloadSize() const1387 uint64_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 
VideoTrack(unsigned int * seed)1407 VideoTrack::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 
~VideoTrack()1425 VideoTrack::~VideoTrack() {
1426   delete colour_;
1427   delete projection_;
1428 }
1429 
SetStereoMode(uint64_t stereo_mode)1430 bool 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 
SetAlphaMode(uint64_t alpha_mode)1441 bool 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 
PayloadSize() const1449 uint64_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 
Write(IMkvWriter * writer) const1458 bool 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 
SetColour(const Colour & colour)1543 bool 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 
SetProjection(const Projection & projection)1571 bool 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 
VideoPayloadSize() const1592 uint64_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 
AudioTrack(unsigned int * seed)1638 AudioTrack::AudioTrack(unsigned int* seed)
1639     : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
1640 
~AudioTrack()1641 AudioTrack::~AudioTrack() {}
1642 
PayloadSize() const1643 uint64_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 
Write(IMkvWriter * writer) const1658 bool 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 
1701 const char Tracks::kOpusCodecId[] = "A_OPUS";
1702 const char Tracks::kVorbisCodecId[] = "A_VORBIS";
1703 const char Tracks::kVp8CodecId[] = "V_VP8";
1704 const char Tracks::kVp9CodecId[] = "V_VP9";
1705 const char Tracks::kVp10CodecId[] = "V_VP10";
1706 const char Tracks::kWebVttCaptionsId[] = "D_WEBVTT/CAPTIONS";
1707 const char Tracks::kWebVttDescriptionsId[] = "D_WEBVTT/DESCRIPTIONS";
1708 const char Tracks::kWebVttMetadataId[] = "D_WEBVTT/METADATA";
1709 const char Tracks::kWebVttSubtitlesId[] = "D_WEBVTT/SUBTITLES";
1710 
Tracks()1711 Tracks::Tracks()
1712     : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {}
1713 
~Tracks()1714 Tracks::~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 
AddTrack(Track * track,int32_t number)1724 bool 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 
GetTrackByIndex(uint32_t index) const1783 const 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 
GetTrackByNumber(uint64_t track_number) const1793 Track* 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 
TrackIsAudio(uint64_t track_number) const1803 bool 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 
TrackIsVideo(uint64_t track_number) const1812 bool 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 
Write(IMkvWriter * writer) const1821 bool 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 
set_id(const char * id)1859 bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
1860 
set_time(const Segment & segment,uint64_t start_ns,uint64_t end_ns)1861 void 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 
add_string(const char * title,const char * language,const char * country)1869 bool 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 
Chapter()1889 Chapter::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 
~Chapter()1901 Chapter::~Chapter() {}
1902 
Init(unsigned int * seed)1903 void 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 
ShallowCopy(Chapter * dst) const1913 void 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 
Clear()1923 void 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 
ExpandDisplaysArray()1937 bool 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 
WriteAtom(IMkvWriter * writer) const1959 uint64_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 
Init()2015 void Chapter::Display::Init() {
2016   title_ = NULL;
2017   language_ = NULL;
2018   country_ = NULL;
2019 }
2020 
Clear()2021 void Chapter::Display::Clear() {
2022   StrCpy(NULL, &title_);
2023   StrCpy(NULL, &language_);
2024   StrCpy(NULL, &country_);
2025 }
2026 
set_title(const char * title)2027 bool Chapter::Display::set_title(const char* title) {
2028   return StrCpy(title, &title_);
2029 }
2030 
set_language(const char * language)2031 bool Chapter::Display::set_language(const char* language) {
2032   return StrCpy(language, &language_);
2033 }
2034 
set_country(const char * country)2035 bool Chapter::Display::set_country(const char* country) {
2036   return StrCpy(country, &country_);
2037 }
2038 
WriteDisplay(IMkvWriter * writer) const2039 uint64_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 
Chapters()2086 Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
2087 
~Chapters()2088 Chapters::~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 
Count() const2098 int Chapters::Count() const { return chapters_count_; }
2099 
AddChapter(unsigned int * seed)2100 Chapter* 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 
Write(IMkvWriter * writer) const2110 bool 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 
ExpandChaptersArray()2132 bool 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 
WriteEdition(IMkvWriter * writer) const2156 uint64_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 
add_simple_tag(const char * tag_name,const char * tag_string)2194 bool 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 
Tag()2210 Tag::Tag() {
2211   simple_tags_ = NULL;
2212   simple_tags_size_ = 0;
2213   simple_tags_count_ = 0;
2214 }
2215 
~Tag()2216 Tag::~Tag() {}
2217 
ShallowCopy(Tag * dst) const2218 void 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 
Clear()2224 void 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 
ExpandSimpleTagsArray()2236 bool 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 
Write(IMkvWriter * writer) const2258 uint64_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 
Init()2294 void Tag::SimpleTag::Init() {
2295   tag_name_ = NULL;
2296   tag_string_ = NULL;
2297 }
2298 
Clear()2299 void Tag::SimpleTag::Clear() {
2300   StrCpy(NULL, &tag_name_);
2301   StrCpy(NULL, &tag_string_);
2302 }
2303 
set_tag_name(const char * tag_name)2304 bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
2305   return StrCpy(tag_name, &tag_name_);
2306 }
2307 
set_tag_string(const char * tag_string)2308 bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
2309   return StrCpy(tag_string, &tag_string_);
2310 }
2311 
Write(IMkvWriter * writer) const2312 uint64_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 
Tags()2345 Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
2346 
~Tags()2347 Tags::~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 
Count() const2357 int Tags::Count() const { return tags_count_; }
2358 
AddTag()2359 Tag* Tags::AddTag() {
2360   if (!ExpandTagsArray())
2361     return NULL;
2362 
2363   Tag& tag = tags_[tags_count_++];
2364 
2365   return &tag;
2366 }
2367 
Write(IMkvWriter * writer) const2368 bool 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 
ExpandTagsArray()2400 bool 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 
Cluster(uint64_t timecode,int64_t cues_pos,uint64_t timecode_scale,bool write_last_frame_with_duration,bool fixed_size_timecode)2428 Cluster::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 
~Cluster()2442 Cluster::~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 
Init(IMkvWriter * ptr_writer)2454 bool Cluster::Init(IMkvWriter* ptr_writer) {
2455   if (!ptr_writer) {
2456     return false;
2457   }
2458   writer_ = ptr_writer;
2459   return true;
2460 }
2461 
AddFrame(const Frame * const frame)2462 bool Cluster::AddFrame(const Frame* const frame) {
2463   return QueueOrWriteFrame(frame);
2464 }
2465 
AddFrame(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t abs_timecode,bool is_key)2466 bool 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 
AddFrameWithAdditional(const uint8_t * data,uint64_t length,const uint8_t * additional,uint64_t additional_length,uint64_t add_id,uint64_t track_number,uint64_t abs_timecode,bool is_key)2478 bool 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 
AddFrameWithDiscardPadding(const uint8_t * data,uint64_t length,int64_t discard_padding,uint64_t track_number,uint64_t abs_timecode,bool is_key)2497 bool 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 
AddMetadata(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t abs_timecode,uint64_t duration_timecode)2511 bool 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 
AddPayloadSize(uint64_t size)2524 void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; }
2525 
Finalize()2526 bool Cluster::Finalize() {
2527   return !write_last_frame_with_duration_ && Finalize(false, 0);
2528 }
2529 
Finalize(bool set_last_frame_duration,uint64_t duration)2530 bool 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 
Size() const2593 uint64_t Cluster::Size() const {
2594   const uint64_t element_size =
2595       EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) +
2596       payload_size_;
2597   return element_size;
2598 }
2599 
PreWriteBlock()2600 bool 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 
PostWriteBlock(uint64_t element_size)2612 void Cluster::PostWriteBlock(uint64_t element_size) {
2613   AddPayloadSize(element_size);
2614   ++blocks_added_;
2615 }
2616 
GetRelativeTimecode(int64_t abs_timecode) const2617 int64_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 
DoWriteFrame(const Frame * const frame)2628 bool 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 
QueueOrWriteFrame(const Frame * const frame)2644 bool 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 
WriteClusterHeader()2699 bool 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 
SeekHead()2729 SeekHead::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 
~SeekHead()2736 SeekHead::~SeekHead() {}
2737 
Finalize(IMkvWriter * writer) const2738 bool 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 
Write(IMkvWriter * writer)2802 bool 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 
AddSeekEntry(uint32_t id,uint64_t pos)2816 bool 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 
GetId(int index) const2827 uint32_t SeekHead::GetId(int index) const {
2828   if (index < 0 || index >= kSeekEntryCount)
2829     return UINT_MAX;
2830   return seek_entry_id_[index];
2831 }
2832 
GetPosition(int index) const2833 uint64_t SeekHead::GetPosition(int index) const {
2834   if (index < 0 || index >= kSeekEntryCount)
2835     return ULLONG_MAX;
2836   return seek_entry_pos_[index];
2837 }
2838 
SetSeekEntry(int index,uint32_t id,uint64_t position)2839 bool 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 
MaxEntrySize() const2847 uint64_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 
SegmentInfo()2864 SegmentInfo::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 
~SegmentInfo()2872 SegmentInfo::~SegmentInfo() {
2873   delete[] muxing_app_;
2874   delete[] writing_app_;
2875 }
2876 
Init()2877 bool 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 
Finalize(IMkvWriter * writer) const2912 bool 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 
Write(IMkvWriter * writer)2938 bool 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 
set_muxing_app(const char * app)2988 void 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 
set_writing_app(const char * app)3006 void 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 
Segment()3028 Segment::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 
~Segment()3072 Segment::~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 
MoveCuesBeforeClustersHelper(uint64_t diff,int32_t index,uint64_t * cues_size)3106 void 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 
MoveCuesBeforeClusters()3135 void 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 
Init(IMkvWriter * ptr_writer)3159 bool 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 
CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader * reader,IMkvWriter * writer)3173 bool 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 
Finalize()3209 bool 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 
AddTrack(int32_t number)3336 Track* 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 
AddChapter()3350 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
3351 
AddTag()3352 Tag* Segment::AddTag() { return tags_.AddTag(); }
3353 
AddVideoTrack(int32_t width,int32_t height,int32_t number)3354 uint64_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 
AddCuePoint(uint64_t timestamp,uint64_t track)3370 bool 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 
AddAudioTrack(int32_t sample_rate,int32_t channels,int32_t number)3393 uint64_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 
AddFrame(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t timestamp,bool is_key)3409 bool 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 
AddFrameWithAdditional(const uint8_t * data,uint64_t length,const uint8_t * additional,uint64_t additional_length,uint64_t add_id,uint64_t track_number,uint64_t timestamp,bool is_key)3423 bool 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 
AddFrameWithDiscardPadding(const uint8_t * data,uint64_t length,int64_t discard_padding,uint64_t track_number,uint64_t timestamp,bool is_key)3442 bool 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 
AddMetadata(const uint8_t * data,uint64_t length,uint64_t track_number,uint64_t timestamp_ns,uint64_t duration_ns)3459 bool 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 
AddGenericFrame(const Frame * frame)3475 bool 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 
OutputCues(bool output_cues)3551 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
3552 
AccurateClusterDuration(bool accurate_cluster_duration)3553 void Segment::AccurateClusterDuration(bool accurate_cluster_duration) {
3554   accurate_cluster_duration_ = accurate_cluster_duration;
3555 }
3556 
UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode)3557 void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) {
3558   fixed_size_cluster_timecode_ = fixed_size_cluster_timecode;
3559 }
3560 
SetChunking(bool chunking,const char * filename)3561 bool 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 
CuesTrack(uint64_t track_number)3640 bool 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 
ForceNewClusterOnNextFrame()3649 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
3650 
GetTrackByNumber(uint64_t track_number) const3651 Track* Segment::GetTrackByNumber(uint64_t track_number) const {
3652   return tracks_.GetTrackByNumber(track_number);
3653 }
3654 
WriteSegmentHeader()3655 bool 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 //
TestFrame(uint64_t track_number,uint64_t frame_timestamp_ns,bool is_key) const3730 int 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 
MakeNewCluster(uint64_t frame_timestamp_ns)3802 bool 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 
DoNewClusterProcessing(uint64_t track_number,uint64_t frame_timestamp_ns,bool is_key)3877 bool 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 
CheckHeaderInfo()3909 bool 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 
UpdateDocTypeVersion()3943 void 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 
UpdateChunkName(const char * ext,char ** name) const3956 bool 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 
MaxOffset()3986 int64_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 
QueueFrame(Frame * frame)4005 bool 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 
WriteFramesAll()4033 int 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 
WriteFramesLessThan(uint64_t timestamp)4074 bool 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 
DocTypeIsWebm() const4133 bool 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