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"
20 #include "common/webmids.h"
21 #include "mkvmuxer/mkvmuxerutil.h"
22 #include "mkvmuxer/mkvwriter.h"
23 #include "mkvparser/mkvparser.h"
26 // Disable MSVC warnings that suggest making code non-portable.
27 #pragma warning(disable : 4996)
32 const float MasteringMetadata::kValueNotPresent = FLT_MAX;
33 const uint64_t Colour::kValueNotPresent = UINT64_MAX;
36 // Deallocate the string designated by |dst|, and then copy the |src|
37 // string to |dst|. The caller owns both the |src| string and the
38 // |dst| copy (hence the caller is responsible for eventually
39 // deallocating the strings, either directly, or indirectly via
40 // StrCpy). Returns true if the source string was successfully copied
41 // to the destination.
42 bool StrCpy(const char* src, char** dst_ptr) {
46 char*& dst = *dst_ptr;
54 const size_t size = strlen(src) + 1;
56 dst = new (std::nothrow) char[size]; // NOLINT
60 strcpy(dst, src); // NOLINT
64 typedef std::auto_ptr<PrimaryChromaticity> PrimaryChromaticityPtr;
65 bool CopyChromaticity(const PrimaryChromaticity* src,
66 PrimaryChromaticityPtr* dst) {
70 dst->reset(new (std::nothrow) PrimaryChromaticity(src->x, src->y));
79 ///////////////////////////////////////////////////////////////
83 IMkvWriter::IMkvWriter() {}
85 IMkvWriter::~IMkvWriter() {}
87 bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) {
89 uint64_t size = EbmlElementSize(libwebm::kMkvEBMLVersion, UINT64_C(1));
90 size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, UINT64_C(1));
91 size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, UINT64_C(4));
92 size += EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, UINT64_C(8));
93 size += EbmlElementSize(libwebm::kMkvDocType, "webm");
94 size += EbmlElementSize(libwebm::kMkvDocTypeVersion, doc_type_version);
95 size += EbmlElementSize(libwebm::kMkvDocTypeReadVersion, UINT64_C(2));
97 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size))
99 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion, UINT64_C(1)))
101 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion, UINT64_C(1)))
103 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength, UINT64_C(4)))
105 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength, UINT64_C(8)))
107 if (!WriteEbmlElement(writer, libwebm::kMkvDocType, "webm"))
109 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion, doc_type_version))
111 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion, UINT64_C(2)))
117 bool WriteEbmlHeader(IMkvWriter* writer) {
118 return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion);
121 bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst,
122 int64_t start, int64_t size) {
123 // TODO(vigneshv): Check if this is a reasonable value.
124 const uint32_t kBufSize = 2048;
125 uint8_t* buf = new uint8_t[kBufSize];
126 int64_t offset = start;
128 const int64_t read_len = (size > kBufSize) ? kBufSize : size;
129 if (source->Read(offset, static_cast<long>(read_len), buf))
131 dst->Write(buf, static_cast<uint32_t>(read_len));
139 ///////////////////////////////////////////////////////////////
146 additional_length_(0),
148 duration_set_(false),
155 reference_block_timestamp_(0),
156 reference_block_timestamp_set_(false) {}
160 delete[] additional_;
163 bool Frame::CopyFrom(const Frame& frame) {
167 if (frame.length() > 0 && frame.frame() != NULL &&
168 !Init(frame.frame(), frame.length())) {
172 delete[] additional_;
174 additional_length_ = 0;
175 if (frame.additional_length() > 0 && frame.additional() != NULL &&
176 !AddAdditionalData(frame.additional(), frame.additional_length(),
180 duration_ = frame.duration();
181 duration_set_ = frame.duration_set();
182 is_key_ = frame.is_key();
183 track_number_ = frame.track_number();
184 timestamp_ = frame.timestamp();
185 discard_padding_ = frame.discard_padding();
186 reference_block_timestamp_ = frame.reference_block_timestamp();
187 reference_block_timestamp_set_ = frame.reference_block_timestamp_set();
191 bool Frame::Init(const uint8_t* frame, uint64_t length) {
192 uint8_t* const data =
193 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
201 memcpy(frame_, frame, static_cast<size_t>(length_));
205 bool Frame::AddAdditionalData(const uint8_t* additional, uint64_t length,
207 uint8_t* const data =
208 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
212 delete[] additional_;
214 additional_length_ = length;
217 memcpy(additional_, additional, static_cast<size_t>(additional_length_));
221 bool Frame::IsValid() const {
222 if (length_ == 0 || !frame_) {
225 if ((additional_length_ != 0 && !additional_) ||
226 (additional_ != NULL && additional_length_ == 0)) {
229 if (track_number_ == 0 || track_number_ > kMaxTrackNumber) {
232 if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) {
238 bool Frame::CanBeSimpleBlock() const {
239 return additional_ == NULL && discard_padding_ == 0 && duration_ == 0;
242 void Frame::set_duration(uint64_t duration) {
243 duration_ = duration;
244 duration_set_ = true;
247 void Frame::set_reference_block_timestamp(int64_t reference_block_timestamp) {
248 reference_block_timestamp_ = reference_block_timestamp;
249 reference_block_timestamp_set_ = true;
252 ///////////////////////////////////////////////////////////////
261 output_block_number_(true) {}
263 CuePoint::~CuePoint() {}
265 bool CuePoint::Write(IMkvWriter* writer) const {
266 if (!writer || track_ < 1 || cluster_pos_ < 1)
270 EbmlElementSize(libwebm::kMkvCueClusterPosition, cluster_pos_);
271 size += EbmlElementSize(libwebm::kMkvCueTrack, track_);
272 if (output_block_number_ && block_number_ > 1)
273 size += EbmlElementSize(libwebm::kMkvCueBlockNumber, block_number_);
274 const uint64_t track_pos_size =
275 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
276 const uint64_t payload_size =
277 EbmlElementSize(libwebm::kMkvCueTime, time_) + track_pos_size;
279 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCuePoint, payload_size))
282 const int64_t payload_position = writer->Position();
283 if (payload_position < 0)
286 if (!WriteEbmlElement(writer, libwebm::kMkvCueTime, time_))
289 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size))
291 if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack, track_))
293 if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition, cluster_pos_))
295 if (output_block_number_ && block_number_ > 1)
296 if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber, block_number_))
299 const int64_t stop_position = writer->Position();
300 if (stop_position < 0)
303 if (stop_position - payload_position != static_cast<int64_t>(payload_size))
309 uint64_t CuePoint::PayloadSize() const {
311 EbmlElementSize(libwebm::kMkvCueClusterPosition, cluster_pos_);
312 size += EbmlElementSize(libwebm::kMkvCueTrack, track_);
313 if (output_block_number_ && block_number_ > 1)
314 size += EbmlElementSize(libwebm::kMkvCueBlockNumber, block_number_);
315 const uint64_t track_pos_size =
316 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
317 const uint64_t payload_size =
318 EbmlElementSize(libwebm::kMkvCueTime, time_) + track_pos_size;
323 uint64_t CuePoint::Size() const {
324 const uint64_t payload_size = PayloadSize();
325 return EbmlMasterElementSize(libwebm::kMkvCuePoint, payload_size) +
329 ///////////////////////////////////////////////////////////////
334 : cue_entries_capacity_(0),
335 cue_entries_size_(0),
337 output_block_number_(true) {}
341 for (int32_t i = 0; i < cue_entries_size_; ++i) {
342 CuePoint* const cue = cue_entries_[i];
345 delete[] cue_entries_;
349 bool Cues::AddCue(CuePoint* cue) {
353 if ((cue_entries_size_ + 1) > cue_entries_capacity_) {
354 // Add more CuePoints.
355 const int32_t new_capacity =
356 (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2;
358 if (new_capacity < 1)
361 CuePoint** const cues =
362 new (std::nothrow) CuePoint*[new_capacity]; // NOLINT
366 for (int32_t i = 0; i < cue_entries_size_; ++i) {
367 cues[i] = cue_entries_[i];
370 delete[] cue_entries_;
373 cue_entries_capacity_ = new_capacity;
376 cue->set_output_block_number(output_block_number_);
377 cue_entries_[cue_entries_size_++] = cue;
381 CuePoint* Cues::GetCueByIndex(int32_t index) const {
382 if (cue_entries_ == NULL)
385 if (index >= cue_entries_size_)
388 return cue_entries_[index];
391 uint64_t Cues::Size() {
393 for (int32_t i = 0; i < cue_entries_size_; ++i)
394 size += GetCueByIndex(i)->Size();
395 size += EbmlMasterElementSize(libwebm::kMkvCues, size);
399 bool Cues::Write(IMkvWriter* writer) const {
404 for (int32_t i = 0; i < cue_entries_size_; ++i) {
405 const CuePoint* const cue = GetCueByIndex(i);
413 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCues, size))
416 const int64_t payload_position = writer->Position();
417 if (payload_position < 0)
420 for (int32_t i = 0; i < cue_entries_size_; ++i) {
421 const CuePoint* const cue = GetCueByIndex(i);
423 if (!cue->Write(writer))
427 const int64_t stop_position = writer->Position();
428 if (stop_position < 0)
431 if (stop_position - payload_position != static_cast<int64_t>(size))
437 ///////////////////////////////////////////////////////////////
439 // ContentEncAESSettings Class
441 ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
443 uint64_t ContentEncAESSettings::Size() const {
444 const uint64_t payload = PayloadSize();
445 const uint64_t size =
446 EbmlMasterElementSize(libwebm::kMkvContentEncAESSettings, payload) +
451 bool ContentEncAESSettings::Write(IMkvWriter* writer) const {
452 const uint64_t payload = PayloadSize();
454 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncAESSettings,
458 const int64_t payload_position = writer->Position();
459 if (payload_position < 0)
462 if (!WriteEbmlElement(writer, libwebm::kMkvAESSettingsCipherMode,
466 const int64_t stop_position = writer->Position();
467 if (stop_position < 0 ||
468 stop_position - payload_position != static_cast<int64_t>(payload))
474 uint64_t ContentEncAESSettings::PayloadSize() const {
476 EbmlElementSize(libwebm::kMkvAESSettingsCipherMode, cipher_mode_);
480 ///////////////////////////////////////////////////////////////
482 // ContentEncoding Class
484 ContentEncoding::ContentEncoding()
490 enc_key_id_length_(0) {}
492 ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
494 bool ContentEncoding::SetEncryptionID(const uint8_t* id, uint64_t length) {
495 if (!id || length < 1)
498 delete[] enc_key_id_;
501 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
505 memcpy(enc_key_id_, id, static_cast<size_t>(length));
506 enc_key_id_length_ = length;
511 uint64_t ContentEncoding::Size() const {
512 const uint64_t encryption_size = EncryptionSize();
513 const uint64_t encoding_size = EncodingSize(0, encryption_size);
514 const uint64_t encodings_size =
515 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
518 return encodings_size;
521 bool ContentEncoding::Write(IMkvWriter* writer) const {
522 const uint64_t encryption_size = EncryptionSize();
523 const uint64_t encoding_size = EncodingSize(0, encryption_size);
524 const uint64_t size =
525 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
528 const int64_t payload_position = writer->Position();
529 if (payload_position < 0)
532 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncoding,
535 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingOrder,
538 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope,
541 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType,
545 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption,
548 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo, enc_algo_))
550 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncKeyID, enc_key_id_,
554 if (!enc_aes_settings_.Write(writer))
557 const int64_t stop_position = writer->Position();
558 if (stop_position < 0 ||
559 stop_position - payload_position != static_cast<int64_t>(size))
565 uint64_t ContentEncoding::EncodingSize(uint64_t compresion_size,
566 uint64_t encryption_size) const {
567 // TODO(fgalligan): Add support for compression settings.
568 if (compresion_size != 0)
571 uint64_t encoding_size = 0;
573 if (encryption_size > 0) {
575 EbmlMasterElementSize(libwebm::kMkvContentEncryption, encryption_size) +
579 EbmlElementSize(libwebm::kMkvContentEncodingType, encoding_type_);
581 EbmlElementSize(libwebm::kMkvContentEncodingScope, encoding_scope_);
583 EbmlElementSize(libwebm::kMkvContentEncodingOrder, encoding_order_);
585 return encoding_size;
588 uint64_t ContentEncoding::EncryptionSize() const {
589 const uint64_t aes_size = enc_aes_settings_.Size();
591 uint64_t encryption_size = EbmlElementSize(libwebm::kMkvContentEncKeyID,
592 enc_key_id_, enc_key_id_length_);
593 encryption_size += EbmlElementSize(libwebm::kMkvContentEncAlgo, enc_algo_);
595 return encryption_size + aes_size;
598 ///////////////////////////////////////////////////////////////
602 Track::Track(unsigned int* seed)
604 codec_private_(NULL),
606 max_block_additional_id_(0),
613 default_duration_(0),
614 codec_private_length_(0),
615 content_encoding_entries_(NULL),
616 content_encoding_entries_size_(0) {}
620 delete[] codec_private_;
624 if (content_encoding_entries_) {
625 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
626 ContentEncoding* const encoding = content_encoding_entries_[i];
629 delete[] content_encoding_entries_;
633 bool Track::AddContentEncoding() {
634 const uint32_t count = content_encoding_entries_size_ + 1;
636 ContentEncoding** const content_encoding_entries =
637 new (std::nothrow) ContentEncoding*[count]; // NOLINT
638 if (!content_encoding_entries)
641 ContentEncoding* const content_encoding =
642 new (std::nothrow) ContentEncoding(); // NOLINT
643 if (!content_encoding) {
644 delete[] content_encoding_entries;
648 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
649 content_encoding_entries[i] = content_encoding_entries_[i];
652 delete[] content_encoding_entries_;
654 content_encoding_entries_ = content_encoding_entries;
655 content_encoding_entries_[content_encoding_entries_size_] = content_encoding;
656 content_encoding_entries_size_ = count;
660 ContentEncoding* Track::GetContentEncodingByIndex(uint32_t index) const {
661 if (content_encoding_entries_ == NULL)
664 if (index >= content_encoding_entries_size_)
667 return content_encoding_entries_[index];
670 uint64_t Track::PayloadSize() const {
671 uint64_t size = EbmlElementSize(libwebm::kMkvTrackNumber, number_);
672 size += EbmlElementSize(libwebm::kMkvTrackUID, uid_);
673 size += EbmlElementSize(libwebm::kMkvTrackType, type_);
675 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
677 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
678 codec_private_length_);
680 size += EbmlElementSize(libwebm::kMkvLanguage, language_);
682 size += EbmlElementSize(libwebm::kMkvName, name_);
683 if (max_block_additional_id_)
684 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
685 max_block_additional_id_);
687 size += EbmlElementSize(libwebm::kMkvCodecDelay, codec_delay_);
689 size += EbmlElementSize(libwebm::kMkvSeekPreRoll, seek_pre_roll_);
690 if (default_duration_)
691 size += EbmlElementSize(libwebm::kMkvDefaultDuration, default_duration_);
693 if (content_encoding_entries_size_ > 0) {
694 uint64_t content_encodings_size = 0;
695 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
696 ContentEncoding* const encoding = content_encoding_entries_[i];
697 content_encodings_size += encoding->Size();
700 size += EbmlMasterElementSize(libwebm::kMkvContentEncodings,
701 content_encodings_size) +
702 content_encodings_size;
708 uint64_t Track::Size() const {
709 uint64_t size = PayloadSize();
710 size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size);
714 bool Track::Write(IMkvWriter* writer) const {
718 // mandatory elements without a default value.
719 if (!type_ || !codec_id_)
722 // |size| may be bigger than what is written out in this function because
723 // derived classes may write out more data in the Track element.
724 const uint64_t payload_size = PayloadSize();
726 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTrackEntry, payload_size))
729 uint64_t size = EbmlElementSize(libwebm::kMkvTrackNumber, number_);
730 size += EbmlElementSize(libwebm::kMkvTrackUID, uid_);
731 size += EbmlElementSize(libwebm::kMkvTrackType, type_);
733 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
735 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
736 codec_private_length_);
738 size += EbmlElementSize(libwebm::kMkvLanguage, language_);
740 size += EbmlElementSize(libwebm::kMkvName, name_);
741 if (max_block_additional_id_)
742 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
743 max_block_additional_id_);
745 size += EbmlElementSize(libwebm::kMkvCodecDelay, codec_delay_);
747 size += EbmlElementSize(libwebm::kMkvSeekPreRoll, seek_pre_roll_);
748 if (default_duration_)
749 size += EbmlElementSize(libwebm::kMkvDefaultDuration, default_duration_);
751 const int64_t payload_position = writer->Position();
752 if (payload_position < 0)
755 if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber, number_))
757 if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID, uid_))
759 if (!WriteEbmlElement(writer, libwebm::kMkvTrackType, type_))
761 if (max_block_additional_id_) {
762 if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID,
763 max_block_additional_id_)) {
768 if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay, codec_delay_))
771 if (seek_pre_roll_) {
772 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll, seek_pre_roll_))
775 if (default_duration_) {
776 if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration,
781 if (!WriteEbmlElement(writer, libwebm::kMkvCodecID, codec_id_))
784 if (codec_private_) {
785 if (!WriteEbmlElement(writer, libwebm::kMkvCodecPrivate, codec_private_,
786 codec_private_length_))
790 if (!WriteEbmlElement(writer, libwebm::kMkvLanguage, language_))
794 if (!WriteEbmlElement(writer, libwebm::kMkvName, name_))
798 int64_t stop_position = writer->Position();
799 if (stop_position < 0 ||
800 stop_position - payload_position != static_cast<int64_t>(size))
803 if (content_encoding_entries_size_ > 0) {
804 uint64_t content_encodings_size = 0;
805 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
806 ContentEncoding* const encoding = content_encoding_entries_[i];
807 content_encodings_size += encoding->Size();
810 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncodings,
811 content_encodings_size))
814 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
815 ContentEncoding* const encoding = content_encoding_entries_[i];
816 if (!encoding->Write(writer))
821 stop_position = writer->Position();
822 if (stop_position < 0)
827 bool Track::SetCodecPrivate(const uint8_t* codec_private, uint64_t length) {
828 if (!codec_private || length < 1)
831 delete[] codec_private_;
834 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
838 memcpy(codec_private_, codec_private, static_cast<size_t>(length));
839 codec_private_length_ = length;
844 void Track::set_codec_id(const char* codec_id) {
848 const size_t length = strlen(codec_id) + 1;
849 codec_id_ = new (std::nothrow) char[length]; // NOLINT
852 strcpy_s(codec_id_, length, codec_id);
854 strcpy(codec_id_, codec_id);
860 // TODO(fgalligan): Vet the language parameter.
861 void Track::set_language(const char* language) {
865 const size_t length = strlen(language) + 1;
866 language_ = new (std::nothrow) char[length]; // NOLINT
869 strcpy_s(language_, length, language);
871 strcpy(language_, language);
877 void Track::set_name(const char* name) {
881 const size_t length = strlen(name) + 1;
882 name_ = new (std::nothrow) char[length]; // NOLINT
885 strcpy_s(name_, length, name);
893 ///////////////////////////////////////////////////////////////
895 // Colour and its child elements
897 uint64_t PrimaryChromaticity::PrimaryChromaticityPayloadSize(
898 libwebm::MkvId x_id, libwebm::MkvId y_id) const {
899 return EbmlElementSize(x_id, x) + EbmlElementSize(y_id, y);
902 bool PrimaryChromaticity::Write(IMkvWriter* writer, libwebm::MkvId x_id,
903 libwebm::MkvId y_id) const {
904 return WriteEbmlElement(writer, x_id, x) && WriteEbmlElement(writer, y_id, y);
907 uint64_t MasteringMetadata::MasteringMetadataSize() const {
908 uint64_t size = PayloadSize();
911 size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size);
916 bool MasteringMetadata::Write(IMkvWriter* writer) const {
917 const uint64_t size = PayloadSize();
919 // Don't write an empty element.
923 if (!WriteEbmlMasterElement(writer, libwebm::kMkvMasteringMetadata, size))
925 if (luminance_max != kValueNotPresent &&
926 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMax, luminance_max)) {
929 if (luminance_min != kValueNotPresent &&
930 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMin, luminance_min)) {
934 !r_->Write(writer, libwebm::kMkvPrimaryRChromaticityX,
935 libwebm::kMkvPrimaryRChromaticityY)) {
939 !g_->Write(writer, libwebm::kMkvPrimaryGChromaticityX,
940 libwebm::kMkvPrimaryGChromaticityY)) {
944 !b_->Write(writer, libwebm::kMkvPrimaryBChromaticityX,
945 libwebm::kMkvPrimaryBChromaticityY)) {
949 !white_point_->Write(writer, libwebm::kMkvWhitePointChromaticityX,
950 libwebm::kMkvWhitePointChromaticityY)) {
957 bool MasteringMetadata::SetChromaticity(
958 const PrimaryChromaticity* r, const PrimaryChromaticity* g,
959 const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) {
960 PrimaryChromaticityPtr r_ptr(NULL);
962 if (!CopyChromaticity(r, &r_ptr))
965 PrimaryChromaticityPtr g_ptr(NULL);
967 if (!CopyChromaticity(g, &g_ptr))
970 PrimaryChromaticityPtr b_ptr(NULL);
972 if (!CopyChromaticity(b, &b_ptr))
975 PrimaryChromaticityPtr wp_ptr(NULL);
977 if (!CopyChromaticity(white_point, &wp_ptr))
981 r_ = r_ptr.release();
982 g_ = g_ptr.release();
983 b_ = b_ptr.release();
984 white_point_ = wp_ptr.release();
988 uint64_t MasteringMetadata::PayloadSize() const {
991 if (luminance_max != kValueNotPresent)
992 size += EbmlElementSize(libwebm::kMkvLuminanceMax, luminance_max);
993 if (luminance_min != kValueNotPresent)
994 size += EbmlElementSize(libwebm::kMkvLuminanceMin, luminance_min);
997 size += r_->PrimaryChromaticityPayloadSize(
998 libwebm::kMkvPrimaryRChromaticityX, libwebm::kMkvPrimaryRChromaticityY);
1001 size += g_->PrimaryChromaticityPayloadSize(
1002 libwebm::kMkvPrimaryGChromaticityX, libwebm::kMkvPrimaryGChromaticityY);
1005 size += b_->PrimaryChromaticityPayloadSize(
1006 libwebm::kMkvPrimaryBChromaticityX, libwebm::kMkvPrimaryBChromaticityY);
1009 size += white_point_->PrimaryChromaticityPayloadSize(
1010 libwebm::kMkvWhitePointChromaticityX,
1011 libwebm::kMkvWhitePointChromaticityY);
1017 uint64_t Colour::ColourSize() const {
1018 uint64_t size = PayloadSize();
1021 size += EbmlMasterElementSize(libwebm::kMkvColour, size);
1026 bool Colour::Write(IMkvWriter* writer) const {
1027 const uint64_t size = PayloadSize();
1029 // Don't write an empty element.
1033 if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size))
1036 if (matrix_coefficients != kValueNotPresent &&
1037 !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients,
1038 matrix_coefficients)) {
1041 if (bits_per_channel != kValueNotPresent &&
1042 !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel,
1043 bits_per_channel)) {
1046 if (chroma_subsampling_horz != kValueNotPresent &&
1047 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz,
1048 chroma_subsampling_horz)) {
1051 if (chroma_subsampling_vert != kValueNotPresent &&
1052 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert,
1053 chroma_subsampling_vert)) {
1057 if (cb_subsampling_horz != kValueNotPresent &&
1058 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz,
1059 cb_subsampling_horz)) {
1062 if (cb_subsampling_vert != kValueNotPresent &&
1063 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert,
1064 cb_subsampling_vert)) {
1067 if (chroma_siting_horz != kValueNotPresent &&
1068 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz,
1069 chroma_siting_horz)) {
1072 if (chroma_siting_vert != kValueNotPresent &&
1073 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert,
1074 chroma_siting_vert)) {
1077 if (range != kValueNotPresent &&
1078 !WriteEbmlElement(writer, libwebm::kMkvRange, range)) {
1081 if (transfer_characteristics != kValueNotPresent &&
1082 !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics,
1083 transfer_characteristics)) {
1086 if (primaries != kValueNotPresent &&
1087 !WriteEbmlElement(writer, libwebm::kMkvPrimaries, primaries)) {
1090 if (max_cll != kValueNotPresent &&
1091 !WriteEbmlElement(writer, libwebm::kMkvMaxCLL, max_cll)) {
1094 if (max_fall != kValueNotPresent &&
1095 !WriteEbmlElement(writer, libwebm::kMkvMaxFALL, max_fall)) {
1099 if (mastering_metadata_ && !mastering_metadata_->Write(writer))
1105 bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) {
1106 std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
1110 mm_ptr->luminance_max = mastering_metadata.luminance_max;
1111 mm_ptr->luminance_min = mastering_metadata.luminance_min;
1113 if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(),
1114 mastering_metadata.b(),
1115 mastering_metadata.white_point())) {
1119 delete mastering_metadata_;
1120 mastering_metadata_ = mm_ptr.release();
1124 uint64_t Colour::PayloadSize() const {
1127 if (matrix_coefficients != kValueNotPresent)
1129 EbmlElementSize(libwebm::kMkvMatrixCoefficients, matrix_coefficients);
1130 if (bits_per_channel != kValueNotPresent)
1131 size += EbmlElementSize(libwebm::kMkvBitsPerChannel, bits_per_channel);
1132 if (chroma_subsampling_horz != kValueNotPresent)
1133 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz,
1134 chroma_subsampling_horz);
1135 if (chroma_subsampling_vert != kValueNotPresent)
1136 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert,
1137 chroma_subsampling_vert);
1138 if (cb_subsampling_horz != kValueNotPresent)
1140 EbmlElementSize(libwebm::kMkvCbSubsamplingHorz, cb_subsampling_horz);
1141 if (cb_subsampling_vert != kValueNotPresent)
1143 EbmlElementSize(libwebm::kMkvCbSubsamplingVert, cb_subsampling_vert);
1144 if (chroma_siting_horz != kValueNotPresent)
1145 size += EbmlElementSize(libwebm::kMkvChromaSitingHorz, chroma_siting_horz);
1146 if (chroma_siting_vert != kValueNotPresent)
1147 size += EbmlElementSize(libwebm::kMkvChromaSitingVert, chroma_siting_vert);
1148 if (range != kValueNotPresent)
1149 size += EbmlElementSize(libwebm::kMkvRange, range);
1150 if (transfer_characteristics != kValueNotPresent)
1151 size += EbmlElementSize(libwebm::kMkvTransferCharacteristics,
1152 transfer_characteristics);
1153 if (primaries != kValueNotPresent)
1154 size += EbmlElementSize(libwebm::kMkvPrimaries, primaries);
1155 if (max_cll != kValueNotPresent)
1156 size += EbmlElementSize(libwebm::kMkvMaxCLL, max_cll);
1157 if (max_fall != kValueNotPresent)
1158 size += EbmlElementSize(libwebm::kMkvMaxFALL, max_fall);
1160 if (mastering_metadata_)
1161 size += mastering_metadata_->MasteringMetadataSize();
1166 ///////////////////////////////////////////////////////////////
1170 VideoTrack::VideoTrack(unsigned int* seed)
1185 VideoTrack::~VideoTrack() { delete colour_; }
1187 bool VideoTrack::SetStereoMode(uint64_t stereo_mode) {
1188 if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst &&
1189 stereo_mode != kTopBottomRightIsFirst &&
1190 stereo_mode != kTopBottomLeftIsFirst &&
1191 stereo_mode != kSideBySideRightIsFirst)
1194 stereo_mode_ = stereo_mode;
1198 bool VideoTrack::SetAlphaMode(uint64_t alpha_mode) {
1199 if (alpha_mode != kNoAlpha && alpha_mode != kAlpha)
1202 alpha_mode_ = alpha_mode;
1206 uint64_t VideoTrack::PayloadSize() const {
1207 const uint64_t parent_size = Track::PayloadSize();
1209 uint64_t size = VideoPayloadSize();
1210 size += EbmlMasterElementSize(libwebm::kMkvVideo, size);
1212 return parent_size + size;
1215 bool VideoTrack::Write(IMkvWriter* writer) const {
1216 if (!Track::Write(writer))
1219 const uint64_t size = VideoPayloadSize();
1221 if (!WriteEbmlMasterElement(writer, libwebm::kMkvVideo, size))
1224 const int64_t payload_position = writer->Position();
1225 if (payload_position < 0)
1228 if (!WriteEbmlElement(writer, libwebm::kMkvPixelWidth, width_))
1230 if (!WriteEbmlElement(writer, libwebm::kMkvPixelHeight, height_))
1232 if (display_width_ > 0) {
1233 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth, display_width_))
1236 if (display_height_ > 0) {
1237 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight, display_height_))
1240 if (crop_left_ > 0) {
1241 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft, crop_left_))
1244 if (crop_right_ > 0) {
1245 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight, crop_right_))
1248 if (crop_top_ > 0) {
1249 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop, crop_top_))
1252 if (crop_bottom_ > 0) {
1253 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom, crop_bottom_))
1256 if (stereo_mode_ > kMono) {
1257 if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode, stereo_mode_))
1260 if (alpha_mode_ > kNoAlpha) {
1261 if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode, alpha_mode_))
1264 if (frame_rate_ > 0.0) {
1265 if (!WriteEbmlElement(writer, libwebm::kMkvFrameRate,
1266 static_cast<float>(frame_rate_))) {
1271 if (!colour_->Write(writer))
1275 const int64_t stop_position = writer->Position();
1276 if (stop_position < 0 ||
1277 stop_position - payload_position != static_cast<int64_t>(size)) {
1284 bool VideoTrack::SetColour(const Colour& colour) {
1285 std::auto_ptr<Colour> colour_ptr(new Colour());
1286 if (!colour_ptr.get())
1289 if (colour.mastering_metadata()) {
1290 if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata()))
1294 colour_ptr->matrix_coefficients = colour.matrix_coefficients;
1295 colour_ptr->bits_per_channel = colour.bits_per_channel;
1296 colour_ptr->chroma_subsampling_horz = colour.chroma_subsampling_horz;
1297 colour_ptr->chroma_subsampling_vert = colour.chroma_subsampling_vert;
1298 colour_ptr->cb_subsampling_horz = colour.cb_subsampling_horz;
1299 colour_ptr->cb_subsampling_vert = colour.cb_subsampling_vert;
1300 colour_ptr->chroma_siting_horz = colour.chroma_siting_horz;
1301 colour_ptr->chroma_siting_vert = colour.chroma_siting_vert;
1302 colour_ptr->range = colour.range;
1303 colour_ptr->transfer_characteristics = colour.transfer_characteristics;
1304 colour_ptr->primaries = colour.primaries;
1305 colour_ptr->max_cll = colour.max_cll;
1306 colour_ptr->max_fall = colour.max_fall;
1307 colour_ = colour_ptr.release();
1311 uint64_t VideoTrack::VideoPayloadSize() const {
1312 uint64_t size = EbmlElementSize(libwebm::kMkvPixelWidth, width_);
1313 size += EbmlElementSize(libwebm::kMkvPixelHeight, height_);
1314 if (display_width_ > 0)
1315 size += EbmlElementSize(libwebm::kMkvDisplayWidth, display_width_);
1316 if (display_height_ > 0)
1317 size += EbmlElementSize(libwebm::kMkvDisplayHeight, display_height_);
1319 size += EbmlElementSize(libwebm::kMkvPixelCropLeft, crop_left_);
1320 if (crop_right_ > 0)
1321 size += EbmlElementSize(libwebm::kMkvPixelCropRight, crop_right_);
1323 size += EbmlElementSize(libwebm::kMkvPixelCropTop, crop_top_);
1324 if (crop_bottom_ > 0)
1325 size += EbmlElementSize(libwebm::kMkvPixelCropBottom, crop_bottom_);
1326 if (stereo_mode_ > kMono)
1327 size += EbmlElementSize(libwebm::kMkvStereoMode, stereo_mode_);
1328 if (alpha_mode_ > kNoAlpha)
1329 size += EbmlElementSize(libwebm::kMkvAlphaMode, alpha_mode_);
1330 if (frame_rate_ > 0.0)
1331 size += EbmlElementSize(libwebm::kMkvFrameRate,
1332 static_cast<float>(frame_rate_));
1334 size += colour_->ColourSize();
1339 ///////////////////////////////////////////////////////////////
1343 AudioTrack::AudioTrack(unsigned int* seed)
1344 : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
1346 AudioTrack::~AudioTrack() {}
1348 uint64_t AudioTrack::PayloadSize() const {
1349 const uint64_t parent_size = Track::PayloadSize();
1351 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
1352 static_cast<float>(sample_rate_));
1353 size += EbmlElementSize(libwebm::kMkvChannels, channels_);
1355 size += EbmlElementSize(libwebm::kMkvBitDepth, bit_depth_);
1356 size += EbmlMasterElementSize(libwebm::kMkvAudio, size);
1358 return parent_size + size;
1361 bool AudioTrack::Write(IMkvWriter* writer) const {
1362 if (!Track::Write(writer))
1365 // Calculate AudioSettings size.
1366 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
1367 static_cast<float>(sample_rate_));
1368 size += EbmlElementSize(libwebm::kMkvChannels, channels_);
1370 size += EbmlElementSize(libwebm::kMkvBitDepth, bit_depth_);
1372 if (!WriteEbmlMasterElement(writer, libwebm::kMkvAudio, size))
1375 const int64_t payload_position = writer->Position();
1376 if (payload_position < 0)
1379 if (!WriteEbmlElement(writer, libwebm::kMkvSamplingFrequency,
1380 static_cast<float>(sample_rate_)))
1382 if (!WriteEbmlElement(writer, libwebm::kMkvChannels, channels_))
1385 if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth, bit_depth_))
1388 const int64_t stop_position = writer->Position();
1389 if (stop_position < 0 ||
1390 stop_position - payload_position != static_cast<int64_t>(size))
1396 ///////////////////////////////////////////////////////////////
1400 const char Tracks::kOpusCodecId[] = "A_OPUS";
1401 const char Tracks::kVorbisCodecId[] = "A_VORBIS";
1402 const char Tracks::kVp8CodecId[] = "V_VP8";
1403 const char Tracks::kVp9CodecId[] = "V_VP9";
1404 const char Tracks::kVp10CodecId[] = "V_VP10";
1407 : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {}
1410 if (track_entries_) {
1411 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1412 Track* const track = track_entries_[i];
1415 delete[] track_entries_;
1419 bool Tracks::AddTrack(Track* track, int32_t number) {
1420 if (number < 0 || wrote_tracks_)
1423 // This muxer only supports track numbers in the range [1, 126], in
1424 // order to be able (to use Matroska integer representation) to
1425 // serialize the block header (of which the track number is a part)
1426 // for a frame using exactly 4 bytes.
1431 uint32_t track_num = number;
1433 if (track_num > 0) {
1434 // Check to make sure a track does not already have |track_num|.
1435 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1436 if (track_entries_[i]->number() == track_num)
1441 const uint32_t count = track_entries_size_ + 1;
1443 Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
1447 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1448 track_entries[i] = track_entries_[i];
1451 delete[] track_entries_;
1453 // Find the lowest availible track number > 0.
1454 if (track_num == 0) {
1457 // Check to make sure a track does not already have |track_num|.
1461 for (uint32_t i = 0; i < track_entries_size_; ++i) {
1462 if (track_entries[i]->number() == track_num) {
1470 track->set_number(track_num);
1472 track_entries_ = track_entries;
1473 track_entries_[track_entries_size_] = track;
1474 track_entries_size_ = count;
1478 const Track* Tracks::GetTrackByIndex(uint32_t index) const {
1479 if (track_entries_ == NULL)
1482 if (index >= track_entries_size_)
1485 return track_entries_[index];
1488 Track* Tracks::GetTrackByNumber(uint64_t track_number) const {
1489 const int32_t count = track_entries_size();
1490 for (int32_t i = 0; i < count; ++i) {
1491 if (track_entries_[i]->number() == track_number)
1492 return track_entries_[i];
1498 bool Tracks::TrackIsAudio(uint64_t track_number) const {
1499 const Track* const track = GetTrackByNumber(track_number);
1501 if (track->type() == kAudio)
1507 bool Tracks::TrackIsVideo(uint64_t track_number) const {
1508 const Track* const track = GetTrackByNumber(track_number);
1510 if (track->type() == kVideo)
1516 bool Tracks::Write(IMkvWriter* writer) const {
1518 const int32_t count = track_entries_size();
1519 for (int32_t i = 0; i < count; ++i) {
1520 const Track* const track = GetTrackByIndex(i);
1525 size += track->Size();
1528 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTracks, size))
1531 const int64_t payload_position = writer->Position();
1532 if (payload_position < 0)
1535 for (int32_t i = 0; i < count; ++i) {
1536 const Track* const track = GetTrackByIndex(i);
1537 if (!track->Write(writer))
1541 const int64_t stop_position = writer->Position();
1542 if (stop_position < 0 ||
1543 stop_position - payload_position != static_cast<int64_t>(size))
1546 wrote_tracks_ = true;
1550 ///////////////////////////////////////////////////////////////
1554 bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
1556 void Chapter::set_time(const Segment& segment, uint64_t start_ns,
1558 const SegmentInfo* const info = segment.GetSegmentInfo();
1559 const uint64_t timecode_scale = info->timecode_scale();
1560 start_timecode_ = start_ns / timecode_scale;
1561 end_timecode_ = end_ns / timecode_scale;
1564 bool Chapter::add_string(const char* title, const char* language,
1565 const char* country) {
1566 if (!ExpandDisplaysArray())
1569 Display& d = displays_[displays_count_++];
1572 if (!d.set_title(title))
1575 if (!d.set_language(language))
1578 if (!d.set_country(country))
1584 Chapter::Chapter() {
1585 // This ctor only constructs the object. Proper initialization is
1586 // done in Init() (called in Chapters::AddChapter()). The only
1587 // reason we bother implementing this ctor is because we had to
1588 // declare it as private (along with the dtor), in order to prevent
1589 // clients from creating Chapter instances (a privelege we grant
1590 // only to the Chapters class). Doing no initialization here also
1591 // means that creating arrays of chapter objects is more efficient,
1592 // because we only initialize each new chapter object as it becomes
1593 // active on the array.
1596 Chapter::~Chapter() {}
1598 void Chapter::Init(unsigned int* seed) {
1600 start_timecode_ = 0;
1604 displays_count_ = 0;
1605 uid_ = MakeUID(seed);
1608 void Chapter::ShallowCopy(Chapter* dst) const {
1610 dst->start_timecode_ = start_timecode_;
1611 dst->end_timecode_ = end_timecode_;
1613 dst->displays_ = displays_;
1614 dst->displays_size_ = displays_size_;
1615 dst->displays_count_ = displays_count_;
1618 void Chapter::Clear() {
1621 while (displays_count_ > 0) {
1622 Display& d = displays_[--displays_count_];
1632 bool Chapter::ExpandDisplaysArray() {
1633 if (displays_size_ > displays_count_)
1634 return true; // nothing to do yet
1636 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
1638 Display* const displays = new (std::nothrow) Display[size]; // NOLINT
1639 if (displays == NULL)
1642 for (int idx = 0; idx < displays_count_; ++idx) {
1643 displays[idx] = displays_[idx]; // shallow copy
1648 displays_ = displays;
1649 displays_size_ = size;
1654 uint64_t Chapter::WriteAtom(IMkvWriter* writer) const {
1655 uint64_t payload_size =
1656 EbmlElementSize(libwebm::kMkvChapterStringUID, id_) +
1657 EbmlElementSize(libwebm::kMkvChapterUID, uid_) +
1658 EbmlElementSize(libwebm::kMkvChapterTimeStart, start_timecode_) +
1659 EbmlElementSize(libwebm::kMkvChapterTimeEnd, end_timecode_);
1661 for (int idx = 0; idx < displays_count_; ++idx) {
1662 const Display& d = displays_[idx];
1663 payload_size += d.WriteDisplay(NULL);
1666 const uint64_t atom_size =
1667 EbmlMasterElementSize(libwebm::kMkvChapterAtom, payload_size) +
1673 const int64_t start = writer->Position();
1675 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterAtom, payload_size))
1678 if (!WriteEbmlElement(writer, libwebm::kMkvChapterStringUID, id_))
1681 if (!WriteEbmlElement(writer, libwebm::kMkvChapterUID, uid_))
1684 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart, start_timecode_))
1687 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd, end_timecode_))
1690 for (int idx = 0; idx < displays_count_; ++idx) {
1691 const Display& d = displays_[idx];
1693 if (!d.WriteDisplay(writer))
1697 const int64_t stop = writer->Position();
1699 if (stop >= start && uint64_t(stop - start) != atom_size)
1705 void Chapter::Display::Init() {
1711 void Chapter::Display::Clear() {
1712 StrCpy(NULL, &title_);
1713 StrCpy(NULL, &language_);
1714 StrCpy(NULL, &country_);
1717 bool Chapter::Display::set_title(const char* title) {
1718 return StrCpy(title, &title_);
1721 bool Chapter::Display::set_language(const char* language) {
1722 return StrCpy(language, &language_);
1725 bool Chapter::Display::set_country(const char* country) {
1726 return StrCpy(country, &country_);
1729 uint64_t Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
1730 uint64_t payload_size = EbmlElementSize(libwebm::kMkvChapString, title_);
1733 payload_size += EbmlElementSize(libwebm::kMkvChapLanguage, language_);
1736 payload_size += EbmlElementSize(libwebm::kMkvChapCountry, country_);
1738 const uint64_t display_size =
1739 EbmlMasterElementSize(libwebm::kMkvChapterDisplay, payload_size) +
1743 return display_size;
1745 const int64_t start = writer->Position();
1747 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterDisplay,
1751 if (!WriteEbmlElement(writer, libwebm::kMkvChapString, title_))
1755 if (!WriteEbmlElement(writer, libwebm::kMkvChapLanguage, language_))
1760 if (!WriteEbmlElement(writer, libwebm::kMkvChapCountry, country_))
1764 const int64_t stop = writer->Position();
1766 if (stop >= start && uint64_t(stop - start) != display_size)
1769 return display_size;
1772 ///////////////////////////////////////////////////////////////
1776 Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
1778 Chapters::~Chapters() {
1779 while (chapters_count_ > 0) {
1780 Chapter& chapter = chapters_[--chapters_count_];
1788 int Chapters::Count() const { return chapters_count_; }
1790 Chapter* Chapters::AddChapter(unsigned int* seed) {
1791 if (!ExpandChaptersArray())
1794 Chapter& chapter = chapters_[chapters_count_++];
1800 bool Chapters::Write(IMkvWriter* writer) const {
1804 const uint64_t payload_size = WriteEdition(NULL); // return size only
1806 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapters, payload_size))
1809 const int64_t start = writer->Position();
1811 if (WriteEdition(writer) == 0) // error
1814 const int64_t stop = writer->Position();
1816 if (stop >= start && uint64_t(stop - start) != payload_size)
1822 bool Chapters::ExpandChaptersArray() {
1823 if (chapters_size_ > chapters_count_)
1824 return true; // nothing to do yet
1826 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
1828 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
1829 if (chapters == NULL)
1832 for (int idx = 0; idx < chapters_count_; ++idx) {
1833 const Chapter& src = chapters_[idx];
1834 Chapter* const dst = chapters + idx;
1835 src.ShallowCopy(dst);
1840 chapters_ = chapters;
1841 chapters_size_ = size;
1846 uint64_t Chapters::WriteEdition(IMkvWriter* writer) const {
1847 uint64_t payload_size = 0;
1849 for (int idx = 0; idx < chapters_count_; ++idx) {
1850 const Chapter& chapter = chapters_[idx];
1851 payload_size += chapter.WriteAtom(NULL);
1854 const uint64_t edition_size =
1855 EbmlMasterElementSize(libwebm::kMkvEditionEntry, payload_size) +
1858 if (writer == NULL) // return size only
1859 return edition_size;
1861 const int64_t start = writer->Position();
1863 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEditionEntry, payload_size))
1866 for (int idx = 0; idx < chapters_count_; ++idx) {
1867 const Chapter& chapter = chapters_[idx];
1869 const uint64_t chapter_size = chapter.WriteAtom(writer);
1870 if (chapter_size == 0) // error
1874 const int64_t stop = writer->Position();
1876 if (stop >= start && uint64_t(stop - start) != edition_size)
1879 return edition_size;
1884 bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) {
1885 if (!ExpandSimpleTagsArray())
1888 SimpleTag& st = simple_tags_[simple_tags_count_++];
1891 if (!st.set_tag_name(tag_name))
1894 if (!st.set_tag_string(tag_string))
1901 simple_tags_ = NULL;
1902 simple_tags_size_ = 0;
1903 simple_tags_count_ = 0;
1908 void Tag::ShallowCopy(Tag* dst) const {
1909 dst->simple_tags_ = simple_tags_;
1910 dst->simple_tags_size_ = simple_tags_size_;
1911 dst->simple_tags_count_ = simple_tags_count_;
1915 while (simple_tags_count_ > 0) {
1916 SimpleTag& st = simple_tags_[--simple_tags_count_];
1920 delete[] simple_tags_;
1921 simple_tags_ = NULL;
1923 simple_tags_size_ = 0;
1926 bool Tag::ExpandSimpleTagsArray() {
1927 if (simple_tags_size_ > simple_tags_count_)
1928 return true; // nothing to do yet
1930 const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_;
1932 SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT
1933 if (simple_tags == NULL)
1936 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1937 simple_tags[idx] = simple_tags_[idx]; // shallow copy
1940 delete[] simple_tags_;
1942 simple_tags_ = simple_tags;
1943 simple_tags_size_ = size;
1948 uint64_t Tag::Write(IMkvWriter* writer) const {
1949 uint64_t payload_size = 0;
1951 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1952 const SimpleTag& st = simple_tags_[idx];
1953 payload_size += st.Write(NULL);
1956 const uint64_t tag_size =
1957 EbmlMasterElementSize(libwebm::kMkvTag, payload_size) + payload_size;
1962 const int64_t start = writer->Position();
1964 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTag, payload_size))
1967 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1968 const SimpleTag& st = simple_tags_[idx];
1970 if (!st.Write(writer))
1974 const int64_t stop = writer->Position();
1976 if (stop >= start && uint64_t(stop - start) != tag_size)
1984 void Tag::SimpleTag::Init() {
1989 void Tag::SimpleTag::Clear() {
1990 StrCpy(NULL, &tag_name_);
1991 StrCpy(NULL, &tag_string_);
1994 bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
1995 return StrCpy(tag_name, &tag_name_);
1998 bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
1999 return StrCpy(tag_string, &tag_string_);
2002 uint64_t Tag::SimpleTag::Write(IMkvWriter* writer) const {
2003 uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_);
2005 payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_);
2007 const uint64_t simple_tag_size =
2008 EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) +
2012 return simple_tag_size;
2014 const int64_t start = writer->Position();
2016 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size))
2019 if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_))
2022 if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_))
2025 const int64_t stop = writer->Position();
2027 if (stop >= start && uint64_t(stop - start) != simple_tag_size)
2030 return simple_tag_size;
2035 Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
2038 while (tags_count_ > 0) {
2039 Tag& tag = tags_[--tags_count_];
2047 int Tags::Count() const { return tags_count_; }
2049 Tag* Tags::AddTag() {
2050 if (!ExpandTagsArray())
2053 Tag& tag = tags_[tags_count_++];
2058 bool Tags::Write(IMkvWriter* writer) const {
2062 uint64_t payload_size = 0;
2064 for (int idx = 0; idx < tags_count_; ++idx) {
2065 const Tag& tag = tags_[idx];
2066 payload_size += tag.Write(NULL);
2069 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTags, payload_size))
2072 const int64_t start = writer->Position();
2074 for (int idx = 0; idx < tags_count_; ++idx) {
2075 const Tag& tag = tags_[idx];
2077 const uint64_t tag_size = tag.Write(writer);
2078 if (tag_size == 0) // error
2082 const int64_t stop = writer->Position();
2084 if (stop >= start && uint64_t(stop - start) != payload_size)
2090 bool Tags::ExpandTagsArray() {
2091 if (tags_size_ > tags_count_)
2092 return true; // nothing to do yet
2094 const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_;
2096 Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT
2100 for (int idx = 0; idx < tags_count_; ++idx) {
2101 const Tag& src = tags_[idx];
2102 Tag* const dst = tags + idx;
2103 src.ShallowCopy(dst);
2114 ///////////////////////////////////////////////////////////////
2118 Cluster::Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale,
2119 bool write_last_frame_with_duration)
2122 header_written_(false),
2124 position_for_cues_(cues_pos),
2126 timecode_(timecode),
2127 timecode_scale_(timecode_scale),
2128 write_last_frame_with_duration_(write_last_frame_with_duration),
2131 Cluster::~Cluster() {}
2133 bool Cluster::Init(IMkvWriter* ptr_writer) {
2137 writer_ = ptr_writer;
2141 bool Cluster::AddFrame(const Frame* const frame) {
2142 return QueueOrWriteFrame(frame);
2145 bool Cluster::AddFrame(const uint8_t* data, uint64_t length,
2146 uint64_t track_number, uint64_t abs_timecode,
2149 if (!frame.Init(data, length))
2151 frame.set_track_number(track_number);
2152 frame.set_timestamp(abs_timecode);
2153 frame.set_is_key(is_key);
2154 return QueueOrWriteFrame(&frame);
2157 bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
2158 const uint8_t* additional,
2159 uint64_t additional_length,
2160 uint64_t add_id, uint64_t track_number,
2161 uint64_t abs_timecode, bool is_key) {
2162 if (!additional || additional_length == 0) {
2166 if (!frame.Init(data, length) ||
2167 !frame.AddAdditionalData(additional, additional_length, add_id)) {
2170 frame.set_track_number(track_number);
2171 frame.set_timestamp(abs_timecode);
2172 frame.set_is_key(is_key);
2173 return QueueOrWriteFrame(&frame);
2176 bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
2177 int64_t discard_padding,
2178 uint64_t track_number,
2179 uint64_t abs_timecode, bool is_key) {
2181 if (!frame.Init(data, length))
2183 frame.set_discard_padding(discard_padding);
2184 frame.set_track_number(track_number);
2185 frame.set_timestamp(abs_timecode);
2186 frame.set_is_key(is_key);
2187 return QueueOrWriteFrame(&frame);
2190 bool Cluster::AddMetadata(const uint8_t* data, uint64_t length,
2191 uint64_t track_number, uint64_t abs_timecode,
2192 uint64_t duration_timecode) {
2194 if (!frame.Init(data, length))
2196 frame.set_track_number(track_number);
2197 frame.set_timestamp(abs_timecode);
2198 frame.set_duration(duration_timecode);
2199 frame.set_is_key(true); // All metadata blocks are keyframes.
2200 return QueueOrWriteFrame(&frame);
2203 void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; }
2205 bool Cluster::Finalize() {
2206 return !write_last_frame_with_duration_ && Finalize(false, 0);
2209 bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) {
2210 if (!writer_ || finalized_)
2213 if (write_last_frame_with_duration_) {
2214 // Write out held back Frames. This essentially performs a k-way merge
2215 // across all tracks in the increasing order of timestamps.
2216 while (!stored_frames_.empty()) {
2217 Frame* frame = stored_frames_.begin()->second.front();
2219 // Get the next frame to write (frame with least timestamp across all
2221 for (FrameMapIterator frames_iterator = ++stored_frames_.begin();
2222 frames_iterator != stored_frames_.end(); ++frames_iterator) {
2223 if (frames_iterator->second.front()->timestamp() < frame->timestamp()) {
2224 frame = frames_iterator->second.front();
2228 // Set the duration if it's the last frame for the track.
2229 if (set_last_frame_duration &&
2230 stored_frames_[frame->track_number()].size() == 1 &&
2231 !frame->duration_set()) {
2232 frame->set_duration(duration - frame->timestamp());
2233 if (!frame->is_key() && !frame->reference_block_timestamp_set()) {
2234 frame->set_reference_block_timestamp(
2235 last_block_timestamp_[frame->track_number()]);
2239 // Write the frame and remove it from |stored_frames_|.
2240 const bool wrote_frame = DoWriteFrame(frame);
2241 stored_frames_[frame->track_number()].pop_front();
2242 if (stored_frames_[frame->track_number()].empty()) {
2243 stored_frames_.erase(frame->track_number());
2251 if (size_position_ == -1)
2254 if (writer_->Seekable()) {
2255 const int64_t pos = writer_->Position();
2257 if (writer_->Position(size_position_))
2260 if (WriteUIntSize(writer_, payload_size(), 8))
2263 if (writer_->Position(pos))
2272 uint64_t Cluster::Size() const {
2273 const uint64_t element_size =
2274 EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) +
2276 return element_size;
2279 bool Cluster::PreWriteBlock() {
2283 if (!header_written_) {
2284 if (!WriteClusterHeader())
2291 void Cluster::PostWriteBlock(uint64_t element_size) {
2292 AddPayloadSize(element_size);
2296 int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const {
2297 const int64_t cluster_timecode = this->Cluster::timecode();
2298 const int64_t rel_timecode =
2299 static_cast<int64_t>(abs_timecode) - cluster_timecode;
2301 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
2304 return rel_timecode;
2307 bool Cluster::DoWriteFrame(const Frame* const frame) {
2308 if (!frame || !frame->IsValid())
2311 if (!PreWriteBlock())
2314 const uint64_t element_size = WriteFrame(writer_, frame, this);
2315 if (element_size == 0)
2318 PostWriteBlock(element_size);
2319 last_block_timestamp_[frame->track_number()] = frame->timestamp();
2323 bool Cluster::QueueOrWriteFrame(const Frame* const frame) {
2324 if (!frame || !frame->IsValid())
2327 // If |write_last_frame_with_duration_| is not set, then write the frame right
2329 if (!write_last_frame_with_duration_) {
2330 return DoWriteFrame(frame);
2333 // Queue the current frame.
2334 uint64_t track_number = frame->track_number();
2335 Frame* const frame_to_store = new Frame();
2336 frame_to_store->CopyFrom(*frame);
2337 stored_frames_[track_number].push_back(frame_to_store);
2339 // Iterate through all queued frames in the current track except the last one
2340 // and write it if it is okay to do so (i.e.) no other track has an held back
2341 // frame with timestamp <= the timestamp of the frame in question.
2342 std::vector<std::list<Frame*>::iterator> frames_to_erase;
2343 for (std::list<Frame *>::iterator
2344 current_track_iterator = stored_frames_[track_number].begin(),
2345 end = --stored_frames_[track_number].end();
2346 current_track_iterator != end; ++current_track_iterator) {
2347 const Frame* const frame_to_write = *current_track_iterator;
2348 bool okay_to_write = true;
2349 for (FrameMapIterator track_iterator = stored_frames_.begin();
2350 track_iterator != stored_frames_.end(); ++track_iterator) {
2351 if (track_iterator->first == track_number) {
2354 if (track_iterator->second.front()->timestamp() <
2355 frame_to_write->timestamp()) {
2356 okay_to_write = false;
2360 if (okay_to_write) {
2361 const bool wrote_frame = DoWriteFrame(frame_to_write);
2362 delete frame_to_write;
2365 frames_to_erase.push_back(current_track_iterator);
2370 for (std::vector<std::list<Frame*>::iterator>::iterator iterator =
2371 frames_to_erase.begin();
2372 iterator != frames_to_erase.end(); ++iterator) {
2373 stored_frames_[track_number].erase(*iterator);
2378 bool Cluster::WriteClusterHeader() {
2382 if (WriteID(writer_, libwebm::kMkvCluster))
2386 size_position_ = writer_->Position();
2388 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
2389 // bytes because we do not know how big our cluster will be.
2390 if (SerializeInt(writer_, kEbmlUnknownValue, 8))
2393 if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode()))
2395 AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode()));
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),
2728 doc_type_version_(kDefaultDocTypeVersion),
2729 doc_type_version_written_(0),
2730 writer_cluster_(NULL),
2732 writer_header_(NULL) {
2733 const time_t curr_time = time(NULL);
2734 seed_ = static_cast<unsigned int>(curr_time);
2740 Segment::~Segment() {
2741 if (cluster_list_) {
2742 for (int32_t i = 0; i < cluster_list_size_; ++i) {
2743 Cluster* const cluster = cluster_list_[i];
2746 delete[] cluster_list_;
2750 for (int32_t i = 0; i < frames_size_; ++i) {
2751 Frame* const frame = frames_[i];
2757 delete[] chunk_name_;
2758 delete[] chunking_base_name_;
2760 if (chunk_writer_cluster_) {
2761 chunk_writer_cluster_->Close();
2762 delete chunk_writer_cluster_;
2764 if (chunk_writer_cues_) {
2765 chunk_writer_cues_->Close();
2766 delete chunk_writer_cues_;
2768 if (chunk_writer_header_) {
2769 chunk_writer_header_->Close();
2770 delete chunk_writer_header_;
2774 void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index,
2775 uint64_t* cues_size) {
2776 CuePoint* const cue_point = cues_.GetCueByIndex(index);
2777 if (cue_point == NULL)
2779 const uint64_t old_cue_point_size = cue_point->Size();
2780 const uint64_t cluster_pos = cue_point->cluster_pos() + diff;
2781 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
2782 // New size of the cue is computed as follows
2783 // Let a = current sum of size of all CuePoints
2784 // Let b = Increase in Cue Point's size due to this iteration
2785 // Let c = Increase in size of Cues Element's length due to this iteration
2786 // (This is computed as CodedSize(a + b) - CodedSize(a))
2787 // Let d = b + c. Now d is the |diff| passed to the next recursive call.
2788 // Let e = a + b. Now e is the |cues_size| passed to the next recursive
2790 const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size;
2791 const uint64_t cue_size_diff =
2792 GetCodedUIntSize(*cues_size + cue_point_size_diff) -
2793 GetCodedUIntSize(*cues_size);
2794 *cues_size += cue_point_size_diff;
2795 diff = cue_size_diff + cue_point_size_diff;
2797 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) {
2798 MoveCuesBeforeClustersHelper(diff, i, cues_size);
2803 void Segment::MoveCuesBeforeClusters() {
2804 const uint64_t current_cue_size = cues_.Size();
2805 uint64_t cue_size = 0;
2806 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2807 cue_size += cues_.GetCueByIndex(i)->Size();
2808 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
2809 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
2811 // Adjust the Seek Entry to reflect the change in position
2812 // of Cluster and Cues
2813 int32_t cluster_index = 0;
2814 int32_t cues_index = 0;
2815 for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) {
2816 if (seek_head_.GetId(i) == libwebm::kMkvCluster)
2818 if (seek_head_.GetId(i) == libwebm::kMkvCues)
2821 seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues,
2822 seek_head_.GetPosition(cluster_index));
2823 seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster,
2824 cues_.Size() + seek_head_.GetPosition(cues_index));
2827 bool Segment::Init(IMkvWriter* ptr_writer) {
2831 writer_cluster_ = ptr_writer;
2832 writer_cues_ = ptr_writer;
2833 writer_header_ = ptr_writer;
2834 return segment_info_.Init();
2837 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
2838 IMkvWriter* writer) {
2839 if (!writer->Seekable() || chunking_)
2841 const int64_t cluster_offset =
2842 cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster);
2844 // Copy the headers.
2845 if (!ChunkedCopy(reader, writer, 0, cluster_offset))
2848 // Recompute cue positions and seek entries.
2849 MoveCuesBeforeClusters();
2851 // Write cues and seek entries.
2852 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
2853 // second time with a different writer object. But the name Finalize() doesn't
2854 // indicate something we want to call more than once. So consider renaming it
2855 // to write() or some such.
2856 if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
2859 // Copy the Clusters.
2860 if (!ChunkedCopy(reader, writer, cluster_offset,
2861 cluster_end_offset_ - cluster_offset))
2864 // Update the Segment size in case the Cues size has changed.
2865 const int64_t pos = writer->Position();
2866 const int64_t segment_size = writer->Position() - payload_pos_;
2867 if (writer->Position(size_position_) ||
2868 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
2873 bool Segment::Finalize() {
2874 if (WriteFramesAll() < 0)
2877 if (cluster_list_size_ > 0) {
2878 // Update last cluster's size
2879 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2881 // For the last frame of the last Cluster, we don't write it as a BlockGroup
2882 // with Duration unless the frame itself has duration set explicitly.
2883 if (!old_cluster || !old_cluster->Finalize(false, 0))
2887 if (mode_ == kFile) {
2888 if (chunking_ && chunk_writer_cluster_) {
2889 chunk_writer_cluster_->Close();
2893 const double duration =
2894 (static_cast<double>(last_timestamp_) + last_block_duration_) /
2895 segment_info_.timecode_scale();
2896 segment_info_.set_duration(duration);
2897 if (!segment_info_.Finalize(writer_header_))
2901 if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset()))
2905 if (!chunk_writer_cues_)
2909 if (!UpdateChunkName("cues", &name))
2912 const bool cues_open = chunk_writer_cues_->Open(name);
2918 cluster_end_offset_ = writer_cluster_->Position();
2920 // Write the seek headers and cues
2922 if (!cues_.Write(writer_cues_))
2925 if (!seek_head_.Finalize(writer_header_))
2928 if (writer_header_->Seekable()) {
2929 if (size_position_ == -1)
2932 const int64_t segment_size = MaxOffset();
2933 if (segment_size < 1)
2936 const int64_t pos = writer_header_->Position();
2937 UpdateDocTypeVersion();
2938 if (doc_type_version_ != doc_type_version_written_) {
2939 if (writer_header_->Position(0))
2942 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2944 if (writer_header_->Position() != ebml_header_size_)
2947 doc_type_version_written_ = doc_type_version_;
2950 if (writer_header_->Position(size_position_))
2953 if (WriteUIntSize(writer_header_, segment_size, 8))
2956 if (writer_header_->Position(pos))
2961 // Do not close any writers until the segment size has been written,
2962 // otherwise the size may be off.
2963 if (!chunk_writer_cues_ || !chunk_writer_header_)
2966 chunk_writer_cues_->Close();
2967 chunk_writer_header_->Close();
2974 Track* Segment::AddTrack(int32_t number) {
2975 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
2980 if (!tracks_.AddTrack(track, number)) {
2988 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
2990 Tag* Segment::AddTag() { return tags_.AddTag(); }
2992 uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) {
2993 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
2997 track->set_type(Tracks::kVideo);
2998 track->set_codec_id(Tracks::kVp8CodecId);
2999 track->set_width(width);
3000 track->set_height(height);
3002 tracks_.AddTrack(track, number);
3005 return track->number();
3008 bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) {
3009 if (cluster_list_size_ < 1)
3012 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3016 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
3020 cue->set_time(timestamp / segment_info_.timecode_scale());
3021 cue->set_block_number(cluster->blocks_added());
3022 cue->set_cluster_pos(cluster->position_for_cues());
3023 cue->set_track(track);
3024 if (!cues_.AddCue(cue))
3027 new_cuepoint_ = false;
3031 uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels,
3033 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
3037 track->set_type(Tracks::kAudio);
3038 track->set_codec_id(Tracks::kVorbisCodecId);
3039 track->set_sample_rate(sample_rate);
3040 track->set_channels(channels);
3042 tracks_.AddTrack(track, number);
3044 return track->number();
3047 bool Segment::AddFrame(const uint8_t* data, uint64_t length,
3048 uint64_t track_number, uint64_t timestamp, bool is_key) {
3053 if (!frame.Init(data, length))
3055 frame.set_track_number(track_number);
3056 frame.set_timestamp(timestamp);
3057 frame.set_is_key(is_key);
3058 return AddGenericFrame(&frame);
3061 bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
3062 const uint8_t* additional,
3063 uint64_t additional_length,
3064 uint64_t add_id, uint64_t track_number,
3065 uint64_t timestamp, bool is_key) {
3066 if (!data || !additional)
3070 if (!frame.Init(data, length) ||
3071 !frame.AddAdditionalData(additional, additional_length, add_id)) {
3074 frame.set_track_number(track_number);
3075 frame.set_timestamp(timestamp);
3076 frame.set_is_key(is_key);
3077 return AddGenericFrame(&frame);
3080 bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
3081 int64_t discard_padding,
3082 uint64_t track_number,
3083 uint64_t timestamp, bool is_key) {
3088 if (!frame.Init(data, length))
3090 frame.set_discard_padding(discard_padding);
3091 frame.set_track_number(track_number);
3092 frame.set_timestamp(timestamp);
3093 frame.set_is_key(is_key);
3094 return AddGenericFrame(&frame);
3097 bool Segment::AddMetadata(const uint8_t* data, uint64_t length,
3098 uint64_t track_number, uint64_t timestamp_ns,
3099 uint64_t duration_ns) {
3104 if (!frame.Init(data, length))
3106 frame.set_track_number(track_number);
3107 frame.set_timestamp(timestamp_ns);
3108 frame.set_duration(duration_ns);
3109 frame.set_is_key(true); // All metadata blocks are keyframes.
3110 return AddGenericFrame(&frame);
3113 bool Segment::AddGenericFrame(const Frame* frame) {
3117 if (!CheckHeaderInfo())
3120 // Check for non-monotonically increasing timestamps.
3121 if (frame->timestamp() < last_timestamp_)
3124 // Check if the track number is valid.
3125 if (!tracks_.GetTrackByNumber(frame->track_number()))
3128 if (frame->discard_padding() != 0)
3129 doc_type_version_ = 4;
3131 // If the segment has a video track hold onto audio frames to make sure the
3132 // audio that is associated with the start time of a video key-frame is
3133 // muxed into the same cluster.
3134 if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
3135 !force_new_cluster_) {
3136 Frame* const new_frame = new (std::nothrow) Frame();
3137 if (!new_frame || !new_frame->CopyFrom(*frame))
3139 return QueueFrame(new_frame);
3142 if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
3147 if (cluster_list_size_ < 1)
3150 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3154 // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
3155 // if it is not set already.
3156 bool frame_created = false;
3157 if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
3158 !frame->reference_block_timestamp_set()) {
3159 Frame* const new_frame = new (std::nothrow) Frame();
3160 if (!new_frame->CopyFrom(*frame))
3162 new_frame->set_reference_block_timestamp(
3163 last_track_timestamp_[frame->track_number() - 1]);
3165 frame_created = true;
3168 if (!cluster->AddFrame(frame))
3171 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3172 if (!AddCuePoint(frame->timestamp(), cues_track_))
3176 last_timestamp_ = frame->timestamp();
3177 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3178 last_block_duration_ = frame->duration();
3186 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
3188 void Segment::AccurateClusterDuration(bool accurate_cluster_duration) {
3189 accurate_cluster_duration_ = accurate_cluster_duration;
3192 bool Segment::SetChunking(bool chunking, const char* filename) {
3193 if (chunk_count_ > 0)
3200 // Check if we are being set to what is already set.
3201 if (chunking_ && !strcmp(filename, chunking_base_name_))
3204 const size_t name_length = strlen(filename) + 1;
3205 char* const temp = new (std::nothrow) char[name_length]; // NOLINT
3210 strcpy_s(temp, name_length, filename);
3212 strcpy(temp, filename);
3215 delete[] chunking_base_name_;
3216 chunking_base_name_ = temp;
3218 if (!UpdateChunkName("chk", &chunk_name_))
3221 if (!chunk_writer_cluster_) {
3222 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
3223 if (!chunk_writer_cluster_)
3227 if (!chunk_writer_cues_) {
3228 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
3229 if (!chunk_writer_cues_)
3233 if (!chunk_writer_header_) {
3234 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
3235 if (!chunk_writer_header_)
3239 if (!chunk_writer_cluster_->Open(chunk_name_))
3242 const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
3243 char* const header = new (std::nothrow) char[header_length]; // NOLINT
3248 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
3249 strcat_s(header, header_length, ".hdr");
3251 strcpy(header, chunking_base_name_);
3252 strcat(header, ".hdr");
3254 if (!chunk_writer_header_->Open(header)) {
3259 writer_cluster_ = chunk_writer_cluster_;
3260 writer_cues_ = chunk_writer_cues_;
3261 writer_header_ = chunk_writer_header_;
3266 chunking_ = chunking;
3271 bool Segment::CuesTrack(uint64_t track_number) {
3272 const Track* const track = GetTrackByNumber(track_number);
3276 cues_track_ = track_number;
3280 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
3282 Track* Segment::GetTrackByNumber(uint64_t track_number) const {
3283 return tracks_.GetTrackByNumber(track_number);
3286 bool Segment::WriteSegmentHeader() {
3287 UpdateDocTypeVersion();
3289 // TODO(fgalligan): Support more than one segment.
3290 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
3292 doc_type_version_written_ = doc_type_version_;
3293 ebml_header_size_ = static_cast<int32_t>(writer_header_->Position());
3295 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
3296 // will write over duration when the file is finalized.
3297 if (WriteID(writer_header_, libwebm::kMkvSegment))
3301 size_position_ = writer_header_->Position();
3303 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
3304 // bytes because if we are going to overwrite the segment size later we do
3305 // not know how big our segment will be.
3306 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
3309 payload_pos_ = writer_header_->Position();
3311 if (mode_ == kFile && writer_header_->Seekable()) {
3312 // Set the duration > 0.0 so SegmentInfo will write out the duration. When
3313 // the muxer is done writing we will set the correct duration and have
3314 // SegmentInfo upadte it.
3315 segment_info_.set_duration(1.0);
3317 if (!seek_head_.Write(writer_header_))
3321 if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset()))
3323 if (!segment_info_.Write(writer_header_))
3326 if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset()))
3328 if (!tracks_.Write(writer_header_))
3331 if (chapters_.Count() > 0) {
3332 if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset()))
3334 if (!chapters_.Write(writer_header_))
3338 if (tags_.Count() > 0) {
3339 if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset()))
3341 if (!tags_.Write(writer_header_))
3345 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
3346 if (!chunk_writer_header_)
3349 chunk_writer_header_->Close();
3352 header_written_ = true;
3357 // Here we are testing whether to create a new cluster, given a frame
3358 // having time frame_timestamp_ns.
3360 int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns,
3361 bool is_key) const {
3362 if (force_new_cluster_)
3365 // If no clusters have been created yet, then create a new cluster
3366 // and write this frame immediately, in the new cluster. This path
3367 // should only be followed once, the first time we attempt to write
3370 if (cluster_list_size_ <= 0)
3373 // There exists at least one cluster. We must compare the frame to
3374 // the last cluster, in order to determine whether the frame is
3375 // written to the existing cluster, or that a new cluster should be
3378 const uint64_t timecode_scale = segment_info_.timecode_scale();
3379 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3381 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
3382 const uint64_t last_cluster_timecode = last_cluster->timecode();
3384 // For completeness we test for the case when the frame's timecode
3385 // is less than the cluster's timecode. Although in principle that
3386 // is allowed, this muxer doesn't actually write clusters like that,
3387 // so this indicates a bug somewhere in our algorithm.
3389 if (frame_timecode < last_cluster_timecode) // should never happen
3392 // If the frame has a timestamp significantly larger than the last
3393 // cluster (in Matroska, cluster-relative timestamps are serialized
3394 // using a 16-bit signed integer), then we cannot write this frame
3395 // to that cluster, and so we must create a new cluster.
3397 const int64_t delta_timecode = frame_timecode - last_cluster_timecode;
3399 if (delta_timecode > kMaxBlockTimecode)
3402 // We decide to create a new cluster when we have a video keyframe.
3403 // This will flush queued (audio) frames, and write the keyframe
3404 // immediately, in the newly-created cluster.
3406 if (is_key && tracks_.TrackIsVideo(track_number))
3409 // Create a new cluster if we have accumulated too many frames
3410 // already, where "too many" is defined as "the total time of frames
3411 // in the cluster exceeds a threshold".
3413 const uint64_t delta_ns = delta_timecode * timecode_scale;
3415 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
3418 // This is similar to the case above, with the difference that a new
3419 // cluster is created when the size of the current cluster exceeds a
3422 const uint64_t cluster_size = last_cluster->payload_size();
3424 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
3427 // There's no need to create a new cluster, so emit this frame now.
3432 bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) {
3433 const int32_t new_size = cluster_list_size_ + 1;
3435 if (new_size > cluster_list_capacity_) {
3436 // Add more clusters.
3437 const int32_t new_capacity =
3438 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
3439 Cluster** const clusters =
3440 new (std::nothrow) Cluster*[new_capacity]; // NOLINT
3444 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3445 clusters[i] = cluster_list_[i];
3448 delete[] cluster_list_;
3450 cluster_list_ = clusters;
3451 cluster_list_capacity_ = new_capacity;
3454 if (!WriteFramesLessThan(frame_timestamp_ns))
3457 if (cluster_list_size_ > 0) {
3458 // Update old cluster's size
3459 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
3461 if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns))
3466 new_cuepoint_ = true;
3468 if (chunking_ && cluster_list_size_ > 0) {
3469 chunk_writer_cluster_->Close();
3472 if (!UpdateChunkName("chk", &chunk_name_))
3474 if (!chunk_writer_cluster_->Open(chunk_name_))
3478 const uint64_t timecode_scale = segment_info_.timecode_scale();
3479 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
3481 uint64_t cluster_timecode = frame_timecode;
3483 if (frames_size_ > 0) {
3484 const Frame* const f = frames_[0]; // earliest queued frame
3485 const uint64_t ns = f->timestamp();
3486 const uint64_t tc = ns / timecode_scale;
3488 if (tc < cluster_timecode)
3489 cluster_timecode = tc;
3492 Cluster*& cluster = cluster_list_[cluster_list_size_];
3493 const int64_t offset = MaxOffset();
3494 cluster = new (std::nothrow)
3495 Cluster(cluster_timecode, offset, segment_info_.timecode_scale(),
3496 accurate_cluster_duration_);
3500 if (!cluster->Init(writer_cluster_))
3503 cluster_list_size_ = new_size;
3507 bool Segment::DoNewClusterProcessing(uint64_t track_number,
3508 uint64_t frame_timestamp_ns, bool is_key) {
3510 // Based on the characteristics of the current frame and current
3511 // cluster, decide whether to create a new cluster.
3512 const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
3513 if (result < 0) // error
3516 // Always set force_new_cluster_ to false after TestFrame.
3517 force_new_cluster_ = false;
3519 // A non-zero result means create a new cluster.
3520 if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
3523 // Write queued (audio) frames.
3524 const int frame_count = WriteFramesAll();
3525 if (frame_count < 0) // error
3528 // Write the current frame to the current cluster (if TestFrame
3529 // returns 0) or to a newly created cluster (TestFrame returns 1).
3533 // TestFrame returned 2, which means there was a large time
3534 // difference between the cluster and the frame itself. Do the
3535 // test again, comparing the frame to the new cluster.
3539 bool Segment::CheckHeaderInfo() {
3540 if (!header_written_) {
3541 if (!WriteSegmentHeader())
3544 if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset()))
3547 if (output_cues_ && cues_track_ == 0) {
3548 // Check for a video track
3549 for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) {
3550 const Track* const track = tracks_.GetTrackByIndex(i);
3554 if (tracks_.TrackIsVideo(track->number())) {
3555 cues_track_ = track->number();
3560 // Set first track found
3561 if (cues_track_ == 0) {
3562 const Track* const track = tracks_.GetTrackByIndex(0);
3566 cues_track_ = track->number();
3573 void Segment::UpdateDocTypeVersion() {
3574 for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) {
3575 const Track* track = tracks_.GetTrackByIndex(index);
3578 if ((track->codec_delay() || track->seek_pre_roll()) &&
3579 doc_type_version_ < 4) {
3580 doc_type_version_ = 4;
3586 bool Segment::UpdateChunkName(const char* ext, char** name) const {
3592 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3594 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3597 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
3598 char* const str = new (std::nothrow) char[length]; // NOLINT
3603 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
3604 strcat_s(str, length, ext_chk);
3606 strcpy(str, chunking_base_name_);
3607 strcat(str, ext_chk);
3616 int64_t Segment::MaxOffset() {
3617 if (!writer_header_)
3620 int64_t offset = writer_header_->Position() - payload_pos_;
3623 for (int32_t i = 0; i < cluster_list_size_; ++i) {
3624 Cluster* const cluster = cluster_list_[i];
3625 offset += cluster->Size();
3629 offset += writer_cues_->Position();
3635 bool Segment::QueueFrame(Frame* frame) {
3636 const int32_t new_size = frames_size_ + 1;
3638 if (new_size > frames_capacity_) {
3640 const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
3642 if (new_capacity < 1)
3645 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
3649 for (int32_t i = 0; i < frames_size_; ++i) {
3650 frames[i] = frames_[i];
3655 frames_capacity_ = new_capacity;
3658 frames_[frames_size_++] = frame;
3663 int Segment::WriteFramesAll() {
3664 if (frames_ == NULL)
3667 if (cluster_list_size_ < 1)
3670 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3675 for (int32_t i = 0; i < frames_size_; ++i) {
3676 Frame*& frame = frames_[i];
3677 // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
3678 // places where |doc_type_version_| needs to be updated.
3679 if (frame->discard_padding() != 0)
3680 doc_type_version_ = 4;
3681 if (!cluster->AddFrame(frame))
3684 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3685 if (!AddCuePoint(frame->timestamp(), cues_track_))
3689 if (frame->timestamp() > last_timestamp_) {
3690 last_timestamp_ = frame->timestamp();
3691 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3698 const int result = frames_size_;
3704 bool Segment::WriteFramesLessThan(uint64_t timestamp) {
3705 // Check |cluster_list_size_| to see if this is the first cluster. If it is
3706 // the first cluster the audio frames that are less than the first video
3707 // timesatmp will be written in a later step.
3708 if (frames_size_ > 0 && cluster_list_size_ > 0) {
3712 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3716 int32_t shift_left = 0;
3718 // TODO(fgalligan): Change this to use the durations of frames instead of
3719 // the next frame's start time if the duration is accurate.
3720 for (int32_t i = 1; i < frames_size_; ++i) {
3721 const Frame* const frame_curr = frames_[i];
3723 if (frame_curr->timestamp() > timestamp)
3726 const Frame* const frame_prev = frames_[i - 1];
3727 if (frame_prev->discard_padding() != 0)
3728 doc_type_version_ = 4;
3729 if (!cluster->AddFrame(frame_prev))
3732 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
3733 if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
3738 if (frame_prev->timestamp() > last_timestamp_) {
3739 last_timestamp_ = frame_prev->timestamp();
3740 last_track_timestamp_[frame_prev->track_number() - 1] =
3741 frame_prev->timestamp();
3747 if (shift_left > 0) {
3748 if (shift_left >= frames_size_)
3751 const int32_t new_frames_size = frames_size_ - shift_left;
3752 for (int32_t i = 0; i < new_frames_size; ++i) {
3753 frames_[i] = frames_[i + shift_left];
3756 frames_size_ = new_frames_size;
3763 } // namespace mkvmuxer