summaryrefslogtreecommitdiff
path: root/third_party
diff options
context:
space:
mode:
authorJames Zern <jzern@google.com>2015-08-03 20:24:44 -0700
committerJames Zern <jzern@google.com>2015-08-03 20:29:47 -0700
commitc353da68aa10a8d75513e32e25188aedfc8d532f (patch)
tree81c418c9e4805e219d5638751b38f59c31a1beb3 /third_party
parent18427983ab9d5c50ec6e49d81570391ff8281abd (diff)
downloadlibvpx-c353da68aa10a8d75513e32e25188aedfc8d532f.tar.gz
third_party/libwebm: pull from upstream
Changes: b6de61a Adds support for simple tags 75a6d2d sample_muxer: Don't write huge files. cec1f85 mkvmuxer: remove unused timecode_scale variable 8a61b40 Merge "mkvparser: Tiny whitespace fix." 7affc5c clang-format re-run d6d04ac mkvmuxer: use generic Cluster::AddFrame 4928b0b Merge "mkvmuxer: Write Block key frames correctly." c2e4a46 Merge "sample_muxer: Use AddGenericFrame to add frames." e97f296 mkvparser: Tiny whitespace fix. d66ba44 Merge "Add support to parse DisplayUnit." deb41c2 Add support to parse DisplayUnit. 42e5660 Fix issues on EBML lacing block parsing fe1e9bb Fix block parsing to not allow frame_size = 0 2cb6a28 Change assertions to checks when parsing TrackPositions d04580f Fixes issues on Block Group parsing c3550fd mkvmuxer: Write Block key frames correctly. 5dd0e40 Merge "mkvmuxer: Set is_key to true for metadata blocks." 8e96863 mkvmuxer: Set is_key to true for metadata blocks. a9e4819 sample_muxer: Use AddGenericFrame to add frames. 5a3be73 Change assertions to checks when load CuePoints f99f3b2 mkvmuxerutil::EbmlDateElementSize: remove value param ff572b5 Frame::IsValid: fix track_number check b6311dc mkvmuxer: Refactor to remove a lot of duplicate code 256cd02 Merge "mkvmuxer: DiscardPadding should be signed integer." 16c8e78 mkvmuxer: s/frame/data in all AddFrame* functions. c5e511c mkvmuxer: DiscardPadding should be signed integer. 4baaa2c Add framework build script: iosbuild.sh 3d06eb1 PATENTS: fix a typo: constitutes -> constitute d3849c2 mkvparser: Dead code removal. f439e52 Change assertions to checks when preloading Cues d3a44cd Fix track transversal when listing Cues on sample c6255af Tweak .gitignore so git status is clean after checkout and build: - added missing underscore to sample_muxer - added cmake and make related files b5229c7 Makefile.unix: s/samplemuxer/sample_muxer/ e3616a6 Add support to parse stereo mode, display width and display height in mkvparser a4b68f8 parser: Fix bug in Chapters::Atom::Parse() bab0a00 cmake: Set library and project name the proper way on Windows. feeb9b1 Set library name to match Windows expectations. b9a549b Fix CMakefile to generate libwebm.a b386aa5 Add CMakeLists.txt and msvc_runtime.cmake. b0f8a81 parser: Fix memory leak in Chapter parsing f06e152 mkvmuxer: Fix MoveCuesBeforeClustersHelper recursive call. 27bb747 allow subtitle tracks with ContentEncodings 623d182 DoLoadCluster: tolerate empty clusters 1156da8 Update PATENTS to reflect s/VP8/WebM/g 0d4cb40 mkvmuxerutil: Use rand() in MSVC builds. e12fff0 mkvmuxer: Overload WriteEbmlHeader for backward compatibility a321704 mkvmuxer: write correct DocTypeVersion 574045e mkvmuxer: fix DiscardPadding 8be6397 Include crop elements when calculating size of Video element 8f2d1b3 mkvparser: fix DiscardPadding extraction 1c36c24 mkvmuxer: fix style guide violations 568504e Merge "UUIDs can have their high bit set" acf788b Add support for CropLeft, CropRight, CropTop and CropBottom elements. 418188b Merge "muxer: codec_id is a mandatory element" 07688c9 mkvmuxer: Reject frames if invalid track number is passed. 2a63e47 muxer: codec_id is a mandatory element d13c017 UUIDs can have their high bit set Change-Id: Iba28acb1ff774349d03e565f2641ddea132cf1e7
Diffstat (limited to 'third_party')
-rw-r--r--third_party/libwebm/Android.mk17
-rw-r--r--third_party/libwebm/PATENTS.TXT2
-rw-r--r--third_party/libwebm/README.libvpx2
-rw-r--r--third_party/libwebm/mkvmuxer.cpp905
-rw-r--r--third_party/libwebm/mkvmuxer.hpp293
-rw-r--r--third_party/libwebm/mkvmuxerutil.cpp483
-rw-r--r--third_party/libwebm/mkvmuxerutil.hpp70
-rw-r--r--third_party/libwebm/mkvparser.cpp1680
-rw-r--r--third_party/libwebm/mkvparser.hpp111
-rw-r--r--third_party/libwebm/webmids.hpp8
10 files changed, 1519 insertions, 2052 deletions
diff --git a/third_party/libwebm/Android.mk b/third_party/libwebm/Android.mk
index 13868b660..be9d77dee 100644
--- 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)
diff --git a/third_party/libwebm/PATENTS.TXT b/third_party/libwebm/PATENTS.TXT
index 79d17d7d6..caedf607e 100644
--- a/third_party/libwebm/PATENTS.TXT
+++ b/third_party/libwebm/PATENTS.TXT
@@ -17,7 +17,7 @@ or agree to the institution of patent litigation or any other patent
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.
diff --git a/third_party/libwebm/README.libvpx b/third_party/libwebm/README.libvpx
index 93814b79f..8839257d6 100644
--- 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
diff --git a/third_party/libwebm/mkvmuxer.cpp b/third_party/libwebm/mkvmuxer.cpp
index 45167ea4c..fe7eb2fe9 100644
--- a/third_party/libwebm/mkvmuxer.cpp
+++ b/third_party/libwebm/mkvmuxer.cpp
@@ -65,14 +65,14 @@ IMkvWriter::IMkvWriter() {}
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);
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 @@ bool WriteEbmlHeader(IMkvWriter* writer) {
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 @@ bool WriteEbmlHeader(IMkvWriter* writer) {
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,13 +131,40 @@ Frame::Frame()
length_(0),
track_number_(0),
timestamp_(0),
- discard_padding_(0) {}
+ discard_padding_(0),
+ reference_block_timestamp_(0),
+ reference_block_timestamp_set_(false) {}
Frame::~Frame() {
delete[] frame_;
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 @@ bool Frame::AddAdditionalData(const uint8* additional, uint64 length,
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 @@ bool Cues::AddCue(CuePoint* cue) {
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 @@ bool Track::AddContentEncoding() {
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 @@ bool Track::Write(IMkvWriter* writer) const {
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 @@ bool Track::Write(IMkvWriter* writer) const {
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 @@ VideoTrack::VideoTrack(unsigned int* seed)
: 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 @@ bool VideoTrack::Write(IMkvWriter* writer) const {
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 @@ uint64 VideoTrack::VideoPayloadSize() const {
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 @@ bool Tracks::AddTrack(Track* track, int32 number) {
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 @@ Chapter::~Chapter() {}
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 @@ uint64 Chapters::WriteEdition(IMkvWriter* writer) const {
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 @@ Cluster::Cluster(uint64 timecode, int64 cues_pos)
position_for_cues_(cues_pos),
size_position_(-1),
timecode_(timecode),
+ timecode_scale_(timecode_scale),
writer_(NULL) {}
Cluster::~Cluster() {}
@@ -1444,36 +1770,62 @@ bool Cluster::Init(IMkvWriter* ptr_writer) {
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 @@ uint64 Cluster::Size() const {
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 @@ void Cluster::PostWriteBlock(uint64 element_size) {
++blocks_added_;
}
-bool Cluster::IsValidTrackNumber(uint64 track_number) const {
- return (track_number > 0 && track_number <= 0x7E);
-}
-
int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const {
const int64 cluster_timecode = this->Cluster::timecode();
const int64 rel_timecode =
@@ -1542,79 +1886,14 @@ int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const {
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)
- 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, track_number, rel_timecode, generic_arg);
- if (element_size == 0)
- return false;
-
- PostWriteBlock(element_size);
- 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)
+bool Cluster::DoWriteFrame(const Frame* const frame) {
+ if (!frame || !frame->IsValid())
return false;
- if (!PreWriteBlock(write_block))
+ if (!PreWriteBlock())
return false;
- const uint64 element_size =
- (*write_block)(writer_, frame, length, discard_padding, track_number,
- rel_timecode, generic_arg);
+ const uint64 element_size = WriteFrame(writer_, frame, this);
if (element_size == 0)
return false;
@@ -1860,7 +2139,7 @@ bool SegmentInfo::Write(IMkvWriter* writer) {
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 @@ Segment::Segment()
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 @@ Segment::~Segment() {
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 @@ void Segment::MoveCuesBeforeClustersHelper(uint64 diff, int32 index,
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::MoveCuesBeforeClustersHelper(uint64 diff, int32 index,
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 @@ bool Segment::Finalize() {
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 @@ Track* Segment::AddTrack(int32 number) {
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,157 +2561,104 @@ uint64 Segment::AddAudioTrack(int32 sample_rate, int32 channels, int32 number) {
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)
- return false;
-
- if (!CheckHeaderInfo())
- return false;
-
- // Check for non-monotonically increasing timestamps.
- if (timestamp < last_timestamp_)
- return false;
-
- // If the segment has a video track hold onto audio frames to make sure the
- // audio that is associated with the start time of a video key-frame is
- // muxed into the same cluster.
- if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) {
- Frame* const new_frame = new (std::nothrow) Frame();
- if (new_frame == NULL || !new_frame->Init(frame, length))
- return false;
- new_frame->set_track_number(track_number);
- new_frame->set_timestamp(timestamp);
- new_frame->set_is_key(is_key);
-
- 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)
+ if (!data)
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))
+ 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);
+ 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())
- return false;
-
- // Check for non-monotonically increasing timestamps.
- if (timestamp < last_timestamp_)
+ Frame frame;
+ if (!frame.Init(data, length) ||
+ !frame.AddAdditionalData(additional, additional_length, add_id)) {
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())
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 @@ bool Segment::AddFrameWithDiscardPadding(const uint8* frame, uint64 length,
if (!cluster)
return false;
- const uint64 timecode_scale = segment_info_.timecode_scale();
- const uint64 abs_timecode = timestamp / timecode_scale;
-
- if (!cluster->AddFrameWithDiscardPadding(
- frame, length, discard_padding, track_number, abs_timecode, is_key)) {
- return false;
- }
-
- if (new_cuepoint_ && cues_track_ == track_number) {
- if (!AddCuePoint(timestamp, cues_track_))
+ // 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 (timestamp > last_timestamp_)
- last_timestamp_ = timestamp;
-
- return true;
-}
-
-bool Segment::AddMetadata(const uint8* frame, uint64 length,
- uint64 track_number, uint64 timestamp_ns,
- uint64 duration_ns) {
- if (!frame)
- return false;
-
- if (!CheckHeaderInfo())
+ if (!cluster->AddFrame(frame))
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 (new_cuepoint_ && cues_track_ == frame->track_number()) {
+ if (!AddCuePoint(frame->timestamp(), cues_track_))
+ return false;
+ }
- if (!cluster->AddMetadata(frame, length, track_number, abs_timecode,
- duration_timecode))
- return false;
+ last_timestamp_ = frame->timestamp();
+ last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
+ last_block_duration_ = frame->duration();
- if (timestamp_ns > last_timestamp_)
- last_timestamp_ = timestamp_ns;
+ if (frame_created)
+ delete frame;
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 @@ Track* Segment::GetTrackByNumber(uint64 track_number) const {
}
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 @@ bool Segment::WriteSegmentHeader() {
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 @@ bool Segment::MakeNewCluster(uint64 frame_timestamp_ns) {
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 @@ bool Segment::MakeNewCluster(uint64 frame_timestamp_ns) {
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 @@ bool Segment::CheckHeaderInfo() {
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 @@ bool Segment::QueueFrame(Frame* frame) {
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,34 +3185,24 @@ int Segment::WriteFramesAll() {
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;
-
- if (frame->discard_padding() > 0) {
- if (!cluster->AddFrameWithDiscardPadding(
- frame->frame(), frame->length(), frame->discard_padding(),
- frame->track_number(), frame_timecode, frame->is_key())) {
- return -1;
- }
- } else {
- if (!cluster->AddFrame(frame->frame(), frame->length(),
- frame->track_number(), frame_timecode,
- frame->is_key())) {
- return -1;
- }
- }
+ // 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 (new_cuepoint_ && cues_track_ == frame->track_number()) {
- if (!AddCuePoint(frame_timestamp, cues_track_))
+ if (!AddCuePoint(frame->timestamp(), cues_track_))
return -1;
}
- if (frame_timestamp > last_timestamp_)
- last_timestamp_ = frame_timestamp;
+ if (frame->timestamp() > last_timestamp_) {
+ last_timestamp_ = frame->timestamp();
+ last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
+ }
delete frame;
frame = NULL;
@@ -3013,7 +3226,6 @@ bool Segment::WriteFramesLessThan(uint64 timestamp) {
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 @@ bool Segment::WriteFramesLessThan(uint64 timestamp) {
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 (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 (frame_prev->discard_padding() != 0)
+ doc_type_version_ = 4;
+ if (!cluster->AddFrame(frame_prev))
+ 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;
}
diff --git a/third_party/libwebm/mkvmuxer.hpp b/third_party/libwebm/mkvmuxer.hpp
index 1c1c31006..ecd8a745c 100644
--- a/third_party/libwebm/mkvmuxer.hpp
+++ b/third_party/libwebm/mkvmuxer.hpp
@@ -23,6 +23,8 @@ namespace mkvmuxer {
class MkvWriter;
class Segment;
+const uint64 kMaxTrackNumber = 126;
+
///////////////////////////////////////////////////////////////
// Interface used by the mkvmuxer to write out the Mkv data.
class IMkvWriter {
@@ -57,6 +59,10 @@ class IMkvWriter {
// 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,12 +76,23 @@ class Frame {
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);
// 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 @@ class Frame {
uint64 track_number() const { return track_number_; }
void set_timestamp(uint64 timestamp) { timestamp_ = timestamp; }
uint64 timestamp() const { return timestamp_; }
- void set_discard_padding(uint64 discard_padding) {
+ 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 @@ class Frame {
// Discard padding for the frame.
int64 discard_padding_;
+
+ // Reference block timestamp.
+ int64 reference_block_timestamp_;
+
+ // Flag indicating if |reference_block_timestamp_| has been set.
+ bool reference_block_timestamp_set_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Frame);
};
///////////////////////////////////////////////////////////////
@@ -422,6 +452,16 @@ class VideoTrack : public Track {
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 @@ class VideoTrack : public Track {
// 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,38 +737,148 @@ class Chapters {
};
///////////////////////////////////////////////////////////////
+// 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:
// |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].
// 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);
// 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 @@ class Cluster {
// 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 @@ class Cluster {
// 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,14 +904,14 @@ class Cluster {
// 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);
// 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 @@ class Cluster {
// 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,75 +935,29 @@ class Cluster {
// 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;
-
- // 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);
+ // Does some verification and calls WriteFrame.
+ bool DoWriteFrame(const Frame* const frame);
// Outputs the Cluster header to |writer_|. Returns true on success.
bool WriteClusterHeader();
@@ -875,6 +983,9 @@ class Cluster {
// 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 @@ class Segment {
kBeforeClusters = 0x1 // Position Cues before Clusters
};
+ const static uint32 kDefaultDocTypeVersion = 2;
const static uint64 kDefaultMaxClusterDuration = 30000000000ULL;
Segment();
@@ -1023,6 +1135,11 @@ class Segment {
// 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 @@ class Segment {
// 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 @@ class Segment {
// 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 @@ class Segment {
// 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 @@ class Segment {
// 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 @@ class Segment {
// 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 @@ class Segment {
// 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 @@ class Segment {
// 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 @@ class Segment {
SegmentInfo segment_info_;
Tracks tracks_;
Chapters chapters_;
+ Tags tags_;
// Number of chunks written.
int chunk_count_;
@@ -1316,6 +1437,9 @@ class Segment {
// Last timestamp in nanoseconds added to a cluster.
uint64 last_timestamp_;
+ // Last timestamp in nanoseconds by track number added to a cluster.
+ uint64 last_track_timestamp_[kMaxTrackNumber];
+
// Maximum time in nanoseconds for a cluster duration. This variable is a
// guideline and some clusters may have a longer duration. Default is 30
// seconds.
@@ -1337,12 +1461,23 @@ class Segment {
// 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_;
IMkvWriter* writer_cues_;
diff --git a/third_party/libwebm/mkvmuxerutil.cpp b/third_party/libwebm/mkvmuxerutil.cpp
index 3fb9bc9dc..27ab15d51 100644
--- 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 @@ namespace {
// 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 @@ int32 GetUIntSize(uint64 value) {
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 EbmlMasterElementSize(uint64 type, uint64 value) {
}
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 @@ uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) {
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 @@ bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) {
return true;
}
+bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) {
+ if (!writer)
+ return false;
+
+ if (WriteID(writer, type))
+ return 0;
+
+ const uint64 size = GetIntSize(value);
+ if (WriteUInt(writer, size))
+ return false;
+
+ if (SerializeInt(writer, value, static_cast<int32>(size)))
+ return false;
+
+ return true;
+}
+
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) {
if (!writer)
return false;
@@ -355,289 +527,25 @@ bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) {
return true;
}
-uint64 WriteSimpleBlock(IMkvWriter* writer, const uint8* data, uint64 length,
- uint64 track_number, int64 timecode, uint64 is_key) {
- if (!writer)
- return false;
-
- 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
- // 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))
- 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;
-}
-
-// 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)
+uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
+ Cluster* cluster) {
+ if (!writer || !frame || !frame->IsValid() || !cluster ||
+ !cluster->timecode_scale())
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))
+ // 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 timecodes for blocks.
+ const int64 relative_timecode = cluster->GetRelativeTimecode(
+ frame->timestamp() / cluster->timecode_scale());
+ if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode)
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;
+ return frame->CanBeSimpleBlock() ?
+ WriteSimpleBlock(writer, frame, relative_timecode) :
+ WriteBlock(writer, frame, relative_timecode,
+ cluster->timecode_scale());
}
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
@@ -698,10 +606,7 @@ mkvmuxer::uint64 mkvmuxer::MakeUID(unsigned int* seed) {
// 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);
diff --git a/third_party/libwebm/mkvmuxerutil.hpp b/third_party/libwebm/mkvmuxerutil.hpp
index a092abe7a..e31857694 100644
--- 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 @@ int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size);
// 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, uint64 value);
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,73 +53,17 @@ int32 WriteID(IMkvWriter* writer, uint64 type);
// Output an Mkv non-master element. Returns true if the element was written.
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value);
+bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value);
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value);
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value);
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
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
diff --git a/third_party/libwebm/mkvparser.cpp b/third_party/libwebm/mkvparser.cpp
index 441f16596..9d4ddc893 100644
--- a/third_party/libwebm/mkvparser.cpp
+++ b/third_party/libwebm/mkvparser.cpp
@@ -23,7 +23,7 @@ void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision) {
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 @@ long long mkvparser::GetUIntLength(IMkvReader* pReader, long long pos,
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 @@ Segment::Segment(IMkvReader* pReader, long long elem_start,
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 @@ Segment::~Segment() {
delete m_pInfo;
delete m_pCues;
delete m_pChapters;
+ delete m_pTags;
delete m_pSeekHead;
}
@@ -907,6 +911,19 @@ long long Segment::ParseHeaders() {
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 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
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,10 +1162,8 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
}
if (status == 0) { // no entries found
- if (cluster_size < 0)
- return E_FILE_FORMAT_INVALID; // TODO: handle this
-
- pos += cluster_size;
+ if (cluster_size >= 0)
+ pos += cluster_size;
if ((total >= 0) && (pos >= total)) {
m_pos = total;
@@ -1201,306 +1204,15 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
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) {
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 @@ long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
m_pUnknownSize = 0;
return 2; // continue parsing
-#endif
}
void Segment::AppendCluster(Cluster* pCluster) {
@@ -1540,7 +1251,7 @@ void Segment::AppendCluster(Cluster* pCluster) {
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 @@ void Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
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 @@ const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
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 @@ long Segment::ParseCues(long long off, long long& pos, long& len) {
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 @@ bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
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 @@ bool Cues::DoneParsing() const {
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 @@ void Cues::Init() const {
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 @@ void Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
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 @@ bool Cues::LoadCuePoint() const {
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 @@ bool Cues::LoadCuePoint() const {
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 @@ bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
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 @@ bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
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 @@ bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
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 @@ const CuePoint* Cues::GetFirst() const {
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 @@ const CuePoint* Cues::GetLast() const {
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 @@ const CuePoint* Cues::GetLast() const {
CuePoint* const pCP = pp[index];
assert(pCP);
assert(pCP->GetTimeCode() >= 0);
-#endif
return pCP;
}
@@ -2533,26 +1999,6 @@ const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
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 @@ const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
CuePoint* const pNext = pp[index];
assert(pNext);
assert(pNext->GetTimeCode() >= 0);
-#endif
return pNext;
}
@@ -2705,12 +2150,12 @@ CuePoint::CuePoint(long idx, long long pos)
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 @@ void CuePoint::Load(IMkvReader* pReader) {
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 @@ void CuePoint::Load(IMkvReader* pReader) {
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 @@ void CuePoint::Load(IMkvReader* pReader) {
++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 @@ void CuePoint::Load(IMkvReader* pReader) {
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 @@ void CuePoint::Load(IMkvReader* pReader) {
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 @@ void CuePoint::Load(IMkvReader* pReader) {
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 @@ void CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
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 @@ void CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
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 @@ long long CuePoint::GetTime(const Segment* pSegment) const {
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 @@ bool Segment::DoneParsing() const {
return (m_pos >= stop);
}
-#endif
const Cluster* Segment::GetFirst() const {
if ((m_clusters == NULL) || (m_clusterCount <= 0))
@@ -3395,15 +2838,7 @@ long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
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 @@ const Cluster* Segment::FindCluster(long long time_ns) const {
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 @@ Chapters::~Chapters() {
Edition& e = m_editions[--m_editions_count];
e.Clear();
}
+ delete[] m_editions;
}
long Chapters::Parse() {
@@ -4128,12 +3387,13 @@ long Chapters::Atom::Parse(IMkvReader* pReader, long long pos, long long size) {
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 @@ long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
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 @@ ContentEncoding::~ContentEncoding() {
}
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 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
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 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
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 @@ long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
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 @@ long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
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 @@ long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
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 @@ long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
}
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 @@ long Track::GetNext(const BlockEntry* pCurrEntry,
}
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 @@ long Track::ParseContentEncodingsEntry(long long start, long long size) {
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 VideoTrack::Parse(Segment* pSegment, const Info& info,
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 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
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 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
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 @@ long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
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::GetWidth() const { return m_width; }
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 @@ long Tracks::Parse() {
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 @@ long Tracks::ParseTrackEntry(long long track_start, long long track_size,
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 @@ const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
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 @@ long Cluster::Load(long long& pos, long& len) const {
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 @@ long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
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,12 +6170,7 @@ long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
return E_FILE_FORMAT_INVALID;
if (id == 0x35A2) { // DiscardPadding
- result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- status = UnserializeInt(pReader, pos, len, discard_padding);
+ status = UnserializeInt(pReader, pos, size, discard_padding);
if (status < 0) // error
return status;
@@ -6733,36 +6219,6 @@ long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
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::GetPosition() const {
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 @@ long Cluster::CreateBlock(long long id,
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 @@ long Cluster::CreateBlock(long long id,
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 @@ long Cluster::CreateBlockGroup(long long start_offset, long long size,
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 @@ long Cluster::CreateBlockGroup(long long start_offset, long long size,
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 @@ const BlockEntry* Cluster::GetEntry(const Track* pTrack,
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 @@ const BlockEntry* Cluster::GetEntry(const Track* pTrack,
++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 @@ const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
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 @@ SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
: 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 @@ long BlockGroup::Parse() {
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 @@ long Block::Parse(const Cluster* pCluster) {
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 @@ long Block::Parse(const Cluster* pCluster) {
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 @@ long Block::Parse(const Cluster* pCluster) {
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 @@ long Block::Parse(const Cluster* pCluster) {
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 @@ long Block::Parse(const Cluster* pCluster) {
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 Block::Parse(const Cluster* pCluster) {
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 @@ long Block::Parse(const Cluster* pCluster) {
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 @@ long Block::Parse(const Cluster* pCluster) {
--frame_count;
}
- {
+ // parse last frame
+ if (frame_count > 0) {
assert(pos <= stop);
assert(pf < pf_end);
@@ -8262,7 +7461,7 @@ long Block::Parse(const Cluster* pCluster) {
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 @@ long Block::Parse(const Cluster* pCluster) {
pos += f.len;
}
- assert(pos == stop);
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
}
return 0; // success
diff --git a/third_party/libwebm/mkvparser.hpp b/third_party/libwebm/mkvparser.hpp
index 3e17d07cc..ebb69559f 100644
--- a/third_party/libwebm/mkvparser.hpp
+++ b/third_party/libwebm/mkvparser.hpp
@@ -398,6 +398,10 @@ class VideoTrack : public Track {
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 @@ class VideoTrack : public Track {
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 @@ class Chapters {
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 @@ class CuePoint {
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 @@ class CuePoint {
// 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 @@ class Cues {
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 @@ class Cues {
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 @@ class Segment {
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 @@ class Segment {
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
diff --git a/third_party/libwebm/webmids.hpp b/third_party/libwebm/webmids.hpp
index eeb52d8d8..6874e44e9 100644
--- a/third_party/libwebm/webmids.hpp
+++ b/third_party/libwebm/webmids.hpp
@@ -133,7 +133,13 @@ enum MkvId {
kMkvChapterDisplay = 0x80,
kMkvChapString = 0x85,
kMkvChapLanguage = 0x437C,
- kMkvChapCountry = 0x437E
+ kMkvChapCountry = 0x437E,
+ // Tags
+ kMkvTags = 0x1254C367,
+ kMkvTag = 0x7373,
+ kMkvSimpleTag = 0x67C8,
+ kMkvTagName = 0x45A3,
+ kMkvTagString = 0x4487
};
} // end namespace mkvmuxer