1 // Copyright (c) 2012 The WebM project authors. All Rights Reserved.
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.
9 #include "mkvmuxer/mkvmuxer.h"
21 #include "common/webmids.h"
22 #include "mkvmuxer/mkvmuxerutil.h"
23 #include "mkvmuxer/mkvwriter.h"
24 #include "mkvparser/mkvparser.h"
28 const float MasteringMetadata::kValueNotPresent = FLT_MAX;
29 const uint64_t Colour::kValueNotPresent = UINT64_MAX;
32 // Deallocate the string designated by |dst|, and then copy the |src|
33 // string to |dst|. The caller owns both the |src| string and the
34 // |dst| copy (hence the caller is responsible for eventually
35 // deallocating the strings, either directly, or indirectly via
36 // StrCpy). Returns true if the source string was successfully copied
37 // to the destination.
38 bool StrCpy(const char* src, char** dst_ptr) {
42 char*& dst = *dst_ptr;
50 const size_t size = strlen(src) + 1;
52 dst = new (std::nothrow) char[size]; // NOLINT
56 strcpy(dst, src); // NOLINT
60 typedef std::auto_ptr<PrimaryChromaticity> PrimaryChromaticityPtr;
61 bool CopyChromaticity(const PrimaryChromaticity* src,
62 PrimaryChromaticityPtr* dst) {
66 dst->reset(new (std::nothrow) PrimaryChromaticity(src->x, src->y));
75 ///////////////////////////////////////////////////////////////
79 IMkvWriter::IMkvWriter() {}
81 IMkvWriter::~IMkvWriter() {}
83 bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) {
85 uint64_t size = EbmlElementSize(libwebm::kMkvEBMLVersion, UINT64_C(1));
86 size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, UINT64_C(1));
87 size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, UINT64_C(4));
88 size += EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, UINT64_C(8));
89 size += EbmlElementSize(libwebm::kMkvDocType, "webm");
90 size += EbmlElementSize(libwebm::kMkvDocTypeVersion, doc_type_version);
91 size += EbmlElementSize(libwebm::kMkvDocTypeReadVersion, UINT64_C(2));
93 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size))
95 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion, UINT64_C(1)))
97 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion, UINT64_C(1)))
99 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength, UINT64_C(4)))
101 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength, UINT64_C(8)))
103 if (!WriteEbmlElement(writer, libwebm::kMkvDocType, "webm"))
105 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion, doc_type_version))
107 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion, UINT64_C(2)))
113 bool WriteEbmlHeader(IMkvWriter* writer) {
114 return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion);
117 bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst,
118 int64_t start, int64_t size) {
119 // TODO(vigneshv): Check if this is a reasonable value.
120 const uint32_t kBufSize = 2048;
121 uint8_t* buf = new uint8_t[kBufSize];
122 int64_t offset = start;
124 const int64_t read_len = (size > kBufSize) ? kBufSize : size;
125 if (source->Read(offset, static_cast<long>(read_len), buf))
127 dst->Write(buf, static_cast<uint32_t>(read_len));
135 ///////////////////////////////////////////////////////////////
142 additional_length_(0),
144 duration_set_(false),
151 reference_block_timestamp_(0),
152 reference_block_timestamp_set_(false) {}
156 delete[] additional_;
159 bool Frame::CopyFrom(const Frame& frame) {
163 if (frame.length() > 0 && frame.frame() != NULL &&
164 !Init(frame.frame(), frame.length())) {
168 delete[] additional_;
170 additional_length_ = 0;
171 if (frame.additional_length() > 0 && frame.additional() != NULL &&
172 !AddAdditionalData(frame.additional(), frame.additional_length(),
176 duration_ = frame.duration();
177 duration_set_ = frame.duration_set();
178 is_key_ = frame.is_key();
179 track_number_ = frame.track_number();
180 timestamp_ = frame.timestamp();
181 discard_padding_ = frame.discard_padding();
182 reference_block_timestamp_ = frame.reference_block_timestamp();
183 reference_block_timestamp_set_ = frame.reference_block_timestamp_set();
187 bool Frame::Init(const uint8_t* frame, uint64_t length) {
188 uint8_t* const data =
189 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
197 memcpy(frame_, frame, static_cast<size_t>(length_));
201 bool Frame::AddAdditionalData(const uint8_t* additional, uint64_t length,
203 uint8_t* const data =
204 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
208 delete[] additional_;
210 additional_length_ = length;
213 memcpy(additional_, additional, static_cast<size_t>(additional_length_));
217 bool Frame::IsValid() const {
218 if (length_ == 0 || !frame_) {
221 if ((additional_length_ != 0 && !additional_) ||
222 (additional_ != NULL && additional_length_ == 0)) {
225 if (track_number_ == 0 || track_number_ > kMaxTrackNumber) {
228 if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) {
234 bool Frame::CanBeSimpleBlock() const {
235 return additional_ == NULL && discard_padding_ == 0 && duration_ == 0;
238 void Frame::set_duration(uint64_t duration) {
239 duration_ = duration;
240 duration_set_ = true;
243 void Frame::set_reference_block_timestamp(int64_t reference_block_timestamp) {
244 reference_block_timestamp_ = reference_block_timestamp;
245 reference_block_timestamp_set_ = true;
248 ///////////////////////////////////////////////////////////////
257 output_block_number_(true) {}
259 CuePoint::~CuePoint() {}
261 bool CuePoint::Write(IMkvWriter* writer) const {
262 if (!writer || track_ < 1 || cluster_pos_ < 1)
266 EbmlElementSize(libwebm::kMkvCueClusterPosition, cluster_pos_);
267 size += EbmlElementSize(libwebm::kMkvCueTrack, track_);
268 if (output_block_number_ && block_number_ > 1)
269 size += EbmlElementSize(libwebm::kMkvCueBlockNumber, block_number_);
270 const uint64_t track_pos_size =
271 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
272 const uint64_t payload_size =
273 EbmlElementSize(libwebm::kMkvCueTime, time_) + track_pos_size;
275 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCuePoint, payload_size))
278 const int64_t payload_position = writer->Position();
279 if (payload_position < 0)
282 if (!WriteEbmlElement(writer, libwebm::kMkvCueTime, time_))
285 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size))
287 if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack, track_))
289 if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition, cluster_pos_))
291 if (output_block_number_ && block_number_ > 1)
292 if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber, block_number_))
295 const int64_t stop_position = writer->Position();
296 if (stop_position < 0)
299 if (stop_position - payload_position != static_cast<int64_t>(payload_size))
305 uint64_t CuePoint::PayloadSize() const {
307 EbmlElementSize(libwebm::kMkvCueClusterPosition, cluster_pos_);
308 size += EbmlElementSize(libwebm::kMkvCueTrack, track_);
309 if (output_block_number_ && block_number_ > 1)
310 size += EbmlElementSize(libwebm::kMkvCueBlockNumber, block_number_);
311 const uint64_t track_pos_size =
312 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
313 const uint64_t payload_size =
314 EbmlElementSize(libwebm::kMkvCueTime, time_) + track_pos_size;
319 uint64_t CuePoint::Size() const {
320 const uint64_t payload_size = PayloadSize();
321 return EbmlMasterElementSize(libwebm::kMkvCuePoint, payload_size) +
325 ///////////////////////////////////////////////////////////////
330 : cue_entries_capacity_(0),
331 cue_entries_size_(0),
333 output_block_number_(true) {}
337 for (int32_t i = 0; i < cue_entries_size_; ++i) {
338 CuePoint* const cue = cue_entries_[i];
341 delete[] cue_entries_;
345 bool Cues::AddCue(CuePoint* cue) {
349 if ((cue_entries_size_ + 1) > cue_entries_capacity_) {
350 // Add more CuePoints.
351 const int32_t new_capacity =
352 (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2;
354 if (new_capacity < 1)
357 CuePoint** const cues =
358 new (std::nothrow) CuePoint*[new_capacity]; // NOLINT
362 for (int32_t i = 0; i < cue_entries_size_; ++i) {
363 cues[i] = cue_entries_[i];
366 delete[] cue_entries_;
369 cue_entries_capacity_ = new_capacity;
372 cue->set_output_block_number(output_block_number_);
373 cue_entries_[cue_entries_size_++] = cue;
377 CuePoint* Cues::GetCueByIndex(int32_t index) const {
378 if (cue_entries_ == NULL)
381 if (index >= cue_entries_size_)
384 return cue_entries_[index];
387 uint64_t Cues::Size() {
389 for (int32_t i = 0; i < cue_entries_size_; ++i)
390 size += GetCueByIndex(i)->Size();
391 size += EbmlMasterElementSize(libwebm::kMkvCues, size);
395 bool Cues::Write(IMkvWriter* writer) const {
400 for (int32_t i = 0; i < cue_entries_size_; ++i) {
401 const CuePoint* const cue = GetCueByIndex(i);
409 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCues, size))
412 const int64_t payload_position = writer->Position();
413 if (payload_position < 0)
416 for (int32_t i = 0; i < cue_entries_size_; ++i) {
417 const CuePoint* const cue = GetCueByIndex(i);
419 if (!cue->Write(writer))
423 const int64_t stop_position = writer->Position();
424 if (stop_position < 0)
427 if (stop_position - payload_position != static_cast<int64_t>(size))
433 ///////////////////////////////////////////////////////////////
435 // ContentEncAESSettings Class
437 ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
439 uint64_t ContentEncAESSettings::Size() const {
440 const uint64_t payload = PayloadSize();
441 const uint64_t size =
442 EbmlMasterElementSize(libwebm::kMkvContentEncAESSettings, payload) +
447 bool ContentEncAESSettings::Write(IMkvWriter* writer) const {
448 const uint64_t payload = PayloadSize();
450 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncAESSettings,
454 const int64_t payload_position = writer->Position();
455 if (payload_position < 0)
458 if (!WriteEbmlElement(writer, libwebm::kMkvAESSettingsCipherMode,
462 const int64_t stop_position = writer->Position();
463 if (stop_position < 0 ||
464 stop_position - payload_position != static_cast<int64_t>(payload))
470 uint64_t ContentEncAESSettings::PayloadSize() const {
472 EbmlElementSize(libwebm::kMkvAESSettingsCipherMode, cipher_mode_);
476 ///////////////////////////////////////////////////////////////
478 // ContentEncoding Class
480 ContentEncoding::ContentEncoding()
486 enc_key_id_length_(0) {}
488 ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
490 bool ContentEncoding::SetEncryptionID(const uint8_t* id, uint64_t length) {
491 if (!id || length < 1)
494 delete[] enc_key_id_;
497 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
501 memcpy(enc_key_id_, id, static_cast<size_t>(length));
502 enc_key_id_length_ = length;
507 uint64_t ContentEncoding::Size() const {
508 const uint64_t encryption_size = EncryptionSize();
509 const uint64_t encoding_size = EncodingSize(0, encryption_size);
510 const uint64_t encodings_size =
511 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
514 return encodings_size;
517 bool ContentEncoding::Write(IMkvWriter* writer) const {
518 const uint64_t encryption_size = EncryptionSize();
519 const uint64_t encoding_size = EncodingSize(0, encryption_size);
520 const uint64_t size =
521 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
524 const int64_t payload_position = writer->Position();
525 if (payload_position < 0)
528 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncoding,
531 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingOrder,
534 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope,
537 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType,
541 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption,
544 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo, enc_algo_))
546 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncKeyID, enc_key_id_,
550 if (!enc_aes_settings_.Write(writer))
553 const int64_t stop_position = writer->Position();
554 if (stop_position < 0 ||
555 stop_position - payload_position != static_cast<int64_t>(size))
561 uint64_t ContentEncoding::EncodingSize(uint64_t compresion_size,
562 uint64_t encryption_size) const {
563 // TODO(fgalligan): Add support for compression settings.
564 if (compresion_size != 0)
567 uint64_t encoding_size = 0;
569 if (encryption_size > 0) {
571 EbmlMasterElementSize(libwebm::kMkvContentEncryption, encryption_size) +
575 EbmlElementSize(libwebm::kMkvContentEncodingType, encoding_type_);
577 EbmlElementSize(libwebm::kMkvContentEncodingScope, encoding_scope_);
579 EbmlElementSize(libwebm::kMkvContentEncodingOrder, encoding_order_);
581 return encoding_size;
584 uint64_t ContentEncoding::EncryptionSize() const {
585 const uint64_t aes_size = enc_aes_settings_.Size();
587 uint64_t encryption_size = EbmlElementSize(libwebm::kMkvContentEncKeyID,
588 enc_key_id_, enc_key_id_length_);
589 encryption_size += EbmlElementSize(libwebm::kMkvContentEncAlgo, enc_algo_);
591 return encryption_size + aes_size;
594 ///////////////////////////////////////////////////////////////
598 Track::Track(unsigned int* seed)
600 codec_private_(NULL),
602 max_block_additional_id_(0),
609 default_duration_(0),
610 codec_private_length_(0),
611 content_encoding_entries_(NULL),
612 content_encoding_entries_size_(0) {}
616 delete[] codec_private_;
620 if (content_encoding_entries_) {
621 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
622 ContentEncoding* const encoding = content_encoding_entries_[i];
625 delete[] content_encoding_entries_;
629 bool Track::AddContentEncoding() {
630 const uint32_t count = content_encoding_entries_size_ + 1;
632 ContentEncoding** const content_encoding_entries =
633 new (std::nothrow) ContentEncoding*[count]; // NOLINT
634 if (!content_encoding_entries)
637 ContentEncoding* const content_encoding =
638 new (std::nothrow) ContentEncoding(); // NOLINT
639 if (!content_encoding) {
640 delete[] content_encoding_entries;
644 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
645 content_encoding_entries[i] = content_encoding_entries_[i];
648 delete[] content_encoding_entries_;
650 content_encoding_entries_ = content_encoding_entries;
651 content_encoding_entries_[content_encoding_entries_size_] = content_encoding;
652 content_encoding_entries_size_ = count;
656 ContentEncoding* Track::GetContentEncodingByIndex(uint32_t index) const {
657 if (content_encoding_entries_ == NULL)
660 if (index >= content_encoding_entries_size_)
663 return content_encoding_entries_[index];
666 uint64_t Track::PayloadSize() const {
667 uint64_t size = EbmlElementSize(libwebm::kMkvTrackNumber, number_);
668 size += EbmlElementSize(libwebm::kMkvTrackUID, uid_);
669 size += EbmlElementSize(libwebm::kMkvTrackType, type_);
671 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
673 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
674 codec_private_length_);
676 size += EbmlElementSize(libwebm::kMkvLanguage, language_);
678 size += EbmlElementSize(libwebm::kMkvName, name_);
679 if (max_block_additional_id_)
680 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
681 max_block_additional_id_);
683 size += EbmlElementSize(libwebm::kMkvCodecDelay, codec_delay_);
685 size += EbmlElementSize(libwebm::kMkvSeekPreRoll, seek_pre_roll_);
686 if (default_duration_)
687 size += EbmlElementSize(libwebm::kMkvDefaultDuration, default_duration_);
689 if (content_encoding_entries_size_ > 0) {
690 uint64_t content_encodings_size = 0;
691 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
692 ContentEncoding* const encoding = content_encoding_entries_[i];
693 content_encodings_size += encoding->Size();
696 size += EbmlMasterElementSize(libwebm::kMkvContentEncodings,
697 content_encodings_size) +
698 content_encodings_size;
704 uint64_t Track::Size() const {
705 uint64_t size = PayloadSize();
706 size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size);
710 bool Track::Write(IMkvWriter* writer) const {
714 // mandatory elements without a default value.
715 if (!type_ || !codec_id_)
718 // |size| may be bigger than what is written out in this function because
719 // derived classes may write out more data in the Track element.
720 const uint64_t payload_size = PayloadSize();
722 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTrackEntry, payload_size))
725 uint64_t size = EbmlElementSize(libwebm::kMkvTrackNumber, number_);
726 size += EbmlElementSize(libwebm::kMkvTrackUID, uid_);
727 size += EbmlElementSize(libwebm::kMkvTrackType, type_);
729 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
731 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
732 codec_private_length_);
734 size += EbmlElementSize(libwebm::kMkvLanguage, language_);
736 size += EbmlElementSize(libwebm::kMkvName, name_);
737 if (max_block_additional_id_)
738 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
739 max_block_additional_id_);
741 size += EbmlElementSize(libwebm::kMkvCodecDelay, codec_delay_);
743 size += EbmlElementSize(libwebm::kMkvSeekPreRoll, seek_pre_roll_);
744 if (default_duration_)
745 size += EbmlElementSize(libwebm::kMkvDefaultDuration, default_duration_);
747 const int64_t payload_position = writer->Position();
748 if (payload_position < 0)
751 if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber, number_))
753 if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID, uid_))
755 if (!WriteEbmlElement(writer, libwebm::kMkvTrackType, type_))
757 if (max_block_additional_id_) {
758 if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID,
759 max_block_additional_id_)) {
764 if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay, codec_delay_))
767 if (seek_pre_roll_) {
768 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll, seek_pre_roll_))
771 if (default_duration_) {
772 if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration,
777 if (!WriteEbmlElement(writer, libwebm::kMkvCodecID, codec_id_))
780 if (codec_private_) {
781 if (!WriteEbmlElement(writer, libwebm::kMkvCodecPrivate, codec_private_,
782 codec_private_length_))
786 if (!WriteEbmlElement(writer, libwebm::kMkvLanguage, language_))
790 if (!WriteEbmlElement(writer, libwebm::kMkvName, name_))
794 int64_t stop_position = writer->Position();
795 if (stop_position < 0 ||
796 stop_position - payload_position != static_cast<int64_t>(size))
799 if (content_encoding_entries_size_ > 0) {
800 uint64_t content_encodings_size = 0;
801 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
802 ContentEncoding* const encoding = content_encoding_entries_[i];
803 content_encodings_size += encoding->Size();
806 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncodings,
807 content_encodings_size))
810 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
811 ContentEncoding* const encoding = content_encoding_entries_[i];
812 if (!encoding->Write(writer))
817 stop_position = writer->Position();
818 if (stop_position < 0)
823 bool Track::SetCodecPrivate(const uint8_t* codec_private, uint64_t length) {
824 if (!codec_private || length < 1)
827 delete[] codec_private_;
830 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
834 memcpy(codec_private_, codec_private, static_cast<size_t>(length));
835 codec_private_length_ = length;
840 void Track::set_codec_id(const char* codec_id) {
844 const size_t length = strlen(codec_id) + 1;
845 codec_id_ = new (std::nothrow) char[length]; // NOLINT
848 strcpy_s(codec_id_, length, codec_id);
850 strcpy(codec_id_, codec_id);
856 // TODO(fgalligan): Vet the language parameter.
857 void Track::set_language(const char* language) {
861 const size_t length = strlen(language) + 1;
862 language_ = new (std::nothrow) char[length]; // NOLINT
865 strcpy_s(language_, length, language);
867 strcpy(language_, language);
873 void Track::set_name(const char* name) {
877 const size_t length = strlen(name) + 1;
878 name_ = new (std::nothrow) char[length]; // NOLINT
881 strcpy_s(name_, length, name);
889 ///////////////////////////////////////////////////////////////
891 // Colour and its child elements
893 uint64_t PrimaryChromaticity::PrimaryChromaticityPayloadSize(
894 libwebm::MkvId x_id, libwebm::MkvId y_id) const {
895 return EbmlElementSize(x_id, x) + EbmlElementSize(y_id, y);
898 bool PrimaryChromaticity::Write(IMkvWriter* writer, libwebm::MkvId x_id,
899 libwebm::MkvId y_id) const {
900 return WriteEbmlElement(writer, x_id, x) && WriteEbmlElement(writer, y_id, y);
903 uint64_t MasteringMetadata::MasteringMetadataSize() const {
904 uint64_t size = PayloadSize();
907 size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size);
912 bool MasteringMetadata::Write(IMkvWriter* writer) const {
913 const uint64_t size = PayloadSize();
915 // Don't write an empty element.
919 if (!WriteEbmlMasterElement(writer, libwebm::kMkvMasteringMetadata, size))
921 if (luminance_max != kValueNotPresent &&
922 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMax, luminance_max)) {
925 if (luminance_min != kValueNotPresent &&
926 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMin, luminance_min)) {
930 !r_->Write(writer, libwebm::kMkvPrimaryRChromaticityX,
931 libwebm::kMkvPrimaryRChromaticityY)) {
935 !g_->Write(writer, libwebm::kMkvPrimaryGChromaticityX,
936 libwebm::kMkvPrimaryGChromaticityY)) {
940 !b_->Write(writer, libwebm::kMkvPrimaryBChromaticityX,
941 libwebm::kMkvPrimaryBChromaticityY)) {
945 !white_point_->Write(writer, libwebm::kMkvWhitePointChromaticityX,
946 libwebm::kMkvWhitePointChromaticityY)) {
953 bool MasteringMetadata::SetChromaticity(
954 const PrimaryChromaticity* r, const PrimaryChromaticity* g,
955 const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) {
956 PrimaryChromaticityPtr r_ptr(NULL);
958 if (!CopyChromaticity(r, &r_ptr))
961 PrimaryChromaticityPtr g_ptr(NULL);
963 if (!CopyChromaticity(g, &g_ptr))
966 PrimaryChromaticityPtr b_ptr(NULL);
968 if (!CopyChromaticity(b, &b_ptr))
971 PrimaryChromaticityPtr wp_ptr(NULL);
973 if (!CopyChromaticity(white_point, &wp_ptr))
977 r_ = r_ptr.release();
978 g_ = g_ptr.release();
979 b_ = b_ptr.release();
980 white_point_ = wp_ptr.release();
984 uint64_t MasteringMetadata::PayloadSize() const {
987 if (luminance_max != kValueNotPresent)
988 size += EbmlElementSize(libwebm::kMkvLuminanceMax, luminance_max);
989 if (luminance_min != kValueNotPresent)
990 size += EbmlElementSize(libwebm::kMkvLuminanceMin, luminance_min);
993 size += r_->PrimaryChromaticityPayloadSize(
994 libwebm::kMkvPrimaryRChromaticityX, libwebm::kMkvPrimaryRChromaticityY);
997 size += g_->PrimaryChromaticityPayloadSize(
998 libwebm::kMkvPrimaryGChromaticityX, libwebm::kMkvPrimaryGChromaticityY);
1001 size += b_->PrimaryChromaticityPayloadSize(
1002 libwebm::kMkvPrimaryBChromaticityX, libwebm::kMkvPrimaryBChromaticityY);
1005 size += white_point_->PrimaryChromaticityPayloadSize(
1006 libwebm::kMkvWhitePointChromaticityX,
1007 libwebm::kMkvWhitePointChromaticityY);
1013 uint64_t Colour::ColourSize() const {
1014 uint64_t size = PayloadSize();
1017 size += EbmlMasterElementSize(libwebm::kMkvColour, size);
1022 bool Colour::Write(IMkvWriter* writer) const {
1023 const uint64_t size = PayloadSize();
1025 // Don't write an empty element.
1029 if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size))
1032 if (matrix_coefficients != kValueNotPresent &&
1033 !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients,
1034 matrix_coefficients)) {
1037 if (bits_per_channel != kValueNotPresent &&
1038 !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel,
1039 bits_per_channel)) {
1042 if (chroma_subsampling_horz != kValueNotPresent &&
1043 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz,
1044 chroma_subsampling_horz)) {
1047 if (chroma_subsampling_vert != kValueNotPresent &&
1048 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert,
1049 chroma_subsampling_vert)) {
1053 if (cb_subsampling_horz != kValueNotPresent &&
1054 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz,
1055 cb_subsampling_horz)) {
1058 if (cb_subsampling_vert != kValueNotPresent &&
1059 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert,
1060 cb_subsampling_vert)) {
1063 if (chroma_siting_horz != kValueNotPresent &&
1064 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz,
1065 chroma_siting_horz)) {
1068 if (chroma_siting_vert != kValueNotPresent &&
1069 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert,
1070 chroma_siting_vert)) {
1073 if (range != kValueNotPresent &&
1074 !WriteEbmlElement(writer, libwebm::kMkvRange, range)) {
1077 if (transfer_characteristics != kValueNotPresent &&
1078 !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics,
1079 transfer_characteristics)) {
1082 if (primaries != kValueNotPresent &&
1083 !WriteEbmlElement(writer, libwebm::kMkvPrimaries, primaries)) {
1086 if (max_cll != kValueNotPresent &&
1087 !WriteEbmlElement(writer, libwebm::kMkvMaxCLL, max_cll)) {
1090 if (max_fall != kValueNotPresent &&
1091 !WriteEbmlElement(writer, libwebm::kMkvMaxFALL, max_fall)) {
1095 if (mastering_metadata_ && !mastering_metadata_->Write(writer))
1101 bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) {
1102 std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
1106 mm_ptr->luminance_max = mastering_metadata.luminance_max;
1107 mm_ptr->luminance_min = mastering_metadata.luminance_min;
1109 if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(),
1110 mastering_metadata.b(),
1111 mastering_metadata.white_point())) {
1115 delete mastering_metadata_;
1116 mastering_metadata_ = mm_ptr.release();
1120 uint64_t Colour::PayloadSize() const {
1123 if (matrix_coefficients != kValueNotPresent)
1125 EbmlElementSize(libwebm::kMkvMatrixCoefficients, matrix_coefficients);
1126 if (bits_per_channel != kValueNotPresent)
1127 size += EbmlElementSize(libwebm::kMkvBitsPerChannel, bits_per_channel);
1128 if (chroma_subsampling_horz != kValueNotPresent)
1129 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz,
1130 chroma_subsampling_horz);
1131 if (chroma_subsampling_vert != kValueNotPresent)
1132 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert,
1133 chroma_subsampling_vert);
1134 if (cb_subsampling_horz != kValueNotPresent)
1136 EbmlElementSize(libwebm::kMkvCbSubsamplingHorz, cb_subsampling_horz);
1137 if (cb_subsampling_vert != kValueNotPresent)
1139 EbmlElementSize(libwebm::kMkvCbSubsamplingVert, cb_subsampling_vert);
1140 if (chroma_siting_horz != kValueNotPresent)
1141 size += EbmlElementSize(libwebm::kMkvChromaSitingHorz, chroma_siting_horz);
1142 if (chroma_siting_vert != kValueNotPresent)
1143 size += EbmlElementSize(libwebm::kMkvChromaSitingVert, chroma_siting_vert);
1144 if (range != kValueNotPresent)
1145 size += EbmlElementSize(libwebm::kMkvRange, range);
1146 if (transfer_characteristics != kValueNotPresent)
1147 size += EbmlElementSize(libwebm::kMkvTransferCharacteristics,
1148 transfer_characteristics);
1149 if (primaries != kValueNotPresent)
1150 size += EbmlElementSize(libwebm::kMkvPrimaries, primaries);
1151 if (max_cll != kValueNotPresent)
1152 size += EbmlElementSize(libwebm::kMkvMaxCLL, max_cll);
1153 if (max_fall != kValueNotPresent)
1154 size += EbmlElementSize(libwebm::kMkvMaxFALL, max_fall);
1156 if (mastering_metadata_)
1157 size += mastering_metadata_->MasteringMetadataSize();
1162 ///////////////////////////////////////////////////////////////
1166 VideoTrack::VideoTrack(unsigned int* seed)
1181 VideoTrack::~VideoTrack() { delete colour_; }
1183 bool VideoTrack::SetStereoMode(uint64_t stereo_mode) {
1184 if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst &&
1185 stereo_mode != kTopBottomRightIsFirst &&
1186 stereo_mode != kTopBottomLeftIsFirst &&
1187 stereo_mode != kSideBySideRightIsFirst)
1190 stereo_mode_ = stereo_mode;
1194 bool VideoTrack::SetAlphaMode(uint64_t alpha_mode) {
1195 if (alpha_mode != kNoAlpha && alpha_mode != kAlpha)
1198 alpha_mode_ = alpha_mode;
1202 uint64_t VideoTrack::PayloadSize() const {
1203 const uint64_t parent_size = Track::PayloadSize();
1205 uint64_t size = VideoPayloadSize();
1206 size += EbmlMasterElementSize(libwebm::kMkvVideo, size);
1208 return parent_size + size;
1211 bool VideoTrack::Write(IMkvWriter* writer) const {
1212 if (!Track::Write(writer))
1215 const uint64_t size = VideoPayloadSize();
1217 if (!WriteEbmlMasterElement(writer, libwebm::kMkvVideo, size))
1220 const int64_t payload_position = writer->Position();
1221 if (payload_position < 0)
1224 if (!WriteEbmlElement(writer, libwebm::kMkvPixelWidth, width_))
1226 if (!WriteEbmlElement(writer, libwebm::kMkvPixelHeight, height_))
1228 if (display_width_ > 0) {
1229 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth, display_width_))
1232 if (display_height_ > 0) {
1233 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight, display_height_))
1236 if (crop_left_ > 0) {
1237 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft, crop_left_))
1240 if (crop_right_ > 0) {
1241 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight, crop_right_))
1244 if (crop_top_ > 0) {
1245 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop, crop_top_))
1248 if (crop_bottom_ > 0) {
1249 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom, crop_bottom_))
1252 if (stereo_mode_ > kMono) {
1253 if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode, stereo_mode_))
1256 if (alpha_mode_ > kNoAlpha) {
1257 if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode, alpha_mode_))
1260 if (frame_rate_ > 0.0) {
1261 if (!WriteEbmlElement(writer, libwebm::kMkvFrameRate,
1262 static_cast<float>(frame_rate_))) {
1267 if (!colour_->Write(writer))
1271 const int64_t stop_position = writer->Position();
1272 if (stop_position < 0 ||
1273 stop_position - payload_position != static_cast<int64_t>(size)) {
1280 bool VideoTrack::SetColour(const Colour& colour) {
1281 std::auto_ptr<Colour> colour_ptr(new Colour());
1282 if (!colour_ptr.get())
1285 if (colour.mastering_metadata()) {
1286 if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata()))
1290 colour_ptr->matrix_coefficients = colour.matrix_coefficients;
1291 colour_ptr->bits_per_channel = colour.bits_per_channel;
1292 colour_ptr->chroma_subsampling_horz = colour.chroma_subsampling_horz;
1293 colour_ptr->chroma_subsampling_vert = colour.chroma_subsampling_vert;
1294 colour_ptr->cb_subsampling_horz = colour.cb_subsampling_horz;
1295 colour_ptr->cb_subsampling_vert = colour.cb_subsampling_vert;
1296 colour_ptr->chroma_siting_horz = colour.chroma_siting_horz;
1297 colour_ptr->chroma_siting_vert = colour.chroma_siting_vert;
1298 colour_ptr->range = colour.range;
1299 colour_ptr->transfer_characteristics = colour.transfer_characteristics;
1300 colour_ptr->primaries = colour.primaries;
1301 colour_ptr->max_cll = colour.max_cll;
1302 colour_ptr->max_fall = colour.max_fall;
1303 colour_ = colour_ptr.release();
1307 uint64_t VideoTrack::VideoPayloadSize() const {
1308 uint64_t size = EbmlElementSize(libwebm::kMkvPixelWidth, width_);
1309 size += EbmlElementSize(libwebm::kMkvPixelHeight, height_);
1310 if (display_width_ > 0)
1311 size += EbmlElementSize(libwebm::kMkvDisplayWidth, display_width_);
1312 if (display_height_ > 0)
1313 size += EbmlElementSize(libwebm::kMkvDisplayHeight, display_height_);
1315 size += EbmlElementSize(libwebm::kMkvPixelCropLeft, crop_left_);
1316 if (crop_right_ > 0)
1317 size += EbmlElementSize(libwebm::kMkvPixelCropRight, crop_right_);
1319 size += EbmlElementSize(libwebm::kMkvPixelCropTop, crop_top_);
1320 if (crop_bottom_ > 0)
1321 size += EbmlElementSize(libwebm::kMkvPixelCropBottom, crop_bottom_);
1322 if (stereo_mode_ > kMono)
1323 size += EbmlElementSize(libwebm::kMkvStereoMode, stereo_mode_);
1324 if (alpha_mode_ > kNoAlpha)
1325 size += EbmlElementSize(libwebm::kMkvAlphaMode, alpha_mode_);
1326 if (frame_rate_ > 0.0)
1327 size += EbmlElementSize(libwebm::kMkvFrameRate,
1328 static_cast<float>(frame_rate_));
1330 size += colour_->ColourSize();
1335 ///////////////////////////////////////////////////////////////
1339 AudioTrack::AudioTrack(unsigned int* seed)
1340 : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
1342 AudioTrack::~AudioTrack() {}
1344 uint64_t AudioTrack::PayloadSize() const {
1345 const uint64_t parent_size = Track::PayloadSize();
1347 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
1348 static_cast<float>(sample_rate_));
1349 size += EbmlElementSize(libwebm::kMkvChannels, channels_);
1351 size += EbmlElementSize(libwebm::kMkvBitDepth, bit_depth_);
1352 size += EbmlMasterElementSize(libwebm::kMkvAudio, size);
1354 return parent_size + size;
1357 bool AudioTrack::Write(IMkvWriter* writer) const {
1358 if (!Track::Write(writer))
1361 // Calculate AudioSettings size.
1362 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
1363 static_cast<float>(sample_rate_));
1364 size += EbmlElementSize(libwebm::kMkvChannels, channels_);
1366 size += EbmlElementSize(libwebm::kMkvBitDepth, bit_depth_);
1368 if (!WriteEbmlMasterElement(writer, libwebm::kMkvAudio, size))
1371 const int64_t payload_position = writer->Position();
1372 if (payload_position < 0)
1375 if (!WriteEbmlElement(writer, libwebm::kMkvSamplingFrequency,
1376 static_cast<float>(sample_rate_)))
1378 if (!WriteEbmlElement(writer, libwebm::kMkvChannels, channels_))
1381 if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth, bit_depth_))
1384 const int64_t stop_position = writer->Position();
1385 if (stop_position < 0 ||
1386 stop_position - payload_position != static_cast<int64_t>(size))
1392 ///////////////////////////////////////////////////////////////
1396 const char Tracks::kOpusCodecId[] = "A_OPUS";
1397 const char Tracks::kVorbisCodecId[] = "A_VORBIS";
1398 const char Tracks::kVp8CodecId[] = "V_VP8";
1399 const char Tracks::kVp9CodecId[] = "V_VP9";
1400 const char Tracks::kVp10CodecId[] = "V_VP10";
1403 : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {}
1406 if (track_entries_) {
1407 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1408 Track* const track = track_entries_[i];
1411 delete[] track_entries_;
1415 bool Tracks::AddTrack(Track* track, int32_t number) {
1416 if (number < 0 || wrote_tracks_)
1419 // This muxer only supports track numbers in the range [1, 126], in
1420 // order to be able (to use Matroska integer representation) to
1421 // serialize the block header (of which the track number is a part)
1422 // for a frame using exactly 4 bytes.
1427 uint32_t track_num = number;
1429 if (track_num > 0) {
1430 // Check to make sure a track does not already have |track_num|.
1431 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1432 if (track_entries_[i]->number() == track_num)
1437 const uint32_t count = track_entries_size_ + 1;
1439 Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
1443 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1444 track_entries[i] = track_entries_[i];
1447 delete[] track_entries_;
1449 // Find the lowest availible track number > 0.
1450 if (track_num == 0) {
1453 // Check to make sure a track does not already have |track_num|.
1457 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1458 if (track_entries[i]->number() == track_num) {
1466 track->set_number(track_num);
1468 track_entries_ = track_entries;
1469 track_entries_[track_entries_size_] = track;
1470 track_entries_size_ = count;
1474 const Track* Tracks::GetTrackByIndex(uint32_t index) const {
1475 if (track_entries_ == NULL)
1478 if (index >= track_entries_size_)
1481 return track_entries_[index];
1484 Track* Tracks::GetTrackByNumber(uint64_t track_number) const {
1485 const int32_t count = track_entries_size();
1486 for (int32_t i = 0; i < count; ++i) {
1487 if (track_entries_[i]->number() == track_number)
1488 return track_entries_[i];
1494 bool Tracks::TrackIsAudio(uint64_t track_number) const {
1495 const Track* const track = GetTrackByNumber(track_number);
1497 if (track->type() == kAudio)
1503 bool Tracks::TrackIsVideo(uint64_t track_number) const {
1504 const Track* const track = GetTrackByNumber(track_number);
1506 if (track->type() == kVideo)
1512 bool Tracks::Write(IMkvWriter* writer) const {
1514 const int32_t count = track_entries_size();
1515 for (int32_t i = 0; i < count; ++i) {
1516 const Track* const track = GetTrackByIndex(i);
1521 size += track->Size();
1524 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTracks, size))
1527 const int64_t payload_position = writer->Position();
1528 if (payload_position < 0)
1531 for (int32_t i = 0; i < count; ++i) {
1532 const Track* const track = GetTrackByIndex(i);
1533 if (!track->Write(writer))
1537 const int64_t stop_position = writer->Position();
1538 if (stop_position < 0 ||
1539 stop_position - payload_position != static_cast<int64_t>(size))
1542 wrote_tracks_ = true;
1546 ///////////////////////////////////////////////////////////////
1550 bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
1552 void Chapter::set_time(const Segment& segment, uint64_t start_ns,
1554 const SegmentInfo* const info = segment.GetSegmentInfo();
1555 const uint64_t timecode_scale = info->timecode_scale();
1556 start_timecode_ = start_ns / timecode_scale;
1557 end_timecode_ = end_ns / timecode_scale;
1560 bool Chapter::add_string(const char* title, const char* language,
1561 const char* country) {
1562 if (!ExpandDisplaysArray())
1565 Display& d = displays_[displays_count_++];
1568 if (!d.set_title(title))
1571 if (!d.set_language(language))
1574 if (!d.set_country(country))
1580 Chapter::Chapter() {
1581 // This ctor only constructs the object. Proper initialization is
1582 // done in Init() (called in Chapters::AddChapter()). The only
1583 // reason we bother implementing this ctor is because we had to
1584 // declare it as private (along with the dtor), in order to prevent
1585 // clients from creating Chapter instances (a privelege we grant
1586 // only to the Chapters class). Doing no initialization here also
1587 // means that creating arrays of chapter objects is more efficient,
1588 // because we only initialize each new chapter object as it becomes
1589 // active on the array.
1592 Chapter::~Chapter() {}
1594 void Chapter::Init(unsigned int* seed) {
1596 start_timecode_ = 0;
1600 displays_count_ = 0;
1601 uid_ = MakeUID(seed);
1604 void Chapter::ShallowCopy(Chapter* dst) const {
1606 dst->start_timecode_ = start_timecode_;
1607 dst->end_timecode_ = end_timecode_;
1609 dst->displays_ = displays_;
1610 dst->displays_size_ = displays_size_;
1611 dst->displays_count_ = displays_count_;
1614 void Chapter::Clear() {
1617 while (displays_count_ > 0) {
1618 Display& d = displays_[--displays_count_];
1628 bool Chapter::ExpandDisplaysArray() {
1629 if (displays_size_ > displays_count_)
1630 return true; // nothing to do yet
1632 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
1634 Display* const displays = new (std::nothrow) Display[size]; // NOLINT
1635 if (displays == NULL)
1638 for (int idx = 0; idx < displays_count_; ++idx) {
1639 displays[idx] = displays_[idx]; // shallow copy
1644 displays_ = displays;
1645 displays_size_ = size;
1650 uint64_t Chapter::WriteAtom(IMkvWriter* writer) const {
1651 uint64_t payload_size =
1652 EbmlElementSize(libwebm::kMkvChapterStringUID, id_) +
1653 EbmlElementSize(libwebm::kMkvChapterUID, uid_) +
1654 EbmlElementSize(libwebm::kMkvChapterTimeStart, start_timecode_) +
1655 EbmlElementSize(libwebm::kMkvChapterTimeEnd, end_timecode_);
1657 for (int idx = 0; idx < displays_count_; ++idx) {
1658 const Display& d = displays_[idx];
1659 payload_size += d.WriteDisplay(NULL);
1662 const uint64_t atom_size =
1663 EbmlMasterElementSize(libwebm::kMkvChapterAtom, payload_size) +
1669 const int64_t start = writer->Position();
1671 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterAtom, payload_size))
1674 if (!WriteEbmlElement(writer, libwebm::kMkvChapterStringUID, id_))
1677 if (!WriteEbmlElement(writer, libwebm::kMkvChapterUID, uid_))
1680 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart, start_timecode_))
1683 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd, end_timecode_))
1686 for (int idx = 0; idx < displays_count_; ++idx) {
1687 const Display& d = displays_[idx];
1689 if (!d.WriteDisplay(writer))
1693 const int64_t stop = writer->Position();
1695 if (stop >= start && uint64_t(stop - start) != atom_size)
1701 void Chapter::Display::Init() {
1707 void Chapter::Display::Clear() {
1708 StrCpy(NULL, &title_);
1709 StrCpy(NULL, &language_);
1710 StrCpy(NULL, &country_);
1713 bool Chapter::Display::set_title(const char* title) {
1714 return StrCpy(title, &title_);
1717 bool Chapter::Display::set_language(const char* language) {
1718 return StrCpy(language, &language_);
1721 bool Chapter::Display::set_country(const char* country) {
1722 return StrCpy(country, &country_);
1725 uint64_t Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
1726 uint64_t payload_size = EbmlElementSize(libwebm::kMkvChapString, title_);
1729 payload_size += EbmlElementSize(libwebm::kMkvChapLanguage, language_);
1732 payload_size += EbmlElementSize(libwebm::kMkvChapCountry, country_);
1734 const uint64_t display_size =
1735 EbmlMasterElementSize(libwebm::kMkvChapterDisplay, payload_size) +
1739 return display_size;
1741 const int64_t start = writer->Position();
1743 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterDisplay,
1747 if (!WriteEbmlElement(writer, libwebm::kMkvChapString, title_))
1751 if (!WriteEbmlElement(writer, libwebm::kMkvChapLanguage, language_))
1756 if (!WriteEbmlElement(writer, libwebm::kMkvChapCountry, country_))
1760 const int64_t stop = writer->Position();
1762 if (stop >= start && uint64_t(stop - start) != display_size)
1765 return display_size;
1768 ///////////////////////////////////////////////////////////////
1772 Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
1774 Chapters::~Chapters() {
1775 while (chapters_count_ > 0) {
1776 Chapter& chapter = chapters_[--chapters_count_];
1784 int Chapters::Count() const { return chapters_count_; }
1786 Chapter* Chapters::AddChapter(unsigned int* seed) {
1787 if (!ExpandChaptersArray())
1790 Chapter& chapter = chapters_[chapters_count_++];
1796 bool Chapters::Write(IMkvWriter* writer) const {
1800 const uint64_t payload_size = WriteEdition(NULL); // return size only
1802 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapters, payload_size))
1805 const int64_t start = writer->Position();
1807 if (WriteEdition(writer) == 0) // error
1810 const int64_t stop = writer->Position();
1812 if (stop >= start && uint64_t(stop - start) != payload_size)
1818 bool Chapters::ExpandChaptersArray() {
1819 if (chapters_size_ > chapters_count_)
1820 return true; // nothing to do yet
1822 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
1824 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
1825 if (chapters == NULL)
1828 for (int idx = 0; idx < chapters_count_; ++idx) {
1829 const Chapter& src = chapters_[idx];
1830 Chapter* const dst = chapters + idx;
1831 src.ShallowCopy(dst);
1836 chapters_ = chapters;
1837 chapters_size_ = size;
1842 uint64_t Chapters::WriteEdition(IMkvWriter* writer) const {
1843 uint64_t payload_size = 0;
1845 for (int idx = 0; idx < chapters_count_; ++idx) {
1846 const Chapter& chapter = chapters_[idx];
1847 payload_size += chapter.WriteAtom(NULL);
1850 const uint64_t edition_size =
1851 EbmlMasterElementSize(libwebm::kMkvEditionEntry, payload_size) +
1854 if (writer == NULL) // return size only
1855 return edition_size;
1857 const int64_t start = writer->Position();
1859 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEditionEntry, payload_size))
1862 for (int idx = 0; idx < chapters_count_; ++idx) {
1863 const Chapter& chapter = chapters_[idx];
1865 const uint64_t chapter_size = chapter.WriteAtom(writer);
1866 if (chapter_size == 0) // error
1870 const int64_t stop = writer->Position();
1872 if (stop >= start && uint64_t(stop - start) != edition_size)
1875 return edition_size;
1880 bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) {
1881 if (!ExpandSimpleTagsArray())
1884 SimpleTag& st = simple_tags_[simple_tags_count_++];
1887 if (!st.set_tag_name(tag_name))
1890 if (!st.set_tag_string(tag_string))
1897 simple_tags_ = NULL;
1898 simple_tags_size_ = 0;
1899 simple_tags_count_ = 0;
1904 void Tag::ShallowCopy(Tag* dst) const {
1905 dst->simple_tags_ = simple_tags_;
1906 dst->simple_tags_size_ = simple_tags_size_;
1907 dst->simple_tags_count_ = simple_tags_count_;
1911 while (simple_tags_count_ > 0) {
1912 SimpleTag& st = simple_tags_[--simple_tags_count_];
1916 delete[] simple_tags_;
1917 simple_tags_ = NULL;
1919 simple_tags_size_ = 0;
1922 bool Tag::ExpandSimpleTagsArray() {
1923 if (simple_tags_size_ > simple_tags_count_)
1924 return true; // nothing to do yet
1926 const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_;
1928 SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT
1929 if (simple_tags == NULL)
1932 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1933 simple_tags[idx] = simple_tags_[idx]; // shallow copy
1936 delete[] simple_tags_;
1938 simple_tags_ = simple_tags;
1939 simple_tags_size_ = size;
1944 uint64_t Tag::Write(IMkvWriter* writer) const {
1945 uint64_t payload_size = 0;
1947 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1948 const SimpleTag& st = simple_tags_[idx];
1949 payload_size += st.Write(NULL);
1952 const uint64_t tag_size =
1953 EbmlMasterElementSize(libwebm::kMkvTag, payload_size) + payload_size;
1958 const int64_t start = writer->Position();
1960 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTag, payload_size))
1963 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1964 const SimpleTag& st = simple_tags_[idx];
1966 if (!st.Write(writer))
1970 const int64_t stop = writer->Position();
1972 if (stop >= start && uint64_t(stop - start) != tag_size)
1980 void Tag::SimpleTag::Init() {
1985 void Tag::SimpleTag::Clear() {
1986 StrCpy(NULL, &tag_name_);
1987 StrCpy(NULL, &tag_string_);
1990 bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
1991 return StrCpy(tag_name, &tag_name_);
1994 bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
1995 return StrCpy(tag_string, &tag_string_);
1998 uint64_t Tag::SimpleTag::Write(IMkvWriter* writer) const {
1999 uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_);
2001 payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_);
2003 const uint64_t simple_tag_size =
2004 EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) +
2008 return simple_tag_size;
2010 const int64_t start = writer->Position();
2012 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size))
2015 if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_))
2018 if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_))
2021 const int64_t stop = writer->Position();
2023 if (stop >= start && uint64_t(stop - start) != simple_tag_size)
2026 return simple_tag_size;
2031 Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
2034 while (tags_count_ > 0) {
2035 Tag& tag = tags_[--tags_count_];
2043 int Tags::Count() const { return tags_count_; }
2045 Tag* Tags::AddTag() {
2046 if (!ExpandTagsArray())
2049 Tag& tag = tags_[tags_count_++];
2054 bool Tags::Write(IMkvWriter* writer) const {
2058 uint64_t payload_size = 0;
2060 for (int idx = 0; idx < tags_count_; ++idx) {
2061 const Tag& tag = tags_[idx];
2062 payload_size += tag.Write(NULL);
2065 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTags, payload_size))
2068 const int64_t start = writer->Position();
2070 for (int idx = 0; idx < tags_count_; ++idx) {
2071 const Tag& tag = tags_[idx];
2073 const uint64_t tag_size = tag.Write(writer);
2074 if (tag_size == 0) // error
2078 const int64_t stop = writer->Position();
2080 if (stop >= start && uint64_t(stop - start) != payload_size)
2086 bool Tags::ExpandTagsArray() {
2087 if (tags_size_ > tags_count_)
2088 return true; // nothing to do yet
2090 const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_;
2092 Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT
2096 for (int idx = 0; idx < tags_count_; ++idx) {
2097 const Tag& src = tags_[idx];
2098 Tag* const dst = tags + idx;
2099 src.ShallowCopy(dst);
2110 ///////////////////////////////////////////////////////////////
2114 Cluster::Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale,
2115 bool write_last_frame_with_duration)
2118 header_written_(false),
2120 position_for_cues_(cues_pos),
2122 timecode_(timecode),
2123 timecode_scale_(timecode_scale),
2124 write_last_frame_with_duration_(write_last_frame_with_duration),
2127 Cluster::~Cluster() {}
2129 bool Cluster::Init(IMkvWriter* ptr_writer) {
2133 writer_ = ptr_writer;
2137 bool Cluster::AddFrame(const Frame* const frame) {
2138 return QueueOrWriteFrame(frame);
2141 bool Cluster::AddFrame(const uint8_t* data, uint64_t length,
2142 uint64_t track_number, uint64_t abs_timecode,
2145 if (!frame.Init(data, length))
2147 frame.set_track_number(track_number);
2148 frame.set_timestamp(abs_timecode);
2149 frame.set_is_key(is_key);
2150 return QueueOrWriteFrame(&frame);
2153 bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
2154 const uint8_t* additional,
2155 uint64_t additional_length,
2156 uint64_t add_id, uint64_t track_number,
2157 uint64_t abs_timecode, bool is_key) {
2158 if (!additional || additional_length == 0) {
2162 if (!frame.Init(data, length) ||
2163 !frame.AddAdditionalData(additional, additional_length, add_id)) {
2166 frame.set_track_number(track_number);
2167 frame.set_timestamp(abs_timecode);
2168 frame.set_is_key(is_key);
2169 return QueueOrWriteFrame(&frame);
2172 bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
2173 int64_t discard_padding,
2174 uint64_t track_number,
2175 uint64_t abs_timecode, bool is_key) {
2177 if (!frame.Init(data, length))
2179 frame.set_discard_padding(discard_padding);
2180 frame.set_track_number(track_number);
2181 frame.set_timestamp(abs_timecode);
2182 frame.set_is_key(is_key);
2183 return QueueOrWriteFrame(&frame);
2186 bool Cluster::AddMetadata(const uint8_t* data, uint64_t length,
2187 uint64_t track_number, uint64_t abs_timecode,
2188 uint64_t duration_timecode) {
2190 if (!frame.Init(data, length))
2192 frame.set_track_number(track_number);
2193 frame.set_timestamp(abs_timecode);
2194 frame.set_duration(duration_timecode);
2195 frame.set_is_key(true); // All metadata blocks are keyframes.
2196 return QueueOrWriteFrame(&frame);
2199 void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; }
2201 bool Cluster::Finalize() {
2202 return !write_last_frame_with_duration_ && Finalize(false, 0);
2205 bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) {
2206 if (!writer_ || finalized_)
2209 if (write_last_frame_with_duration_) {
2210 // Write out held back Frames. This essentially performs a k-way merge
2211 // across all tracks in the increasing order of timestamps.
2212 while (!stored_frames_.empty()) {
2213 Frame* frame = stored_frames_.begin()->second.front();
2215 // Get the next frame to write (frame with least timestamp across all
2217 for (FrameMapIterator frames_iterator = ++stored_frames_.begin();
2218 frames_iterator != stored_frames_.end(); ++frames_iterator) {
2219 if (frames_iterator->second.front()->timestamp() < frame->timestamp()) {
2220 frame = frames_iterator->second.front();
2224 // Set the duration if it's the last frame for the track.
2225 if (set_last_frame_duration &&
2226 stored_frames_[frame->track_number()].size() == 1 &&
2227 !frame->duration_set()) {
2228 frame->set_duration(duration - frame->timestamp());
2229 if (!frame->is_key() && !frame->reference_block_timestamp_set()) {
2230 frame->set_reference_block_timestamp(
2231 last_block_timestamp_[frame->track_number()]);
2235 // Write the frame and remove it from |stored_frames_|.
2236 const bool wrote_frame = DoWriteFrame(frame);
2237 stored_frames_[frame->track_number()].pop_front();
2238 if (stored_frames_[frame->track_number()].empty()) {
2239 stored_frames_.erase(frame->track_number());
2247 if (size_position_ == -1)
2250 if (writer_->Seekable()) {
2251 const int64_t pos = writer_->Position();
2253 if (writer_->Position(size_position_))
2256 if (WriteUIntSize(writer_, payload_size(), 8))
2259 if (writer_->Position(pos))
2268 uint64_t Cluster::Size() const {
2269 const uint64_t element_size =
2270 EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) +
2272 return element_size;
2275 bool Cluster::PreWriteBlock() {
2279 if (!header_written_) {
2280 if (!WriteClusterHeader())
2287 void Cluster::PostWriteBlock(uint64_t element_size) {
2288 AddPayloadSize(element_size);
2292 int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const {
2293 const int64_t cluster_timecode = this->Cluster::timecode();
2294 const int64_t rel_timecode =
2295 static_cast<int64_t>(abs_timecode) - cluster_timecode;
2297 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
2300 return rel_timecode;
2303 bool Cluster::DoWriteFrame(const Frame* const frame) {
2304 if (!frame || !frame->IsValid())
2307 if (!PreWriteBlock())
2310 const uint64_t element_size = WriteFrame(writer_, frame, this);
2311 if (element_size == 0)
2314 PostWriteBlock(element_size);
2315 last_block_timestamp_[frame->track_number()] = frame->timestamp();
2319 bool Cluster::QueueOrWriteFrame(const Frame* const frame) {
2320 if (!frame || !frame->IsValid())
2323 // If |write_last_frame_with_duration_| is not set, then write the frame right
2325 if (!write_last_frame_with_duration_) {
2326 return DoWriteFrame(frame);
2329 // Queue the current frame.
2330 uint64_t track_number = frame->track_number();
2331 Frame* const frame_to_store = new Frame();
2332 frame_to_store->CopyFrom(*frame);
2333 stored_frames_[track_number].push_back(frame_to_store);
2335 // Iterate through all queued frames in the current track except the last one
2336 // and write it if it is okay to do so (i.e.) no other track has an held back
2337 // frame with timestamp <= the timestamp of the frame in question.
2338 std::vector<std::list<Frame*>::iterator> frames_to_erase;
2339 for (std::list<Frame *>::iterator
2340 current_track_iterator = stored_frames_[track_number].begin(),
2341 end = --stored_frames_[track_number].end();
2342 current_track_iterator != end; ++current_track_iterator) {
2343 const Frame* const frame_to_write = *current_track_iterator;
2344 bool okay_to_write = true;
2345 for (FrameMapIterator track_iterator = stored_frames_.begin();
2346 track_iterator != stored_frames_.end(); ++track_iterator) {
2347 if (track_iterator->first == track_number) {
2350 if (track_iterator->second.front()->timestamp() <
2351 frame_to_write->timestamp()) {
2352 okay_to_write = false;
2356 if (okay_to_write) {
2357 const bool wrote_frame = DoWriteFrame(frame_to_write);
2358 delete frame_to_write;
2361 frames_to_erase.push_back(current_track_iterator);
2366 for (std::vector<std::list<Frame*>::iterator>::iterator iterator =
2367 frames_to_erase.begin();
2368 iterator != frames_to_erase.end(); ++iterator) {
2369 stored_frames_[track_number].erase(*iterator);
2374 bool Cluster::WriteClusterHeader() {
2378 if (WriteID(writer_, libwebm::kMkvCluster))
2382 size_position_ = writer_->Position();
2384 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
2385 // bytes because we do not know how big our cluster will be.
2386 if (SerializeInt(writer_, kEbmlUnknownValue, 8))
2389 if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode()))
2391 AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode()));
2392 header_written_ = true;
2397 ///////////////////////////////////////////////////////////////
2401 SeekHead::SeekHead() : start_pos_(0ULL) {
2402 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2403 seek_entry_id_[i] = 0;
2404 seek_entry_pos_[i] = 0;
2408 SeekHead::~SeekHead() {}
2410 bool SeekHead::Finalize(IMkvWriter* writer) const {
2411 if (writer->Seekable()) {
2412 if (start_pos_ == -1)
2415 uint64_t payload_size = 0;
2416 uint64_t entry_size[kSeekEntryCount];
2418 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2419 if (seek_entry_id_[i] != 0) {
2420 entry_size[i] = EbmlElementSize(
2421 libwebm::kMkvSeekID, static_cast<uint64_t>(seek_entry_id_[i]));
2423 EbmlElementSize(libwebm::kMkvSeekPosition, seek_entry_pos_[i]);
2426 EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) +
2431 // No SeekHead elements
2432 if (payload_size == 0)
2435 const int64_t pos = writer->Position();
2436 if (writer->Position(start_pos_))
2439 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size))
2442 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2443 if (seek_entry_id_[i] != 0) {
2444 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i]))
2447 if (!WriteEbmlElement(writer, libwebm::kMkvSeekID,
2448 static_cast<uint64_t>(seek_entry_id_[i])))
2451 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition,
2452 seek_entry_pos_[i]))
2457 const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize();
2458 const uint64_t total_size =
2459 EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) +
2461 const int64_t size_left = total_size - (writer->Position() - start_pos_);
2463 const uint64_t bytes_written = WriteVoidElement(writer, size_left);
2467 if (writer->Position(pos))
2474 bool SeekHead::Write(IMkvWriter* writer) {
2475 const uint64_t entry_size = kSeekEntryCount * MaxEntrySize();
2476 const uint64_t size =
2477 EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size);
2479 start_pos_ = writer->Position();
2481 const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size);
2488 bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) {
2489 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2490 if (seek_entry_id_[i] == 0) {
2491 seek_entry_id_[i] = id;
2492 seek_entry_pos_[i] = pos;
2499 uint32_t SeekHead::GetId(int index) const {
2500 if (index < 0 || index >= kSeekEntryCount)
2502 return seek_entry_id_[index];
2505 uint64_t SeekHead::GetPosition(int index) const {
2506 if (index < 0 || index >= kSeekEntryCount)
2508 return seek_entry_pos_[index];
2511 bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) {
2512 if (index < 0 || index >= kSeekEntryCount)
2514 seek_entry_id_[index] = id;
2515 seek_entry_pos_[index] = position;
2519 uint64_t SeekHead::MaxEntrySize() const {
2520 const uint64_t max_entry_payload_size =
2521 EbmlElementSize(libwebm::kMkvSeekID, UINT64_C(0xffffffff)) +
2522 EbmlElementSize(libwebm::kMkvSeekPosition, UINT64_C(0xffffffffffffffff));
2523 const uint64_t max_entry_size =
2524 EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) +
2525 max_entry_payload_size;
2527 return max_entry_size;
2530 ///////////////////////////////////////////////////////////////
2532 // SegmentInfo Class
2534 SegmentInfo::SegmentInfo()
2537 timecode_scale_(1000000ULL),
2539 date_utc_(LLONG_MIN),
2540 duration_pos_(-1) {}
2542 SegmentInfo::~SegmentInfo() {
2543 delete[] muxing_app_;
2544 delete[] writing_app_;
2547 bool SegmentInfo::Init() {
2552 GetVersion(&major, &minor, &build, &revision);
2555 sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2556 minor, build, revision);
2558 snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2559 minor, build, revision);
2562 const size_t app_len = strlen(temp) + 1;
2564 delete[] muxing_app_;
2566 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
2571 strcpy_s(muxing_app_, app_len, temp);
2573 strcpy(muxing_app_, temp);
2576 set_writing_app(temp);
2582 bool SegmentInfo::Finalize(IMkvWriter* writer) const {
2586 if (duration_ > 0.0) {
2587 if (writer->Seekable()) {
2588 if (duration_pos_ == -1)
2591 const int64_t pos = writer->Position();
2593 if (writer->Position(duration_pos_))
2596 if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2597 static_cast<float>(duration_)))
2600 if (writer->Position(pos))
2608 bool SegmentInfo::Write(IMkvWriter* writer) {
2609 if (!writer || !muxing_app_ || !writing_app_)
2612 uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale, timecode_scale_);
2613 if (duration_ > 0.0)
2615 EbmlElementSize(libwebm::kMkvDuration, static_cast<float>(duration_));
2616 if (date_utc_ != LLONG_MIN)
2617 size += EbmlDateElementSize(libwebm::kMkvDateUTC);
2618 size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_);
2619 size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_);
2621 if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size))
2624 const int64_t payload_position = writer->Position();
2625 if (payload_position < 0)
2628 if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale, timecode_scale_))
2631 if (duration_ > 0.0) {
2633 duration_pos_ = writer->Position();
2635 if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2636 static_cast<float>(duration_)))
2640 if (date_utc_ != LLONG_MIN)
2641 WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_);
2643 if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_))
2645 if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_))
2648 const int64_t stop_position = writer->Position();
2649 if (stop_position < 0 ||
2650 stop_position - payload_position != static_cast<int64_t>(size))
2656 void SegmentInfo::set_muxing_app(const char* app) {
2658 const size_t length = strlen(app) + 1;
2659 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2664 strcpy_s(temp_str, length, app);
2666 strcpy(temp_str, app);
2669 delete[] muxing_app_;
2670 muxing_app_ = temp_str;
2674 void SegmentInfo::set_writing_app(const char* app) {
2676 const size_t length = strlen(app) + 1;
2677 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2682 strcpy_s(temp_str, length, app);
2684 strcpy(temp_str, app);
2687 delete[] writing_app_;
2688 writing_app_ = temp_str;
2692 ///////////////////////////////////////////////////////////////
2699 chunk_writer_cluster_(NULL),
2700 chunk_writer_cues_(NULL),
2701 chunk_writer_header_(NULL),
2703 chunking_base_name_(NULL),
2704 cluster_list_(NULL),
2705 cluster_list_capacity_(0),
2706 cluster_list_size_(0),
2707 cues_position_(kAfterClusters),
2709 force_new_cluster_(false),
2711 frames_capacity_(0),
2714 header_written_(false),
2715 last_block_duration_(0),
2717 max_cluster_duration_(kDefaultMaxClusterDuration),
2718 max_cluster_size_(0),
2720 new_cuepoint_(false),
2724 doc_type_version_(kDefaultDocTypeVersion),
2725 doc_type_version_written_(0),
2726 writer_cluster_(NULL),
2728 writer_header_(NULL) {
2729 const time_t curr_time = time(NULL);
2730 seed_ = static_cast<unsigned int>(curr_time);
2736 Segment::~Segment() {
2737 if (cluster_list_) {
2738 for (int32_t i = 0; i < cluster_list_size_; ++i) {
2739 Cluster* const cluster = cluster_list_[i];
2742 delete[] cluster_list_;
2746 for (int32_t i = 0; i < frames_size_; ++i) {
2747 Frame* const frame = frames_[i];
2753 delete[] chunk_name_;
2754 delete[] chunking_base_name_;
2756 if (chunk_writer_cluster_) {
2757 chunk_writer_cluster_->Close();
2758 delete chunk_writer_cluster_;
2760 if (chunk_writer_cues_) {
2761 chunk_writer_cues_->Close();
2762 delete chunk_writer_cues_;
2764 if (chunk_writer_header_) {
2765 chunk_writer_header_->Close();
2766 delete chunk_writer_header_;
2770 void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index,
2771 uint64_t* cues_size) {
2772 CuePoint* const cue_point = cues_.GetCueByIndex(index);
2773 if (cue_point == NULL)
2775 const uint64_t old_cue_point_size = cue_point->Size();
2776 const uint64_t cluster_pos = cue_point->cluster_pos() + diff;
2777 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
2778 // New size of the cue is computed as follows
2779 // Let a = current sum of size of all CuePoints
2780 // Let b = Increase in Cue Point's size due to this iteration
2781 // Let c = Increase in size of Cues Element's length due to this iteration
2782 // (This is computed as CodedSize(a + b) - CodedSize(a))
2783 // Let d = b + c. Now d is the |diff| passed to the next recursive call.
2784 // Let e = a + b. Now e is the |cues_size| passed to the next recursive
2786 const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size;
2787 const uint64_t cue_size_diff =
2788 GetCodedUIntSize(*cues_size + cue_point_size_diff) -
2789 GetCodedUIntSize(*cues_size);
2790 *cues_size += cue_point_size_diff;
2791 diff = cue_size_diff + cue_point_size_diff;
2793 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) {
2794 MoveCuesBeforeClustersHelper(diff, i, cues_size);
2799 void Segment::MoveCuesBeforeClusters() {
2800 const uint64_t current_cue_size = cues_.Size();
2801 uint64_t cue_size = 0;
2802 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2803 cue_size += cues_.GetCueByIndex(i)->Size();
2804 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2805 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
2807 // Adjust the Seek Entry to reflect the change in position
2808 // of Cluster and Cues
2809 int32_t cluster_index = 0;
2810 int32_t cues_index = 0;
2811 for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) {
2812 if (seek_head_.GetId(i) == libwebm::kMkvCluster)
2814 if (seek_head_.GetId(i) == libwebm::kMkvCues)
2817 seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues,
2818 seek_head_.GetPosition(cluster_index));
2819 seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster,
2820 cues_.Size() + seek_head_.GetPosition(cues_index));
2823 bool Segment::Init(IMkvWriter* ptr_writer) {
2827 writer_cluster_ = ptr_writer;
2828 writer_cues_ = ptr_writer;
2829 writer_header_ = ptr_writer;
2830 return segment_info_.Init();
2833 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
2834 IMkvWriter* writer) {
2835 if (!writer->Seekable() || chunking_)
2837 const int64_t cluster_offset =
2838 cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster);
2840 // Copy the headers.
2841 if (!ChunkedCopy(reader, writer, 0, cluster_offset))
2844 // Recompute cue positions and seek entries.
2845 MoveCuesBeforeClusters();
2847 // Write cues and seek entries.
2848 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
2849 // second time with a different writer object. But the name Finalize() doesn't
2850 // indicate something we want to call more than once. So consider renaming it
2851 // to write() or some such.
2852 if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
2855 // Copy the Clusters.
2856 if (!ChunkedCopy(reader, writer, cluster_offset,
2857 cluster_end_offset_ - cluster_offset))
2860 // Update the Segment size in case the Cues size has changed.
2861 const int64_t pos = writer->Position();
2862 const int64_t segment_size = writer->Position() - payload_pos_;
2863 if (writer->Position(size_position_) ||
2864 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
2869 bool Segment::Finalize() {
2870 if (WriteFramesAll() < 0)
2873 if (cluster_list_size_ > 0) {
2874 // Update last cluster's size
2875 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2877 // For the last frame of the last Cluster, we don't write it as a BlockGroup
2878 // with Duration unless the frame itself has duration set explicitly.
2879 if (!old_cluster || !old_cluster->Finalize(false, 0))
2883 if (mode_ == kFile) {
2884 if (chunking_ && chunk_writer_cluster_) {
2885 chunk_writer_cluster_->Close();
2889 const double duration =
2890 (static_cast<double>(last_timestamp_) + last_block_duration_) /
2891 segment_info_.timecode_scale();
2892 segment_info_.set_duration(duration);
2893 if (!segment_info_.Finalize(writer_header_))
2897 if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset()))
2901 if (!chunk_writer_cues_)
2905 if (!UpdateChunkName("cues", &name))
2908 const bool cues_open = chunk_writer_cues_->Open(name);
2914 cluster_end_offset_ = writer_cluster_->Position();
2916 // Write the seek headers and cues
2918 if (!cues_.Write(writer_cues_))
2921 if (!seek_head_.Finalize(writer_header_))
2924 if (writer_header_->Seekable()) {
2925 if (size_position_ == -1)
2928 const int64_t segment_size = MaxOffset();
2929 if (segment_size < 1)
2932 const int64_t pos = writer_header_->Position();
2933 UpdateDocTypeVersion();
2934 if (doc_type_version_ != doc_type_version_written_) {
2935 if (writer_header_->Position(0))
2938 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2940 if (writer_header_->Position() != ebml_header_size_)
2943 doc_type_version_written_ = doc_type_version_;
2946 if (writer_header_->Position(size_position_))
2949 if (WriteUIntSize(writer_header_, segment_size, 8))
2952 if (writer_header_->Position(pos))
2957 // Do not close any writers until the segment size has been written,
2958 // otherwise the size may be off.
2959 if (!chunk_writer_cues_ || !chunk_writer_header_)
2962 chunk_writer_cues_->Close();
2963 chunk_writer_header_->Close();
2970 Track* Segment::AddTrack(int32_t number) {
2971 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
2976 if (!tracks_.AddTrack(track, number)) {
2984 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
2986 Tag* Segment::AddTag() { return tags_.AddTag(); }
2988 uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) {
2989 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
2993 track->set_type(Tracks::kVideo);
2994 track->set_codec_id(Tracks::kVp8CodecId);
2995 track->set_width(width);
2996 track->set_height(height);
2998 tracks_.AddTrack(track, number);
3001 return track->number();
3004 bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) {
3005 if (cluster_list_size_ < 1)
3008 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3012 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
3016 cue->set_time(timestamp / segment_info_.timecode_scale());
3017 cue->set_block_number(cluster->blocks_added());
3018 cue->set_cluster_pos(cluster->position_for_cues());
3019 cue->set_track(track);
3020 if (!cues_.AddCue(cue))
3023 new_cuepoint_ = false;
3027 uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels,
3029 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
3033 track->set_type(Tracks::kAudio);
3034 track->set_codec_id(Tracks::kVorbisCodecId);
3035 track->set_sample_rate(sample_rate);
3036 track->set_channels(channels);
3038 tracks_.AddTrack(track, number);
3040 return track->number();
3043 bool Segment::AddFrame(const uint8_t* data, uint64_t length,
3044 uint64_t track_number, uint64_t timestamp, bool is_key) {
3049 if (!frame.Init(data, length))
3051 frame.set_track_number(track_number);
3052 frame.set_timestamp(timestamp);
3053 frame.set_is_key(is_key);
3054 return AddGenericFrame(&frame);
3057 bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
3058 const uint8_t* additional,
3059 uint64_t additional_length,
3060 uint64_t add_id, uint64_t track_number,
3061 uint64_t timestamp, bool is_key) {
3062 if (!data || !additional)
3066 if (!frame.Init(data, length) ||
3067 !frame.AddAdditionalData(additional, additional_length, add_id)) {
3070 frame.set_track_number(track_number);
3071 frame.set_timestamp(timestamp);
3072 frame.set_is_key(is_key);
3073 return AddGenericFrame(&frame);
3076 bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
3077 int64_t discard_padding,
3078 uint64_t track_number,
3079 uint64_t timestamp, bool is_key) {
3084 if (!frame.Init(data, length))
3086 frame.set_discard_padding(discard_padding);
3087 frame.set_track_number(track_number);
3088 frame.set_timestamp(timestamp);
3089 frame.set_is_key(is_key);
3090 return AddGenericFrame(&frame);
3093 bool Segment::AddMetadata(const uint8_t* data, uint64_t length,
3094 uint64_t track_number, uint64_t timestamp_ns,
3095 uint64_t duration_ns) {
3100 if (!frame.Init(data, length))
3102 frame.set_track_number(track_number);
3103 frame.set_timestamp(timestamp_ns);
3104 frame.set_duration(duration_ns);
3105 frame.set_is_key(true); // All metadata blocks are keyframes.
3106 return AddGenericFrame(&frame);
3109 bool Segment::AddGenericFrame(const Frame* frame) {
3113 if (!CheckHeaderInfo())
3116 // Check for non-monotonically increasing timestamps.
3117 if (frame->timestamp() < last_timestamp_)
3120 // Check if the track number is valid.
3121 if (!tracks_.GetTrackByNumber(frame->track_number()))
3124 if (frame->discard_padding() != 0)
3125 doc_type_version_ = 4;
3127 // If the segment has a video track hold onto audio frames to make sure the
3128 // audio that is associated with the start time of a video key-frame is
3129 // muxed into the same cluster.
3130 if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
3131 !force_new_cluster_) {
3132 Frame* const new_frame = new (std::nothrow) Frame();
3133 if (!new_frame || !new_frame->CopyFrom(*frame))
3135 return QueueFrame(new_frame);
3138 if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
3143 if (cluster_list_size_ < 1)
3146 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3150 // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
3151 // if it is not set already.
3152 bool frame_created = false;
3153 if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
3154 !frame->reference_block_timestamp_set()) {
3155 Frame* const new_frame = new (std::nothrow) Frame();
3156 if (!new_frame->CopyFrom(*frame))
3158 new_frame->set_reference_block_timestamp(
3159 last_track_timestamp_[frame->track_number() - 1]);
3161 frame_created = true;
3164 if (!cluster->AddFrame(frame))
3167 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3168 if (!AddCuePoint(frame->timestamp(), cues_track_))
3172 last_timestamp_ = frame->timestamp();
3173 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3174 last_block_duration_ = frame->duration();
3182 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
3184 void Segment::AccurateClusterDuration(bool accurate_cluster_duration) {
3185 accurate_cluster_duration_ = accurate_cluster_duration;
3188 bool Segment::SetChunking(bool chunking, const char* filename) {
3189 if (chunk_count_ > 0)
3196 // Check if we are being set to what is already set.
3197 if (chunking_ && !strcmp(filename, chunking_base_name_))
3200 const size_t name_length = strlen(filename) + 1;
3201 char* const temp = new (std::nothrow) char[name_length]; // NOLINT
3206 strcpy_s(temp, name_length, filename);
3208 strcpy(temp, filename);
3211 delete[] chunking_base_name_;
3212 chunking_base_name_ = temp;
3214 if (!UpdateChunkName("chk", &chunk_name_))
3217 if (!chunk_writer_cluster_) {
3218 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
3219 if (!chunk_writer_cluster_)
3223 if (!chunk_writer_cues_) {
3224 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
3225 if (!chunk_writer_cues_)
3229 if (!chunk_writer_header_) {
3230 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
3231 if (!chunk_writer_header_)
3235 if (!chunk_writer_cluster_->Open(chunk_name_))
3238 const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
3239 char* const header = new (std::nothrow) char[header_length]; // NOLINT
3244 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
3245 strcat_s(header, header_length, ".hdr");
3247 strcpy(header, chunking_base_name_);
3248 strcat(header, ".hdr");
3250 if (!chunk_writer_header_->Open(header)) {
3255 writer_cluster_ = chunk_writer_cluster_;
3256 writer_cues_ = chunk_writer_cues_;
3257 writer_header_ = chunk_writer_header_;
3262 chunking_ = chunking;
3267 bool Segment::CuesTrack(uint64_t track_number) {
3268 const Track* const track = GetTrackByNumber(track_number);
3272 cues_track_ = track_number;
3276 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
3278 Track* Segment::GetTrackByNumber(uint64_t track_number) const {
3279 return tracks_.GetTrackByNumber(track_number);
3282 bool Segment::WriteSegmentHeader() {
3283 UpdateDocTypeVersion();
3285 // TODO(fgalligan): Support more than one segment.
3286 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
3288 doc_type_version_written_ = doc_type_version_;
3289 ebml_header_size_ = static_cast<int32_t>(writer_header_->Position());
3291 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
3292 // will write over duration when the file is finalized.
3293 if (WriteID(writer_header_, libwebm::kMkvSegment))
3297 size_position_ = writer_header_->Position();
3299 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
3300 // bytes because if we are going to overwrite the segment size later we do
3301 // not know how big our segment will be.
3302 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
3305 payload_pos_ = writer_header_->Position();
3307 if (mode_ == kFile && writer_header_->Seekable()) {
3308 // Set the duration > 0.0 so SegmentInfo will write out the duration. When
3309 // the muxer is done writing we will set the correct duration and have
3310 // SegmentInfo upadte it.
3311 segment_info_.set_duration(1.0);
3313 if (!seek_head_.Write(writer_header_))
3317 if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset()))
3319 if (!segment_info_.Write(writer_header_))
3322 if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset()))
3324 if (!tracks_.Write(writer_header_))
3327 if (chapters_.Count() > 0) {
3328 if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset()))
3330 if (!chapters_.Write(writer_header_))
3334 if (tags_.Count() > 0) {
3335 if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset()))
3337 if (!tags_.Write(writer_header_))
3341 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
3342 if (!chunk_writer_header_)
3345 chunk_writer_header_->Close();
3348 header_written_ = true;
3353 // Here we are testing whether to create a new cluster, given a frame
3354 // having time frame_timestamp_ns.
3356 int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns,
3357 bool is_key) const {
3358 if (force_new_cluster_)
3361 // If no clusters have been created yet, then create a new cluster
3362 // and write this frame immediately, in the new cluster. This path
3363 // should only be followed once, the first time we attempt to write
3366 if (cluster_list_size_ <= 0)
3369 // There exists at least one cluster. We must compare the frame to
3370 // the last cluster, in order to determine whether the frame is
3371 // written to the existing cluster, or that a new cluster should be
3374 const uint64_t timecode_scale = segment_info_.timecode_scale();
3375 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3377 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
3378 const uint64_t last_cluster_timecode = last_cluster->timecode();
3380 // For completeness we test for the case when the frame's timecode
3381 // is less than the cluster's timecode. Although in principle that
3382 // is allowed, this muxer doesn't actually write clusters like that,
3383 // so this indicates a bug somewhere in our algorithm.
3385 if (frame_timecode < last_cluster_timecode) // should never happen
3388 // If the frame has a timestamp significantly larger than the last
3389 // cluster (in Matroska, cluster-relative timestamps are serialized
3390 // using a 16-bit signed integer), then we cannot write this frame
3391 // to that cluster, and so we must create a new cluster.
3393 const int64_t delta_timecode = frame_timecode - last_cluster_timecode;
3395 if (delta_timecode > kMaxBlockTimecode)
3398 // We decide to create a new cluster when we have a video keyframe.
3399 // This will flush queued (audio) frames, and write the keyframe
3400 // immediately, in the newly-created cluster.
3402 if (is_key && tracks_.TrackIsVideo(track_number))
3405 // Create a new cluster if we have accumulated too many frames
3406 // already, where "too many" is defined as "the total time of frames
3407 // in the cluster exceeds a threshold".
3409 const uint64_t delta_ns = delta_timecode * timecode_scale;
3411 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
3414 // This is similar to the case above, with the difference that a new
3415 // cluster is created when the size of the current cluster exceeds a
3418 const uint64_t cluster_size = last_cluster->payload_size();
3420 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
3423 // There's no need to create a new cluster, so emit this frame now.
3428 bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) {
3429 const int32_t new_size = cluster_list_size_ + 1;
3431 if (new_size > cluster_list_capacity_) {
3432 // Add more clusters.
3433 const int32_t new_capacity =
3434 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
3435 Cluster** const clusters =
3436 new (std::nothrow) Cluster*[new_capacity]; // NOLINT
3440 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3441 clusters[i] = cluster_list_[i];
3444 delete[] cluster_list_;
3446 cluster_list_ = clusters;
3447 cluster_list_capacity_ = new_capacity;
3450 if (!WriteFramesLessThan(frame_timestamp_ns))
3453 if (cluster_list_size_ > 0) {
3454 // Update old cluster's size
3455 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
3457 if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns))
3462 new_cuepoint_ = true;
3464 if (chunking_ && cluster_list_size_ > 0) {
3465 chunk_writer_cluster_->Close();
3468 if (!UpdateChunkName("chk", &chunk_name_))
3470 if (!chunk_writer_cluster_->Open(chunk_name_))
3474 const uint64_t timecode_scale = segment_info_.timecode_scale();
3475 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3477 uint64_t cluster_timecode = frame_timecode;
3479 if (frames_size_ > 0) {
3480 const Frame* const f = frames_[0]; // earliest queued frame
3481 const uint64_t ns = f->timestamp();
3482 const uint64_t tc = ns / timecode_scale;
3484 if (tc < cluster_timecode)
3485 cluster_timecode = tc;
3488 Cluster*& cluster = cluster_list_[cluster_list_size_];
3489 const int64_t offset = MaxOffset();
3490 cluster = new (std::nothrow)
3491 Cluster(cluster_timecode, offset, segment_info_.timecode_scale(),
3492 accurate_cluster_duration_);
3496 if (!cluster->Init(writer_cluster_))
3499 cluster_list_size_ = new_size;
3503 bool Segment::DoNewClusterProcessing(uint64_t track_number,
3504 uint64_t frame_timestamp_ns, bool is_key) {
3506 // Based on the characteristics of the current frame and current
3507 // cluster, decide whether to create a new cluster.
3508 const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
3509 if (result < 0) // error
3512 // Always set force_new_cluster_ to false after TestFrame.
3513 force_new_cluster_ = false;
3515 // A non-zero result means create a new cluster.
3516 if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
3519 // Write queued (audio) frames.
3520 const int frame_count = WriteFramesAll();
3521 if (frame_count < 0) // error
3524 // Write the current frame to the current cluster (if TestFrame
3525 // returns 0) or to a newly created cluster (TestFrame returns 1).
3529 // TestFrame returned 2, which means there was a large time
3530 // difference between the cluster and the frame itself. Do the
3531 // test again, comparing the frame to the new cluster.
3535 bool Segment::CheckHeaderInfo() {
3536 if (!header_written_) {
3537 if (!WriteSegmentHeader())
3540 if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset()))
3543 if (output_cues_ && cues_track_ == 0) {
3544 // Check for a video track
3545 for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) {
3546 const Track* const track = tracks_.GetTrackByIndex(i);
3550 if (tracks_.TrackIsVideo(track->number())) {
3551 cues_track_ = track->number();
3556 // Set first track found
3557 if (cues_track_ == 0) {
3558 const Track* const track = tracks_.GetTrackByIndex(0);
3562 cues_track_ = track->number();
3569 void Segment::UpdateDocTypeVersion() {
3570 for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) {
3571 const Track* track = tracks_.GetTrackByIndex(index);
3574 if ((track->codec_delay() || track->seek_pre_roll()) &&
3575 doc_type_version_ < 4) {
3576 doc_type_version_ = 4;
3582 bool Segment::UpdateChunkName(const char* ext, char** name) const {
3588 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3590 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3593 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
3594 char* const str = new (std::nothrow) char[length]; // NOLINT
3599 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
3600 strcat_s(str, length, ext_chk);
3602 strcpy(str, chunking_base_name_);
3603 strcat(str, ext_chk);
3612 int64_t Segment::MaxOffset() {
3613 if (!writer_header_)
3616 int64_t offset = writer_header_->Position() - payload_pos_;
3619 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3620 Cluster* const cluster = cluster_list_[i];
3621 offset += cluster->Size();
3625 offset += writer_cues_->Position();
3631 bool Segment::QueueFrame(Frame* frame) {
3632 const int32_t new_size = frames_size_ + 1;
3634 if (new_size > frames_capacity_) {
3636 const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
3638 if (new_capacity < 1)
3641 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
3645 for (int32_t i = 0; i < frames_size_; ++i) {
3646 frames[i] = frames_[i];
3651 frames_capacity_ = new_capacity;
3654 frames_[frames_size_++] = frame;
3659 int Segment::WriteFramesAll() {
3660 if (frames_ == NULL)
3663 if (cluster_list_size_ < 1)
3666 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3671 for (int32_t i = 0; i < frames_size_; ++i) {
3672 Frame*& frame = frames_[i];
3673 // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
3674 // places where |doc_type_version_| needs to be updated.
3675 if (frame->discard_padding() != 0)
3676 doc_type_version_ = 4;
3677 if (!cluster->AddFrame(frame))
3680 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3681 if (!AddCuePoint(frame->timestamp(), cues_track_))
3685 if (frame->timestamp() > last_timestamp_) {
3686 last_timestamp_ = frame->timestamp();
3687 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3694 const int result = frames_size_;
3700 bool Segment::WriteFramesLessThan(uint64_t timestamp) {
3701 // Check |cluster_list_size_| to see if this is the first cluster. If it is
3702 // the first cluster the audio frames that are less than the first video
3703 // timesatmp will be written in a later step.
3704 if (frames_size_ > 0 && cluster_list_size_ > 0) {
3708 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3712 int32_t shift_left = 0;
3714 // TODO(fgalligan): Change this to use the durations of frames instead of
3715 // the next frame's start time if the duration is accurate.
3716 for (int32_t i = 1; i < frames_size_; ++i) {
3717 const Frame* const frame_curr = frames_[i];
3719 if (frame_curr->timestamp() > timestamp)
3722 const Frame* const frame_prev = frames_[i - 1];
3723 if (frame_prev->discard_padding() != 0)
3724 doc_type_version_ = 4;
3725 if (!cluster->AddFrame(frame_prev))
3728 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
3729 if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
3734 if (frame_prev->timestamp() > last_timestamp_) {
3735 last_timestamp_ = frame_prev->timestamp();
3736 last_track_timestamp_[frame_prev->track_number() - 1] =
3737 frame_prev->timestamp();
3743 if (shift_left > 0) {
3744 if (shift_left >= frames_size_)
3747 const int32_t new_frames_size = frames_size_ - shift_left;
3748 for (int32_t i = 0; i < new_frames_size; ++i) {
3749 frames_[i] = frames_[i + shift_left];
3752 frames_size_ = new_frames_size;
3759 } // namespace mkvmuxer