ref: 16c0ec711c02062d10cf9ad53e1a4978792ecb43
parent: c93dc38ca386397b1f01198ff54828c7c452e9cc
parent: c353da68aa10a8d75513e32e25188aedfc8d532f
author: James Zern <[email protected]>
date: Tue Aug 4 20:48:39 EDT 2015
Merge "third_party/libwebm: pull from upstream"
--- a/third_party/libwebm/Android.mk
+++ b/third_party/libwebm/Android.mk
@@ -1,11 +1,10 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
+LOCAL_PATH:= $(call my-dir)
-LOCAL_CPP_EXTENSION := .cpp
-LOCAL_SRC_FILES := mkvmuxer.cpp \
- mkvmuxerutil.cpp \
- mkvparser.cpp \
- mkvreader.cpp \
- mkvwriter.cpp
-LOCAL_MODULE := libwebm
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libwebm
+LOCAL_SRC_FILES:= mkvparser.cpp \
+ mkvreader.cpp \
+ mkvmuxer.cpp \
+ mkvmuxerutil.cpp \
+ mkvwriter.cpp
include $(BUILD_STATIC_LIBRARY)
--- a/third_party/libwebm/PATENTS.TXT
+++ b/third_party/libwebm/PATENTS.TXT
@@ -17,7 +17,7 @@
enforcement activity against any entity (including a cross-claim or
counterclaim in a lawsuit) alleging that any of these implementations of WebM
or any code incorporated within any of these implementations of WebM
-constitutes direct or contributory patent infringement, or inducement of
+constitute direct or contributory patent infringement, or inducement of
patent infringement, then any patent rights granted to you under this License
for these implementations of WebM shall terminate as of the date such
litigation is filed.
--- a/third_party/libwebm/README.libvpx
+++ b/third_party/libwebm/README.libvpx
@@ -1,5 +1,5 @@
URL: https://chromium.googlesource.com/webm/libwebm
-Version: 249629d46c6e9391f25a90cff6d19075f47474cb
+Version: b6de61a5c0d441c91afb0785e3b26efd2f9e7337
License: BSD
License File: LICENSE.txt
--- a/third_party/libwebm/mkvmuxer.cpp
+++ b/third_party/libwebm/mkvmuxer.cpp
@@ -65,7 +65,7 @@
IMkvWriter::~IMkvWriter() {}
-bool WriteEbmlHeader(IMkvWriter* writer) {
+bool WriteEbmlHeader(IMkvWriter* writer, uint64 doc_type_version) {
// Level 0
uint64 size = EbmlElementSize(kMkvEBMLVersion, 1ULL);
size += EbmlElementSize(kMkvEBMLReadVersion, 1ULL);
@@ -72,7 +72,7 @@
size += EbmlElementSize(kMkvEBMLMaxIDLength, 4ULL);
size += EbmlElementSize(kMkvEBMLMaxSizeLength, 8ULL);
size += EbmlElementSize(kMkvDocType, "webm");
- size += EbmlElementSize(kMkvDocTypeVersion, 2ULL);
+ size += EbmlElementSize(kMkvDocTypeVersion, doc_type_version);
size += EbmlElementSize(kMkvDocTypeReadVersion, 2ULL);
if (!WriteEbmlMasterElement(writer, kMkvEBML, size))
@@ -87,7 +87,7 @@
return false;
if (!WriteEbmlElement(writer, kMkvDocType, "webm"))
return false;
- if (!WriteEbmlElement(writer, kMkvDocTypeVersion, 2ULL))
+ if (!WriteEbmlElement(writer, kMkvDocTypeVersion, doc_type_version))
return false;
if (!WriteEbmlElement(writer, kMkvDocTypeReadVersion, 2ULL))
return false;
@@ -95,6 +95,10 @@
return true;
}
+bool WriteEbmlHeader(IMkvWriter* writer) {
+ return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion);
+}
+
bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst,
mkvmuxer::int64 start, int64 size) {
// TODO(vigneshv): Check if this is a reasonable value.
@@ -127,7 +131,9 @@
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_;
@@ -134,6 +140,31 @@
delete[] additional_;
}
+bool Frame::CopyFrom(const Frame& frame) {
+ delete[] frame_;
+ frame_ = NULL;
+ length_ = 0;
+ if (frame.length() > 0 && frame.frame() != NULL &&
+ !Init(frame.frame(), frame.length())) {
+ return false;
+ }
+ add_id_ = 0;
+ delete[] additional_;
+ additional_ = NULL;
+ additional_length_ = 0;
+ if (frame.additional_length() > 0 && frame.additional() != NULL &&
+ !AddAdditionalData(frame.additional(), frame.additional_length(),
+ frame.add_id())) {
+ return false;
+ }
+ duration_ = frame.duration();
+ is_key_ = frame.is_key();
+ track_number_ = frame.track_number();
+ timestamp_ = frame.timestamp();
+ discard_padding_ = frame.discard_padding();
+ return true;
+}
+
bool Frame::Init(const uint8* frame, uint64 length) {
uint8* const data =
new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
@@ -164,6 +195,32 @@
return true;
}
+bool Frame::IsValid() const {
+ if (length_ == 0 || !frame_) {
+ return false;
+ }
+ if ((additional_length_ != 0 && !additional_) ||
+ (additional_ != NULL && additional_length_ == 0)) {
+ return false;
+ }
+ if (track_number_ == 0 || track_number_ > kMaxTrackNumber) {
+ return false;
+ }
+ if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) {
+ return false;
+ }
+ return true;
+}
+
+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
@@ -271,7 +328,7 @@
return false;
CuePoint** const cues =
- new (std::nothrow) CuePoint* [new_capacity]; // NOLINT
+ new (std::nothrow) CuePoint*[new_capacity]; // NOLINT
if (!cues)
return false;
@@ -532,7 +589,7 @@
const uint32 count = content_encoding_entries_size_ + 1;
ContentEncoding** const content_encoding_entries =
- new (std::nothrow) ContentEncoding* [count]; // NOLINT
+ new (std::nothrow) ContentEncoding*[count]; // NOLINT
if (!content_encoding_entries)
return false;
@@ -612,6 +669,10 @@
if (!writer)
return false;
+ // mandatory elements without a default value.
+ if (!type_ || !codec_id_)
+ return false;
+
// |size| may be bigger than what is written out in this function because
// derived classes may write out more data in the Track element.
const uint64 payload_size = PayloadSize();
@@ -619,10 +680,6 @@
if (!WriteEbmlMasterElement(writer, kMkvTrackEntry, payload_size))
return false;
- // |type_| has to be specified before the Track can be written.
- if (!type_)
- return false;
-
uint64 size = EbmlElementSize(kMkvTrackNumber, number_);
size += EbmlElementSize(kMkvTrackUID, uid_);
size += EbmlElementSize(kMkvTrackType, type_);
@@ -793,6 +850,10 @@
: Track(seed),
display_height_(0),
display_width_(0),
+ crop_left_(0),
+ crop_right_(0),
+ crop_top_(0),
+ crop_bottom_(0),
frame_rate_(0.0),
height_(0),
stereo_mode_(0),
@@ -846,27 +907,50 @@
return false;
if (!WriteEbmlElement(writer, kMkvPixelHeight, height_))
return false;
- if (display_width_ > 0)
+ if (display_width_ > 0) {
if (!WriteEbmlElement(writer, kMkvDisplayWidth, display_width_))
return false;
- if (display_height_ > 0)
+ }
+ if (display_height_ > 0) {
if (!WriteEbmlElement(writer, kMkvDisplayHeight, display_height_))
return false;
- if (stereo_mode_ > kMono)
+ }
+ if (crop_left_ > 0) {
+ if (!WriteEbmlElement(writer, kMkvPixelCropLeft, crop_left_))
+ return false;
+ }
+ if (crop_right_ > 0) {
+ if (!WriteEbmlElement(writer, kMkvPixelCropRight, crop_right_))
+ return false;
+ }
+ if (crop_top_ > 0) {
+ if (!WriteEbmlElement(writer, kMkvPixelCropTop, crop_top_))
+ return false;
+ }
+ if (crop_bottom_ > 0) {
+ if (!WriteEbmlElement(writer, kMkvPixelCropBottom, crop_bottom_))
+ return false;
+ }
+ if (stereo_mode_ > kMono) {
if (!WriteEbmlElement(writer, kMkvStereoMode, stereo_mode_))
return false;
- if (alpha_mode_ > kNoAlpha)
+ }
+ if (alpha_mode_ > kNoAlpha) {
if (!WriteEbmlElement(writer, kMkvAlphaMode, alpha_mode_))
return false;
- if (frame_rate_ > 0.0)
+ }
+ if (frame_rate_ > 0.0) {
if (!WriteEbmlElement(writer, kMkvFrameRate,
- static_cast<float>(frame_rate_)))
+ static_cast<float>(frame_rate_))) {
return false;
+ }
+ }
const int64 stop_position = writer->Position();
if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(size))
+ stop_position - payload_position != static_cast<int64>(size)) {
return false;
+ }
return true;
}
@@ -878,6 +962,14 @@
size += EbmlElementSize(kMkvDisplayWidth, display_width_);
if (display_height_ > 0)
size += EbmlElementSize(kMkvDisplayHeight, display_height_);
+ if (crop_left_ > 0)
+ size += EbmlElementSize(kMkvPixelCropLeft, crop_left_);
+ if (crop_right_ > 0)
+ size += EbmlElementSize(kMkvPixelCropRight, crop_right_);
+ if (crop_top_ > 0)
+ size += EbmlElementSize(kMkvPixelCropTop, crop_top_);
+ if (crop_bottom_ > 0)
+ size += EbmlElementSize(kMkvPixelCropBottom, crop_bottom_);
if (stereo_mode_ > kMono)
size += EbmlElementSize(kMkvStereoMode, stereo_mode_);
if (alpha_mode_ > kNoAlpha)
@@ -990,7 +1082,7 @@
const uint32 count = track_entries_size_ + 1;
- Track** const track_entries = new (std::nothrow) Track* [count]; // NOLINT
+ Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
if (!track_entries)
return false;
@@ -1145,6 +1237,8 @@
void Chapter::Init(unsigned int* seed) {
id_ = NULL;
+ start_timecode_ = 0;
+ end_timecode_ = 0;
displays_ = NULL;
displays_size_ = 0;
displays_count_ = 0;
@@ -1420,11 +1514,242 @@
return edition_size;
}
+// Tag Class
+
+bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) {
+ if (!ExpandSimpleTagsArray())
+ return false;
+
+ SimpleTag& st = simple_tags_[simple_tags_count_++];
+ st.Init();
+
+ if (!st.set_tag_name(tag_name))
+ return false;
+
+ if (!st.set_tag_string(tag_string))
+ return false;
+
+ return true;
+}
+
+Tag::Tag() {
+ simple_tags_ = NULL;
+ simple_tags_size_ = 0;
+ simple_tags_count_ = 0;
+}
+
+Tag::~Tag() {}
+
+void Tag::ShallowCopy(Tag* dst) const {
+ dst->simple_tags_ = simple_tags_;
+ dst->simple_tags_size_ = simple_tags_size_;
+ dst->simple_tags_count_ = simple_tags_count_;
+}
+
+void Tag::Clear() {
+ while (simple_tags_count_ > 0) {
+ SimpleTag& st = simple_tags_[--simple_tags_count_];
+ st.Clear();
+ }
+
+ delete[] simple_tags_;
+ simple_tags_ = NULL;
+
+ simple_tags_size_ = 0;
+}
+
+bool Tag::ExpandSimpleTagsArray() {
+ if (simple_tags_size_ > simple_tags_count_)
+ return true; // nothing to do yet
+
+ const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_;
+
+ SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT
+ if (simple_tags == NULL)
+ return false;
+
+ for (int idx = 0; idx < simple_tags_count_; ++idx) {
+ simple_tags[idx] = simple_tags_[idx]; // shallow copy
+ }
+
+ delete[] simple_tags_;
+
+ simple_tags_ = simple_tags;
+ simple_tags_size_ = size;
+
+ return true;
+}
+
+uint64 Tag::Write(IMkvWriter* writer) const {
+ uint64 payload_size = 0;
+
+ for (int idx = 0; idx < simple_tags_count_; ++idx) {
+ const SimpleTag& st = simple_tags_[idx];
+ payload_size += st.Write(NULL);
+ }
+
+ const uint64 tag_size =
+ EbmlMasterElementSize(kMkvTag, payload_size) + payload_size;
+
+ if (writer == NULL)
+ return tag_size;
+
+ const int64 start = writer->Position();
+
+ if (!WriteEbmlMasterElement(writer, kMkvTag, payload_size))
+ return 0;
+
+ for (int idx = 0; idx < simple_tags_count_; ++idx) {
+ const SimpleTag& st = simple_tags_[idx];
+
+ if (!st.Write(writer))
+ return 0;
+ }
+
+ const int64 stop = writer->Position();
+
+ if (stop >= start && uint64(stop - start) != tag_size)
+ return 0;
+
+ return tag_size;
+}
+
+// Tag::SimpleTag
+
+void Tag::SimpleTag::Init() {
+ tag_name_ = NULL;
+ tag_string_ = NULL;
+}
+
+void Tag::SimpleTag::Clear() {
+ StrCpy(NULL, &tag_name_);
+ StrCpy(NULL, &tag_string_);
+}
+
+bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
+ return StrCpy(tag_name, &tag_name_);
+}
+
+bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
+ return StrCpy(tag_string, &tag_string_);
+}
+
+uint64 Tag::SimpleTag::Write(IMkvWriter* writer) const {
+ uint64 payload_size = EbmlElementSize(kMkvTagName, tag_name_);
+
+ payload_size += EbmlElementSize(kMkvTagString, tag_string_);
+
+ const uint64 simple_tag_size =
+ EbmlMasterElementSize(kMkvSimpleTag, payload_size) + payload_size;
+
+ if (writer == NULL)
+ return simple_tag_size;
+
+ const int64 start = writer->Position();
+
+ if (!WriteEbmlMasterElement(writer, kMkvSimpleTag, payload_size))
+ return 0;
+
+ if (!WriteEbmlElement(writer, kMkvTagName, tag_name_))
+ return 0;
+
+ if (!WriteEbmlElement(writer, kMkvTagString, tag_string_))
+ return 0;
+
+ const int64 stop = writer->Position();
+
+ if (stop >= start && uint64(stop - start) != simple_tag_size)
+ return 0;
+
+ return simple_tag_size;
+}
+
+// Tags Class
+
+Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
+
+Tags::~Tags() {
+ while (tags_count_ > 0) {
+ Tag& tag = tags_[--tags_count_];
+ tag.Clear();
+ }
+
+ delete[] tags_;
+ tags_ = NULL;
+}
+
+int Tags::Count() const { return tags_count_; }
+
+Tag* Tags::AddTag() {
+ if (!ExpandTagsArray())
+ return NULL;
+
+ Tag& tag = tags_[tags_count_++];
+
+ return &tag;
+}
+
+bool Tags::Write(IMkvWriter* writer) const {
+ if (writer == NULL)
+ return false;
+
+ uint64 payload_size = 0;
+
+ for (int idx = 0; idx < tags_count_; ++idx) {
+ const Tag& tag = tags_[idx];
+ payload_size += tag.Write(NULL);
+ }
+
+ if (!WriteEbmlMasterElement(writer, kMkvTags, payload_size))
+ return false;
+
+ const int64 start = writer->Position();
+
+ for (int idx = 0; idx < tags_count_; ++idx) {
+ const Tag& tag = tags_[idx];
+
+ const uint64 tag_size = tag.Write(writer);
+ if (tag_size == 0) // error
+ return 0;
+ }
+
+ const int64 stop = writer->Position();
+
+ if (stop >= start && uint64(stop - start) != payload_size)
+ return false;
+
+ return true;
+}
+
+bool Tags::ExpandTagsArray() {
+ if (tags_size_ > tags_count_)
+ return true; // nothing to do yet
+
+ const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_;
+
+ Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT
+ if (tags == NULL)
+ return false;
+
+ for (int idx = 0; idx < tags_count_; ++idx) {
+ const Tag& src = tags_[idx];
+ Tag* const dst = tags + idx;
+ src.ShallowCopy(dst);
+ }
+
+ delete[] tags_;
+
+ tags_ = tags;
+ tags_size_ = size;
+
+ return true;
+}
+
///////////////////////////////////////////////////////////////
//
// Cluster class
-Cluster::Cluster(uint64 timecode, int64 cues_pos)
+Cluster::Cluster(uint64 timecode, int64 cues_pos, uint64 timecode_scale)
: blocks_added_(0),
finalized_(false),
header_written_(false),
@@ -1432,6 +1757,7 @@
position_for_cues_(cues_pos),
size_position_(-1),
timecode_(timecode),
+ timecode_scale_(timecode_scale),
writer_(NULL) {}
Cluster::~Cluster() {}
@@ -1444,36 +1770,62 @@
return true;
}
-bool Cluster::AddFrame(const uint8* frame, uint64 length, uint64 track_number,
+bool Cluster::AddFrame(const Frame* const frame) { return DoWriteFrame(frame); }
+
+bool Cluster::AddFrame(const uint8* data, uint64 length, uint64 track_number,
uint64 abs_timecode, bool is_key) {
- return DoWriteBlock(frame, length, track_number, abs_timecode, is_key ? 1 : 0,
- &WriteSimpleBlock);
+ Frame frame;
+ if (!frame.Init(data, length))
+ return false;
+ frame.set_track_number(track_number);
+ frame.set_timestamp(abs_timecode);
+ frame.set_is_key(is_key);
+ return DoWriteFrame(&frame);
}
-bool Cluster::AddFrameWithAdditional(const uint8* frame, uint64 length,
+bool Cluster::AddFrameWithAdditional(const uint8* data, 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);
+ if (!additional || additional_length == 0) {
+ return false;
+ }
+ Frame frame;
+ if (!frame.Init(data, length) ||
+ !frame.AddAdditionalData(additional, additional_length, add_id)) {
+ return false;
+ }
+ frame.set_track_number(track_number);
+ frame.set_timestamp(abs_timecode);
+ frame.set_is_key(is_key);
+ return DoWriteFrame(&frame);
}
-bool Cluster::AddFrameWithDiscardPadding(const uint8* frame, uint64 length,
+bool Cluster::AddFrameWithDiscardPadding(const uint8* data, 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);
+ Frame frame;
+ if (!frame.Init(data, length))
+ return false;
+ frame.set_discard_padding(discard_padding);
+ frame.set_track_number(track_number);
+ frame.set_timestamp(abs_timecode);
+ frame.set_is_key(is_key);
+ return DoWriteFrame(&frame);
}
-bool Cluster::AddMetadata(const uint8* frame, uint64 length,
- uint64 track_number, uint64 abs_timecode,
- uint64 duration_timecode) {
- return DoWriteBlock(frame, length, track_number, abs_timecode,
- duration_timecode, &WriteMetadataBlock);
+bool Cluster::AddMetadata(const uint8* data, uint64 length, uint64 track_number,
+ uint64 abs_timecode, uint64 duration_timecode) {
+ Frame frame;
+ if (!frame.Init(data, length))
+ return false;
+ frame.set_track_number(track_number);
+ frame.set_timestamp(abs_timecode);
+ frame.set_duration(duration_timecode);
+ frame.set_is_key(true); // All metadata blocks are keyframes.
+ return DoWriteFrame(&frame);
}
void Cluster::AddPayloadSize(uint64 size) { payload_size_ += size; }
@@ -1506,11 +1858,7 @@
return element_size;
}
-template <typename Type>
-bool Cluster::PreWriteBlock(Type* write_function) {
- if (write_function == NULL)
- return false;
-
+bool Cluster::PreWriteBlock() {
if (finalized_)
return false;
@@ -1527,10 +1875,6 @@
++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 =
@@ -1542,24 +1886,14 @@
return rel_timecode;
}
-bool Cluster::DoWriteBlock(const uint8* frame, uint64 length,
- uint64 track_number, uint64 abs_timecode,
- uint64 generic_arg, WriteBlock write_block) {
- if (frame == NULL || length == 0)
+bool Cluster::DoWriteFrame(const Frame* const frame) {
+ if (!frame || !frame->IsValid())
return false;
- if (!IsValidTrackNumber(track_number))
+ if (!PreWriteBlock())
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, track_number, rel_timecode, generic_arg);
+ const uint64 element_size = WriteFrame(writer_, frame, this);
if (element_size == 0)
return false;
@@ -1567,61 +1901,6 @@
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;
-
- 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, additional, additional_length,
- add_id, track_number, rel_timecode, generic_arg);
- if (element_size == 0)
- return false;
-
- 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;
-}
-
bool Cluster::WriteClusterHeader() {
if (finalized_)
return false;
@@ -1860,7 +2139,7 @@
if (duration_ > 0.0)
size += EbmlElementSize(kMkvDuration, static_cast<float>(duration_));
if (date_utc_ != LLONG_MIN)
- size += EbmlDateElementSize(kMkvDateUTC, date_utc_);
+ size += EbmlDateElementSize(kMkvDateUTC);
size += EbmlElementSize(kMkvMuxingApp, muxing_app_);
size += EbmlElementSize(kMkvWritingApp, writing_app_);
@@ -1966,6 +2245,8 @@
output_cues_(true),
payload_pos_(0),
size_position_(0),
+ doc_type_version_(kDefaultDocTypeVersion),
+ doc_type_version_written_(0),
writer_cluster_(NULL),
writer_cues_(NULL),
writer_header_(NULL) {
@@ -2012,7 +2293,6 @@
void Segment::MoveCuesBeforeClustersHelper(uint64 diff, int32 index,
uint64* cues_size) {
- const uint64 old_cues_size = *cues_size;
CuePoint* const cue_point = cues_.GetCueByIndex(index);
if (cue_point == NULL)
return;
@@ -2020,18 +2300,19 @@
const uint64 cluster_pos = cue_point->cluster_pos() + diff;
cue_point->set_cluster_pos(cluster_pos); // update the new cluster position
// New size of the cue is computed as follows
- // Let a = current size of Cues Element
- // Let b = Difference in Cue Point's size after this pass
- // Let c = Difference in length of Cues Element's size
- // (This is computed as CodedSize(a + b) - CodedSize(a)
- // Let d = a + b + c. Now d is the new size of the Cues element which is
- // passed on to the next recursive call.
+ // Let a = current sum of size of all CuePoints
+ // Let b = Increase in Cue Point's size due to this iteration
+ // Let c = Increase in size of Cues Element's length due to this iteration
+ // (This is computed as CodedSize(a + b) - CodedSize(a))
+ // Let d = b + c. Now d is the |diff| passed to the next recursive call.
+ // Let e = a + b. Now e is the |cues_size| passed to the next recursive
+ // call.
const uint64 cue_point_size_diff = cue_point->Size() - old_cue_point_size;
const uint64 cue_size_diff =
GetCodedUIntSize(*cues_size + cue_point_size_diff) -
GetCodedUIntSize(*cues_size);
- *cues_size += cue_point_size_diff + cue_size_diff;
- diff = *cues_size - old_cues_size;
+ *cues_size += cue_point_size_diff;
+ diff = cue_size_diff + cue_point_size_diff;
if (diff > 0) {
for (int32 i = 0; i < cues_.cue_entries_size(); ++i) {
MoveCuesBeforeClustersHelper(diff, i, cues_size);
@@ -2041,8 +2322,10 @@
void Segment::MoveCuesBeforeClusters() {
const uint64 current_cue_size = cues_.Size();
- uint64 cue_size = current_cue_size;
- for (int32 i = 0; i < cues_.cue_entries_size(); i++)
+ uint64 cue_size = 0;
+ for (int32 i = 0; i < cues_.cue_entries_size(); ++i)
+ cue_size += cues_.GetCueByIndex(i)->Size();
+ for (int32 i = 0; i < cues_.cue_entries_size(); ++i)
MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size);
// Adjust the Seek Entry to reflect the change in position
@@ -2164,12 +2447,24 @@
if (size_position_ == -1)
return false;
- const int64 pos = writer_header_->Position();
const int64 segment_size = MaxOffset();
-
if (segment_size < 1)
return false;
+ const int64 pos = writer_header_->Position();
+ UpdateDocTypeVersion();
+ if (doc_type_version_ != doc_type_version_written_) {
+ if (writer_header_->Position(0))
+ return false;
+
+ if (!WriteEbmlHeader(writer_header_, doc_type_version_))
+ return false;
+ if (writer_header_->Position() != ebml_header_size_)
+ return false;
+
+ doc_type_version_written_ = doc_type_version_;
+ }
+
if (writer_header_->Position(size_position_))
return false;
@@ -2210,6 +2505,8 @@
Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
+Tag* Segment::AddTag() { return tags_.AddTag(); }
+
uint64 Segment::AddVideoTrack(int32 width, int32 height, int32 number) {
VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
if (!track)
@@ -2264,128 +2561,73 @@
return track->number();
}
-bool Segment::AddFrame(const uint8* frame, uint64 length, uint64 track_number,
+bool Segment::AddFrame(const uint8* data, uint64 length, uint64 track_number,
uint64 timestamp, bool is_key) {
- if (!frame)
+ if (!data)
return false;
- if (!CheckHeaderInfo())
+ Frame frame;
+ if (!frame.Init(data, length))
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);
-
- 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->AddFrame(frame, length, 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;
+ frame.set_track_number(track_number);
+ frame.set_timestamp(timestamp);
+ frame.set_is_key(is_key);
+ return AddGenericFrame(&frame);
}
-bool Segment::AddFrameWithAdditional(const uint8* frame, uint64 length,
+bool Segment::AddFrameWithAdditional(const uint8* data, uint64 length,
const uint8* additional,
uint64 additional_length, uint64 add_id,
uint64 track_number, uint64 timestamp,
bool is_key) {
- if (frame == NULL || additional == NULL)
+ if (!data || !additional)
return false;
- if (!CheckHeaderInfo())
+ Frame frame;
+ if (!frame.Init(data, length) ||
+ !frame.AddAdditionalData(additional, additional_length, add_id)) {
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);
-
- if (!QueueFrame(new_frame))
- return false;
-
- return true;
}
+ frame.set_track_number(track_number);
+ frame.set_timestamp(timestamp);
+ frame.set_is_key(is_key);
+ return AddGenericFrame(&frame);
+}
- if (!DoNewClusterProcessing(track_number, timestamp, is_key))
+bool Segment::AddFrameWithDiscardPadding(const uint8* data, uint64 length,
+ int64 discard_padding,
+ uint64 track_number, uint64 timestamp,
+ bool is_key) {
+ if (!data)
return false;
- if (cluster_list_size_ < 1)
+ Frame frame;
+ if (!frame.Init(data, length))
return false;
+ frame.set_discard_padding(discard_padding);
+ frame.set_track_number(track_number);
+ frame.set_timestamp(timestamp);
+ frame.set_is_key(is_key);
+ return AddGenericFrame(&frame);
+}
- Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
- if (cluster == NULL)
+bool Segment::AddMetadata(const uint8* data, uint64 length, uint64 track_number,
+ uint64 timestamp_ns, uint64 duration_ns) {
+ if (!data)
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))
+ Frame frame;
+ if (!frame.Init(data, length))
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;
+ frame.set_track_number(track_number);
+ frame.set_timestamp(timestamp_ns);
+ frame.set_duration(duration_ns);
+ frame.set_is_key(true); // All metadata blocks are keyframes.
+ return AddGenericFrame(&frame);
}
-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)
+bool Segment::AddGenericFrame(const Frame* frame) {
+ if (!frame)
return false;
if (!CheckHeaderInfo())
@@ -2392,29 +2634,31 @@
return false;
// Check for non-monotonically increasing timestamps.
- if (timestamp < last_timestamp_)
+ if (frame->timestamp() < last_timestamp_)
return false;
+ // Check if the track number is valid.
+ if (!tracks_.GetTrackByNumber(frame->track_number()))
+ return false;
+
+ if (frame->discard_padding() != 0)
+ doc_type_version_ = 4;
+
// 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_) {
+ if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) &&
+ !force_new_cluster_) {
Frame* const new_frame = new (std::nothrow) Frame();
- if (new_frame == NULL || !new_frame->Init(frame, length))
+ if (!new_frame || !new_frame->CopyFrom(*frame))
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;
+ return QueueFrame(new_frame);
}
- if (!DoNewClusterProcessing(track_number, timestamp, is_key))
+ if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
+ frame->is_key())) {
return false;
+ }
if (cluster_list_size_ < 1)
return false;
@@ -2423,84 +2667,38 @@
if (!cluster)
return false;
- const uint64 timecode_scale = segment_info_.timecode_scale();
- const uint64 abs_timecode = timestamp / timecode_scale;
+ // 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->AddFrameWithDiscardPadding(
- frame, length, discard_padding, track_number, abs_timecode, is_key)) {
+ if (!cluster->AddFrame(frame))
return false;
- }
- if (new_cuepoint_ && cues_track_ == track_number) {
- if (!AddCuePoint(timestamp, cues_track_))
+ if (new_cuepoint_ && cues_track_ == frame->track_number()) {
+ if (!AddCuePoint(frame->timestamp(), cues_track_))
return false;
}
- if (timestamp > last_timestamp_)
- last_timestamp_ = timestamp;
+ last_timestamp_ = frame->timestamp();
+ last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
+ last_block_duration_ = frame->duration();
- return true;
-}
+ if (frame_created)
+ delete frame;
-bool Segment::AddMetadata(const uint8* frame, uint64 length,
- uint64 track_number, uint64 timestamp_ns,
- uint64 duration_ns) {
- if (!frame)
- return false;
-
- if (!CheckHeaderInfo())
- return false;
-
- // Check for non-monotonically increasing timestamps.
- if (timestamp_ns < last_timestamp_)
- return false;
-
- if (!DoNewClusterProcessing(track_number, timestamp_ns, true))
- 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_ns / timecode_scale;
- const uint64 duration_timecode = duration_ns / timecode_scale;
-
- if (!cluster->AddMetadata(frame, length, track_number, abs_timecode,
- duration_timecode))
- return false;
-
- if (timestamp_ns > last_timestamp_)
- last_timestamp_ = timestamp_ns;
-
return true;
}
-bool Segment::AddGenericFrame(const Frame* frame) {
- last_block_duration_ = frame->duration();
- if (!tracks_.TrackIsAudio(frame->track_number()) &&
- !tracks_.TrackIsVideo(frame->track_number()) && frame->duration() > 0) {
- return AddMetadata(frame->frame(), frame->length(), frame->track_number(),
- frame->timestamp(), frame->duration());
- } else if (frame->additional() && frame->additional_length() > 0) {
- return AddFrameWithAdditional(
- frame->frame(), frame->length(), frame->additional(),
- frame->additional_length(), frame->add_id(), 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(), frame->track_number(),
- frame->timestamp(), frame->is_key());
- }
-}
-
void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
bool Segment::SetChunking(bool chunking, const char* filename) {
@@ -2598,9 +2796,13 @@
}
bool Segment::WriteSegmentHeader() {
+ UpdateDocTypeVersion();
+
// TODO(fgalligan): Support more than one segment.
- if (!WriteEbmlHeader(writer_header_))
+ if (!WriteEbmlHeader(writer_header_, doc_type_version_))
return false;
+ doc_type_version_written_ = doc_type_version_;
+ ebml_header_size_ = static_cast<int32>(writer_header_->Position());
// Write "unknown" (-1) as segment size value. If mode is kFile, Segment
// will write over duration when the file is finalized.
@@ -2645,6 +2847,13 @@
return false;
}
+ if (tags_.Count() > 0) {
+ if (!seek_head_.AddSeekEntry(kMkvTags, MaxOffset()))
+ return false;
+ if (!tags_.Write(writer_header_))
+ return false;
+ }
+
if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
if (!chunk_writer_header_)
return false;
@@ -2740,7 +2949,7 @@
const int32 new_capacity =
(cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
Cluster** const clusters =
- new (std::nothrow) Cluster* [new_capacity]; // NOLINT
+ new (std::nothrow) Cluster*[new_capacity]; // NOLINT
if (!clusters)
return false;
@@ -2796,7 +3005,8 @@
Cluster*& cluster = cluster_list_[cluster_list_size_];
const int64 offset = MaxOffset();
- cluster = new (std::nothrow) Cluster(cluster_timecode, offset); // NOLINT
+ cluster = new (std::nothrow) Cluster(cluster_timecode, // NOLINT
+ offset, segment_info_.timecode_scale());
if (!cluster)
return false;
@@ -2873,6 +3083,19 @@
return true;
}
+void Segment::UpdateDocTypeVersion() {
+ for (uint32 index = 0; index < tracks_.track_entries_size(); ++index) {
+ const Track* track = tracks_.GetTrackByIndex(index);
+ if (track == NULL)
+ break;
+ if ((track->codec_delay() || track->seek_pre_roll()) &&
+ doc_type_version_ < 4) {
+ doc_type_version_ = 4;
+ break;
+ }
+ }
+}
+
bool Segment::UpdateChunkName(const char* ext, char** name) const {
if (!name || !ext)
return false;
@@ -2932,7 +3155,7 @@
if (new_capacity < 1)
return false;
- Frame** const frames = new (std::nothrow) Frame* [new_capacity]; // NOLINT
+ Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
if (!frames)
return false;
@@ -2962,35 +3185,25 @@
if (!cluster)
return -1;
- const uint64 timecode_scale = segment_info_.timecode_scale();
-
for (int32 i = 0; i < frames_size_; ++i) {
Frame*& frame = frames_[i];
- const uint64 frame_timestamp = frame->timestamp(); // ns
- const uint64 frame_timecode = frame_timestamp / timecode_scale;
+ // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the
+ // places where |doc_type_version_| needs to be updated.
+ if (frame->discard_padding() != 0)
+ doc_type_version_ = 4;
+ if (!cluster->AddFrame(frame))
+ 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())) {
+ if (new_cuepoint_ && cues_track_ == frame->track_number()) {
+ if (!AddCuePoint(frame->timestamp(), cues_track_))
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_))
- return -1;
+ if (frame->timestamp() > last_timestamp_) {
+ last_timestamp_ = frame->timestamp();
+ last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
}
- if (frame_timestamp > last_timestamp_)
- last_timestamp_ = frame_timestamp;
-
delete frame;
frame = NULL;
}
@@ -3013,7 +3226,6 @@
if (!cluster)
return false;
- const uint64 timecode_scale = segment_info_.timecode_scale();
int32 shift_left = 0;
// TODO(fgalligan): Change this to use the durations of frames instead of
@@ -3025,33 +3237,22 @@
break;
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 (frame_prev->discard_padding() != 0)
+ doc_type_version_ = 4;
+ if (!cluster->AddFrame(frame_prev))
+ 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_))
+ if (!AddCuePoint(frame_prev->timestamp(), cues_track_))
return false;
}
++shift_left;
- if (frame_timestamp > last_timestamp_)
- last_timestamp_ = frame_timestamp;
+ if (frame_prev->timestamp() > last_timestamp_) {
+ last_timestamp_ = frame_prev->timestamp();
+ last_track_timestamp_[frame_prev->track_number() - 1] =
+ frame_prev->timestamp();
+ }
delete frame_prev;
}
--- a/third_party/libwebm/mkvmuxer.hpp
+++ b/third_party/libwebm/mkvmuxer.hpp
@@ -23,6 +23,8 @@
class MkvWriter;
class Segment;
+const uint64 kMaxTrackNumber = 126;
+
///////////////////////////////////////////////////////////////
// Interface used by the mkvmuxer to write out the Mkv data.
class IMkvWriter {
@@ -57,6 +59,10 @@
// Writes out the EBML header for a WebM file. This function must be called
// before any other libwebm writing functions are called.
+bool WriteEbmlHeader(IMkvWriter* writer, uint64 doc_type_version);
+
+// Deprecated. Writes out EBML header with doc_type_version as
+// kDefaultDocTypeVersion. Exists for backward compatibility.
bool WriteEbmlHeader(IMkvWriter* writer);
// Copies in Chunk from source to destination between the given byte positions
@@ -70,6 +76,10 @@
Frame();
~Frame();
+ // Sets this frame's contents based on |frame|. Returns true on success. On
+ // failure, this frame's existing contents may be lost.
+ bool CopyFrom(const Frame& frame);
+
// Copies |frame| data into |frame_|. Returns true on success.
bool Init(const uint8* frame, uint64 length);
@@ -76,6 +86,13 @@
// Copies |additional| data into |additional_|. Returns true on success.
bool AddAdditionalData(const uint8* additional, uint64 length, uint64 add_id);
+ // Returns true if the frame has valid parameters.
+ bool IsValid() const;
+
+ // Returns true if the frame can be written as a SimpleBlock based on current
+ // parameters.
+ bool CanBeSimpleBlock() const;
+
uint64 add_id() const { return add_id_; }
const uint8* additional() const { return additional_; }
uint64 additional_length() const { return additional_length_; }
@@ -89,10 +106,15 @@
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) {
+ void set_discard_padding(int64 discard_padding) {
discard_padding_ = discard_padding;
}
- uint64 discard_padding() const { return 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.
@@ -124,6 +146,14 @@
// 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);
};
///////////////////////////////////////////////////////////////
@@ -422,6 +452,16 @@
uint64 display_height() const { return display_height_; }
void set_display_width(uint64 width) { display_width_ = width; }
uint64 display_width() const { return display_width_; }
+
+ void set_crop_left(uint64 crop_left) { crop_left_ = crop_left; }
+ uint64 crop_left() const { return crop_left_; }
+ void set_crop_right(uint64 crop_right) { crop_right_ = crop_right; }
+ uint64 crop_right() const { return crop_right_; }
+ void set_crop_top(uint64 crop_top) { crop_top_ = crop_top; }
+ uint64 crop_top() const { return crop_top_; }
+ void set_crop_bottom(uint64 crop_bottom) { crop_bottom_ = crop_bottom; }
+ uint64 crop_bottom() const { return crop_bottom_; }
+
void set_frame_rate(double frame_rate) { frame_rate_ = frame_rate; }
double frame_rate() const { return frame_rate_; }
void set_height(uint64 height) { height_ = height; }
@@ -438,6 +478,10 @@
// Video track element names.
uint64 display_height_;
uint64 display_width_;
+ uint64 crop_left_;
+ uint64 crop_right_;
+ uint64 crop_top_;
+ uint64 crop_bottom_;
double frame_rate_;
uint64 height_;
uint64 stereo_mode_;
@@ -693,6 +737,112 @@
};
///////////////////////////////////////////////////////////////
+// Tag element
+//
+class Tag {
+ public:
+ bool add_simple_tag(const char* tag_name, const char* tag_string);
+
+ private:
+ // Tags calls Clear and the destructor of Tag
+ friend class Tags;
+
+ // For storage of simple tags
+ class SimpleTag {
+ public:
+ // Establish representation invariant for new SimpleTag object.
+ void Init();
+
+ // Reclaim resources, in anticipation of destruction.
+ void Clear();
+
+ // Copies the title to the |tag_name_| member. Returns false on
+ // error.
+ bool set_tag_name(const char* tag_name);
+
+ // Copies the language to the |tag_string_| member. Returns false
+ // on error.
+ bool set_tag_string(const char* tag_string);
+
+ // If |writer| is non-NULL, serialize the SimpleTag sub-element of
+ // the Atom into the stream. Returns the SimpleTag element size on
+ // success, 0 if error.
+ uint64 Write(IMkvWriter* writer) const;
+
+ private:
+ char* tag_name_;
+ char* tag_string_;
+ };
+
+ Tag();
+ ~Tag();
+
+ // Copies this Tag object to a different one. This is used when
+ // expanding a plain array of Tag objects (see Tags).
+ void ShallowCopy(Tag* dst) const;
+
+ // Reclaim resources used by this Tag object, pending its
+ // destruction.
+ void Clear();
+
+ // If there is no storage remaining on the |simple_tags_| array for a
+ // new display object, creates a new, longer array and copies the
+ // existing SimpleTag objects to the new array. Returns false if the
+ // array cannot be expanded.
+ bool ExpandSimpleTagsArray();
+
+ // If |writer| is non-NULL, serialize the Tag sub-element into the
+ // stream. Returns the total size of the element on success, 0 if
+ // error.
+ uint64 Write(IMkvWriter* writer) const;
+
+ // The Atom element can contain multiple SimpleTag sub-elements
+ SimpleTag* simple_tags_;
+
+ // The physical length (total size) of the |simple_tags_| array.
+ int simple_tags_size_;
+
+ // The logical length (number of active elements) on the |simple_tags_|
+ // array.
+ int simple_tags_count_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tag);
+};
+
+///////////////////////////////////////////////////////////////
+// Tags element
+//
+class Tags {
+ public:
+ Tags();
+ ~Tags();
+
+ Tag* AddTag();
+
+ // Returns the number of tags that have been added.
+ int Count() const;
+
+ // Output the Tags element to the writer. Returns true on success.
+ bool Write(IMkvWriter* writer) const;
+
+ private:
+ // Expands the tags_ array if there is not enough space to contain
+ // another tag object. Returns true on success.
+ bool ExpandTagsArray();
+
+ // Total length of the tags_ array.
+ int tags_size_;
+
+ // Number of active tags on the tags_ array.
+ int tags_count_;
+
+ // Array for storage of tag objects.
+ Tag* tags_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tags);
+};
+
+///////////////////////////////////////////////////////////////
// Cluster element
//
// Notes:
@@ -699,18 +849,22 @@
// |Init| must be called before any other method in this class.
class Cluster {
public:
- Cluster(uint64 timecode, int64 cues_pos);
- ~Cluster();
-
// |timecode| is the absolute timecode of the cluster. |cues_pos| is the
// position for the cluster within the segment that should be written in
- // the cues element.
+ // the cues element. |timecode_scale| is the timecode scale of the segment.
+ Cluster(uint64 timecode, int64 cues_pos, uint64 timecode_scale);
+ ~Cluster();
+
bool Init(IMkvWriter* ptr_writer);
// Adds a frame to be output in the file. The frame is written out through
// |writer_| if successful. Returns true on success.
+ bool AddFrame(const Frame* frame);
+
+ // 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
+ // data: Pointer to the data
// length: Length of the data
// track_number: Track to add the data to. Value returned by Add track
// functions. The range of allowed values is [1, 126].
@@ -717,7 +871,7 @@
// 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 AddFrame(const uint8* frame, uint64 length, uint64 track_number,
+ bool AddFrame(const uint8* data, uint64 length, uint64 track_number,
uint64 timecode, // timecode units (absolute)
bool is_key);
@@ -724,7 +878,7 @@
// 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
+ // data: Pointer to the data
// length: Length of the data
// additional: Pointer to the additional data
// additional_length: Length of the additional data
@@ -734,7 +888,7 @@
// 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,
+ bool AddFrameWithAdditional(const uint8* data, uint64 length,
const uint8* additional, uint64 additional_length,
uint64 add_id, uint64 track_number,
uint64 abs_timecode, bool is_key);
@@ -742,7 +896,7 @@
// 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.
+ // data: 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
@@ -750,7 +904,7 @@
// 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,
+ bool AddFrameWithDiscardPadding(const uint8* data, uint64 length,
int64 discard_padding, uint64 track_number,
uint64 abs_timecode, bool is_key);
@@ -757,7 +911,7 @@
// Writes a frame of metadata to the output medium; returns true on
// success.
// Inputs:
- // frame: Pointer to the data
+ // data: Pointer to the data
// length: Length of the data
// track_number: Track to add the data to. Value returned by Add track
// functions. The range of allowed values is [1, 126].
@@ -768,7 +922,7 @@
// The metadata frame is written as a block group, with a duration
// sub-element but no reference time sub-elements (indicating that
// it is considered a keyframe, per Matroska semantics).
- bool AddMetadata(const uint8* frame, uint64 length, uint64 track_number,
+ bool AddMetadata(const uint8* data, uint64 length, uint64 track_number,
uint64 timecode, uint64 duration);
// Increments the size of the cluster's data in bytes.
@@ -781,76 +935,30 @@
// Returns the size in bytes for the entire Cluster element.
uint64 Size() 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;
+
int64 size_position() const { return size_position_; }
int32 blocks_added() const { return blocks_added_; }
uint64 payload_size() const { return payload_size_; }
int64 position_for_cues() const { return position_for_cues_; }
uint64 timecode() const { return timecode_; }
+ uint64 timecode_scale() const { return timecode_scale_; }
private:
- // Signature that matches either of WriteSimpleBlock or WriteMetadataBlock
- // in the muxer utilities package.
- typedef uint64 (*WriteBlock)(IMkvWriter* writer, const uint8* data,
- uint64 length, uint64 track_number,
- 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);
-
- // 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
+ // cluster header has been written. Used by |DoWriteFrame*|. Returns true
// when successful.
- template <typename Type>
- bool PreWriteBlock(Type* write_function);
+ bool PreWriteBlock();
- // Utility method used by the |DoWriteBlock*| methods that handles the book
+ // Utility method used by the |DoWriteFrame*| 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;
+ // Does some verification and calls WriteFrame.
+ bool DoWriteFrame(const Frame* const frame);
- // 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, uint64 track_number,
- uint64 absolute_timecode, 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);
-
- // 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();
@@ -875,6 +983,9 @@
// The absolute timecode of the cluster.
const uint64 timecode_;
+ // The timecode scale of the Segment containing the cluster.
+ const uint64 timecode_scale_;
+
// Pointer to the writer object. Not owned by this class.
IMkvWriter* writer_;
@@ -996,6 +1107,7 @@
kBeforeClusters = 0x1 // Position Cues before Clusters
};
+ const static uint32 kDefaultDocTypeVersion = 2;
const static uint64 kDefaultMaxClusterDuration = 30000000000ULL;
Segment();
@@ -1023,6 +1135,11 @@
// populate its fields via the Chapter member functions.
Chapter* AddChapter();
+ // Adds an empty tag to the tags of this segment. Returns
+ // non-NULL on success. After adding the tag, the caller should
+ // populate its fields via the Tag member functions.
+ Tag* AddTag();
+
// Adds a cue point to the Cues element. |timestamp| is the time in
// nanoseconds of the cue's time. |track| is the Track of the Cue. This
// function must be called after AddFrame to calculate the correct
@@ -1031,19 +1148,19 @@
// Adds a frame to be output in the file. Returns true on success.
// Inputs:
- // frame: Pointer to the data
+ // data: Pointer to the data
// length: Length of the data
// track_number: Track to add the data to. Value returned by Add track
// functions.
// timestamp: Timestamp of the frame in nanoseconds from 0.
// is_key: Flag telling whether or not this frame is a key frame.
- bool AddFrame(const uint8* frame, uint64 length, uint64 track_number,
+ bool AddFrame(const uint8* data, uint64 length, uint64 track_number,
uint64 timestamp_ns, bool is_key);
// Writes a frame of metadata to the output medium; returns true on
// success.
// Inputs:
- // frame: Pointer to the data
+ // data: Pointer to the data
// length: Length of the data
// track_number: Track to add the data to. Value returned by Add track
// functions.
@@ -1054,13 +1171,13 @@
// The metadata frame is written as a block group, with a duration
// sub-element but no reference time sub-elements (indicating that
// it is considered a keyframe, per Matroska semantics).
- bool AddMetadata(const uint8* frame, uint64 length, uint64 track_number,
+ bool AddMetadata(const uint8* data, uint64 length, uint64 track_number,
uint64 timestamp_ns, uint64 duration_ns);
// Writes a frame with additional data to the output medium; returns true on
// success.
// Inputs:
- // frame: Pointer to the data.
+ // data: Pointer to the data.
// length: Length of the data.
// additional: Pointer to additional data.
// additional_length: Length of additional data.
@@ -1070,7 +1187,7 @@
// timestamp: Absolute timestamp of the frame, expressed in nanosecond
// units.
// is_key: Flag telling whether or not this frame is a key frame.
- bool AddFrameWithAdditional(const uint8* frame, uint64 length,
+ bool AddFrameWithAdditional(const uint8* data, uint64 length,
const uint8* additional, uint64 additional_length,
uint64 add_id, uint64 track_number,
uint64 timestamp, bool is_key);
@@ -1078,7 +1195,7 @@
// Writes a frame with DiscardPadding to the output medium; returns true on
// success.
// Inputs:
- // frame: Pointer to the data.
+ // data: 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
@@ -1086,7 +1203,7 @@
// 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,
+ bool AddFrameWithDiscardPadding(const uint8* data, uint64 length,
int64 discard_padding, uint64 track_number,
uint64 timestamp, bool is_key);
@@ -1177,6 +1294,9 @@
// Cues elements.
bool CheckHeaderInfo();
+ // Sets |doc_type_version_| based on the current element requirements.
+ void UpdateDocTypeVersion();
+
// Sets |name| according to how many chunks have been written. |ext| is the
// file extension. |name| must be deleted by the calling app. Returns true
// on success.
@@ -1233,7 +1353,7 @@
// diff - indicates the difference in size of the Cues element that needs to
// accounted for.
// index - index in the list of Cues which is currently being adjusted.
- // cue_size - size of the Cues element.
+ // cue_size - sum of size of all the CuePoint elements.
void MoveCuesBeforeClustersHelper(uint64 diff, int index, uint64* cue_size);
// Seeds the random number generator used to make UIDs.
@@ -1245,6 +1365,7 @@
SegmentInfo segment_info_;
Tracks tracks_;
Chapters chapters_;
+ Tags tags_;
// Number of chunks written.
int chunk_count_;
@@ -1316,6 +1437,9 @@
// 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.
@@ -1337,11 +1461,22 @@
// Flag whether or not the muxer should output a Cues element.
bool output_cues_;
+ // The size of the EBML header, used to validate the header if
+ // WriteEbmlHeader() is called more than once.
+ int32 ebml_header_size_;
+
// The file position of the segment's payload.
int64 payload_pos_;
// The file position of the element's size.
int64 size_position_;
+
+ // Current DocTypeVersion (|doc_type_version_|) and that written in
+ // WriteSegmentHeader().
+ // WriteEbmlHeader() will be called from Finalize() if |doc_type_version_|
+ // differs from |doc_type_version_written_|.
+ uint32 doc_type_version_;
+ uint32 doc_type_version_written_;
// Pointer to the writer objects. Not owned by this class.
IMkvWriter* writer_cluster_;
--- a/third_party/libwebm/mkvmuxerutil.cpp
+++ b/third_party/libwebm/mkvmuxerutil.cpp
@@ -15,18 +15,19 @@
#include <cassert>
#include <cmath>
#include <cstdio>
-#ifdef _MSC_VER
-#define _CRT_RAND_S
-#endif
#include <cstdlib>
#include <cstring>
#include <ctime>
-
#include <new>
#include "mkvwriter.hpp"
#include "webmids.hpp"
+#ifdef _MSC_VER
+// Disable MSVC warnings that suggest making code non-portable.
+#pragma warning(disable : 4996)
+#endif
+
namespace mkvmuxer {
namespace {
@@ -34,6 +35,144 @@
// Date elements are always 8 octets in size.
const int kDateElementSize = 8;
+uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode,
+ uint64 timecode_scale) {
+ uint64 block_additional_elem_size = 0;
+ uint64 block_addid_elem_size = 0;
+ uint64 block_more_payload_size = 0;
+ uint64 block_more_elem_size = 0;
+ uint64 block_additions_payload_size = 0;
+ uint64 block_additions_elem_size = 0;
+ if (frame->additional()) {
+ block_additional_elem_size = EbmlElementSize(
+ kMkvBlockAdditional, frame->additional(), frame->additional_length());
+ block_addid_elem_size = EbmlElementSize(kMkvBlockAddID, frame->add_id());
+
+ block_more_payload_size =
+ block_addid_elem_size + block_additional_elem_size;
+ block_more_elem_size =
+ EbmlMasterElementSize(kMkvBlockMore, block_more_payload_size) +
+ block_more_payload_size;
+ block_additions_payload_size = block_more_elem_size;
+ block_additions_elem_size =
+ EbmlMasterElementSize(kMkvBlockAdditions,
+ block_additions_payload_size) +
+ block_additions_payload_size;
+ }
+
+ uint64 discard_padding_elem_size = 0;
+ if (frame->discard_padding() != 0) {
+ discard_padding_elem_size =
+ 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);
+
+ const uint64 block_payload_size = 4 + frame->length();
+ const uint64 block_elem_size =
+ EbmlMasterElementSize(kMkvBlock, block_payload_size) + block_payload_size;
+
+ const uint64 block_group_payload_size =
+ block_elem_size + block_additions_elem_size + block_duration_elem_size +
+ discard_padding_elem_size + reference_block_elem_size;
+
+ if (!WriteEbmlMasterElement(writer, kMkvBlockGroup,
+ block_group_payload_size)) {
+ return 0;
+ }
+
+ if (!WriteEbmlMasterElement(writer, kMkvBlock, block_payload_size))
+ return 0;
+
+ if (WriteUInt(writer, frame->track_number()))
+ return 0;
+
+ if (SerializeInt(writer, timecode, 2))
+ return 0;
+
+ // For a Block, flags is always 0.
+ if (SerializeInt(writer, 0, 1))
+ return 0;
+
+ if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
+ return 0;
+
+ if (frame->additional()) {
+ if (!WriteEbmlMasterElement(writer, kMkvBlockAdditions,
+ block_additions_payload_size)) {
+ return 0;
+ }
+
+ if (!WriteEbmlMasterElement(writer, kMkvBlockMore, block_more_payload_size))
+ return 0;
+
+ if (!WriteEbmlElement(writer, kMkvBlockAddID, frame->add_id()))
+ return 0;
+
+ if (!WriteEbmlElement(writer, kMkvBlockAdditional, frame->additional(),
+ frame->additional_length())) {
+ return 0;
+ }
+ }
+
+ if (frame->discard_padding() != 0 &&
+ !WriteEbmlElement(writer, kMkvDiscardPadding, frame->discard_padding())) {
+ return false;
+ }
+
+ if (!frame->is_key() &&
+ !WriteEbmlElement(writer, kMkvReferenceBlock,
+ reference_block_timestamp)) {
+ return false;
+ }
+
+ if (duration > 0 && !WriteEbmlElement(writer, kMkvBlockDuration, duration)) {
+ return false;
+ }
+ return EbmlMasterElementSize(kMkvBlockGroup, block_group_payload_size) +
+ block_group_payload_size;
+}
+
+uint64 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame,
+ int64 timecode) {
+ if (WriteID(writer, kMkvSimpleBlock))
+ return 0;
+
+ const int32 size = static_cast<int32>(frame->length()) + 4;
+ if (WriteUInt(writer, size))
+ return 0;
+
+ if (WriteUInt(writer, static_cast<uint64>(frame->track_number())))
+ return 0;
+
+ if (SerializeInt(writer, timecode, 2))
+ return 0;
+
+ uint64 flags = 0;
+ if (frame->is_key())
+ flags |= 0x80;
+
+ if (SerializeInt(writer, flags, 1))
+ return 0;
+
+ if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
+ return 0;
+
+ return GetUIntSize(kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 +
+ frame->length();
+}
+
} // namespace
int32 GetCodedUIntSize(uint64 value) {
@@ -72,6 +211,13 @@
return 8;
}
+int32 GetIntSize(int64 value) {
+ // Doubling the requested value ensures positive values with their high bit
+ // set are written with 0-padding to avoid flipping the signedness.
+ const uint64 v = (value < 0) ? value ^ -1LL : value;
+ return GetUIntSize(2 * v);
+}
+
uint64 EbmlMasterElementSize(uint64 type, uint64 value) {
// Size of EBML ID
int32 ebml_size = GetUIntSize(type);
@@ -83,7 +229,16 @@
}
uint64 EbmlElementSize(uint64 type, int64 value) {
- return EbmlElementSize(type, static_cast<uint64>(value));
+ // Size of EBML ID
+ int32 ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += GetIntSize(value);
+
+ // Size of Datasize
+ ebml_size++;
+
+ return ebml_size;
}
uint64 EbmlElementSize(uint64 type, uint64 value) {
@@ -144,7 +299,7 @@
return ebml_size;
}
-uint64 EbmlDateElementSize(uint64 type, int64 value) {
+uint64 EbmlDateElementSize(uint64 type) {
// Size of EBML ID
uint64 ebml_size = GetUIntSize(type);
@@ -289,6 +444,23 @@
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<int32>(size)))
+ return false;
+
+ return true;
+}
+
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) {
if (!writer)
return false;
@@ -355,291 +527,27 @@
return true;
}
-uint64 WriteSimpleBlock(IMkvWriter* writer, const uint8* data, uint64 length,
- uint64 track_number, int64 timecode, uint64 is_key) {
- if (!writer)
- return false;
+uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
+ Cluster* cluster) {
+ if (!writer || !frame || !frame->IsValid() || !cluster ||
+ !cluster->timecode_scale())
+ return 0;
- if (!data || length < 1)
- return false;
-
- // Here we only permit track number values to be no greater than
- // 126, which the largest value we can store having a Matroska
- // integer representation of only 1 byte.
-
- if (track_number < 1 || track_number > 126)
- return false;
-
- // Technically the timestamp for a block can be less than the
- // timestamp for the cluster itself (remember that block timestamp
+ // Technically the timecode for a block can be less than the
+ // timecode for the cluster itself (remember that block timecode
// is a signed, 16-bit integer). However, as a simplification we
- // only permit non-negative cluster-relative timestamps for blocks.
-
- if (timecode < 0 || timecode > kMaxBlockTimecode)
- return false;
-
- if (WriteID(writer, kMkvSimpleBlock))
+ // only permit non-negative cluster-relative timecodes for blocks.
+ const int64 relative_timecode = cluster->GetRelativeTimecode(
+ frame->timestamp() / cluster->timecode_scale());
+ if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode)
return 0;
- const int32 size = static_cast<int32>(length) + 4;
- if (WriteUInt(writer, size))
- return 0;
-
- if (WriteUInt(writer, static_cast<uint64>(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;
-
- const uint64 element_size =
- GetUIntSize(kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + length;
-
- return element_size;
+ return frame->CanBeSimpleBlock() ?
+ WriteSimpleBlock(writer, frame, relative_timecode) :
+ WriteBlock(writer, frame, relative_timecode,
+ cluster->timecode_scale());
}
-// We must write the metadata (key)frame as a BlockGroup element,
-// because we need to specify a duration for the frame. The
-// BlockGroup element comprises the frame itself and its duration,
-// and is laid out as follows:
-//
-// BlockGroup tag
-// BlockGroup size
-// Block tag
-// Block size
-// (the frame is the block payload)
-// Duration tag
-// Duration size
-// (duration payload)
-//
-uint64 WriteMetadataBlock(IMkvWriter* writer, const uint8* data, uint64 length,
- uint64 track_number, int64 timecode,
- uint64 duration) {
- // We don't backtrack when writing to the stream, so we must
- // pre-compute the BlockGroup size, by summing the sizes of each
- // sub-element (the block and the duration).
-
- // We use a single byte for the track number of the block, which
- // means the block header is exactly 4 bytes.
-
- // TODO(matthewjheaney): use EbmlMasterElementSize and WriteEbmlMasterElement
-
- const uint64 block_payload_size = 4 + length;
- const int32 block_size = GetCodedUIntSize(block_payload_size);
- const uint64 block_elem_size = 1 + block_size + block_payload_size;
-
- const int32 duration_payload_size = GetUIntSize(duration);
- const int32 duration_size = GetCodedUIntSize(duration_payload_size);
- const uint64 duration_elem_size = 1 + duration_size + duration_payload_size;
-
- const uint64 blockg_payload_size = block_elem_size + duration_elem_size;
- const int32 blockg_size = GetCodedUIntSize(blockg_payload_size);
- const uint64 blockg_elem_size = 1 + blockg_size + blockg_payload_size;
-
- if (WriteID(writer, kMkvBlockGroup)) // 1-byte ID size
- return 0;
-
- if (WriteUInt(writer, blockg_payload_size))
- return 0;
-
- // Write Block element
-
- if (WriteID(writer, kMkvBlock)) // 1-byte ID size
- return 0;
-
- if (WriteUInt(writer, block_payload_size))
- return 0;
-
- // Byte 1 of 4
-
- if (WriteUInt(writer, track_number))
- return 0;
-
- // Bytes 2 & 3 of 4
-
- if (SerializeInt(writer, timecode, 2))
- return 0;
-
- // Byte 4 of 4
-
- const uint64 flags = 0;
-
- if (SerializeInt(writer, flags, 1))
- return 0;
-
- // Now write the actual frame (of metadata)
-
- if (writer->Write(data, static_cast<uint32>(length)))
- return 0;
-
- // Write Duration element
-
- if (WriteID(writer, kMkvBlockDuration)) // 1-byte ID size
- return 0;
-
- if (WriteUInt(writer, duration_payload_size))
- return 0;
-
- if (SerializeInt(writer, duration, duration_payload_size))
- return 0;
-
- // Note that we don't write a reference time as part of the block
- // group; no reference time(s) indicates that this block is a
- // keyframe. (Unlike the case for a SimpleBlock element, the header
- // bits of the Block sub-element of a BlockGroup element do not
- // indicate keyframe status. The keyframe status is inferred from
- // the absence of reference time sub-elements.)
-
- return blockg_elem_size;
-}
-
-// Writes a WebM BlockGroup with BlockAdditional data. 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(kMkvBlockAdditions, 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;
-}
-
-// 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;
@@ -698,10 +606,7 @@
// TODO(fgalligan): Move random number generation to platform specific code.
#ifdef _MSC_VER
(void)seed;
- unsigned int random_value;
- const errno_t e = rand_s(&random_value);
- (void)e;
- const int32 nn = random_value;
+ const int32 nn = rand();
#elif __ANDROID__
int32 temp_num = 1;
int fd = open("/dev/urandom", O_RDONLY);
--- a/third_party/libwebm/mkvmuxerutil.hpp
+++ b/third_party/libwebm/mkvmuxerutil.hpp
@@ -9,6 +9,7 @@
#ifndef MKVMUXERUTIL_HPP
#define MKVMUXERUTIL_HPP
+#include "mkvmuxer.hpp"
#include "mkvmuxertypes.hpp"
namespace mkvmuxer {
@@ -23,6 +24,7 @@
// Returns the size in bytes of the element.
int32 GetUIntSize(uint64 value);
+int32 GetIntSize(int64 value);
int32 GetCodedUIntSize(uint64 value);
uint64 EbmlMasterElementSize(uint64 type, uint64 value);
uint64 EbmlElementSize(uint64 type, int64 value);
@@ -30,7 +32,7 @@
uint64 EbmlElementSize(uint64 type, float value);
uint64 EbmlElementSize(uint64 type, const char* value);
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size);
-uint64 EbmlDateElementSize(uint64 type, int64 value);
+uint64 EbmlDateElementSize(uint64 type);
// Creates an EBML coded number from |value| and writes it out. The size of
// the coded number is determined by the value of |value|. |value| must not
@@ -51,6 +53,7 @@
// 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,
@@ -57,67 +60,10 @@
uint64 size);
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value);
-// Output an Mkv Simple Block.
-// Inputs:
-// data: Pointer to the data.
-// length: Length of the data.
-// 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 WriteSimpleBlock(IMkvWriter* writer, const uint8* data, uint64 length,
- uint64 track_number, int64 timecode, uint64 is_key);
-
-// Output a metadata keyframe, using a Block Group element.
-// Inputs:
-// data: Pointer to the (meta)data.
-// length: Length of the (meta)data.
-// track_number: Track to add the data to. Value returned by Add track
-// functions. Only values in the range [1, 126] are
-// permitted.
-// timecode Timecode of frame, relative to cluster timecode. Only
-// values in the range [0, 2^15) are permitted.
-// duration_timecode Duration of frame, using timecode units.
-uint64 WriteMetadataBlock(IMkvWriter* writer, const uint8* data, uint64 length,
- uint64 track_number, 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 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 Mkv Frame. It decides the correct element to write (Block vs
+// SimpleBlock) based on the parameters of the Frame.
+uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
+ Cluster* cluster);
// 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
--- a/third_party/libwebm/mkvparser.cpp
+++ b/third_party/libwebm/mkvparser.cpp
@@ -23,7 +23,7 @@
major = 1;
minor = 0;
build = 0;
- revision = 28;
+ revision = 29;
}
long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len) {
@@ -130,6 +130,8 @@
return 0; // success
}
+// TODO(vigneshv): This function assumes that unsigned values never have their
+// high bit set.
long long mkvparser::UnserializeUInt(IMkvReader* pReader, long long pos,
long long size) {
assert(pReader);
@@ -605,6 +607,7 @@
m_pTracks(NULL),
m_pCues(NULL),
m_pChapters(NULL),
+ m_pTags(NULL),
m_clusters(NULL),
m_clusterCount(0),
m_clusterPreloadCount(0),
@@ -629,6 +632,7 @@
delete m_pInfo;
delete m_pCues;
delete m_pChapters;
+ delete m_pTags;
delete m_pSeekHead;
}
@@ -907,6 +911,19 @@
if (status)
return status;
}
+ } else if (id == 0x0254C367) { // Tags ID
+ if (m_pTags == NULL) {
+ m_pTags = new (std::nothrow)
+ Tags(this, pos, size, element_start, element_size);
+
+ if (m_pTags == NULL)
+ return -1;
+
+ const long status = m_pTags->Parse();
+
+ if (status)
+ return status;
+ }
}
m_pos = pos + size; // consume payload
@@ -1025,23 +1042,11 @@
const long long unknown_size = (1LL << (7 * len)) - 1;
-#if 0 // we must handle this to support live webm
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID; //TODO: allow this
-#endif
-
if ((segment_stop >= 0) && (size != unknown_size) &&
((pos + size) > segment_stop)) {
return E_FILE_FORMAT_INVALID;
}
-#if 0 // commented-out, to support incremental cluster parsing
- len = static_cast<long>(size);
-
- if ((pos + size) > avail)
- return E_BUFFER_NOT_FULL;
-#endif
-
if (id == 0x0C53BB6B) { // Cues ID
if (size == unknown_size)
return E_FILE_FORMAT_INVALID; // TODO: liberalize
@@ -1157,11 +1162,9 @@
}
if (status == 0) { // no entries found
- if (cluster_size < 0)
- return E_FILE_FORMAT_INVALID; // TODO: handle this
+ if (cluster_size >= 0)
+ pos += cluster_size;
- pos += cluster_size;
-
if ((total >= 0) && (pos >= total)) {
m_pos = total;
return 1; // no more clusters
@@ -1201,149 +1204,9 @@
return 0; // partial success, since we have a new cluster
-// status == 0 means "no block entries found"
-
-// pos designates start of payload
-// m_pos has NOT been adjusted yet (in case we need to come back here)
-
-#if 0
-
- if (cluster_size < 0) { //unknown size
- const long long payload_pos = pos; //absolute pos of cluster payload
-
- for (;;) { //determine cluster size
- if ((total >= 0) && (pos >= total))
- break;
-
- if ((segment_stop >= 0) && (pos >= segment_stop))
- break; //no more clusters
-
- //Read ID
-
- if ((pos + 1) > avail)
- {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if (result > 0) //weird
- return E_BUFFER_NOT_FULL;
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long idpos = pos;
- const long long id = ReadUInt(m_pReader, idpos, len);
-
- if (id < 0) //error (or underflow)
- return static_cast<long>(id);
-
- //This is the distinguished set of ID's we use to determine
- //that we have exhausted the sub-element's inside the cluster
- //whose ID we parsed earlier.
-
- if (id == 0x0F43B675) //Cluster ID
- break;
-
- if (id == 0x0C53BB6B) //Cues ID
- break;
-
- switch (id)
- {
- case 0x20: //BlockGroup
- case 0x23: //Simple Block
- case 0x67: //TimeCode
- case 0x2B: //PrevSize
- break;
-
- default:
- assert(false);
- break;
- }
-
- pos += len; //consume ID (of sub-element)
-
- //Read Size
-
- if ((pos + 1) > avail)
- {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if (result > 0) //weird
- return E_BUFFER_NOT_FULL;
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- pos += len; //consume size field of element
-
- //pos now points to start of sub-element's payload
-
- if (size == 0) //weird
- continue;
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID; //not allowed for sub-elements
-
- if ((segment_stop >= 0) && ((pos + size) > segment_stop)) //weird
- return E_FILE_FORMAT_INVALID;
-
- pos += size; //consume payload of sub-element
- assert((segment_stop < 0) || (pos <= segment_stop));
- } //determine cluster size
-
- cluster_size = pos - payload_pos;
- assert(cluster_size >= 0);
-
- pos = payload_pos; //reset and re-parse original cluster
- }
-
- if (m_clusterPreloadCount > 0)
- {
- assert(idx < m_clusterSize);
-
- Cluster* const pCluster = m_clusters[idx];
- assert(pCluster);
- assert(pCluster->m_index < 0);
-
- const long long off = pCluster->GetPosition();
- assert(off >= 0);
-
- if (off == cluster_off) //preloaded already
- return E_FILE_FORMAT_INVALID; //subtle
- }
-
- m_pos = pos + cluster_size; //consume payload
- assert((segment_stop < 0) || (m_pos <= segment_stop));
-
- return 2; //try to find another cluster
-
-#endif
+ // status == 0 means "no block entries found"
+ // pos designates start of payload
+ // m_pos has NOT been adjusted yet (in case we need to come back here)
}
long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
@@ -1350,157 +1213,6 @@
assert(m_pos < 0);
assert(m_pUnknownSize);
-#if 0
- assert(m_pUnknownSize->GetElementSize() < 0); //TODO: verify this
-
- const long long element_start = m_pUnknownSize->m_element_start;
-
- pos = -m_pos;
- assert(pos > element_start);
-
- //We have already consumed the (cluster) ID and size fields.
- //We just need to consume the blocks and other sub-elements
- //of this cluster, until we discover the boundary.
-
- long long total, avail;
-
- long status = m_pReader->Length(&total, &avail);
-
- if (status < 0) //error
- return status;
-
- assert((total < 0) || (avail <= total));
-
- const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
-
- long long element_size = -1;
-
- for (;;) { //determine cluster size
- if ((total >= 0) && (pos >= total))
- {
- element_size = total - element_start;
- assert(element_size > 0);
-
- break;
- }
-
- if ((segment_stop >= 0) && (pos >= segment_stop))
- {
- element_size = segment_stop - element_start;
- assert(element_size > 0);
-
- break;
- }
-
- //Read ID
-
- if ((pos + 1) > avail)
- {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if (result > 0) //weird
- return E_BUFFER_NOT_FULL;
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long idpos = pos;
- const long long id = ReadUInt(m_pReader, idpos, len);
-
- if (id < 0) //error (or underflow)
- return static_cast<long>(id);
-
- //This is the distinguished set of ID's we use to determine
- //that we have exhausted the sub-element's inside the cluster
- //whose ID we parsed earlier.
-
- if ((id == 0x0F43B675) || (id == 0x0C53BB6B)) { //Cluster ID or Cues ID
- element_size = pos - element_start;
- assert(element_size > 0);
-
- break;
- }
-
-#ifdef _DEBUG
- switch (id)
- {
- case 0x20: //BlockGroup
- case 0x23: //Simple Block
- case 0x67: //TimeCode
- case 0x2B: //PrevSize
- break;
-
- default:
- assert(false);
- break;
- }
-#endif
-
- pos += len; //consume ID (of sub-element)
-
- //Read Size
-
- if ((pos + 1) > avail)
- {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if (result > 0) //weird
- return E_BUFFER_NOT_FULL;
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- pos += len; //consume size field of element
-
- //pos now points to start of sub-element's payload
-
- if (size == 0) //weird
- continue;
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID; //not allowed for sub-elements
-
- if ((segment_stop >= 0) && ((pos + size) > segment_stop)) //weird
- return E_FILE_FORMAT_INVALID;
-
- pos += size; //consume payload of sub-element
- assert((segment_stop < 0) || (pos <= segment_stop));
- } //determine cluster size
-
- assert(element_size >= 0);
-
- m_pos = element_start + element_size;
- m_pUnknownSize = 0;
-
- return 2; //continue parsing
-#else
const long status = m_pUnknownSize->Parse(pos, len);
if (status < 0) // error or underflow
@@ -1522,7 +1234,6 @@
m_pUnknownSize = 0;
return 2; // continue parsing
-#endif
}
void Segment::AppendCluster(Cluster* pCluster) {
@@ -1540,7 +1251,7 @@
if (count >= size) {
const long n = (size <= 0) ? 2048 : 2 * size;
- Cluster** const qq = new Cluster* [n];
+ Cluster** const qq = new Cluster*[n];
Cluster** q = qq;
Cluster** p = m_clusters;
@@ -1594,7 +1305,7 @@
if (count >= size) {
const long n = (size <= 0) ? 2048 : 2 * size;
- Cluster** const qq = new Cluster* [n];
+ Cluster** const qq = new Cluster*[n];
Cluster** q = qq;
Cluster** p = m_clusters;
@@ -1794,55 +1505,6 @@
return m_void_elements + idx;
}
-#if 0
-void Segment::ParseCues(long long off)
-{
- if (m_pCues)
- return;
-
- //odbgstream os;
- //os << "Segment::ParseCues (begin)" << endl;
-
- long long pos = m_start + off;
- const long long element_start = pos;
- const long long stop = m_start + m_size;
-
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0);
- assert((pos + len) <= stop);
-
- const long long idpos = pos;
-
- const long long id = ReadUInt(m_pReader, idpos, len);
- assert(id == 0x0C53BB6B); //Cues ID
-
- pos += len; //consume ID
- assert(pos < stop);
-
- //Read Size
-
- result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0);
- assert((pos + len) <= stop);
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size >= 0);
-
- pos += len; //consume length of size of element
- assert((pos + size) <= stop);
-
- const long long element_size = size + pos - element_start;
-
- //Pos now points to start of payload
-
- m_pCues = new Cues(this, pos, size, element_start, element_size);
- assert(m_pCues); //TODO
-
- //os << "Segment::ParseCues (end)" << endl;
-}
-#else
long Segment::ParseCues(long long off, long long& pos, long& len) {
if (m_pCues)
return 0; // success
@@ -1957,67 +1619,7 @@
return 0; // success
}
-#endif
-#if 0
-void Segment::ParseSeekEntry(
- long long start,
- long long size_)
-{
- long long pos = start;
-
- const long long stop = start + size_;
-
- long len;
-
- const long long seekIdId = ReadUInt(m_pReader, pos, len);
- //seekIdId;
- assert(seekIdId == 0x13AB); //SeekID ID
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long seekIdSize = ReadUInt(m_pReader, pos, len);
- assert(seekIdSize >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume size
-
- const long long seekId = ReadUInt(m_pReader, pos, len); //payload
- assert(seekId >= 0);
- assert(len == seekIdSize);
- assert((pos + len) <= stop);
-
- pos += seekIdSize; //consume payload
-
- const long long seekPosId = ReadUInt(m_pReader, pos, len);
- //seekPosId;
- assert(seekPosId == 0x13AC); //SeekPos ID
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long seekPosSize = ReadUInt(m_pReader, pos, len);
- assert(seekPosSize >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume size
- assert((pos + seekPosSize) <= stop);
-
- const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);
- assert(seekOff >= 0);
- assert(seekOff < m_size);
-
- pos += seekPosSize; //consume payload
- assert(pos == stop);
-
- const long long seekPos = m_start + seekOff;
- assert(seekPos < (m_start + m_size));
-
- if (seekId == 0x0C53BB6B) //Cues ID
- ParseCues(seekOff);
-}
-#else
bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
Entry* pEntry) {
if (size_ <= 0)
@@ -2110,7 +1712,6 @@
return true;
}
-#endif
Cues::Cues(Segment* pSegment, long long start_, long long size_,
long long element_start, long long element_size)
@@ -2152,9 +1753,9 @@
return (m_pos >= stop);
}
-void Cues::Init() const {
+bool Cues::Init() const {
if (m_cue_points)
- return;
+ return true;
assert(m_count == 0);
assert(m_preload_count == 0);
@@ -2172,24 +1773,28 @@
long len;
const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); // TODO
- assert((pos + len) <= stop);
+ if (id < 0 || (pos + len) > stop) {
+ return false;
+ }
pos += len; // consume ID
const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
+ if (size < 0 || (pos + len > stop)) {
+ return false;
+ }
pos += len; // consume Size field
- assert((pos + size) <= stop);
+ if (pos + size > stop) {
+ return false;
+ }
if (id == 0x3B) // CuePoint ID
PreloadCuePoint(cue_points_size, idpos);
- pos += size; // consume payload
- assert(pos <= stop);
+ pos += size; // skip payload
}
+ return true;
}
void Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
@@ -2198,7 +1803,7 @@
if (m_preload_count >= cue_points_size) {
const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
- CuePoint** const qq = new CuePoint* [n];
+ CuePoint** const qq = new CuePoint*[n];
CuePoint** q = qq; // beginning of target
CuePoint** p = m_cue_points; // beginning of source
@@ -2226,7 +1831,10 @@
if (m_pos >= stop)
return false; // nothing else to do
- Init();
+ if (!Init()) {
+ m_pos = stop;
+ return false;
+ }
IMkvReader* const pReader = m_pSegment->m_pReader;
@@ -2263,7 +1871,10 @@
if (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos))
return false;
- pCP->Load(pReader);
+ if (!pCP->Load(pReader)) {
+ m_pos = stop;
+ return false;
+ }
++m_count;
--m_preload_count;
@@ -2282,62 +1893,6 @@
assert(time_ns >= 0);
assert(pTrack);
-#if 0
- LoadCuePoint(); //establish invariant
-
- assert(m_cue_points);
- assert(m_count > 0);
-
- CuePoint** const ii = m_cue_points;
- CuePoint** i = ii;
-
- CuePoint** const jj = ii + m_count + m_preload_count;
- CuePoint** j = jj;
-
- pCP = *i;
- assert(pCP);
-
- if (time_ns <= pCP->GetTime(m_pSegment))
- {
- pTP = pCP->Find(pTrack);
- return (pTP != NULL);
- }
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- while (i < j)
- {
- //INVARIANT:
- //[ii, i) <= time_ns
- //[i, j) ?
- //[j, jj) > time_ns
-
- CuePoint** const k = i + (j - i) / 2;
- assert(k < jj);
-
- CuePoint* const pCP = *k;
- assert(pCP);
-
- pCP->Load(pReader);
-
- const long long t = pCP->GetTime(m_pSegment);
-
- if (t <= time_ns)
- i = k + 1;
- else
- j = k;
-
- assert(i <= j);
- }
-
- assert(i == j);
- assert(i <= jj);
- assert(i > ii);
-
- pCP = *--i;
- assert(pCP);
- assert(pCP->GetTime(m_pSegment) <= time_ns);
-#else
if (m_cue_points == NULL)
return false;
@@ -2387,7 +1942,6 @@
pCP = *--i;
assert(pCP);
assert(pCP->GetTime(m_pSegment) <= time_ns);
-#endif
// TODO: here and elsewhere, it's probably not correct to search
// for the cue point with this time, and then search for a matching
@@ -2401,65 +1955,6 @@
return (pTP != NULL);
}
-#if 0
-bool Cues::FindNext(
- long long time_ns,
- const Track* pTrack,
- const CuePoint*& pCP,
- const CuePoint::TrackPosition*& pTP) const
-{
- pCP = 0;
- pTP = 0;
-
- if (m_count == 0)
- return false;
-
- assert(m_cue_points);
-
- const CuePoint* const* const ii = m_cue_points;
- const CuePoint* const* i = ii;
-
- const CuePoint* const* const jj = ii + m_count;
- const CuePoint* const* j = jj;
-
- while (i < j)
- {
- //INVARIANT:
- //[ii, i) <= time_ns
- //[i, j) ?
- //[j, jj) > time_ns
-
- const CuePoint* const* const k = i + (j - i) / 2;
- assert(k < jj);
-
- pCP = *k;
- assert(pCP);
-
- const long long t = pCP->GetTime(m_pSegment);
-
- if (t <= time_ns)
- i = k + 1;
- else
- j = k;
-
- assert(i <= j);
- }
-
- assert(i == j);
- assert(i <= jj);
-
- if (i >= jj) //time_ns is greater than max cue point
- return false;
-
- pCP = *i;
- assert(pCP);
- assert(pCP->GetTime(m_pSegment) > time_ns);
-
- pTP = pCP->Find(pTrack);
- return (pTP != NULL);
-}
-#endif
-
const CuePoint* Cues::GetFirst() const {
if (m_cue_points == NULL)
return NULL;
@@ -2467,15 +1962,6 @@
if (m_count == 0)
return NULL;
-#if 0
- LoadCuePoint(); //init cues
-
- const size_t count = m_count + m_preload_count;
-
- if (count == 0) //weird
- return NULL;
-#endif
-
CuePoint* const* const pp = m_cue_points;
assert(pp);
@@ -2493,25 +1979,6 @@
if (m_count <= 0)
return NULL;
-#if 0
- LoadCuePoint(); //init cues
-
- const size_t count = m_count + m_preload_count;
-
- if (count == 0) //weird
- return NULL;
-
- const size_t index = count - 1;
-
- CuePoint* const* const pp = m_cue_points;
- assert(pp);
-
- CuePoint* const pCP = pp[index];
- assert(pCP);
-
- pCP->Load(m_pSegment->m_pReader);
- assert(pCP->GetTimeCode() >= 0);
-#else
const long index = m_count - 1;
CuePoint* const* const pp = m_cue_points;
@@ -2520,7 +1987,6 @@
CuePoint* const pCP = pp[index];
assert(pCP);
assert(pCP->GetTimeCode() >= 0);
-#endif
return pCP;
}
@@ -2533,26 +1999,6 @@
assert(m_cue_points);
assert(m_count >= 1);
-#if 0
- const size_t count = m_count + m_preload_count;
-
- size_t index = pCurr->m_index;
- assert(index < count);
-
- CuePoint* const* const pp = m_cue_points;
- assert(pp);
- assert(pp[index] == pCurr);
-
- ++index;
-
- if (index >= count)
- return NULL;
-
- CuePoint* const pNext = pp[index];
- assert(pNext);
-
- pNext->Load(m_pSegment->m_pReader);
-#else
long index = pCurr->m_index;
assert(index < m_count);
@@ -2568,7 +2014,6 @@
CuePoint* const pNext = pp[index];
assert(pNext);
assert(pNext->GetTimeCode() >= 0);
-#endif
return pNext;
}
@@ -2705,12 +2150,12 @@
CuePoint::~CuePoint() { delete[] m_track_positions; }
-void CuePoint::Load(IMkvReader* pReader) {
+bool CuePoint::Load(IMkvReader* pReader) {
// odbgstream os;
// os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
if (m_timecode >= 0) // already loaded
- return;
+ return true;
assert(m_track_positions == NULL);
assert(m_track_positions_count == 0);
@@ -2726,7 +2171,7 @@
const long long id = ReadUInt(pReader, pos_, len);
assert(id == 0x3B); // CuePoint ID
if (id != 0x3B)
- return;
+ return false;
pos_ += len; // consume ID
@@ -2749,17 +2194,21 @@
long len;
const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); // TODO
- assert((pos + len) <= stop);
+ if ((id < 0) || (pos + len > stop)) {
+ return false;
+ }
pos += len; // consume ID
const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
+ if ((size < 0) || (pos + len > stop)) {
+ return false;
+ }
pos += len; // consume Size field
- assert((pos + size) <= stop);
+ if ((pos + size) > stop) {
+ return false;
+ }
if (id == 0x33) // CueTime ID
m_timecode = UnserializeUInt(pReader, pos, size);
@@ -2768,11 +2217,11 @@
++m_track_positions_count;
pos += size; // consume payload
- assert(pos <= stop);
}
- assert(m_timecode >= 0);
- assert(m_track_positions_count > 0);
+ if (m_timecode < 0 || m_track_positions_count <= 0) {
+ return false;
+ }
// os << "CuePoint::Load(cont'd): idpos=" << idpos
// << " timecode=" << m_timecode
@@ -2789,7 +2238,7 @@
long len;
const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); // TODO
+ assert(id >= 0);
assert((pos + len) <= stop);
pos += len; // consume ID
@@ -2803,7 +2252,9 @@
if (id == 0x37) { // CueTrackPosition(s) ID
TrackPosition& tp = *p++;
- tp.Parse(pReader, pos, size);
+ if (!tp.Parse(pReader, pos, size)) {
+ return false;
+ }
}
pos += size; // consume payload
@@ -2814,9 +2265,11 @@
m_element_start = element_start;
m_element_size = element_size;
+
+ return true;
}
-void CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
+bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
long long size_) {
const long long stop = start_ + size_;
long long pos = start_;
@@ -2829,17 +2282,21 @@
long len;
const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); // TODO
- assert((pos + len) <= stop);
+ if ((id < 0) || ((pos + len) > stop)) {
+ return false;
+ }
pos += len; // consume ID
const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
+ if ((size < 0) || ((pos + len) > stop)) {
+ return false;
+ }
pos += len; // consume Size field
- assert((pos + size) <= stop);
+ if ((pos + size) > stop) {
+ return false;
+ }
if (id == 0x77) // CueTrack ID
m_track = UnserializeUInt(pReader, pos, size);
@@ -2851,12 +2308,13 @@
m_block = UnserializeUInt(pReader, pos, size);
pos += size; // consume payload
- assert(pos <= stop);
}
- assert(m_pos >= 0);
- assert(m_track > 0);
- // assert(m_block > 0);
+ if ((m_pos < 0) || (m_track <= 0)) {
+ return false;
+ }
+
+ return true;
}
const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
@@ -2894,20 +2352,6 @@
return time;
}
-#if 0
-long long Segment::Unparsed() const
-{
- if (m_size < 0)
- return LLONG_MAX;
-
- const long long stop = m_start + m_size;
-
- const long long result = stop - m_pos;
- assert(result >= 0);
-
- return result;
-}
-#else
bool Segment::DoneParsing() const {
if (m_size < 0) {
long long total, avail;
@@ -2927,7 +2371,6 @@
return (m_pos >= stop);
}
-#endif
const Cluster* Segment::GetFirst() const {
if ((m_clusters == NULL) || (m_clusterCount <= 0))
@@ -3395,15 +2838,7 @@
continue;
}
-#if 0 // this is commented-out to support incremental cluster parsing
- len = static_cast<long>(size);
-
- if (element_stop > avail)
- return E_BUFFER_NOT_FULL;
-#endif
-
// We have a cluster.
-
off_next = idoff;
if (size != unknown_size)
@@ -3647,188 +3082,11 @@
return pCluster;
}
-#if 0
-const BlockEntry* Segment::Seek(
- long long time_ns,
- const Track* pTrack) const
-{
- assert(pTrack);
-
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return pTrack->GetEOS();
-
- Cluster** const i = m_clusters;
- assert(i);
-
- {
- Cluster* const pCluster = *i;
- assert(pCluster);
- assert(pCluster->m_index == 0); //m_clusterCount > 0
- assert(pCluster->m_pSegment == this);
-
- if (time_ns <= pCluster->GetTime())
- return pCluster->GetEntry(pTrack);
- }
-
- Cluster** const j = i + m_clusterCount;
-
- if (pTrack->GetType() == 2) { //audio
- //TODO: we could decide to use cues for this, as we do for video.
- //But we only use it for video because looking around for a keyframe
- //can get expensive. Audio doesn't require anything special so a
- //straight cluster search is good enough (we assume).
-
- Cluster** lo = i;
- Cluster** hi = j;
-
- while (lo < hi)
- {
- //INVARIANT:
- //[i, lo) <= time_ns
- //[lo, hi) ?
- //[hi, j) > time_ns
-
- Cluster** const mid = lo + (hi - lo) / 2;
- assert(mid < hi);
-
- Cluster* const pCluster = *mid;
- assert(pCluster);
- assert(pCluster->m_index == long(mid - m_clusters));
- assert(pCluster->m_pSegment == this);
-
- const long long t = pCluster->GetTime();
-
- if (t <= time_ns)
- lo = mid + 1;
- else
- hi = mid;
-
- assert(lo <= hi);
- }
-
- assert(lo == hi);
- assert(lo > i);
- assert(lo <= j);
-
- while (lo > i)
- {
- Cluster* const pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- const BlockEntry* const pBE = pCluster->GetEntry(pTrack);
-
- if ((pBE != 0) && !pBE->EOS())
- return pBE;
-
- //landed on empty cluster (no entries)
- }
-
- return pTrack->GetEOS(); //weird
- }
-
- assert(pTrack->GetType() == 1); //video
-
- Cluster** lo = i;
- Cluster** hi = j;
-
- while (lo < hi)
- {
- //INVARIANT:
- //[i, lo) <= time_ns
- //[lo, hi) ?
- //[hi, j) > time_ns
-
- Cluster** const mid = lo + (hi - lo) / 2;
- assert(mid < hi);
-
- Cluster* const pCluster = *mid;
- assert(pCluster);
-
- const long long t = pCluster->GetTime();
-
- if (t <= time_ns)
- lo = mid + 1;
- else
- hi = mid;
-
- assert(lo <= hi);
- }
-
- assert(lo == hi);
- assert(lo > i);
- assert(lo <= j);
-
- Cluster* pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- {
- const BlockEntry* const pBE = pCluster->GetEntry(pTrack, time_ns);
-
- if ((pBE != 0) && !pBE->EOS()) //found a keyframe
- return pBE;
- }
-
- const VideoTrack* const pVideo = static_cast<const VideoTrack*>(pTrack);
-
- while (lo != i)
- {
- pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- const BlockEntry* const pBlockEntry = pCluster->GetMaxKey(pVideo);
-
- if ((pBlockEntry != 0) && !pBlockEntry->EOS())
- return pBlockEntry;
- }
-
- //weird: we're on the first cluster, but no keyframe found
- //should never happen but we must return something anyway
-
- return pTrack->GetEOS();
-}
-#endif
-
-#if 0
-bool Segment::SearchCues(
- long long time_ns,
- Track* pTrack,
- Cluster*& pCluster,
- const BlockEntry*& pBlockEntry,
- const CuePoint*& pCP,
- const CuePoint::TrackPosition*& pTP)
-{
- if (pTrack->GetType() != 1) //not video
- return false; //TODO: for now, just handle video stream
-
- if (m_pCues == NULL)
- return false;
-
- if (!m_pCues->Find(time_ns, pTrack, pCP, pTP))
- return false; //weird
-
- assert(pCP);
- assert(pTP);
- assert(pTP->m_track == pTrack->GetNumber());
-
- //We have the cue point and track position we want,
- //so we now need to search for the cluster having
- //the indicated position.
-
- return GetCluster(pCP, pTP, pCluster, pBlockEntry);
-}
-#endif
-
const Tracks* Segment::GetTracks() const { return m_pTracks; }
-
const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
-
const Cues* Segment::GetCues() const { return m_pCues; }
-
const Chapters* Segment::GetChapters() const { return m_pChapters; }
-
+const Tags* Segment::GetTags() const { return m_pTags; }
const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
long long Segment::GetDuration() const {
@@ -3853,6 +3111,7 @@
Edition& e = m_editions[--m_editions_count];
e.Clear();
}
+ delete[] m_editions;
}
long Chapters::Parse() {
@@ -4128,12 +3387,13 @@
if (status < 0) // error
return status;
} else if (id == 0x33C4) { // UID ID
- const long long val = UnserializeUInt(pReader, pos, size);
+ long long val;
+ status = UnserializeInt(pReader, pos, size, val);
- if (val < 0) // error
- return static_cast<long>(val);
+ if (status < 0) // error
+ return status;
- m_uid = val;
+ m_uid = static_cast<unsigned long long>(val);
} else if (id == 0x11) { // TimeStart ID
const long long val = UnserializeUInt(pReader, pos, size);
@@ -4292,6 +3552,277 @@
return 0;
}
+Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size,
+ long long element_start, long long element_size)
+ : m_pSegment(pSegment),
+ m_start(payload_start),
+ m_size(payload_size),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ m_tags(NULL),
+ m_tags_size(0),
+ m_tags_count(0) {}
+
+Tags::~Tags() {
+ while (m_tags_count > 0) {
+ Tag& t = m_tags[--m_tags_count];
+ t.Clear();
+ }
+ delete[] m_tags;
+}
+
+long Tags::Parse() {
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = m_start; // payload start
+ const long long stop = pos + m_size; // payload stop
+
+ while (pos < stop) {
+ long long id, size;
+
+ long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0)
+ return status;
+
+ if (size == 0) // 0 length tag, read another
+ continue;
+
+ if (id == 0x3373) { // Tag ID
+ status = ParseTag(pos, size);
+
+ if (status < 0)
+ return status;
+ }
+
+ pos += size;
+ assert(pos <= stop);
+ if (pos > stop)
+ return -1;
+ }
+
+ assert(pos == stop);
+ if (pos != stop)
+ return -1;
+
+ return 0;
+}
+
+int Tags::GetTagCount() const { return m_tags_count; }
+
+const Tags::Tag* Tags::GetTag(int idx) const {
+ if (idx < 0)
+ return NULL;
+
+ if (idx >= m_tags_count)
+ return NULL;
+
+ return m_tags + idx;
+}
+
+bool Tags::ExpandTagsArray() {
+ if (m_tags_size > m_tags_count)
+ return true; // nothing else to do
+
+ const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
+
+ Tag* const tags = new (std::nothrow) Tag[size];
+
+ if (tags == NULL)
+ return false;
+
+ for (int idx = 0; idx < m_tags_count; ++idx) {
+ m_tags[idx].ShallowCopy(tags[idx]);
+ }
+
+ delete[] m_tags;
+ m_tags = tags;
+
+ m_tags_size = size;
+ return true;
+}
+
+long Tags::ParseTag(long long pos, long long size) {
+ if (!ExpandTagsArray())
+ return -1;
+
+ Tag& t = m_tags[m_tags_count++];
+ t.Init();
+
+ return t.Parse(m_pSegment->m_pReader, pos, size);
+}
+
+Tags::Tag::Tag() {}
+
+Tags::Tag::~Tag() {}
+
+int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
+
+const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
+ if (index < 0)
+ return NULL;
+
+ if (index >= m_simple_tags_count)
+ return NULL;
+
+ return m_simple_tags + index;
+}
+
+void Tags::Tag::Init() {
+ m_simple_tags = NULL;
+ m_simple_tags_size = 0;
+ m_simple_tags_count = 0;
+}
+
+void Tags::Tag::ShallowCopy(Tag& rhs) const {
+ rhs.m_simple_tags = m_simple_tags;
+ rhs.m_simple_tags_size = m_simple_tags_size;
+ rhs.m_simple_tags_count = m_simple_tags_count;
+}
+
+void Tags::Tag::Clear() {
+ while (m_simple_tags_count > 0) {
+ SimpleTag& d = m_simple_tags[--m_simple_tags_count];
+ d.Clear();
+ }
+
+ delete[] m_simple_tags;
+ m_simple_tags = NULL;
+
+ m_simple_tags_size = 0;
+}
+
+long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) {
+ const long long stop = pos + size;
+
+ while (pos < stop) {
+ long long id, size;
+
+ long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0)
+ return status;
+
+ if (size == 0) // 0 length tag, read another
+ continue;
+
+ if (id == 0x27C8) { // SimpleTag ID
+ status = ParseSimpleTag(pReader, pos, size);
+
+ if (status < 0)
+ return status;
+ }
+
+ pos += size;
+ assert(pos <= stop);
+ if (pos > stop)
+ return -1;
+ }
+
+ assert(pos == stop);
+ if (pos != stop)
+ return -1;
+ return 0;
+}
+
+long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
+ long long size) {
+ if (!ExpandSimpleTagsArray())
+ return -1;
+
+ SimpleTag& st = m_simple_tags[m_simple_tags_count++];
+ st.Init();
+
+ return st.Parse(pReader, pos, size);
+}
+
+bool Tags::Tag::ExpandSimpleTagsArray() {
+ if (m_simple_tags_size > m_simple_tags_count)
+ return true; // nothing else to do
+
+ const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
+
+ SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
+
+ if (displays == NULL)
+ return false;
+
+ for (int idx = 0; idx < m_simple_tags_count; ++idx) {
+ m_simple_tags[idx].ShallowCopy(displays[idx]);
+ }
+
+ delete[] m_simple_tags;
+ m_simple_tags = displays;
+
+ m_simple_tags_size = size;
+ return true;
+}
+
+Tags::SimpleTag::SimpleTag() {}
+
+Tags::SimpleTag::~SimpleTag() {}
+
+const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
+
+const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
+
+void Tags::SimpleTag::Init() {
+ m_tag_name = NULL;
+ m_tag_string = NULL;
+}
+
+void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
+ rhs.m_tag_name = m_tag_name;
+ rhs.m_tag_string = m_tag_string;
+}
+
+void Tags::SimpleTag::Clear() {
+ delete[] m_tag_name;
+ m_tag_name = NULL;
+
+ delete[] m_tag_string;
+ m_tag_string = NULL;
+}
+
+long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos,
+ long long size) {
+ const long long stop = pos + size;
+
+ while (pos < stop) {
+ long long id, size;
+
+ long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (size == 0) // weird
+ continue;
+
+ if (id == 0x5A3) { // TagName ID
+ status = UnserializeString(pReader, pos, size, m_tag_name);
+
+ if (status)
+ return status;
+ } else if (id == 0x487) { // TagString ID
+ status = UnserializeString(pReader, pos, size, m_tag_string);
+
+ if (status)
+ return status;
+ }
+
+ pos += size;
+ assert(pos <= stop);
+ if (pos > stop)
+ return -1;
+ }
+
+ assert(pos == stop);
+ if (pos != stop)
+ return -1;
+ return 0;
+}
+
SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
long long element_start, long long element_size)
: m_pSegment(pSegment),
@@ -4458,7 +3989,7 @@
}
const ContentEncoding::ContentCompression*
-ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
+ ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
const ptrdiff_t count = compression_entries_end_ - compression_entries_;
assert(count >= 0);
@@ -4554,7 +4085,7 @@
if (compression_count > 0) {
compression_entries_ =
- new (std::nothrow) ContentCompression* [compression_count];
+ new (std::nothrow) ContentCompression*[compression_count];
if (!compression_entries_)
return -1;
compression_entries_end_ = compression_entries_;
@@ -4562,7 +4093,7 @@
if (encryption_count > 0) {
encryption_entries_ =
- new (std::nothrow) ContentEncryption* [encryption_count];
+ new (std::nothrow) ContentEncryption*[encryption_count];
if (!encryption_entries_) {
delete[] compression_entries_;
return -1;
@@ -4703,7 +4234,7 @@
return E_FILE_FORMAT_INVALID;
} else if (id == 0x7E2) {
// ContentEncKeyID
- delete[] encryption -> key_id;
+ delete[] encryption->key_id;
encryption->key_id = NULL;
encryption->key_id_len = 0;
@@ -4727,7 +4258,7 @@
encryption->key_id_len = buflen;
} else if (id == 0x7E3) {
// ContentSignature
- delete[] encryption -> signature;
+ delete[] encryption->signature;
encryption->signature = NULL;
encryption->signature_len = 0;
@@ -4751,7 +4282,7 @@
encryption->signature_len = buflen;
} else if (id == 0x7E4) {
// ContentSigKeyID
- delete[] encryption -> sig_key_id;
+ delete[] encryption->sig_key_id;
encryption->sig_key_id = NULL;
encryption->sig_key_id_len = 0;
@@ -4991,17 +4522,10 @@
}
if (pCluster->EOS()) {
-#if 0
- if (m_pSegment->Unparsed() <= 0) { //all clusters have been loaded
- pBlockEntry = GetEOS();
- return 1;
- }
-#else
if (m_pSegment->DoneParsing()) {
pBlockEntry = GetEOS();
return 1;
}
-#endif
pBlockEntry = 0;
return E_BUFFER_NOT_FULL;
@@ -5098,18 +4622,10 @@
}
if (pCluster->EOS()) {
-#if 0
- if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded
- {
- pNextEntry = GetEOS();
- return 1;
- }
-#else
if (m_pSegment->DoneParsing()) {
pNextEntry = GetEOS();
return 1;
}
-#endif
// TODO: there is a potential O(n^2) problem here: we tell the
// caller to (pre)load another cluster, which he does, but then he
@@ -5291,7 +4807,7 @@
if (count <= 0)
return -1;
- content_encoding_entries_ = new (std::nothrow) ContentEncoding* [count];
+ content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
if (!content_encoding_entries_)
return -1;
@@ -5350,6 +4866,11 @@
long long width = 0;
long long height = 0;
+ long long display_width = 0;
+ long long display_height = 0;
+ long long display_unit = 0;
+ long long stereo_mode = 0;
+
double rate = 0.0;
IMkvReader* const pReader = pSegment->m_pReader;
@@ -5381,6 +4902,26 @@
if (height <= 0)
return E_FILE_FORMAT_INVALID;
+ } else if (id == 0x14B0) { // display width
+ display_width = UnserializeUInt(pReader, pos, size);
+
+ if (display_width <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == 0x14BA) { // display height
+ display_height = UnserializeUInt(pReader, pos, size);
+
+ if (display_height <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == 0x14B2) { // display unit
+ display_unit = UnserializeUInt(pReader, pos, size);
+
+ if (display_unit < 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == 0x13B8) { // stereo mode
+ stereo_mode = UnserializeUInt(pReader, pos, size);
+
+ if (stereo_mode < 0)
+ return E_FILE_FORMAT_INVALID;
} else if (id == 0x0383E3) { // frame rate
const long status = UnserializeFloat(pReader, pos, size, rate);
@@ -5412,6 +4953,10 @@
pTrack->m_width = width;
pTrack->m_height = height;
+ pTrack->m_display_width = display_width;
+ pTrack->m_display_height = display_height;
+ pTrack->m_display_unit = display_unit;
+ pTrack->m_stereo_mode = stereo_mode;
pTrack->m_rate = rate;
pResult = pTrack;
@@ -5498,16 +5043,7 @@
assert(pCluster);
assert(pCluster->GetTime() <= time_ns);
-#if 0
- //TODO:
- //We need to handle the case when a cluster
- //contains multiple keyframes. Simply returning
- //the largest keyframe on the cluster isn't
- //good enough.
- pResult = pCluster->GetMaxKey(this);
-#else
pResult = pCluster->GetEntry(this, time_ns);
-#endif
if ((pResult != 0) && !pResult->EOS())
return 0;
@@ -5524,6 +5060,18 @@
long long VideoTrack::GetHeight() const { return m_height; }
+long long VideoTrack::GetDisplayWidth() const {
+ return m_display_width > 0 ? m_display_width : GetWidth();
+}
+
+long long VideoTrack::GetDisplayHeight() const {
+ return m_display_height > 0 ? m_display_height : GetHeight();
+}
+
+long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
+
+long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
+
double VideoTrack::GetFrameRate() const { return m_rate; }
AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
@@ -5658,7 +5206,7 @@
if (count <= 0)
return 0; // success
- m_trackEntries = new (std::nothrow) Track* [count];
+ m_trackEntries = new (std::nothrow) Track*[count];
if (m_trackEntries == NULL)
return -1;
@@ -5941,7 +5489,7 @@
if (v.start >= 0)
return E_FILE_FORMAT_INVALID;
- if (e.start >= 0)
+ if (info.type == Track::kMetadata && e.start >= 0)
return E_FILE_FORMAT_INVALID;
info.settings.start = -1;
@@ -6003,25 +5551,6 @@
return m_trackEntries[idx];
}
-#if 0
-long long Cluster::Unparsed() const
-{
- if (m_timecode < 0) //not even partially loaded
- return LLONG_MAX;
-
- assert(m_pos >= m_element_start);
- //assert(m_element_size > m_size);
-
- const long long element_stop = m_element_start + m_element_size;
- assert(m_pos <= element_stop);
-
- const long long result = element_stop - m_pos;
- assert(result >= 0);
-
- return result;
-}
-#endif
-
long Cluster::Load(long long& pos, long& len) const {
assert(m_pSegment);
assert(m_pos >= m_element_start);
@@ -6115,15 +5644,7 @@
cluster_size = size;
}
-// pos points to start of payload
-
-#if 0
- len = static_cast<long>(size_);
-
- if (cluster_stop > avail)
- return E_BUFFER_NOT_FULL;
-#endif
-
+ // pos points to start of payload
long long timecode = -1;
long long new_pos = -1;
bool bBlock = false;
@@ -6495,36 +6016,6 @@
if (track == 0)
return E_FILE_FORMAT_INVALID;
-#if 0
- //TODO(matthewjheaney)
- //This turned out to be too conservative. The problem is that
- //if we see a track header in the tracks element with an unsupported
- //track type, we throw that track header away, so it is not present
- //in the track map. But even though we don't understand the track
- //header, there are still blocks in the cluster with that track
- //number. It was our decision to ignore that track header, so it's
- //up to us to deal with blocks associated with that track -- we
- //cannot simply report an error since technically there's nothing
- //wrong with the file.
- //
- //For now we go ahead and finish the parse, creating a block entry
- //for this block. This is somewhat wasteful, because without a
- //track header there's nothing you can do with the block. What
- //we really need here is a special return value that indicates to
- //the caller that he should ignore this particular block, and
- //continue parsing.
-
- const Tracks* const pTracks = m_pSegment->GetTracks();
- assert(pTracks);
-
- const long tn = static_cast<long>(track);
-
- const Track* const pTrack = pTracks->GetTrackByNumber(tn);
-
- if (pTrack == NULL)
- return E_FILE_FORMAT_INVALID;
-#endif
-
pos += len; // consume track number
if ((pos + 2) > block_stop)
@@ -6679,13 +6170,8 @@
return E_FILE_FORMAT_INVALID;
if (id == 0x35A2) { // DiscardPadding
- result = GetUIntLength(pReader, pos, len);
+ status = UnserializeInt(pReader, pos, size, discard_padding);
- if (result < 0) // error
- return static_cast<long>(result);
-
- status = UnserializeInt(pReader, pos, len, discard_padding);
-
if (status < 0) // error
return status;
}
@@ -6733,36 +6219,6 @@
if (track == 0)
return E_FILE_FORMAT_INVALID;
-#if 0
- //TODO(matthewjheaney)
- //This turned out to be too conservative. The problem is that
- //if we see a track header in the tracks element with an unsupported
- //track type, we throw that track header away, so it is not present
- //in the track map. But even though we don't understand the track
- //header, there are still blocks in the cluster with that track
- //number. It was our decision to ignore that track header, so it's
- //up to us to deal with blocks associated with that track -- we
- //cannot simply report an error since technically there's nothing
- //wrong with the file.
- //
- //For now we go ahead and finish the parse, creating a block entry
- //for this block. This is somewhat wasteful, because without a
- //track header there's nothing you can do with the block. What
- //we really need here is a special return value that indicates to
- //the caller that he should ignore this particular block, and
- //continue parsing.
-
- const Tracks* const pTracks = m_pSegment->GetTracks();
- assert(pTracks);
-
- const long tn = static_cast<long>(track);
-
- const Track* const pTrack = pTracks->GetTrackByNumber(tn);
-
- if (pTrack == NULL)
- return E_FILE_FORMAT_INVALID;
-#endif
-
pos += len; // consume track number
if ((pos + 2) > block_stop)
@@ -6924,68 +6380,6 @@
long long Cluster::GetElementSize() const { return m_element_size; }
-#if 0
-bool Cluster::HasBlockEntries(
- const Segment* pSegment,
- long long off) {
- assert(pSegment);
- assert(off >= 0); //relative to start of segment payload
-
- IMkvReader* const pReader = pSegment->m_pReader;
-
- long long pos = pSegment->m_start + off; //absolute
- long long size;
-
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- (void)id;
- assert(id >= 0);
- assert(id == 0x0F43B675); //Cluster ID
-
- pos += len; //consume id
-
- size = ReadUInt(pReader, pos, len);
- assert(size > 0);
-
- pos += len; //consume size
-
- //pos now points to start of payload
- }
-
- const long long stop = pos + size;
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume size
-
- if (id == 0x20) //BlockGroup ID
- return true;
-
- if (id == 0x23) //SimpleBlock ID
- return true;
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- return false;
-}
-#endif
-
long Cluster::HasBlockEntries(
const Segment* pSegment,
long long off, // relative to start of segment payload
@@ -7269,7 +6663,7 @@
assert(m_entries_size == 0);
m_entries_size = 1024;
- m_entries = new BlockEntry* [m_entries_size];
+ m_entries = new BlockEntry*[m_entries_size];
m_entries_count = 0;
} else {
@@ -7280,7 +6674,7 @@
if (m_entries_count >= m_entries_size) {
const long entries_size = 2 * m_entries_size;
- BlockEntry** const entries = new BlockEntry* [entries_size];
+ BlockEntry** const entries = new BlockEntry*[entries_size];
assert(entries);
BlockEntry** src = m_entries;
@@ -7349,12 +6743,16 @@
bsize = size;
}
} else if (id == 0x1B) { // Duration ID
- assert(size <= 8);
+ if (size > 8)
+ return E_FILE_FORMAT_INVALID;
duration = UnserializeUInt(pReader, pos, size);
- assert(duration >= 0); // TODO
+
+ if (duration < 0)
+ return E_FILE_FORMAT_INVALID;
} else if (id == 0x7B) { // ReferenceBlock
- assert(size <= 8);
+ if (size > 8 || size <= 0)
+ return E_FILE_FORMAT_INVALID;
const long size_ = static_cast<long>(size);
long long time;
@@ -7373,9 +6771,10 @@
pos += size; // consume payload
assert(pos <= stop);
}
+ if (bpos < 0)
+ return E_FILE_FORMAT_INVALID;
assert(pos == stop);
- assert(bpos >= 0);
assert(bsize >= 0);
const long idx = m_entries_count;
@@ -7539,57 +6938,6 @@
if (m_pSegment == NULL) // this is the special EOS cluster
return pTrack->GetEOS();
-#if 0
-
- LoadBlockEntries();
-
- if ((m_entries == NULL) || (m_entries_count <= 0))
- return NULL; //return EOS here?
-
- const BlockEntry* pResult = pTrack->GetEOS();
-
- BlockEntry** i = m_entries;
- assert(i);
-
- BlockEntry** const j = i + m_entries_count;
-
- while (i != j)
- {
- const BlockEntry* const pEntry = *i++;
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() != pTrack->GetNumber())
- continue;
-
- if (pTrack->VetEntry(pEntry))
- {
- if (time_ns < 0) //just want first candidate block
- return pEntry;
-
- const long long ns = pBlock->GetTime(this);
-
- if (ns > time_ns)
- break;
-
- pResult = pEntry;
- }
- else if (time_ns >= 0)
- {
- const long long ns = pBlock->GetTime(this);
-
- if (ns > time_ns)
- break;
- }
- }
-
- return pResult;
-
-#else
-
const BlockEntry* pResult = pTrack->GetEOS();
long index = 0;
@@ -7643,103 +6991,11 @@
++index;
}
-
-#endif
}
const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
const CuePoint::TrackPosition& tp) const {
assert(m_pSegment);
-
-#if 0
-
- LoadBlockEntries();
-
- if (m_entries == NULL)
- return NULL;
-
- const long long count = m_entries_count;
-
- if (count <= 0)
- return NULL;
-
- const long long tc = cp.GetTimeCode();
-
- if ((tp.m_block > 0) && (tp.m_block <= count))
- {
- const size_t block = static_cast<size_t>(tp.m_block);
- const size_t index = block - 1;
-
- const BlockEntry* const pEntry = m_entries[index];
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if ((pBlock->GetTrackNumber() == tp.m_track) &&
- (pBlock->GetTimeCode(this) == tc))
- {
- return pEntry;
- }
- }
-
- const BlockEntry* const* i = m_entries;
- const BlockEntry* const* const j = i + count;
-
- while (i != j)
- {
-#ifdef _DEBUG
- const ptrdiff_t idx = i - m_entries;
- idx;
-#endif
-
- const BlockEntry* const pEntry = *i++;
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() != tp.m_track)
- continue;
-
- const long long tc_ = pBlock->GetTimeCode(this);
- assert(tc_ >= 0);
-
- if (tc_ < tc)
- continue;
-
- if (tc_ > tc)
- return NULL;
-
- const Tracks* const pTracks = m_pSegment->GetTracks();
- assert(pTracks);
-
- const long tn = static_cast<long>(tp.m_track);
- const Track* const pTrack = pTracks->GetTrackByNumber(tn);
-
- if (pTrack == NULL)
- return NULL;
-
- const long long type = pTrack->GetType();
-
- if (type == 2) //audio
- return pEntry;
-
- if (type != 1) //not video
- return NULL;
-
- if (!pBlock->IsKey())
- return NULL;
-
- return pEntry;
- }
-
- return NULL;
-
-#else
-
const long long tc = cp.GetTimeCode();
if (tp.m_block > 0) {
@@ -7835,54 +7091,12 @@
return pEntry;
}
-
-#endif
}
-#if 0
-const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack) const
-{
- assert(pTrack);
-
- if (m_pSegment == NULL) //EOS
- return pTrack->GetEOS();
-
- LoadBlockEntries();
-
- if ((m_entries == NULL) || (m_entries_count <= 0))
- return pTrack->GetEOS();
-
- BlockEntry** i = m_entries + m_entries_count;
- BlockEntry** const j = m_entries;
-
- while (i != j)
- {
- const BlockEntry* const pEntry = *--i;
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() != pTrack->GetNumber())
- continue;
-
- if (pBlock->IsKey())
- return pEntry;
- }
-
- return pTrack->GetEOS(); //no satisfactory block found
-}
-#endif
-
BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
-
BlockEntry::~BlockEntry() {}
-
bool BlockEntry::EOS() const { return (GetKind() == kBlockEOS); }
-
const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
-
long BlockEntry::GetIndex() const { return m_index; }
SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
@@ -7890,9 +7104,7 @@
: BlockEntry(pCluster, idx), m_block(start, size, 0) {}
long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
-
BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
-
const Block* SimpleBlock::GetBlock() const { return &m_block; }
BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
@@ -7915,30 +7127,10 @@
return 0;
}
-#if 0
-void BlockGroup::ParseBlock(long long start, long long size)
-{
- IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
-
- Block* const pBlock = new Block(start, size, pReader);
- assert(pBlock); //TODO
-
- //TODO: the Matroska spec says you have multiple blocks within the
- //same block group, with blocks ranked by priority (the flag bits).
-
- assert(m_pBlock == NULL);
- m_pBlock = pBlock;
-}
-#endif
-
BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
-
const Block* BlockGroup::GetBlock() const { return &m_block; }
-
long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
-
long long BlockGroup::GetNextTimeCode() const { return m_next; }
-
long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
Block::Block(long long start, long long size_, long long discard_padding)
@@ -8028,7 +7220,7 @@
const long long frame_size = stop - pos;
- if (frame_size > LONG_MAX)
+ if (frame_size > LONG_MAX || frame_size <= 0)
return E_FILE_FORMAT_INVALID;
f.len = static_cast<long>(frame_size);
@@ -8088,6 +7280,9 @@
f.pos = 0; // patch later
+ if (frame_size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
f.len = frame_size;
size += frame_size; // contribution of this frame
@@ -8112,7 +7307,7 @@
const long long frame_size = total_size - size;
- if (frame_size > LONG_MAX)
+ if (frame_size > LONG_MAX || frame_size <= 0)
return E_FILE_FORMAT_INVALID;
f.len = static_cast<long>(frame_size);
@@ -8129,6 +7324,9 @@
assert(pos == stop);
} else if (lacing == 2) { // fixed-size lacing
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
const long long total_size = stop - pos;
if ((total_size % m_frame_count) != 0)
@@ -8136,7 +7334,7 @@
const long long frame_size = total_size / m_frame_count;
- if (frame_size > LONG_MAX)
+ if (frame_size > LONG_MAX || frame_size <= 0)
return E_FILE_FORMAT_INVALID;
Frame* pf = m_frames;
@@ -8165,7 +7363,7 @@
long long frame_size = ReadUInt(pReader, pos, len);
- if (frame_size < 0)
+ if (frame_size <= 0)
return E_FILE_FORMAT_INVALID;
if (frame_size > LONG_MAX)
@@ -8227,7 +7425,7 @@
frame_size += delta_size;
- if (frame_size < 0)
+ if (frame_size <= 0)
return E_FILE_FORMAT_INVALID;
if (frame_size > LONG_MAX)
@@ -8239,7 +7437,8 @@
--frame_count;
}
- {
+ // parse last frame
+ if (frame_count > 0) {
assert(pos <= stop);
assert(pf < pf_end);
@@ -8262,7 +7461,7 @@
frame_size = total_size - size;
- if (frame_size > LONG_MAX)
+ if (frame_size > LONG_MAX || frame_size <= 0)
return E_FILE_FORMAT_INVALID;
curr.len = static_cast<long>(frame_size);
@@ -8277,7 +7476,8 @@
pos += f.len;
}
- assert(pos == stop);
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
}
return 0; // success
--- a/third_party/libwebm/mkvparser.hpp
+++ b/third_party/libwebm/mkvparser.hpp
@@ -398,6 +398,10 @@
long long GetWidth() const;
long long GetHeight() const;
+ long long GetDisplayWidth() const;
+ long long GetDisplayHeight() const;
+ long long GetDisplayUnit() const;
+ long long GetStereoMode() const;
double GetFrameRate() const;
bool VetEntry(const BlockEntry*) const;
@@ -406,6 +410,11 @@
private:
long long m_width;
long long m_height;
+ long long m_display_width;
+ long long m_display_height;
+ long long m_display_unit;
+ long long m_stereo_mode;
+
double m_rate;
};
@@ -582,6 +591,85 @@
int m_editions_count;
};
+class Tags {
+ Tags(const Tags&);
+ Tags& operator=(const Tags&);
+
+ public:
+ Segment* const m_pSegment;
+ const long long m_start;
+ const long long m_size;
+ const long long m_element_start;
+ const long long m_element_size;
+
+ Tags(Segment*, long long payload_start, long long payload_size,
+ long long element_start, long long element_size);
+
+ ~Tags();
+
+ long Parse();
+
+ class Tag;
+ class SimpleTag;
+
+ class SimpleTag {
+ friend class Tag;
+ SimpleTag();
+ SimpleTag(const SimpleTag&);
+ ~SimpleTag();
+ SimpleTag& operator=(const SimpleTag&);
+
+ public:
+ const char* GetTagName() const;
+ const char* GetTagString() const;
+
+ private:
+ void Init();
+ void ShallowCopy(SimpleTag&) const;
+ void Clear();
+ long Parse(IMkvReader*, long long pos, long long size);
+
+ char* m_tag_name;
+ char* m_tag_string;
+ };
+
+ class Tag {
+ friend class Tags;
+ Tag();
+ Tag(const Tag&);
+ ~Tag();
+ Tag& operator=(const Tag&);
+
+ public:
+ int GetSimpleTagCount() const;
+ const SimpleTag* GetSimpleTag(int index) const;
+
+ private:
+ void Init();
+ void ShallowCopy(Tag&) const;
+ void Clear();
+ long Parse(IMkvReader*, long long pos, long long size);
+
+ long ParseSimpleTag(IMkvReader*, long long pos, long long size);
+ bool ExpandSimpleTagsArray();
+
+ SimpleTag* m_simple_tags;
+ int m_simple_tags_size;
+ int m_simple_tags_count;
+ };
+
+ int GetTagCount() const;
+ const Tag* GetTag(int index) const;
+
+ private:
+ long ParseTag(long long pos, long long size);
+ bool ExpandTagsArray();
+
+ Tag* m_tags;
+ int m_tags_size;
+ int m_tags_count;
+};
+
class SegmentInfo {
SegmentInfo(const SegmentInfo&);
SegmentInfo& operator=(const SegmentInfo&);
@@ -684,7 +772,7 @@
long long m_element_start;
long long m_element_size;
- void Load(IMkvReader*);
+ bool Load(IMkvReader*);
long long GetTimeCode() const; // absolute but unscaled
long long GetTime(const Segment*) const; // absolute and scaled (ns units)
@@ -697,7 +785,7 @@
// reference = clusters containing req'd referenced blocks
// reftime = timecode of the referenced block
- void Parse(IMkvReader*, long long, long long);
+ bool Parse(IMkvReader*, long long, long long);
};
const TrackPosition* Find(const Track*) const;
@@ -730,14 +818,6 @@
long long time_ns, const Track*, const CuePoint*&,
const CuePoint::TrackPosition*&) const;
-#if 0
- bool FindNext( //upper_bound of time_ns
- long long time_ns,
- const Track*,
- const CuePoint*&,
- const CuePoint::TrackPosition*&) const;
-#endif
-
const CuePoint* GetFirst() const;
const CuePoint* GetLast() const;
const CuePoint* GetNext(const CuePoint*) const;
@@ -751,7 +831,7 @@
bool DoneParsing() const;
private:
- void Init() const;
+ bool Init() const;
void PreloadCuePoint(long&, long long) const;
mutable CuePoint** m_cue_points;
@@ -877,18 +957,12 @@
long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos,
long& size);
-#if 0
- //This pair parses one cluster, but only changes the state of the
- //segment object when the cluster is actually added to the index.
- long ParseCluster(long long& cluster_pos, long long& new_pos) const;
- bool AddCluster(long long cluster_pos, long long new_pos);
-#endif
-
const SeekHead* GetSeekHead() const;
const Tracks* GetTracks() const;
const SegmentInfo* GetInfo() const;
const Cues* GetCues() const;
const Chapters* GetChapters() const;
+ const Tags* GetTags() const;
long long GetDuration() const;
@@ -914,6 +988,7 @@
Tracks* m_pTracks;
Cues* m_pCues;
Chapters* m_pChapters;
+ Tags* m_pTags;
Cluster** m_clusters;
long m_clusterCount; // number of entries for which m_index >= 0
long m_clusterPreloadCount; // number of entries for which m_index < 0
--- a/third_party/libwebm/webmids.hpp
+++ b/third_party/libwebm/webmids.hpp
@@ -133,7 +133,13 @@
kMkvChapterDisplay = 0x80,
kMkvChapString = 0x85,
kMkvChapLanguage = 0x437C,
- kMkvChapCountry = 0x437E
+ kMkvChapCountry = 0x437E,
+ // Tags
+ kMkvTags = 0x1254C367,
+ kMkvTag = 0x7373,
+ kMkvSimpleTag = 0x67C8,
+ kMkvTagName = 0x45A3,
+ kMkvTagString = 0x4487
};
} // end namespace mkvmuxer