Merge "mkvmuxer: Add DiscardPadding support."

This commit is contained in:
Tom Finegan 2014-01-23 15:34:12 -08:00 committed by Gerrit Code Review
commit 84f2156ca1
6 changed files with 406 additions and 79 deletions

View File

@ -124,7 +124,8 @@ Frame::Frame()
is_key_(false),
length_(0),
track_number_(0),
timestamp_(0) {
timestamp_(0),
discard_padding_(0) {
}
Frame::~Frame() {
@ -1513,6 +1514,21 @@ bool Cluster::AddFrameWithAdditional(const uint8* frame,
&WriteBlockWithAdditional);
}
bool Cluster::AddFrameWithDiscardPadding(const uint8* frame,
uint64 length,
int64 discard_padding,
uint64 track_number,
uint64 abs_timecode,
bool is_key) {
return DoWriteBlockWithDiscardPadding(frame,
length,
discard_padding,
track_number,
abs_timecode,
is_key ? 1 : 0,
&WriteBlockWithDiscardPadding);
}
bool Cluster::AddMetadata(const uint8* frame,
uint64 length,
uint64 track_number,
@ -1559,6 +1575,42 @@ uint64 Cluster::Size() const {
return element_size;
}
template <typename Type>
bool Cluster::PreWriteBlock(Type* write_function) {
if (write_function == NULL)
return false;
if (finalized_)
return false;
if (!header_written_) {
if (!WriteClusterHeader())
return false;
}
return true;
}
void Cluster::PostWriteBlock(uint64 element_size) {
AddPayloadSize(element_size);
++blocks_added_;
}
bool Cluster::IsValidTrackNumber(uint64 track_number) const {
return (track_number > 0 && track_number <= 0x7E);
}
int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const {
const int64 cluster_timecode = this->Cluster::timecode();
const int64 rel_timecode =
static_cast<int64>(abs_timecode) - cluster_timecode;
if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
return -1;
return rel_timecode;
}
bool Cluster::DoWriteBlock(
const uint8* frame,
uint64 length,
@ -1569,46 +1621,26 @@ bool Cluster::DoWriteBlock(
if (frame == NULL || length == 0)
return false;
// To simplify things, we require that there be fewer than 127
// tracks -- this allows us to serialize the track number value for
// a stream using a single byte, per the Matroska encoding.
if (track_number == 0 || track_number > 0x7E)
if (!IsValidTrackNumber(track_number))
return false;
const int64 cluster_timecode = this->Cluster::timecode();
const int64 rel_timecode =
static_cast<int64>(abs_timecode) - cluster_timecode;
const int64 rel_timecode = GetRelativeTimecode(abs_timecode);
if (rel_timecode < 0)
return false;
if (rel_timecode > kMaxBlockTimecode)
if (!PreWriteBlock(write_block))
return false;
if (write_block == NULL)
return false;
if (finalized_)
return false;
if (!header_written_)
if (!WriteClusterHeader())
return false;
const uint64 element_size = (*write_block)(writer_,
frame,
length,
track_number,
rel_timecode,
generic_arg);
if (element_size == 0)
return false;
AddPayloadSize(element_size);
blocks_added_++;
PostWriteBlock(element_size);
return true;
}
@ -1626,33 +1658,16 @@ bool Cluster::DoWriteBlockWithAdditional(
additional == NULL || additional_length == 0)
return false;
// To simplify things, we require that there be fewer than 127
// tracks -- this allows us to serialize the track number value for
// a stream using a single byte, per the Matroska encoding.
if (track_number == 0 || track_number > 0x7E)
if (!IsValidTrackNumber(track_number))
return false;
const int64 cluster_timecode = this->Cluster::timecode();
const int64 rel_timecode =
static_cast<int64>(abs_timecode) - cluster_timecode;
const int64 rel_timecode = GetRelativeTimecode(abs_timecode);
if (rel_timecode < 0)
return false;
if (rel_timecode > kMaxBlockTimecode)
if (!PreWriteBlock(write_block))
return false;
if (write_block == NULL)
return false;
if (finalized_)
return false;
if (!header_written_)
if (!WriteClusterHeader())
return false;
const uint64 element_size = (*write_block)(writer_,
frame,
length,
@ -1662,13 +1677,45 @@ bool Cluster::DoWriteBlockWithAdditional(
track_number,
rel_timecode,
generic_arg);
if (element_size == 0)
return false;
AddPayloadSize(element_size);
blocks_added_++;
PostWriteBlock(element_size);
return true;
}
bool Cluster::DoWriteBlockWithDiscardPadding(
const uint8* frame,
uint64 length,
int64 discard_padding,
uint64 track_number,
uint64 abs_timecode,
uint64 generic_arg,
WriteBlockDiscardPadding write_block) {
if (frame == NULL || length == 0 || discard_padding <= 0)
return false;
if (!IsValidTrackNumber(track_number))
return false;
const int64 rel_timecode = GetRelativeTimecode(abs_timecode);
if (rel_timecode < 0)
return false;
if (!PreWriteBlock(write_block))
return false;
const uint64 element_size = (*write_block)(writer_,
frame,
length,
discard_padding,
track_number,
rel_timecode,
generic_arg);
if (element_size == 0)
return false;
PostWriteBlock(element_size);
return true;
}
@ -2348,8 +2395,8 @@ bool Segment::AddFrame(const uint8* frame,
// audio that is associated with the start time of a video key-frame is
// muxed into the same cluster.
if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) {
Frame* const new_frame = new Frame();
if (!new_frame->Init(frame, length))
Frame* const new_frame = new (std::nothrow) Frame();
if (new_frame == NULL || !new_frame->Init(frame, length))
return false;
new_frame->set_track_number(track_number);
new_frame->set_timestamp(timestamp);
@ -2400,7 +2447,7 @@ bool Segment::AddFrameWithAdditional(const uint8* frame,
uint64 track_number,
uint64 timestamp,
bool is_key) {
if (!frame || !additional)
if (frame == NULL || additional == NULL)
return false;
if (!CheckHeaderInfo())
@ -2414,8 +2461,8 @@ bool Segment::AddFrameWithAdditional(const uint8* frame,
// audio that is associated with the start time of a video key-frame is
// muxed into the same cluster.
if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) {
Frame* const new_frame = new Frame();
if (!new_frame->Init(frame, length))
Frame* const new_frame = new (std::nothrow) Frame();
if (new_frame == NULL || !new_frame->Init(frame, length))
return false;
new_frame->set_track_number(track_number);
new_frame->set_timestamp(timestamp);
@ -2427,6 +2474,74 @@ bool Segment::AddFrameWithAdditional(const uint8* frame,
return true;
}
if (!DoNewClusterProcessing(track_number, timestamp, is_key))
return false;
if (cluster_list_size_ < 1)
return false;
Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
if (cluster == NULL)
return false;
const uint64 timecode_scale = segment_info_.timecode_scale();
const uint64 abs_timecode = timestamp / timecode_scale;
if (!cluster->AddFrameWithAdditional(frame,
length,
additional,
additional_length,
add_id,
track_number,
abs_timecode,
is_key))
return false;
if (new_cuepoint_ && cues_track_ == track_number) {
if (!AddCuePoint(timestamp, cues_track_))
return false;
}
if (timestamp > last_timestamp_)
last_timestamp_ = timestamp;
return true;
}
bool Segment::AddFrameWithDiscardPadding(const uint8* frame,
uint64 length,
int64 discard_padding,
uint64 track_number,
uint64 timestamp,
bool is_key) {
if (frame == NULL || discard_padding <= 0)
return false;
if (!CheckHeaderInfo())
return false;
// Check for non-monotonically increasing timestamps.
if (timestamp < last_timestamp_)
return false;
// If the segment has a video track hold onto audio frames to make sure the
// audio that is associated with the start time of a video key-frame is
// muxed into the same cluster.
if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) {
Frame* const new_frame = new (std::nothrow) Frame();
if (new_frame == NULL || !new_frame->Init(frame, length))
return false;
new_frame->set_track_number(track_number);
new_frame->set_timestamp(timestamp);
new_frame->set_is_key(is_key);
new_frame->set_discard_padding(discard_padding);
if (!QueueFrame(new_frame))
return false;
return true;
}
if (!DoNewClusterProcessing(track_number, timestamp, is_key))
return false;
@ -2440,15 +2555,13 @@ bool Segment::AddFrameWithAdditional(const uint8* frame,
const uint64 timecode_scale = segment_info_.timecode_scale();
const uint64 abs_timecode = timestamp / timecode_scale;
if (!cluster->AddFrameWithAdditional(frame,
length,
additional,
additional_length,
add_id,
track_number,
abs_timecode,
is_key))
if (!cluster->AddFrameWithDiscardPadding(frame, length,
discard_padding,
track_number,
abs_timecode,
is_key)) {
return false;
}
if (new_cuepoint_ && cues_track_ == track_number) {
if (!AddCuePoint(timestamp, cues_track_))
@ -2523,6 +2636,12 @@ bool Segment::AddGenericFrame(const Frame* frame) {
frame->track_number(),
frame->timestamp(),
frame->is_key());
} else if (frame->discard_padding() > 0) {
return AddFrameWithDiscardPadding(frame->frame(), frame->length(),
frame->discard_padding(),
frame->track_number(),
frame->timestamp(),
frame->is_key());
} else {
return AddFrame(frame->frame(),
frame->length(),
@ -3006,12 +3125,24 @@ int Segment::WriteFramesAll() {
const uint64 frame_timestamp = frame->timestamp(); // ns
const uint64 frame_timecode = frame_timestamp / timecode_scale;
if (!cluster->AddFrame(frame->frame(),
frame->length(),
frame->track_number(),
frame_timecode,
frame->is_key()))
return -1;
if (frame->discard_padding() > 0) {
if (!cluster->AddFrameWithDiscardPadding(frame->frame(),
frame->length(),
frame->discard_padding(),
frame->track_number(),
frame_timecode,
frame->is_key())) {
return -1;
}
} else {
if (!cluster->AddFrame(frame->frame(),
frame->length(),
frame->track_number(),
frame_timecode,
frame->is_key())) {
return -1;
}
}
if (new_cuepoint_ && cues_track_ == frame->track_number()) {
if (!AddCuePoint(frame_timestamp, cues_track_))
@ -3057,13 +3188,26 @@ bool Segment::WriteFramesLessThan(uint64 timestamp) {
const Frame* const frame_prev = frames_[i-1];
const uint64 frame_timestamp = frame_prev->timestamp();
const uint64 frame_timecode = frame_timestamp / timecode_scale;
const int64 discard_padding = frame_prev->discard_padding();
if (!cluster->AddFrame(frame_prev->frame(),
frame_prev->length(),
frame_prev->track_number(),
frame_timecode,
frame_prev->is_key()))
return false;
if (discard_padding > 0) {
if (!cluster->AddFrameWithDiscardPadding(frame_prev->frame(),
frame_prev->length(),
discard_padding,
frame_prev->track_number(),
frame_timecode,
frame_prev->is_key())) {
return false;
}
} else {
if (!cluster->AddFrame(frame_prev->frame(),
frame_prev->length(),
frame_prev->track_number(),
frame_timecode,
frame_prev->is_key())) {
return false;
}
}
if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) {
if (!AddCuePoint(frame_timestamp, cues_track_))

View File

@ -90,6 +90,10 @@ class Frame {
uint64 track_number() const { return track_number_; }
void set_timestamp(uint64 timestamp) { timestamp_ = timestamp; }
uint64 timestamp() const { return timestamp_; }
void set_discard_padding(uint64 discard_padding) {
discard_padding_ = discard_padding;
}
uint64 discard_padding() const { return discard_padding_; }
private:
// Id of the Additional data.
@ -118,6 +122,9 @@ class Frame {
// Timestamp of the data in nanoseconds.
uint64 timestamp_;
// Discard padding for the frame.
int64 discard_padding_;
};
///////////////////////////////////////////////////////////////
@ -745,6 +752,24 @@ class Cluster {
uint64 abs_timecode,
bool is_key);
// Adds a frame to be output in the file. The frame is written out through
// |writer_| if successful. Returns true on success.
// Inputs:
// frame: Pointer to the data.
// length: Length of the data.
// discard_padding: DiscardPadding element value.
// track_number: Track to add the data to. Value returned by Add track
// functions. The range of allowed values is [1, 126].
// abs_timecode: Absolute (not relative to cluster) timestamp of the
// frame, expressed in timecode units.
// is_key: Flag telling whether or not this frame is a key frame.
bool AddFrameWithDiscardPadding(const uint8* frame,
uint64 length,
int64 discard_padding,
uint64 track_number,
uint64 abs_timecode,
bool is_key);
// Writes a frame of metadata to the output medium; returns true on
// success.
// Inputs:
@ -803,6 +828,35 @@ class Cluster {
int64 timecode,
uint64 is_key);
// Signature that matches WriteBlockWithDiscardPadding
// in the muxer utilities package.
typedef uint64 (*WriteBlockDiscardPadding)(IMkvWriter* writer,
const uint8* data,
uint64 length,
int64 discard_padding,
uint64 track_number,
int64 timecode,
uint64 is_key);
// Utility method that confirms that blocks can still be added, and that the
// cluster header has been written. Used by |DoWriteBlock*|. Returns true
// when successful.
template <typename Type>
bool PreWriteBlock(Type* write_function);
// Utility method used by the |DoWriteBlock*| methods that handles the book
// keeping required after each block is written.
void PostWriteBlock(uint64 element_size);
// To simplify things, we require that there be fewer than 127
// tracks -- this allows us to serialize the track number value for
// a stream using a single byte, per the Matroska encoding.
bool IsValidTrackNumber(uint64 track_number) const;
// Given |abs_timecode|, calculates timecode relative to most recent timecode.
// Returns -1 on failure, or a relative timecode.
int64 GetRelativeTimecode(int64 abs_timecode) const;
// Used to implement AddFrame and AddMetadata.
bool DoWriteBlock(const uint8* frame,
uint64 length,
@ -822,6 +876,15 @@ class Cluster {
uint64 generic_arg,
WriteBlockAdditional write_block);
// Used to implement AddFrameWithDiscardPadding
bool DoWriteBlockWithDiscardPadding(const uint8* frame,
uint64 length,
int64 discard_padding,
uint64 track_number,
uint64 absolute_timecode,
uint64 generic_arg,
WriteBlockDiscardPadding write_block);
// Outputs the Cluster header to |writer_|. Returns true on success.
bool WriteClusterHeader();
@ -1055,6 +1118,24 @@ class Segment {
uint64 timestamp,
bool is_key);
// Writes a frame with DiscardPadding to the output medium; returns true on
// success.
// Inputs:
// frame: Pointer to the data.
// length: Length of the data.
// discard_padding: DiscardPadding element value.
// track_number: Track to add the data to. Value returned by Add track
// functions.
// timestamp: Absolute timestamp of the frame, expressed in nanosecond
// units.
// is_key: Flag telling whether or not this frame is a key frame.
bool AddFrameWithDiscardPadding(const uint8* frame,
uint64 length,
int64 discard_padding,
uint64 track_number,
uint64 timestamp,
bool is_key);
// Writes a Frame to the output medium. Chooses the correct way of writing
// the frame (Block vs SimpleBlock) based on the parameters passed.
// Inputs:

View File

@ -74,6 +74,10 @@ uint64 EbmlMasterElementSize(uint64 type, uint64 value) {
return ebml_size;
}
uint64 EbmlElementSize(uint64 type, int64 value) {
return EbmlElementSize(type, static_cast<uint64>(value));
}
uint64 EbmlElementSize(uint64 type, uint64 value) {
// Size of EBML ID
int32 ebml_size = GetUIntSize(type);
@ -464,7 +468,8 @@ uint64 WriteMetadataBlock(IMkvWriter* writer,
return blockg_elem_size;
}
// Writes a WebM Block with Additional. The structure is as follows
// Writes a WebM BlockGroup with BlockAdditional data. The structure is as
// follows:
// Indentation shows sub-levels
// BlockGroup
// Block
@ -553,6 +558,70 @@ uint64 WriteBlockWithAdditional(IMkvWriter* writer,
return block_group_elem_size;
}
// Writes a WebM BlockGroup with DiscardPadding. The structure is as follows:
// Indentation shows sub-levels
// BlockGroup
// Block
// Data
// DiscardPadding
uint64 WriteBlockWithDiscardPadding(IMkvWriter* writer,
const uint8* data,
uint64 length,
int64 discard_padding,
uint64 track_number,
int64 timecode,
uint64 is_key) {
if (!data || length < 1 || discard_padding <= 0)
return 0;
const uint64 block_payload_size = 4 + length;
const uint64 block_elem_size = EbmlMasterElementSize(kMkvBlock,
block_payload_size) +
block_payload_size;
const uint64 discard_padding_elem_size = EbmlElementSize(kMkvDiscardPadding,
discard_padding);
const uint64 block_group_payload_size = block_elem_size +
discard_padding_elem_size;
const uint64 block_group_elem_size = EbmlMasterElementSize(
kMkvBlockGroup,
block_group_payload_size) +
block_group_payload_size;
if (!WriteEbmlMasterElement(writer, kMkvBlockGroup,
block_group_payload_size))
return 0;
if (!WriteEbmlMasterElement(writer, kMkvBlock, block_payload_size))
return 0;
if (WriteUInt(writer, track_number))
return 0;
if (SerializeInt(writer, timecode, 2))
return 0;
uint64 flags = 0;
if (is_key)
flags |= 0x80;
if (SerializeInt(writer, flags, 1))
return 0;
if (writer->Write(data, static_cast<uint32>(length)))
return 0;
if (WriteID(writer, kMkvDiscardPadding))
return 0;
const uint64 size = GetUIntSize(discard_padding);
if (WriteUInt(writer, size))
return false;
if (SerializeInt(writer, discard_padding, static_cast<int32>(size)))
return false;
return block_group_elem_size;
}
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
if (!writer)
return false;

View File

@ -25,6 +25,7 @@ int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size);
int32 GetUIntSize(uint64 value);
int32 GetCodedUIntSize(uint64 value);
uint64 EbmlMasterElementSize(uint64 type, uint64 value);
uint64 EbmlElementSize(uint64 type, int64 value);
uint64 EbmlElementSize(uint64 type, uint64 value);
uint64 EbmlElementSize(uint64 type, float value);
uint64 EbmlElementSize(uint64 type, const char* value);
@ -113,6 +114,25 @@ uint64 WriteBlockWithAdditional(IMkvWriter* writer,
int64 timecode,
uint64 is_key);
// Output an Mkv Block with a DiscardPadding element.
// Inputs:
// data: Pointer to the data.
// length: Length of the data.
// discard_padding: DiscardPadding value.
// track_number: Track to add the data to. Value returned by Add track
// functions. Only values in the range [1, 126] are
// permitted.
// timecode: Relative timecode of the Block. Only values in the
// range [0, 2^15) are permitted.
// is_key: Non-zero value specifies that frame is a key frame.
uint64 WriteBlockWithDiscardPadding(IMkvWriter* writer,
const uint8* data,
uint64 length,
int64 discard_padding,
uint64 track_number,
int64 timecode,
uint64 is_key);
// Output a void element. |size| must be the entire size in bytes that will be
// void. The function will calculate the size of the void header and subtract
// it from |size|.

View File

@ -23,6 +23,7 @@
#include "sample_muxer_metadata.h"
using mkvmuxer::int64;
using mkvmuxer::uint64;
#ifdef _MSC_VER
@ -475,6 +476,7 @@ int main(int argc, char* argv[]) {
(track_type == Track::kVideo && output_video)) {
const int frame_count = block->GetFrameCount();
const bool is_key = block->IsKey();
const int64 discard_padding = block->GetDiscardPadding();
for (int i = 0; i < frame_count; ++i) {
const mkvparser::Block::Frame& frame = block->GetFrame(i);
@ -494,11 +496,21 @@ int main(int argc, char* argv[]) {
if (track_type == Track::kAudio)
track_num = aud_track;
if (!muxer_segment.AddFrame(data,
frame.len,
track_num,
time_ns,
is_key)) {
bool frame_added = false;
if (discard_padding) {
frame_added =
muxer_segment.AddFrameWithDiscardPadding(data, frame.len,
discard_padding,
track_num,
time_ns,
is_key);
} else {
frame_added = muxer_segment.AddFrame(data, frame.len,
track_num,
time_ns,
is_key);
}
if (!frame_added) {
printf("\n Could not add frame.\n");
return EXIT_FAILURE;
}

View File

@ -57,6 +57,7 @@ enum MkvId {
kMkvBlockMore = 0xA6,
kMkvBlockAddID = 0xEE,
kMkvBlockAdditional = 0xA5,
kMkvDiscardPadding = 0x75A2,
//Track
kMkvTracks = 0x1654AE6B,
kMkvTrackEntry = 0xAE,