diff --git a/mkvmuxer.cpp b/mkvmuxer.cpp index fb6c9c3..4cf97d5 100644 --- a/mkvmuxer.cpp +++ b/mkvmuxer.cpp @@ -131,7 +131,9 @@ Frame::Frame() length_(0), track_number_(0), timestamp_(0), - discard_padding_(0) {} + discard_padding_(0), + reference_block_timestamp_(0), + reference_block_timestamp_set_(false) {} Frame::~Frame() { delete[] frame_; @@ -204,6 +206,9 @@ bool Frame::IsValid() const { if (track_number_ == 0 || track_number_ > kMaxTrackNumber) { return false; } + if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) { + return false; + } return true; } @@ -211,6 +216,11 @@ bool Frame::CanBeSimpleBlock() const { return additional_ == NULL && discard_padding_ == 0 && duration_ == 0; } +void Frame::set_reference_block_timestamp(int64 reference_block_timestamp) { + reference_block_timestamp_ = reference_block_timestamp; + reference_block_timestamp_set_ = true; +} + /////////////////////////////////////////////////////////////// // // CuePoint Class @@ -2426,6 +2436,20 @@ bool Segment::AddGenericFrame(const Frame* frame) { if (!cluster) return false; + // If the Frame is not a SimpleBlock, then set the reference_block_timestamp + // if it is not set already. + bool frame_created = false; + if (!frame->CanBeSimpleBlock() && !frame->is_key() && + !frame->reference_block_timestamp_set()) { + Frame* const new_frame = new (std::nothrow) Frame(); + if (!new_frame->CopyFrom(*frame)) + return false; + new_frame->set_reference_block_timestamp( + last_track_timestamp_[frame->track_number() - 1]); + frame = new_frame; + frame_created = true; + } + if (!cluster->AddFrame(frame)) return false; @@ -2435,8 +2459,12 @@ bool Segment::AddGenericFrame(const Frame* frame) { } last_timestamp_ = frame->timestamp(); + last_track_timestamp_[frame->track_number() - 1] = frame->timestamp(); last_block_duration_ = frame->duration(); + if (frame_created) + delete frame; + return true; } @@ -2947,8 +2975,10 @@ int Segment::WriteFramesAll() { return -1; } - if (frame_timestamp > last_timestamp_) + if (frame_timestamp > last_timestamp_) { last_timestamp_ = frame_timestamp; + last_track_timestamp_[frame->track_number() - 1] = frame_timestamp; + } delete frame; frame = NULL; @@ -3010,8 +3040,10 @@ bool Segment::WriteFramesLessThan(uint64 timestamp) { } ++shift_left; - if (frame_timestamp > last_timestamp_) + if (frame_timestamp > last_timestamp_) { last_timestamp_ = frame_timestamp; + last_track_timestamp_[frame_prev->track_number() - 1] = frame_timestamp; + } delete frame_prev; } diff --git a/mkvmuxer.hpp b/mkvmuxer.hpp index cf93436..900b48b 100644 --- a/mkvmuxer.hpp +++ b/mkvmuxer.hpp @@ -23,6 +23,8 @@ namespace mkvmuxer { class MkvWriter; class Segment; +const uint64 kMaxTrackNumber = 126; + /////////////////////////////////////////////////////////////// // Interface used by the mkvmuxer to write out the Mkv data. class IMkvWriter { @@ -108,6 +110,11 @@ class Frame { discard_padding_ = discard_padding; } int64 discard_padding() const { return discard_padding_; } + void set_reference_block_timestamp(int64 reference_block_timestamp); + int64 reference_block_timestamp() const { return reference_block_timestamp_; } + bool reference_block_timestamp_set() const { + return reference_block_timestamp_set_; + } private: // Id of the Additional data. @@ -140,6 +147,12 @@ class Frame { // Discard padding for the frame. int64 discard_padding_; + // Reference block timestamp. + int64 reference_block_timestamp_; + + // Flag indicating if |reference_block_timestamp_| has been set. + bool reference_block_timestamp_set_; + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Frame); }; @@ -1312,6 +1325,9 @@ class Segment { // Last timestamp in nanoseconds added to a cluster. uint64 last_timestamp_; + // Last timestamp in nanoseconds by track number added to a cluster. + uint64 last_track_timestamp_[kMaxTrackNumber]; + // Maximum time in nanoseconds for a cluster duration. This variable is a // guideline and some clusters may have a longer duration. Default is 30 // seconds. diff --git a/mkvmuxerutil.cpp b/mkvmuxerutil.cpp index 5d34db4..6d55ed8 100644 --- a/mkvmuxerutil.cpp +++ b/mkvmuxerutil.cpp @@ -36,7 +36,7 @@ namespace { const int kDateElementSize = 8; uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, - uint64 duration) { + uint64 timecode_scale) { uint64 block_additional_elem_size = 0; uint64 block_addid_elem_size = 0; uint64 block_more_payload_size = 0; @@ -66,6 +66,15 @@ uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, EbmlElementSize(kMkvDiscardPadding, frame->discard_padding()); } + const uint64 reference_block_timestamp = + frame->reference_block_timestamp() / timecode_scale; + uint64 reference_block_elem_size = 0; + if (!frame->is_key()) { + reference_block_elem_size = + EbmlElementSize(kMkvReferenceBlock, reference_block_timestamp); + } + + const uint64 duration = frame->duration() / timecode_scale; uint64 block_duration_elem_size = 0; if (duration > 0) block_duration_elem_size = EbmlElementSize(kMkvBlockDuration, duration); @@ -76,7 +85,7 @@ uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, const uint64 block_group_payload_size = block_elem_size + block_additions_elem_size + block_duration_elem_size + - discard_padding_elem_size; + discard_padding_elem_size + reference_block_elem_size; if (!WriteEbmlMasterElement(writer, kMkvBlockGroup, block_group_payload_size)) { @@ -92,10 +101,8 @@ uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, if (SerializeInt(writer, timecode, 2)) return 0; - uint64 flags = 0; - if (frame->is_key()) - flags |= 0x80; - if (SerializeInt(writer, flags, 1)) + // For a Block, flags is always 0. + if (SerializeInt(writer, 0, 1)) return 0; if (writer->Write(frame->frame(), static_cast(frame->length()))) @@ -119,18 +126,15 @@ uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, } } - if (frame->discard_padding() != 0) { - if (WriteID(writer, kMkvDiscardPadding)) - return 0; + if (frame->discard_padding() != 0 && + !WriteEbmlElement(writer, kMkvDiscardPadding, frame->discard_padding())) { + return false; + } - const uint64 size = GetIntSize(frame->discard_padding()); - if (WriteUInt(writer, size)) - return false; - - if (SerializeInt(writer, frame->discard_padding(), - static_cast(size))) { - return false; - } + if (!frame->is_key() && + !WriteEbmlElement(writer, kMkvReferenceBlock, + reference_block_timestamp)) { + return false; } if (duration > 0 && !WriteEbmlElement(writer, kMkvBlockDuration, duration)) { @@ -440,6 +444,23 @@ bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) { return true; } +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return 0; + + const uint64 size = GetIntSize(value); + if (WriteUInt(writer, size)) + return false; + + if (SerializeInt(writer, value, static_cast(size))) + return false; + + return true; +} + bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) { if (!writer) return false; @@ -524,7 +545,7 @@ uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame, return frame->CanBeSimpleBlock() ? WriteSimpleBlock(writer, frame, relative_timecode) : WriteBlock(writer, frame, relative_timecode, - frame->duration() / cluster->timecode_scale()); + cluster->timecode_scale()); } uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) { diff --git a/mkvmuxerutil.hpp b/mkvmuxerutil.hpp index 865d42a..e318576 100644 --- a/mkvmuxerutil.hpp +++ b/mkvmuxerutil.hpp @@ -18,7 +18,6 @@ class IMkvWriter; const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL; const int64 kMaxBlockTimecode = 0x07FFFLL; -const uint64 kMaxTrackNumber = 126; // Writes out |value| in Big Endian order. Returns 0 on success. int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size); @@ -54,6 +53,7 @@ int32 WriteID(IMkvWriter* writer, uint64 type); // Output an Mkv non-master element. Returns true if the element was written. bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value); +bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value); bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value); bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value); bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,