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, bool fixed_size_timecode)
2118 fixed_size_timecode_(fixed_size_timecode),
2119 header_written_(false),
2121 position_for_cues_(cues_pos),
2123 timecode_(timecode),
2124 timecode_scale_(timecode_scale),
2125 write_last_frame_with_duration_(write_last_frame_with_duration),
2128 Cluster::~Cluster() {}
2130 bool Cluster::Init(IMkvWriter* ptr_writer) {
2134 writer_ = ptr_writer;
2138 bool Cluster::AddFrame(const Frame* const frame) {
2139 return QueueOrWriteFrame(frame);
2142 bool Cluster::AddFrame(const uint8_t* data, uint64_t length,
2143 uint64_t track_number, uint64_t abs_timecode,
2146 if (!frame.Init(data, length))
2148 frame.set_track_number(track_number);
2149 frame.set_timestamp(abs_timecode);
2150 frame.set_is_key(is_key);
2151 return QueueOrWriteFrame(&frame);
2154 bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
2155 const uint8_t* additional,
2156 uint64_t additional_length,
2157 uint64_t add_id, uint64_t track_number,
2158 uint64_t abs_timecode, bool is_key) {
2159 if (!additional || additional_length == 0) {
2163 if (!frame.Init(data, length) ||
2164 !frame.AddAdditionalData(additional, additional_length, add_id)) {
2167 frame.set_track_number(track_number);
2168 frame.set_timestamp(abs_timecode);
2169 frame.set_is_key(is_key);
2170 return QueueOrWriteFrame(&frame);
2173 bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
2174 int64_t discard_padding,
2175 uint64_t track_number,
2176 uint64_t abs_timecode, bool is_key) {
2178 if (!frame.Init(data, length))
2180 frame.set_discard_padding(discard_padding);
2181 frame.set_track_number(track_number);
2182 frame.set_timestamp(abs_timecode);
2183 frame.set_is_key(is_key);
2184 return QueueOrWriteFrame(&frame);
2187 bool Cluster::AddMetadata(const uint8_t* data, uint64_t length,
2188 uint64_t track_number, uint64_t abs_timecode,
2189 uint64_t duration_timecode) {
2191 if (!frame.Init(data, length))
2193 frame.set_track_number(track_number);
2194 frame.set_timestamp(abs_timecode);
2195 frame.set_duration(duration_timecode);
2196 frame.set_is_key(true); // All metadata blocks are keyframes.
2197 return QueueOrWriteFrame(&frame);
2200 void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; }
2202 bool Cluster::Finalize() {
2203 return !write_last_frame_with_duration_ && Finalize(false, 0);
2206 bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) {
2207 if (!writer_ || finalized_)
2210 if (write_last_frame_with_duration_) {
2211 // Write out held back Frames. This essentially performs a k-way merge
2212 // across all tracks in the increasing order of timestamps.
2213 while (!stored_frames_.empty()) {
2214 Frame* frame = stored_frames_.begin()->second.front();
2216 // Get the next frame to write (frame with least timestamp across all
2218 for (FrameMapIterator frames_iterator = ++stored_frames_.begin();
2219 frames_iterator != stored_frames_.end(); ++frames_iterator) {
2220 if (frames_iterator->second.front()->timestamp() < frame->timestamp()) {
2221 frame = frames_iterator->second.front();
2225 // Set the duration if it's the last frame for the track.
2226 if (set_last_frame_duration &&
2227 stored_frames_[frame->track_number()].size() == 1 &&
2228 !frame->duration_set()) {
2229 frame->set_duration(duration - frame->timestamp());
2230 if (!frame->is_key() && !frame->reference_block_timestamp_set()) {
2231 frame->set_reference_block_timestamp(
2232 last_block_timestamp_[frame->track_number()]);
2236 // Write the frame and remove it from |stored_frames_|.
2237 const bool wrote_frame = DoWriteFrame(frame);
2238 stored_frames_[frame->track_number()].pop_front();
2239 if (stored_frames_[frame->track_number()].empty()) {
2240 stored_frames_.erase(frame->track_number());
2248 if (size_position_ == -1)
2251 if (writer_->Seekable()) {
2252 const int64_t pos = writer_->Position();
2254 if (writer_->Position(size_position_))
2257 if (WriteUIntSize(writer_, payload_size(), 8))
2260 if (writer_->Position(pos))
2269 uint64_t Cluster::Size() const {
2270 const uint64_t element_size =
2271 EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) +
2273 return element_size;
2276 bool Cluster::PreWriteBlock() {
2280 if (!header_written_) {
2281 if (!WriteClusterHeader())
2288 void Cluster::PostWriteBlock(uint64_t element_size) {
2289 AddPayloadSize(element_size);
2293 int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const {
2294 const int64_t cluster_timecode = this->Cluster::timecode();
2295 const int64_t rel_timecode =
2296 static_cast<int64_t>(abs_timecode) - cluster_timecode;
2298 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
2301 return rel_timecode;
2304 bool Cluster::DoWriteFrame(const Frame* const frame) {
2305 if (!frame || !frame->IsValid())
2308 if (!PreWriteBlock())
2311 const uint64_t element_size = WriteFrame(writer_, frame, this);
2312 if (element_size == 0)
2315 PostWriteBlock(element_size);
2316 last_block_timestamp_[frame->track_number()] = frame->timestamp();
2320 bool Cluster::QueueOrWriteFrame(const Frame* const frame) {
2321 if (!frame || !frame->IsValid())
2324 // If |write_last_frame_with_duration_| is not set, then write the frame right
2326 if (!write_last_frame_with_duration_) {
2327 return DoWriteFrame(frame);
2330 // Queue the current frame.
2331 uint64_t track_number = frame->track_number();
2332 Frame* const frame_to_store = new Frame();
2333 frame_to_store->CopyFrom(*frame);
2334 stored_frames_[track_number].push_back(frame_to_store);
2336 // Iterate through all queued frames in the current track except the last one
2337 // and write it if it is okay to do so (i.e.) no other track has an held back
2338 // frame with timestamp <= the timestamp of the frame in question.
2339 std::vector<std::list<Frame*>::iterator> frames_to_erase;
2340 for (std::list<Frame *>::iterator
2341 current_track_iterator = stored_frames_[track_number].begin(),
2342 end = --stored_frames_[track_number].end();
2343 current_track_iterator != end; ++current_track_iterator) {
2344 const Frame* const frame_to_write = *current_track_iterator;
2345 bool okay_to_write = true;
2346 for (FrameMapIterator track_iterator = stored_frames_.begin();
2347 track_iterator != stored_frames_.end(); ++track_iterator) {
2348 if (track_iterator->first == track_number) {
2351 if (track_iterator->second.front()->timestamp() <
2352 frame_to_write->timestamp()) {
2353 okay_to_write = false;
2357 if (okay_to_write) {
2358 const bool wrote_frame = DoWriteFrame(frame_to_write);
2359 delete frame_to_write;
2362 frames_to_erase.push_back(current_track_iterator);
2367 for (std::vector<std::list<Frame*>::iterator>::iterator iterator =
2368 frames_to_erase.begin();
2369 iterator != frames_to_erase.end(); ++iterator) {
2370 stored_frames_[track_number].erase(*iterator);
2375 bool Cluster::WriteClusterHeader() {
2379 if (WriteID(writer_, libwebm::kMkvCluster))
2383 size_position_ = writer_->Position();
2385 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
2386 // bytes because we do not know how big our cluster will be.
2387 if (SerializeInt(writer_, kEbmlUnknownValue, 8))
2390 if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode(),
2391 fixed_size_timecode_ ? 8 : 0)) {
2394 AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode(),
2395 fixed_size_timecode_ ? 8 : 0));
2396 header_written_ = true;
2401 ///////////////////////////////////////////////////////////////
2405 SeekHead::SeekHead() : start_pos_(0ULL) {
2406 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2407 seek_entry_id_[i] = 0;
2408 seek_entry_pos_[i] = 0;
2412 SeekHead::~SeekHead() {}
2414 bool SeekHead::Finalize(IMkvWriter* writer) const {
2415 if (writer->Seekable()) {
2416 if (start_pos_ == -1)
2419 uint64_t payload_size = 0;
2420 uint64_t entry_size[kSeekEntryCount];
2422 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2423 if (seek_entry_id_[i] != 0) {
2424 entry_size[i] = EbmlElementSize(
2425 libwebm::kMkvSeekID, static_cast<uint64_t>(seek_entry_id_[i]));
2427 EbmlElementSize(libwebm::kMkvSeekPosition, seek_entry_pos_[i]);
2430 EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) +
2435 // No SeekHead elements
2436 if (payload_size == 0)
2439 const int64_t pos = writer->Position();
2440 if (writer->Position(start_pos_))
2443 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size))
2446 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2447 if (seek_entry_id_[i] != 0) {
2448 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i]))
2451 if (!WriteEbmlElement(writer, libwebm::kMkvSeekID,
2452 static_cast<uint64_t>(seek_entry_id_[i])))
2455 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition,
2456 seek_entry_pos_[i]))
2461 const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize();
2462 const uint64_t total_size =
2463 EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) +
2465 const int64_t size_left = total_size - (writer->Position() - start_pos_);
2467 const uint64_t bytes_written = WriteVoidElement(writer, size_left);
2471 if (writer->Position(pos))
2478 bool SeekHead::Write(IMkvWriter* writer) {
2479 const uint64_t entry_size = kSeekEntryCount * MaxEntrySize();
2480 const uint64_t size =
2481 EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size);
2483 start_pos_ = writer->Position();
2485 const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size);
2492 bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) {
2493 for (int32_t i = 0; i < kSeekEntryCount; ++i) {
2494 if (seek_entry_id_[i] == 0) {
2495 seek_entry_id_[i] = id;
2496 seek_entry_pos_[i] = pos;
2503 uint32_t SeekHead::GetId(int index) const {
2504 if (index < 0 || index >= kSeekEntryCount)
2506 return seek_entry_id_[index];
2509 uint64_t SeekHead::GetPosition(int index) const {
2510 if (index < 0 || index >= kSeekEntryCount)
2512 return seek_entry_pos_[index];
2515 bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) {
2516 if (index < 0 || index >= kSeekEntryCount)
2518 seek_entry_id_[index] = id;
2519 seek_entry_pos_[index] = position;
2523 uint64_t SeekHead::MaxEntrySize() const {
2524 const uint64_t max_entry_payload_size =
2525 EbmlElementSize(libwebm::kMkvSeekID, UINT64_C(0xffffffff)) +
2526 EbmlElementSize(libwebm::kMkvSeekPosition, UINT64_C(0xffffffffffffffff));
2527 const uint64_t max_entry_size =
2528 EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) +
2529 max_entry_payload_size;
2531 return max_entry_size;
2534 ///////////////////////////////////////////////////////////////
2536 // SegmentInfo Class
2538 SegmentInfo::SegmentInfo()
2541 timecode_scale_(1000000ULL),
2543 date_utc_(LLONG_MIN),
2544 duration_pos_(-1) {}
2546 SegmentInfo::~SegmentInfo() {
2547 delete[] muxing_app_;
2548 delete[] writing_app_;
2551 bool SegmentInfo::Init() {
2556 GetVersion(&major, &minor, &build, &revision);
2559 sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2560 minor, build, revision);
2562 snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2563 minor, build, revision);
2566 const size_t app_len = strlen(temp) + 1;
2568 delete[] muxing_app_;
2570 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
2575 strcpy_s(muxing_app_, app_len, temp);
2577 strcpy(muxing_app_, temp);
2580 set_writing_app(temp);
2586 bool SegmentInfo::Finalize(IMkvWriter* writer) const {
2590 if (duration_ > 0.0) {
2591 if (writer->Seekable()) {
2592 if (duration_pos_ == -1)
2595 const int64_t pos = writer->Position();
2597 if (writer->Position(duration_pos_))
2600 if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2601 static_cast<float>(duration_)))
2604 if (writer->Position(pos))
2612 bool SegmentInfo::Write(IMkvWriter* writer) {
2613 if (!writer || !muxing_app_ || !writing_app_)
2616 uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale, timecode_scale_);
2617 if (duration_ > 0.0)
2619 EbmlElementSize(libwebm::kMkvDuration, static_cast<float>(duration_));
2620 if (date_utc_ != LLONG_MIN)
2621 size += EbmlDateElementSize(libwebm::kMkvDateUTC);
2622 size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_);
2623 size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_);
2625 if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size))
2628 const int64_t payload_position = writer->Position();
2629 if (payload_position < 0)
2632 if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale, timecode_scale_))
2635 if (duration_ > 0.0) {
2637 duration_pos_ = writer->Position();
2639 if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
2640 static_cast<float>(duration_)))
2644 if (date_utc_ != LLONG_MIN)
2645 WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_);
2647 if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_))
2649 if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_))
2652 const int64_t stop_position = writer->Position();
2653 if (stop_position < 0 ||
2654 stop_position - payload_position != static_cast<int64_t>(size))
2660 void SegmentInfo::set_muxing_app(const char* app) {
2662 const size_t length = strlen(app) + 1;
2663 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2668 strcpy_s(temp_str, length, app);
2670 strcpy(temp_str, app);
2673 delete[] muxing_app_;
2674 muxing_app_ = temp_str;
2678 void SegmentInfo::set_writing_app(const char* app) {
2680 const size_t length = strlen(app) + 1;
2681 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2686 strcpy_s(temp_str, length, app);
2688 strcpy(temp_str, app);
2691 delete[] writing_app_;
2692 writing_app_ = temp_str;
2696 ///////////////////////////////////////////////////////////////
2703 chunk_writer_cluster_(NULL),
2704 chunk_writer_cues_(NULL),
2705 chunk_writer_header_(NULL),
2707 chunking_base_name_(NULL),
2708 cluster_list_(NULL),
2709 cluster_list_capacity_(0),
2710 cluster_list_size_(0),
2711 cues_position_(kAfterClusters),
2713 force_new_cluster_(false),
2715 frames_capacity_(0),
2718 header_written_(false),
2719 last_block_duration_(0),
2721 max_cluster_duration_(kDefaultMaxClusterDuration),
2722 max_cluster_size_(0),
2724 new_cuepoint_(false),
2726 accurate_cluster_duration_(false),
2727 fixed_size_cluster_timecode_(false),
2730 doc_type_version_(kDefaultDocTypeVersion),
2731 doc_type_version_written_(0),
2732 writer_cluster_(NULL),
2734 writer_header_(NULL) {
2735 const time_t curr_time = time(NULL);
2736 seed_ = static_cast<unsigned int>(curr_time);
2742 Segment::~Segment() {
2743 if (cluster_list_) {
2744 for (int32_t i = 0; i < cluster_list_size_; ++i) {
2745 Cluster* const cluster = cluster_list_[i];
2748 delete[] cluster_list_;
2752 for (int32_t i = 0; i < frames_size_; ++i) {
2753 Frame* const frame = frames_[i];
2759 delete[] chunk_name_;
2760 delete[] chunking_base_name_;
2762 if (chunk_writer_cluster_) {
2763 chunk_writer_cluster_->Close();
2764 delete chunk_writer_cluster_;
2766 if (chunk_writer_cues_) {
2767 chunk_writer_cues_->Close();
2768 delete chunk_writer_cues_;
2770 if (chunk_writer_header_) {
2771 chunk_writer_header_->Close();
2772 delete chunk_writer_header_;
2776 void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index,
2777 uint64_t* cues_size) {
2778 CuePoint* const cue_point = cues_.GetCueByIndex(index);
2779 if (cue_point == NULL)
2781 const uint64_t old_cue_point_size = cue_point->Size();
2782 const uint64_t cluster_pos = cue_point->cluster_pos() + diff;
2783 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
2784 // New size of the cue is computed as follows
2785 // Let a = current sum of size of all CuePoints
2786 // Let b = Increase in Cue Point's size due to this iteration
2787 // Let c = Increase in size of Cues Element's length due to this iteration
2788 // (This is computed as CodedSize(a + b) - CodedSize(a))
2789 // Let d = b + c. Now d is the |diff| passed to the next recursive call.
2790 // Let e = a + b. Now e is the |cues_size| passed to the next recursive
2792 const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size;
2793 const uint64_t cue_size_diff =
2794 GetCodedUIntSize(*cues_size + cue_point_size_diff) -
2795 GetCodedUIntSize(*cues_size);
2796 *cues_size += cue_point_size_diff;
2797 diff = cue_size_diff + cue_point_size_diff;
2799 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) {
2800 MoveCuesBeforeClustersHelper(diff, i, cues_size);
2805 void Segment::MoveCuesBeforeClusters() {
2806 const uint64_t current_cue_size = cues_.Size();
2807 uint64_t cue_size = 0;
2808 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2809 cue_size += cues_.GetCueByIndex(i)->Size();
2810 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2811 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
2813 // Adjust the Seek Entry to reflect the change in position
2814 // of Cluster and Cues
2815 int32_t cluster_index = 0;
2816 int32_t cues_index = 0;
2817 for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) {
2818 if (seek_head_.GetId(i) == libwebm::kMkvCluster)
2820 if (seek_head_.GetId(i) == libwebm::kMkvCues)
2823 seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues,
2824 seek_head_.GetPosition(cluster_index));
2825 seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster,
2826 cues_.Size() + seek_head_.GetPosition(cues_index));
2829 bool Segment::Init(IMkvWriter* ptr_writer) {
2833 writer_cluster_ = ptr_writer;
2834 writer_cues_ = ptr_writer;
2835 writer_header_ = ptr_writer;
2836 return segment_info_.Init();
2839 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
2840 IMkvWriter* writer) {
2841 if (!writer->Seekable() || chunking_)
2843 const int64_t cluster_offset =
2844 cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster);
2846 // Copy the headers.
2847 if (!ChunkedCopy(reader, writer, 0, cluster_offset))
2850 // Recompute cue positions and seek entries.
2851 MoveCuesBeforeClusters();
2853 // Write cues and seek entries.
2854 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
2855 // second time with a different writer object. But the name Finalize() doesn't
2856 // indicate something we want to call more than once. So consider renaming it
2857 // to write() or some such.
2858 if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
2861 // Copy the Clusters.
2862 if (!ChunkedCopy(reader, writer, cluster_offset,
2863 cluster_end_offset_ - cluster_offset))
2866 // Update the Segment size in case the Cues size has changed.
2867 const int64_t pos = writer->Position();
2868 const int64_t segment_size = writer->Position() - payload_pos_;
2869 if (writer->Position(size_position_) ||
2870 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
2875 bool Segment::Finalize() {
2876 if (WriteFramesAll() < 0)
2879 if (cluster_list_size_ > 0) {
2880 // Update last cluster's size
2881 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2883 // For the last frame of the last Cluster, we don't write it as a BlockGroup
2884 // with Duration unless the frame itself has duration set explicitly.
2885 if (!old_cluster || !old_cluster->Finalize(false, 0))
2889 if (mode_ == kFile) {
2890 if (chunking_ && chunk_writer_cluster_) {
2891 chunk_writer_cluster_->Close();
2895 const double duration =
2896 (static_cast<double>(last_timestamp_) + last_block_duration_) /
2897 segment_info_.timecode_scale();
2898 segment_info_.set_duration(duration);
2899 if (!segment_info_.Finalize(writer_header_))
2903 if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset()))
2907 if (!chunk_writer_cues_)
2911 if (!UpdateChunkName("cues", &name))
2914 const bool cues_open = chunk_writer_cues_->Open(name);
2920 cluster_end_offset_ = writer_cluster_->Position();
2922 // Write the seek headers and cues
2924 if (!cues_.Write(writer_cues_))
2927 if (!seek_head_.Finalize(writer_header_))
2930 if (writer_header_->Seekable()) {
2931 if (size_position_ == -1)
2934 const int64_t segment_size = MaxOffset();
2935 if (segment_size < 1)
2938 const int64_t pos = writer_header_->Position();
2939 UpdateDocTypeVersion();
2940 if (doc_type_version_ != doc_type_version_written_) {
2941 if (writer_header_->Position(0))
2944 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2946 if (writer_header_->Position() != ebml_header_size_)
2949 doc_type_version_written_ = doc_type_version_;
2952 if (writer_header_->Position(size_position_))
2955 if (WriteUIntSize(writer_header_, segment_size, 8))
2958 if (writer_header_->Position(pos))
2963 // Do not close any writers until the segment size has been written,
2964 // otherwise the size may be off.
2965 if (!chunk_writer_cues_ || !chunk_writer_header_)
2968 chunk_writer_cues_->Close();
2969 chunk_writer_header_->Close();
2976 Track* Segment::AddTrack(int32_t number) {
2977 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
2982 if (!tracks_.AddTrack(track, number)) {
2990 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
2992 Tag* Segment::AddTag() { return tags_.AddTag(); }
2994 uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) {
2995 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
2999 track->set_type(Tracks::kVideo);
3000 track->set_codec_id(Tracks::kVp8CodecId);
3001 track->set_width(width);
3002 track->set_height(height);
3004 tracks_.AddTrack(track, number);
3007 return track->number();
3010 bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) {
3011 if (cluster_list_size_ < 1)
3014 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3018 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
3022 cue->set_time(timestamp / segment_info_.timecode_scale());
3023 cue->set_block_number(cluster->blocks_added());
3024 cue->set_cluster_pos(cluster->position_for_cues());
3025 cue->set_track(track);
3026 if (!cues_.AddCue(cue))
3029 new_cuepoint_ = false;
3033 uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels,
3035 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
3039 track->set_type(Tracks::kAudio);
3040 track->set_codec_id(Tracks::kVorbisCodecId);
3041 track->set_sample_rate(sample_rate);
3042 track->set_channels(channels);
3044 tracks_.AddTrack(track, number);
3046 return track->number();
3049 bool Segment::AddFrame(const uint8_t* data, uint64_t length,
3050 uint64_t track_number, uint64_t timestamp, bool is_key) {
3055 if (!frame.Init(data, length))
3057 frame.set_track_number(track_number);
3058 frame.set_timestamp(timestamp);
3059 frame.set_is_key(is_key);
3060 return AddGenericFrame(&frame);
3063 bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
3064 const uint8_t* additional,
3065 uint64_t additional_length,
3066 uint64_t add_id, uint64_t track_number,
3067 uint64_t timestamp, bool is_key) {
3068 if (!data || !additional)
3072 if (!frame.Init(data, length) ||
3073 !frame.AddAdditionalData(additional, additional_length, add_id)) {
3076 frame.set_track_number(track_number);
3077 frame.set_timestamp(timestamp);
3078 frame.set_is_key(is_key);
3079 return AddGenericFrame(&frame);
3082 bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
3083 int64_t discard_padding,
3084 uint64_t track_number,
3085 uint64_t timestamp, bool is_key) {
3090 if (!frame.Init(data, length))
3092 frame.set_discard_padding(discard_padding);
3093 frame.set_track_number(track_number);
3094 frame.set_timestamp(timestamp);
3095 frame.set_is_key(is_key);
3096 return AddGenericFrame(&frame);
3099 bool Segment::AddMetadata(const uint8_t* data, uint64_t length,
3100 uint64_t track_number, uint64_t timestamp_ns,
3101 uint64_t duration_ns) {
3106 if (!frame.Init(data, length))
3108 frame.set_track_number(track_number);
3109 frame.set_timestamp(timestamp_ns);
3110 frame.set_duration(duration_ns);
3111 frame.set_is_key(true); // All metadata blocks are keyframes.
3112 return AddGenericFrame(&frame);
3115 bool Segment::AddGenericFrame(const Frame* frame) {
3119 if (!CheckHeaderInfo())
3122 // Check for non-monotonically increasing timestamps.
3123 if (frame->timestamp() < last_timestamp_)
3126 // Check if the track number is valid.
3127 if (!tracks_.GetTrackByNumber(frame->track_number()))
3130 if (frame->discard_padding() != 0)
3131 doc_type_version_ = 4;
3133 // If the segment has a video track hold onto audio frames to make sure the
3134 // audio that is associated with the start time of a video key-frame is
3135 // muxed into the same cluster.
3136 if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
3137 !force_new_cluster_) {
3138 Frame* const new_frame = new (std::nothrow) Frame();
3139 if (!new_frame || !new_frame->CopyFrom(*frame))
3141 return QueueFrame(new_frame);
3144 if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
3149 if (cluster_list_size_ < 1)
3152 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3156 // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
3157 // if it is not set already.
3158 bool frame_created = false;
3159 if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
3160 !frame->reference_block_timestamp_set()) {
3161 Frame* const new_frame = new (std::nothrow) Frame();
3162 if (!new_frame->CopyFrom(*frame))
3164 new_frame->set_reference_block_timestamp(
3165 last_track_timestamp_[frame->track_number() - 1]);
3167 frame_created = true;
3170 if (!cluster->AddFrame(frame))
3173 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3174 if (!AddCuePoint(frame->timestamp(), cues_track_))
3178 last_timestamp_ = frame->timestamp();
3179 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3180 last_block_duration_ = frame->duration();
3188 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
3190 void Segment::AccurateClusterDuration(bool accurate_cluster_duration) {
3191 accurate_cluster_duration_ = accurate_cluster_duration;
3194 void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) {
3195 fixed_size_cluster_timecode_ = fixed_size_cluster_timecode;
3198 bool Segment::SetChunking(bool chunking, const char* filename) {
3199 if (chunk_count_ > 0)
3206 // Check if we are being set to what is already set.
3207 if (chunking_ && !strcmp(filename, chunking_base_name_))
3210 const size_t name_length = strlen(filename) + 1;
3211 char* const temp = new (std::nothrow) char[name_length]; // NOLINT
3216 strcpy_s(temp, name_length, filename);
3218 strcpy(temp, filename);
3221 delete[] chunking_base_name_;
3222 chunking_base_name_ = temp;
3224 if (!UpdateChunkName("chk", &chunk_name_))
3227 if (!chunk_writer_cluster_) {
3228 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
3229 if (!chunk_writer_cluster_)
3233 if (!chunk_writer_cues_) {
3234 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
3235 if (!chunk_writer_cues_)
3239 if (!chunk_writer_header_) {
3240 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
3241 if (!chunk_writer_header_)
3245 if (!chunk_writer_cluster_->Open(chunk_name_))
3248 const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
3249 char* const header = new (std::nothrow) char[header_length]; // NOLINT
3254 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
3255 strcat_s(header, header_length, ".hdr");
3257 strcpy(header, chunking_base_name_);
3258 strcat(header, ".hdr");
3260 if (!chunk_writer_header_->Open(header)) {
3265 writer_cluster_ = chunk_writer_cluster_;
3266 writer_cues_ = chunk_writer_cues_;
3267 writer_header_ = chunk_writer_header_;
3272 chunking_ = chunking;
3277 bool Segment::CuesTrack(uint64_t track_number) {
3278 const Track* const track = GetTrackByNumber(track_number);
3282 cues_track_ = track_number;
3286 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
3288 Track* Segment::GetTrackByNumber(uint64_t track_number) const {
3289 return tracks_.GetTrackByNumber(track_number);
3292 bool Segment::WriteSegmentHeader() {
3293 UpdateDocTypeVersion();
3295 // TODO(fgalligan): Support more than one segment.
3296 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
3298 doc_type_version_written_ = doc_type_version_;
3299 ebml_header_size_ = static_cast<int32_t>(writer_header_->Position());
3301 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
3302 // will write over duration when the file is finalized.
3303 if (WriteID(writer_header_, libwebm::kMkvSegment))
3307 size_position_ = writer_header_->Position();
3309 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
3310 // bytes because if we are going to overwrite the segment size later we do
3311 // not know how big our segment will be.
3312 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
3315 payload_pos_ = writer_header_->Position();
3317 if (mode_ == kFile && writer_header_->Seekable()) {
3318 // Set the duration > 0.0 so SegmentInfo will write out the duration. When
3319 // the muxer is done writing we will set the correct duration and have
3320 // SegmentInfo upadte it.
3321 segment_info_.set_duration(1.0);
3323 if (!seek_head_.Write(writer_header_))
3327 if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset()))
3329 if (!segment_info_.Write(writer_header_))
3332 if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset()))
3334 if (!tracks_.Write(writer_header_))
3337 if (chapters_.Count() > 0) {
3338 if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset()))
3340 if (!chapters_.Write(writer_header_))
3344 if (tags_.Count() > 0) {
3345 if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset()))
3347 if (!tags_.Write(writer_header_))
3351 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
3352 if (!chunk_writer_header_)
3355 chunk_writer_header_->Close();
3358 header_written_ = true;
3363 // Here we are testing whether to create a new cluster, given a frame
3364 // having time frame_timestamp_ns.
3366 int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns,
3367 bool is_key) const {
3368 if (force_new_cluster_)
3371 // If no clusters have been created yet, then create a new cluster
3372 // and write this frame immediately, in the new cluster. This path
3373 // should only be followed once, the first time we attempt to write
3376 if (cluster_list_size_ <= 0)
3379 // There exists at least one cluster. We must compare the frame to
3380 // the last cluster, in order to determine whether the frame is
3381 // written to the existing cluster, or that a new cluster should be
3384 const uint64_t timecode_scale = segment_info_.timecode_scale();
3385 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3387 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
3388 const uint64_t last_cluster_timecode = last_cluster->timecode();
3390 // For completeness we test for the case when the frame's timecode
3391 // is less than the cluster's timecode. Although in principle that
3392 // is allowed, this muxer doesn't actually write clusters like that,
3393 // so this indicates a bug somewhere in our algorithm.
3395 if (frame_timecode < last_cluster_timecode) // should never happen
3398 // If the frame has a timestamp significantly larger than the last
3399 // cluster (in Matroska, cluster-relative timestamps are serialized
3400 // using a 16-bit signed integer), then we cannot write this frame
3401 // to that cluster, and so we must create a new cluster.
3403 const int64_t delta_timecode = frame_timecode - last_cluster_timecode;
3405 if (delta_timecode > kMaxBlockTimecode)
3408 // We decide to create a new cluster when we have a video keyframe.
3409 // This will flush queued (audio) frames, and write the keyframe
3410 // immediately, in the newly-created cluster.
3412 if (is_key && tracks_.TrackIsVideo(track_number))
3415 // Create a new cluster if we have accumulated too many frames
3416 // already, where "too many" is defined as "the total time of frames
3417 // in the cluster exceeds a threshold".
3419 const uint64_t delta_ns = delta_timecode * timecode_scale;
3421 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
3424 // This is similar to the case above, with the difference that a new
3425 // cluster is created when the size of the current cluster exceeds a
3428 const uint64_t cluster_size = last_cluster->payload_size();
3430 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
3433 // There's no need to create a new cluster, so emit this frame now.
3438 bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) {
3439 const int32_t new_size = cluster_list_size_ + 1;
3441 if (new_size > cluster_list_capacity_) {
3442 // Add more clusters.
3443 const int32_t new_capacity =
3444 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
3445 Cluster** const clusters =
3446 new (std::nothrow) Cluster*[new_capacity]; // NOLINT
3450 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3451 clusters[i] = cluster_list_[i];
3454 delete[] cluster_list_;
3456 cluster_list_ = clusters;
3457 cluster_list_capacity_ = new_capacity;
3460 if (!WriteFramesLessThan(frame_timestamp_ns))
3463 if (cluster_list_size_ > 0) {
3464 // Update old cluster's size
3465 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
3467 if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns))
3472 new_cuepoint_ = true;
3474 if (chunking_ && cluster_list_size_ > 0) {
3475 chunk_writer_cluster_->Close();
3478 if (!UpdateChunkName("chk", &chunk_name_))
3480 if (!chunk_writer_cluster_->Open(chunk_name_))
3484 const uint64_t timecode_scale = segment_info_.timecode_scale();
3485 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3487 uint64_t cluster_timecode = frame_timecode;
3489 if (frames_size_ > 0) {
3490 const Frame* const f = frames_[0]; // earliest queued frame
3491 const uint64_t ns = f->timestamp();
3492 const uint64_t tc = ns / timecode_scale;
3494 if (tc < cluster_timecode)
3495 cluster_timecode = tc;
3498 Cluster*& cluster = cluster_list_[cluster_list_size_];
3499 const int64_t offset = MaxOffset();
3500 cluster = new (std::nothrow)
3501 Cluster(cluster_timecode, offset, segment_info_.timecode_scale(),
3502 accurate_cluster_duration_, fixed_size_cluster_timecode_);
3506 if (!cluster->Init(writer_cluster_))
3509 cluster_list_size_ = new_size;
3513 bool Segment::DoNewClusterProcessing(uint64_t track_number,
3514 uint64_t frame_timestamp_ns, bool is_key) {
3516 // Based on the characteristics of the current frame and current
3517 // cluster, decide whether to create a new cluster.
3518 const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
3519 if (result < 0) // error
3522 // Always set force_new_cluster_ to false after TestFrame.
3523 force_new_cluster_ = false;
3525 // A non-zero result means create a new cluster.
3526 if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
3529 // Write queued (audio) frames.
3530 const int frame_count = WriteFramesAll();
3531 if (frame_count < 0) // error
3534 // Write the current frame to the current cluster (if TestFrame
3535 // returns 0) or to a newly created cluster (TestFrame returns 1).
3539 // TestFrame returned 2, which means there was a large time
3540 // difference between the cluster and the frame itself. Do the
3541 // test again, comparing the frame to the new cluster.
3545 bool Segment::CheckHeaderInfo() {
3546 if (!header_written_) {
3547 if (!WriteSegmentHeader())
3550 if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset()))
3553 if (output_cues_ && cues_track_ == 0) {
3554 // Check for a video track
3555 for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) {
3556 const Track* const track = tracks_.GetTrackByIndex(i);
3560 if (tracks_.TrackIsVideo(track->number())) {
3561 cues_track_ = track->number();
3566 // Set first track found
3567 if (cues_track_ == 0) {
3568 const Track* const track = tracks_.GetTrackByIndex(0);
3572 cues_track_ = track->number();
3579 void Segment::UpdateDocTypeVersion() {
3580 for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) {
3581 const Track* track = tracks_.GetTrackByIndex(index);
3584 if ((track->codec_delay() || track->seek_pre_roll()) &&
3585 doc_type_version_ < 4) {
3586 doc_type_version_ = 4;
3592 bool Segment::UpdateChunkName(const char* ext, char** name) const {
3598 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3600 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3603 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
3604 char* const str = new (std::nothrow) char[length]; // NOLINT
3609 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
3610 strcat_s(str, length, ext_chk);
3612 strcpy(str, chunking_base_name_);
3613 strcat(str, ext_chk);
3622 int64_t Segment::MaxOffset() {
3623 if (!writer_header_)
3626 int64_t offset = writer_header_->Position() - payload_pos_;
3629 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3630 Cluster* const cluster = cluster_list_[i];
3631 offset += cluster->Size();
3635 offset += writer_cues_->Position();
3641 bool Segment::QueueFrame(Frame* frame) {
3642 const int32_t new_size = frames_size_ + 1;
3644 if (new_size > frames_capacity_) {
3646 const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
3648 if (new_capacity < 1)
3651 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
3655 for (int32_t i = 0; i < frames_size_; ++i) {
3656 frames[i] = frames_[i];
3661 frames_capacity_ = new_capacity;
3664 frames_[frames_size_++] = frame;
3669 int Segment::WriteFramesAll() {
3670 if (frames_ == NULL)
3673 if (cluster_list_size_ < 1)
3676 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3681 for (int32_t i = 0; i < frames_size_; ++i) {
3682 Frame*& frame = frames_[i];
3683 // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
3684 // places where |doc_type_version_| needs to be updated.
3685 if (frame->discard_padding() != 0)
3686 doc_type_version_ = 4;
3687 if (!cluster->AddFrame(frame))
3690 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3691 if (!AddCuePoint(frame->timestamp(), cues_track_))
3695 if (frame->timestamp() > last_timestamp_) {
3696 last_timestamp_ = frame->timestamp();
3697 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3704 const int result = frames_size_;
3710 bool Segment::WriteFramesLessThan(uint64_t timestamp) {
3711 // Check |cluster_list_size_| to see if this is the first cluster. If it is
3712 // the first cluster the audio frames that are less than the first video
3713 // timesatmp will be written in a later step.
3714 if (frames_size_ > 0 && cluster_list_size_ > 0) {
3718 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3722 int32_t shift_left = 0;
3724 // TODO(fgalligan): Change this to use the durations of frames instead of
3725 // the next frame's start time if the duration is accurate.
3726 for (int32_t i = 1; i < frames_size_; ++i) {
3727 const Frame* const frame_curr = frames_[i];
3729 if (frame_curr->timestamp() > timestamp)
3732 const Frame* const frame_prev = frames_[i - 1];
3733 if (frame_prev->discard_padding() != 0)
3734 doc_type_version_ = 4;
3735 if (!cluster->AddFrame(frame_prev))
3738 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
3739 if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
3744 if (frame_prev->timestamp() > last_timestamp_) {
3745 last_timestamp_ = frame_prev->timestamp();
3746 last_track_timestamp_[frame_prev->track_number() - 1] =
3747 frame_prev->timestamp();
3753 if (shift_left > 0) {
3754 if (shift_left >= frames_size_)
3757 const int32_t new_frames_size = frames_size_ - shift_left;
3758 for (int32_t i = 0; i < new_frames_size; ++i) {
3759 frames_[i] = frames_[i + shift_left];
3762 frames_size_ = new_frames_size;
3769 } // namespace mkvmuxer