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.hpp"
18 #include "mkvmuxerutil.hpp"
19 #include "mkvparser.hpp"
20 #include "mkvwriter.hpp"
21 #include "webmids.hpp"
24 // Disable MSVC warnings that suggest making code non-portable.
25 #pragma warning(disable : 4996)
31 // Deallocate the string designated by |dst|, and then copy the |src|
32 // string to |dst|. The caller owns both the |src| string and the
33 // |dst| copy (hence the caller is responsible for eventually
34 // deallocating the strings, either directly, or indirectly via
35 // StrCpy). Returns true if the source string was successfully copied
36 // to the destination.
37 bool StrCpy(const char* src, char** dst_ptr) {
41 char*& dst = *dst_ptr;
49 const size_t size = strlen(src) + 1;
51 dst = new (std::nothrow) char[size]; // NOLINT
55 strcpy(dst, src); // NOLINT
60 ///////////////////////////////////////////////////////////////
64 IMkvWriter::IMkvWriter() {}
66 IMkvWriter::~IMkvWriter() {}
68 bool WriteEbmlHeader(IMkvWriter* writer, uint64 doc_type_version) {
70 uint64 size = EbmlElementSize(kMkvEBMLVersion, 1ULL);
71 size += EbmlElementSize(kMkvEBMLReadVersion, 1ULL);
72 size += EbmlElementSize(kMkvEBMLMaxIDLength, 4ULL);
73 size += EbmlElementSize(kMkvEBMLMaxSizeLength, 8ULL);
74 size += EbmlElementSize(kMkvDocType, "webm");
75 size += EbmlElementSize(kMkvDocTypeVersion, doc_type_version);
76 size += EbmlElementSize(kMkvDocTypeReadVersion, 2ULL);
78 if (!WriteEbmlMasterElement(writer, kMkvEBML, size))
80 if (!WriteEbmlElement(writer, kMkvEBMLVersion, 1ULL))
82 if (!WriteEbmlElement(writer, kMkvEBMLReadVersion, 1ULL))
84 if (!WriteEbmlElement(writer, kMkvEBMLMaxIDLength, 4ULL))
86 if (!WriteEbmlElement(writer, kMkvEBMLMaxSizeLength, 8ULL))
88 if (!WriteEbmlElement(writer, kMkvDocType, "webm"))
90 if (!WriteEbmlElement(writer, kMkvDocTypeVersion, doc_type_version))
92 if (!WriteEbmlElement(writer, kMkvDocTypeReadVersion, 2ULL))
98 bool WriteEbmlHeader(IMkvWriter* writer) {
99 return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion);
102 bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst,
103 mkvmuxer::int64 start, int64 size) {
104 // TODO(vigneshv): Check if this is a reasonable value.
105 const uint32 kBufSize = 2048;
106 uint8* buf = new uint8[kBufSize];
107 int64 offset = start;
109 const int64 read_len = (size > kBufSize) ? kBufSize : size;
110 if (source->Read(offset, static_cast<long>(read_len), buf))
112 dst->Write(buf, static_cast<uint32>(read_len));
120 ///////////////////////////////////////////////////////////////
127 additional_length_(0),
135 reference_block_timestamp_(0),
136 reference_block_timestamp_set_(false) {}
140 delete[] additional_;
143 bool Frame::CopyFrom(const Frame& frame) {
147 if (frame.length() > 0 && frame.frame() != NULL &&
148 !Init(frame.frame(), frame.length())) {
152 delete[] additional_;
154 additional_length_ = 0;
155 if (frame.additional_length() > 0 && frame.additional() != NULL &&
156 !AddAdditionalData(frame.additional(), frame.additional_length(),
160 duration_ = frame.duration();
161 is_key_ = frame.is_key();
162 track_number_ = frame.track_number();
163 timestamp_ = frame.timestamp();
164 discard_padding_ = frame.discard_padding();
168 bool Frame::Init(const uint8* frame, uint64 length) {
170 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
178 memcpy(frame_, frame, static_cast<size_t>(length_));
182 bool Frame::AddAdditionalData(const uint8* additional, uint64 length,
185 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
189 delete[] additional_;
191 additional_length_ = length;
194 memcpy(additional_, additional, static_cast<size_t>(additional_length_));
198 bool Frame::IsValid() const {
199 if (length_ == 0 || !frame_) {
202 if ((additional_length_ != 0 && !additional_) ||
203 (additional_ != NULL && additional_length_ == 0)) {
206 if (track_number_ == 0 || track_number_ > kMaxTrackNumber) {
209 if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) {
215 bool Frame::CanBeSimpleBlock() const {
216 return additional_ == NULL && discard_padding_ == 0 && duration_ == 0;
219 void Frame::set_reference_block_timestamp(int64 reference_block_timestamp) {
220 reference_block_timestamp_ = reference_block_timestamp;
221 reference_block_timestamp_set_ = true;
224 ///////////////////////////////////////////////////////////////
233 output_block_number_(true) {}
235 CuePoint::~CuePoint() {}
237 bool CuePoint::Write(IMkvWriter* writer) const {
238 if (!writer || track_ < 1 || cluster_pos_ < 1)
241 uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_);
242 size += EbmlElementSize(kMkvCueTrack, track_);
243 if (output_block_number_ && block_number_ > 1)
244 size += EbmlElementSize(kMkvCueBlockNumber, block_number_);
245 const uint64 track_pos_size =
246 EbmlMasterElementSize(kMkvCueTrackPositions, size) + size;
247 const uint64 payload_size =
248 EbmlElementSize(kMkvCueTime, time_) + track_pos_size;
250 if (!WriteEbmlMasterElement(writer, kMkvCuePoint, payload_size))
253 const int64 payload_position = writer->Position();
254 if (payload_position < 0)
257 if (!WriteEbmlElement(writer, kMkvCueTime, time_))
260 if (!WriteEbmlMasterElement(writer, kMkvCueTrackPositions, size))
262 if (!WriteEbmlElement(writer, kMkvCueTrack, track_))
264 if (!WriteEbmlElement(writer, kMkvCueClusterPosition, cluster_pos_))
266 if (output_block_number_ && block_number_ > 1)
267 if (!WriteEbmlElement(writer, kMkvCueBlockNumber, block_number_))
270 const int64 stop_position = writer->Position();
271 if (stop_position < 0)
274 if (stop_position - payload_position != static_cast<int64>(payload_size))
280 uint64 CuePoint::PayloadSize() const {
281 uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_);
282 size += EbmlElementSize(kMkvCueTrack, track_);
283 if (output_block_number_ && block_number_ > 1)
284 size += EbmlElementSize(kMkvCueBlockNumber, block_number_);
285 const uint64 track_pos_size =
286 EbmlMasterElementSize(kMkvCueTrackPositions, size) + size;
287 const uint64 payload_size =
288 EbmlElementSize(kMkvCueTime, time_) + track_pos_size;
293 uint64 CuePoint::Size() const {
294 const uint64 payload_size = PayloadSize();
295 return EbmlMasterElementSize(kMkvCuePoint, payload_size) + payload_size;
298 ///////////////////////////////////////////////////////////////
303 : cue_entries_capacity_(0),
304 cue_entries_size_(0),
306 output_block_number_(true) {}
310 for (int32 i = 0; i < cue_entries_size_; ++i) {
311 CuePoint* const cue = cue_entries_[i];
314 delete[] cue_entries_;
318 bool Cues::AddCue(CuePoint* cue) {
322 if ((cue_entries_size_ + 1) > cue_entries_capacity_) {
323 // Add more CuePoints.
324 const int32 new_capacity =
325 (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2;
327 if (new_capacity < 1)
330 CuePoint** const cues =
331 new (std::nothrow) CuePoint*[new_capacity]; // NOLINT
335 for (int32 i = 0; i < cue_entries_size_; ++i) {
336 cues[i] = cue_entries_[i];
339 delete[] cue_entries_;
342 cue_entries_capacity_ = new_capacity;
345 cue->set_output_block_number(output_block_number_);
346 cue_entries_[cue_entries_size_++] = cue;
350 CuePoint* Cues::GetCueByIndex(int32 index) const {
351 if (cue_entries_ == NULL)
354 if (index >= cue_entries_size_)
357 return cue_entries_[index];
360 uint64 Cues::Size() {
362 for (int32 i = 0; i < cue_entries_size_; ++i)
363 size += GetCueByIndex(i)->Size();
364 size += EbmlMasterElementSize(kMkvCues, size);
368 bool Cues::Write(IMkvWriter* writer) const {
373 for (int32 i = 0; i < cue_entries_size_; ++i) {
374 const CuePoint* const cue = GetCueByIndex(i);
382 if (!WriteEbmlMasterElement(writer, kMkvCues, size))
385 const int64 payload_position = writer->Position();
386 if (payload_position < 0)
389 for (int32 i = 0; i < cue_entries_size_; ++i) {
390 const CuePoint* const cue = GetCueByIndex(i);
392 if (!cue->Write(writer))
396 const int64 stop_position = writer->Position();
397 if (stop_position < 0)
400 if (stop_position - payload_position != static_cast<int64>(size))
406 ///////////////////////////////////////////////////////////////
408 // ContentEncAESSettings Class
410 ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
412 uint64 ContentEncAESSettings::Size() const {
413 const uint64 payload = PayloadSize();
415 EbmlMasterElementSize(kMkvContentEncAESSettings, payload) + payload;
419 bool ContentEncAESSettings::Write(IMkvWriter* writer) const {
420 const uint64 payload = PayloadSize();
422 if (!WriteEbmlMasterElement(writer, kMkvContentEncAESSettings, payload))
425 const int64 payload_position = writer->Position();
426 if (payload_position < 0)
429 if (!WriteEbmlElement(writer, kMkvAESSettingsCipherMode, cipher_mode_))
432 const int64 stop_position = writer->Position();
433 if (stop_position < 0 ||
434 stop_position - payload_position != static_cast<int64>(payload))
440 uint64 ContentEncAESSettings::PayloadSize() const {
441 uint64 size = EbmlElementSize(kMkvAESSettingsCipherMode, cipher_mode_);
445 ///////////////////////////////////////////////////////////////
447 // ContentEncoding Class
449 ContentEncoding::ContentEncoding()
455 enc_key_id_length_(0) {}
457 ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
459 bool ContentEncoding::SetEncryptionID(const uint8* id, uint64 length) {
460 if (!id || length < 1)
463 delete[] enc_key_id_;
466 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
470 memcpy(enc_key_id_, id, static_cast<size_t>(length));
471 enc_key_id_length_ = length;
476 uint64 ContentEncoding::Size() const {
477 const uint64 encryption_size = EncryptionSize();
478 const uint64 encoding_size = EncodingSize(0, encryption_size);
479 const uint64 encodings_size =
480 EbmlMasterElementSize(kMkvContentEncoding, encoding_size) + encoding_size;
482 return encodings_size;
485 bool ContentEncoding::Write(IMkvWriter* writer) const {
486 const uint64 encryption_size = EncryptionSize();
487 const uint64 encoding_size = EncodingSize(0, encryption_size);
489 EbmlMasterElementSize(kMkvContentEncoding, encoding_size) + encoding_size;
491 const int64 payload_position = writer->Position();
492 if (payload_position < 0)
495 if (!WriteEbmlMasterElement(writer, kMkvContentEncoding, encoding_size))
497 if (!WriteEbmlElement(writer, kMkvContentEncodingOrder, encoding_order_))
499 if (!WriteEbmlElement(writer, kMkvContentEncodingScope, encoding_scope_))
501 if (!WriteEbmlElement(writer, kMkvContentEncodingType, encoding_type_))
504 if (!WriteEbmlMasterElement(writer, kMkvContentEncryption, encryption_size))
506 if (!WriteEbmlElement(writer, kMkvContentEncAlgo, enc_algo_))
508 if (!WriteEbmlElement(writer, kMkvContentEncKeyID, enc_key_id_,
512 if (!enc_aes_settings_.Write(writer))
515 const int64 stop_position = writer->Position();
516 if (stop_position < 0 ||
517 stop_position - payload_position != static_cast<int64>(size))
523 uint64 ContentEncoding::EncodingSize(uint64 compresion_size,
524 uint64 encryption_size) const {
525 // TODO(fgalligan): Add support for compression settings.
526 if (compresion_size != 0)
529 uint64 encoding_size = 0;
531 if (encryption_size > 0) {
533 EbmlMasterElementSize(kMkvContentEncryption, encryption_size) +
536 encoding_size += EbmlElementSize(kMkvContentEncodingType, encoding_type_);
537 encoding_size += EbmlElementSize(kMkvContentEncodingScope, encoding_scope_);
538 encoding_size += EbmlElementSize(kMkvContentEncodingOrder, encoding_order_);
540 return encoding_size;
543 uint64 ContentEncoding::EncryptionSize() const {
544 const uint64 aes_size = enc_aes_settings_.Size();
546 uint64 encryption_size =
547 EbmlElementSize(kMkvContentEncKeyID, enc_key_id_, enc_key_id_length_);
548 encryption_size += EbmlElementSize(kMkvContentEncAlgo, enc_algo_);
550 return encryption_size + aes_size;
553 ///////////////////////////////////////////////////////////////
557 Track::Track(unsigned int* seed)
559 codec_private_(NULL),
561 max_block_additional_id_(0),
568 default_duration_(0),
569 codec_private_length_(0),
570 content_encoding_entries_(NULL),
571 content_encoding_entries_size_(0) {}
575 delete[] codec_private_;
579 if (content_encoding_entries_) {
580 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
581 ContentEncoding* const encoding = content_encoding_entries_[i];
584 delete[] content_encoding_entries_;
588 bool Track::AddContentEncoding() {
589 const uint32 count = content_encoding_entries_size_ + 1;
591 ContentEncoding** const content_encoding_entries =
592 new (std::nothrow) ContentEncoding*[count]; // NOLINT
593 if (!content_encoding_entries)
596 ContentEncoding* const content_encoding =
597 new (std::nothrow) ContentEncoding(); // NOLINT
598 if (!content_encoding) {
599 delete[] content_encoding_entries;
603 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
604 content_encoding_entries[i] = content_encoding_entries_[i];
607 delete[] content_encoding_entries_;
609 content_encoding_entries_ = content_encoding_entries;
610 content_encoding_entries_[content_encoding_entries_size_] = content_encoding;
611 content_encoding_entries_size_ = count;
615 ContentEncoding* Track::GetContentEncodingByIndex(uint32 index) const {
616 if (content_encoding_entries_ == NULL)
619 if (index >= content_encoding_entries_size_)
622 return content_encoding_entries_[index];
625 uint64 Track::PayloadSize() const {
626 uint64 size = EbmlElementSize(kMkvTrackNumber, number_);
627 size += EbmlElementSize(kMkvTrackUID, uid_);
628 size += EbmlElementSize(kMkvTrackType, type_);
630 size += EbmlElementSize(kMkvCodecID, codec_id_);
632 size += EbmlElementSize(kMkvCodecPrivate, codec_private_,
633 codec_private_length_);
635 size += EbmlElementSize(kMkvLanguage, language_);
637 size += EbmlElementSize(kMkvName, name_);
638 if (max_block_additional_id_)
639 size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_);
641 size += EbmlElementSize(kMkvCodecDelay, codec_delay_);
643 size += EbmlElementSize(kMkvSeekPreRoll, seek_pre_roll_);
644 if (default_duration_)
645 size += EbmlElementSize(kMkvDefaultDuration, default_duration_);
647 if (content_encoding_entries_size_ > 0) {
648 uint64 content_encodings_size = 0;
649 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
650 ContentEncoding* const encoding = content_encoding_entries_[i];
651 content_encodings_size += encoding->Size();
655 EbmlMasterElementSize(kMkvContentEncodings, content_encodings_size) +
656 content_encodings_size;
662 uint64 Track::Size() const {
663 uint64 size = PayloadSize();
664 size += EbmlMasterElementSize(kMkvTrackEntry, size);
668 bool Track::Write(IMkvWriter* writer) const {
672 // mandatory elements without a default value.
673 if (!type_ || !codec_id_)
676 // |size| may be bigger than what is written out in this function because
677 // derived classes may write out more data in the Track element.
678 const uint64 payload_size = PayloadSize();
680 if (!WriteEbmlMasterElement(writer, kMkvTrackEntry, payload_size))
683 uint64 size = EbmlElementSize(kMkvTrackNumber, number_);
684 size += EbmlElementSize(kMkvTrackUID, uid_);
685 size += EbmlElementSize(kMkvTrackType, type_);
687 size += EbmlElementSize(kMkvCodecID, codec_id_);
689 size += EbmlElementSize(kMkvCodecPrivate, codec_private_,
690 codec_private_length_);
692 size += EbmlElementSize(kMkvLanguage, language_);
694 size += EbmlElementSize(kMkvName, name_);
695 if (max_block_additional_id_)
696 size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_);
698 size += EbmlElementSize(kMkvCodecDelay, codec_delay_);
700 size += EbmlElementSize(kMkvSeekPreRoll, seek_pre_roll_);
701 if (default_duration_)
702 size += EbmlElementSize(kMkvDefaultDuration, default_duration_);
704 const int64 payload_position = writer->Position();
705 if (payload_position < 0)
708 if (!WriteEbmlElement(writer, kMkvTrackNumber, number_))
710 if (!WriteEbmlElement(writer, kMkvTrackUID, uid_))
712 if (!WriteEbmlElement(writer, kMkvTrackType, type_))
714 if (max_block_additional_id_) {
715 if (!WriteEbmlElement(writer, kMkvMaxBlockAdditionID,
716 max_block_additional_id_)) {
721 if (!WriteEbmlElement(writer, kMkvCodecDelay, codec_delay_))
724 if (seek_pre_roll_) {
725 if (!WriteEbmlElement(writer, kMkvSeekPreRoll, seek_pre_roll_))
728 if (default_duration_) {
729 if (!WriteEbmlElement(writer, kMkvDefaultDuration, default_duration_))
733 if (!WriteEbmlElement(writer, kMkvCodecID, codec_id_))
736 if (codec_private_) {
737 if (!WriteEbmlElement(writer, kMkvCodecPrivate, codec_private_,
738 codec_private_length_))
742 if (!WriteEbmlElement(writer, kMkvLanguage, language_))
746 if (!WriteEbmlElement(writer, kMkvName, name_))
750 int64 stop_position = writer->Position();
751 if (stop_position < 0 ||
752 stop_position - payload_position != static_cast<int64>(size))
755 if (content_encoding_entries_size_ > 0) {
756 uint64 content_encodings_size = 0;
757 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
758 ContentEncoding* const encoding = content_encoding_entries_[i];
759 content_encodings_size += encoding->Size();
762 if (!WriteEbmlMasterElement(writer, kMkvContentEncodings,
763 content_encodings_size))
766 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
767 ContentEncoding* const encoding = content_encoding_entries_[i];
768 if (!encoding->Write(writer))
773 stop_position = writer->Position();
774 if (stop_position < 0)
779 bool Track::SetCodecPrivate(const uint8* codec_private, uint64 length) {
780 if (!codec_private || length < 1)
783 delete[] codec_private_;
786 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
790 memcpy(codec_private_, codec_private, static_cast<size_t>(length));
791 codec_private_length_ = length;
796 void Track::set_codec_id(const char* codec_id) {
800 const size_t length = strlen(codec_id) + 1;
801 codec_id_ = new (std::nothrow) char[length]; // NOLINT
804 strcpy_s(codec_id_, length, codec_id);
806 strcpy(codec_id_, codec_id);
812 // TODO(fgalligan): Vet the language parameter.
813 void Track::set_language(const char* language) {
817 const size_t length = strlen(language) + 1;
818 language_ = new (std::nothrow) char[length]; // NOLINT
821 strcpy_s(language_, length, language);
823 strcpy(language_, language);
829 void Track::set_name(const char* name) {
833 const size_t length = strlen(name) + 1;
834 name_ = new (std::nothrow) char[length]; // NOLINT
837 strcpy_s(name_, length, name);
845 ///////////////////////////////////////////////////////////////
849 VideoTrack::VideoTrack(unsigned int* seed)
863 VideoTrack::~VideoTrack() {}
865 bool VideoTrack::SetStereoMode(uint64 stereo_mode) {
866 if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst &&
867 stereo_mode != kTopBottomRightIsFirst &&
868 stereo_mode != kTopBottomLeftIsFirst &&
869 stereo_mode != kSideBySideRightIsFirst)
872 stereo_mode_ = stereo_mode;
876 bool VideoTrack::SetAlphaMode(uint64 alpha_mode) {
877 if (alpha_mode != kNoAlpha && alpha_mode != kAlpha)
880 alpha_mode_ = alpha_mode;
884 uint64 VideoTrack::PayloadSize() const {
885 const uint64 parent_size = Track::PayloadSize();
887 uint64 size = VideoPayloadSize();
888 size += EbmlMasterElementSize(kMkvVideo, size);
890 return parent_size + size;
893 bool VideoTrack::Write(IMkvWriter* writer) const {
894 if (!Track::Write(writer))
897 const uint64 size = VideoPayloadSize();
899 if (!WriteEbmlMasterElement(writer, kMkvVideo, size))
902 const int64 payload_position = writer->Position();
903 if (payload_position < 0)
906 if (!WriteEbmlElement(writer, kMkvPixelWidth, width_))
908 if (!WriteEbmlElement(writer, kMkvPixelHeight, height_))
910 if (display_width_ > 0) {
911 if (!WriteEbmlElement(writer, kMkvDisplayWidth, display_width_))
914 if (display_height_ > 0) {
915 if (!WriteEbmlElement(writer, kMkvDisplayHeight, display_height_))
918 if (crop_left_ > 0) {
919 if (!WriteEbmlElement(writer, kMkvPixelCropLeft, crop_left_))
922 if (crop_right_ > 0) {
923 if (!WriteEbmlElement(writer, kMkvPixelCropRight, crop_right_))
927 if (!WriteEbmlElement(writer, kMkvPixelCropTop, crop_top_))
930 if (crop_bottom_ > 0) {
931 if (!WriteEbmlElement(writer, kMkvPixelCropBottom, crop_bottom_))
934 if (stereo_mode_ > kMono) {
935 if (!WriteEbmlElement(writer, kMkvStereoMode, stereo_mode_))
938 if (alpha_mode_ > kNoAlpha) {
939 if (!WriteEbmlElement(writer, kMkvAlphaMode, alpha_mode_))
942 if (frame_rate_ > 0.0) {
943 if (!WriteEbmlElement(writer, kMkvFrameRate,
944 static_cast<float>(frame_rate_))) {
949 const int64 stop_position = writer->Position();
950 if (stop_position < 0 ||
951 stop_position - payload_position != static_cast<int64>(size)) {
958 uint64 VideoTrack::VideoPayloadSize() const {
959 uint64 size = EbmlElementSize(kMkvPixelWidth, width_);
960 size += EbmlElementSize(kMkvPixelHeight, height_);
961 if (display_width_ > 0)
962 size += EbmlElementSize(kMkvDisplayWidth, display_width_);
963 if (display_height_ > 0)
964 size += EbmlElementSize(kMkvDisplayHeight, display_height_);
966 size += EbmlElementSize(kMkvPixelCropLeft, crop_left_);
968 size += EbmlElementSize(kMkvPixelCropRight, crop_right_);
970 size += EbmlElementSize(kMkvPixelCropTop, crop_top_);
971 if (crop_bottom_ > 0)
972 size += EbmlElementSize(kMkvPixelCropBottom, crop_bottom_);
973 if (stereo_mode_ > kMono)
974 size += EbmlElementSize(kMkvStereoMode, stereo_mode_);
975 if (alpha_mode_ > kNoAlpha)
976 size += EbmlElementSize(kMkvAlphaMode, alpha_mode_);
977 if (frame_rate_ > 0.0)
978 size += EbmlElementSize(kMkvFrameRate, static_cast<float>(frame_rate_));
983 ///////////////////////////////////////////////////////////////
987 AudioTrack::AudioTrack(unsigned int* seed)
988 : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
990 AudioTrack::~AudioTrack() {}
992 uint64 AudioTrack::PayloadSize() const {
993 const uint64 parent_size = Track::PayloadSize();
996 EbmlElementSize(kMkvSamplingFrequency, static_cast<float>(sample_rate_));
997 size += EbmlElementSize(kMkvChannels, channels_);
999 size += EbmlElementSize(kMkvBitDepth, bit_depth_);
1000 size += EbmlMasterElementSize(kMkvAudio, size);
1002 return parent_size + size;
1005 bool AudioTrack::Write(IMkvWriter* writer) const {
1006 if (!Track::Write(writer))
1009 // Calculate AudioSettings size.
1011 EbmlElementSize(kMkvSamplingFrequency, static_cast<float>(sample_rate_));
1012 size += EbmlElementSize(kMkvChannels, channels_);
1014 size += EbmlElementSize(kMkvBitDepth, bit_depth_);
1016 if (!WriteEbmlMasterElement(writer, kMkvAudio, size))
1019 const int64 payload_position = writer->Position();
1020 if (payload_position < 0)
1023 if (!WriteEbmlElement(writer, kMkvSamplingFrequency,
1024 static_cast<float>(sample_rate_)))
1026 if (!WriteEbmlElement(writer, kMkvChannels, channels_))
1029 if (!WriteEbmlElement(writer, kMkvBitDepth, bit_depth_))
1032 const int64 stop_position = writer->Position();
1033 if (stop_position < 0 ||
1034 stop_position - payload_position != static_cast<int64>(size))
1040 ///////////////////////////////////////////////////////////////
1044 const char Tracks::kOpusCodecId[] = "A_OPUS";
1045 const char Tracks::kVorbisCodecId[] = "A_VORBIS";
1046 const char Tracks::kVp8CodecId[] = "V_VP8";
1047 const char Tracks::kVp9CodecId[] = "V_VP9";
1048 const char Tracks::kVp10CodecId[] = "V_VP10";
1050 Tracks::Tracks() : track_entries_(NULL), track_entries_size_(0) {}
1053 if (track_entries_) {
1054 for (uint32 i = 0; i < track_entries_size_; ++i) {
1055 Track* const track = track_entries_[i];
1058 delete[] track_entries_;
1062 bool Tracks::AddTrack(Track* track, int32 number) {
1066 // This muxer only supports track numbers in the range [1, 126], in
1067 // order to be able (to use Matroska integer representation) to
1068 // serialize the block header (of which the track number is a part)
1069 // for a frame using exactly 4 bytes.
1074 uint32 track_num = number;
1076 if (track_num > 0) {
1077 // Check to make sure a track does not already have |track_num|.
1078 for (uint32 i = 0; i < track_entries_size_; ++i) {
1079 if (track_entries_[i]->number() == track_num)
1084 const uint32 count = track_entries_size_ + 1;
1086 Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
1090 for (uint32 i = 0; i < track_entries_size_; ++i) {
1091 track_entries[i] = track_entries_[i];
1094 delete[] track_entries_;
1096 // Find the lowest availible track number > 0.
1097 if (track_num == 0) {
1100 // Check to make sure a track does not already have |track_num|.
1104 for (uint32 i = 0; i < track_entries_size_; ++i) {
1105 if (track_entries[i]->number() == track_num) {
1113 track->set_number(track_num);
1115 track_entries_ = track_entries;
1116 track_entries_[track_entries_size_] = track;
1117 track_entries_size_ = count;
1121 const Track* Tracks::GetTrackByIndex(uint32 index) const {
1122 if (track_entries_ == NULL)
1125 if (index >= track_entries_size_)
1128 return track_entries_[index];
1131 Track* Tracks::GetTrackByNumber(uint64 track_number) const {
1132 const int32 count = track_entries_size();
1133 for (int32 i = 0; i < count; ++i) {
1134 if (track_entries_[i]->number() == track_number)
1135 return track_entries_[i];
1141 bool Tracks::TrackIsAudio(uint64 track_number) const {
1142 const Track* const track = GetTrackByNumber(track_number);
1144 if (track->type() == kAudio)
1150 bool Tracks::TrackIsVideo(uint64 track_number) const {
1151 const Track* const track = GetTrackByNumber(track_number);
1153 if (track->type() == kVideo)
1159 bool Tracks::Write(IMkvWriter* writer) const {
1161 const int32 count = track_entries_size();
1162 for (int32 i = 0; i < count; ++i) {
1163 const Track* const track = GetTrackByIndex(i);
1168 size += track->Size();
1171 if (!WriteEbmlMasterElement(writer, kMkvTracks, size))
1174 const int64 payload_position = writer->Position();
1175 if (payload_position < 0)
1178 for (int32 i = 0; i < count; ++i) {
1179 const Track* const track = GetTrackByIndex(i);
1180 if (!track->Write(writer))
1184 const int64 stop_position = writer->Position();
1185 if (stop_position < 0 ||
1186 stop_position - payload_position != static_cast<int64>(size))
1192 ///////////////////////////////////////////////////////////////
1196 bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
1198 void Chapter::set_time(const Segment& segment, uint64 start_ns, uint64 end_ns) {
1199 const SegmentInfo* const info = segment.GetSegmentInfo();
1200 const uint64 timecode_scale = info->timecode_scale();
1201 start_timecode_ = start_ns / timecode_scale;
1202 end_timecode_ = end_ns / timecode_scale;
1205 bool Chapter::add_string(const char* title, const char* language,
1206 const char* country) {
1207 if (!ExpandDisplaysArray())
1210 Display& d = displays_[displays_count_++];
1213 if (!d.set_title(title))
1216 if (!d.set_language(language))
1219 if (!d.set_country(country))
1225 Chapter::Chapter() {
1226 // This ctor only constructs the object. Proper initialization is
1227 // done in Init() (called in Chapters::AddChapter()). The only
1228 // reason we bother implementing this ctor is because we had to
1229 // declare it as private (along with the dtor), in order to prevent
1230 // clients from creating Chapter instances (a privelege we grant
1231 // only to the Chapters class). Doing no initialization here also
1232 // means that creating arrays of chapter objects is more efficient,
1233 // because we only initialize each new chapter object as it becomes
1234 // active on the array.
1237 Chapter::~Chapter() {}
1239 void Chapter::Init(unsigned int* seed) {
1241 start_timecode_ = 0;
1245 displays_count_ = 0;
1246 uid_ = MakeUID(seed);
1249 void Chapter::ShallowCopy(Chapter* dst) const {
1251 dst->start_timecode_ = start_timecode_;
1252 dst->end_timecode_ = end_timecode_;
1254 dst->displays_ = displays_;
1255 dst->displays_size_ = displays_size_;
1256 dst->displays_count_ = displays_count_;
1259 void Chapter::Clear() {
1262 while (displays_count_ > 0) {
1263 Display& d = displays_[--displays_count_];
1273 bool Chapter::ExpandDisplaysArray() {
1274 if (displays_size_ > displays_count_)
1275 return true; // nothing to do yet
1277 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
1279 Display* const displays = new (std::nothrow) Display[size]; // NOLINT
1280 if (displays == NULL)
1283 for (int idx = 0; idx < displays_count_; ++idx) {
1284 displays[idx] = displays_[idx]; // shallow copy
1289 displays_ = displays;
1290 displays_size_ = size;
1295 uint64 Chapter::WriteAtom(IMkvWriter* writer) const {
1296 uint64 payload_size = EbmlElementSize(kMkvChapterStringUID, id_) +
1297 EbmlElementSize(kMkvChapterUID, uid_) +
1298 EbmlElementSize(kMkvChapterTimeStart, start_timecode_) +
1299 EbmlElementSize(kMkvChapterTimeEnd, end_timecode_);
1301 for (int idx = 0; idx < displays_count_; ++idx) {
1302 const Display& d = displays_[idx];
1303 payload_size += d.WriteDisplay(NULL);
1306 const uint64 atom_size =
1307 EbmlMasterElementSize(kMkvChapterAtom, payload_size) + payload_size;
1312 const int64 start = writer->Position();
1314 if (!WriteEbmlMasterElement(writer, kMkvChapterAtom, payload_size))
1317 if (!WriteEbmlElement(writer, kMkvChapterStringUID, id_))
1320 if (!WriteEbmlElement(writer, kMkvChapterUID, uid_))
1323 if (!WriteEbmlElement(writer, kMkvChapterTimeStart, start_timecode_))
1326 if (!WriteEbmlElement(writer, kMkvChapterTimeEnd, end_timecode_))
1329 for (int idx = 0; idx < displays_count_; ++idx) {
1330 const Display& d = displays_[idx];
1332 if (!d.WriteDisplay(writer))
1336 const int64 stop = writer->Position();
1338 if (stop >= start && uint64(stop - start) != atom_size)
1344 void Chapter::Display::Init() {
1350 void Chapter::Display::Clear() {
1351 StrCpy(NULL, &title_);
1352 StrCpy(NULL, &language_);
1353 StrCpy(NULL, &country_);
1356 bool Chapter::Display::set_title(const char* title) {
1357 return StrCpy(title, &title_);
1360 bool Chapter::Display::set_language(const char* language) {
1361 return StrCpy(language, &language_);
1364 bool Chapter::Display::set_country(const char* country) {
1365 return StrCpy(country, &country_);
1368 uint64 Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
1369 uint64 payload_size = EbmlElementSize(kMkvChapString, title_);
1372 payload_size += EbmlElementSize(kMkvChapLanguage, language_);
1375 payload_size += EbmlElementSize(kMkvChapCountry, country_);
1377 const uint64 display_size =
1378 EbmlMasterElementSize(kMkvChapterDisplay, payload_size) + payload_size;
1381 return display_size;
1383 const int64 start = writer->Position();
1385 if (!WriteEbmlMasterElement(writer, kMkvChapterDisplay, payload_size))
1388 if (!WriteEbmlElement(writer, kMkvChapString, title_))
1392 if (!WriteEbmlElement(writer, kMkvChapLanguage, language_))
1397 if (!WriteEbmlElement(writer, kMkvChapCountry, country_))
1401 const int64 stop = writer->Position();
1403 if (stop >= start && uint64(stop - start) != display_size)
1406 return display_size;
1409 ///////////////////////////////////////////////////////////////
1413 Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
1415 Chapters::~Chapters() {
1416 while (chapters_count_ > 0) {
1417 Chapter& chapter = chapters_[--chapters_count_];
1425 int Chapters::Count() const { return chapters_count_; }
1427 Chapter* Chapters::AddChapter(unsigned int* seed) {
1428 if (!ExpandChaptersArray())
1431 Chapter& chapter = chapters_[chapters_count_++];
1437 bool Chapters::Write(IMkvWriter* writer) const {
1441 const uint64 payload_size = WriteEdition(NULL); // return size only
1443 if (!WriteEbmlMasterElement(writer, kMkvChapters, payload_size))
1446 const int64 start = writer->Position();
1448 if (WriteEdition(writer) == 0) // error
1451 const int64 stop = writer->Position();
1453 if (stop >= start && uint64(stop - start) != payload_size)
1459 bool Chapters::ExpandChaptersArray() {
1460 if (chapters_size_ > chapters_count_)
1461 return true; // nothing to do yet
1463 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
1465 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
1466 if (chapters == NULL)
1469 for (int idx = 0; idx < chapters_count_; ++idx) {
1470 const Chapter& src = chapters_[idx];
1471 Chapter* const dst = chapters + idx;
1472 src.ShallowCopy(dst);
1477 chapters_ = chapters;
1478 chapters_size_ = size;
1483 uint64 Chapters::WriteEdition(IMkvWriter* writer) const {
1484 uint64 payload_size = 0;
1486 for (int idx = 0; idx < chapters_count_; ++idx) {
1487 const Chapter& chapter = chapters_[idx];
1488 payload_size += chapter.WriteAtom(NULL);
1491 const uint64 edition_size =
1492 EbmlMasterElementSize(kMkvEditionEntry, payload_size) + payload_size;
1494 if (writer == NULL) // return size only
1495 return edition_size;
1497 const int64 start = writer->Position();
1499 if (!WriteEbmlMasterElement(writer, kMkvEditionEntry, payload_size))
1502 for (int idx = 0; idx < chapters_count_; ++idx) {
1503 const Chapter& chapter = chapters_[idx];
1505 const uint64 chapter_size = chapter.WriteAtom(writer);
1506 if (chapter_size == 0) // error
1510 const int64 stop = writer->Position();
1512 if (stop >= start && uint64(stop - start) != edition_size)
1515 return edition_size;
1520 bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) {
1521 if (!ExpandSimpleTagsArray())
1524 SimpleTag& st = simple_tags_[simple_tags_count_++];
1527 if (!st.set_tag_name(tag_name))
1530 if (!st.set_tag_string(tag_string))
1537 simple_tags_ = NULL;
1538 simple_tags_size_ = 0;
1539 simple_tags_count_ = 0;
1544 void Tag::ShallowCopy(Tag* dst) const {
1545 dst->simple_tags_ = simple_tags_;
1546 dst->simple_tags_size_ = simple_tags_size_;
1547 dst->simple_tags_count_ = simple_tags_count_;
1551 while (simple_tags_count_ > 0) {
1552 SimpleTag& st = simple_tags_[--simple_tags_count_];
1556 delete[] simple_tags_;
1557 simple_tags_ = NULL;
1559 simple_tags_size_ = 0;
1562 bool Tag::ExpandSimpleTagsArray() {
1563 if (simple_tags_size_ > simple_tags_count_)
1564 return true; // nothing to do yet
1566 const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_;
1568 SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT
1569 if (simple_tags == NULL)
1572 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1573 simple_tags[idx] = simple_tags_[idx]; // shallow copy
1576 delete[] simple_tags_;
1578 simple_tags_ = simple_tags;
1579 simple_tags_size_ = size;
1584 uint64 Tag::Write(IMkvWriter* writer) const {
1585 uint64 payload_size = 0;
1587 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1588 const SimpleTag& st = simple_tags_[idx];
1589 payload_size += st.Write(NULL);
1592 const uint64 tag_size =
1593 EbmlMasterElementSize(kMkvTag, payload_size) + payload_size;
1598 const int64 start = writer->Position();
1600 if (!WriteEbmlMasterElement(writer, kMkvTag, payload_size))
1603 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1604 const SimpleTag& st = simple_tags_[idx];
1606 if (!st.Write(writer))
1610 const int64 stop = writer->Position();
1612 if (stop >= start && uint64(stop - start) != tag_size)
1620 void Tag::SimpleTag::Init() {
1625 void Tag::SimpleTag::Clear() {
1626 StrCpy(NULL, &tag_name_);
1627 StrCpy(NULL, &tag_string_);
1630 bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
1631 return StrCpy(tag_name, &tag_name_);
1634 bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
1635 return StrCpy(tag_string, &tag_string_);
1638 uint64 Tag::SimpleTag::Write(IMkvWriter* writer) const {
1639 uint64 payload_size = EbmlElementSize(kMkvTagName, tag_name_);
1641 payload_size += EbmlElementSize(kMkvTagString, tag_string_);
1643 const uint64 simple_tag_size =
1644 EbmlMasterElementSize(kMkvSimpleTag, payload_size) + payload_size;
1647 return simple_tag_size;
1649 const int64 start = writer->Position();
1651 if (!WriteEbmlMasterElement(writer, kMkvSimpleTag, payload_size))
1654 if (!WriteEbmlElement(writer, kMkvTagName, tag_name_))
1657 if (!WriteEbmlElement(writer, kMkvTagString, tag_string_))
1660 const int64 stop = writer->Position();
1662 if (stop >= start && uint64(stop - start) != simple_tag_size)
1665 return simple_tag_size;
1670 Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
1673 while (tags_count_ > 0) {
1674 Tag& tag = tags_[--tags_count_];
1682 int Tags::Count() const { return tags_count_; }
1684 Tag* Tags::AddTag() {
1685 if (!ExpandTagsArray())
1688 Tag& tag = tags_[tags_count_++];
1693 bool Tags::Write(IMkvWriter* writer) const {
1697 uint64 payload_size = 0;
1699 for (int idx = 0; idx < tags_count_; ++idx) {
1700 const Tag& tag = tags_[idx];
1701 payload_size += tag.Write(NULL);
1704 if (!WriteEbmlMasterElement(writer, kMkvTags, payload_size))
1707 const int64 start = writer->Position();
1709 for (int idx = 0; idx < tags_count_; ++idx) {
1710 const Tag& tag = tags_[idx];
1712 const uint64 tag_size = tag.Write(writer);
1713 if (tag_size == 0) // error
1717 const int64 stop = writer->Position();
1719 if (stop >= start && uint64(stop - start) != payload_size)
1725 bool Tags::ExpandTagsArray() {
1726 if (tags_size_ > tags_count_)
1727 return true; // nothing to do yet
1729 const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_;
1731 Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT
1735 for (int idx = 0; idx < tags_count_; ++idx) {
1736 const Tag& src = tags_[idx];
1737 Tag* const dst = tags + idx;
1738 src.ShallowCopy(dst);
1749 ///////////////////////////////////////////////////////////////
1753 Cluster::Cluster(uint64 timecode, int64 cues_pos, uint64 timecode_scale)
1756 header_written_(false),
1758 position_for_cues_(cues_pos),
1760 timecode_(timecode),
1761 timecode_scale_(timecode_scale),
1764 Cluster::~Cluster() {}
1766 bool Cluster::Init(IMkvWriter* ptr_writer) {
1770 writer_ = ptr_writer;
1774 bool Cluster::AddFrame(const Frame* const frame) { return DoWriteFrame(frame); }
1776 bool Cluster::AddFrame(const uint8* data, uint64 length, uint64 track_number,
1777 uint64 abs_timecode, bool is_key) {
1779 if (!frame.Init(data, length))
1781 frame.set_track_number(track_number);
1782 frame.set_timestamp(abs_timecode);
1783 frame.set_is_key(is_key);
1784 return DoWriteFrame(&frame);
1787 bool Cluster::AddFrameWithAdditional(const uint8* data, uint64 length,
1788 const uint8* additional,
1789 uint64 additional_length, uint64 add_id,
1790 uint64 track_number, uint64 abs_timecode,
1792 if (!additional || additional_length == 0) {
1796 if (!frame.Init(data, length) ||
1797 !frame.AddAdditionalData(additional, additional_length, add_id)) {
1800 frame.set_track_number(track_number);
1801 frame.set_timestamp(abs_timecode);
1802 frame.set_is_key(is_key);
1803 return DoWriteFrame(&frame);
1806 bool Cluster::AddFrameWithDiscardPadding(const uint8* data, uint64 length,
1807 int64 discard_padding,
1808 uint64 track_number,
1809 uint64 abs_timecode, bool is_key) {
1811 if (!frame.Init(data, length))
1813 frame.set_discard_padding(discard_padding);
1814 frame.set_track_number(track_number);
1815 frame.set_timestamp(abs_timecode);
1816 frame.set_is_key(is_key);
1817 return DoWriteFrame(&frame);
1820 bool Cluster::AddMetadata(const uint8* data, uint64 length, uint64 track_number,
1821 uint64 abs_timecode, uint64 duration_timecode) {
1823 if (!frame.Init(data, length))
1825 frame.set_track_number(track_number);
1826 frame.set_timestamp(abs_timecode);
1827 frame.set_duration(duration_timecode);
1828 frame.set_is_key(true); // All metadata blocks are keyframes.
1829 return DoWriteFrame(&frame);
1832 void Cluster::AddPayloadSize(uint64 size) { payload_size_ += size; }
1834 bool Cluster::Finalize() {
1835 if (!writer_ || finalized_ || size_position_ == -1)
1838 if (writer_->Seekable()) {
1839 const int64 pos = writer_->Position();
1841 if (writer_->Position(size_position_))
1844 if (WriteUIntSize(writer_, payload_size(), 8))
1847 if (writer_->Position(pos))
1856 uint64 Cluster::Size() const {
1857 const uint64 element_size =
1858 EbmlMasterElementSize(kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) + payload_size_;
1859 return element_size;
1862 bool Cluster::PreWriteBlock() {
1866 if (!header_written_) {
1867 if (!WriteClusterHeader())
1874 void Cluster::PostWriteBlock(uint64 element_size) {
1875 AddPayloadSize(element_size);
1879 int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const {
1880 const int64 cluster_timecode = this->Cluster::timecode();
1881 const int64 rel_timecode =
1882 static_cast<int64>(abs_timecode) - cluster_timecode;
1884 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
1887 return rel_timecode;
1890 bool Cluster::DoWriteFrame(const Frame* const frame) {
1891 if (!frame || !frame->IsValid())
1894 if (!PreWriteBlock())
1897 const uint64 element_size = WriteFrame(writer_, frame, this);
1898 if (element_size == 0)
1901 PostWriteBlock(element_size);
1905 bool Cluster::WriteClusterHeader() {
1909 if (WriteID(writer_, kMkvCluster))
1913 size_position_ = writer_->Position();
1915 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
1916 // bytes because we do not know how big our cluster will be.
1917 if (SerializeInt(writer_, kEbmlUnknownValue, 8))
1920 if (!WriteEbmlElement(writer_, kMkvTimecode, timecode()))
1922 AddPayloadSize(EbmlElementSize(kMkvTimecode, timecode()));
1923 header_written_ = true;
1928 ///////////////////////////////////////////////////////////////
1932 SeekHead::SeekHead() : start_pos_(0ULL) {
1933 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1934 seek_entry_id_[i] = 0;
1935 seek_entry_pos_[i] = 0;
1939 SeekHead::~SeekHead() {}
1941 bool SeekHead::Finalize(IMkvWriter* writer) const {
1942 if (writer->Seekable()) {
1943 if (start_pos_ == -1)
1946 uint64 payload_size = 0;
1947 uint64 entry_size[kSeekEntryCount];
1949 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1950 if (seek_entry_id_[i] != 0) {
1952 EbmlElementSize(kMkvSeekID, static_cast<uint64>(seek_entry_id_[i]));
1953 entry_size[i] += EbmlElementSize(kMkvSeekPosition, seek_entry_pos_[i]);
1956 EbmlMasterElementSize(kMkvSeek, entry_size[i]) + entry_size[i];
1960 // No SeekHead elements
1961 if (payload_size == 0)
1964 const int64 pos = writer->Position();
1965 if (writer->Position(start_pos_))
1968 if (!WriteEbmlMasterElement(writer, kMkvSeekHead, payload_size))
1971 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1972 if (seek_entry_id_[i] != 0) {
1973 if (!WriteEbmlMasterElement(writer, kMkvSeek, entry_size[i]))
1976 if (!WriteEbmlElement(writer, kMkvSeekID,
1977 static_cast<uint64>(seek_entry_id_[i])))
1980 if (!WriteEbmlElement(writer, kMkvSeekPosition, seek_entry_pos_[i]))
1985 const uint64 total_entry_size = kSeekEntryCount * MaxEntrySize();
1986 const uint64 total_size =
1987 EbmlMasterElementSize(kMkvSeekHead, total_entry_size) +
1989 const int64 size_left = total_size - (writer->Position() - start_pos_);
1991 const uint64 bytes_written = WriteVoidElement(writer, size_left);
1995 if (writer->Position(pos))
2002 bool SeekHead::Write(IMkvWriter* writer) {
2003 const uint64 entry_size = kSeekEntryCount * MaxEntrySize();
2004 const uint64 size = EbmlMasterElementSize(kMkvSeekHead, entry_size);
2006 start_pos_ = writer->Position();
2008 const uint64 bytes_written = WriteVoidElement(writer, size + entry_size);
2015 bool SeekHead::AddSeekEntry(uint32 id, uint64 pos) {
2016 for (int32 i = 0; i < kSeekEntryCount; ++i) {
2017 if (seek_entry_id_[i] == 0) {
2018 seek_entry_id_[i] = id;
2019 seek_entry_pos_[i] = pos;
2026 uint32 SeekHead::GetId(int index) const {
2027 if (index < 0 || index >= kSeekEntryCount)
2029 return seek_entry_id_[index];
2032 uint64 SeekHead::GetPosition(int index) const {
2033 if (index < 0 || index >= kSeekEntryCount)
2035 return seek_entry_pos_[index];
2038 bool SeekHead::SetSeekEntry(int index, uint32 id, uint64 position) {
2039 if (index < 0 || index >= kSeekEntryCount)
2041 seek_entry_id_[index] = id;
2042 seek_entry_pos_[index] = position;
2046 uint64 SeekHead::MaxEntrySize() const {
2047 const uint64 max_entry_payload_size =
2048 EbmlElementSize(kMkvSeekID, 0xffffffffULL) +
2049 EbmlElementSize(kMkvSeekPosition, 0xffffffffffffffffULL);
2050 const uint64 max_entry_size =
2051 EbmlMasterElementSize(kMkvSeek, max_entry_payload_size) +
2052 max_entry_payload_size;
2054 return max_entry_size;
2057 ///////////////////////////////////////////////////////////////
2059 // SegmentInfo Class
2061 SegmentInfo::SegmentInfo()
2064 timecode_scale_(1000000ULL),
2066 date_utc_(LLONG_MIN),
2067 duration_pos_(-1) {}
2069 SegmentInfo::~SegmentInfo() {
2070 delete[] muxing_app_;
2071 delete[] writing_app_;
2074 bool SegmentInfo::Init() {
2079 GetVersion(&major, &minor, &build, &revision);
2082 sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2083 minor, build, revision);
2085 snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2086 minor, build, revision);
2089 const size_t app_len = strlen(temp) + 1;
2091 delete[] muxing_app_;
2093 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
2098 strcpy_s(muxing_app_, app_len, temp);
2100 strcpy(muxing_app_, temp);
2103 set_writing_app(temp);
2109 bool SegmentInfo::Finalize(IMkvWriter* writer) const {
2113 if (duration_ > 0.0) {
2114 if (writer->Seekable()) {
2115 if (duration_pos_ == -1)
2118 const int64 pos = writer->Position();
2120 if (writer->Position(duration_pos_))
2123 if (!WriteEbmlElement(writer, kMkvDuration,
2124 static_cast<float>(duration_)))
2127 if (writer->Position(pos))
2135 bool SegmentInfo::Write(IMkvWriter* writer) {
2136 if (!writer || !muxing_app_ || !writing_app_)
2139 uint64 size = EbmlElementSize(kMkvTimecodeScale, timecode_scale_);
2140 if (duration_ > 0.0)
2141 size += EbmlElementSize(kMkvDuration, static_cast<float>(duration_));
2142 if (date_utc_ != LLONG_MIN)
2143 size += EbmlDateElementSize(kMkvDateUTC);
2144 size += EbmlElementSize(kMkvMuxingApp, muxing_app_);
2145 size += EbmlElementSize(kMkvWritingApp, writing_app_);
2147 if (!WriteEbmlMasterElement(writer, kMkvInfo, size))
2150 const int64 payload_position = writer->Position();
2151 if (payload_position < 0)
2154 if (!WriteEbmlElement(writer, kMkvTimecodeScale, timecode_scale_))
2157 if (duration_ > 0.0) {
2159 duration_pos_ = writer->Position();
2161 if (!WriteEbmlElement(writer, kMkvDuration, static_cast<float>(duration_)))
2165 if (date_utc_ != LLONG_MIN)
2166 WriteEbmlDateElement(writer, kMkvDateUTC, date_utc_);
2168 if (!WriteEbmlElement(writer, kMkvMuxingApp, muxing_app_))
2170 if (!WriteEbmlElement(writer, kMkvWritingApp, writing_app_))
2173 const int64 stop_position = writer->Position();
2174 if (stop_position < 0 ||
2175 stop_position - payload_position != static_cast<int64>(size))
2181 void SegmentInfo::set_muxing_app(const char* app) {
2183 const size_t length = strlen(app) + 1;
2184 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2189 strcpy_s(temp_str, length, app);
2191 strcpy(temp_str, app);
2194 delete[] muxing_app_;
2195 muxing_app_ = temp_str;
2199 void SegmentInfo::set_writing_app(const char* app) {
2201 const size_t length = strlen(app) + 1;
2202 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2207 strcpy_s(temp_str, length, app);
2209 strcpy(temp_str, app);
2212 delete[] writing_app_;
2213 writing_app_ = temp_str;
2217 ///////////////////////////////////////////////////////////////
2224 chunk_writer_cluster_(NULL),
2225 chunk_writer_cues_(NULL),
2226 chunk_writer_header_(NULL),
2228 chunking_base_name_(NULL),
2229 cluster_list_(NULL),
2230 cluster_list_capacity_(0),
2231 cluster_list_size_(0),
2232 cues_position_(kAfterClusters),
2234 force_new_cluster_(false),
2236 frames_capacity_(0),
2239 header_written_(false),
2240 last_block_duration_(0),
2242 max_cluster_duration_(kDefaultMaxClusterDuration),
2243 max_cluster_size_(0),
2245 new_cuepoint_(false),
2249 doc_type_version_(kDefaultDocTypeVersion),
2250 doc_type_version_written_(0),
2251 writer_cluster_(NULL),
2253 writer_header_(NULL) {
2254 const time_t curr_time = time(NULL);
2255 seed_ = static_cast<unsigned int>(curr_time);
2261 Segment::~Segment() {
2262 if (cluster_list_) {
2263 for (int32 i = 0; i < cluster_list_size_; ++i) {
2264 Cluster* const cluster = cluster_list_[i];
2267 delete[] cluster_list_;
2271 for (int32 i = 0; i < frames_size_; ++i) {
2272 Frame* const frame = frames_[i];
2278 delete[] chunk_name_;
2279 delete[] chunking_base_name_;
2281 if (chunk_writer_cluster_) {
2282 chunk_writer_cluster_->Close();
2283 delete chunk_writer_cluster_;
2285 if (chunk_writer_cues_) {
2286 chunk_writer_cues_->Close();
2287 delete chunk_writer_cues_;
2289 if (chunk_writer_header_) {
2290 chunk_writer_header_->Close();
2291 delete chunk_writer_header_;
2295 void Segment::MoveCuesBeforeClustersHelper(uint64 diff, int32 index,
2296 uint64* cues_size) {
2297 CuePoint* const cue_point = cues_.GetCueByIndex(index);
2298 if (cue_point == NULL)
2300 const uint64 old_cue_point_size = cue_point->Size();
2301 const uint64 cluster_pos = cue_point->cluster_pos() + diff;
2302 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
2303 // New size of the cue is computed as follows
2304 // Let a = current sum of size of all CuePoints
2305 // Let b = Increase in Cue Point's size due to this iteration
2306 // Let c = Increase in size of Cues Element's length due to this iteration
2307 // (This is computed as CodedSize(a + b) - CodedSize(a))
2308 // Let d = b + c. Now d is the |diff| passed to the next recursive call.
2309 // Let e = a + b. Now e is the |cues_size| passed to the next recursive
2311 const uint64 cue_point_size_diff = cue_point->Size() - old_cue_point_size;
2312 const uint64 cue_size_diff =
2313 GetCodedUIntSize(*cues_size + cue_point_size_diff) -
2314 GetCodedUIntSize(*cues_size);
2315 *cues_size += cue_point_size_diff;
2316 diff = cue_size_diff + cue_point_size_diff;
2318 for (int32 i = 0; i < cues_.cue_entries_size(); ++i) {
2319 MoveCuesBeforeClustersHelper(diff, i, cues_size);
2324 void Segment::MoveCuesBeforeClusters() {
2325 const uint64 current_cue_size = cues_.Size();
2326 uint64 cue_size = 0;
2327 for (int32 i = 0; i < cues_.cue_entries_size(); ++i)
2328 cue_size += cues_.GetCueByIndex(i)->Size();
2329 for (int32 i = 0; i < cues_.cue_entries_size(); ++i)
2330 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
2332 // Adjust the Seek Entry to reflect the change in position
2333 // of Cluster and Cues
2334 int32 cluster_index = 0;
2335 int32 cues_index = 0;
2336 for (int32 i = 0; i < SeekHead::kSeekEntryCount; ++i) {
2337 if (seek_head_.GetId(i) == kMkvCluster)
2339 if (seek_head_.GetId(i) == kMkvCues)
2342 seek_head_.SetSeekEntry(cues_index, kMkvCues,
2343 seek_head_.GetPosition(cluster_index));
2344 seek_head_.SetSeekEntry(cluster_index, kMkvCluster,
2345 cues_.Size() + seek_head_.GetPosition(cues_index));
2348 bool Segment::Init(IMkvWriter* ptr_writer) {
2352 writer_cluster_ = ptr_writer;
2353 writer_cues_ = ptr_writer;
2354 writer_header_ = ptr_writer;
2355 return segment_info_.Init();
2358 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
2359 IMkvWriter* writer) {
2360 if (!writer->Seekable() || chunking_)
2362 const int64 cluster_offset =
2363 cluster_list_[0]->size_position() - GetUIntSize(kMkvCluster);
2365 // Copy the headers.
2366 if (!ChunkedCopy(reader, writer, 0, cluster_offset))
2369 // Recompute cue positions and seek entries.
2370 MoveCuesBeforeClusters();
2372 // Write cues and seek entries.
2373 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
2374 // second time with a different writer object. But the name Finalize() doesn't
2375 // indicate something we want to call more than once. So consider renaming it
2376 // to write() or some such.
2377 if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
2380 // Copy the Clusters.
2381 if (!ChunkedCopy(reader, writer, cluster_offset,
2382 cluster_end_offset_ - cluster_offset))
2385 // Update the Segment size in case the Cues size has changed.
2386 const int64 pos = writer->Position();
2387 const int64 segment_size = writer->Position() - payload_pos_;
2388 if (writer->Position(size_position_) ||
2389 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
2394 bool Segment::Finalize() {
2395 if (WriteFramesAll() < 0)
2398 if (mode_ == kFile) {
2399 if (cluster_list_size_ > 0) {
2400 // Update last cluster's size
2401 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2403 if (!old_cluster || !old_cluster->Finalize())
2407 if (chunking_ && chunk_writer_cluster_) {
2408 chunk_writer_cluster_->Close();
2412 const double duration =
2413 (static_cast<double>(last_timestamp_) + last_block_duration_) /
2414 segment_info_.timecode_scale();
2415 segment_info_.set_duration(duration);
2416 if (!segment_info_.Finalize(writer_header_))
2420 if (!seek_head_.AddSeekEntry(kMkvCues, MaxOffset()))
2424 if (!chunk_writer_cues_)
2428 if (!UpdateChunkName("cues", &name))
2431 const bool cues_open = chunk_writer_cues_->Open(name);
2437 cluster_end_offset_ = writer_cluster_->Position();
2439 // Write the seek headers and cues
2441 if (!cues_.Write(writer_cues_))
2444 if (!seek_head_.Finalize(writer_header_))
2447 if (writer_header_->Seekable()) {
2448 if (size_position_ == -1)
2451 const int64 segment_size = MaxOffset();
2452 if (segment_size < 1)
2455 const int64 pos = writer_header_->Position();
2456 UpdateDocTypeVersion();
2457 if (doc_type_version_ != doc_type_version_written_) {
2458 if (writer_header_->Position(0))
2461 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2463 if (writer_header_->Position() != ebml_header_size_)
2466 doc_type_version_written_ = doc_type_version_;
2469 if (writer_header_->Position(size_position_))
2472 if (WriteUIntSize(writer_header_, segment_size, 8))
2475 if (writer_header_->Position(pos))
2480 // Do not close any writers until the segment size has been written,
2481 // otherwise the size may be off.
2482 if (!chunk_writer_cues_ || !chunk_writer_header_)
2485 chunk_writer_cues_->Close();
2486 chunk_writer_header_->Close();
2493 Track* Segment::AddTrack(int32 number) {
2494 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
2499 if (!tracks_.AddTrack(track, number)) {
2507 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
2509 Tag* Segment::AddTag() { return tags_.AddTag(); }
2511 uint64 Segment::AddVideoTrack(int32 width, int32 height, int32 number) {
2512 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
2516 track->set_type(Tracks::kVideo);
2517 track->set_codec_id(Tracks::kVp8CodecId);
2518 track->set_width(width);
2519 track->set_height(height);
2521 tracks_.AddTrack(track, number);
2524 return track->number();
2527 bool Segment::AddCuePoint(uint64 timestamp, uint64 track) {
2528 if (cluster_list_size_ < 1)
2531 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2535 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
2539 cue->set_time(timestamp / segment_info_.timecode_scale());
2540 cue->set_block_number(cluster->blocks_added());
2541 cue->set_cluster_pos(cluster->position_for_cues());
2542 cue->set_track(track);
2543 if (!cues_.AddCue(cue))
2546 new_cuepoint_ = false;
2550 uint64 Segment::AddAudioTrack(int32 sample_rate, int32 channels, int32 number) {
2551 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
2555 track->set_type(Tracks::kAudio);
2556 track->set_codec_id(Tracks::kVorbisCodecId);
2557 track->set_sample_rate(sample_rate);
2558 track->set_channels(channels);
2560 tracks_.AddTrack(track, number);
2562 return track->number();
2565 bool Segment::AddFrame(const uint8* data, uint64 length, uint64 track_number,
2566 uint64 timestamp, bool is_key) {
2571 if (!frame.Init(data, length))
2573 frame.set_track_number(track_number);
2574 frame.set_timestamp(timestamp);
2575 frame.set_is_key(is_key);
2576 return AddGenericFrame(&frame);
2579 bool Segment::AddFrameWithAdditional(const uint8* data, uint64 length,
2580 const uint8* additional,
2581 uint64 additional_length, uint64 add_id,
2582 uint64 track_number, uint64 timestamp,
2584 if (!data || !additional)
2588 if (!frame.Init(data, length) ||
2589 !frame.AddAdditionalData(additional, additional_length, add_id)) {
2592 frame.set_track_number(track_number);
2593 frame.set_timestamp(timestamp);
2594 frame.set_is_key(is_key);
2595 return AddGenericFrame(&frame);
2598 bool Segment::AddFrameWithDiscardPadding(const uint8* data, uint64 length,
2599 int64 discard_padding,
2600 uint64 track_number, uint64 timestamp,
2606 if (!frame.Init(data, length))
2608 frame.set_discard_padding(discard_padding);
2609 frame.set_track_number(track_number);
2610 frame.set_timestamp(timestamp);
2611 frame.set_is_key(is_key);
2612 return AddGenericFrame(&frame);
2615 bool Segment::AddMetadata(const uint8* data, uint64 length, uint64 track_number,
2616 uint64 timestamp_ns, uint64 duration_ns) {
2621 if (!frame.Init(data, length))
2623 frame.set_track_number(track_number);
2624 frame.set_timestamp(timestamp_ns);
2625 frame.set_duration(duration_ns);
2626 frame.set_is_key(true); // All metadata blocks are keyframes.
2627 return AddGenericFrame(&frame);
2630 bool Segment::AddGenericFrame(const Frame* frame) {
2634 if (!CheckHeaderInfo())
2637 // Check for non-monotonically increasing timestamps.
2638 if (frame->timestamp() < last_timestamp_)
2641 // Check if the track number is valid.
2642 if (!tracks_.GetTrackByNumber(frame->track_number()))
2645 if (frame->discard_padding() != 0)
2646 doc_type_version_ = 4;
2648 // If the segment has a video track hold onto audio frames to make sure the
2649 // audio that is associated with the start time of a video key-frame is
2650 // muxed into the same cluster.
2651 if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
2652 !force_new_cluster_) {
2653 Frame* const new_frame = new (std::nothrow) Frame();
2654 if (!new_frame || !new_frame->CopyFrom(*frame))
2656 return QueueFrame(new_frame);
2659 if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
2664 if (cluster_list_size_ < 1)
2667 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2671 // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
2672 // if it is not set already.
2673 bool frame_created = false;
2674 if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
2675 !frame->reference_block_timestamp_set()) {
2676 Frame* const new_frame = new (std::nothrow) Frame();
2677 if (!new_frame->CopyFrom(*frame))
2679 new_frame->set_reference_block_timestamp(
2680 last_track_timestamp_[frame->track_number() - 1]);
2682 frame_created = true;
2685 if (!cluster->AddFrame(frame))
2688 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
2689 if (!AddCuePoint(frame->timestamp(), cues_track_))
2693 last_timestamp_ = frame->timestamp();
2694 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
2695 last_block_duration_ = frame->duration();
2703 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
2705 bool Segment::SetChunking(bool chunking, const char* filename) {
2706 if (chunk_count_ > 0)
2713 // Check if we are being set to what is already set.
2714 if (chunking_ && !strcmp(filename, chunking_base_name_))
2717 const size_t name_length = strlen(filename) + 1;
2718 char* const temp = new (std::nothrow) char[name_length]; // NOLINT
2723 strcpy_s(temp, name_length, filename);
2725 strcpy(temp, filename);
2728 delete[] chunking_base_name_;
2729 chunking_base_name_ = temp;
2731 if (!UpdateChunkName("chk", &chunk_name_))
2734 if (!chunk_writer_cluster_) {
2735 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
2736 if (!chunk_writer_cluster_)
2740 if (!chunk_writer_cues_) {
2741 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
2742 if (!chunk_writer_cues_)
2746 if (!chunk_writer_header_) {
2747 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
2748 if (!chunk_writer_header_)
2752 if (!chunk_writer_cluster_->Open(chunk_name_))
2755 const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
2756 char* const header = new (std::nothrow) char[header_length]; // NOLINT
2761 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
2762 strcat_s(header, header_length, ".hdr");
2764 strcpy(header, chunking_base_name_);
2765 strcat(header, ".hdr");
2767 if (!chunk_writer_header_->Open(header)) {
2772 writer_cluster_ = chunk_writer_cluster_;
2773 writer_cues_ = chunk_writer_cues_;
2774 writer_header_ = chunk_writer_header_;
2779 chunking_ = chunking;
2784 bool Segment::CuesTrack(uint64 track_number) {
2785 const Track* const track = GetTrackByNumber(track_number);
2789 cues_track_ = track_number;
2793 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
2795 Track* Segment::GetTrackByNumber(uint64 track_number) const {
2796 return tracks_.GetTrackByNumber(track_number);
2799 bool Segment::WriteSegmentHeader() {
2800 UpdateDocTypeVersion();
2802 // TODO(fgalligan): Support more than one segment.
2803 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2805 doc_type_version_written_ = doc_type_version_;
2806 ebml_header_size_ = static_cast<int32>(writer_header_->Position());
2808 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
2809 // will write over duration when the file is finalized.
2810 if (WriteID(writer_header_, kMkvSegment))
2814 size_position_ = writer_header_->Position();
2816 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
2817 // bytes because if we are going to overwrite the segment size later we do
2818 // not know how big our segment will be.
2819 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
2822 payload_pos_ = writer_header_->Position();
2824 if (mode_ == kFile && writer_header_->Seekable()) {
2825 // Set the duration > 0.0 so SegmentInfo will write out the duration. When
2826 // the muxer is done writing we will set the correct duration and have
2827 // SegmentInfo upadte it.
2828 segment_info_.set_duration(1.0);
2830 if (!seek_head_.Write(writer_header_))
2834 if (!seek_head_.AddSeekEntry(kMkvInfo, MaxOffset()))
2836 if (!segment_info_.Write(writer_header_))
2839 if (!seek_head_.AddSeekEntry(kMkvTracks, MaxOffset()))
2841 if (!tracks_.Write(writer_header_))
2844 if (chapters_.Count() > 0) {
2845 if (!seek_head_.AddSeekEntry(kMkvChapters, MaxOffset()))
2847 if (!chapters_.Write(writer_header_))
2851 if (tags_.Count() > 0) {
2852 if (!seek_head_.AddSeekEntry(kMkvTags, MaxOffset()))
2854 if (!tags_.Write(writer_header_))
2858 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
2859 if (!chunk_writer_header_)
2862 chunk_writer_header_->Close();
2865 header_written_ = true;
2870 // Here we are testing whether to create a new cluster, given a frame
2871 // having time frame_timestamp_ns.
2873 int Segment::TestFrame(uint64 track_number, uint64 frame_timestamp_ns,
2874 bool is_key) const {
2875 if (force_new_cluster_)
2878 // If no clusters have been created yet, then create a new cluster
2879 // and write this frame immediately, in the new cluster. This path
2880 // should only be followed once, the first time we attempt to write
2883 if (cluster_list_size_ <= 0)
2886 // There exists at least one cluster. We must compare the frame to
2887 // the last cluster, in order to determine whether the frame is
2888 // written to the existing cluster, or that a new cluster should be
2891 const uint64 timecode_scale = segment_info_.timecode_scale();
2892 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
2894 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
2895 const uint64 last_cluster_timecode = last_cluster->timecode();
2897 // For completeness we test for the case when the frame's timecode
2898 // is less than the cluster's timecode. Although in principle that
2899 // is allowed, this muxer doesn't actually write clusters like that,
2900 // so this indicates a bug somewhere in our algorithm.
2902 if (frame_timecode < last_cluster_timecode) // should never happen
2905 // If the frame has a timestamp significantly larger than the last
2906 // cluster (in Matroska, cluster-relative timestamps are serialized
2907 // using a 16-bit signed integer), then we cannot write this frame
2908 // to that cluster, and so we must create a new cluster.
2910 const int64 delta_timecode = frame_timecode - last_cluster_timecode;
2912 if (delta_timecode > kMaxBlockTimecode)
2915 // We decide to create a new cluster when we have a video keyframe.
2916 // This will flush queued (audio) frames, and write the keyframe
2917 // immediately, in the newly-created cluster.
2919 if (is_key && tracks_.TrackIsVideo(track_number))
2922 // Create a new cluster if we have accumulated too many frames
2923 // already, where "too many" is defined as "the total time of frames
2924 // in the cluster exceeds a threshold".
2926 const uint64 delta_ns = delta_timecode * timecode_scale;
2928 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
2931 // This is similar to the case above, with the difference that a new
2932 // cluster is created when the size of the current cluster exceeds a
2935 const uint64 cluster_size = last_cluster->payload_size();
2937 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
2940 // There's no need to create a new cluster, so emit this frame now.
2945 bool Segment::MakeNewCluster(uint64 frame_timestamp_ns) {
2946 const int32 new_size = cluster_list_size_ + 1;
2948 if (new_size > cluster_list_capacity_) {
2949 // Add more clusters.
2950 const int32 new_capacity =
2951 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
2952 Cluster** const clusters =
2953 new (std::nothrow) Cluster*[new_capacity]; // NOLINT
2957 for (int32 i = 0; i < cluster_list_size_; ++i) {
2958 clusters[i] = cluster_list_[i];
2961 delete[] cluster_list_;
2963 cluster_list_ = clusters;
2964 cluster_list_capacity_ = new_capacity;
2967 if (!WriteFramesLessThan(frame_timestamp_ns))
2970 if (mode_ == kFile) {
2971 if (cluster_list_size_ > 0) {
2972 // Update old cluster's size
2973 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2975 if (!old_cluster || !old_cluster->Finalize())
2980 new_cuepoint_ = true;
2983 if (chunking_ && cluster_list_size_ > 0) {
2984 chunk_writer_cluster_->Close();
2987 if (!UpdateChunkName("chk", &chunk_name_))
2989 if (!chunk_writer_cluster_->Open(chunk_name_))
2993 const uint64 timecode_scale = segment_info_.timecode_scale();
2994 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
2996 uint64 cluster_timecode = frame_timecode;
2998 if (frames_size_ > 0) {
2999 const Frame* const f = frames_[0]; // earliest queued frame
3000 const uint64 ns = f->timestamp();
3001 const uint64 tc = ns / timecode_scale;
3003 if (tc < cluster_timecode)
3004 cluster_timecode = tc;
3007 Cluster*& cluster = cluster_list_[cluster_list_size_];
3008 const int64 offset = MaxOffset();
3009 cluster = new (std::nothrow) Cluster(cluster_timecode, // NOLINT
3010 offset, segment_info_.timecode_scale());
3014 if (!cluster->Init(writer_cluster_))
3017 cluster_list_size_ = new_size;
3021 bool Segment::DoNewClusterProcessing(uint64 track_number,
3022 uint64 frame_timestamp_ns, bool is_key) {
3024 // Based on the characteristics of the current frame and current
3025 // cluster, decide whether to create a new cluster.
3026 const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
3027 if (result < 0) // error
3030 // Always set force_new_cluster_ to false after TestFrame.
3031 force_new_cluster_ = false;
3033 // A non-zero result means create a new cluster.
3034 if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
3037 // Write queued (audio) frames.
3038 const int frame_count = WriteFramesAll();
3039 if (frame_count < 0) // error
3042 // Write the current frame to the current cluster (if TestFrame
3043 // returns 0) or to a newly created cluster (TestFrame returns 1).
3047 // TestFrame returned 2, which means there was a large time
3048 // difference between the cluster and the frame itself. Do the
3049 // test again, comparing the frame to the new cluster.
3053 bool Segment::CheckHeaderInfo() {
3054 if (!header_written_) {
3055 if (!WriteSegmentHeader())
3058 if (!seek_head_.AddSeekEntry(kMkvCluster, MaxOffset()))
3061 if (output_cues_ && cues_track_ == 0) {
3062 // Check for a video track
3063 for (uint32 i = 0; i < tracks_.track_entries_size(); ++i) {
3064 const Track* const track = tracks_.GetTrackByIndex(i);
3068 if (tracks_.TrackIsVideo(track->number())) {
3069 cues_track_ = track->number();
3074 // Set first track found
3075 if (cues_track_ == 0) {
3076 const Track* const track = tracks_.GetTrackByIndex(0);
3080 cues_track_ = track->number();
3087 void Segment::UpdateDocTypeVersion() {
3088 for (uint32 index = 0; index < tracks_.track_entries_size(); ++index) {
3089 const Track* track = tracks_.GetTrackByIndex(index);
3092 if ((track->codec_delay() || track->seek_pre_roll()) &&
3093 doc_type_version_ < 4) {
3094 doc_type_version_ = 4;
3100 bool Segment::UpdateChunkName(const char* ext, char** name) const {
3106 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3108 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3111 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
3112 char* const str = new (std::nothrow) char[length]; // NOLINT
3117 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
3118 strcat_s(str, length, ext_chk);
3120 strcpy(str, chunking_base_name_);
3121 strcat(str, ext_chk);
3130 int64 Segment::MaxOffset() {
3131 if (!writer_header_)
3134 int64 offset = writer_header_->Position() - payload_pos_;
3137 for (int32 i = 0; i < cluster_list_size_; ++i) {
3138 Cluster* const cluster = cluster_list_[i];
3139 offset += cluster->Size();
3143 offset += writer_cues_->Position();
3149 bool Segment::QueueFrame(Frame* frame) {
3150 const int32 new_size = frames_size_ + 1;
3152 if (new_size > frames_capacity_) {
3154 const int32 new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
3156 if (new_capacity < 1)
3159 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
3163 for (int32 i = 0; i < frames_size_; ++i) {
3164 frames[i] = frames_[i];
3169 frames_capacity_ = new_capacity;
3172 frames_[frames_size_++] = frame;
3177 int Segment::WriteFramesAll() {
3178 if (frames_ == NULL)
3181 if (cluster_list_size_ < 1)
3184 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3189 for (int32 i = 0; i < frames_size_; ++i) {
3190 Frame*& frame = frames_[i];
3191 // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
3192 // places where |doc_type_version_| needs to be updated.
3193 if (frame->discard_padding() != 0)
3194 doc_type_version_ = 4;
3195 if (!cluster->AddFrame(frame))
3198 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3199 if (!AddCuePoint(frame->timestamp(), cues_track_))
3203 if (frame->timestamp() > last_timestamp_) {
3204 last_timestamp_ = frame->timestamp();
3205 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3212 const int result = frames_size_;
3218 bool Segment::WriteFramesLessThan(uint64 timestamp) {
3219 // Check |cluster_list_size_| to see if this is the first cluster. If it is
3220 // the first cluster the audio frames that are less than the first video
3221 // timesatmp will be written in a later step.
3222 if (frames_size_ > 0 && cluster_list_size_ > 0) {
3226 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3230 int32 shift_left = 0;
3232 // TODO(fgalligan): Change this to use the durations of frames instead of
3233 // the next frame's start time if the duration is accurate.
3234 for (int32 i = 1; i < frames_size_; ++i) {
3235 const Frame* const frame_curr = frames_[i];
3237 if (frame_curr->timestamp() > timestamp)
3240 const Frame* const frame_prev = frames_[i - 1];
3241 if (frame_prev->discard_padding() != 0)
3242 doc_type_version_ = 4;
3243 if (!cluster->AddFrame(frame_prev))
3246 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
3247 if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
3252 if (frame_prev->timestamp() > last_timestamp_) {
3253 last_timestamp_ = frame_prev->timestamp();
3254 last_track_timestamp_[frame_prev->track_number() - 1] =
3255 frame_prev->timestamp();
3261 if (shift_left > 0) {
3262 if (shift_left >= frames_size_)
3265 const int32 new_frames_size = frames_size_ - shift_left;
3266 for (int32 i = 0; i < new_frames_size; ++i) {
3267 frames_[i] = frames_[i + shift_left];
3270 frames_size_ = new_frames_size;
3277 } // namespace mkvmuxer