Elements and functions to support BlockAdditional

Adding elements and functions to support muxing of BlockAdditional element.
This is required for supporting muxing of streams with Alpha Channel (where the
Alpha data goes into BlockAdditional). Detailed design doc of Alpha Channel can
be found here: http://goo.gl/wCP1y

Change-Id: Idac144d9588de16685734850585ab7115ddd08a4
This commit is contained in:
Vignesh Venkatasubramanian 2013-04-23 13:20:39 -07:00
parent 64cee42b85
commit 5af56bb9ee
5 changed files with 360 additions and 0 deletions

View File

@ -460,6 +460,7 @@ Track::Track(unsigned int* seed)
: codec_id_(NULL),
codec_private_(NULL),
language_(NULL),
max_block_additional_id_(0),
name_(NULL),
number_(0),
type_(0),
@ -535,6 +536,8 @@ uint64 Track::PayloadSize() const {
size += EbmlElementSize(kMkvLanguage, language_);
if (name_)
size += EbmlElementSize(kMkvName, name_);
if (max_block_additional_id_)
size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_);
if (content_encoding_entries_size_ > 0) {
uint64 content_encodings_size = 0;
@ -581,6 +584,8 @@ bool Track::Write(IMkvWriter* writer) const {
size += EbmlElementSize(kMkvLanguage, language_);
if (name_)
size += EbmlElementSize(kMkvName, name_);
if (max_block_additional_id_)
size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_);
const int64 payload_position = writer->Position();
if (payload_position < 0)
@ -592,6 +597,11 @@ bool Track::Write(IMkvWriter* writer) const {
return false;
if (!WriteEbmlElement(writer, kMkvTrackType, type_))
return false;
if (max_block_additional_id_)
if (!WriteEbmlElement(writer,
kMkvMaxBlockAdditionID,
max_block_additional_id_))
return false;
if (codec_id_) {
if (!WriteEbmlElement(writer, kMkvCodecID, codec_id_))
return false;
@ -719,6 +729,7 @@ VideoTrack::VideoTrack(unsigned int* seed)
frame_rate_(0.0),
height_(0),
stereo_mode_(0),
alpha_mode_(0),
width_(0) {
}
@ -737,6 +748,15 @@ bool VideoTrack::SetStereoMode(uint64 stereo_mode) {
return true;
}
bool VideoTrack::SetAlphaMode(uint64 alpha_mode) {
if (alpha_mode != kNoAlpha &&
alpha_mode != kAlpha)
return false;
alpha_mode_ = alpha_mode;
return true;
}
uint64 VideoTrack::PayloadSize() const {
const uint64 parent_size = Track::PayloadSize();
@ -772,6 +792,9 @@ bool VideoTrack::Write(IMkvWriter* writer) const {
if (stereo_mode_ > kMono)
if (!WriteEbmlElement(writer, kMkvStereoMode, stereo_mode_))
return false;
if (alpha_mode_ > kNoAlpha)
if (!WriteEbmlElement(writer, kMkvAlphaMode, alpha_mode_))
return false;
if (frame_rate_ > 0.0)
if (!WriteEbmlElement(writer,
kMkvFrameRate,
@ -795,6 +818,8 @@ uint64 VideoTrack::VideoPayloadSize() const {
size += EbmlElementSize(kMkvDisplayHeight, display_height_);
if (stereo_mode_ > kMono)
size += EbmlElementSize(kMkvStereoMode, stereo_mode_);
if (alpha_mode_ > kNoAlpha)
size += EbmlElementSize(kMkvAlphaMode, alpha_mode_);
if (frame_rate_ > 0.0)
size += EbmlElementSize(kMkvFrameRate, static_cast<float>(frame_rate_));
@ -1395,6 +1420,25 @@ bool Cluster::AddFrame(const uint8* frame,
&WriteSimpleBlock);
}
bool Cluster::AddFrameWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 abs_timecode,
bool is_key) {
return DoWriteBlockWithAdditional(frame,
length,
additional,
additional_length,
add_id,
track_number,
abs_timecode,
is_key ? 1 : 0,
&WriteBlockWithAdditional);
}
bool Cluster::AddMetadata(const uint8* frame,
uint64 length,
uint64 track_number,
@ -1494,6 +1538,66 @@ bool Cluster::DoWriteBlock(
return true;
}
bool Cluster::DoWriteBlockWithAdditional(
const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 abs_timecode,
uint64 generic_arg,
WriteBlockAdditional write_block) {
if (frame == NULL || length == 0 ||
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)
return false;
const int64 cluster_timecode = this->Cluster::timecode();
const int64 rel_timecode =
static_cast<int64>(abs_timecode) - cluster_timecode;
if (rel_timecode < 0)
return false;
if (rel_timecode > kMaxBlockTimecode)
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,
additional,
additional_length,
add_id,
track_number,
rel_timecode,
generic_arg);
if (element_size == 0)
return false;
AddPayloadSize(element_size);
blocks_added_++;
return true;
}
bool Cluster::WriteClusterHeader() {
if (finalized_)
return false;
@ -2100,6 +2204,75 @@ bool Segment::AddFrame(const uint8* frame,
return true;
}
bool Segment::AddFrameWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 timestamp,
bool is_key) {
if (!frame || !additional)
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 Frame();
if (!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);
if (!QueueFrame(new_frame))
return false;
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)
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::AddMetadata(const uint8* frame,
uint64 length,
uint64 track_number,

View File

@ -296,6 +296,10 @@ class Track {
const uint8* codec_private() const { return codec_private_; }
void set_language(const char* language);
const char* language() const { return language_; }
void set_max_block_additional_id(uint64 max_block_additional_id) {
max_block_additional_id_ = max_block_additional_id;
}
uint64 max_block_additional_id() const { return max_block_additional_id_; }
void set_name(const char* name);
const char* name() const { return name_; }
void set_number(uint64 number) { number_ = number; }
@ -314,6 +318,7 @@ class Track {
char* codec_id_;
uint8* codec_private_;
char* language_;
uint64 max_block_additional_id_;
char* name_;
uint64 number_;
uint64 type_;
@ -344,6 +349,11 @@ class VideoTrack : public Track {
kSideBySideRightIsFirst = 11
};
enum AlphaMode {
kNoAlpha = 0,
kAlpha = 1
};
// The |seed| parameter is used to synthesize a UID for the track.
explicit VideoTrack(unsigned int* seed);
virtual ~VideoTrack();
@ -358,6 +368,9 @@ class VideoTrack : public Track {
// Sets the video's stereo mode. Returns true on success.
bool SetStereoMode(uint64 stereo_mode);
// Sets the video's alpha mode. Returns true on success.
bool SetAlphaMode(uint64 alpha_mode);
void set_display_height(uint64 height) { display_height_ = height; }
uint64 display_height() const { return display_height_; }
void set_display_width(uint64 width) { display_width_ = width; }
@ -367,6 +380,7 @@ class VideoTrack : public Track {
void set_height(uint64 height) { height_ = height; }
uint64 height() const { return height_; }
uint64 stereo_mode() { return stereo_mode_; }
uint64 alpha_mode() { return alpha_mode_; }
void set_width(uint64 width) { width_ = width; }
uint64 width() const { return width_; }
@ -380,6 +394,7 @@ class VideoTrack : public Track {
double frame_rate_;
uint64 height_;
uint64 stereo_mode_;
uint64 alpha_mode_;
uint64 width_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack);
@ -661,6 +676,28 @@ class Cluster {
uint64 timecode, // timecode units (absolute)
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
// additional: Pointer to the additional data
// additional_length: Length of the additional data
// add_id: Value of BlockAddID element
// 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 AddFrameWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 abs_timecode,
bool is_key);
// Writes a frame of metadata to the output medium; returns true on
// success.
// Inputs:
@ -706,6 +743,18 @@ class Cluster {
int64 timecode,
uint64 generic_arg);
// Signature that matches WriteBlockWithAdditional
// in the muxer utilities package.
typedef uint64 (*WriteBlockAdditional)(IMkvWriter* writer,
const uint8* data,
uint64 length,
const uint8* additional,
uint64 add_id,
uint64 additional_length,
uint64 track_number,
int64 timecode,
uint64 is_key);
// Used to implement AddFrame and AddMetadata.
bool DoWriteBlock(const uint8* frame,
uint64 length,
@ -714,6 +763,17 @@ class Cluster {
uint64 generic_arg,
WriteBlock write_block);
// Used to implement AddFrameWithAdditional
bool DoWriteBlockWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 absolute_timecode,
uint64 generic_arg,
WriteBlockAdditional write_block);
// Outputs the Cluster header to |writer_|. Returns true on success.
bool WriteClusterHeader();
@ -908,6 +968,15 @@ class Segment {
uint64 timestamp_ns,
uint64 duration_ns);
bool AddFrameWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 timestamp,
bool is_key);
// Adds a video track to the segment. Returns the number of the track on
// success, 0 on error. |number| is the number to use for the video track.
// |number| must be >= 0. If |number| == 0 then the muxer will decide on

View File

@ -464,6 +464,95 @@ uint64 WriteMetadataBlock(IMkvWriter* writer,
return blockg_elem_size;
}
// Writes a WebM Block with Additional. The structure is as follows
// Indentation shows sub-levels
// BlockGroup
// Block
// Data
// BlockAdditions
// BlockMore
// BlockAddID
// 1 (Denotes Alpha)
// BlockAdditional
// Data
uint64 WriteBlockWithAdditional(IMkvWriter* writer,
const uint8* data,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
int64 timecode,
uint64 is_key) {
if (!data || !additional || length < 1 || additional_length < 1)
return 0;
const uint64 block_payload_size = 4 + length;
const uint64 block_elem_size = EbmlMasterElementSize(kMkvBlock,
block_payload_size) +
block_payload_size;
const uint64 block_additional_elem_size = EbmlElementSize(kMkvBlockAdditional,
additional,
additional_length);
const uint64 block_addid_elem_size = EbmlElementSize(kMkvBlockAddID, add_id);
const uint64 block_more_payload_size = block_addid_elem_size +
block_additional_elem_size;
const uint64 block_more_elem_size = EbmlMasterElementSize(
kMkvBlockMore,
block_more_payload_size) +
block_more_payload_size;
const uint64 block_additions_payload_size = block_more_elem_size;
const uint64 block_additions_elem_size = EbmlMasterElementSize(
kMkvBlockMore,
block_additions_payload_size) +
block_additions_payload_size;
const uint64 block_group_payload_size = block_elem_size +
block_additions_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 (!WriteEbmlMasterElement(writer, kMkvBlockAdditions,
block_additions_payload_size))
return 0;
if (!WriteEbmlMasterElement(writer, kMkvBlockMore, block_more_payload_size))
return 0;
if (!WriteEbmlElement(writer, kMkvBlockAddID, add_id))
return 0;
if (!WriteEbmlElement(writer, kMkvBlockAdditional,
additional, additional_length))
return 0;
return block_group_elem_size;
}
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
if (!writer)
return false;

View File

@ -88,6 +88,29 @@ uint64 WriteMetadataBlock(IMkvWriter* writer,
int64 timecode,
uint64 duration_timecode);
// Output an Mkv Block with BlockAdditional data.
// Inputs:
// data: Pointer to the data.
// length: Length of the data.
// additional: Pointer to the additional data
// additional_length: Length of the additional data.
// add_id: Value of BlockAddID element.
// 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 WriteBlockWithAdditional(IMkvWriter* writer,
const uint8* data,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
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

@ -53,6 +53,10 @@ enum MkvId {
kMkvReferenceBlock = 0xFB,
kMkvLaceNumber = 0xCC,
kMkvSimpleBlock = 0xA3,
kMkvBlockAdditions = 0x75A1,
kMkvBlockMore = 0xA6,
kMkvBlockAddID = 0xEE,
kMkvBlockAdditional = 0xA5,
//Track
kMkvTracks = 0x1654AE6B,
kMkvTrackEntry = 0xAE,
@ -69,10 +73,12 @@ enum MkvId {
kMkvCodecID = 0x86,
kMkvCodecPrivate = 0x63A2,
kMkvCodecName = 0x258688,
kMkvMaxBlockAdditionID = 0x55EE,
//video
kMkvVideo = 0xE0,
kMkvFlagInterlaced = 0x9A,
kMkvStereoMode = 0x53B8,
kMkvAlphaMode = 0x53C0,
kMkvPixelWidth = 0xB0,
kMkvPixelHeight = 0xBA,
kMkvPixelCropBottom = 0x54AA,