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) {
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, 2ULL);
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, 2ULL))
92 if (!WriteEbmlElement(writer, kMkvDocTypeReadVersion, 2ULL))
98 bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst,
99 mkvmuxer::int64 start, int64 size) {
100 // TODO(vigneshv): Check if this is a reasonable value.
101 const uint32 kBufSize = 2048;
102 uint8* buf = new uint8[kBufSize];
103 int64 offset = start;
105 const int64 read_len = (size > kBufSize) ? kBufSize : size;
106 if (source->Read(offset, static_cast<long>(read_len), buf))
108 dst->Write(buf, static_cast<uint32>(read_len));
116 ///////////////////////////////////////////////////////////////
123 additional_length_(0),
130 discard_padding_(0) {}
134 delete[] additional_;
137 bool Frame::Init(const uint8* frame, uint64 length) {
139 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
147 memcpy(frame_, frame, static_cast<size_t>(length_));
151 bool Frame::AddAdditionalData(const uint8* additional, uint64 length,
154 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
158 delete[] additional_;
160 additional_length_ = length;
163 memcpy(additional_, additional, static_cast<size_t>(additional_length_));
167 ///////////////////////////////////////////////////////////////
176 output_block_number_(true) {}
178 CuePoint::~CuePoint() {}
180 bool CuePoint::Write(IMkvWriter* writer) const {
181 if (!writer || track_ < 1 || cluster_pos_ < 1)
184 uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_);
185 size += EbmlElementSize(kMkvCueTrack, track_);
186 if (output_block_number_ && block_number_ > 1)
187 size += EbmlElementSize(kMkvCueBlockNumber, block_number_);
188 const uint64 track_pos_size =
189 EbmlMasterElementSize(kMkvCueTrackPositions, size) + size;
190 const uint64 payload_size =
191 EbmlElementSize(kMkvCueTime, time_) + track_pos_size;
193 if (!WriteEbmlMasterElement(writer, kMkvCuePoint, payload_size))
196 const int64 payload_position = writer->Position();
197 if (payload_position < 0)
200 if (!WriteEbmlElement(writer, kMkvCueTime, time_))
203 if (!WriteEbmlMasterElement(writer, kMkvCueTrackPositions, size))
205 if (!WriteEbmlElement(writer, kMkvCueTrack, track_))
207 if (!WriteEbmlElement(writer, kMkvCueClusterPosition, cluster_pos_))
209 if (output_block_number_ && block_number_ > 1)
210 if (!WriteEbmlElement(writer, kMkvCueBlockNumber, block_number_))
213 const int64 stop_position = writer->Position();
214 if (stop_position < 0)
217 if (stop_position - payload_position != static_cast<int64>(payload_size))
223 uint64 CuePoint::PayloadSize() const {
224 uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_);
225 size += EbmlElementSize(kMkvCueTrack, track_);
226 if (output_block_number_ && block_number_ > 1)
227 size += EbmlElementSize(kMkvCueBlockNumber, block_number_);
228 const uint64 track_pos_size =
229 EbmlMasterElementSize(kMkvCueTrackPositions, size) + size;
230 const uint64 payload_size =
231 EbmlElementSize(kMkvCueTime, time_) + track_pos_size;
236 uint64 CuePoint::Size() const {
237 const uint64 payload_size = PayloadSize();
238 return EbmlMasterElementSize(kMkvCuePoint, payload_size) + payload_size;
241 ///////////////////////////////////////////////////////////////
246 : cue_entries_capacity_(0),
247 cue_entries_size_(0),
249 output_block_number_(true) {}
253 for (int32 i = 0; i < cue_entries_size_; ++i) {
254 CuePoint* const cue = cue_entries_[i];
257 delete[] cue_entries_;
261 bool Cues::AddCue(CuePoint* cue) {
265 if ((cue_entries_size_ + 1) > cue_entries_capacity_) {
266 // Add more CuePoints.
267 const int32 new_capacity =
268 (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2;
270 if (new_capacity < 1)
273 CuePoint** const cues =
274 new (std::nothrow) CuePoint* [new_capacity]; // NOLINT
278 for (int32 i = 0; i < cue_entries_size_; ++i) {
279 cues[i] = cue_entries_[i];
282 delete[] cue_entries_;
285 cue_entries_capacity_ = new_capacity;
288 cue->set_output_block_number(output_block_number_);
289 cue_entries_[cue_entries_size_++] = cue;
293 CuePoint* Cues::GetCueByIndex(int32 index) const {
294 if (cue_entries_ == NULL)
297 if (index >= cue_entries_size_)
300 return cue_entries_[index];
303 uint64 Cues::Size() {
305 for (int32 i = 0; i < cue_entries_size_; ++i)
306 size += GetCueByIndex(i)->Size();
307 size += EbmlMasterElementSize(kMkvCues, size);
311 bool Cues::Write(IMkvWriter* writer) const {
316 for (int32 i = 0; i < cue_entries_size_; ++i) {
317 const CuePoint* const cue = GetCueByIndex(i);
325 if (!WriteEbmlMasterElement(writer, kMkvCues, size))
328 const int64 payload_position = writer->Position();
329 if (payload_position < 0)
332 for (int32 i = 0; i < cue_entries_size_; ++i) {
333 const CuePoint* const cue = GetCueByIndex(i);
335 if (!cue->Write(writer))
339 const int64 stop_position = writer->Position();
340 if (stop_position < 0)
343 if (stop_position - payload_position != static_cast<int64>(size))
349 ///////////////////////////////////////////////////////////////
351 // ContentEncAESSettings Class
353 ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
355 uint64 ContentEncAESSettings::Size() const {
356 const uint64 payload = PayloadSize();
358 EbmlMasterElementSize(kMkvContentEncAESSettings, payload) + payload;
362 bool ContentEncAESSettings::Write(IMkvWriter* writer) const {
363 const uint64 payload = PayloadSize();
365 if (!WriteEbmlMasterElement(writer, kMkvContentEncAESSettings, payload))
368 const int64 payload_position = writer->Position();
369 if (payload_position < 0)
372 if (!WriteEbmlElement(writer, kMkvAESSettingsCipherMode, cipher_mode_))
375 const int64 stop_position = writer->Position();
376 if (stop_position < 0 ||
377 stop_position - payload_position != static_cast<int64>(payload))
383 uint64 ContentEncAESSettings::PayloadSize() const {
384 uint64 size = EbmlElementSize(kMkvAESSettingsCipherMode, cipher_mode_);
388 ///////////////////////////////////////////////////////////////
390 // ContentEncoding Class
392 ContentEncoding::ContentEncoding()
398 enc_key_id_length_(0) {}
400 ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
402 bool ContentEncoding::SetEncryptionID(const uint8* id, uint64 length) {
403 if (!id || length < 1)
406 delete[] enc_key_id_;
409 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
413 memcpy(enc_key_id_, id, static_cast<size_t>(length));
414 enc_key_id_length_ = length;
419 uint64 ContentEncoding::Size() const {
420 const uint64 encryption_size = EncryptionSize();
421 const uint64 encoding_size = EncodingSize(0, encryption_size);
422 const uint64 encodings_size =
423 EbmlMasterElementSize(kMkvContentEncoding, encoding_size) + encoding_size;
425 return encodings_size;
428 bool ContentEncoding::Write(IMkvWriter* writer) const {
429 const uint64 encryption_size = EncryptionSize();
430 const uint64 encoding_size = EncodingSize(0, encryption_size);
432 EbmlMasterElementSize(kMkvContentEncoding, encoding_size) + encoding_size;
434 const int64 payload_position = writer->Position();
435 if (payload_position < 0)
438 if (!WriteEbmlMasterElement(writer, kMkvContentEncoding, encoding_size))
440 if (!WriteEbmlElement(writer, kMkvContentEncodingOrder, encoding_order_))
442 if (!WriteEbmlElement(writer, kMkvContentEncodingScope, encoding_scope_))
444 if (!WriteEbmlElement(writer, kMkvContentEncodingType, encoding_type_))
447 if (!WriteEbmlMasterElement(writer, kMkvContentEncryption, encryption_size))
449 if (!WriteEbmlElement(writer, kMkvContentEncAlgo, enc_algo_))
451 if (!WriteEbmlElement(writer, kMkvContentEncKeyID, enc_key_id_,
455 if (!enc_aes_settings_.Write(writer))
458 const int64 stop_position = writer->Position();
459 if (stop_position < 0 ||
460 stop_position - payload_position != static_cast<int64>(size))
466 uint64 ContentEncoding::EncodingSize(uint64 compresion_size,
467 uint64 encryption_size) const {
468 // TODO(fgalligan): Add support for compression settings.
469 if (compresion_size != 0)
472 uint64 encoding_size = 0;
474 if (encryption_size > 0) {
476 EbmlMasterElementSize(kMkvContentEncryption, encryption_size) +
479 encoding_size += EbmlElementSize(kMkvContentEncodingType, encoding_type_);
480 encoding_size += EbmlElementSize(kMkvContentEncodingScope, encoding_scope_);
481 encoding_size += EbmlElementSize(kMkvContentEncodingOrder, encoding_order_);
483 return encoding_size;
486 uint64 ContentEncoding::EncryptionSize() const {
487 const uint64 aes_size = enc_aes_settings_.Size();
489 uint64 encryption_size =
490 EbmlElementSize(kMkvContentEncKeyID, enc_key_id_, enc_key_id_length_);
491 encryption_size += EbmlElementSize(kMkvContentEncAlgo, enc_algo_);
493 return encryption_size + aes_size;
496 ///////////////////////////////////////////////////////////////
500 Track::Track(unsigned int* seed)
502 codec_private_(NULL),
504 max_block_additional_id_(0),
511 default_duration_(0),
512 codec_private_length_(0),
513 content_encoding_entries_(NULL),
514 content_encoding_entries_size_(0) {}
518 delete[] codec_private_;
522 if (content_encoding_entries_) {
523 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
524 ContentEncoding* const encoding = content_encoding_entries_[i];
527 delete[] content_encoding_entries_;
531 bool Track::AddContentEncoding() {
532 const uint32 count = content_encoding_entries_size_ + 1;
534 ContentEncoding** const content_encoding_entries =
535 new (std::nothrow) ContentEncoding* [count]; // NOLINT
536 if (!content_encoding_entries)
539 ContentEncoding* const content_encoding =
540 new (std::nothrow) ContentEncoding(); // NOLINT
541 if (!content_encoding) {
542 delete[] content_encoding_entries;
546 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
547 content_encoding_entries[i] = content_encoding_entries_[i];
550 delete[] content_encoding_entries_;
552 content_encoding_entries_ = content_encoding_entries;
553 content_encoding_entries_[content_encoding_entries_size_] = content_encoding;
554 content_encoding_entries_size_ = count;
558 ContentEncoding* Track::GetContentEncodingByIndex(uint32 index) const {
559 if (content_encoding_entries_ == NULL)
562 if (index >= content_encoding_entries_size_)
565 return content_encoding_entries_[index];
568 uint64 Track::PayloadSize() const {
569 uint64 size = EbmlElementSize(kMkvTrackNumber, number_);
570 size += EbmlElementSize(kMkvTrackUID, uid_);
571 size += EbmlElementSize(kMkvTrackType, type_);
573 size += EbmlElementSize(kMkvCodecID, codec_id_);
575 size += EbmlElementSize(kMkvCodecPrivate, codec_private_,
576 codec_private_length_);
578 size += EbmlElementSize(kMkvLanguage, language_);
580 size += EbmlElementSize(kMkvName, name_);
581 if (max_block_additional_id_)
582 size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_);
584 size += EbmlElementSize(kMkvCodecDelay, codec_delay_);
586 size += EbmlElementSize(kMkvSeekPreRoll, seek_pre_roll_);
587 if (default_duration_)
588 size += EbmlElementSize(kMkvDefaultDuration, default_duration_);
590 if (content_encoding_entries_size_ > 0) {
591 uint64 content_encodings_size = 0;
592 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
593 ContentEncoding* const encoding = content_encoding_entries_[i];
594 content_encodings_size += encoding->Size();
598 EbmlMasterElementSize(kMkvContentEncodings, content_encodings_size) +
599 content_encodings_size;
605 uint64 Track::Size() const {
606 uint64 size = PayloadSize();
607 size += EbmlMasterElementSize(kMkvTrackEntry, size);
611 bool Track::Write(IMkvWriter* writer) const {
615 // |size| may be bigger than what is written out in this function because
616 // derived classes may write out more data in the Track element.
617 const uint64 payload_size = PayloadSize();
619 if (!WriteEbmlMasterElement(writer, kMkvTrackEntry, payload_size))
622 // |type_| has to be specified before the Track can be written.
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 const int64 payload_position = writer->Position();
648 if (payload_position < 0)
651 if (!WriteEbmlElement(writer, kMkvTrackNumber, number_))
653 if (!WriteEbmlElement(writer, kMkvTrackUID, uid_))
655 if (!WriteEbmlElement(writer, kMkvTrackType, type_))
657 if (max_block_additional_id_) {
658 if (!WriteEbmlElement(writer, kMkvMaxBlockAdditionID,
659 max_block_additional_id_)) {
664 if (!WriteEbmlElement(writer, kMkvCodecDelay, codec_delay_))
667 if (seek_pre_roll_) {
668 if (!WriteEbmlElement(writer, kMkvSeekPreRoll, seek_pre_roll_))
671 if (default_duration_) {
672 if (!WriteEbmlElement(writer, kMkvDefaultDuration, default_duration_))
676 if (!WriteEbmlElement(writer, kMkvCodecID, codec_id_))
679 if (codec_private_) {
680 if (!WriteEbmlElement(writer, kMkvCodecPrivate, codec_private_,
681 codec_private_length_))
685 if (!WriteEbmlElement(writer, kMkvLanguage, language_))
689 if (!WriteEbmlElement(writer, kMkvName, name_))
693 int64 stop_position = writer->Position();
694 if (stop_position < 0 ||
695 stop_position - payload_position != static_cast<int64>(size))
698 if (content_encoding_entries_size_ > 0) {
699 uint64 content_encodings_size = 0;
700 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
701 ContentEncoding* const encoding = content_encoding_entries_[i];
702 content_encodings_size += encoding->Size();
705 if (!WriteEbmlMasterElement(writer, kMkvContentEncodings,
706 content_encodings_size))
709 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
710 ContentEncoding* const encoding = content_encoding_entries_[i];
711 if (!encoding->Write(writer))
716 stop_position = writer->Position();
717 if (stop_position < 0)
722 bool Track::SetCodecPrivate(const uint8* codec_private, uint64 length) {
723 if (!codec_private || length < 1)
726 delete[] codec_private_;
729 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
733 memcpy(codec_private_, codec_private, static_cast<size_t>(length));
734 codec_private_length_ = length;
739 void Track::set_codec_id(const char* codec_id) {
743 const size_t length = strlen(codec_id) + 1;
744 codec_id_ = new (std::nothrow) char[length]; // NOLINT
747 strcpy_s(codec_id_, length, codec_id);
749 strcpy(codec_id_, codec_id);
755 // TODO(fgalligan): Vet the language parameter.
756 void Track::set_language(const char* language) {
760 const size_t length = strlen(language) + 1;
761 language_ = new (std::nothrow) char[length]; // NOLINT
764 strcpy_s(language_, length, language);
766 strcpy(language_, language);
772 void Track::set_name(const char* name) {
776 const size_t length = strlen(name) + 1;
777 name_ = new (std::nothrow) char[length]; // NOLINT
780 strcpy_s(name_, length, name);
788 ///////////////////////////////////////////////////////////////
792 VideoTrack::VideoTrack(unsigned int* seed)
802 VideoTrack::~VideoTrack() {}
804 bool VideoTrack::SetStereoMode(uint64 stereo_mode) {
805 if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst &&
806 stereo_mode != kTopBottomRightIsFirst &&
807 stereo_mode != kTopBottomLeftIsFirst &&
808 stereo_mode != kSideBySideRightIsFirst)
811 stereo_mode_ = stereo_mode;
815 bool VideoTrack::SetAlphaMode(uint64 alpha_mode) {
816 if (alpha_mode != kNoAlpha && alpha_mode != kAlpha)
819 alpha_mode_ = alpha_mode;
823 uint64 VideoTrack::PayloadSize() const {
824 const uint64 parent_size = Track::PayloadSize();
826 uint64 size = VideoPayloadSize();
827 size += EbmlMasterElementSize(kMkvVideo, size);
829 return parent_size + size;
832 bool VideoTrack::Write(IMkvWriter* writer) const {
833 if (!Track::Write(writer))
836 const uint64 size = VideoPayloadSize();
838 if (!WriteEbmlMasterElement(writer, kMkvVideo, size))
841 const int64 payload_position = writer->Position();
842 if (payload_position < 0)
845 if (!WriteEbmlElement(writer, kMkvPixelWidth, width_))
847 if (!WriteEbmlElement(writer, kMkvPixelHeight, height_))
849 if (display_width_ > 0)
850 if (!WriteEbmlElement(writer, kMkvDisplayWidth, display_width_))
852 if (display_height_ > 0)
853 if (!WriteEbmlElement(writer, kMkvDisplayHeight, display_height_))
855 if (stereo_mode_ > kMono)
856 if (!WriteEbmlElement(writer, kMkvStereoMode, stereo_mode_))
858 if (alpha_mode_ > kNoAlpha)
859 if (!WriteEbmlElement(writer, kMkvAlphaMode, alpha_mode_))
861 if (frame_rate_ > 0.0)
862 if (!WriteEbmlElement(writer, kMkvFrameRate,
863 static_cast<float>(frame_rate_)))
866 const int64 stop_position = writer->Position();
867 if (stop_position < 0 ||
868 stop_position - payload_position != static_cast<int64>(size))
874 uint64 VideoTrack::VideoPayloadSize() const {
875 uint64 size = EbmlElementSize(kMkvPixelWidth, width_);
876 size += EbmlElementSize(kMkvPixelHeight, height_);
877 if (display_width_ > 0)
878 size += EbmlElementSize(kMkvDisplayWidth, display_width_);
879 if (display_height_ > 0)
880 size += EbmlElementSize(kMkvDisplayHeight, display_height_);
881 if (stereo_mode_ > kMono)
882 size += EbmlElementSize(kMkvStereoMode, stereo_mode_);
883 if (alpha_mode_ > kNoAlpha)
884 size += EbmlElementSize(kMkvAlphaMode, alpha_mode_);
885 if (frame_rate_ > 0.0)
886 size += EbmlElementSize(kMkvFrameRate, static_cast<float>(frame_rate_));
891 ///////////////////////////////////////////////////////////////
895 AudioTrack::AudioTrack(unsigned int* seed)
896 : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
898 AudioTrack::~AudioTrack() {}
900 uint64 AudioTrack::PayloadSize() const {
901 const uint64 parent_size = Track::PayloadSize();
904 EbmlElementSize(kMkvSamplingFrequency, static_cast<float>(sample_rate_));
905 size += EbmlElementSize(kMkvChannels, channels_);
907 size += EbmlElementSize(kMkvBitDepth, bit_depth_);
908 size += EbmlMasterElementSize(kMkvAudio, size);
910 return parent_size + size;
913 bool AudioTrack::Write(IMkvWriter* writer) const {
914 if (!Track::Write(writer))
917 // Calculate AudioSettings size.
919 EbmlElementSize(kMkvSamplingFrequency, static_cast<float>(sample_rate_));
920 size += EbmlElementSize(kMkvChannels, channels_);
922 size += EbmlElementSize(kMkvBitDepth, bit_depth_);
924 if (!WriteEbmlMasterElement(writer, kMkvAudio, size))
927 const int64 payload_position = writer->Position();
928 if (payload_position < 0)
931 if (!WriteEbmlElement(writer, kMkvSamplingFrequency,
932 static_cast<float>(sample_rate_)))
934 if (!WriteEbmlElement(writer, kMkvChannels, channels_))
937 if (!WriteEbmlElement(writer, kMkvBitDepth, bit_depth_))
940 const int64 stop_position = writer->Position();
941 if (stop_position < 0 ||
942 stop_position - payload_position != static_cast<int64>(size))
948 ///////////////////////////////////////////////////////////////
952 const char Tracks::kOpusCodecId[] = "A_OPUS";
953 const char Tracks::kVorbisCodecId[] = "A_VORBIS";
954 const char Tracks::kVp8CodecId[] = "V_VP8";
955 const char Tracks::kVp9CodecId[] = "V_VP9";
957 Tracks::Tracks() : track_entries_(NULL), track_entries_size_(0) {}
960 if (track_entries_) {
961 for (uint32 i = 0; i < track_entries_size_; ++i) {
962 Track* const track = track_entries_[i];
965 delete[] track_entries_;
969 bool Tracks::AddTrack(Track* track, int32 number) {
973 // This muxer only supports track numbers in the range [1, 126], in
974 // order to be able (to use Matroska integer representation) to
975 // serialize the block header (of which the track number is a part)
976 // for a frame using exactly 4 bytes.
981 uint32 track_num = number;
984 // Check to make sure a track does not already have |track_num|.
985 for (uint32 i = 0; i < track_entries_size_; ++i) {
986 if (track_entries_[i]->number() == track_num)
991 const uint32 count = track_entries_size_ + 1;
993 Track** const track_entries = new (std::nothrow) Track* [count]; // NOLINT
997 for (uint32 i = 0; i < track_entries_size_; ++i) {
998 track_entries[i] = track_entries_[i];
1001 delete[] track_entries_;
1003 // Find the lowest availible track number > 0.
1004 if (track_num == 0) {
1007 // Check to make sure a track does not already have |track_num|.
1011 for (uint32 i = 0; i < track_entries_size_; ++i) {
1012 if (track_entries[i]->number() == track_num) {
1020 track->set_number(track_num);
1022 track_entries_ = track_entries;
1023 track_entries_[track_entries_size_] = track;
1024 track_entries_size_ = count;
1028 const Track* Tracks::GetTrackByIndex(uint32 index) const {
1029 if (track_entries_ == NULL)
1032 if (index >= track_entries_size_)
1035 return track_entries_[index];
1038 Track* Tracks::GetTrackByNumber(uint64 track_number) const {
1039 const int32 count = track_entries_size();
1040 for (int32 i = 0; i < count; ++i) {
1041 if (track_entries_[i]->number() == track_number)
1042 return track_entries_[i];
1048 bool Tracks::TrackIsAudio(uint64 track_number) const {
1049 const Track* const track = GetTrackByNumber(track_number);
1051 if (track->type() == kAudio)
1057 bool Tracks::TrackIsVideo(uint64 track_number) const {
1058 const Track* const track = GetTrackByNumber(track_number);
1060 if (track->type() == kVideo)
1066 bool Tracks::Write(IMkvWriter* writer) const {
1068 const int32 count = track_entries_size();
1069 for (int32 i = 0; i < count; ++i) {
1070 const Track* const track = GetTrackByIndex(i);
1075 size += track->Size();
1078 if (!WriteEbmlMasterElement(writer, kMkvTracks, size))
1081 const int64 payload_position = writer->Position();
1082 if (payload_position < 0)
1085 for (int32 i = 0; i < count; ++i) {
1086 const Track* const track = GetTrackByIndex(i);
1087 if (!track->Write(writer))
1091 const int64 stop_position = writer->Position();
1092 if (stop_position < 0 ||
1093 stop_position - payload_position != static_cast<int64>(size))
1099 ///////////////////////////////////////////////////////////////
1103 bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
1105 void Chapter::set_time(const Segment& segment, uint64 start_ns, uint64 end_ns) {
1106 const SegmentInfo* const info = segment.GetSegmentInfo();
1107 const uint64 timecode_scale = info->timecode_scale();
1108 start_timecode_ = start_ns / timecode_scale;
1109 end_timecode_ = end_ns / timecode_scale;
1112 bool Chapter::add_string(const char* title, const char* language,
1113 const char* country) {
1114 if (!ExpandDisplaysArray())
1117 Display& d = displays_[displays_count_++];
1120 if (!d.set_title(title))
1123 if (!d.set_language(language))
1126 if (!d.set_country(country))
1132 Chapter::Chapter() {
1133 // This ctor only constructs the object. Proper initialization is
1134 // done in Init() (called in Chapters::AddChapter()). The only
1135 // reason we bother implementing this ctor is because we had to
1136 // declare it as private (along with the dtor), in order to prevent
1137 // clients from creating Chapter instances (a privelege we grant
1138 // only to the Chapters class). Doing no initialization here also
1139 // means that creating arrays of chapter objects is more efficient,
1140 // because we only initialize each new chapter object as it becomes
1141 // active on the array.
1144 Chapter::~Chapter() {}
1146 void Chapter::Init(unsigned int* seed) {
1150 displays_count_ = 0;
1151 uid_ = MakeUID(seed);
1154 void Chapter::ShallowCopy(Chapter* dst) const {
1156 dst->start_timecode_ = start_timecode_;
1157 dst->end_timecode_ = end_timecode_;
1159 dst->displays_ = displays_;
1160 dst->displays_size_ = displays_size_;
1161 dst->displays_count_ = displays_count_;
1164 void Chapter::Clear() {
1167 while (displays_count_ > 0) {
1168 Display& d = displays_[--displays_count_];
1178 bool Chapter::ExpandDisplaysArray() {
1179 if (displays_size_ > displays_count_)
1180 return true; // nothing to do yet
1182 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
1184 Display* const displays = new (std::nothrow) Display[size]; // NOLINT
1185 if (displays == NULL)
1188 for (int idx = 0; idx < displays_count_; ++idx) {
1189 displays[idx] = displays_[idx]; // shallow copy
1194 displays_ = displays;
1195 displays_size_ = size;
1200 uint64 Chapter::WriteAtom(IMkvWriter* writer) const {
1201 uint64 payload_size = EbmlElementSize(kMkvChapterStringUID, id_) +
1202 EbmlElementSize(kMkvChapterUID, uid_) +
1203 EbmlElementSize(kMkvChapterTimeStart, start_timecode_) +
1204 EbmlElementSize(kMkvChapterTimeEnd, end_timecode_);
1206 for (int idx = 0; idx < displays_count_; ++idx) {
1207 const Display& d = displays_[idx];
1208 payload_size += d.WriteDisplay(NULL);
1211 const uint64 atom_size =
1212 EbmlMasterElementSize(kMkvChapterAtom, payload_size) + payload_size;
1217 const int64 start = writer->Position();
1219 if (!WriteEbmlMasterElement(writer, kMkvChapterAtom, payload_size))
1222 if (!WriteEbmlElement(writer, kMkvChapterStringUID, id_))
1225 if (!WriteEbmlElement(writer, kMkvChapterUID, uid_))
1228 if (!WriteEbmlElement(writer, kMkvChapterTimeStart, start_timecode_))
1231 if (!WriteEbmlElement(writer, kMkvChapterTimeEnd, end_timecode_))
1234 for (int idx = 0; idx < displays_count_; ++idx) {
1235 const Display& d = displays_[idx];
1237 if (!d.WriteDisplay(writer))
1241 const int64 stop = writer->Position();
1243 if (stop >= start && uint64(stop - start) != atom_size)
1249 void Chapter::Display::Init() {
1255 void Chapter::Display::Clear() {
1256 StrCpy(NULL, &title_);
1257 StrCpy(NULL, &language_);
1258 StrCpy(NULL, &country_);
1261 bool Chapter::Display::set_title(const char* title) {
1262 return StrCpy(title, &title_);
1265 bool Chapter::Display::set_language(const char* language) {
1266 return StrCpy(language, &language_);
1269 bool Chapter::Display::set_country(const char* country) {
1270 return StrCpy(country, &country_);
1273 uint64 Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
1274 uint64 payload_size = EbmlElementSize(kMkvChapString, title_);
1277 payload_size += EbmlElementSize(kMkvChapLanguage, language_);
1280 payload_size += EbmlElementSize(kMkvChapCountry, country_);
1282 const uint64 display_size =
1283 EbmlMasterElementSize(kMkvChapterDisplay, payload_size) + payload_size;
1286 return display_size;
1288 const int64 start = writer->Position();
1290 if (!WriteEbmlMasterElement(writer, kMkvChapterDisplay, payload_size))
1293 if (!WriteEbmlElement(writer, kMkvChapString, title_))
1297 if (!WriteEbmlElement(writer, kMkvChapLanguage, language_))
1302 if (!WriteEbmlElement(writer, kMkvChapCountry, country_))
1306 const int64 stop = writer->Position();
1308 if (stop >= start && uint64(stop - start) != display_size)
1311 return display_size;
1314 ///////////////////////////////////////////////////////////////
1318 Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
1320 Chapters::~Chapters() {
1321 while (chapters_count_ > 0) {
1322 Chapter& chapter = chapters_[--chapters_count_];
1330 int Chapters::Count() const { return chapters_count_; }
1332 Chapter* Chapters::AddChapter(unsigned int* seed) {
1333 if (!ExpandChaptersArray())
1336 Chapter& chapter = chapters_[chapters_count_++];
1342 bool Chapters::Write(IMkvWriter* writer) const {
1346 const uint64 payload_size = WriteEdition(NULL); // return size only
1348 if (!WriteEbmlMasterElement(writer, kMkvChapters, payload_size))
1351 const int64 start = writer->Position();
1353 if (WriteEdition(writer) == 0) // error
1356 const int64 stop = writer->Position();
1358 if (stop >= start && uint64(stop - start) != payload_size)
1364 bool Chapters::ExpandChaptersArray() {
1365 if (chapters_size_ > chapters_count_)
1366 return true; // nothing to do yet
1368 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
1370 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
1371 if (chapters == NULL)
1374 for (int idx = 0; idx < chapters_count_; ++idx) {
1375 const Chapter& src = chapters_[idx];
1376 Chapter* const dst = chapters + idx;
1377 src.ShallowCopy(dst);
1382 chapters_ = chapters;
1383 chapters_size_ = size;
1388 uint64 Chapters::WriteEdition(IMkvWriter* writer) const {
1389 uint64 payload_size = 0;
1391 for (int idx = 0; idx < chapters_count_; ++idx) {
1392 const Chapter& chapter = chapters_[idx];
1393 payload_size += chapter.WriteAtom(NULL);
1396 const uint64 edition_size =
1397 EbmlMasterElementSize(kMkvEditionEntry, payload_size) + payload_size;
1399 if (writer == NULL) // return size only
1400 return edition_size;
1402 const int64 start = writer->Position();
1404 if (!WriteEbmlMasterElement(writer, kMkvEditionEntry, payload_size))
1407 for (int idx = 0; idx < chapters_count_; ++idx) {
1408 const Chapter& chapter = chapters_[idx];
1410 const uint64 chapter_size = chapter.WriteAtom(writer);
1411 if (chapter_size == 0) // error
1415 const int64 stop = writer->Position();
1417 if (stop >= start && uint64(stop - start) != edition_size)
1420 return edition_size;
1423 ///////////////////////////////////////////////////////////////
1427 Cluster::Cluster(uint64 timecode, int64 cues_pos)
1430 header_written_(false),
1432 position_for_cues_(cues_pos),
1434 timecode_(timecode),
1437 Cluster::~Cluster() {}
1439 bool Cluster::Init(IMkvWriter* ptr_writer) {
1443 writer_ = ptr_writer;
1447 bool Cluster::AddFrame(const uint8* frame, uint64 length, uint64 track_number,
1448 uint64 abs_timecode, bool is_key) {
1449 return DoWriteBlock(frame, length, track_number, abs_timecode, is_key ? 1 : 0,
1453 bool Cluster::AddFrameWithAdditional(const uint8* frame, uint64 length,
1454 const uint8* additional,
1455 uint64 additional_length, uint64 add_id,
1456 uint64 track_number, uint64 abs_timecode,
1458 return DoWriteBlockWithAdditional(
1459 frame, length, additional, additional_length, add_id, track_number,
1460 abs_timecode, is_key ? 1 : 0, &WriteBlockWithAdditional);
1463 bool Cluster::AddFrameWithDiscardPadding(const uint8* frame, uint64 length,
1464 int64 discard_padding,
1465 uint64 track_number,
1466 uint64 abs_timecode, bool is_key) {
1467 return DoWriteBlockWithDiscardPadding(
1468 frame, length, discard_padding, track_number, abs_timecode,
1469 is_key ? 1 : 0, &WriteBlockWithDiscardPadding);
1472 bool Cluster::AddMetadata(const uint8* frame, uint64 length,
1473 uint64 track_number, uint64 abs_timecode,
1474 uint64 duration_timecode) {
1475 return DoWriteBlock(frame, length, track_number, abs_timecode,
1476 duration_timecode, &WriteMetadataBlock);
1479 void Cluster::AddPayloadSize(uint64 size) { payload_size_ += size; }
1481 bool Cluster::Finalize() {
1482 if (!writer_ || finalized_ || size_position_ == -1)
1485 if (writer_->Seekable()) {
1486 const int64 pos = writer_->Position();
1488 if (writer_->Position(size_position_))
1491 if (WriteUIntSize(writer_, payload_size(), 8))
1494 if (writer_->Position(pos))
1503 uint64 Cluster::Size() const {
1504 const uint64 element_size =
1505 EbmlMasterElementSize(kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) + payload_size_;
1506 return element_size;
1509 template <typename Type>
1510 bool Cluster::PreWriteBlock(Type* write_function) {
1511 if (write_function == NULL)
1517 if (!header_written_) {
1518 if (!WriteClusterHeader())
1525 void Cluster::PostWriteBlock(uint64 element_size) {
1526 AddPayloadSize(element_size);
1530 bool Cluster::IsValidTrackNumber(uint64 track_number) const {
1531 return (track_number > 0 && track_number <= 0x7E);
1534 int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const {
1535 const int64 cluster_timecode = this->Cluster::timecode();
1536 const int64 rel_timecode =
1537 static_cast<int64>(abs_timecode) - cluster_timecode;
1539 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
1542 return rel_timecode;
1545 bool Cluster::DoWriteBlock(const uint8* frame, uint64 length,
1546 uint64 track_number, uint64 abs_timecode,
1547 uint64 generic_arg, WriteBlock write_block) {
1548 if (frame == NULL || length == 0)
1551 if (!IsValidTrackNumber(track_number))
1554 const int64 rel_timecode = GetRelativeTimecode(abs_timecode);
1555 if (rel_timecode < 0)
1558 if (!PreWriteBlock(write_block))
1561 const uint64 element_size = (*write_block)(
1562 writer_, frame, length, track_number, rel_timecode, generic_arg);
1563 if (element_size == 0)
1566 PostWriteBlock(element_size);
1570 bool Cluster::DoWriteBlockWithAdditional(
1571 const uint8* frame, uint64 length, const uint8* additional,
1572 uint64 additional_length, uint64 add_id, uint64 track_number,
1573 uint64 abs_timecode, uint64 generic_arg, WriteBlockAdditional write_block) {
1574 if (frame == NULL || length == 0 || additional == NULL ||
1575 additional_length == 0)
1578 if (!IsValidTrackNumber(track_number))
1581 const int64 rel_timecode = GetRelativeTimecode(abs_timecode);
1582 if (rel_timecode < 0)
1585 if (!PreWriteBlock(write_block))
1588 const uint64 element_size =
1589 (*write_block)(writer_, frame, length, additional, additional_length,
1590 add_id, track_number, rel_timecode, generic_arg);
1591 if (element_size == 0)
1594 PostWriteBlock(element_size);
1598 bool Cluster::DoWriteBlockWithDiscardPadding(
1599 const uint8* frame, uint64 length, int64 discard_padding,
1600 uint64 track_number, uint64 abs_timecode, uint64 generic_arg,
1601 WriteBlockDiscardPadding write_block) {
1602 if (frame == NULL || length == 0 || discard_padding <= 0)
1605 if (!IsValidTrackNumber(track_number))
1608 const int64 rel_timecode = GetRelativeTimecode(abs_timecode);
1609 if (rel_timecode < 0)
1612 if (!PreWriteBlock(write_block))
1615 const uint64 element_size =
1616 (*write_block)(writer_, frame, length, discard_padding, track_number,
1617 rel_timecode, generic_arg);
1618 if (element_size == 0)
1621 PostWriteBlock(element_size);
1625 bool Cluster::WriteClusterHeader() {
1629 if (WriteID(writer_, kMkvCluster))
1633 size_position_ = writer_->Position();
1635 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
1636 // bytes because we do not know how big our cluster will be.
1637 if (SerializeInt(writer_, kEbmlUnknownValue, 8))
1640 if (!WriteEbmlElement(writer_, kMkvTimecode, timecode()))
1642 AddPayloadSize(EbmlElementSize(kMkvTimecode, timecode()));
1643 header_written_ = true;
1648 ///////////////////////////////////////////////////////////////
1652 SeekHead::SeekHead() : start_pos_(0ULL) {
1653 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1654 seek_entry_id_[i] = 0;
1655 seek_entry_pos_[i] = 0;
1659 SeekHead::~SeekHead() {}
1661 bool SeekHead::Finalize(IMkvWriter* writer) const {
1662 if (writer->Seekable()) {
1663 if (start_pos_ == -1)
1666 uint64 payload_size = 0;
1667 uint64 entry_size[kSeekEntryCount];
1669 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1670 if (seek_entry_id_[i] != 0) {
1672 EbmlElementSize(kMkvSeekID, static_cast<uint64>(seek_entry_id_[i]));
1673 entry_size[i] += EbmlElementSize(kMkvSeekPosition, seek_entry_pos_[i]);
1676 EbmlMasterElementSize(kMkvSeek, entry_size[i]) + entry_size[i];
1680 // No SeekHead elements
1681 if (payload_size == 0)
1684 const int64 pos = writer->Position();
1685 if (writer->Position(start_pos_))
1688 if (!WriteEbmlMasterElement(writer, kMkvSeekHead, payload_size))
1691 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1692 if (seek_entry_id_[i] != 0) {
1693 if (!WriteEbmlMasterElement(writer, kMkvSeek, entry_size[i]))
1696 if (!WriteEbmlElement(writer, kMkvSeekID,
1697 static_cast<uint64>(seek_entry_id_[i])))
1700 if (!WriteEbmlElement(writer, kMkvSeekPosition, seek_entry_pos_[i]))
1705 const uint64 total_entry_size = kSeekEntryCount * MaxEntrySize();
1706 const uint64 total_size =
1707 EbmlMasterElementSize(kMkvSeekHead, total_entry_size) +
1709 const int64 size_left = total_size - (writer->Position() - start_pos_);
1711 const uint64 bytes_written = WriteVoidElement(writer, size_left);
1715 if (writer->Position(pos))
1722 bool SeekHead::Write(IMkvWriter* writer) {
1723 const uint64 entry_size = kSeekEntryCount * MaxEntrySize();
1724 const uint64 size = EbmlMasterElementSize(kMkvSeekHead, entry_size);
1726 start_pos_ = writer->Position();
1728 const uint64 bytes_written = WriteVoidElement(writer, size + entry_size);
1735 bool SeekHead::AddSeekEntry(uint32 id, uint64 pos) {
1736 for (int32 i = 0; i < kSeekEntryCount; ++i) {
1737 if (seek_entry_id_[i] == 0) {
1738 seek_entry_id_[i] = id;
1739 seek_entry_pos_[i] = pos;
1746 uint32 SeekHead::GetId(int index) const {
1747 if (index < 0 || index >= kSeekEntryCount)
1749 return seek_entry_id_[index];
1752 uint64 SeekHead::GetPosition(int index) const {
1753 if (index < 0 || index >= kSeekEntryCount)
1755 return seek_entry_pos_[index];
1758 bool SeekHead::SetSeekEntry(int index, uint32 id, uint64 position) {
1759 if (index < 0 || index >= kSeekEntryCount)
1761 seek_entry_id_[index] = id;
1762 seek_entry_pos_[index] = position;
1766 uint64 SeekHead::MaxEntrySize() const {
1767 const uint64 max_entry_payload_size =
1768 EbmlElementSize(kMkvSeekID, 0xffffffffULL) +
1769 EbmlElementSize(kMkvSeekPosition, 0xffffffffffffffffULL);
1770 const uint64 max_entry_size =
1771 EbmlMasterElementSize(kMkvSeek, max_entry_payload_size) +
1772 max_entry_payload_size;
1774 return max_entry_size;
1777 ///////////////////////////////////////////////////////////////
1779 // SegmentInfo Class
1781 SegmentInfo::SegmentInfo()
1784 timecode_scale_(1000000ULL),
1786 date_utc_(LLONG_MIN),
1787 duration_pos_(-1) {}
1789 SegmentInfo::~SegmentInfo() {
1790 delete[] muxing_app_;
1791 delete[] writing_app_;
1794 bool SegmentInfo::Init() {
1799 GetVersion(&major, &minor, &build, &revision);
1802 sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
1803 minor, build, revision);
1805 snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
1806 minor, build, revision);
1809 const size_t app_len = strlen(temp) + 1;
1811 delete[] muxing_app_;
1813 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
1818 strcpy_s(muxing_app_, app_len, temp);
1820 strcpy(muxing_app_, temp);
1823 set_writing_app(temp);
1829 bool SegmentInfo::Finalize(IMkvWriter* writer) const {
1833 if (duration_ > 0.0) {
1834 if (writer->Seekable()) {
1835 if (duration_pos_ == -1)
1838 const int64 pos = writer->Position();
1840 if (writer->Position(duration_pos_))
1843 if (!WriteEbmlElement(writer, kMkvDuration,
1844 static_cast<float>(duration_)))
1847 if (writer->Position(pos))
1855 bool SegmentInfo::Write(IMkvWriter* writer) {
1856 if (!writer || !muxing_app_ || !writing_app_)
1859 uint64 size = EbmlElementSize(kMkvTimecodeScale, timecode_scale_);
1860 if (duration_ > 0.0)
1861 size += EbmlElementSize(kMkvDuration, static_cast<float>(duration_));
1862 if (date_utc_ != LLONG_MIN)
1863 size += EbmlDateElementSize(kMkvDateUTC, date_utc_);
1864 size += EbmlElementSize(kMkvMuxingApp, muxing_app_);
1865 size += EbmlElementSize(kMkvWritingApp, writing_app_);
1867 if (!WriteEbmlMasterElement(writer, kMkvInfo, size))
1870 const int64 payload_position = writer->Position();
1871 if (payload_position < 0)
1874 if (!WriteEbmlElement(writer, kMkvTimecodeScale, timecode_scale_))
1877 if (duration_ > 0.0) {
1879 duration_pos_ = writer->Position();
1881 if (!WriteEbmlElement(writer, kMkvDuration, static_cast<float>(duration_)))
1885 if (date_utc_ != LLONG_MIN)
1886 WriteEbmlDateElement(writer, kMkvDateUTC, date_utc_);
1888 if (!WriteEbmlElement(writer, kMkvMuxingApp, muxing_app_))
1890 if (!WriteEbmlElement(writer, kMkvWritingApp, writing_app_))
1893 const int64 stop_position = writer->Position();
1894 if (stop_position < 0 ||
1895 stop_position - payload_position != static_cast<int64>(size))
1901 void SegmentInfo::set_muxing_app(const char* app) {
1903 const size_t length = strlen(app) + 1;
1904 char* temp_str = new (std::nothrow) char[length]; // NOLINT
1909 strcpy_s(temp_str, length, app);
1911 strcpy(temp_str, app);
1914 delete[] muxing_app_;
1915 muxing_app_ = temp_str;
1919 void SegmentInfo::set_writing_app(const char* app) {
1921 const size_t length = strlen(app) + 1;
1922 char* temp_str = new (std::nothrow) char[length]; // NOLINT
1927 strcpy_s(temp_str, length, app);
1929 strcpy(temp_str, app);
1932 delete[] writing_app_;
1933 writing_app_ = temp_str;
1937 ///////////////////////////////////////////////////////////////
1944 chunk_writer_cluster_(NULL),
1945 chunk_writer_cues_(NULL),
1946 chunk_writer_header_(NULL),
1948 chunking_base_name_(NULL),
1949 cluster_list_(NULL),
1950 cluster_list_capacity_(0),
1951 cluster_list_size_(0),
1952 cues_position_(kAfterClusters),
1954 force_new_cluster_(false),
1956 frames_capacity_(0),
1959 header_written_(false),
1960 last_block_duration_(0),
1962 max_cluster_duration_(kDefaultMaxClusterDuration),
1963 max_cluster_size_(0),
1965 new_cuepoint_(false),
1969 writer_cluster_(NULL),
1971 writer_header_(NULL) {
1972 const time_t curr_time = time(NULL);
1973 seed_ = static_cast<unsigned int>(curr_time);
1979 Segment::~Segment() {
1980 if (cluster_list_) {
1981 for (int32 i = 0; i < cluster_list_size_; ++i) {
1982 Cluster* const cluster = cluster_list_[i];
1985 delete[] cluster_list_;
1989 for (int32 i = 0; i < frames_size_; ++i) {
1990 Frame* const frame = frames_[i];
1996 delete[] chunk_name_;
1997 delete[] chunking_base_name_;
1999 if (chunk_writer_cluster_) {
2000 chunk_writer_cluster_->Close();
2001 delete chunk_writer_cluster_;
2003 if (chunk_writer_cues_) {
2004 chunk_writer_cues_->Close();
2005 delete chunk_writer_cues_;
2007 if (chunk_writer_header_) {
2008 chunk_writer_header_->Close();
2009 delete chunk_writer_header_;
2013 void Segment::MoveCuesBeforeClustersHelper(uint64 diff, int32 index,
2014 uint64* cues_size) {
2015 const uint64 old_cues_size = *cues_size;
2016 CuePoint* const cue_point = cues_.GetCueByIndex(index);
2017 if (cue_point == NULL)
2019 const uint64 old_cue_point_size = cue_point->Size();
2020 const uint64 cluster_pos = cue_point->cluster_pos() + diff;
2021 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
2022 // New size of the cue is computed as follows
2023 // Let a = current size of Cues Element
2024 // Let b = Difference in Cue Point's size after this pass
2025 // Let c = Difference in length of Cues Element's size
2026 // (This is computed as CodedSize(a + b) - CodedSize(a)
2027 // Let d = a + b + c. Now d is the new size of the Cues element which is
2028 // passed on to the next recursive call.
2029 const uint64 cue_point_size_diff = cue_point->Size() - old_cue_point_size;
2030 const uint64 cue_size_diff =
2031 GetCodedUIntSize(*cues_size + cue_point_size_diff) -
2032 GetCodedUIntSize(*cues_size);
2033 *cues_size += cue_point_size_diff + cue_size_diff;
2034 diff = *cues_size - old_cues_size;
2036 for (int32 i = 0; i < cues_.cue_entries_size(); ++i) {
2037 MoveCuesBeforeClustersHelper(diff, i, cues_size);
2042 void Segment::MoveCuesBeforeClusters() {
2043 const uint64 current_cue_size = cues_.Size();
2044 uint64 cue_size = current_cue_size;
2045 for (int32 i = 0; i < cues_.cue_entries_size(); i++)
2046 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
2048 // Adjust the Seek Entry to reflect the change in position
2049 // of Cluster and Cues
2050 int32 cluster_index = 0;
2051 int32 cues_index = 0;
2052 for (int32 i = 0; i < SeekHead::kSeekEntryCount; ++i) {
2053 if (seek_head_.GetId(i) == kMkvCluster)
2055 if (seek_head_.GetId(i) == kMkvCues)
2058 seek_head_.SetSeekEntry(cues_index, kMkvCues,
2059 seek_head_.GetPosition(cluster_index));
2060 seek_head_.SetSeekEntry(cluster_index, kMkvCluster,
2061 cues_.Size() + seek_head_.GetPosition(cues_index));
2064 bool Segment::Init(IMkvWriter* ptr_writer) {
2068 writer_cluster_ = ptr_writer;
2069 writer_cues_ = ptr_writer;
2070 writer_header_ = ptr_writer;
2071 return segment_info_.Init();
2074 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
2075 IMkvWriter* writer) {
2076 if (!writer->Seekable() || chunking_)
2078 const int64 cluster_offset =
2079 cluster_list_[0]->size_position() - GetUIntSize(kMkvCluster);
2081 // Copy the headers.
2082 if (!ChunkedCopy(reader, writer, 0, cluster_offset))
2085 // Recompute cue positions and seek entries.
2086 MoveCuesBeforeClusters();
2088 // Write cues and seek entries.
2089 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
2090 // second time with a different writer object. But the name Finalize() doesn't
2091 // indicate something we want to call more than once. So consider renaming it
2092 // to write() or some such.
2093 if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
2096 // Copy the Clusters.
2097 if (!ChunkedCopy(reader, writer, cluster_offset,
2098 cluster_end_offset_ - cluster_offset))
2101 // Update the Segment size in case the Cues size has changed.
2102 const int64 pos = writer->Position();
2103 const int64 segment_size = writer->Position() - payload_pos_;
2104 if (writer->Position(size_position_) ||
2105 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
2110 bool Segment::Finalize() {
2111 if (WriteFramesAll() < 0)
2114 if (mode_ == kFile) {
2115 if (cluster_list_size_ > 0) {
2116 // Update last cluster's size
2117 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2119 if (!old_cluster || !old_cluster->Finalize())
2123 if (chunking_ && chunk_writer_cluster_) {
2124 chunk_writer_cluster_->Close();
2128 const double duration =
2129 (static_cast<double>(last_timestamp_) + last_block_duration_) /
2130 segment_info_.timecode_scale();
2131 segment_info_.set_duration(duration);
2132 if (!segment_info_.Finalize(writer_header_))
2136 if (!seek_head_.AddSeekEntry(kMkvCues, MaxOffset()))
2140 if (!chunk_writer_cues_)
2144 if (!UpdateChunkName("cues", &name))
2147 const bool cues_open = chunk_writer_cues_->Open(name);
2153 cluster_end_offset_ = writer_cluster_->Position();
2155 // Write the seek headers and cues
2157 if (!cues_.Write(writer_cues_))
2160 if (!seek_head_.Finalize(writer_header_))
2163 if (writer_header_->Seekable()) {
2164 if (size_position_ == -1)
2167 const int64 pos = writer_header_->Position();
2168 const int64 segment_size = MaxOffset();
2170 if (segment_size < 1)
2173 if (writer_header_->Position(size_position_))
2176 if (WriteUIntSize(writer_header_, segment_size, 8))
2179 if (writer_header_->Position(pos))
2184 // Do not close any writers until the segment size has been written,
2185 // otherwise the size may be off.
2186 if (!chunk_writer_cues_ || !chunk_writer_header_)
2189 chunk_writer_cues_->Close();
2190 chunk_writer_header_->Close();
2197 Track* Segment::AddTrack(int32 number) {
2198 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
2203 if (!tracks_.AddTrack(track, number)) {
2211 Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
2213 uint64 Segment::AddVideoTrack(int32 width, int32 height, int32 number) {
2214 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
2218 track->set_type(Tracks::kVideo);
2219 track->set_codec_id(Tracks::kVp8CodecId);
2220 track->set_width(width);
2221 track->set_height(height);
2223 tracks_.AddTrack(track, number);
2226 return track->number();
2229 bool Segment::AddCuePoint(uint64 timestamp, uint64 track) {
2230 if (cluster_list_size_ < 1)
2233 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2237 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
2241 cue->set_time(timestamp / segment_info_.timecode_scale());
2242 cue->set_block_number(cluster->blocks_added());
2243 cue->set_cluster_pos(cluster->position_for_cues());
2244 cue->set_track(track);
2245 if (!cues_.AddCue(cue))
2248 new_cuepoint_ = false;
2252 uint64 Segment::AddAudioTrack(int32 sample_rate, int32 channels, int32 number) {
2253 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
2257 track->set_type(Tracks::kAudio);
2258 track->set_codec_id(Tracks::kVorbisCodecId);
2259 track->set_sample_rate(sample_rate);
2260 track->set_channels(channels);
2262 tracks_.AddTrack(track, number);
2264 return track->number();
2267 bool Segment::AddFrame(const uint8* frame, uint64 length, uint64 track_number,
2268 uint64 timestamp, bool is_key) {
2272 if (!CheckHeaderInfo())
2275 // Check for non-monotonically increasing timestamps.
2276 if (timestamp < last_timestamp_)
2279 // If the segment has a video track hold onto audio frames to make sure the
2280 // audio that is associated with the start time of a video key-frame is
2281 // muxed into the same cluster.
2282 if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) {
2283 Frame* const new_frame = new (std::nothrow) Frame();
2284 if (new_frame == NULL || !new_frame->Init(frame, length))
2286 new_frame->set_track_number(track_number);
2287 new_frame->set_timestamp(timestamp);
2288 new_frame->set_is_key(is_key);
2290 if (!QueueFrame(new_frame))
2296 if (!DoNewClusterProcessing(track_number, timestamp, is_key))
2299 if (cluster_list_size_ < 1)
2302 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2306 const uint64 timecode_scale = segment_info_.timecode_scale();
2307 const uint64 abs_timecode = timestamp / timecode_scale;
2309 if (!cluster->AddFrame(frame, length, track_number, abs_timecode, is_key))
2312 if (new_cuepoint_ && cues_track_ == track_number) {
2313 if (!AddCuePoint(timestamp, cues_track_))
2317 if (timestamp > last_timestamp_)
2318 last_timestamp_ = timestamp;
2323 bool Segment::AddFrameWithAdditional(const uint8* frame, uint64 length,
2324 const uint8* additional,
2325 uint64 additional_length, uint64 add_id,
2326 uint64 track_number, uint64 timestamp,
2328 if (frame == NULL || additional == NULL)
2331 if (!CheckHeaderInfo())
2334 // Check for non-monotonically increasing timestamps.
2335 if (timestamp < last_timestamp_)
2338 // If the segment has a video track hold onto audio frames to make sure the
2339 // audio that is associated with the start time of a video key-frame is
2340 // muxed into the same cluster.
2341 if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) {
2342 Frame* const new_frame = new (std::nothrow) Frame();
2343 if (new_frame == NULL || !new_frame->Init(frame, length))
2345 new_frame->set_track_number(track_number);
2346 new_frame->set_timestamp(timestamp);
2347 new_frame->set_is_key(is_key);
2349 if (!QueueFrame(new_frame))
2355 if (!DoNewClusterProcessing(track_number, timestamp, is_key))
2358 if (cluster_list_size_ < 1)
2361 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2362 if (cluster == NULL)
2365 const uint64 timecode_scale = segment_info_.timecode_scale();
2366 const uint64 abs_timecode = timestamp / timecode_scale;
2368 if (!cluster->AddFrameWithAdditional(frame, length, additional,
2369 additional_length, add_id, track_number,
2370 abs_timecode, is_key))
2373 if (new_cuepoint_ && cues_track_ == track_number) {
2374 if (!AddCuePoint(timestamp, cues_track_))
2378 if (timestamp > last_timestamp_)
2379 last_timestamp_ = timestamp;
2384 bool Segment::AddFrameWithDiscardPadding(const uint8* frame, uint64 length,
2385 int64 discard_padding,
2386 uint64 track_number, uint64 timestamp,
2388 if (frame == NULL || discard_padding <= 0)
2391 if (!CheckHeaderInfo())
2394 // Check for non-monotonically increasing timestamps.
2395 if (timestamp < last_timestamp_)
2398 // If the segment has a video track hold onto audio frames to make sure the
2399 // audio that is associated with the start time of a video key-frame is
2400 // muxed into the same cluster.
2401 if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) {
2402 Frame* const new_frame = new (std::nothrow) Frame();
2403 if (new_frame == NULL || !new_frame->Init(frame, length))
2405 new_frame->set_track_number(track_number);
2406 new_frame->set_timestamp(timestamp);
2407 new_frame->set_is_key(is_key);
2408 new_frame->set_discard_padding(discard_padding);
2410 if (!QueueFrame(new_frame))
2416 if (!DoNewClusterProcessing(track_number, timestamp, is_key))
2419 if (cluster_list_size_ < 1)
2422 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2426 const uint64 timecode_scale = segment_info_.timecode_scale();
2427 const uint64 abs_timecode = timestamp / timecode_scale;
2429 if (!cluster->AddFrameWithDiscardPadding(
2430 frame, length, discard_padding, track_number, abs_timecode, is_key)) {
2434 if (new_cuepoint_ && cues_track_ == track_number) {
2435 if (!AddCuePoint(timestamp, cues_track_))
2439 if (timestamp > last_timestamp_)
2440 last_timestamp_ = timestamp;
2445 bool Segment::AddMetadata(const uint8* frame, uint64 length,
2446 uint64 track_number, uint64 timestamp_ns,
2447 uint64 duration_ns) {
2451 if (!CheckHeaderInfo())
2454 // Check for non-monotonically increasing timestamps.
2455 if (timestamp_ns < last_timestamp_)
2458 if (!DoNewClusterProcessing(track_number, timestamp_ns, true))
2461 if (cluster_list_size_ < 1)
2464 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2469 const uint64 timecode_scale = segment_info_.timecode_scale();
2470 const uint64 abs_timecode = timestamp_ns / timecode_scale;
2471 const uint64 duration_timecode = duration_ns / timecode_scale;
2473 if (!cluster->AddMetadata(frame, length, track_number, abs_timecode,
2477 if (timestamp_ns > last_timestamp_)
2478 last_timestamp_ = timestamp_ns;
2483 bool Segment::AddGenericFrame(const Frame* frame) {
2484 last_block_duration_ = frame->duration();
2485 if (!tracks_.TrackIsAudio(frame->track_number()) &&
2486 !tracks_.TrackIsVideo(frame->track_number()) && frame->duration() > 0) {
2487 return AddMetadata(frame->frame(), frame->length(), frame->track_number(),
2488 frame->timestamp(), frame->duration());
2489 } else if (frame->additional() && frame->additional_length() > 0) {
2490 return AddFrameWithAdditional(
2491 frame->frame(), frame->length(), frame->additional(),
2492 frame->additional_length(), frame->add_id(), frame->track_number(),
2493 frame->timestamp(), frame->is_key());
2494 } else if (frame->discard_padding() > 0) {
2495 return AddFrameWithDiscardPadding(
2496 frame->frame(), frame->length(), frame->discard_padding(),
2497 frame->track_number(), frame->timestamp(), frame->is_key());
2499 return AddFrame(frame->frame(), frame->length(), frame->track_number(),
2500 frame->timestamp(), frame->is_key());
2504 void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
2506 bool Segment::SetChunking(bool chunking, const char* filename) {
2507 if (chunk_count_ > 0)
2514 // Check if we are being set to what is already set.
2515 if (chunking_ && !strcmp(filename, chunking_base_name_))
2518 const size_t name_length = strlen(filename) + 1;
2519 char* const temp = new (std::nothrow) char[name_length]; // NOLINT
2524 strcpy_s(temp, name_length, filename);
2526 strcpy(temp, filename);
2529 delete[] chunking_base_name_;
2530 chunking_base_name_ = temp;
2532 if (!UpdateChunkName("chk", &chunk_name_))
2535 if (!chunk_writer_cluster_) {
2536 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
2537 if (!chunk_writer_cluster_)
2541 if (!chunk_writer_cues_) {
2542 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
2543 if (!chunk_writer_cues_)
2547 if (!chunk_writer_header_) {
2548 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
2549 if (!chunk_writer_header_)
2553 if (!chunk_writer_cluster_->Open(chunk_name_))
2556 const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
2557 char* const header = new (std::nothrow) char[header_length]; // NOLINT
2562 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
2563 strcat_s(header, header_length, ".hdr");
2565 strcpy(header, chunking_base_name_);
2566 strcat(header, ".hdr");
2568 if (!chunk_writer_header_->Open(header)) {
2573 writer_cluster_ = chunk_writer_cluster_;
2574 writer_cues_ = chunk_writer_cues_;
2575 writer_header_ = chunk_writer_header_;
2580 chunking_ = chunking;
2585 bool Segment::CuesTrack(uint64 track_number) {
2586 const Track* const track = GetTrackByNumber(track_number);
2590 cues_track_ = track_number;
2594 void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
2596 Track* Segment::GetTrackByNumber(uint64 track_number) const {
2597 return tracks_.GetTrackByNumber(track_number);
2600 bool Segment::WriteSegmentHeader() {
2601 // TODO(fgalligan): Support more than one segment.
2602 if (!WriteEbmlHeader(writer_header_))
2605 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
2606 // will write over duration when the file is finalized.
2607 if (WriteID(writer_header_, kMkvSegment))
2611 size_position_ = writer_header_->Position();
2613 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
2614 // bytes because if we are going to overwrite the segment size later we do
2615 // not know how big our segment will be.
2616 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
2619 payload_pos_ = writer_header_->Position();
2621 if (mode_ == kFile && writer_header_->Seekable()) {
2622 // Set the duration > 0.0 so SegmentInfo will write out the duration. When
2623 // the muxer is done writing we will set the correct duration and have
2624 // SegmentInfo upadte it.
2625 segment_info_.set_duration(1.0);
2627 if (!seek_head_.Write(writer_header_))
2631 if (!seek_head_.AddSeekEntry(kMkvInfo, MaxOffset()))
2633 if (!segment_info_.Write(writer_header_))
2636 if (!seek_head_.AddSeekEntry(kMkvTracks, MaxOffset()))
2638 if (!tracks_.Write(writer_header_))
2641 if (chapters_.Count() > 0) {
2642 if (!seek_head_.AddSeekEntry(kMkvChapters, MaxOffset()))
2644 if (!chapters_.Write(writer_header_))
2648 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
2649 if (!chunk_writer_header_)
2652 chunk_writer_header_->Close();
2655 header_written_ = true;
2660 // Here we are testing whether to create a new cluster, given a frame
2661 // having time frame_timestamp_ns.
2663 int Segment::TestFrame(uint64 track_number, uint64 frame_timestamp_ns,
2664 bool is_key) const {
2665 if (force_new_cluster_)
2668 // If no clusters have been created yet, then create a new cluster
2669 // and write this frame immediately, in the new cluster. This path
2670 // should only be followed once, the first time we attempt to write
2673 if (cluster_list_size_ <= 0)
2676 // There exists at least one cluster. We must compare the frame to
2677 // the last cluster, in order to determine whether the frame is
2678 // written to the existing cluster, or that a new cluster should be
2681 const uint64 timecode_scale = segment_info_.timecode_scale();
2682 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
2684 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
2685 const uint64 last_cluster_timecode = last_cluster->timecode();
2687 // For completeness we test for the case when the frame's timecode
2688 // is less than the cluster's timecode. Although in principle that
2689 // is allowed, this muxer doesn't actually write clusters like that,
2690 // so this indicates a bug somewhere in our algorithm.
2692 if (frame_timecode < last_cluster_timecode) // should never happen
2695 // If the frame has a timestamp significantly larger than the last
2696 // cluster (in Matroska, cluster-relative timestamps are serialized
2697 // using a 16-bit signed integer), then we cannot write this frame
2698 // to that cluster, and so we must create a new cluster.
2700 const int64 delta_timecode = frame_timecode - last_cluster_timecode;
2702 if (delta_timecode > kMaxBlockTimecode)
2705 // We decide to create a new cluster when we have a video keyframe.
2706 // This will flush queued (audio) frames, and write the keyframe
2707 // immediately, in the newly-created cluster.
2709 if (is_key && tracks_.TrackIsVideo(track_number))
2712 // Create a new cluster if we have accumulated too many frames
2713 // already, where "too many" is defined as "the total time of frames
2714 // in the cluster exceeds a threshold".
2716 const uint64 delta_ns = delta_timecode * timecode_scale;
2718 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
2721 // This is similar to the case above, with the difference that a new
2722 // cluster is created when the size of the current cluster exceeds a
2725 const uint64 cluster_size = last_cluster->payload_size();
2727 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
2730 // There's no need to create a new cluster, so emit this frame now.
2735 bool Segment::MakeNewCluster(uint64 frame_timestamp_ns) {
2736 const int32 new_size = cluster_list_size_ + 1;
2738 if (new_size > cluster_list_capacity_) {
2739 // Add more clusters.
2740 const int32 new_capacity =
2741 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
2742 Cluster** const clusters =
2743 new (std::nothrow) Cluster* [new_capacity]; // NOLINT
2747 for (int32 i = 0; i < cluster_list_size_; ++i) {
2748 clusters[i] = cluster_list_[i];
2751 delete[] cluster_list_;
2753 cluster_list_ = clusters;
2754 cluster_list_capacity_ = new_capacity;
2757 if (!WriteFramesLessThan(frame_timestamp_ns))
2760 if (mode_ == kFile) {
2761 if (cluster_list_size_ > 0) {
2762 // Update old cluster's size
2763 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
2765 if (!old_cluster || !old_cluster->Finalize())
2770 new_cuepoint_ = true;
2773 if (chunking_ && cluster_list_size_ > 0) {
2774 chunk_writer_cluster_->Close();
2777 if (!UpdateChunkName("chk", &chunk_name_))
2779 if (!chunk_writer_cluster_->Open(chunk_name_))
2783 const uint64 timecode_scale = segment_info_.timecode_scale();
2784 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
2786 uint64 cluster_timecode = frame_timecode;
2788 if (frames_size_ > 0) {
2789 const Frame* const f = frames_[0]; // earliest queued frame
2790 const uint64 ns = f->timestamp();
2791 const uint64 tc = ns / timecode_scale;
2793 if (tc < cluster_timecode)
2794 cluster_timecode = tc;
2797 Cluster*& cluster = cluster_list_[cluster_list_size_];
2798 const int64 offset = MaxOffset();
2799 cluster = new (std::nothrow) Cluster(cluster_timecode, offset); // NOLINT
2803 if (!cluster->Init(writer_cluster_))
2806 cluster_list_size_ = new_size;
2810 bool Segment::DoNewClusterProcessing(uint64 track_number,
2811 uint64 frame_timestamp_ns, bool is_key) {
2813 // Based on the characteristics of the current frame and current
2814 // cluster, decide whether to create a new cluster.
2815 const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
2816 if (result < 0) // error
2819 // Always set force_new_cluster_ to false after TestFrame.
2820 force_new_cluster_ = false;
2822 // A non-zero result means create a new cluster.
2823 if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
2826 // Write queued (audio) frames.
2827 const int frame_count = WriteFramesAll();
2828 if (frame_count < 0) // error
2831 // Write the current frame to the current cluster (if TestFrame
2832 // returns 0) or to a newly created cluster (TestFrame returns 1).
2836 // TestFrame returned 2, which means there was a large time
2837 // difference between the cluster and the frame itself. Do the
2838 // test again, comparing the frame to the new cluster.
2842 bool Segment::CheckHeaderInfo() {
2843 if (!header_written_) {
2844 if (!WriteSegmentHeader())
2847 if (!seek_head_.AddSeekEntry(kMkvCluster, MaxOffset()))
2850 if (output_cues_ && cues_track_ == 0) {
2851 // Check for a video track
2852 for (uint32 i = 0; i < tracks_.track_entries_size(); ++i) {
2853 const Track* const track = tracks_.GetTrackByIndex(i);
2857 if (tracks_.TrackIsVideo(track->number())) {
2858 cues_track_ = track->number();
2863 // Set first track found
2864 if (cues_track_ == 0) {
2865 const Track* const track = tracks_.GetTrackByIndex(0);
2869 cues_track_ = track->number();
2876 bool Segment::UpdateChunkName(const char* ext, char** name) const {
2882 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
2884 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
2887 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
2888 char* const str = new (std::nothrow) char[length]; // NOLINT
2893 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
2894 strcat_s(str, length, ext_chk);
2896 strcpy(str, chunking_base_name_);
2897 strcat(str, ext_chk);
2906 int64 Segment::MaxOffset() {
2907 if (!writer_header_)
2910 int64 offset = writer_header_->Position() - payload_pos_;
2913 for (int32 i = 0; i < cluster_list_size_; ++i) {
2914 Cluster* const cluster = cluster_list_[i];
2915 offset += cluster->Size();
2919 offset += writer_cues_->Position();
2925 bool Segment::QueueFrame(Frame* frame) {
2926 const int32 new_size = frames_size_ + 1;
2928 if (new_size > frames_capacity_) {
2930 const int32 new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
2932 if (new_capacity < 1)
2935 Frame** const frames = new (std::nothrow) Frame* [new_capacity]; // NOLINT
2939 for (int32 i = 0; i < frames_size_; ++i) {
2940 frames[i] = frames_[i];
2945 frames_capacity_ = new_capacity;
2948 frames_[frames_size_++] = frame;
2953 int Segment::WriteFramesAll() {
2954 if (frames_ == NULL)
2957 if (cluster_list_size_ < 1)
2960 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
2965 const uint64 timecode_scale = segment_info_.timecode_scale();
2967 for (int32 i = 0; i < frames_size_; ++i) {
2968 Frame*& frame = frames_[i];
2969 const uint64 frame_timestamp = frame->timestamp(); // ns
2970 const uint64 frame_timecode = frame_timestamp / timecode_scale;
2972 if (frame->discard_padding() > 0) {
2973 if (!cluster->AddFrameWithDiscardPadding(
2974 frame->frame(), frame->length(), frame->discard_padding(),
2975 frame->track_number(), frame_timecode, frame->is_key())) {
2979 if (!cluster->AddFrame(frame->frame(), frame->length(),
2980 frame->track_number(), frame_timecode,
2986 if (new_cuepoint_ && cues_track_ == frame->track_number()) {
2987 if (!AddCuePoint(frame_timestamp, cues_track_))
2991 if (frame_timestamp > last_timestamp_)
2992 last_timestamp_ = frame_timestamp;
2998 const int result = frames_size_;
3004 bool Segment::WriteFramesLessThan(uint64 timestamp) {
3005 // Check |cluster_list_size_| to see if this is the first cluster. If it is
3006 // the first cluster the audio frames that are less than the first video
3007 // timesatmp will be written in a later step.
3008 if (frames_size_ > 0 && cluster_list_size_ > 0) {
3012 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
3016 const uint64 timecode_scale = segment_info_.timecode_scale();
3017 int32 shift_left = 0;
3019 // TODO(fgalligan): Change this to use the durations of frames instead of
3020 // the next frame's start time if the duration is accurate.
3021 for (int32 i = 1; i < frames_size_; ++i) {
3022 const Frame* const frame_curr = frames_[i];
3024 if (frame_curr->timestamp() > timestamp)
3027 const Frame* const frame_prev = frames_[i - 1];
3028 const uint64 frame_timestamp = frame_prev->timestamp();
3029 const uint64 frame_timecode = frame_timestamp / timecode_scale;
3030 const int64 discard_padding = frame_prev->discard_padding();
3032 if (discard_padding > 0) {
3033 if (!cluster->AddFrameWithDiscardPadding(
3034 frame_prev->frame(), frame_prev->length(), discard_padding,
3035 frame_prev->track_number(), frame_timecode,
3036 frame_prev->is_key())) {
3040 if (!cluster->AddFrame(frame_prev->frame(), frame_prev->length(),
3041 frame_prev->track_number(), frame_timecode,
3042 frame_prev->is_key())) {
3047 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
3048 if (!AddCuePoint(frame_timestamp, cues_track_))
3053 if (frame_timestamp > last_timestamp_)
3054 last_timestamp_ = frame_timestamp;
3059 if (shift_left > 0) {
3060 if (shift_left >= frames_size_)
3063 const int32 new_frames_size = frames_size_ - shift_left;
3064 for (int32 i = 0; i < new_frames_size; ++i) {
3065 frames_[i] = frames_[i + shift_left];
3068 frames_size_ = new_frames_size;
3075 } // namespace mkvmuxer