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";
1049 Tracks::Tracks() : track_entries_(NULL), track_entries_size_(0) {}
1052 if (track_entries_) {
1053 for (uint32 i = 0; i < track_entries_size_; ++i) {
1054 Track* const track = track_entries_[i];
1057 delete[] track_entries_;
1061 bool Tracks::AddTrack(Track* track, int32 number) {
1065 // This muxer only supports track numbers in the range [1, 126], in
1066 // order to be able (to use Matroska integer representation) to
1067 // serialize the block header (of which the track number is a part)
1068 // for a frame using exactly 4 bytes.
1073 uint32 track_num = number;
1075 if (track_num > 0) {
1076 // Check to make sure a track does not already have |track_num|.
1077 for (uint32 i = 0; i < track_entries_size_; ++i) {
1078 if (track_entries_[i]->number() == track_num)
1083 const uint32 count = track_entries_size_ + 1;
1085 Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
1089 for (uint32 i = 0; i < track_entries_size_; ++i) {
1090 track_entries[i] = track_entries_[i];
1093 delete[] track_entries_;
1095 // Find the lowest availible track number > 0.
1096 if (track_num == 0) {
1099 // Check to make sure a track does not already have |track_num|.
1103 for (uint32 i = 0; i < track_entries_size_; ++i) {
1104 if (track_entries[i]->number() == track_num) {
1112 track->set_number(track_num);
1114 track_entries_ = track_entries;
1115 track_entries_[track_entries_size_] = track;
1116 track_entries_size_ = count;
1120 const Track* Tracks::GetTrackByIndex(uint32 index) const {
1121 if (track_entries_ == NULL)
1124 if (index >= track_entries_size_)
1127 return track_entries_[index];
1130 Track* Tracks::GetTrackByNumber(uint64 track_number) const {
1131 const int32 count = track_entries_size();
1132 for (int32 i = 0; i < count; ++i) {
1133 if (track_entries_[i]->number() == track_number)
1134 return track_entries_[i];
1140 bool Tracks::TrackIsAudio(uint64 track_number) const {
1141 const Track* const track = GetTrackByNumber(track_number);
1143 if (track->type() == kAudio)
1149 bool Tracks::TrackIsVideo(uint64 track_number) const {
1150 const Track* const track = GetTrackByNumber(track_number);
1152 if (track->type() == kVideo)
1158 bool Tracks::Write(IMkvWriter* writer) const {
1160 const int32 count = track_entries_size();
1161 for (int32 i = 0; i < count; ++i) {
1162 const Track* const track = GetTrackByIndex(i);
1167 size += track->Size();
1170 if (!WriteEbmlMasterElement(writer, kMkvTracks, size))
1173 const int64 payload_position = writer->Position();
1174 if (payload_position < 0)
1177 for (int32 i = 0; i < count; ++i) {
1178 const Track* const track = GetTrackByIndex(i);
1179 if (!track->Write(writer))
1183 const int64 stop_position = writer->Position();
1184 if (stop_position < 0 ||
1185 stop_position - payload_position != static_cast<int64>(size))
1191 ///////////////////////////////////////////////////////////////
1195 bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
1197 void Chapter::set_time(const Segment& segment, uint64 start_ns, uint64 end_ns) {
1198 const SegmentInfo* const info = segment.GetSegmentInfo();
1199 const uint64 timecode_scale = info->timecode_scale();
1200 start_timecode_ = start_ns / timecode_scale;
1201 end_timecode_ = end_ns / timecode_scale;
1204 bool Chapter::add_string(const char* title, const char* language,
1205 const char* country) {
1206 if (!ExpandDisplaysArray())
1209 Display& d = displays_[displays_count_++];
1212 if (!d.set_title(title))
1215 if (!d.set_language(language))
1218 if (!d.set_country(country))
1224 Chapter::Chapter() {
1225 // This ctor only constructs the object. Proper initialization is
1226 // done in Init() (called in Chapters::AddChapter()). The only
1227 // reason we bother implementing this ctor is because we had to
1228 // declare it as private (along with the dtor), in order to prevent
1229 // clients from creating Chapter instances (a privelege we grant
1230 // only to the Chapters class). Doing no initialization here also
1231 // means that creating arrays of chapter objects is more efficient,
1232 // because we only initialize each new chapter object as it becomes
1233 // active on the array.
1236 Chapter::~Chapter() {}
1238 void Chapter::Init(unsigned int* seed) {
1240 start_timecode_ = 0;
1244 displays_count_ = 0;
1245 uid_ = MakeUID(seed);
1248 void Chapter::ShallowCopy(Chapter* dst) const {
1250 dst->start_timecode_ = start_timecode_;
1251 dst->end_timecode_ = end_timecode_;
1253 dst->displays_ = displays_;
1254 dst->displays_size_ = displays_size_;
1255 dst->displays_count_ = displays_count_;
1258 void Chapter::Clear() {
1261 while (displays_count_ > 0) {
1262 Display& d = displays_[--displays_count_];
1272 bool Chapter::ExpandDisplaysArray() {
1273 if (displays_size_ > displays_count_)
1274 return true; // nothing to do yet
1276 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
1278 Display* const displays = new (std::nothrow) Display[size]; // NOLINT
1279 if (displays == NULL)
1282 for (int idx = 0; idx < displays_count_; ++idx) {
1283 displays[idx] = displays_[idx]; // shallow copy
1288 displays_ = displays;
1289 displays_size_ = size;
1294 uint64 Chapter::WriteAtom(IMkvWriter* writer) const {
1295 uint64 payload_size = EbmlElementSize(kMkvChapterStringUID, id_) +
1296 EbmlElementSize(kMkvChapterUID, uid_) +
1297 EbmlElementSize(kMkvChapterTimeStart, start_timecode_) +
1298 EbmlElementSize(kMkvChapterTimeEnd, end_timecode_);
1300 for (int idx = 0; idx < displays_count_; ++idx) {
1301 const Display& d = displays_[idx];
1302 payload_size += d.WriteDisplay(NULL);
1305 const uint64 atom_size =
1306 EbmlMasterElementSize(kMkvChapterAtom, payload_size) + payload_size;
1311 const int64 start = writer->Position();
1313 if (!WriteEbmlMasterElement(writer, kMkvChapterAtom, payload_size))
1316 if (!WriteEbmlElement(writer, kMkvChapterStringUID, id_))
1319 if (!WriteEbmlElement(writer, kMkvChapterUID, uid_))
1322 if (!WriteEbmlElement(writer, kMkvChapterTimeStart, start_timecode_))
1325 if (!WriteEbmlElement(writer, kMkvChapterTimeEnd, end_timecode_))
1328 for (int idx = 0; idx < displays_count_; ++idx) {
1329 const Display& d = displays_[idx];
1331 if (!d.WriteDisplay(writer))
1335 const int64 stop = writer->Position();
1337 if (stop >= start && uint64(stop - start) != atom_size)
1343 void Chapter::Display::Init() {
1349 void Chapter::Display::Clear() {
1350 StrCpy(NULL, &title_);
1351 StrCpy(NULL, &language_);
1352 StrCpy(NULL, &country_);
1355 bool Chapter::Display::set_title(const char* title) {
1356 return StrCpy(title, &title_);
1359 bool Chapter::Display::set_language(const char* language) {
1360 return StrCpy(language, &language_);
1363 bool Chapter::Display::set_country(const char* country) {
1364 return StrCpy(country, &country_);
1367 uint64 Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
1368 uint64 payload_size = EbmlElementSize(kMkvChapString, title_);
1371 payload_size += EbmlElementSize(kMkvChapLanguage, language_);
1374 payload_size += EbmlElementSize(kMkvChapCountry, country_);
1376 const uint64 display_size =
1377 EbmlMasterElementSize(kMkvChapterDisplay, payload_size) + payload_size;
1380 return display_size;
1382 const int64 start = writer->Position();
1384 if (!WriteEbmlMasterElement(writer, kMkvChapterDisplay, payload_size))
1387 if (!WriteEbmlElement(writer, kMkvChapString, title_))
1391 if (!WriteEbmlElement(writer, kMkvChapLanguage, language_))
1396 if (!WriteEbmlElement(writer, kMkvChapCountry, country_))
1400 const int64 stop = writer->Position();
1402 if (stop >= start && uint64(stop - start) != display_size)
1405 return display_size;
1408 ///////////////////////////////////////////////////////////////
1412 Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
1414 Chapters::~Chapters() {
1415 while (chapters_count_ > 0) {
1416 Chapter& chapter = chapters_[--chapters_count_];
1424 int Chapters::Count() const { return chapters_count_; }
1426 Chapter* Chapters::AddChapter(unsigned int* seed) {
1427 if (!ExpandChaptersArray())
1430 Chapter& chapter = chapters_[chapters_count_++];
1436 bool Chapters::Write(IMkvWriter* writer) const {
1440 const uint64 payload_size = WriteEdition(NULL); // return size only
1442 if (!WriteEbmlMasterElement(writer, kMkvChapters, payload_size))
1445 const int64 start = writer->Position();
1447 if (WriteEdition(writer) == 0) // error
1450 const int64 stop = writer->Position();
1452 if (stop >= start && uint64(stop - start) != payload_size)
1458 bool Chapters::ExpandChaptersArray() {
1459 if (chapters_size_ > chapters_count_)
1460 return true; // nothing to do yet
1462 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
1464 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
1465 if (chapters == NULL)
1468 for (int idx = 0; idx < chapters_count_; ++idx) {
1469 const Chapter& src = chapters_[idx];
1470 Chapter* const dst = chapters + idx;
1471 src.ShallowCopy(dst);
1476 chapters_ = chapters;
1477 chapters_size_ = size;
1482 uint64 Chapters::WriteEdition(IMkvWriter* writer) const {
1483 uint64 payload_size = 0;
1485 for (int idx = 0; idx < chapters_count_; ++idx) {
1486 const Chapter& chapter = chapters_[idx];
1487 payload_size += chapter.WriteAtom(NULL);
1490 const uint64 edition_size =
1491 EbmlMasterElementSize(kMkvEditionEntry, payload_size) + payload_size;
1493 if (writer == NULL) // return size only
1494 return edition_size;
1496 const int64 start = writer->Position();
1498 if (!WriteEbmlMasterElement(writer, kMkvEditionEntry, payload_size))
1501 for (int idx = 0; idx < chapters_count_; ++idx) {
1502 const Chapter& chapter = chapters_[idx];
1504 const uint64 chapter_size = chapter.WriteAtom(writer);
1505 if (chapter_size == 0) // error
1509 const int64 stop = writer->Position();
1511 if (stop >= start && uint64(stop - start) != edition_size)
1514 return edition_size;
1519 bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) {
1520 if (!ExpandSimpleTagsArray())
1523 SimpleTag& st = simple_tags_[simple_tags_count_++];
1526 if (!st.set_tag_name(tag_name))
1529 if (!st.set_tag_string(tag_string))
1536 simple_tags_ = NULL;
1537 simple_tags_size_ = 0;
1538 simple_tags_count_ = 0;
1543 void Tag::ShallowCopy(Tag* dst) const {
1544 dst->simple_tags_ = simple_tags_;
1545 dst->simple_tags_size_ = simple_tags_size_;
1546 dst->simple_tags_count_ = simple_tags_count_;
1550 while (simple_tags_count_ > 0) {
1551 SimpleTag& st = simple_tags_[--simple_tags_count_];
1555 delete[] simple_tags_;
1556 simple_tags_ = NULL;
1558 simple_tags_size_ = 0;
1561 bool Tag::ExpandSimpleTagsArray() {
1562 if (simple_tags_size_ > simple_tags_count_)
1563 return true; // nothing to do yet
1565 const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_;
1567 SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT
1568 if (simple_tags == NULL)
1571 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1572 simple_tags[idx] = simple_tags_[idx]; // shallow copy
1575 delete[] simple_tags_;
1577 simple_tags_ = simple_tags;
1578 simple_tags_size_ = size;
1583 uint64 Tag::Write(IMkvWriter* writer) const {
1584 uint64 payload_size = 0;
1586 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1587 const SimpleTag& st = simple_tags_[idx];
1588 payload_size += st.Write(NULL);
1591 const uint64 tag_size =
1592 EbmlMasterElementSize(kMkvTag, payload_size) + payload_size;
1597 const int64 start = writer->Position();
1599 if (!WriteEbmlMasterElement(writer, kMkvTag, payload_size))
1602 for (int idx = 0; idx < simple_tags_count_; ++idx) {
1603 const SimpleTag& st = simple_tags_[idx];
1605 if (!st.Write(writer))
1609 const int64 stop = writer->Position();
1611 if (stop >= start && uint64(stop - start) != tag_size)
1619 void Tag::SimpleTag::Init() {
1624 void Tag::SimpleTag::Clear() {
1625 StrCpy(NULL, &tag_name_);
1626 StrCpy(NULL, &tag_string_);
1629 bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
1630 return StrCpy(tag_name, &tag_name_);
1633 bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
1634 return StrCpy(tag_string, &tag_string_);
1637 uint64 Tag::SimpleTag::Write(IMkvWriter* writer) const {
1638 uint64 payload_size = EbmlElementSize(kMkvTagName, tag_name_);
1640 payload_size += EbmlElementSize(kMkvTagString, tag_string_);
1642 const uint64 simple_tag_size =
1643 EbmlMasterElementSize(kMkvSimpleTag, payload_size) + payload_size;
1646 return simple_tag_size;
1648 const int64 start = writer->Position();
1650 if (!WriteEbmlMasterElement(writer, kMkvSimpleTag, payload_size))
1653 if (!WriteEbmlElement(writer, kMkvTagName, tag_name_))
1656 if (!WriteEbmlElement(writer, kMkvTagString, tag_string_))
1659 const int64 stop = writer->Position();
1661 if (stop >= start && uint64(stop - start) != simple_tag_size)
1664 return simple_tag_size;
1669 Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
1672 while (tags_count_ > 0) {
1673 Tag& tag = tags_[--tags_count_];
1681 int Tags::Count() const { return tags_count_; }
1683 Tag* Tags::AddTag() {
1684 if (!ExpandTagsArray())
1687 Tag& tag = tags_[tags_count_++];
1692 bool Tags::Write(IMkvWriter* writer) const {
1696 uint64 payload_size = 0;
1698 for (int idx = 0; idx < tags_count_; ++idx) {
1699 const Tag& tag = tags_[idx];
1700 payload_size += tag.Write(NULL);
1703 if (!WriteEbmlMasterElement(writer, kMkvTags, payload_size))
1706 const int64 start = writer->Position();
1708 for (int idx = 0; idx < tags_count_; ++idx) {
1709 const Tag& tag = tags_[idx];
1711 const uint64 tag_size = tag.Write(writer);
1712 if (tag_size == 0) // error
1716 const int64 stop = writer->Position();
1718 if (stop >= start && uint64(stop - start) != payload_size)
1724 bool Tags::ExpandTagsArray() {
1725 if (tags_size_ > tags_count_)
1726 return true; // nothing to do yet
1728 const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_;
1730 Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT
1734 for (int idx = 0; idx < tags_count_; ++idx) {
1735 const Tag& src = tags_[idx];
1736 Tag* const dst = tags + idx;
1737 src.ShallowCopy(dst);
1748 ///////////////////////////////////////////////////////////////
1752 Cluster::Cluster(uint64 timecode, int64 cues_pos, uint64 timecode_scale)
1755 header_written_(false),
1757 position_for_cues_(cues_pos),
1759 timecode_(timecode),
1760 timecode_scale_(timecode_scale),
1763 Cluster::~Cluster() {}
1765 bool Cluster::Init(IMkvWriter* ptr_writer) {
1769 writer_ = ptr_writer;
1773 bool Cluster::AddFrame(const Frame* const frame) { return DoWriteFrame(frame); }
1775 bool Cluster::AddFrame(const uint8* data, uint64 length, uint64 track_number,
1776 uint64 abs_timecode, bool is_key) {
1778 if (!frame.Init(data, length))
1780 frame.set_track_number(track_number);
1781 frame.set_timestamp(abs_timecode);
1782 frame.set_is_key(is_key);
1783 return DoWriteFrame(&frame);
1786 bool Cluster::AddFrameWithAdditional(const uint8* data, uint64 length,
1787 const uint8* additional,
1788 uint64 additional_length, uint64 add_id,
1789 uint64 track_number, uint64 abs_timecode,
1791 if (!additional || additional_length == 0) {
1795 if (!frame.Init(data, length) ||
1796 !frame.AddAdditionalData(additional, additional_length, add_id)) {
1799 frame.set_track_number(track_number);
1800 frame.set_timestamp(abs_timecode);
1801 frame.set_is_key(is_key);
1802 return DoWriteFrame(&frame);
1805 bool Cluster::AddFrameWithDiscardPadding(const uint8* data, uint64 length,
1806 int64 discard_padding,
1807 uint64 track_number,
1808 uint64 abs_timecode, bool is_key) {
1810 if (!frame.Init(data, length))
1812 frame.set_discard_padding(discard_padding);
1813 frame.set_track_number(track_number);
1814 frame.set_timestamp(abs_timecode);
1815 frame.set_is_key(is_key);
1816 return DoWriteFrame(&frame);
1819 bool Cluster::AddMetadata(const uint8* data, uint64 length, uint64 track_number,
1820 uint64 abs_timecode, uint64 duration_timecode) {
1822 if (!frame.Init(data, length))
1824 frame.set_track_number(track_number);
1825 frame.set_timestamp(abs_timecode);
1826 frame.set_duration(duration_timecode);
1827 frame.set_is_key(true); // All metadata blocks are keyframes.
1828 return DoWriteFrame(&frame);
1831 void Cluster::AddPayloadSize(uint64 size) { payload_size_ += size; }
1833 bool Cluster::Finalize() {
1834 if (!writer_ || finalized_ || size_position_ == -1)
1837 if (writer_->Seekable()) {
1838 const int64 pos = writer_->Position();
1840 if (writer_->Position(size_position_))
1843 if (WriteUIntSize(writer_, payload_size(), 8))
1846 if (writer_->Position(pos))
1855 uint64 Cluster::Size() const {
1856 const uint64 element_size =
1857 EbmlMasterElementSize(kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) + payload_size_;
1858 return element_size;
1861 bool Cluster::PreWriteBlock() {
1865 if (!header_written_) {
1866 if (!WriteClusterHeader())
1873 void Cluster::PostWriteBlock(uint64 element_size) {
1874 AddPayloadSize(element_size);
1878 int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const {
1879 const int64 cluster_timecode = this->Cluster::timecode();
1880 const int64 rel_timecode =
1881 static_cast<int64>(abs_timecode) - cluster_timecode;
1883 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
1886 return rel_timecode;
1889 bool Cluster::DoWriteFrame(const Frame* const frame) {
1890 if (!frame || !frame->IsValid())
1893 if (!PreWriteBlock())
1896 const uint64 element_size = WriteFrame(writer_, frame, this);
1897 if (element_size == 0)
1900 PostWriteBlock(element_size);
1904 bool Cluster::WriteClusterHeader() {
1908 if (WriteID(writer_, kMkvCluster))
1912 size_position_ = writer_->Position();
1914 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
1915 // bytes because we do not know how big our cluster will be.
1916 if (SerializeInt(writer_, kEbmlUnknownValue, 8))
1919 if (!WriteEbmlElement(writer_, kMkvTimecode, timecode()))
1921 AddPayloadSize(EbmlElementSize(kMkvTimecode, timecode()));
1922 header_written_ = true;
1927 ///////////////////////////////////////////////////////////////
1931 SeekHead::SeekHead() : start_pos_(0ULL) {
1932 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1933 seek_entry_id_[i] = 0;
1934 seek_entry_pos_[i] = 0;
1938 SeekHead::~SeekHead() {}
1940 bool SeekHead::Finalize(IMkvWriter* writer) const {
1941 if (writer->Seekable()) {
1942 if (start_pos_ == -1)
1945 uint64 payload_size = 0;
1946 uint64 entry_size[kSeekEntryCount];
1948 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1949 if (seek_entry_id_[i] != 0) {
1951 EbmlElementSize(kMkvSeekID, static_cast<uint64>(seek_entry_id_[i]));
1952 entry_size[i] += EbmlElementSize(kMkvSeekPosition, seek_entry_pos_[i]);
1955 EbmlMasterElementSize(kMkvSeek, entry_size[i]) + entry_size[i];
1959 // No SeekHead elements
1960 if (payload_size == 0)
1963 const int64 pos = writer->Position();
1964 if (writer->Position(start_pos_))
1967 if (!WriteEbmlMasterElement(writer, kMkvSeekHead, payload_size))
1970 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1971 if (seek_entry_id_[i] != 0) {
1972 if (!WriteEbmlMasterElement(writer, kMkvSeek, entry_size[i]))
1975 if (!WriteEbmlElement(writer, kMkvSeekID,
1976 static_cast<uint64>(seek_entry_id_[i])))
1979 if (!WriteEbmlElement(writer, kMkvSeekPosition, seek_entry_pos_[i]))
1984 const uint64 total_entry_size = kSeekEntryCount * MaxEntrySize();
1985 const uint64 total_size =
1986 EbmlMasterElementSize(kMkvSeekHead, total_entry_size) +
1988 const int64 size_left = total_size - (writer->Position() - start_pos_);
1990 const uint64 bytes_written = WriteVoidElement(writer, size_left);
1994 if (writer->Position(pos))
2001 bool SeekHead::Write(IMkvWriter* writer) {
2002 const uint64 entry_size = kSeekEntryCount * MaxEntrySize();
2003 const uint64 size = EbmlMasterElementSize(kMkvSeekHead, entry_size);
2005 start_pos_ = writer->Position();
2007 const uint64 bytes_written = WriteVoidElement(writer, size + entry_size);
2014 bool SeekHead::AddSeekEntry(uint32 id, uint64 pos) {
2015 for (int32 i = 0; i < kSeekEntryCount; ++i) {
2016 if (seek_entry_id_[i] == 0) {
2017 seek_entry_id_[i] = id;
2018 seek_entry_pos_[i] = pos;
2025 uint32 SeekHead::GetId(int index) const {
2026 if (index < 0 || index >= kSeekEntryCount)
2028 return seek_entry_id_[index];
2031 uint64 SeekHead::GetPosition(int index) const {
2032 if (index < 0 || index >= kSeekEntryCount)
2034 return seek_entry_pos_[index];
2037 bool SeekHead::SetSeekEntry(int index, uint32 id, uint64 position) {
2038 if (index < 0 || index >= kSeekEntryCount)
2040 seek_entry_id_[index] = id;
2041 seek_entry_pos_[index] = position;
2045 uint64 SeekHead::MaxEntrySize() const {
2046 const uint64 max_entry_payload_size =
2047 EbmlElementSize(kMkvSeekID, 0xffffffffULL) +
2048 EbmlElementSize(kMkvSeekPosition, 0xffffffffffffffffULL);
2049 const uint64 max_entry_size =
2050 EbmlMasterElementSize(kMkvSeek, max_entry_payload_size) +
2051 max_entry_payload_size;
2053 return max_entry_size;
2056 ///////////////////////////////////////////////////////////////
2058 // SegmentInfo Class
2060 SegmentInfo::SegmentInfo()
2063 timecode_scale_(1000000ULL),
2065 date_utc_(LLONG_MIN),
2066 duration_pos_(-1) {}
2068 SegmentInfo::~SegmentInfo() {
2069 delete[] muxing_app_;
2070 delete[] writing_app_;
2073 bool SegmentInfo::Init() {
2078 GetVersion(&major, &minor, &build, &revision);
2081 sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2082 minor, build, revision);
2084 snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
2085 minor, build, revision);
2088 const size_t app_len = strlen(temp) + 1;
2090 delete[] muxing_app_;
2092 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
2097 strcpy_s(muxing_app_, app_len, temp);
2099 strcpy(muxing_app_, temp);
2102 set_writing_app(temp);
2108 bool SegmentInfo::Finalize(IMkvWriter* writer) const {
2112 if (duration_ > 0.0) {
2113 if (writer->Seekable()) {
2114 if (duration_pos_ == -1)
2117 const int64 pos = writer->Position();
2119 if (writer->Position(duration_pos_))
2122 if (!WriteEbmlElement(writer, kMkvDuration,
2123 static_cast<float>(duration_)))
2126 if (writer->Position(pos))
2134 bool SegmentInfo::Write(IMkvWriter* writer) {
2135 if (!writer || !muxing_app_ || !writing_app_)
2138 uint64 size = EbmlElementSize(kMkvTimecodeScale, timecode_scale_);
2139 if (duration_ > 0.0)
2140 size += EbmlElementSize(kMkvDuration, static_cast<float>(duration_));
2141 if (date_utc_ != LLONG_MIN)
2142 size += EbmlDateElementSize(kMkvDateUTC);
2143 size += EbmlElementSize(kMkvMuxingApp, muxing_app_);
2144 size += EbmlElementSize(kMkvWritingApp, writing_app_);
2146 if (!WriteEbmlMasterElement(writer, kMkvInfo, size))
2149 const int64 payload_position = writer->Position();
2150 if (payload_position < 0)
2153 if (!WriteEbmlElement(writer, kMkvTimecodeScale, timecode_scale_))
2156 if (duration_ > 0.0) {
2158 duration_pos_ = writer->Position();
2160 if (!WriteEbmlElement(writer, kMkvDuration, static_cast<float>(duration_)))
2164 if (date_utc_ != LLONG_MIN)
2165 WriteEbmlDateElement(writer, kMkvDateUTC, date_utc_);
2167 if (!WriteEbmlElement(writer, kMkvMuxingApp, muxing_app_))
2169 if (!WriteEbmlElement(writer, kMkvWritingApp, writing_app_))
2172 const int64 stop_position = writer->Position();
2173 if (stop_position < 0 ||
2174 stop_position - payload_position != static_cast<int64>(size))
2180 void SegmentInfo::set_muxing_app(const char* app) {
2182 const size_t length = strlen(app) + 1;
2183 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2188 strcpy_s(temp_str, length, app);
2190 strcpy(temp_str, app);
2193 delete[] muxing_app_;
2194 muxing_app_ = temp_str;
2198 void SegmentInfo::set_writing_app(const char* app) {
2200 const size_t length = strlen(app) + 1;
2201 char* temp_str = new (std::nothrow) char[length]; // NOLINT
2206 strcpy_s(temp_str, length, app);
2208 strcpy(temp_str, app);
2211 delete[] writing_app_;
2212 writing_app_ = temp_str;
2216 ///////////////////////////////////////////////////////////////
2223 chunk_writer_cluster_(NULL),
2224 chunk_writer_cues_(NULL),
2225 chunk_writer_header_(NULL),
2227 chunking_base_name_(NULL),
2228 cluster_list_(NULL),
2229 cluster_list_capacity_(0),
2230 cluster_list_size_(0),
2231 cues_position_(kAfterClusters),
2233 force_new_cluster_(false),
2235 frames_capacity_(0),
2238 header_written_(false),
2239 last_block_duration_(0),
2241 max_cluster_duration_(kDefaultMaxClusterDuration),
2242 max_cluster_size_(0),
2244 new_cuepoint_(false),
2248 doc_type_version_(kDefaultDocTypeVersion),
2249 doc_type_version_written_(0),
2250 writer_cluster_(NULL),
2252 writer_header_(NULL) {
2253 const time_t curr_time = time(NULL);
2254 seed_ = static_cast<unsigned int>(curr_time);
2260 Segment::~Segment() {
2261 if (cluster_list_) {
2262 for (int32 i = 0; i < cluster_list_size_; ++i) {
2263 Cluster* const cluster = cluster_list_[i];
2266 delete[] cluster_list_;
2270 for (int32 i = 0; i < frames_size_; ++i) {
2271 Frame* const frame = frames_[i];
2277 delete[] chunk_name_;
2278 delete[] chunking_base_name_;
2280 if (chunk_writer_cluster_) {
2281 chunk_writer_cluster_->Close();
2282 delete chunk_writer_cluster_;
2284 if (chunk_writer_cues_) {
2285 chunk_writer_cues_->Close();
2286 delete chunk_writer_cues_;
2288 if (chunk_writer_header_) {
2289 chunk_writer_header_->Close();
2290 delete chunk_writer_header_;
2294 void Segment::MoveCuesBeforeClustersHelper(uint64 diff, int32 index,
2295 uint64* cues_size) {
2296 CuePoint* const cue_point = cues_.GetCueByIndex(index);
2297 if (cue_point == NULL)
2299 const uint64 old_cue_point_size = cue_point->Size();
2300 const uint64 cluster_pos = cue_point->cluster_pos() + diff;
2301 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
2302 // New size of the cue is computed as follows
2303 // Let a = current sum of size of all CuePoints
2304 // Let b = Increase in Cue Point's size due to this iteration
2305 // Let c = Increase in size of Cues Element's length due to this iteration
2306 // (This is computed as CodedSize(a + b) - CodedSize(a))
2307 // Let d = b + c. Now d is the |diff| passed to the next recursive call.
2308 // Let e = a + b. Now e is the |cues_size| passed to the next recursive
2310 const uint64 cue_point_size_diff = cue_point->Size() - old_cue_point_size;
2311 const uint64 cue_size_diff =
2312 GetCodedUIntSize(*cues_size + cue_point_size_diff) -
2313 GetCodedUIntSize(*cues_size);
2314 *cues_size += cue_point_size_diff;
2315 diff = cue_size_diff + cue_point_size_diff;
2317 for (int32 i = 0; i < cues_.cue_entries_size(); ++i) {
2318 MoveCuesBeforeClustersHelper(diff, i, cues_size);
2323 void Segment::MoveCuesBeforeClusters() {
2324 const uint64 current_cue_size = cues_.Size();
2325 uint64 cue_size = 0;
2326 for (int32 i = 0; i < cues_.cue_entries_size(); ++i)
2327 cue_size += cues_.GetCueByIndex(i)->Size();
2328 for (int32 i = 0; i < cues_.cue_entries_size(); ++i)
2329 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
2331 // Adjust the Seek Entry to reflect the change in position
2332 // of Cluster and Cues
2333 int32 cluster_index = 0;
2334 int32 cues_index = 0;
2335 for (int32 i = 0; i < SeekHead::kSeekEntryCount; ++i) {
2336 if (seek_head_.GetId(i) == kMkvCluster)
2338 if (seek_head_.GetId(i) == kMkvCues)
2341 seek_head_.SetSeekEntry(cues_index, kMkvCues,
2342 seek_head_.GetPosition(cluster_index));
2343 seek_head_.SetSeekEntry(cluster_index, kMkvCluster,
2344 cues_.Size() + seek_head_.GetPosition(cues_index));
2347 bool Segment::Init(IMkvWriter* ptr_writer) {
2351 writer_cluster_ = ptr_writer;
2352 writer_cues_ = ptr_writer;
2353 writer_header_ = ptr_writer;
2354 return segment_info_.Init();
2357 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
2358 IMkvWriter* writer) {
2359 if (!writer->Seekable() || chunking_)
2361 const int64 cluster_offset =
2362 cluster_list_[0]->size_position() - GetUIntSize(kMkvCluster);
2364 // Copy the headers.
2365 if (!ChunkedCopy(reader, writer, 0, cluster_offset))
2368 // Recompute cue positions and seek entries.
2369 MoveCuesBeforeClusters();
2371 // Write cues and seek entries.
2372 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
2373 // second time with a different writer object. But the name Finalize() doesn't
2374 // indicate something we want to call more than once. So consider renaming it
2375 // to write() or some such.
2376 if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
2379 // Copy the Clusters.
2380 if (!ChunkedCopy(reader, writer, cluster_offset,
2381 cluster_end_offset_ - cluster_offset))
2384 // Update the Segment size in case the Cues size has changed.
2385 const int64 pos = writer->Position();
2386 const int64 segment_size = writer->Position() - payload_pos_;
2387 if (writer->Position(size_position_) ||
2388 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
2393 bool Segment::Finalize() {
2394 if (WriteFramesAll() < 0)
2397 if (mode_ == kFile) {
2398 if (cluster_list_size_ > 0) {
2399 // Update last cluster's size
2400 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2402 if (!old_cluster || !old_cluster->Finalize())
2406 if (chunking_ && chunk_writer_cluster_) {
2407 chunk_writer_cluster_->Close();
2411 const double duration =
2412 (static_cast<double>(last_timestamp_) + last_block_duration_) /
2413 segment_info_.timecode_scale();
2414 segment_info_.set_duration(duration);
2415 if (!segment_info_.Finalize(writer_header_))
2419 if (!seek_head_.AddSeekEntry(kMkvCues, MaxOffset()))
2423 if (!chunk_writer_cues_)
2427 if (!UpdateChunkName("cues", &name))
2430 const bool cues_open = chunk_writer_cues_->Open(name);
2436 cluster_end_offset_ = writer_cluster_->Position();
2438 // Write the seek headers and cues
2440 if (!cues_.Write(writer_cues_))
2443 if (!seek_head_.Finalize(writer_header_))
2446 if (writer_header_->Seekable()) {
2447 if (size_position_ == -1)
2450 const int64 segment_size = MaxOffset();
2451 if (segment_size < 1)
2454 const int64 pos = writer_header_->Position();
2455 UpdateDocTypeVersion();
2456 if (doc_type_version_ != doc_type_version_written_) {
2457 if (writer_header_->Position(0))
2460 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2462 if (writer_header_->Position() != ebml_header_size_)
2465 doc_type_version_written_ = doc_type_version_;
2468 if (writer_header_->Position(size_position_))
2471 if (WriteUIntSize(writer_header_, segment_size, 8))
2474 if (writer_header_->Position(pos))
2479 // Do not close any writers until the segment size has been written,
2480 // otherwise the size may be off.
2481 if (!chunk_writer_cues_ || !chunk_writer_header_)
2484 chunk_writer_cues_->Close();
2485 chunk_writer_header_->Close();
2492 Track* Segment::AddTrack(int32 number) {
2493 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
2498 if (!tracks_.AddTrack(track, number)) {
2506 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
2508 Tag* Segment::AddTag() { return tags_.AddTag(); }
2510 uint64 Segment::AddVideoTrack(int32 width, int32 height, int32 number) {
2511 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
2515 track->set_type(Tracks::kVideo);
2516 track->set_codec_id(Tracks::kVp8CodecId);
2517 track->set_width(width);
2518 track->set_height(height);
2520 tracks_.AddTrack(track, number);
2523 return track->number();
2526 bool Segment::AddCuePoint(uint64 timestamp, uint64 track) {
2527 if (cluster_list_size_ < 1)
2530 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2534 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
2538 cue->set_time(timestamp / segment_info_.timecode_scale());
2539 cue->set_block_number(cluster->blocks_added());
2540 cue->set_cluster_pos(cluster->position_for_cues());
2541 cue->set_track(track);
2542 if (!cues_.AddCue(cue))
2545 new_cuepoint_ = false;
2549 uint64 Segment::AddAudioTrack(int32 sample_rate, int32 channels, int32 number) {
2550 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
2554 track->set_type(Tracks::kAudio);
2555 track->set_codec_id(Tracks::kVorbisCodecId);
2556 track->set_sample_rate(sample_rate);
2557 track->set_channels(channels);
2559 tracks_.AddTrack(track, number);
2561 return track->number();
2564 bool Segment::AddFrame(const uint8* data, uint64 length, uint64 track_number,
2565 uint64 timestamp, bool is_key) {
2570 if (!frame.Init(data, length))
2572 frame.set_track_number(track_number);
2573 frame.set_timestamp(timestamp);
2574 frame.set_is_key(is_key);
2575 return AddGenericFrame(&frame);
2578 bool Segment::AddFrameWithAdditional(const uint8* data, uint64 length,
2579 const uint8* additional,
2580 uint64 additional_length, uint64 add_id,
2581 uint64 track_number, uint64 timestamp,
2583 if (!data || !additional)
2587 if (!frame.Init(data, length) ||
2588 !frame.AddAdditionalData(additional, additional_length, add_id)) {
2591 frame.set_track_number(track_number);
2592 frame.set_timestamp(timestamp);
2593 frame.set_is_key(is_key);
2594 return AddGenericFrame(&frame);
2597 bool Segment::AddFrameWithDiscardPadding(const uint8* data, uint64 length,
2598 int64 discard_padding,
2599 uint64 track_number, uint64 timestamp,
2605 if (!frame.Init(data, length))
2607 frame.set_discard_padding(discard_padding);
2608 frame.set_track_number(track_number);
2609 frame.set_timestamp(timestamp);
2610 frame.set_is_key(is_key);
2611 return AddGenericFrame(&frame);
2614 bool Segment::AddMetadata(const uint8* data, uint64 length, uint64 track_number,
2615 uint64 timestamp_ns, uint64 duration_ns) {
2620 if (!frame.Init(data, length))
2622 frame.set_track_number(track_number);
2623 frame.set_timestamp(timestamp_ns);
2624 frame.set_duration(duration_ns);
2625 frame.set_is_key(true); // All metadata blocks are keyframes.
2626 return AddGenericFrame(&frame);
2629 bool Segment::AddGenericFrame(const Frame* frame) {
2633 if (!CheckHeaderInfo())
2636 // Check for non-monotonically increasing timestamps.
2637 if (frame->timestamp() < last_timestamp_)
2640 // Check if the track number is valid.
2641 if (!tracks_.GetTrackByNumber(frame->track_number()))
2644 if (frame->discard_padding() != 0)
2645 doc_type_version_ = 4;
2647 // If the segment has a video track hold onto audio frames to make sure the
2648 // audio that is associated with the start time of a video key-frame is
2649 // muxed into the same cluster.
2650 if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
2651 !force_new_cluster_) {
2652 Frame* const new_frame = new (std::nothrow) Frame();
2653 if (!new_frame || !new_frame->CopyFrom(*frame))
2655 return QueueFrame(new_frame);
2658 if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
2663 if (cluster_list_size_ < 1)
2666 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2670 // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
2671 // if it is not set already.
2672 bool frame_created = false;
2673 if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
2674 !frame->reference_block_timestamp_set()) {
2675 Frame* const new_frame = new (std::nothrow) Frame();
2676 if (!new_frame->CopyFrom(*frame))
2678 new_frame->set_reference_block_timestamp(
2679 last_track_timestamp_[frame->track_number() - 1]);
2681 frame_created = true;
2684 if (!cluster->AddFrame(frame))
2687 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
2688 if (!AddCuePoint(frame->timestamp(), cues_track_))
2692 last_timestamp_ = frame->timestamp();
2693 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
2694 last_block_duration_ = frame->duration();
2702 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
2704 bool Segment::SetChunking(bool chunking, const char* filename) {
2705 if (chunk_count_ > 0)
2712 // Check if we are being set to what is already set.
2713 if (chunking_ && !strcmp(filename, chunking_base_name_))
2716 const size_t name_length = strlen(filename) + 1;
2717 char* const temp = new (std::nothrow) char[name_length]; // NOLINT
2722 strcpy_s(temp, name_length, filename);
2724 strcpy(temp, filename);
2727 delete[] chunking_base_name_;
2728 chunking_base_name_ = temp;
2730 if (!UpdateChunkName("chk", &chunk_name_))
2733 if (!chunk_writer_cluster_) {
2734 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
2735 if (!chunk_writer_cluster_)
2739 if (!chunk_writer_cues_) {
2740 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
2741 if (!chunk_writer_cues_)
2745 if (!chunk_writer_header_) {
2746 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
2747 if (!chunk_writer_header_)
2751 if (!chunk_writer_cluster_->Open(chunk_name_))
2754 const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
2755 char* const header = new (std::nothrow) char[header_length]; // NOLINT
2760 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
2761 strcat_s(header, header_length, ".hdr");
2763 strcpy(header, chunking_base_name_);
2764 strcat(header, ".hdr");
2766 if (!chunk_writer_header_->Open(header)) {
2771 writer_cluster_ = chunk_writer_cluster_;
2772 writer_cues_ = chunk_writer_cues_;
2773 writer_header_ = chunk_writer_header_;
2778 chunking_ = chunking;
2783 bool Segment::CuesTrack(uint64 track_number) {
2784 const Track* const track = GetTrackByNumber(track_number);
2788 cues_track_ = track_number;
2792 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
2794 Track* Segment::GetTrackByNumber(uint64 track_number) const {
2795 return tracks_.GetTrackByNumber(track_number);
2798 bool Segment::WriteSegmentHeader() {
2799 UpdateDocTypeVersion();
2801 // TODO(fgalligan): Support more than one segment.
2802 if (!WriteEbmlHeader(writer_header_, doc_type_version_))
2804 doc_type_version_written_ = doc_type_version_;
2805 ebml_header_size_ = static_cast<int32>(writer_header_->Position());
2807 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
2808 // will write over duration when the file is finalized.
2809 if (WriteID(writer_header_, kMkvSegment))
2813 size_position_ = writer_header_->Position();
2815 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
2816 // bytes because if we are going to overwrite the segment size later we do
2817 // not know how big our segment will be.
2818 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
2821 payload_pos_ = writer_header_->Position();
2823 if (mode_ == kFile && writer_header_->Seekable()) {
2824 // Set the duration > 0.0 so SegmentInfo will write out the duration. When
2825 // the muxer is done writing we will set the correct duration and have
2826 // SegmentInfo upadte it.
2827 segment_info_.set_duration(1.0);
2829 if (!seek_head_.Write(writer_header_))
2833 if (!seek_head_.AddSeekEntry(kMkvInfo, MaxOffset()))
2835 if (!segment_info_.Write(writer_header_))
2838 if (!seek_head_.AddSeekEntry(kMkvTracks, MaxOffset()))
2840 if (!tracks_.Write(writer_header_))
2843 if (chapters_.Count() > 0) {
2844 if (!seek_head_.AddSeekEntry(kMkvChapters, MaxOffset()))
2846 if (!chapters_.Write(writer_header_))
2850 if (tags_.Count() > 0) {
2851 if (!seek_head_.AddSeekEntry(kMkvTags, MaxOffset()))
2853 if (!tags_.Write(writer_header_))
2857 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
2858 if (!chunk_writer_header_)
2861 chunk_writer_header_->Close();
2864 header_written_ = true;
2869 // Here we are testing whether to create a new cluster, given a frame
2870 // having time frame_timestamp_ns.
2872 int Segment::TestFrame(uint64 track_number, uint64 frame_timestamp_ns,
2873 bool is_key) const {
2874 if (force_new_cluster_)
2877 // If no clusters have been created yet, then create a new cluster
2878 // and write this frame immediately, in the new cluster. This path
2879 // should only be followed once, the first time we attempt to write
2882 if (cluster_list_size_ <= 0)
2885 // There exists at least one cluster. We must compare the frame to
2886 // the last cluster, in order to determine whether the frame is
2887 // written to the existing cluster, or that a new cluster should be
2890 const uint64 timecode_scale = segment_info_.timecode_scale();
2891 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
2893 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
2894 const uint64 last_cluster_timecode = last_cluster->timecode();
2896 // For completeness we test for the case when the frame's timecode
2897 // is less than the cluster's timecode. Although in principle that
2898 // is allowed, this muxer doesn't actually write clusters like that,
2899 // so this indicates a bug somewhere in our algorithm.
2901 if (frame_timecode < last_cluster_timecode) // should never happen
2904 // If the frame has a timestamp significantly larger than the last
2905 // cluster (in Matroska, cluster-relative timestamps are serialized
2906 // using a 16-bit signed integer), then we cannot write this frame
2907 // to that cluster, and so we must create a new cluster.
2909 const int64 delta_timecode = frame_timecode - last_cluster_timecode;
2911 if (delta_timecode > kMaxBlockTimecode)
2914 // We decide to create a new cluster when we have a video keyframe.
2915 // This will flush queued (audio) frames, and write the keyframe
2916 // immediately, in the newly-created cluster.
2918 if (is_key && tracks_.TrackIsVideo(track_number))
2921 // Create a new cluster if we have accumulated too many frames
2922 // already, where "too many" is defined as "the total time of frames
2923 // in the cluster exceeds a threshold".
2925 const uint64 delta_ns = delta_timecode * timecode_scale;
2927 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
2930 // This is similar to the case above, with the difference that a new
2931 // cluster is created when the size of the current cluster exceeds a
2934 const uint64 cluster_size = last_cluster->payload_size();
2936 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
2939 // There's no need to create a new cluster, so emit this frame now.
2944 bool Segment::MakeNewCluster(uint64 frame_timestamp_ns) {
2945 const int32 new_size = cluster_list_size_ + 1;
2947 if (new_size > cluster_list_capacity_) {
2948 // Add more clusters.
2949 const int32 new_capacity =
2950 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
2951 Cluster** const clusters =
2952 new (std::nothrow) Cluster*[new_capacity]; // NOLINT
2956 for (int32 i = 0; i < cluster_list_size_; ++i) {
2957 clusters[i] = cluster_list_[i];
2960 delete[] cluster_list_;
2962 cluster_list_ = clusters;
2963 cluster_list_capacity_ = new_capacity;
2966 if (!WriteFramesLessThan(frame_timestamp_ns))
2969 if (mode_ == kFile) {
2970 if (cluster_list_size_ > 0) {
2971 // Update old cluster's size
2972 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2974 if (!old_cluster || !old_cluster->Finalize())
2979 new_cuepoint_ = true;
2982 if (chunking_ && cluster_list_size_ > 0) {
2983 chunk_writer_cluster_->Close();
2986 if (!UpdateChunkName("chk", &chunk_name_))
2988 if (!chunk_writer_cluster_->Open(chunk_name_))
2992 const uint64 timecode_scale = segment_info_.timecode_scale();
2993 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
2995 uint64 cluster_timecode = frame_timecode;
2997 if (frames_size_ > 0) {
2998 const Frame* const f = frames_[0]; // earliest queued frame
2999 const uint64 ns = f->timestamp();
3000 const uint64 tc = ns / timecode_scale;
3002 if (tc < cluster_timecode)
3003 cluster_timecode = tc;
3006 Cluster*& cluster = cluster_list_[cluster_list_size_];
3007 const int64 offset = MaxOffset();
3008 cluster = new (std::nothrow) Cluster(cluster_timecode, // NOLINT
3009 offset, segment_info_.timecode_scale());
3013 if (!cluster->Init(writer_cluster_))
3016 cluster_list_size_ = new_size;
3020 bool Segment::DoNewClusterProcessing(uint64 track_number,
3021 uint64 frame_timestamp_ns, bool is_key) {
3023 // Based on the characteristics of the current frame and current
3024 // cluster, decide whether to create a new cluster.
3025 const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
3026 if (result < 0) // error
3029 // Always set force_new_cluster_ to false after TestFrame.
3030 force_new_cluster_ = false;
3032 // A non-zero result means create a new cluster.
3033 if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
3036 // Write queued (audio) frames.
3037 const int frame_count = WriteFramesAll();
3038 if (frame_count < 0) // error
3041 // Write the current frame to the current cluster (if TestFrame
3042 // returns 0) or to a newly created cluster (TestFrame returns 1).
3046 // TestFrame returned 2, which means there was a large time
3047 // difference between the cluster and the frame itself. Do the
3048 // test again, comparing the frame to the new cluster.
3052 bool Segment::CheckHeaderInfo() {
3053 if (!header_written_) {
3054 if (!WriteSegmentHeader())
3057 if (!seek_head_.AddSeekEntry(kMkvCluster, MaxOffset()))
3060 if (output_cues_ && cues_track_ == 0) {
3061 // Check for a video track
3062 for (uint32 i = 0; i < tracks_.track_entries_size(); ++i) {
3063 const Track* const track = tracks_.GetTrackByIndex(i);
3067 if (tracks_.TrackIsVideo(track->number())) {
3068 cues_track_ = track->number();
3073 // Set first track found
3074 if (cues_track_ == 0) {
3075 const Track* const track = tracks_.GetTrackByIndex(0);
3079 cues_track_ = track->number();
3086 void Segment::UpdateDocTypeVersion() {
3087 for (uint32 index = 0; index < tracks_.track_entries_size(); ++index) {
3088 const Track* track = tracks_.GetTrackByIndex(index);
3091 if ((track->codec_delay() || track->seek_pre_roll()) &&
3092 doc_type_version_ < 4) {
3093 doc_type_version_ = 4;
3099 bool Segment::UpdateChunkName(const char* ext, char** name) const {
3105 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3107 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
3110 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
3111 char* const str = new (std::nothrow) char[length]; // NOLINT
3116 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
3117 strcat_s(str, length, ext_chk);
3119 strcpy(str, chunking_base_name_);
3120 strcat(str, ext_chk);
3129 int64 Segment::MaxOffset() {
3130 if (!writer_header_)
3133 int64 offset = writer_header_->Position() - payload_pos_;
3136 for (int32 i = 0; i < cluster_list_size_; ++i) {
3137 Cluster* const cluster = cluster_list_[i];
3138 offset += cluster->Size();
3142 offset += writer_cues_->Position();
3148 bool Segment::QueueFrame(Frame* frame) {
3149 const int32 new_size = frames_size_ + 1;
3151 if (new_size > frames_capacity_) {
3153 const int32 new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
3155 if (new_capacity < 1)
3158 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
3162 for (int32 i = 0; i < frames_size_; ++i) {
3163 frames[i] = frames_[i];
3168 frames_capacity_ = new_capacity;
3171 frames_[frames_size_++] = frame;
3176 int Segment::WriteFramesAll() {
3177 if (frames_ == NULL)
3180 if (cluster_list_size_ < 1)
3183 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3188 for (int32 i = 0; i < frames_size_; ++i) {
3189 Frame*& frame = frames_[i];
3190 // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
3191 // places where |doc_type_version_| needs to be updated.
3192 if (frame->discard_padding() != 0)
3193 doc_type_version_ = 4;
3194 if (!cluster->AddFrame(frame))
3197 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
3198 if (!AddCuePoint(frame->timestamp(), cues_track_))
3202 if (frame->timestamp() > last_timestamp_) {
3203 last_timestamp_ = frame->timestamp();
3204 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
3211 const int result = frames_size_;
3217 bool Segment::WriteFramesLessThan(uint64 timestamp) {
3218 // Check |cluster_list_size_| to see if this is the first cluster. If it is
3219 // the first cluster the audio frames that are less than the first video
3220 // timesatmp will be written in a later step.
3221 if (frames_size_ > 0 && cluster_list_size_ > 0) {
3225 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3229 int32 shift_left = 0;
3231 // TODO(fgalligan): Change this to use the durations of frames instead of
3232 // the next frame's start time if the duration is accurate.
3233 for (int32 i = 1; i < frames_size_; ++i) {
3234 const Frame* const frame_curr = frames_[i];
3236 if (frame_curr->timestamp() > timestamp)
3239 const Frame* const frame_prev = frames_[i - 1];
3240 if (frame_prev->discard_padding() != 0)
3241 doc_type_version_ = 4;
3242 if (!cluster->AddFrame(frame_prev))
3245 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
3246 if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
3251 if (frame_prev->timestamp() > last_timestamp_) {
3252 last_timestamp_ = frame_prev->timestamp();
3253 last_track_timestamp_[frame_prev->track_number() - 1] =
3254 frame_prev->timestamp();
3260 if (shift_left > 0) {
3261 if (shift_left >= frames_size_)
3264 const int32 new_frames_size = frames_size_ - shift_left;
3265 for (int32 i = 0; i < new_frames_size; ++i) {
3266 frames_[i] = frames_[i + shift_left];
3269 frames_size_ = new_frames_size;
3276 } // namespace mkvmuxer