ref: 31b975713cf69879bcfd66bc653791112a8c3644
parent: 758fc662b237ffd2dafaf4e6418a83dcc2926820
author: Tom Finegan <[email protected]>
date: Thu Mar 24 09:12:51 EDT 2016
third_party: Roll libwebm snapshot. eb50da8 Option to write timecode using fixed # of bytes c1991fe mkvmuxer: Add missing Segment member initializer. 10aed96 Android.mk: Make libwebm easier to build downstream. 5c50e31 Add support for parsing VPx track codec private data. 4cbdbf1 Fix Android build. bb48a3f mkvmuxerutil: remove stray 'int32' a1cba34 Support cross compile for windows via mingw64. 596f5e0 Add webm_info. ccf75f6 msvc/muxer_tests: Silence integer conversion warnings. 2ff2954 msvc/webm2pes: Silence integer and floating point conversion warnings. 1f24323 msvc/hdr_util: Silence double to float conversion warnings. 0744563 msvc/vpxpes_parser: Silence integer conversion warning. 59614b8 msvc/libwebm_util: Fix floating point to int conversion warning. 6481c24 webvtt: Fix include in vttreader. e6ed0f4 msvc/vpxpes2ts: Fix MSVC integer conversion warning. da64396 cmake/msvc: Disable C4996 project wide. 6ef8264 Merge "mkvparser::BlockEntry: inline EOS()" 3fa6aec mkvparser::BlockEntry: inline EOS() 26306f9 mkvmuxer: Remove unused Cluster ctor overload. 0d76597 mkvmuxer: Fix build with GCC 5.3. 0ba80bc mkvparser/sample: Minor clean up. 2e0e906 iosbuild.sh: Fix build. 918440a Makefile.unix: allow CXXFLAGS to be easily overridden 4ff5785 cmake: Add C++11 move ctor and member initializer tests. 402ef4d cmake: remove argc and argv from C++11 test main fns. cbe5c40 Restore original namespaces for mkvmuxer and mkvparser. 504e0f2 Mass file extension update. 79cb980 Android.mk: Update source file locations. 01db4c2 webmids: Move to common/ sub dir. 235ce59 mkvparser: Explicitly reference internal sources in includes. f578419 mkvmuxer: Move sources to mkvmuxer/ sub dir. 5f1065e webvtt: Organize and clean up webvtt support. 7abe8ac cmake: Add missing dumpvtt target. f2f87e2 Makefile.unix: Tidy things up. 12f6dc3 Use <stdint.h> types instead of custom typedefs. 0407360 mkvmuxer: Write last block in each Cluster with Duration 008aa63 mkvparser: move to mkvparser sub dir. e64bf75 Namespace reorg: Make everything a child of libwebm. 5fdb386 cmake: move c++11 checks into build/cxx11_tests.cmake. 3672488 Copy reference block values in Frame::CopyFrom() 91ca780 reapply clang-format 8d34215 Merge "Clean up AddAudioTrack in muxer_tests" 90861d4 Clean up AddAudioTrack in muxer_tests a9dfb3d Un-ignore webm files in testdata c5b76d8 Extract PES parser from WebM2Pes tests. 16524e8 cmake: Add include-what-you-use integration. 7015af5 iwyu/vpxpes2ts: Update includes. c1d6a70 iwyu/webm2pes: Update includes. 110e797 iwyu/libwebm_util: Update includes. 44e31fb iwyu/webm2pes_tests: Update includes. d919f96 iwyu/mkvwriter: Update includes. 75790e1 iwyu/mkvparser: Update includes. 5f673ca iwyu/webm2pes_main: Update includes. 747244a iwyu/vpxpes2ts_main: Update includes. 94c985f iwyu/mkvmuxerutil: Update includes. c365630 iwyu/mkvmuxer: Update includes. b15b8ef iwyu/file_util: Update includes. 3dfba95 iwyu/hdr_util: Update includes. baba8b1 iwyu/vttdemux: Update includes. 3212ec1 iwyu/webvttparser: Update includes. b6d8d92 iwyu/sample_muxer_metadata: Update includes. a9a1a01 iwyu/sample_muxer: Update includes. e020ffd iwyu/sample: Update includes. 18834bc iwyu/parser_tests: Update includes. 9c00ae3 iwyu/muxer_tests: Update includes. 41a17eb iwyu/test_util: Update includes b6174be muxer_tests: Fix windows brokenness. e092515 file_util: Remove tmpnam() usage in MSVC. b9dc4ac test_util: Don't pass NULL to std::string() in GetTestDataDir(). 1f74651 webmts: Move PES/TS sources to m2ts sub directory. 1b895e9 Rename libwebm_utils to libwebm_util. 2fabcd3 sample_muxer: Replace std::tmpnam() with libwebm::GetTempFileName(). e6a0033 Add file_util. 87f9bea Move hdr_util to common. 1f64aaf cmake: Expand C++11 tests. 6dc81c1 muxer_tests: Die immediately when unable to prep for file writing. 521ce4d webm2pes: Fix type limit warning. 64c4163 vpxpes2ts: Fix sign-compare and type-limits warnings. 741ba68 muxer_tests: Replace std::tmpnam() with GetTempFileName(). 6159e83 Merge "test_util: add missing include for close()" ff81c74 parser_tests: Fix sign compare warnings. 163f57d test_util: add missing include for close() 7c89eb5 Merge "test_util: Remove tmpnam() usage on non-MSVC targets." c4b8686 Merge "webm2pes_tests: Fix sign compare warnings." 9c9f546 Merge "muxer_tests: Fix sign compare warnings." 0fbefef webm2pes: Silence sign compare warnings. 599e4e8 cmake: Silence clang/gcc deprecation warnings. 82f376f test_util: Remove tmpnam() usage on non-MSVC targets. 4d31d6b webm2pes_tests: Fix sign compare warnings. 07ed7e0 muxer_tests: Fix sign compare warnings. ae2fbfe parser_tests: Silence sign compare warning. f488528 libwebm_utils: Silence sign compare warning. 777247b Add C++11 detection to cmake file. 9b89187 Add missing include to libwebm_utils.h. 421874a Merge "mkvmuxer: Fix GCC build." dd6ab35 Set the mastering metadata on the muxers colour 8b61ef5 mkvmuxer: Fix GCC build. 353b050 Add hdr_util. c92e080 mkvmuxer: Use kValueNotPresent in Colour/MasteringMetadata. 2d09128 Colour element: TransferFunction renamed to TransferCharacteristics. f2fc28e Colour element: Matrix renamed to MatrixCoefficients. e0b1135 cmake: Minor CMakeLists.txt refactor. 1e1872b Revert change from auto_ptr to unique_ptr in sample code. d7fc382 Track updates to the proposed Matroska Colour spec. 99981ee sample(mkvparser): Output Colour element when present. 375e416 mkvmuxer: Fix Colour element support. eaeca34 mkvmuxer: Fix bits per channel in the colour element. 1dab7f3 mkvparser: Avoid crash when encountering a Colour element. a1517aa sample_muxer: copy the Colour element. ea9dd94 Merge "webm2pes: Fix tests." 8635c5b Merge "mkvparser: Make omitted values detectable in the Colour element." ae4ae7e mkvparser: Make omitted values detectable in the Colour element. 8c8cba6 webm2pes: Fix tests. a281a22 mkvmuxer: Add support for the Colour element and its children. 41a9147 sample_muxer: clang-format include order fix. 939a64d Signal E_BUFFER_NOT_FULL in EBMLHeader::Parse fb1406e mkvparser: Add support for the Colour element and its children. 22bfdf7 Merge "parser_tests: Add validation of cues." b873000 parser_tests: Add validation of cues. 799891e Update .gitignore to include some new binaries e051c60 Merge "Update muxer test gold files" b81d5f0 Update muxer test gold files 48b1e9a mkvparser: clang format run 93c4690 webm2pes: Add PES packet parsing tests. 65ca38f Merge "test_util: Fix gcc build." 520ca6c Merge "parser_tests: Fix gcc build." 37a38ca test_util: Fix gcc build. ee0ebba parser_tests: Fix gcc build. c32f970 Replace auto_ptr usage with unique_ptr. e569ab0 webm2pes/ts: Fix gcc build. 2e55d6c Merge "add bitcode embedding support for ios" 0cfb2dc add bitcode embedding support for ios bb8cefd webm2ts: Converts WebM VPx video to a MPEG TS. 453bf44 webm2pes: Begin addition of tests. 9299bbb libwebm: Googletest integration. 3bec1ba Merge changes I7bcb5b3e,I8ce733be,I98a928ff,I71910f24 5c83bbe Fix ParseElementHeader to support 0 payload elements be35869 libwebm_utils: Add FileDeleter. d6db1e1 webm2pes: Add a WebM parser init method. aa3593e webm2pes: Rename Convert to ConvertToFile(). e8fca12 webm2pes: Fix super frame splitting. 3cb96b6 webm2pes: Move main() and helper functions into their own files. 021432b webm2pes: Fix the linux build. 82ac5fc Remove RELEASE.TXT. 852e173 webm2pes: Split super frames and packetize large frames. faf85c2 webm2pes: Refactor header/optional header writing. 7c19266 Add Webm2Pes. 01fdee4 mkvmuxer: Disallow AddTrack() after Tracks element is output. 1ad314e mkvparser: EBMLHeader::Parse: remove dead init Change-Id: I13637ceacab953786064b7f8f6ecf1495b7475d0
--- a/examples.mk
+++ b/examples.mk
@@ -36,22 +36,31 @@
third_party/libyuv/source/scale_neon64.cc \
third_party/libyuv/source/scale_win.cc \
-LIBWEBM_COMMON_SRCS += third_party/libwebm/webmids.hpp
+LIBWEBM_COMMON_SRCS += third_party/libwebm/common/hdr_util.cc \
+ third_party/libwebm/common/hdr_util.h \
+ third_party/libwebm/common/webmids.h
-LIBWEBM_MUXER_SRCS += third_party/libwebm/mkvmuxer.cpp \
- third_party/libwebm/mkvmuxerutil.cpp \
- third_party/libwebm/mkvwriter.cpp \
- third_party/libwebm/mkvmuxer.hpp \
- third_party/libwebm/mkvmuxertypes.hpp \
- third_party/libwebm/mkvmuxerutil.hpp \
- third_party/libwebm/mkvparser.hpp \
- third_party/libwebm/mkvwriter.hpp
+LIBWEBM_MUXER_SRCS += third_party/libwebm/mkvmuxer/mkvmuxer.cc \
+ third_party/libwebm/mkvmuxer/mkvmuxerutil.cc \
+ third_party/libwebm/mkvmuxer/mkvwriter.cc \
+ third_party/libwebm/mkvmuxer/mkvmuxer.h \
+ third_party/libwebm/mkvmuxer/mkvmuxertypes.h \
+ third_party/libwebm/mkvmuxer/mkvmuxerutil.h \
+ third_party/libwebm/mkvparser/mkvparser.h \
+ third_party/libwebm/mkvmuxer/mkvwriter.h
-LIBWEBM_PARSER_SRCS = third_party/libwebm/mkvparser.cpp \
- third_party/libwebm/mkvreader.cpp \
- third_party/libwebm/mkvparser.hpp \
- third_party/libwebm/mkvreader.hpp
+LIBWEBM_PARSER_SRCS = third_party/libwebm/mkvparser/mkvparser.cc \
+ third_party/libwebm/mkvparser/mkvreader.cc \
+ third_party/libwebm/mkvparser/mkvparser.h \
+ third_party/libwebm/mkvparser/mkvreader.h
+# Add compile flags and include path for libwebm sources.
+ifeq ($(CONFIG_WEBM_IO),yes)
+ CXXFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS
+ INC_PATH-yes += $(SRC_PATH_BARE)/third_party/libwebm
+endif
+
+
# List of examples to build. UTILS are tools meant for distribution
# while EXAMPLES demonstrate specific portions of the API.
UTILS-$(CONFIG_DECODERS) += vpxdec.c
@@ -70,6 +79,7 @@
endif
ifeq ($(CONFIG_WEBM_IO),yes)
vpxdec.SRCS += $(LIBWEBM_COMMON_SRCS)
+ vpxdec.SRCS += $(LIBWEBM_MUXER_SRCS)
vpxdec.SRCS += $(LIBWEBM_PARSER_SRCS)
vpxdec.SRCS += webmdec.cc webmdec.h
endif
@@ -93,6 +103,7 @@
ifeq ($(CONFIG_WEBM_IO),yes)
vpxenc.SRCS += $(LIBWEBM_COMMON_SRCS)
vpxenc.SRCS += $(LIBWEBM_MUXER_SRCS)
+ vpxenc.SRCS += $(LIBWEBM_PARSER_SRCS)
vpxenc.SRCS += webmenc.cc webmenc.h
endif
vpxenc.GUID = 548DEC74-7A15-4B2B-AFC3-AA102E7C25C1
--- a/libs.mk
+++ b/libs.mk
@@ -394,6 +394,12 @@
$(shell $(SRC_PATH_BARE)/build/make/version.sh "$(SRC_PATH_BARE)" $(BUILD_PFX)vpx_version.h)
CLEAN-OBJS += $(BUILD_PFX)vpx_version.h
+#
+# Add include path for libwebm sources.
+#
+ifeq ($(CONFIG_WEBM_IO),yes)
+ CXXFLAGS += -I$(SRC_PATH_BARE)/third_party/libwebm
+endif
##
## libvpx test directives
@@ -469,6 +475,7 @@
$(if $(CONFIG_STATIC_MSVCRT),--static-crt) \
--out=$@ $(INTERNAL_CFLAGS) $(CFLAGS) \
-I. -I"$(SRC_PATH_BARE)/third_party/googletest/src/include" \
+ $(if $(CONFIG_WEBM_IO),-I"$(SRC_PATH_BARE)/third_party/libwebm") \
-L. -l$(CODEC_LIB) -l$(GTEST_LIB) $^
PROJECTS-$(CONFIG_MSVS) += test_libvpx.$(VCPROJ_SFX)
--- a/test/test.mk
+++ b/test/test.mk
@@ -59,10 +59,10 @@
## WebM Parsing
ifeq ($(CONFIG_WEBM_IO), yes)
-LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvparser.cpp
-LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvreader.cpp
-LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvparser.hpp
-LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvreader.hpp
+LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvparser/mkvparser.cc
+LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvparser/mkvreader.cc
+LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvparser/mkvparser.h
+LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvparser/mkvreader.h
LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += $(LIBWEBM_PARSER_SRCS)
LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ../tools_common.h
LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ../webmdec.cc
--- a/third_party/libwebm/Android.mk
+++ b/third_party/libwebm/Android.mk
@@ -2,9 +2,16 @@
include $(CLEAR_VARS)
LOCAL_MODULE:= libwebm
-LOCAL_SRC_FILES:= mkvparser.cpp \
- mkvreader.cpp \
- mkvmuxer.cpp \
- mkvmuxerutil.cpp \
- mkvwriter.cpp
+LOCAL_CPPFLAGS:=-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
+LOCAL_CPPFLAGS+=-D__STDC_LIMIT_MACROS -Wno-extern-c-compat
+LOCAL_C_INCLUDES:= $(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)
+
+LOCAL_SRC_FILES:= common/file_util.cc \
+ common/hdr_util.cc \
+ mkvparser/mkvparser.cc \
+ mkvparser/mkvreader.cc \
+ mkvmuxer/mkvmuxer.cc \
+ mkvmuxer/mkvmuxerutil.cc \
+ mkvmuxer/mkvwriter.cc
include $(BUILD_STATIC_LIBRARY)
--- a/third_party/libwebm/README.libvpx
+++ b/third_party/libwebm/README.libvpx
@@ -1,5 +1,5 @@
URL: https://chromium.googlesource.com/webm/libwebm
-Version: 476366249e1fda7710a389cd41c57db42305e0d4
+Version: eb50da8e38633d987aabcc2fe60c9a72bad753ae
License: BSD
License File: LICENSE.txt
--- a/third_party/libwebm/RELEASE.TXT
+++ /dev/null
@@ -1,34 +1,0 @@
-1.0.0.5
- * Handled case when no duration
- * Handled empty clusters
- * Handled empty clusters when seeking
- * Implemented check lacing bits
-
-1.0.0.4
- * Made Cues member variables mutables
- * Defined against badly-formatted cue points
- * Segment::GetCluster returns CuePoint too
- * Separated cue-based searches
-
-1.0.0.3
- * Added Block::GetOffset() to get a frame's offset in a block
- * Changed cluster count type from size_t to long
- * Parsed SeekHead to find cues
- * Allowed seeking beyond end of cluster cache
- * Added not to attempt to reparse cues element
- * Restructured Segment::LoadCluster
- * Marked position of cues without parsing cues element
- * Allowed cue points to be loaded incrementally
- * Implemented to load lazily cue points as they're searched
- * Merged Cues::LoadCuePoint into Cues::Find
- * Lazy init cues
- * Loaded cue point during find
-
-1.0.0.2
- * added support for Cues element
- * seeking was improved
-
-1.0.0.1
- * fixed item 141
- * added item 142
- * added this file, RELEASE.TXT, to repository
--- /dev/null
+++ b/third_party/libwebm/common/file_util.cc
@@ -1,0 +1,67 @@
+// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#include "common/file_util.h"
+
+#include <sys/stat.h>
+#ifndef _MSC_VER
+#include <unistd.h> // close()
+#endif
+
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+#include <ios>
+
+namespace libwebm {
+
+std::string GetTempFileName() {
+#if !defined _MSC_VER && !defined __MINGW32__
+ char temp_file_name_template[] = "libwebm_temp.XXXXXX";
+ int fd = mkstemp(temp_file_name_template);
+ if (fd != -1) {
+ close(fd);
+ return std::string(temp_file_name_template);
+ }
+ return std::string();
+#else
+ char tmp_file_name[_MAX_PATH];
+ errno_t err = tmpnam_s(tmp_file_name);
+ if (err == 0) {
+ return std::string(tmp_file_name);
+ }
+ return std::string();
+#endif
+}
+
+uint64_t GetFileSize(const std::string& file_name) {
+ uint64_t file_size = 0;
+#ifndef _MSC_VER
+ struct stat st;
+ st.st_size = 0;
+ if (stat(file_name.c_str(), &st) == 0) {
+#else
+ struct _stat st;
+ st.st_size = 0;
+ if (_stat(file_name.c_str(), &st) == 0) {
+#endif
+ file_size = st.st_size;
+ }
+ return file_size;
+}
+
+TempFileDeleter::TempFileDeleter() { file_name_ = GetTempFileName(); }
+
+TempFileDeleter::~TempFileDeleter() {
+ std::ifstream file(file_name_.c_str());
+ if (file.good()) {
+ file.close();
+ std::remove(file_name_.c_str());
+ }
+}
+
+} // namespace libwebm
--- /dev/null
+++ b/third_party/libwebm/common/file_util.h
@@ -1,0 +1,41 @@
+// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#ifndef LIBWEBM_COMMON_FILE_UTIL_H_
+#define LIBWEBM_COMMON_FILE_UTIL_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "mkvmuxer/mkvmuxertypes.h" // LIBWEBM_DISALLOW_COPY_AND_ASSIGN()
+
+namespace libwebm {
+
+// Returns a temporary file name.
+std::string GetTempFileName();
+
+// Returns size of file specified by |file_name|, or 0 upon failure.
+uint64_t GetFileSize(const std::string& file_name);
+
+// Manages life of temporary file specified at time of construction. Deletes
+// file upon destruction.
+class TempFileDeleter {
+ public:
+ TempFileDeleter();
+ explicit TempFileDeleter(std::string file_name) : file_name_(file_name) {}
+ ~TempFileDeleter();
+ const std::string& name() const { return file_name_; }
+
+ private:
+ std::string file_name_;
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TempFileDeleter);
+};
+
+} // namespace libwebm
+
+#endif // LIBWEBM_COMMON_FILE_UTIL_H_
\ No newline at end of file
--- /dev/null
+++ b/third_party/libwebm/common/hdr_util.cc
@@ -1,0 +1,182 @@
+// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#include "hdr_util.h"
+
+#include <cstddef>
+#include <new>
+
+#include "mkvparser/mkvparser.h"
+
+namespace libwebm {
+bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc,
+ PrimaryChromaticityPtr* muxer_pc) {
+ muxer_pc->reset(new (std::nothrow)
+ mkvmuxer::PrimaryChromaticity(parser_pc.x, parser_pc.y));
+ if (!muxer_pc->get())
+ return false;
+ return true;
+}
+
+bool MasteringMetadataValuePresent(double value) {
+ return value != mkvparser::MasteringMetadata::kValueNotPresent;
+}
+
+bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm,
+ mkvmuxer::MasteringMetadata* muxer_mm) {
+ if (MasteringMetadataValuePresent(parser_mm.luminance_max))
+ muxer_mm->luminance_max = parser_mm.luminance_max;
+ if (MasteringMetadataValuePresent(parser_mm.luminance_min))
+ muxer_mm->luminance_min = parser_mm.luminance_min;
+
+ PrimaryChromaticityPtr r_ptr(NULL);
+ PrimaryChromaticityPtr g_ptr(NULL);
+ PrimaryChromaticityPtr b_ptr(NULL);
+ PrimaryChromaticityPtr wp_ptr(NULL);
+
+ if (parser_mm.r) {
+ if (!CopyPrimaryChromaticity(*parser_mm.r, &r_ptr))
+ return false;
+ }
+ if (parser_mm.g) {
+ if (!CopyPrimaryChromaticity(*parser_mm.g, &g_ptr))
+ return false;
+ }
+ if (parser_mm.b) {
+ if (!CopyPrimaryChromaticity(*parser_mm.b, &b_ptr))
+ return false;
+ }
+ if (parser_mm.white_point) {
+ if (!CopyPrimaryChromaticity(*parser_mm.white_point, &wp_ptr))
+ return false;
+ }
+
+ if (!muxer_mm->SetChromaticity(r_ptr.get(), g_ptr.get(), b_ptr.get(),
+ wp_ptr.get())) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ColourValuePresent(long long value) {
+ return value != mkvparser::Colour::kValueNotPresent;
+}
+
+bool CopyColour(const mkvparser::Colour& parser_colour,
+ mkvmuxer::Colour* muxer_colour) {
+ if (!muxer_colour)
+ return false;
+
+ if (ColourValuePresent(parser_colour.matrix_coefficients))
+ muxer_colour->matrix_coefficients = parser_colour.matrix_coefficients;
+ if (ColourValuePresent(parser_colour.bits_per_channel))
+ muxer_colour->bits_per_channel = parser_colour.bits_per_channel;
+ if (ColourValuePresent(parser_colour.chroma_subsampling_horz))
+ muxer_colour->chroma_subsampling_horz =
+ parser_colour.chroma_subsampling_horz;
+ if (ColourValuePresent(parser_colour.chroma_subsampling_vert))
+ muxer_colour->chroma_subsampling_vert =
+ parser_colour.chroma_subsampling_vert;
+ if (ColourValuePresent(parser_colour.cb_subsampling_horz))
+ muxer_colour->cb_subsampling_horz = parser_colour.cb_subsampling_horz;
+ if (ColourValuePresent(parser_colour.cb_subsampling_vert))
+ muxer_colour->cb_subsampling_vert = parser_colour.cb_subsampling_vert;
+ if (ColourValuePresent(parser_colour.chroma_siting_horz))
+ muxer_colour->chroma_siting_horz = parser_colour.chroma_siting_horz;
+ if (ColourValuePresent(parser_colour.chroma_siting_vert))
+ muxer_colour->chroma_siting_vert = parser_colour.chroma_siting_vert;
+ if (ColourValuePresent(parser_colour.range))
+ muxer_colour->range = parser_colour.range;
+ if (ColourValuePresent(parser_colour.transfer_characteristics))
+ muxer_colour->transfer_characteristics =
+ parser_colour.transfer_characteristics;
+ if (ColourValuePresent(parser_colour.primaries))
+ muxer_colour->primaries = parser_colour.primaries;
+ if (ColourValuePresent(parser_colour.max_cll))
+ muxer_colour->max_cll = parser_colour.max_cll;
+ if (ColourValuePresent(parser_colour.max_fall))
+ muxer_colour->max_fall = parser_colour.max_fall;
+
+ if (parser_colour.mastering_metadata) {
+ mkvmuxer::MasteringMetadata muxer_mm;
+ if (!CopyMasteringMetadata(*parser_colour.mastering_metadata, &muxer_mm))
+ return false;
+ if (!muxer_colour->SetMasteringMetadata(muxer_mm))
+ return false;
+ }
+ return true;
+}
+
+// Format of VPx private data:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID Byte | Length | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | |
+// : Bytes 1..Length of Codec Feature :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// ID Byte Format
+// ID byte is an unsigned byte.
+// 0 1 2 3 4 5 6 7
+// +-+-+-+-+-+-+-+-+
+// |X| ID |
+// +-+-+-+-+-+-+-+-+
+//
+// The X bit is reserved.
+//
+// Currently only profile level is supported. ID byte must be set to 1, and
+// length must be 1. Supported values are:
+//
+// 10: Level 1
+// 11: Level 1.1
+// 20: Level 2
+// 21: Level 2.1
+// 30: Level 3
+// 31: Level 3.1
+// 40: Level 4
+// 41: Level 4.1
+// 50: Level 5
+// 51: Level 5.1
+// 52: Level 5.2
+// 60: Level 6
+// 61: Level 6.1
+// 62: Level 6.2
+//
+// See the following link for more information:
+// http://www.webmproject.org/vp9/profiles/
+int ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length) {
+ const int kVpxCodecPrivateLength = 3;
+ if (!private_data || length != kVpxCodecPrivateLength)
+ return 0;
+
+ const uint8_t id_byte = *private_data;
+ if (id_byte != 1)
+ return 0;
+
+ const int kVpxProfileLength = 1;
+ const uint8_t length_byte = private_data[1];
+ if (length_byte != kVpxProfileLength)
+ return 0;
+
+ const int level = static_cast<int>(private_data[2]);
+
+ const int kNumLevels = 14;
+ const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40,
+ 41, 50, 51, 52, 60, 61, 62};
+
+ for (int i = 0; i < kNumLevels; ++i) {
+ if (level == levels[i])
+ return level;
+ }
+
+ return 0;
+}
+} // namespace libwebm
--- /dev/null
+++ b/third_party/libwebm/common/hdr_util.h
@@ -1,0 +1,51 @@
+// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#ifndef LIBWEBM_COMMON_HDR_UTIL_H_
+#define LIBWEBM_COMMON_HDR_UTIL_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "mkvmuxer/mkvmuxer.h"
+
+namespace mkvparser {
+struct Colour;
+struct MasteringMetadata;
+struct PrimaryChromaticity;
+} // namespace mkvparser
+
+namespace libwebm {
+// Utility types and functions for working with the Colour element and its
+// children. Copiers return true upon success. Presence functions return true
+// when the specified element is present.
+
+// TODO(tomfinegan): These should be moved to libwebm_utils once c++11 is
+// required by libwebm.
+
+typedef std::auto_ptr<mkvmuxer::PrimaryChromaticity> PrimaryChromaticityPtr;
+
+bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc,
+ PrimaryChromaticityPtr* muxer_pc);
+
+bool MasteringMetadataValuePresent(double value);
+
+bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm,
+ mkvmuxer::MasteringMetadata* muxer_mm);
+
+bool ColourValuePresent(long long value);
+
+bool CopyColour(const mkvparser::Colour& parser_colour,
+ mkvmuxer::Colour* muxer_colour);
+
+// Returns VP9 profile upon success or 0 upon failure.
+int ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length);
+
+} // namespace libwebm
+
+#endif // LIBWEBM_COMMON_HDR_UTIL_H_
--- /dev/null
+++ b/third_party/libwebm/common/webmids.h
@@ -1,0 +1,184 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef COMMON_WEBMIDS_H_
+#define COMMON_WEBMIDS_H_
+
+namespace libwebm {
+
+enum MkvId {
+ kMkvEBML = 0x1A45DFA3,
+ kMkvEBMLVersion = 0x4286,
+ kMkvEBMLReadVersion = 0x42F7,
+ kMkvEBMLMaxIDLength = 0x42F2,
+ kMkvEBMLMaxSizeLength = 0x42F3,
+ kMkvDocType = 0x4282,
+ kMkvDocTypeVersion = 0x4287,
+ kMkvDocTypeReadVersion = 0x4285,
+ kMkvVoid = 0xEC,
+ kMkvSignatureSlot = 0x1B538667,
+ kMkvSignatureAlgo = 0x7E8A,
+ kMkvSignatureHash = 0x7E9A,
+ kMkvSignaturePublicKey = 0x7EA5,
+ kMkvSignature = 0x7EB5,
+ kMkvSignatureElements = 0x7E5B,
+ kMkvSignatureElementList = 0x7E7B,
+ kMkvSignedElement = 0x6532,
+ // segment
+ kMkvSegment = 0x18538067,
+ // Meta Seek Information
+ kMkvSeekHead = 0x114D9B74,
+ kMkvSeek = 0x4DBB,
+ kMkvSeekID = 0x53AB,
+ kMkvSeekPosition = 0x53AC,
+ // Segment Information
+ kMkvInfo = 0x1549A966,
+ kMkvTimecodeScale = 0x2AD7B1,
+ kMkvDuration = 0x4489,
+ kMkvDateUTC = 0x4461,
+ kMkvTitle = 0x7BA9,
+ kMkvMuxingApp = 0x4D80,
+ kMkvWritingApp = 0x5741,
+ // Cluster
+ kMkvCluster = 0x1F43B675,
+ kMkvTimecode = 0xE7,
+ kMkvPrevSize = 0xAB,
+ kMkvBlockGroup = 0xA0,
+ kMkvBlock = 0xA1,
+ kMkvBlockDuration = 0x9B,
+ kMkvReferenceBlock = 0xFB,
+ kMkvLaceNumber = 0xCC,
+ kMkvSimpleBlock = 0xA3,
+ kMkvBlockAdditions = 0x75A1,
+ kMkvBlockMore = 0xA6,
+ kMkvBlockAddID = 0xEE,
+ kMkvBlockAdditional = 0xA5,
+ kMkvDiscardPadding = 0x75A2,
+ // Track
+ kMkvTracks = 0x1654AE6B,
+ kMkvTrackEntry = 0xAE,
+ kMkvTrackNumber = 0xD7,
+ kMkvTrackUID = 0x73C5,
+ kMkvTrackType = 0x83,
+ kMkvFlagEnabled = 0xB9,
+ kMkvFlagDefault = 0x88,
+ kMkvFlagForced = 0x55AA,
+ kMkvFlagLacing = 0x9C,
+ kMkvDefaultDuration = 0x23E383,
+ kMkvMaxBlockAdditionID = 0x55EE,
+ kMkvName = 0x536E,
+ kMkvLanguage = 0x22B59C,
+ kMkvCodecID = 0x86,
+ kMkvCodecPrivate = 0x63A2,
+ kMkvCodecName = 0x258688,
+ kMkvCodecDelay = 0x56AA,
+ kMkvSeekPreRoll = 0x56BB,
+ // video
+ kMkvVideo = 0xE0,
+ kMkvFlagInterlaced = 0x9A,
+ kMkvStereoMode = 0x53B8,
+ kMkvAlphaMode = 0x53C0,
+ kMkvPixelWidth = 0xB0,
+ kMkvPixelHeight = 0xBA,
+ kMkvPixelCropBottom = 0x54AA,
+ kMkvPixelCropTop = 0x54BB,
+ kMkvPixelCropLeft = 0x54CC,
+ kMkvPixelCropRight = 0x54DD,
+ kMkvDisplayWidth = 0x54B0,
+ kMkvDisplayHeight = 0x54BA,
+ kMkvDisplayUnit = 0x54B2,
+ kMkvAspectRatioType = 0x54B3,
+ kMkvFrameRate = 0x2383E3,
+ // end video
+ // colour
+ kMkvColour = 0x55B0,
+ kMkvMatrixCoefficients = 0x55B1,
+ kMkvBitsPerChannel = 0x55B2,
+ kMkvChromaSubsamplingHorz = 0x55B3,
+ kMkvChromaSubsamplingVert = 0x55B4,
+ kMkvCbSubsamplingHorz = 0x55B5,
+ kMkvCbSubsamplingVert = 0x55B6,
+ kMkvChromaSitingHorz = 0x55B7,
+ kMkvChromaSitingVert = 0x55B8,
+ kMkvRange = 0x55B9,
+ kMkvTransferCharacteristics = 0x55BA,
+ kMkvPrimaries = 0x55BB,
+ kMkvMaxCLL = 0x55BC,
+ kMkvMaxFALL = 0x55BD,
+ // mastering metadata
+ kMkvMasteringMetadata = 0x55D0,
+ kMkvPrimaryRChromaticityX = 0x55D1,
+ kMkvPrimaryRChromaticityY = 0x55D2,
+ kMkvPrimaryGChromaticityX = 0x55D3,
+ kMkvPrimaryGChromaticityY = 0x55D4,
+ kMkvPrimaryBChromaticityX = 0x55D5,
+ kMkvPrimaryBChromaticityY = 0x55D6,
+ kMkvWhitePointChromaticityX = 0x55D7,
+ kMkvWhitePointChromaticityY = 0x55D8,
+ kMkvLuminanceMax = 0x55D9,
+ kMkvLuminanceMin = 0x55DA,
+ // end mastering metadata
+ // end colour
+ // audio
+ kMkvAudio = 0xE1,
+ kMkvSamplingFrequency = 0xB5,
+ kMkvOutputSamplingFrequency = 0x78B5,
+ kMkvChannels = 0x9F,
+ kMkvBitDepth = 0x6264,
+ // end audio
+ // ContentEncodings
+ kMkvContentEncodings = 0x6D80,
+ kMkvContentEncoding = 0x6240,
+ kMkvContentEncodingOrder = 0x5031,
+ kMkvContentEncodingScope = 0x5032,
+ kMkvContentEncodingType = 0x5033,
+ kMkvContentCompression = 0x5034,
+ kMkvContentCompAlgo = 0x4254,
+ kMkvContentCompSettings = 0x4255,
+ kMkvContentEncryption = 0x5035,
+ kMkvContentEncAlgo = 0x47E1,
+ kMkvContentEncKeyID = 0x47E2,
+ kMkvContentSignature = 0x47E3,
+ kMkvContentSigKeyID = 0x47E4,
+ kMkvContentSigAlgo = 0x47E5,
+ kMkvContentSigHashAlgo = 0x47E6,
+ kMkvContentEncAESSettings = 0x47E7,
+ kMkvAESSettingsCipherMode = 0x47E8,
+ kMkvAESSettingsCipherInitData = 0x47E9,
+ // end ContentEncodings
+ // Cueing Data
+ kMkvCues = 0x1C53BB6B,
+ kMkvCuePoint = 0xBB,
+ kMkvCueTime = 0xB3,
+ kMkvCueTrackPositions = 0xB7,
+ kMkvCueTrack = 0xF7,
+ kMkvCueClusterPosition = 0xF1,
+ kMkvCueBlockNumber = 0x5378,
+ // Chapters
+ kMkvChapters = 0x1043A770,
+ kMkvEditionEntry = 0x45B9,
+ kMkvChapterAtom = 0xB6,
+ kMkvChapterUID = 0x73C4,
+ kMkvChapterStringUID = 0x5654,
+ kMkvChapterTimeStart = 0x91,
+ kMkvChapterTimeEnd = 0x92,
+ kMkvChapterDisplay = 0x80,
+ kMkvChapString = 0x85,
+ kMkvChapLanguage = 0x437C,
+ kMkvChapCountry = 0x437E,
+ // Tags
+ kMkvTags = 0x1254C367,
+ kMkvTag = 0x7373,
+ kMkvSimpleTag = 0x67C8,
+ kMkvTagName = 0x45A3,
+ kMkvTagString = 0x4487
+};
+
+} // namespace libwebm
+
+#endif // COMMON_WEBMIDS_H_
--- a/third_party/libwebm/mkvmuxer.cpp
+++ /dev/null
@@ -1,3277 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#include "mkvmuxer.hpp"
-
-#include <climits>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <ctime>
-#include <new>
-
-#include "mkvmuxerutil.hpp"
-#include "mkvparser.hpp"
-#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 {
-// Deallocate the string designated by |dst|, and then copy the |src|
-// string to |dst|. The caller owns both the |src| string and the
-// |dst| copy (hence the caller is responsible for eventually
-// deallocating the strings, either directly, or indirectly via
-// StrCpy). Returns true if the source string was successfully copied
-// to the destination.
-bool StrCpy(const char* src, char** dst_ptr) {
- if (dst_ptr == NULL)
- return false;
-
- char*& dst = *dst_ptr;
-
- delete[] dst;
- dst = NULL;
-
- if (src == NULL)
- return true;
-
- const size_t size = strlen(src) + 1;
-
- dst = new (std::nothrow) char[size]; // NOLINT
- if (dst == NULL)
- return false;
-
- strcpy(dst, src); // NOLINT
- return true;
-}
-} // namespace
-
-///////////////////////////////////////////////////////////////
-//
-// IMkvWriter Class
-
-IMkvWriter::IMkvWriter() {}
-
-IMkvWriter::~IMkvWriter() {}
-
-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, doc_type_version);
- size += EbmlElementSize(kMkvDocTypeReadVersion, 2ULL);
-
- if (!WriteEbmlMasterElement(writer, kMkvEBML, size))
- return false;
- if (!WriteEbmlElement(writer, kMkvEBMLVersion, 1ULL))
- return false;
- if (!WriteEbmlElement(writer, kMkvEBMLReadVersion, 1ULL))
- return false;
- if (!WriteEbmlElement(writer, kMkvEBMLMaxIDLength, 4ULL))
- return false;
- if (!WriteEbmlElement(writer, kMkvEBMLMaxSizeLength, 8ULL))
- return false;
- if (!WriteEbmlElement(writer, kMkvDocType, "webm"))
- return false;
- if (!WriteEbmlElement(writer, kMkvDocTypeVersion, doc_type_version))
- return false;
- if (!WriteEbmlElement(writer, kMkvDocTypeReadVersion, 2ULL))
- return false;
-
- 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.
- const uint32 kBufSize = 2048;
- uint8* buf = new uint8[kBufSize];
- int64 offset = start;
- while (size > 0) {
- const int64 read_len = (size > kBufSize) ? kBufSize : size;
- if (source->Read(offset, static_cast<long>(read_len), buf))
- return false;
- dst->Write(buf, static_cast<uint32>(read_len));
- offset += read_len;
- size -= read_len;
- }
- delete[] buf;
- return true;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// Frame Class
-
-Frame::Frame()
- : add_id_(0),
- additional_(NULL),
- additional_length_(0),
- duration_(0),
- frame_(NULL),
- is_key_(false),
- length_(0),
- track_number_(0),
- timestamp_(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
- if (!data)
- return false;
-
- delete[] frame_;
- frame_ = data;
- length_ = length;
-
- memcpy(frame_, frame, static_cast<size_t>(length_));
- return true;
-}
-
-bool Frame::AddAdditionalData(const uint8* additional, uint64 length,
- uint64 add_id) {
- uint8* const data =
- new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
- if (!data)
- return false;
-
- delete[] additional_;
- additional_ = data;
- additional_length_ = length;
- add_id_ = add_id;
-
- memcpy(additional_, additional, static_cast<size_t>(additional_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
-
-CuePoint::CuePoint()
- : time_(0),
- track_(0),
- cluster_pos_(0),
- block_number_(1),
- output_block_number_(true) {}
-
-CuePoint::~CuePoint() {}
-
-bool CuePoint::Write(IMkvWriter* writer) const {
- if (!writer || track_ < 1 || cluster_pos_ < 1)
- return false;
-
- uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_);
- size += EbmlElementSize(kMkvCueTrack, track_);
- if (output_block_number_ && block_number_ > 1)
- size += EbmlElementSize(kMkvCueBlockNumber, block_number_);
- const uint64 track_pos_size =
- EbmlMasterElementSize(kMkvCueTrackPositions, size) + size;
- const uint64 payload_size =
- EbmlElementSize(kMkvCueTime, time_) + track_pos_size;
-
- if (!WriteEbmlMasterElement(writer, kMkvCuePoint, payload_size))
- return false;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- if (!WriteEbmlElement(writer, kMkvCueTime, time_))
- return false;
-
- if (!WriteEbmlMasterElement(writer, kMkvCueTrackPositions, size))
- return false;
- if (!WriteEbmlElement(writer, kMkvCueTrack, track_))
- return false;
- if (!WriteEbmlElement(writer, kMkvCueClusterPosition, cluster_pos_))
- return false;
- if (output_block_number_ && block_number_ > 1)
- if (!WriteEbmlElement(writer, kMkvCueBlockNumber, block_number_))
- return false;
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0)
- return false;
-
- if (stop_position - payload_position != static_cast<int64>(payload_size))
- return false;
-
- return true;
-}
-
-uint64 CuePoint::PayloadSize() const {
- uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_);
- size += EbmlElementSize(kMkvCueTrack, track_);
- if (output_block_number_ && block_number_ > 1)
- size += EbmlElementSize(kMkvCueBlockNumber, block_number_);
- const uint64 track_pos_size =
- EbmlMasterElementSize(kMkvCueTrackPositions, size) + size;
- const uint64 payload_size =
- EbmlElementSize(kMkvCueTime, time_) + track_pos_size;
-
- return payload_size;
-}
-
-uint64 CuePoint::Size() const {
- const uint64 payload_size = PayloadSize();
- return EbmlMasterElementSize(kMkvCuePoint, payload_size) + payload_size;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// Cues Class
-
-Cues::Cues()
- : cue_entries_capacity_(0),
- cue_entries_size_(0),
- cue_entries_(NULL),
- output_block_number_(true) {}
-
-Cues::~Cues() {
- if (cue_entries_) {
- for (int32 i = 0; i < cue_entries_size_; ++i) {
- CuePoint* const cue = cue_entries_[i];
- delete cue;
- }
- delete[] cue_entries_;
- }
-}
-
-bool Cues::AddCue(CuePoint* cue) {
- if (!cue)
- return false;
-
- if ((cue_entries_size_ + 1) > cue_entries_capacity_) {
- // Add more CuePoints.
- const int32 new_capacity =
- (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2;
-
- if (new_capacity < 1)
- return false;
-
- CuePoint** const cues =
- new (std::nothrow) CuePoint*[new_capacity]; // NOLINT
- if (!cues)
- return false;
-
- for (int32 i = 0; i < cue_entries_size_; ++i) {
- cues[i] = cue_entries_[i];
- }
-
- delete[] cue_entries_;
-
- cue_entries_ = cues;
- cue_entries_capacity_ = new_capacity;
- }
-
- cue->set_output_block_number(output_block_number_);
- cue_entries_[cue_entries_size_++] = cue;
- return true;
-}
-
-CuePoint* Cues::GetCueByIndex(int32 index) const {
- if (cue_entries_ == NULL)
- return NULL;
-
- if (index >= cue_entries_size_)
- return NULL;
-
- return cue_entries_[index];
-}
-
-uint64 Cues::Size() {
- uint64 size = 0;
- for (int32 i = 0; i < cue_entries_size_; ++i)
- size += GetCueByIndex(i)->Size();
- size += EbmlMasterElementSize(kMkvCues, size);
- return size;
-}
-
-bool Cues::Write(IMkvWriter* writer) const {
- if (!writer)
- return false;
-
- uint64 size = 0;
- for (int32 i = 0; i < cue_entries_size_; ++i) {
- const CuePoint* const cue = GetCueByIndex(i);
-
- if (!cue)
- return false;
-
- size += cue->Size();
- }
-
- if (!WriteEbmlMasterElement(writer, kMkvCues, size))
- return false;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- for (int32 i = 0; i < cue_entries_size_; ++i) {
- const CuePoint* const cue = GetCueByIndex(i);
-
- if (!cue->Write(writer))
- return false;
- }
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0)
- return false;
-
- if (stop_position - payload_position != static_cast<int64>(size))
- return false;
-
- return true;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// ContentEncAESSettings Class
-
-ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
-
-uint64 ContentEncAESSettings::Size() const {
- const uint64 payload = PayloadSize();
- const uint64 size =
- EbmlMasterElementSize(kMkvContentEncAESSettings, payload) + payload;
- return size;
-}
-
-bool ContentEncAESSettings::Write(IMkvWriter* writer) const {
- const uint64 payload = PayloadSize();
-
- if (!WriteEbmlMasterElement(writer, kMkvContentEncAESSettings, payload))
- return false;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- if (!WriteEbmlElement(writer, kMkvAESSettingsCipherMode, cipher_mode_))
- return false;
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(payload))
- return false;
-
- return true;
-}
-
-uint64 ContentEncAESSettings::PayloadSize() const {
- uint64 size = EbmlElementSize(kMkvAESSettingsCipherMode, cipher_mode_);
- return size;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// ContentEncoding Class
-
-ContentEncoding::ContentEncoding()
- : enc_algo_(5),
- enc_key_id_(NULL),
- encoding_order_(0),
- encoding_scope_(1),
- encoding_type_(1),
- enc_key_id_length_(0) {}
-
-ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
-
-bool ContentEncoding::SetEncryptionID(const uint8* id, uint64 length) {
- if (!id || length < 1)
- return false;
-
- delete[] enc_key_id_;
-
- enc_key_id_ =
- new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
- if (!enc_key_id_)
- return false;
-
- memcpy(enc_key_id_, id, static_cast<size_t>(length));
- enc_key_id_length_ = length;
-
- return true;
-}
-
-uint64 ContentEncoding::Size() const {
- const uint64 encryption_size = EncryptionSize();
- const uint64 encoding_size = EncodingSize(0, encryption_size);
- const uint64 encodings_size =
- EbmlMasterElementSize(kMkvContentEncoding, encoding_size) + encoding_size;
-
- return encodings_size;
-}
-
-bool ContentEncoding::Write(IMkvWriter* writer) const {
- const uint64 encryption_size = EncryptionSize();
- const uint64 encoding_size = EncodingSize(0, encryption_size);
- const uint64 size =
- EbmlMasterElementSize(kMkvContentEncoding, encoding_size) + encoding_size;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- if (!WriteEbmlMasterElement(writer, kMkvContentEncoding, encoding_size))
- return false;
- if (!WriteEbmlElement(writer, kMkvContentEncodingOrder, encoding_order_))
- return false;
- if (!WriteEbmlElement(writer, kMkvContentEncodingScope, encoding_scope_))
- return false;
- if (!WriteEbmlElement(writer, kMkvContentEncodingType, encoding_type_))
- return false;
-
- if (!WriteEbmlMasterElement(writer, kMkvContentEncryption, encryption_size))
- return false;
- if (!WriteEbmlElement(writer, kMkvContentEncAlgo, enc_algo_))
- return false;
- if (!WriteEbmlElement(writer, kMkvContentEncKeyID, enc_key_id_,
- enc_key_id_length_))
- return false;
-
- if (!enc_aes_settings_.Write(writer))
- return false;
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(size))
- return false;
-
- return true;
-}
-
-uint64 ContentEncoding::EncodingSize(uint64 compresion_size,
- uint64 encryption_size) const {
- // TODO(fgalligan): Add support for compression settings.
- if (compresion_size != 0)
- return 0;
-
- uint64 encoding_size = 0;
-
- if (encryption_size > 0) {
- encoding_size +=
- EbmlMasterElementSize(kMkvContentEncryption, encryption_size) +
- encryption_size;
- }
- encoding_size += EbmlElementSize(kMkvContentEncodingType, encoding_type_);
- encoding_size += EbmlElementSize(kMkvContentEncodingScope, encoding_scope_);
- encoding_size += EbmlElementSize(kMkvContentEncodingOrder, encoding_order_);
-
- return encoding_size;
-}
-
-uint64 ContentEncoding::EncryptionSize() const {
- const uint64 aes_size = enc_aes_settings_.Size();
-
- uint64 encryption_size =
- EbmlElementSize(kMkvContentEncKeyID, enc_key_id_, enc_key_id_length_);
- encryption_size += EbmlElementSize(kMkvContentEncAlgo, enc_algo_);
-
- return encryption_size + aes_size;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// Track Class
-
-Track::Track(unsigned int* seed)
- : codec_id_(NULL),
- codec_private_(NULL),
- language_(NULL),
- max_block_additional_id_(0),
- name_(NULL),
- number_(0),
- type_(0),
- uid_(MakeUID(seed)),
- codec_delay_(0),
- seek_pre_roll_(0),
- default_duration_(0),
- codec_private_length_(0),
- content_encoding_entries_(NULL),
- content_encoding_entries_size_(0) {}
-
-Track::~Track() {
- delete[] codec_id_;
- delete[] codec_private_;
- delete[] language_;
- delete[] name_;
-
- if (content_encoding_entries_) {
- for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
- ContentEncoding* const encoding = content_encoding_entries_[i];
- delete encoding;
- }
- delete[] content_encoding_entries_;
- }
-}
-
-bool Track::AddContentEncoding() {
- const uint32 count = content_encoding_entries_size_ + 1;
-
- ContentEncoding** const content_encoding_entries =
- new (std::nothrow) ContentEncoding*[count]; // NOLINT
- if (!content_encoding_entries)
- return false;
-
- ContentEncoding* const content_encoding =
- new (std::nothrow) ContentEncoding(); // NOLINT
- if (!content_encoding) {
- delete[] content_encoding_entries;
- return false;
- }
-
- for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
- content_encoding_entries[i] = content_encoding_entries_[i];
- }
-
- delete[] content_encoding_entries_;
-
- content_encoding_entries_ = content_encoding_entries;
- content_encoding_entries_[content_encoding_entries_size_] = content_encoding;
- content_encoding_entries_size_ = count;
- return true;
-}
-
-ContentEncoding* Track::GetContentEncodingByIndex(uint32 index) const {
- if (content_encoding_entries_ == NULL)
- return NULL;
-
- if (index >= content_encoding_entries_size_)
- return NULL;
-
- return content_encoding_entries_[index];
-}
-
-uint64 Track::PayloadSize() const {
- uint64 size = EbmlElementSize(kMkvTrackNumber, number_);
- size += EbmlElementSize(kMkvTrackUID, uid_);
- size += EbmlElementSize(kMkvTrackType, type_);
- if (codec_id_)
- size += EbmlElementSize(kMkvCodecID, codec_id_);
- if (codec_private_)
- size += EbmlElementSize(kMkvCodecPrivate, codec_private_,
- codec_private_length_);
- if (language_)
- size += EbmlElementSize(kMkvLanguage, language_);
- if (name_)
- size += EbmlElementSize(kMkvName, name_);
- if (max_block_additional_id_)
- size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_);
- if (codec_delay_)
- size += EbmlElementSize(kMkvCodecDelay, codec_delay_);
- if (seek_pre_roll_)
- size += EbmlElementSize(kMkvSeekPreRoll, seek_pre_roll_);
- if (default_duration_)
- size += EbmlElementSize(kMkvDefaultDuration, default_duration_);
-
- if (content_encoding_entries_size_ > 0) {
- uint64 content_encodings_size = 0;
- for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
- ContentEncoding* const encoding = content_encoding_entries_[i];
- content_encodings_size += encoding->Size();
- }
-
- size +=
- EbmlMasterElementSize(kMkvContentEncodings, content_encodings_size) +
- content_encodings_size;
- }
-
- return size;
-}
-
-uint64 Track::Size() const {
- uint64 size = PayloadSize();
- size += EbmlMasterElementSize(kMkvTrackEntry, size);
- return size;
-}
-
-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();
-
- if (!WriteEbmlMasterElement(writer, kMkvTrackEntry, payload_size))
- return false;
-
- uint64 size = EbmlElementSize(kMkvTrackNumber, number_);
- size += EbmlElementSize(kMkvTrackUID, uid_);
- size += EbmlElementSize(kMkvTrackType, type_);
- if (codec_id_)
- size += EbmlElementSize(kMkvCodecID, codec_id_);
- if (codec_private_)
- size += EbmlElementSize(kMkvCodecPrivate, codec_private_,
- codec_private_length_);
- if (language_)
- size += EbmlElementSize(kMkvLanguage, language_);
- if (name_)
- size += EbmlElementSize(kMkvName, name_);
- if (max_block_additional_id_)
- size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_);
- if (codec_delay_)
- size += EbmlElementSize(kMkvCodecDelay, codec_delay_);
- if (seek_pre_roll_)
- size += EbmlElementSize(kMkvSeekPreRoll, seek_pre_roll_);
- if (default_duration_)
- size += EbmlElementSize(kMkvDefaultDuration, default_duration_);
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- if (!WriteEbmlElement(writer, kMkvTrackNumber, number_))
- return false;
- if (!WriteEbmlElement(writer, kMkvTrackUID, uid_))
- return false;
- if (!WriteEbmlElement(writer, kMkvTrackType, type_))
- return false;
- if (max_block_additional_id_) {
- if (!WriteEbmlElement(writer, kMkvMaxBlockAdditionID,
- max_block_additional_id_)) {
- return false;
- }
- }
- if (codec_delay_) {
- if (!WriteEbmlElement(writer, kMkvCodecDelay, codec_delay_))
- return false;
- }
- if (seek_pre_roll_) {
- if (!WriteEbmlElement(writer, kMkvSeekPreRoll, seek_pre_roll_))
- return false;
- }
- if (default_duration_) {
- if (!WriteEbmlElement(writer, kMkvDefaultDuration, default_duration_))
- return false;
- }
- if (codec_id_) {
- if (!WriteEbmlElement(writer, kMkvCodecID, codec_id_))
- return false;
- }
- if (codec_private_) {
- if (!WriteEbmlElement(writer, kMkvCodecPrivate, codec_private_,
- codec_private_length_))
- return false;
- }
- if (language_) {
- if (!WriteEbmlElement(writer, kMkvLanguage, language_))
- return false;
- }
- if (name_) {
- if (!WriteEbmlElement(writer, kMkvName, name_))
- return false;
- }
-
- int64 stop_position = writer->Position();
- if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(size))
- return false;
-
- if (content_encoding_entries_size_ > 0) {
- uint64 content_encodings_size = 0;
- for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
- ContentEncoding* const encoding = content_encoding_entries_[i];
- content_encodings_size += encoding->Size();
- }
-
- if (!WriteEbmlMasterElement(writer, kMkvContentEncodings,
- content_encodings_size))
- return false;
-
- for (uint32 i = 0; i < content_encoding_entries_size_; ++i) {
- ContentEncoding* const encoding = content_encoding_entries_[i];
- if (!encoding->Write(writer))
- return false;
- }
- }
-
- stop_position = writer->Position();
- if (stop_position < 0)
- return false;
- return true;
-}
-
-bool Track::SetCodecPrivate(const uint8* codec_private, uint64 length) {
- if (!codec_private || length < 1)
- return false;
-
- delete[] codec_private_;
-
- codec_private_ =
- new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT
- if (!codec_private_)
- return false;
-
- memcpy(codec_private_, codec_private, static_cast<size_t>(length));
- codec_private_length_ = length;
-
- return true;
-}
-
-void Track::set_codec_id(const char* codec_id) {
- if (codec_id) {
- delete[] codec_id_;
-
- const size_t length = strlen(codec_id) + 1;
- codec_id_ = new (std::nothrow) char[length]; // NOLINT
- if (codec_id_) {
-#ifdef _MSC_VER
- strcpy_s(codec_id_, length, codec_id);
-#else
- strcpy(codec_id_, codec_id);
-#endif
- }
- }
-}
-
-// TODO(fgalligan): Vet the language parameter.
-void Track::set_language(const char* language) {
- if (language) {
- delete[] language_;
-
- const size_t length = strlen(language) + 1;
- language_ = new (std::nothrow) char[length]; // NOLINT
- if (language_) {
-#ifdef _MSC_VER
- strcpy_s(language_, length, language);
-#else
- strcpy(language_, language);
-#endif
- }
- }
-}
-
-void Track::set_name(const char* name) {
- if (name) {
- delete[] name_;
-
- const size_t length = strlen(name) + 1;
- name_ = new (std::nothrow) char[length]; // NOLINT
- if (name_) {
-#ifdef _MSC_VER
- strcpy_s(name_, length, name);
-#else
- strcpy(name_, name);
-#endif
- }
- }
-}
-
-///////////////////////////////////////////////////////////////
-//
-// VideoTrack Class
-
-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),
- alpha_mode_(0),
- width_(0) {}
-
-VideoTrack::~VideoTrack() {}
-
-bool VideoTrack::SetStereoMode(uint64 stereo_mode) {
- if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst &&
- stereo_mode != kTopBottomRightIsFirst &&
- stereo_mode != kTopBottomLeftIsFirst &&
- stereo_mode != kSideBySideRightIsFirst)
- return false;
-
- stereo_mode_ = stereo_mode;
- return true;
-}
-
-bool VideoTrack::SetAlphaMode(uint64 alpha_mode) {
- if (alpha_mode != kNoAlpha && alpha_mode != kAlpha)
- return false;
-
- alpha_mode_ = alpha_mode;
- return true;
-}
-
-uint64 VideoTrack::PayloadSize() const {
- const uint64 parent_size = Track::PayloadSize();
-
- uint64 size = VideoPayloadSize();
- size += EbmlMasterElementSize(kMkvVideo, size);
-
- return parent_size + size;
-}
-
-bool VideoTrack::Write(IMkvWriter* writer) const {
- if (!Track::Write(writer))
- return false;
-
- const uint64 size = VideoPayloadSize();
-
- if (!WriteEbmlMasterElement(writer, kMkvVideo, size))
- return false;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- if (!WriteEbmlElement(writer, kMkvPixelWidth, width_))
- return false;
- if (!WriteEbmlElement(writer, kMkvPixelHeight, height_))
- return false;
- if (display_width_ > 0) {
- if (!WriteEbmlElement(writer, kMkvDisplayWidth, display_width_))
- return false;
- }
- if (display_height_ > 0) {
- if (!WriteEbmlElement(writer, kMkvDisplayHeight, display_height_))
- return false;
- }
- 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 (!WriteEbmlElement(writer, kMkvAlphaMode, alpha_mode_))
- return false;
- }
- if (frame_rate_ > 0.0) {
- if (!WriteEbmlElement(writer, kMkvFrameRate,
- 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)) {
- return false;
- }
-
- return true;
-}
-
-uint64 VideoTrack::VideoPayloadSize() const {
- uint64 size = EbmlElementSize(kMkvPixelWidth, width_);
- size += EbmlElementSize(kMkvPixelHeight, height_);
- if (display_width_ > 0)
- 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)
- size += EbmlElementSize(kMkvAlphaMode, alpha_mode_);
- if (frame_rate_ > 0.0)
- size += EbmlElementSize(kMkvFrameRate, static_cast<float>(frame_rate_));
-
- return size;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// AudioTrack Class
-
-AudioTrack::AudioTrack(unsigned int* seed)
- : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
-
-AudioTrack::~AudioTrack() {}
-
-uint64 AudioTrack::PayloadSize() const {
- const uint64 parent_size = Track::PayloadSize();
-
- uint64 size =
- EbmlElementSize(kMkvSamplingFrequency, static_cast<float>(sample_rate_));
- size += EbmlElementSize(kMkvChannels, channels_);
- if (bit_depth_ > 0)
- size += EbmlElementSize(kMkvBitDepth, bit_depth_);
- size += EbmlMasterElementSize(kMkvAudio, size);
-
- return parent_size + size;
-}
-
-bool AudioTrack::Write(IMkvWriter* writer) const {
- if (!Track::Write(writer))
- return false;
-
- // Calculate AudioSettings size.
- uint64 size =
- EbmlElementSize(kMkvSamplingFrequency, static_cast<float>(sample_rate_));
- size += EbmlElementSize(kMkvChannels, channels_);
- if (bit_depth_ > 0)
- size += EbmlElementSize(kMkvBitDepth, bit_depth_);
-
- if (!WriteEbmlMasterElement(writer, kMkvAudio, size))
- return false;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- if (!WriteEbmlElement(writer, kMkvSamplingFrequency,
- static_cast<float>(sample_rate_)))
- return false;
- if (!WriteEbmlElement(writer, kMkvChannels, channels_))
- return false;
- if (bit_depth_ > 0)
- if (!WriteEbmlElement(writer, kMkvBitDepth, bit_depth_))
- return false;
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(size))
- return false;
-
- return true;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// Tracks Class
-
-const char Tracks::kOpusCodecId[] = "A_OPUS";
-const char Tracks::kVorbisCodecId[] = "A_VORBIS";
-const char Tracks::kVp8CodecId[] = "V_VP8";
-const char Tracks::kVp9CodecId[] = "V_VP9";
-const char Tracks::kVp10CodecId[] = "V_VP10";
-
-Tracks::Tracks() : track_entries_(NULL), track_entries_size_(0) {}
-
-Tracks::~Tracks() {
- if (track_entries_) {
- for (uint32 i = 0; i < track_entries_size_; ++i) {
- Track* const track = track_entries_[i];
- delete track;
- }
- delete[] track_entries_;
- }
-}
-
-bool Tracks::AddTrack(Track* track, int32 number) {
- if (number < 0)
- return false;
-
- // This muxer only supports track numbers in the range [1, 126], in
- // order to be able (to use Matroska integer representation) to
- // serialize the block header (of which the track number is a part)
- // for a frame using exactly 4 bytes.
-
- if (number > 0x7E)
- return false;
-
- uint32 track_num = number;
-
- if (track_num > 0) {
- // Check to make sure a track does not already have |track_num|.
- for (uint32 i = 0; i < track_entries_size_; ++i) {
- if (track_entries_[i]->number() == track_num)
- return false;
- }
- }
-
- const uint32 count = track_entries_size_ + 1;
-
- Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
- if (!track_entries)
- return false;
-
- for (uint32 i = 0; i < track_entries_size_; ++i) {
- track_entries[i] = track_entries_[i];
- }
-
- delete[] track_entries_;
-
- // Find the lowest availible track number > 0.
- if (track_num == 0) {
- track_num = count;
-
- // Check to make sure a track does not already have |track_num|.
- bool exit = false;
- do {
- exit = true;
- for (uint32 i = 0; i < track_entries_size_; ++i) {
- if (track_entries[i]->number() == track_num) {
- track_num++;
- exit = false;
- break;
- }
- }
- } while (!exit);
- }
- track->set_number(track_num);
-
- track_entries_ = track_entries;
- track_entries_[track_entries_size_] = track;
- track_entries_size_ = count;
- return true;
-}
-
-const Track* Tracks::GetTrackByIndex(uint32 index) const {
- if (track_entries_ == NULL)
- return NULL;
-
- if (index >= track_entries_size_)
- return NULL;
-
- return track_entries_[index];
-}
-
-Track* Tracks::GetTrackByNumber(uint64 track_number) const {
- const int32 count = track_entries_size();
- for (int32 i = 0; i < count; ++i) {
- if (track_entries_[i]->number() == track_number)
- return track_entries_[i];
- }
-
- return NULL;
-}
-
-bool Tracks::TrackIsAudio(uint64 track_number) const {
- const Track* const track = GetTrackByNumber(track_number);
-
- if (track->type() == kAudio)
- return true;
-
- return false;
-}
-
-bool Tracks::TrackIsVideo(uint64 track_number) const {
- const Track* const track = GetTrackByNumber(track_number);
-
- if (track->type() == kVideo)
- return true;
-
- return false;
-}
-
-bool Tracks::Write(IMkvWriter* writer) const {
- uint64 size = 0;
- const int32 count = track_entries_size();
- for (int32 i = 0; i < count; ++i) {
- const Track* const track = GetTrackByIndex(i);
-
- if (!track)
- return false;
-
- size += track->Size();
- }
-
- if (!WriteEbmlMasterElement(writer, kMkvTracks, size))
- return false;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- for (int32 i = 0; i < count; ++i) {
- const Track* const track = GetTrackByIndex(i);
- if (!track->Write(writer))
- return false;
- }
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(size))
- return false;
-
- return true;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// Chapter Class
-
-bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
-
-void Chapter::set_time(const Segment& segment, uint64 start_ns, uint64 end_ns) {
- const SegmentInfo* const info = segment.GetSegmentInfo();
- const uint64 timecode_scale = info->timecode_scale();
- start_timecode_ = start_ns / timecode_scale;
- end_timecode_ = end_ns / timecode_scale;
-}
-
-bool Chapter::add_string(const char* title, const char* language,
- const char* country) {
- if (!ExpandDisplaysArray())
- return false;
-
- Display& d = displays_[displays_count_++];
- d.Init();
-
- if (!d.set_title(title))
- return false;
-
- if (!d.set_language(language))
- return false;
-
- if (!d.set_country(country))
- return false;
-
- return true;
-}
-
-Chapter::Chapter() {
- // This ctor only constructs the object. Proper initialization is
- // done in Init() (called in Chapters::AddChapter()). The only
- // reason we bother implementing this ctor is because we had to
- // declare it as private (along with the dtor), in order to prevent
- // clients from creating Chapter instances (a privelege we grant
- // only to the Chapters class). Doing no initialization here also
- // means that creating arrays of chapter objects is more efficient,
- // because we only initialize each new chapter object as it becomes
- // active on the array.
-}
-
-Chapter::~Chapter() {}
-
-void Chapter::Init(unsigned int* seed) {
- id_ = NULL;
- start_timecode_ = 0;
- end_timecode_ = 0;
- displays_ = NULL;
- displays_size_ = 0;
- displays_count_ = 0;
- uid_ = MakeUID(seed);
-}
-
-void Chapter::ShallowCopy(Chapter* dst) const {
- dst->id_ = id_;
- dst->start_timecode_ = start_timecode_;
- dst->end_timecode_ = end_timecode_;
- dst->uid_ = uid_;
- dst->displays_ = displays_;
- dst->displays_size_ = displays_size_;
- dst->displays_count_ = displays_count_;
-}
-
-void Chapter::Clear() {
- StrCpy(NULL, &id_);
-
- while (displays_count_ > 0) {
- Display& d = displays_[--displays_count_];
- d.Clear();
- }
-
- delete[] displays_;
- displays_ = NULL;
-
- displays_size_ = 0;
-}
-
-bool Chapter::ExpandDisplaysArray() {
- if (displays_size_ > displays_count_)
- return true; // nothing to do yet
-
- const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
-
- Display* const displays = new (std::nothrow) Display[size]; // NOLINT
- if (displays == NULL)
- return false;
-
- for (int idx = 0; idx < displays_count_; ++idx) {
- displays[idx] = displays_[idx]; // shallow copy
- }
-
- delete[] displays_;
-
- displays_ = displays;
- displays_size_ = size;
-
- return true;
-}
-
-uint64 Chapter::WriteAtom(IMkvWriter* writer) const {
- uint64 payload_size = EbmlElementSize(kMkvChapterStringUID, id_) +
- EbmlElementSize(kMkvChapterUID, uid_) +
- EbmlElementSize(kMkvChapterTimeStart, start_timecode_) +
- EbmlElementSize(kMkvChapterTimeEnd, end_timecode_);
-
- for (int idx = 0; idx < displays_count_; ++idx) {
- const Display& d = displays_[idx];
- payload_size += d.WriteDisplay(NULL);
- }
-
- const uint64 atom_size =
- EbmlMasterElementSize(kMkvChapterAtom, payload_size) + payload_size;
-
- if (writer == NULL)
- return atom_size;
-
- const int64 start = writer->Position();
-
- if (!WriteEbmlMasterElement(writer, kMkvChapterAtom, payload_size))
- return 0;
-
- if (!WriteEbmlElement(writer, kMkvChapterStringUID, id_))
- return 0;
-
- if (!WriteEbmlElement(writer, kMkvChapterUID, uid_))
- return 0;
-
- if (!WriteEbmlElement(writer, kMkvChapterTimeStart, start_timecode_))
- return 0;
-
- if (!WriteEbmlElement(writer, kMkvChapterTimeEnd, end_timecode_))
- return 0;
-
- for (int idx = 0; idx < displays_count_; ++idx) {
- const Display& d = displays_[idx];
-
- if (!d.WriteDisplay(writer))
- return 0;
- }
-
- const int64 stop = writer->Position();
-
- if (stop >= start && uint64(stop - start) != atom_size)
- return 0;
-
- return atom_size;
-}
-
-void Chapter::Display::Init() {
- title_ = NULL;
- language_ = NULL;
- country_ = NULL;
-}
-
-void Chapter::Display::Clear() {
- StrCpy(NULL, &title_);
- StrCpy(NULL, &language_);
- StrCpy(NULL, &country_);
-}
-
-bool Chapter::Display::set_title(const char* title) {
- return StrCpy(title, &title_);
-}
-
-bool Chapter::Display::set_language(const char* language) {
- return StrCpy(language, &language_);
-}
-
-bool Chapter::Display::set_country(const char* country) {
- return StrCpy(country, &country_);
-}
-
-uint64 Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
- uint64 payload_size = EbmlElementSize(kMkvChapString, title_);
-
- if (language_)
- payload_size += EbmlElementSize(kMkvChapLanguage, language_);
-
- if (country_)
- payload_size += EbmlElementSize(kMkvChapCountry, country_);
-
- const uint64 display_size =
- EbmlMasterElementSize(kMkvChapterDisplay, payload_size) + payload_size;
-
- if (writer == NULL)
- return display_size;
-
- const int64 start = writer->Position();
-
- if (!WriteEbmlMasterElement(writer, kMkvChapterDisplay, payload_size))
- return 0;
-
- if (!WriteEbmlElement(writer, kMkvChapString, title_))
- return 0;
-
- if (language_) {
- if (!WriteEbmlElement(writer, kMkvChapLanguage, language_))
- return 0;
- }
-
- if (country_) {
- if (!WriteEbmlElement(writer, kMkvChapCountry, country_))
- return 0;
- }
-
- const int64 stop = writer->Position();
-
- if (stop >= start && uint64(stop - start) != display_size)
- return 0;
-
- return display_size;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// Chapters Class
-
-Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
-
-Chapters::~Chapters() {
- while (chapters_count_ > 0) {
- Chapter& chapter = chapters_[--chapters_count_];
- chapter.Clear();
- }
-
- delete[] chapters_;
- chapters_ = NULL;
-}
-
-int Chapters::Count() const { return chapters_count_; }
-
-Chapter* Chapters::AddChapter(unsigned int* seed) {
- if (!ExpandChaptersArray())
- return NULL;
-
- Chapter& chapter = chapters_[chapters_count_++];
- chapter.Init(seed);
-
- return &chapter;
-}
-
-bool Chapters::Write(IMkvWriter* writer) const {
- if (writer == NULL)
- return false;
-
- const uint64 payload_size = WriteEdition(NULL); // return size only
-
- if (!WriteEbmlMasterElement(writer, kMkvChapters, payload_size))
- return false;
-
- const int64 start = writer->Position();
-
- if (WriteEdition(writer) == 0) // error
- return false;
-
- const int64 stop = writer->Position();
-
- if (stop >= start && uint64(stop - start) != payload_size)
- return false;
-
- return true;
-}
-
-bool Chapters::ExpandChaptersArray() {
- if (chapters_size_ > chapters_count_)
- return true; // nothing to do yet
-
- const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
-
- Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
- if (chapters == NULL)
- return false;
-
- for (int idx = 0; idx < chapters_count_; ++idx) {
- const Chapter& src = chapters_[idx];
- Chapter* const dst = chapters + idx;
- src.ShallowCopy(dst);
- }
-
- delete[] chapters_;
-
- chapters_ = chapters;
- chapters_size_ = size;
-
- return true;
-}
-
-uint64 Chapters::WriteEdition(IMkvWriter* writer) const {
- uint64 payload_size = 0;
-
- for (int idx = 0; idx < chapters_count_; ++idx) {
- const Chapter& chapter = chapters_[idx];
- payload_size += chapter.WriteAtom(NULL);
- }
-
- const uint64 edition_size =
- EbmlMasterElementSize(kMkvEditionEntry, payload_size) + payload_size;
-
- if (writer == NULL) // return size only
- return edition_size;
-
- const int64 start = writer->Position();
-
- if (!WriteEbmlMasterElement(writer, kMkvEditionEntry, payload_size))
- return 0; // error
-
- for (int idx = 0; idx < chapters_count_; ++idx) {
- const Chapter& chapter = chapters_[idx];
-
- const uint64 chapter_size = chapter.WriteAtom(writer);
- if (chapter_size == 0) // error
- return 0;
- }
-
- const int64 stop = writer->Position();
-
- if (stop >= start && uint64(stop - start) != edition_size)
- return 0;
-
- 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, uint64 timecode_scale)
- : blocks_added_(0),
- finalized_(false),
- header_written_(false),
- payload_size_(0),
- position_for_cues_(cues_pos),
- size_position_(-1),
- timecode_(timecode),
- timecode_scale_(timecode_scale),
- writer_(NULL) {}
-
-Cluster::~Cluster() {}
-
-bool Cluster::Init(IMkvWriter* ptr_writer) {
- if (!ptr_writer) {
- return false;
- }
- writer_ = ptr_writer;
- return true;
-}
-
-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) {
- 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* data, uint64 length,
- const uint8* additional,
- uint64 additional_length, uint64 add_id,
- uint64 track_number, uint64 abs_timecode,
- bool is_key) {
- 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* data, uint64 length,
- int64 discard_padding,
- uint64 track_number,
- uint64 abs_timecode, bool is_key) {
- 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* 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; }
-
-bool Cluster::Finalize() {
- if (!writer_ || finalized_ || size_position_ == -1)
- return false;
-
- if (writer_->Seekable()) {
- const int64 pos = writer_->Position();
-
- if (writer_->Position(size_position_))
- return false;
-
- if (WriteUIntSize(writer_, payload_size(), 8))
- return false;
-
- if (writer_->Position(pos))
- return false;
- }
-
- finalized_ = true;
-
- return true;
-}
-
-uint64 Cluster::Size() const {
- const uint64 element_size =
- EbmlMasterElementSize(kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) + payload_size_;
- return element_size;
-}
-
-bool Cluster::PreWriteBlock() {
- if (finalized_)
- return false;
-
- if (!header_written_) {
- if (!WriteClusterHeader())
- return false;
- }
-
- return true;
-}
-
-void Cluster::PostWriteBlock(uint64 element_size) {
- AddPayloadSize(element_size);
- ++blocks_added_;
-}
-
-int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const {
- const int64 cluster_timecode = this->Cluster::timecode();
- const int64 rel_timecode =
- static_cast<int64>(abs_timecode) - cluster_timecode;
-
- if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
- return -1;
-
- return rel_timecode;
-}
-
-bool Cluster::DoWriteFrame(const Frame* const frame) {
- if (!frame || !frame->IsValid())
- return false;
-
- if (!PreWriteBlock())
- return false;
-
- const uint64 element_size = WriteFrame(writer_, frame, this);
- if (element_size == 0)
- return false;
-
- PostWriteBlock(element_size);
- return true;
-}
-
-bool Cluster::WriteClusterHeader() {
- if (finalized_)
- return false;
-
- if (WriteID(writer_, kMkvCluster))
- return false;
-
- // Save for later.
- size_position_ = writer_->Position();
-
- // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
- // bytes because we do not know how big our cluster will be.
- if (SerializeInt(writer_, kEbmlUnknownValue, 8))
- return false;
-
- if (!WriteEbmlElement(writer_, kMkvTimecode, timecode()))
- return false;
- AddPayloadSize(EbmlElementSize(kMkvTimecode, timecode()));
- header_written_ = true;
-
- return true;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// SeekHead Class
-
-SeekHead::SeekHead() : start_pos_(0ULL) {
- for (int32 i = 0; i < kSeekEntryCount; ++i) {
- seek_entry_id_[i] = 0;
- seek_entry_pos_[i] = 0;
- }
-}
-
-SeekHead::~SeekHead() {}
-
-bool SeekHead::Finalize(IMkvWriter* writer) const {
- if (writer->Seekable()) {
- if (start_pos_ == -1)
- return false;
-
- uint64 payload_size = 0;
- uint64 entry_size[kSeekEntryCount];
-
- for (int32 i = 0; i < kSeekEntryCount; ++i) {
- if (seek_entry_id_[i] != 0) {
- entry_size[i] =
- EbmlElementSize(kMkvSeekID, static_cast<uint64>(seek_entry_id_[i]));
- entry_size[i] += EbmlElementSize(kMkvSeekPosition, seek_entry_pos_[i]);
-
- payload_size +=
- EbmlMasterElementSize(kMkvSeek, entry_size[i]) + entry_size[i];
- }
- }
-
- // No SeekHead elements
- if (payload_size == 0)
- return true;
-
- const int64 pos = writer->Position();
- if (writer->Position(start_pos_))
- return false;
-
- if (!WriteEbmlMasterElement(writer, kMkvSeekHead, payload_size))
- return false;
-
- for (int32 i = 0; i < kSeekEntryCount; ++i) {
- if (seek_entry_id_[i] != 0) {
- if (!WriteEbmlMasterElement(writer, kMkvSeek, entry_size[i]))
- return false;
-
- if (!WriteEbmlElement(writer, kMkvSeekID,
- static_cast<uint64>(seek_entry_id_[i])))
- return false;
-
- if (!WriteEbmlElement(writer, kMkvSeekPosition, seek_entry_pos_[i]))
- return false;
- }
- }
-
- const uint64 total_entry_size = kSeekEntryCount * MaxEntrySize();
- const uint64 total_size =
- EbmlMasterElementSize(kMkvSeekHead, total_entry_size) +
- total_entry_size;
- const int64 size_left = total_size - (writer->Position() - start_pos_);
-
- const uint64 bytes_written = WriteVoidElement(writer, size_left);
- if (!bytes_written)
- return false;
-
- if (writer->Position(pos))
- return false;
- }
-
- return true;
-}
-
-bool SeekHead::Write(IMkvWriter* writer) {
- const uint64 entry_size = kSeekEntryCount * MaxEntrySize();
- const uint64 size = EbmlMasterElementSize(kMkvSeekHead, entry_size);
-
- start_pos_ = writer->Position();
-
- const uint64 bytes_written = WriteVoidElement(writer, size + entry_size);
- if (!bytes_written)
- return false;
-
- return true;
-}
-
-bool SeekHead::AddSeekEntry(uint32 id, uint64 pos) {
- for (int32 i = 0; i < kSeekEntryCount; ++i) {
- if (seek_entry_id_[i] == 0) {
- seek_entry_id_[i] = id;
- seek_entry_pos_[i] = pos;
- return true;
- }
- }
- return false;
-}
-
-uint32 SeekHead::GetId(int index) const {
- if (index < 0 || index >= kSeekEntryCount)
- return UINT_MAX;
- return seek_entry_id_[index];
-}
-
-uint64 SeekHead::GetPosition(int index) const {
- if (index < 0 || index >= kSeekEntryCount)
- return ULLONG_MAX;
- return seek_entry_pos_[index];
-}
-
-bool SeekHead::SetSeekEntry(int index, uint32 id, uint64 position) {
- if (index < 0 || index >= kSeekEntryCount)
- return false;
- seek_entry_id_[index] = id;
- seek_entry_pos_[index] = position;
- return true;
-}
-
-uint64 SeekHead::MaxEntrySize() const {
- const uint64 max_entry_payload_size =
- EbmlElementSize(kMkvSeekID, 0xffffffffULL) +
- EbmlElementSize(kMkvSeekPosition, 0xffffffffffffffffULL);
- const uint64 max_entry_size =
- EbmlMasterElementSize(kMkvSeek, max_entry_payload_size) +
- max_entry_payload_size;
-
- return max_entry_size;
-}
-
-///////////////////////////////////////////////////////////////
-//
-// SegmentInfo Class
-
-SegmentInfo::SegmentInfo()
- : duration_(-1.0),
- muxing_app_(NULL),
- timecode_scale_(1000000ULL),
- writing_app_(NULL),
- date_utc_(LLONG_MIN),
- duration_pos_(-1) {}
-
-SegmentInfo::~SegmentInfo() {
- delete[] muxing_app_;
- delete[] writing_app_;
-}
-
-bool SegmentInfo::Init() {
- int32 major;
- int32 minor;
- int32 build;
- int32 revision;
- GetVersion(&major, &minor, &build, &revision);
- char temp[256];
-#ifdef _MSC_VER
- sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
- minor, build, revision);
-#else
- snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
- minor, build, revision);
-#endif
-
- const size_t app_len = strlen(temp) + 1;
-
- delete[] muxing_app_;
-
- muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
- if (!muxing_app_)
- return false;
-
-#ifdef _MSC_VER
- strcpy_s(muxing_app_, app_len, temp);
-#else
- strcpy(muxing_app_, temp);
-#endif
-
- set_writing_app(temp);
- if (!writing_app_)
- return false;
- return true;
-}
-
-bool SegmentInfo::Finalize(IMkvWriter* writer) const {
- if (!writer)
- return false;
-
- if (duration_ > 0.0) {
- if (writer->Seekable()) {
- if (duration_pos_ == -1)
- return false;
-
- const int64 pos = writer->Position();
-
- if (writer->Position(duration_pos_))
- return false;
-
- if (!WriteEbmlElement(writer, kMkvDuration,
- static_cast<float>(duration_)))
- return false;
-
- if (writer->Position(pos))
- return false;
- }
- }
-
- return true;
-}
-
-bool SegmentInfo::Write(IMkvWriter* writer) {
- if (!writer || !muxing_app_ || !writing_app_)
- return false;
-
- uint64 size = EbmlElementSize(kMkvTimecodeScale, timecode_scale_);
- if (duration_ > 0.0)
- size += EbmlElementSize(kMkvDuration, static_cast<float>(duration_));
- if (date_utc_ != LLONG_MIN)
- size += EbmlDateElementSize(kMkvDateUTC);
- size += EbmlElementSize(kMkvMuxingApp, muxing_app_);
- size += EbmlElementSize(kMkvWritingApp, writing_app_);
-
- if (!WriteEbmlMasterElement(writer, kMkvInfo, size))
- return false;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return false;
-
- if (!WriteEbmlElement(writer, kMkvTimecodeScale, timecode_scale_))
- return false;
-
- if (duration_ > 0.0) {
- // Save for later
- duration_pos_ = writer->Position();
-
- if (!WriteEbmlElement(writer, kMkvDuration, static_cast<float>(duration_)))
- return false;
- }
-
- if (date_utc_ != LLONG_MIN)
- WriteEbmlDateElement(writer, kMkvDateUTC, date_utc_);
-
- if (!WriteEbmlElement(writer, kMkvMuxingApp, muxing_app_))
- return false;
- if (!WriteEbmlElement(writer, kMkvWritingApp, writing_app_))
- return false;
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(size))
- return false;
-
- return true;
-}
-
-void SegmentInfo::set_muxing_app(const char* app) {
- if (app) {
- const size_t length = strlen(app) + 1;
- char* temp_str = new (std::nothrow) char[length]; // NOLINT
- if (!temp_str)
- return;
-
-#ifdef _MSC_VER
- strcpy_s(temp_str, length, app);
-#else
- strcpy(temp_str, app);
-#endif
-
- delete[] muxing_app_;
- muxing_app_ = temp_str;
- }
-}
-
-void SegmentInfo::set_writing_app(const char* app) {
- if (app) {
- const size_t length = strlen(app) + 1;
- char* temp_str = new (std::nothrow) char[length]; // NOLINT
- if (!temp_str)
- return;
-
-#ifdef _MSC_VER
- strcpy_s(temp_str, length, app);
-#else
- strcpy(temp_str, app);
-#endif
-
- delete[] writing_app_;
- writing_app_ = temp_str;
- }
-}
-
-///////////////////////////////////////////////////////////////
-//
-// Segment Class
-
-Segment::Segment()
- : chunk_count_(0),
- chunk_name_(NULL),
- chunk_writer_cluster_(NULL),
- chunk_writer_cues_(NULL),
- chunk_writer_header_(NULL),
- chunking_(false),
- chunking_base_name_(NULL),
- cluster_list_(NULL),
- cluster_list_capacity_(0),
- cluster_list_size_(0),
- cues_position_(kAfterClusters),
- cues_track_(0),
- force_new_cluster_(false),
- frames_(NULL),
- frames_capacity_(0),
- frames_size_(0),
- has_video_(false),
- header_written_(false),
- last_block_duration_(0),
- last_timestamp_(0),
- max_cluster_duration_(kDefaultMaxClusterDuration),
- max_cluster_size_(0),
- mode_(kFile),
- new_cuepoint_(false),
- 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) {
- const time_t curr_time = time(NULL);
- seed_ = static_cast<unsigned int>(curr_time);
-#ifdef _WIN32
- srand(seed_);
-#endif
-}
-
-Segment::~Segment() {
- if (cluster_list_) {
- for (int32 i = 0; i < cluster_list_size_; ++i) {
- Cluster* const cluster = cluster_list_[i];
- delete cluster;
- }
- delete[] cluster_list_;
- }
-
- if (frames_) {
- for (int32 i = 0; i < frames_size_; ++i) {
- Frame* const frame = frames_[i];
- delete frame;
- }
- delete[] frames_;
- }
-
- delete[] chunk_name_;
- delete[] chunking_base_name_;
-
- if (chunk_writer_cluster_) {
- chunk_writer_cluster_->Close();
- delete chunk_writer_cluster_;
- }
- if (chunk_writer_cues_) {
- chunk_writer_cues_->Close();
- delete chunk_writer_cues_;
- }
- if (chunk_writer_header_) {
- chunk_writer_header_->Close();
- delete chunk_writer_header_;
- }
-}
-
-void Segment::MoveCuesBeforeClustersHelper(uint64 diff, int32 index,
- uint64* cues_size) {
- CuePoint* const cue_point = cues_.GetCueByIndex(index);
- if (cue_point == NULL)
- return;
- const uint64 old_cue_point_size = cue_point->Size();
- 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 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;
- 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);
- }
- }
-}
-
-void Segment::MoveCuesBeforeClusters() {
- const uint64 current_cue_size = cues_.Size();
- 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
- // of Cluster and Cues
- int32 cluster_index = 0;
- int32 cues_index = 0;
- for (int32 i = 0; i < SeekHead::kSeekEntryCount; ++i) {
- if (seek_head_.GetId(i) == kMkvCluster)
- cluster_index = i;
- if (seek_head_.GetId(i) == kMkvCues)
- cues_index = i;
- }
- seek_head_.SetSeekEntry(cues_index, kMkvCues,
- seek_head_.GetPosition(cluster_index));
- seek_head_.SetSeekEntry(cluster_index, kMkvCluster,
- cues_.Size() + seek_head_.GetPosition(cues_index));
-}
-
-bool Segment::Init(IMkvWriter* ptr_writer) {
- if (!ptr_writer) {
- return false;
- }
- writer_cluster_ = ptr_writer;
- writer_cues_ = ptr_writer;
- writer_header_ = ptr_writer;
- return segment_info_.Init();
-}
-
-bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
- IMkvWriter* writer) {
- if (!writer->Seekable() || chunking_)
- return false;
- const int64 cluster_offset =
- cluster_list_[0]->size_position() - GetUIntSize(kMkvCluster);
-
- // Copy the headers.
- if (!ChunkedCopy(reader, writer, 0, cluster_offset))
- return false;
-
- // Recompute cue positions and seek entries.
- MoveCuesBeforeClusters();
-
- // Write cues and seek entries.
- // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
- // second time with a different writer object. But the name Finalize() doesn't
- // indicate something we want to call more than once. So consider renaming it
- // to write() or some such.
- if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
- return false;
-
- // Copy the Clusters.
- if (!ChunkedCopy(reader, writer, cluster_offset,
- cluster_end_offset_ - cluster_offset))
- return false;
-
- // Update the Segment size in case the Cues size has changed.
- const int64 pos = writer->Position();
- const int64 segment_size = writer->Position() - payload_pos_;
- if (writer->Position(size_position_) ||
- WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
- return false;
- return true;
-}
-
-bool Segment::Finalize() {
- if (WriteFramesAll() < 0)
- return false;
-
- if (mode_ == kFile) {
- if (cluster_list_size_ > 0) {
- // Update last cluster's size
- Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
-
- if (!old_cluster || !old_cluster->Finalize())
- return false;
- }
-
- if (chunking_ && chunk_writer_cluster_) {
- chunk_writer_cluster_->Close();
- chunk_count_++;
- }
-
- const double duration =
- (static_cast<double>(last_timestamp_) + last_block_duration_) /
- segment_info_.timecode_scale();
- segment_info_.set_duration(duration);
- if (!segment_info_.Finalize(writer_header_))
- return false;
-
- if (output_cues_)
- if (!seek_head_.AddSeekEntry(kMkvCues, MaxOffset()))
- return false;
-
- if (chunking_) {
- if (!chunk_writer_cues_)
- return false;
-
- char* name = NULL;
- if (!UpdateChunkName("cues", &name))
- return false;
-
- const bool cues_open = chunk_writer_cues_->Open(name);
- delete[] name;
- if (!cues_open)
- return false;
- }
-
- cluster_end_offset_ = writer_cluster_->Position();
-
- // Write the seek headers and cues
- if (output_cues_)
- if (!cues_.Write(writer_cues_))
- return false;
-
- if (!seek_head_.Finalize(writer_header_))
- return false;
-
- if (writer_header_->Seekable()) {
- if (size_position_ == -1)
- return false;
-
- 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;
-
- if (WriteUIntSize(writer_header_, segment_size, 8))
- return false;
-
- if (writer_header_->Position(pos))
- return false;
- }
-
- if (chunking_) {
- // Do not close any writers until the segment size has been written,
- // otherwise the size may be off.
- if (!chunk_writer_cues_ || !chunk_writer_header_)
- return false;
-
- chunk_writer_cues_->Close();
- chunk_writer_header_->Close();
- }
- }
-
- return true;
-}
-
-Track* Segment::AddTrack(int32 number) {
- Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
-
- if (!track)
- return NULL;
-
- if (!tracks_.AddTrack(track, number)) {
- delete track;
- return NULL;
- }
-
- return track;
-}
-
-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)
- return 0;
-
- track->set_type(Tracks::kVideo);
- track->set_codec_id(Tracks::kVp8CodecId);
- track->set_width(width);
- track->set_height(height);
-
- tracks_.AddTrack(track, number);
- has_video_ = true;
-
- return track->number();
-}
-
-bool Segment::AddCuePoint(uint64 timestamp, uint64 track) {
- if (cluster_list_size_ < 1)
- return false;
-
- const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
- if (!cluster)
- return false;
-
- CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
- if (!cue)
- return false;
-
- cue->set_time(timestamp / segment_info_.timecode_scale());
- cue->set_block_number(cluster->blocks_added());
- cue->set_cluster_pos(cluster->position_for_cues());
- cue->set_track(track);
- if (!cues_.AddCue(cue))
- return false;
-
- new_cuepoint_ = false;
- return true;
-}
-
-uint64 Segment::AddAudioTrack(int32 sample_rate, int32 channels, int32 number) {
- AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
- if (!track)
- return 0;
-
- track->set_type(Tracks::kAudio);
- track->set_codec_id(Tracks::kVorbisCodecId);
- track->set_sample_rate(sample_rate);
- track->set_channels(channels);
-
- tracks_.AddTrack(track, number);
-
- return track->number();
-}
-
-bool Segment::AddFrame(const uint8* data, uint64 length, uint64 track_number,
- uint64 timestamp, bool is_key) {
- if (!data)
- return false;
-
- Frame frame;
- if (!frame.Init(data, length))
- return false;
- frame.set_track_number(track_number);
- frame.set_timestamp(timestamp);
- frame.set_is_key(is_key);
- return AddGenericFrame(&frame);
-}
-
-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 (!data || !additional)
- 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(timestamp);
- frame.set_is_key(is_key);
- return AddGenericFrame(&frame);
-}
-
-bool Segment::AddFrameWithDiscardPadding(const uint8* data, uint64 length,
- int64 discard_padding,
- uint64 track_number, uint64 timestamp,
- bool is_key) {
- if (!data)
- return false;
-
- 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);
-}
-
-bool Segment::AddMetadata(const uint8* data, uint64 length, uint64 track_number,
- uint64 timestamp_ns, uint64 duration_ns) {
- if (!data)
- return false;
-
- Frame frame;
- if (!frame.Init(data, length))
- return false;
- 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::AddGenericFrame(const Frame* frame) {
- if (!frame)
- return false;
-
- if (!CheckHeaderInfo())
- return false;
-
- // Check for non-monotonically increasing timestamps.
- 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(frame->track_number()) &&
- !force_new_cluster_) {
- Frame* const new_frame = new (std::nothrow) Frame();
- if (!new_frame || !new_frame->CopyFrom(*frame))
- return false;
- return QueueFrame(new_frame);
- }
-
- if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
- frame->is_key())) {
- return false;
- }
-
- if (cluster_list_size_ < 1)
- return false;
-
- Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
- if (!cluster)
- return false;
-
- // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
- // if it is not set already.
- bool frame_created = false;
- if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
- !frame->reference_block_timestamp_set()) {
- Frame* const new_frame = new (std::nothrow) Frame();
- if (!new_frame->CopyFrom(*frame))
- return false;
- new_frame->set_reference_block_timestamp(
- last_track_timestamp_[frame->track_number() - 1]);
- frame = new_frame;
- frame_created = true;
- }
-
- if (!cluster->AddFrame(frame))
- return false;
-
- if (new_cuepoint_ && cues_track_ == frame->track_number()) {
- if (!AddCuePoint(frame->timestamp(), cues_track_))
- return false;
- }
-
- last_timestamp_ = frame->timestamp();
- last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
- last_block_duration_ = frame->duration();
-
- if (frame_created)
- delete frame;
-
- return true;
-}
-
-void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
-
-bool Segment::SetChunking(bool chunking, const char* filename) {
- if (chunk_count_ > 0)
- return false;
-
- if (chunking) {
- if (!filename)
- return false;
-
- // Check if we are being set to what is already set.
- if (chunking_ && !strcmp(filename, chunking_base_name_))
- return true;
-
- const size_t name_length = strlen(filename) + 1;
- char* const temp = new (std::nothrow) char[name_length]; // NOLINT
- if (!temp)
- return false;
-
-#ifdef _MSC_VER
- strcpy_s(temp, name_length, filename);
-#else
- strcpy(temp, filename);
-#endif
-
- delete[] chunking_base_name_;
- chunking_base_name_ = temp;
-
- if (!UpdateChunkName("chk", &chunk_name_))
- return false;
-
- if (!chunk_writer_cluster_) {
- chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
- if (!chunk_writer_cluster_)
- return false;
- }
-
- if (!chunk_writer_cues_) {
- chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
- if (!chunk_writer_cues_)
- return false;
- }
-
- if (!chunk_writer_header_) {
- chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
- if (!chunk_writer_header_)
- return false;
- }
-
- if (!chunk_writer_cluster_->Open(chunk_name_))
- return false;
-
- const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
- char* const header = new (std::nothrow) char[header_length]; // NOLINT
- if (!header)
- return false;
-
-#ifdef _MSC_VER
- strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
- strcat_s(header, header_length, ".hdr");
-#else
- strcpy(header, chunking_base_name_);
- strcat(header, ".hdr");
-#endif
- if (!chunk_writer_header_->Open(header)) {
- delete[] header;
- return false;
- }
-
- writer_cluster_ = chunk_writer_cluster_;
- writer_cues_ = chunk_writer_cues_;
- writer_header_ = chunk_writer_header_;
-
- delete[] header;
- }
-
- chunking_ = chunking;
-
- return true;
-}
-
-bool Segment::CuesTrack(uint64 track_number) {
- const Track* const track = GetTrackByNumber(track_number);
- if (!track)
- return false;
-
- cues_track_ = track_number;
- return true;
-}
-
-void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
-
-Track* Segment::GetTrackByNumber(uint64 track_number) const {
- return tracks_.GetTrackByNumber(track_number);
-}
-
-bool Segment::WriteSegmentHeader() {
- UpdateDocTypeVersion();
-
- // TODO(fgalligan): Support more than one segment.
- 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.
- if (WriteID(writer_header_, kMkvSegment))
- return false;
-
- // Save for later.
- size_position_ = writer_header_->Position();
-
- // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
- // bytes because if we are going to overwrite the segment size later we do
- // not know how big our segment will be.
- if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
- return false;
-
- payload_pos_ = writer_header_->Position();
-
- if (mode_ == kFile && writer_header_->Seekable()) {
- // Set the duration > 0.0 so SegmentInfo will write out the duration. When
- // the muxer is done writing we will set the correct duration and have
- // SegmentInfo upadte it.
- segment_info_.set_duration(1.0);
-
- if (!seek_head_.Write(writer_header_))
- return false;
- }
-
- if (!seek_head_.AddSeekEntry(kMkvInfo, MaxOffset()))
- return false;
- if (!segment_info_.Write(writer_header_))
- return false;
-
- if (!seek_head_.AddSeekEntry(kMkvTracks, MaxOffset()))
- return false;
- if (!tracks_.Write(writer_header_))
- return false;
-
- if (chapters_.Count() > 0) {
- if (!seek_head_.AddSeekEntry(kMkvChapters, MaxOffset()))
- return false;
- if (!chapters_.Write(writer_header_))
- 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;
-
- chunk_writer_header_->Close();
- }
-
- header_written_ = true;
-
- return true;
-}
-
-// Here we are testing whether to create a new cluster, given a frame
-// having time frame_timestamp_ns.
-//
-int Segment::TestFrame(uint64 track_number, uint64 frame_timestamp_ns,
- bool is_key) const {
- if (force_new_cluster_)
- return 1;
-
- // If no clusters have been created yet, then create a new cluster
- // and write this frame immediately, in the new cluster. This path
- // should only be followed once, the first time we attempt to write
- // a frame.
-
- if (cluster_list_size_ <= 0)
- return 1;
-
- // There exists at least one cluster. We must compare the frame to
- // the last cluster, in order to determine whether the frame is
- // written to the existing cluster, or that a new cluster should be
- // created.
-
- const uint64 timecode_scale = segment_info_.timecode_scale();
- const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
-
- const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
- const uint64 last_cluster_timecode = last_cluster->timecode();
-
- // For completeness we test for the case when the frame's timecode
- // is less than the cluster's timecode. Although in principle that
- // is allowed, this muxer doesn't actually write clusters like that,
- // so this indicates a bug somewhere in our algorithm.
-
- if (frame_timecode < last_cluster_timecode) // should never happen
- return -1;
-
- // If the frame has a timestamp significantly larger than the last
- // cluster (in Matroska, cluster-relative timestamps are serialized
- // using a 16-bit signed integer), then we cannot write this frame
- // to that cluster, and so we must create a new cluster.
-
- const int64 delta_timecode = frame_timecode - last_cluster_timecode;
-
- if (delta_timecode > kMaxBlockTimecode)
- return 2;
-
- // We decide to create a new cluster when we have a video keyframe.
- // This will flush queued (audio) frames, and write the keyframe
- // immediately, in the newly-created cluster.
-
- if (is_key && tracks_.TrackIsVideo(track_number))
- return 1;
-
- // Create a new cluster if we have accumulated too many frames
- // already, where "too many" is defined as "the total time of frames
- // in the cluster exceeds a threshold".
-
- const uint64 delta_ns = delta_timecode * timecode_scale;
-
- if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
- return 1;
-
- // This is similar to the case above, with the difference that a new
- // cluster is created when the size of the current cluster exceeds a
- // threshold.
-
- const uint64 cluster_size = last_cluster->payload_size();
-
- if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
- return 1;
-
- // There's no need to create a new cluster, so emit this frame now.
-
- return 0;
-}
-
-bool Segment::MakeNewCluster(uint64 frame_timestamp_ns) {
- const int32 new_size = cluster_list_size_ + 1;
-
- if (new_size > cluster_list_capacity_) {
- // Add more clusters.
- const int32 new_capacity =
- (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
- Cluster** const clusters =
- new (std::nothrow) Cluster*[new_capacity]; // NOLINT
- if (!clusters)
- return false;
-
- for (int32 i = 0; i < cluster_list_size_; ++i) {
- clusters[i] = cluster_list_[i];
- }
-
- delete[] cluster_list_;
-
- cluster_list_ = clusters;
- cluster_list_capacity_ = new_capacity;
- }
-
- if (!WriteFramesLessThan(frame_timestamp_ns))
- return false;
-
- if (mode_ == kFile) {
- if (cluster_list_size_ > 0) {
- // Update old cluster's size
- Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
-
- if (!old_cluster || !old_cluster->Finalize())
- return false;
- }
-
- if (output_cues_)
- new_cuepoint_ = true;
- }
-
- if (chunking_ && cluster_list_size_ > 0) {
- chunk_writer_cluster_->Close();
- chunk_count_++;
-
- if (!UpdateChunkName("chk", &chunk_name_))
- return false;
- if (!chunk_writer_cluster_->Open(chunk_name_))
- return false;
- }
-
- const uint64 timecode_scale = segment_info_.timecode_scale();
- const uint64 frame_timecode = frame_timestamp_ns / timecode_scale;
-
- uint64 cluster_timecode = frame_timecode;
-
- if (frames_size_ > 0) {
- const Frame* const f = frames_[0]; // earliest queued frame
- const uint64 ns = f->timestamp();
- const uint64 tc = ns / timecode_scale;
-
- if (tc < cluster_timecode)
- cluster_timecode = tc;
- }
-
- Cluster*& cluster = cluster_list_[cluster_list_size_];
- const int64 offset = MaxOffset();
- cluster = new (std::nothrow) Cluster(cluster_timecode, // NOLINT
- offset, segment_info_.timecode_scale());
- if (!cluster)
- return false;
-
- if (!cluster->Init(writer_cluster_))
- return false;
-
- cluster_list_size_ = new_size;
- return true;
-}
-
-bool Segment::DoNewClusterProcessing(uint64 track_number,
- uint64 frame_timestamp_ns, bool is_key) {
- for (;;) {
- // Based on the characteristics of the current frame and current
- // cluster, decide whether to create a new cluster.
- const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
- if (result < 0) // error
- return false;
-
- // Always set force_new_cluster_ to false after TestFrame.
- force_new_cluster_ = false;
-
- // A non-zero result means create a new cluster.
- if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
- return false;
-
- // Write queued (audio) frames.
- const int frame_count = WriteFramesAll();
- if (frame_count < 0) // error
- return false;
-
- // Write the current frame to the current cluster (if TestFrame
- // returns 0) or to a newly created cluster (TestFrame returns 1).
- if (result <= 1)
- return true;
-
- // TestFrame returned 2, which means there was a large time
- // difference between the cluster and the frame itself. Do the
- // test again, comparing the frame to the new cluster.
- }
-}
-
-bool Segment::CheckHeaderInfo() {
- if (!header_written_) {
- if (!WriteSegmentHeader())
- return false;
-
- if (!seek_head_.AddSeekEntry(kMkvCluster, MaxOffset()))
- return false;
-
- if (output_cues_ && cues_track_ == 0) {
- // Check for a video track
- for (uint32 i = 0; i < tracks_.track_entries_size(); ++i) {
- const Track* const track = tracks_.GetTrackByIndex(i);
- if (!track)
- return false;
-
- if (tracks_.TrackIsVideo(track->number())) {
- cues_track_ = track->number();
- break;
- }
- }
-
- // Set first track found
- if (cues_track_ == 0) {
- const Track* const track = tracks_.GetTrackByIndex(0);
- if (!track)
- return false;
-
- cues_track_ = track->number();
- }
- }
- }
- 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;
-
- char ext_chk[64];
-#ifdef _MSC_VER
- sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
-#else
- snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
-#endif
-
- const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
- char* const str = new (std::nothrow) char[length]; // NOLINT
- if (!str)
- return false;
-
-#ifdef _MSC_VER
- strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
- strcat_s(str, length, ext_chk);
-#else
- strcpy(str, chunking_base_name_);
- strcat(str, ext_chk);
-#endif
-
- delete[] * name;
- *name = str;
-
- return true;
-}
-
-int64 Segment::MaxOffset() {
- if (!writer_header_)
- return -1;
-
- int64 offset = writer_header_->Position() - payload_pos_;
-
- if (chunking_) {
- for (int32 i = 0; i < cluster_list_size_; ++i) {
- Cluster* const cluster = cluster_list_[i];
- offset += cluster->Size();
- }
-
- if (writer_cues_)
- offset += writer_cues_->Position();
- }
-
- return offset;
-}
-
-bool Segment::QueueFrame(Frame* frame) {
- const int32 new_size = frames_size_ + 1;
-
- if (new_size > frames_capacity_) {
- // Add more frames.
- const int32 new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
-
- if (new_capacity < 1)
- return false;
-
- Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
- if (!frames)
- return false;
-
- for (int32 i = 0; i < frames_size_; ++i) {
- frames[i] = frames_[i];
- }
-
- delete[] frames_;
- frames_ = frames;
- frames_capacity_ = new_capacity;
- }
-
- frames_[frames_size_++] = frame;
-
- return true;
-}
-
-int Segment::WriteFramesAll() {
- if (frames_ == NULL)
- return 0;
-
- if (cluster_list_size_ < 1)
- return -1;
-
- Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
-
- if (!cluster)
- return -1;
-
- for (int32 i = 0; i < frames_size_; ++i) {
- Frame*& frame = frames_[i];
- // 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_))
- return -1;
- }
-
- if (frame->timestamp() > last_timestamp_) {
- last_timestamp_ = frame->timestamp();
- last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
- }
-
- delete frame;
- frame = NULL;
- }
-
- const int result = frames_size_;
- frames_size_ = 0;
-
- return result;
-}
-
-bool Segment::WriteFramesLessThan(uint64 timestamp) {
- // Check |cluster_list_size_| to see if this is the first cluster. If it is
- // the first cluster the audio frames that are less than the first video
- // timesatmp will be written in a later step.
- if (frames_size_ > 0 && cluster_list_size_ > 0) {
- if (!frames_)
- return false;
-
- Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
- if (!cluster)
- return false;
-
- int32 shift_left = 0;
-
- // TODO(fgalligan): Change this to use the durations of frames instead of
- // the next frame's start time if the duration is accurate.
- for (int32 i = 1; i < frames_size_; ++i) {
- const Frame* const frame_curr = frames_[i];
-
- if (frame_curr->timestamp() > timestamp)
- break;
-
- const Frame* const frame_prev = frames_[i - 1];
- 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_prev->timestamp(), cues_track_))
- return false;
- }
-
- ++shift_left;
- 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;
- }
-
- if (shift_left > 0) {
- if (shift_left >= frames_size_)
- return false;
-
- const int32 new_frames_size = frames_size_ - shift_left;
- for (int32 i = 0; i < new_frames_size; ++i) {
- frames_[i] = frames_[i + shift_left];
- }
-
- frames_size_ = new_frames_size;
- }
- }
-
- return true;
-}
-
-} // namespace mkvmuxer
--- a/third_party/libwebm/mkvmuxer.hpp
+++ /dev/null
@@ -1,1492 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef MKVMUXER_HPP
-#define MKVMUXER_HPP
-
-#include "mkvmuxertypes.hpp"
-
-// For a description of the WebM elements see
-// http://www.webmproject.org/code/specs/container/.
-
-namespace mkvparser {
-class IMkvReader;
-} // end namespace
-
-namespace mkvmuxer {
-
-class MkvWriter;
-class Segment;
-
-const uint64 kMaxTrackNumber = 126;
-
-///////////////////////////////////////////////////////////////
-// Interface used by the mkvmuxer to write out the Mkv data.
-class IMkvWriter {
- public:
- // Writes out |len| bytes of |buf|. Returns 0 on success.
- virtual int32 Write(const void* buf, uint32 len) = 0;
-
- // Returns the offset of the output position from the beginning of the
- // output.
- virtual int64 Position() const = 0;
-
- // Set the current File position. Returns 0 on success.
- virtual int32 Position(int64 position) = 0;
-
- // Returns true if the writer is seekable.
- virtual bool Seekable() const = 0;
-
- // Element start notification. Called whenever an element identifier is about
- // to be written to the stream. |element_id| is the element identifier, and
- // |position| is the location in the WebM stream where the first octet of the
- // element identifier will be written.
- // Note: the |MkvId| enumeration in webmids.hpp defines element values.
- virtual void ElementStartNotify(uint64 element_id, int64 position) = 0;
-
- protected:
- IMkvWriter();
- virtual ~IMkvWriter();
-
- private:
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(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
-bool ChunkedCopy(mkvparser::IMkvReader* source, IMkvWriter* dst, int64 start,
- int64 size);
-
-///////////////////////////////////////////////////////////////
-// Class to hold data the will be written to a block.
-class Frame {
- public:
- 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_; }
- void set_duration(uint64 duration) { duration_ = duration; }
- uint64 duration() const { return duration_; }
- const uint8* frame() const { return frame_; }
- void set_is_key(bool key) { is_key_ = key; }
- bool is_key() const { return is_key_; }
- uint64 length() const { return length_; }
- void set_track_number(uint64 track_number) { track_number_ = track_number; }
- uint64 track_number() const { return track_number_; }
- void set_timestamp(uint64 timestamp) { timestamp_ = timestamp; }
- uint64 timestamp() const { return timestamp_; }
- void set_discard_padding(int64 discard_padding) {
- discard_padding_ = discard_padding;
- }
- int64 discard_padding() const { return discard_padding_; }
- void set_reference_block_timestamp(int64 reference_block_timestamp);
- int64 reference_block_timestamp() const { return reference_block_timestamp_; }
- bool reference_block_timestamp_set() const {
- return reference_block_timestamp_set_;
- }
-
- private:
- // Id of the Additional data.
- uint64 add_id_;
-
- // Pointer to additional data. Owned by this class.
- uint8* additional_;
-
- // Length of the additional data.
- uint64 additional_length_;
-
- // Duration of the frame in nanoseconds.
- uint64 duration_;
-
- // Pointer to the data. Owned by this class.
- uint8* frame_;
-
- // Flag telling if the data should set the key flag of a block.
- bool is_key_;
-
- // Length of the data.
- uint64 length_;
-
- // Mkv track number the data is associated with.
- uint64 track_number_;
-
- // Timestamp of the data in nanoseconds.
- uint64 timestamp_;
-
- // 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);
-};
-
-///////////////////////////////////////////////////////////////
-// Class to hold one cue point in a Cues element.
-class CuePoint {
- public:
- CuePoint();
- ~CuePoint();
-
- // Returns the size in bytes for the entire CuePoint element.
- uint64 Size() const;
-
- // Output the CuePoint element to the writer. Returns true on success.
- bool Write(IMkvWriter* writer) const;
-
- void set_time(uint64 time) { time_ = time; }
- uint64 time() const { return time_; }
- void set_track(uint64 track) { track_ = track; }
- uint64 track() const { return track_; }
- void set_cluster_pos(uint64 cluster_pos) { cluster_pos_ = cluster_pos; }
- uint64 cluster_pos() const { return cluster_pos_; }
- void set_block_number(uint64 block_number) { block_number_ = block_number; }
- uint64 block_number() const { return block_number_; }
- void set_output_block_number(bool output_block_number) {
- output_block_number_ = output_block_number;
- }
- bool output_block_number() const { return output_block_number_; }
-
- private:
- // Returns the size in bytes for the payload of the CuePoint element.
- uint64 PayloadSize() const;
-
- // Absolute timecode according to the segment time base.
- uint64 time_;
-
- // The Track element associated with the CuePoint.
- uint64 track_;
-
- // The position of the Cluster containing the Block.
- uint64 cluster_pos_;
-
- // Number of the Block within the Cluster, starting from 1.
- uint64 block_number_;
-
- // If true the muxer will write out the block number for the cue if the
- // block number is different than the default of 1. Default is set to true.
- bool output_block_number_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(CuePoint);
-};
-
-///////////////////////////////////////////////////////////////
-// Cues element.
-class Cues {
- public:
- Cues();
- ~Cues();
-
- // Adds a cue point to the Cues element. Returns true on success.
- bool AddCue(CuePoint* cue);
-
- // Returns the cue point by index. Returns NULL if there is no cue point
- // match.
- CuePoint* GetCueByIndex(int32 index) const;
-
- // Returns the total size of the Cues element
- uint64 Size();
-
- // Output the Cues element to the writer. Returns true on success.
- bool Write(IMkvWriter* writer) const;
-
- int32 cue_entries_size() const { return cue_entries_size_; }
- void set_output_block_number(bool output_block_number) {
- output_block_number_ = output_block_number;
- }
- bool output_block_number() const { return output_block_number_; }
-
- private:
- // Number of allocated elements in |cue_entries_|.
- int32 cue_entries_capacity_;
-
- // Number of CuePoints in |cue_entries_|.
- int32 cue_entries_size_;
-
- // CuePoint list.
- CuePoint** cue_entries_;
-
- // If true the muxer will write out the block number for the cue if the
- // block number is different than the default of 1. Default is set to true.
- bool output_block_number_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cues);
-};
-
-///////////////////////////////////////////////////////////////
-// ContentEncAESSettings element
-class ContentEncAESSettings {
- public:
- enum { kCTR = 1 };
-
- ContentEncAESSettings();
- ~ContentEncAESSettings() {}
-
- // Returns the size in bytes for the ContentEncAESSettings element.
- uint64 Size() const;
-
- // Writes out the ContentEncAESSettings element to |writer|. Returns true on
- // success.
- bool Write(IMkvWriter* writer) const;
-
- uint64 cipher_mode() const { return cipher_mode_; }
-
- private:
- // Returns the size in bytes for the payload of the ContentEncAESSettings
- // element.
- uint64 PayloadSize() const;
-
- // Sub elements
- uint64 cipher_mode_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncAESSettings);
-};
-
-///////////////////////////////////////////////////////////////
-// ContentEncoding element
-// Elements used to describe if the track data has been encrypted or
-// compressed with zlib or header stripping.
-// Currently only whole frames can be encrypted with AES. This dictates that
-// ContentEncodingOrder will be 0, ContentEncodingScope will be 1,
-// ContentEncodingType will be 1, and ContentEncAlgo will be 5.
-class ContentEncoding {
- public:
- ContentEncoding();
- ~ContentEncoding();
-
- // Sets the content encryption id. Copies |length| bytes from |id| to
- // |enc_key_id_|. Returns true on success.
- bool SetEncryptionID(const uint8* id, uint64 length);
-
- // Returns the size in bytes for the ContentEncoding element.
- uint64 Size() const;
-
- // Writes out the ContentEncoding element to |writer|. Returns true on
- // success.
- bool Write(IMkvWriter* writer) const;
-
- uint64 enc_algo() const { return enc_algo_; }
- uint64 encoding_order() const { return encoding_order_; }
- uint64 encoding_scope() const { return encoding_scope_; }
- uint64 encoding_type() const { return encoding_type_; }
- ContentEncAESSettings* enc_aes_settings() { return &enc_aes_settings_; }
-
- private:
- // Returns the size in bytes for the encoding elements.
- uint64 EncodingSize(uint64 compresion_size, uint64 encryption_size) const;
-
- // Returns the size in bytes for the encryption elements.
- uint64 EncryptionSize() const;
-
- // Track element names
- uint64 enc_algo_;
- uint8* enc_key_id_;
- uint64 encoding_order_;
- uint64 encoding_scope_;
- uint64 encoding_type_;
-
- // ContentEncAESSettings element.
- ContentEncAESSettings enc_aes_settings_;
-
- // Size of the ContentEncKeyID data in bytes.
- uint64 enc_key_id_length_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
-};
-
-///////////////////////////////////////////////////////////////
-// Track element.
-class Track {
- public:
- // The |seed| parameter is used to synthesize a UID for the track.
- explicit Track(unsigned int* seed);
- virtual ~Track();
-
- // Adds a ContentEncoding element to the Track. Returns true on success.
- virtual bool AddContentEncoding();
-
- // Returns the ContentEncoding by index. Returns NULL if there is no
- // ContentEncoding match.
- ContentEncoding* GetContentEncodingByIndex(uint32 index) const;
-
- // Returns the size in bytes for the payload of the Track element.
- virtual uint64 PayloadSize() const;
-
- // Returns the size in bytes of the Track element.
- virtual uint64 Size() const;
-
- // Output the Track element to the writer. Returns true on success.
- virtual bool Write(IMkvWriter* writer) const;
-
- // Sets the CodecPrivate element of the Track element. Copies |length|
- // bytes from |codec_private| to |codec_private_|. Returns true on success.
- bool SetCodecPrivate(const uint8* codec_private, uint64 length);
-
- void set_codec_id(const char* codec_id);
- const char* codec_id() const { return codec_id_; }
- const uint8* codec_private() const { return codec_private_; }
- void set_language(const char* language);
- const char* language() const { return language_; }
- void set_max_block_additional_id(uint64 max_block_additional_id) {
- max_block_additional_id_ = max_block_additional_id;
- }
- uint64 max_block_additional_id() const { return max_block_additional_id_; }
- void set_name(const char* name);
- const char* name() const { return name_; }
- void set_number(uint64 number) { number_ = number; }
- uint64 number() const { return number_; }
- void set_type(uint64 type) { type_ = type; }
- uint64 type() const { return type_; }
- void set_uid(uint64 uid) { uid_ = uid; }
- uint64 uid() const { return uid_; }
- void set_codec_delay(uint64 codec_delay) { codec_delay_ = codec_delay; }
- uint64 codec_delay() const { return codec_delay_; }
- void set_seek_pre_roll(uint64 seek_pre_roll) {
- seek_pre_roll_ = seek_pre_roll;
- }
- uint64 seek_pre_roll() const { return seek_pre_roll_; }
- void set_default_duration(uint64 default_duration) {
- default_duration_ = default_duration;
- }
- uint64 default_duration() const { return default_duration_; }
-
- uint64 codec_private_length() const { return codec_private_length_; }
- uint32 content_encoding_entries_size() const {
- return content_encoding_entries_size_;
- }
-
- private:
- // Track element names.
- char* codec_id_;
- uint8* codec_private_;
- char* language_;
- uint64 max_block_additional_id_;
- char* name_;
- uint64 number_;
- uint64 type_;
- uint64 uid_;
- uint64 codec_delay_;
- uint64 seek_pre_roll_;
- uint64 default_duration_;
-
- // Size of the CodecPrivate data in bytes.
- uint64 codec_private_length_;
-
- // ContentEncoding element list.
- ContentEncoding** content_encoding_entries_;
-
- // Number of ContentEncoding elements added.
- uint32 content_encoding_entries_size_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Track);
-};
-
-///////////////////////////////////////////////////////////////
-// Track that has video specific elements.
-class VideoTrack : public Track {
- public:
- // Supported modes for stereo 3D.
- enum StereoMode {
- kMono = 0,
- kSideBySideLeftIsFirst = 1,
- kTopBottomRightIsFirst = 2,
- kTopBottomLeftIsFirst = 3,
- kSideBySideRightIsFirst = 11
- };
-
- enum AlphaMode { kNoAlpha = 0, kAlpha = 1 };
-
- // The |seed| parameter is used to synthesize a UID for the track.
- explicit VideoTrack(unsigned int* seed);
- virtual ~VideoTrack();
-
- // Returns the size in bytes for the payload of the Track element plus the
- // video specific elements.
- virtual uint64 PayloadSize() const;
-
- // Output the VideoTrack element to the writer. Returns true on success.
- virtual bool Write(IMkvWriter* writer) const;
-
- // Sets the video's stereo mode. Returns true on success.
- bool SetStereoMode(uint64 stereo_mode);
-
- // Sets the video's alpha mode. Returns true on success.
- bool SetAlphaMode(uint64 alpha_mode);
-
- void set_display_height(uint64 height) { display_height_ = height; }
- uint64 display_height() const { return display_height_; }
- void set_display_width(uint64 width) { display_width_ = width; }
- 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; }
- uint64 height() const { return height_; }
- uint64 stereo_mode() { return stereo_mode_; }
- uint64 alpha_mode() { return alpha_mode_; }
- void set_width(uint64 width) { width_ = width; }
- uint64 width() const { return width_; }
-
- private:
- // Returns the size in bytes of the Video element.
- uint64 VideoPayloadSize() const;
-
- // 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_;
- uint64 alpha_mode_;
- uint64 width_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack);
-};
-
-///////////////////////////////////////////////////////////////
-// Track that has audio specific elements.
-class AudioTrack : public Track {
- public:
- // The |seed| parameter is used to synthesize a UID for the track.
- explicit AudioTrack(unsigned int* seed);
- virtual ~AudioTrack();
-
- // Returns the size in bytes for the payload of the Track element plus the
- // audio specific elements.
- virtual uint64 PayloadSize() const;
-
- // Output the AudioTrack element to the writer. Returns true on success.
- virtual bool Write(IMkvWriter* writer) const;
-
- void set_bit_depth(uint64 bit_depth) { bit_depth_ = bit_depth; }
- uint64 bit_depth() const { return bit_depth_; }
- void set_channels(uint64 channels) { channels_ = channels; }
- uint64 channels() const { return channels_; }
- void set_sample_rate(double sample_rate) { sample_rate_ = sample_rate; }
- double sample_rate() const { return sample_rate_; }
-
- private:
- // Audio track element names.
- uint64 bit_depth_;
- uint64 channels_;
- double sample_rate_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(AudioTrack);
-};
-
-///////////////////////////////////////////////////////////////
-// Tracks element
-class Tracks {
- public:
- // Audio and video type defined by the Matroska specs.
- enum { kVideo = 0x1, kAudio = 0x2 };
-
- static const char kOpusCodecId[];
- static const char kVorbisCodecId[];
- static const char kVp8CodecId[];
- static const char kVp9CodecId[];
- static const char kVp10CodecId[];
-
- Tracks();
- ~Tracks();
-
- // Adds a Track element to the Tracks object. |track| will be owned and
- // deleted by the Tracks object. Returns true on success. |number| is the
- // number to use for the track. |number| must be >= 0. If |number| == 0
- // then the muxer will decide on the track number.
- bool AddTrack(Track* track, int32 number);
-
- // Returns the track by index. Returns NULL if there is no track match.
- const Track* GetTrackByIndex(uint32 idx) const;
-
- // Search the Tracks and return the track that matches |tn|. Returns NULL
- // if there is no track match.
- Track* GetTrackByNumber(uint64 track_number) const;
-
- // Returns true if the track number is an audio track.
- bool TrackIsAudio(uint64 track_number) const;
-
- // Returns true if the track number is a video track.
- bool TrackIsVideo(uint64 track_number) const;
-
- // Output the Tracks element to the writer. Returns true on success.
- bool Write(IMkvWriter* writer) const;
-
- uint32 track_entries_size() const { return track_entries_size_; }
-
- private:
- // Track element list.
- Track** track_entries_;
-
- // Number of Track elements added.
- uint32 track_entries_size_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tracks);
-};
-
-///////////////////////////////////////////////////////////////
-// Chapter element
-//
-class Chapter {
- public:
- // Set the identifier for this chapter. (This corresponds to the
- // Cue Identifier line in WebVTT.)
- // TODO(matthewjheaney): the actual serialization of this item in
- // MKV is pending.
- bool set_id(const char* id);
-
- // Converts the nanosecond start and stop times of this chapter to
- // their corresponding timecode values, and stores them that way.
- void set_time(const Segment& segment, uint64 start_time_ns,
- uint64 end_time_ns);
-
- // Sets the uid for this chapter. Primarily used to enable
- // deterministic output from the muxer.
- void set_uid(const uint64 uid) { uid_ = uid; }
-
- // Add a title string to this chapter, per the semantics described
- // here:
- // http://www.matroska.org/technical/specs/index.html
- //
- // The title ("chapter string") is a UTF-8 string.
- //
- // The language has ISO 639-2 representation, described here:
- // http://www.loc.gov/standards/iso639-2/englangn.html
- // http://www.loc.gov/standards/iso639-2/php/English_list.php
- // If you specify NULL as the language value, this implies
- // English ("eng").
- //
- // The country value corresponds to the codes listed here:
- // http://www.iana.org/domains/root/db/
- //
- // The function returns false if the string could not be allocated.
- bool add_string(const char* title, const char* language, const char* country);
-
- private:
- friend class Chapters;
-
- // For storage of chapter titles that differ by language.
- class Display {
- public:
- // Establish representation invariant for new Display object.
- void Init();
-
- // Reclaim resources, in anticipation of destruction.
- void Clear();
-
- // Copies the title to the |title_| member. Returns false on
- // error.
- bool set_title(const char* title);
-
- // Copies the language to the |language_| member. Returns false
- // on error.
- bool set_language(const char* language);
-
- // Copies the country to the |country_| member. Returns false on
- // error.
- bool set_country(const char* country);
-
- // If |writer| is non-NULL, serialize the Display sub-element of
- // the Atom into the stream. Returns the Display element size on
- // success, 0 if error.
- uint64 WriteDisplay(IMkvWriter* writer) const;
-
- private:
- char* title_;
- char* language_;
- char* country_;
- };
-
- Chapter();
- ~Chapter();
-
- // Establish the representation invariant for a newly-created
- // Chapter object. The |seed| parameter is used to create the UID
- // for this chapter atom.
- void Init(unsigned int* seed);
-
- // Copies this Chapter object to a different one. This is used when
- // expanding a plain array of Chapter objects (see Chapters).
- void ShallowCopy(Chapter* dst) const;
-
- // Reclaim resources used by this Chapter object, pending its
- // destruction.
- void Clear();
-
- // If there is no storage remaining on the |displays_| array for a
- // new display object, creates a new, longer array and copies the
- // existing Display objects to the new array. Returns false if the
- // array cannot be expanded.
- bool ExpandDisplaysArray();
-
- // If |writer| is non-NULL, serialize the Atom sub-element into the
- // stream. Returns the total size of the element on success, 0 if
- // error.
- uint64 WriteAtom(IMkvWriter* writer) const;
-
- // The string identifier for this chapter (corresponds to WebVTT cue
- // identifier).
- char* id_;
-
- // Start timecode of the chapter.
- uint64 start_timecode_;
-
- // Stop timecode of the chapter.
- uint64 end_timecode_;
-
- // The binary identifier for this chapter.
- uint64 uid_;
-
- // The Atom element can contain multiple Display sub-elements, as
- // the same logical title can be rendered in different languages.
- Display* displays_;
-
- // The physical length (total size) of the |displays_| array.
- int displays_size_;
-
- // The logical length (number of active elements) on the |displays_|
- // array.
- int displays_count_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapter);
-};
-
-///////////////////////////////////////////////////////////////
-// Chapters element
-//
-class Chapters {
- public:
- Chapters();
- ~Chapters();
-
- Chapter* AddChapter(unsigned int* seed);
-
- // Returns the number of chapters that have been added.
- int Count() const;
-
- // Output the Chapters element to the writer. Returns true on success.
- bool Write(IMkvWriter* writer) const;
-
- private:
- // Expands the chapters_ array if there is not enough space to contain
- // another chapter object. Returns true on success.
- bool ExpandChaptersArray();
-
- // If |writer| is non-NULL, serialize the Edition sub-element of the
- // Chapters element into the stream. Returns the Edition element
- // size on success, 0 if error.
- uint64 WriteEdition(IMkvWriter* writer) const;
-
- // Total length of the chapters_ array.
- int chapters_size_;
-
- // Number of active chapters on the chapters_ array.
- int chapters_count_;
-
- // Array for storage of chapter objects.
- Chapter* chapters_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(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:
- // |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. |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:
- // 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* 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:
- // 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. The range of allowed values is [1, 126].
- // abs_timecode: Absolute (not relative to cluster) timestamp of the
- // frame, expressed in timecode units.
- // is_key: Flag telling whether or not this frame is a key frame.
- bool AddFrameWithAdditional(const uint8* data, uint64 length,
- const uint8* additional, uint64 additional_length,
- uint64 add_id, uint64 track_number,
- uint64 abs_timecode, bool is_key);
-
- // Adds a frame to be output in the file. The frame is written out through
- // |writer_| if successful. Returns true on success.
- // Inputs:
- // 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
- // functions. The range of allowed values is [1, 126].
- // abs_timecode: Absolute (not relative to cluster) timestamp of the
- // frame, expressed in timecode units.
- // is_key: Flag telling whether or not this frame is a key frame.
- bool AddFrameWithDiscardPadding(const uint8* 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:
- // 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
- // metadata frame, expressed in timecode units.
- // duration: Duration of metadata frame, in timecode units.
- //
- // 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* data, uint64 length, uint64 track_number,
- uint64 timecode, uint64 duration);
-
- // Increments the size of the cluster's data in bytes.
- void AddPayloadSize(uint64 size);
-
- // Closes the cluster so no more data can be written to it. Will update the
- // cluster's size if |writer_| is seekable. Returns true on success.
- bool Finalize();
-
- // 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:
- // Utility method that confirms that blocks can still be added, and that the
- // cluster header has been written. Used by |DoWriteFrame*|. Returns true
- // when successful.
- bool PreWriteBlock();
-
- // Utility method used by the |DoWriteFrame*| methods that handles the book
- // keeping required after each block is written.
- void PostWriteBlock(uint64 element_size);
-
- // Does some verification and calls WriteFrame.
- bool DoWriteFrame(const Frame* const frame);
-
- // Outputs the Cluster header to |writer_|. Returns true on success.
- bool WriteClusterHeader();
-
- // Number of blocks added to the cluster.
- int32 blocks_added_;
-
- // Flag telling if the cluster has been closed.
- bool finalized_;
-
- // Flag telling if the cluster's header has been written.
- bool header_written_;
-
- // The size of the cluster elements in bytes.
- uint64 payload_size_;
-
- // The file position used for cue points.
- const int64 position_for_cues_;
-
- // The file position of the cluster's size element.
- int64 size_position_;
-
- // 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_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cluster);
-};
-
-///////////////////////////////////////////////////////////////
-// SeekHead element
-class SeekHead {
- public:
- SeekHead();
- ~SeekHead();
-
- // TODO(fgalligan): Change this to reserve a certain size. Then check how
- // big the seek entry to be added is as not every seek entry will be the
- // maximum size it could be.
- // Adds a seek entry to be written out when the element is finalized. |id|
- // must be the coded mkv element id. |pos| is the file position of the
- // element. Returns true on success.
- bool AddSeekEntry(uint32 id, uint64 pos);
-
- // Writes out SeekHead and SeekEntry elements. Returns true on success.
- bool Finalize(IMkvWriter* writer) const;
-
- // Returns the id of the Seek Entry at the given index. Returns -1 if index is
- // out of range.
- uint32 GetId(int index) const;
-
- // Returns the position of the Seek Entry at the given index. Returns -1 if
- // index is out of range.
- uint64 GetPosition(int index) const;
-
- // Sets the Seek Entry id and position at given index.
- // Returns true on success.
- bool SetSeekEntry(int index, uint32 id, uint64 position);
-
- // Reserves space by writing out a Void element which will be updated with
- // a SeekHead element later. Returns true on success.
- bool Write(IMkvWriter* writer);
-
- // We are going to put a cap on the number of Seek Entries.
- const static int32 kSeekEntryCount = 5;
-
- private:
- // Returns the maximum size in bytes of one seek entry.
- uint64 MaxEntrySize() const;
-
- // Seek entry id element list.
- uint32 seek_entry_id_[kSeekEntryCount];
-
- // Seek entry pos element list.
- uint64 seek_entry_pos_[kSeekEntryCount];
-
- // The file position of SeekHead element.
- int64 start_pos_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SeekHead);
-};
-
-///////////////////////////////////////////////////////////////
-// Segment Information element
-class SegmentInfo {
- public:
- SegmentInfo();
- ~SegmentInfo();
-
- // Will update the duration if |duration_| is > 0.0. Returns true on success.
- bool Finalize(IMkvWriter* writer) const;
-
- // Sets |muxing_app_| and |writing_app_|.
- bool Init();
-
- // Output the Segment Information element to the writer. Returns true on
- // success.
- bool Write(IMkvWriter* writer);
-
- void set_duration(double duration) { duration_ = duration; }
- double duration() const { return duration_; }
- void set_muxing_app(const char* app);
- const char* muxing_app() const { return muxing_app_; }
- void set_timecode_scale(uint64 scale) { timecode_scale_ = scale; }
- uint64 timecode_scale() const { return timecode_scale_; }
- void set_writing_app(const char* app);
- const char* writing_app() const { return writing_app_; }
- void set_date_utc(int64 date_utc) { date_utc_ = date_utc; }
- int64 date_utc() const { return date_utc_; }
-
- private:
- // Segment Information element names.
- // Initially set to -1 to signify that a duration has not been set and should
- // not be written out.
- double duration_;
- // Set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
- char* muxing_app_;
- uint64 timecode_scale_;
- // Initially set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
- char* writing_app_;
- // LLONG_MIN when DateUTC is not set.
- int64 date_utc_;
-
- // The file position of the duration element.
- int64 duration_pos_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SegmentInfo);
-};
-
-///////////////////////////////////////////////////////////////
-// This class represents the main segment in a WebM file. Currently only
-// supports one Segment element.
-//
-// Notes:
-// |Init| must be called before any other method in this class.
-class Segment {
- public:
- enum Mode { kLive = 0x1, kFile = 0x2 };
-
- enum CuesPosition {
- kAfterClusters = 0x0, // Position Cues after Clusters - Default
- kBeforeClusters = 0x1 // Position Cues before Clusters
- };
-
- const static uint32 kDefaultDocTypeVersion = 2;
- const static uint64 kDefaultMaxClusterDuration = 30000000000ULL;
-
- Segment();
- ~Segment();
-
- // Initializes |SegmentInfo| and returns result. Always returns false when
- // |ptr_writer| is NULL.
- bool Init(IMkvWriter* ptr_writer);
-
- // Adds a generic track to the segment. Returns the newly-allocated
- // track object (which is owned by the segment) on success, NULL on
- // error. |number| is the number to use for the track. |number|
- // must be >= 0. If |number| == 0 then the muxer will decide on the
- // track number.
- Track* AddTrack(int32 number);
-
- // Adds a Vorbis audio track to the segment. Returns the number of the track
- // on success, 0 on error. |number| is the number to use for the audio track.
- // |number| must be >= 0. If |number| == 0 then the muxer will decide on
- // the track number.
- uint64 AddAudioTrack(int32 sample_rate, int32 channels, int32 number);
-
- // Adds an empty chapter to the chapters of this segment. Returns
- // non-NULL on success. After adding the chapter, the caller should
- // 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
- // BlockNumber for the CuePoint. Returns true on success.
- bool AddCuePoint(uint64 timestamp, uint64 track);
-
- // Adds a frame to be output in the file. Returns true on success.
- // 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.
- // 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* 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:
- // data: Pointer to the data
- // length: Length of the data
- // track_number: Track to add the data to. Value returned by Add track
- // functions.
- // timecode: Absolute timestamp of the metadata frame, expressed
- // in nanosecond units.
- // duration: Duration of metadata frame, in nanosecond units.
- //
- // 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* 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:
- // data: Pointer to the data.
- // length: Length of the data.
- // additional: Pointer to additional data.
- // additional_length: Length of additional data.
- // add_id: Additional ID which identifies the type of additional data.
- // track_number: Track to add the data to. Value returned by Add track
- // functions.
- // timestamp: Absolute timestamp of the frame, expressed in nanosecond
- // units.
- // is_key: Flag telling whether or not this frame is a key frame.
- bool AddFrameWithAdditional(const uint8* data, uint64 length,
- const uint8* additional, uint64 additional_length,
- uint64 add_id, uint64 track_number,
- uint64 timestamp, bool is_key);
-
- // Writes a frame with DiscardPadding to the output medium; returns true on
- // success.
- // Inputs:
- // 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
- // functions.
- // timestamp: Absolute timestamp of the frame, expressed in nanosecond
- // units.
- // is_key: Flag telling whether or not this frame is a key frame.
- bool AddFrameWithDiscardPadding(const uint8* data, uint64 length,
- int64 discard_padding, uint64 track_number,
- uint64 timestamp, bool is_key);
-
- // Writes a Frame to the output medium. Chooses the correct way of writing
- // the frame (Block vs SimpleBlock) based on the parameters passed.
- // Inputs:
- // frame: frame object
- bool AddGenericFrame(const Frame* frame);
-
- // Adds a VP8 video track to the segment. Returns the number of the track on
- // success, 0 on error. |number| is the number to use for the video track.
- // |number| must be >= 0. If |number| == 0 then the muxer will decide on
- // the track number.
- uint64 AddVideoTrack(int32 width, int32 height, int32 number);
-
- // This function must be called after Finalize() if you need a copy of the
- // output with Cues written before the Clusters. It will return false if the
- // writer is not seekable of if chunking is set to true.
- // Input parameters:
- // reader - an IMkvReader object created with the same underlying file of the
- // current writer object. Make sure to close the existing writer
- // object before creating this so that all the data is properly
- // flushed and available for reading.
- // writer - an IMkvWriter object pointing to a *different* file than the one
- // pointed by the current writer object. This file will contain the
- // Cues element before the Clusters.
- bool CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
- IMkvWriter* writer);
-
- // Sets which track to use for the Cues element. Must have added the track
- // before calling this function. Returns true on success. |track_number| is
- // returned by the Add track functions.
- bool CuesTrack(uint64 track_number);
-
- // This will force the muxer to create a new Cluster when the next frame is
- // added.
- void ForceNewClusterOnNextFrame();
-
- // Writes out any frames that have not been written out. Finalizes the last
- // cluster. May update the size and duration of the segment. May output the
- // Cues element. May finalize the SeekHead element. Returns true on success.
- bool Finalize();
-
- // Returns the Cues object.
- Cues* GetCues() { return &cues_; }
-
- // Returns the Segment Information object.
- const SegmentInfo* GetSegmentInfo() const { return &segment_info_; }
- SegmentInfo* GetSegmentInfo() { return &segment_info_; }
-
- // Search the Tracks and return the track that matches |track_number|.
- // Returns NULL if there is no track match.
- Track* GetTrackByNumber(uint64 track_number) const;
-
- // Toggles whether to output a cues element.
- void OutputCues(bool output_cues);
-
- // Sets if the muxer will output files in chunks or not. |chunking| is a
- // flag telling whether or not to turn on chunking. |filename| is the base
- // filename for the chunk files. The header chunk file will be named
- // |filename|.hdr and the data chunks will be named
- // |filename|_XXXXXX.chk. Chunking implies that the muxer will be writing
- // to files so the muxer will use the default MkvWriter class to control
- // what data is written to what files. Returns true on success.
- // TODO: Should we change the IMkvWriter Interface to add Open and Close?
- // That will force the interface to be dependent on files.
- bool SetChunking(bool chunking, const char* filename);
-
- bool chunking() const { return chunking_; }
- uint64 cues_track() const { return cues_track_; }
- void set_max_cluster_duration(uint64 max_cluster_duration) {
- max_cluster_duration_ = max_cluster_duration;
- }
- uint64 max_cluster_duration() const { return max_cluster_duration_; }
- void set_max_cluster_size(uint64 max_cluster_size) {
- max_cluster_size_ = max_cluster_size;
- }
- uint64 max_cluster_size() const { return max_cluster_size_; }
- void set_mode(Mode mode) { mode_ = mode; }
- Mode mode() const { return mode_; }
- CuesPosition cues_position() const { return cues_position_; }
- bool output_cues() const { return output_cues_; }
- const SegmentInfo* segment_info() const { return &segment_info_; }
-
- private:
- // Checks if header information has been output and initialized. If not it
- // will output the Segment element and initialize the SeekHead elment and
- // 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.
- bool UpdateChunkName(const char* ext, char** name) const;
-
- // Returns the maximum offset within the segment's payload. When chunking
- // this function is needed to determine offsets of elements within the
- // chunked files. Returns -1 on error.
- int64 MaxOffset();
-
- // Adds the frame to our frame array.
- bool QueueFrame(Frame* frame);
-
- // Output all frames that are queued. Returns -1 on error, otherwise
- // it returns the number of frames written.
- int WriteFramesAll();
-
- // Output all frames that are queued that have an end time that is less
- // then |timestamp|. Returns true on success and if there are no frames
- // queued.
- bool WriteFramesLessThan(uint64 timestamp);
-
- // Outputs the segment header, Segment Information element, SeekHead element,
- // and Tracks element to |writer_|.
- bool WriteSegmentHeader();
-
- // Given a frame with the specified timestamp (nanosecond units) and
- // keyframe status, determine whether a new cluster should be
- // created, before writing enqueued frames and the frame itself. The
- // function returns one of the following values:
- // -1 = error: an out-of-order frame was detected
- // 0 = do not create a new cluster, and write frame to the existing cluster
- // 1 = create a new cluster, and write frame to that new cluster
- // 2 = create a new cluster, and re-run test
- int TestFrame(uint64 track_num, uint64 timestamp_ns, bool key) const;
-
- // Create a new cluster, using the earlier of the first enqueued
- // frame, or the indicated time. Returns true on success.
- bool MakeNewCluster(uint64 timestamp_ns);
-
- // Checks whether a new cluster needs to be created, and if so
- // creates a new cluster. Returns false if creation of a new cluster
- // was necessary but creation was not successful.
- bool DoNewClusterProcessing(uint64 track_num, uint64 timestamp_ns, bool key);
-
- // Adjusts Cue Point values (to place Cues before Clusters) so that they
- // reflect the correct offsets.
- void MoveCuesBeforeClusters();
-
- // This function recursively computes the correct cluster offsets (this is
- // done to move the Cues before Clusters). It recursively updates the change
- // in size (which indicates a change in cluster offset) until no sizes change.
- // Parameters:
- // 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 - 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.
- unsigned int seed_;
-
- // WebM elements
- Cues cues_;
- SeekHead seek_head_;
- SegmentInfo segment_info_;
- Tracks tracks_;
- Chapters chapters_;
- Tags tags_;
-
- // Number of chunks written.
- int chunk_count_;
-
- // Current chunk filename.
- char* chunk_name_;
-
- // Default MkvWriter object created by this class used for writing clusters
- // out in separate files.
- MkvWriter* chunk_writer_cluster_;
-
- // Default MkvWriter object created by this class used for writing Cues
- // element out to a file.
- MkvWriter* chunk_writer_cues_;
-
- // Default MkvWriter object created by this class used for writing the
- // Matroska header out to a file.
- MkvWriter* chunk_writer_header_;
-
- // Flag telling whether or not the muxer is chunking output to multiple
- // files.
- bool chunking_;
-
- // Base filename for the chunked files.
- char* chunking_base_name_;
-
- // File position offset where the Clusters end.
- int64 cluster_end_offset_;
-
- // List of clusters.
- Cluster** cluster_list_;
-
- // Number of cluster pointers allocated in the cluster list.
- int32 cluster_list_capacity_;
-
- // Number of clusters in the cluster list.
- int32 cluster_list_size_;
-
- // Indicates whether Cues should be written before or after Clusters
- CuesPosition cues_position_;
-
- // Track number that is associated with the cues element for this segment.
- uint64 cues_track_;
-
- // Tells the muxer to force a new cluster on the next Block.
- bool force_new_cluster_;
-
- // List of stored audio frames. These variables are used to store frames so
- // the muxer can follow the guideline "Audio blocks that contain the video
- // key frame's timecode should be in the same cluster as the video key frame
- // block."
- Frame** frames_;
-
- // Number of frame pointers allocated in the frame list.
- int32 frames_capacity_;
-
- // Number of frames in the frame list.
- int32 frames_size_;
-
- // Flag telling if a video track has been added to the segment.
- bool has_video_;
-
- // Flag telling if the segment's header has been written.
- bool header_written_;
-
- // Duration of the last block in nanoseconds.
- uint64 last_block_duration_;
-
- // 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.
- uint64 max_cluster_duration_;
-
- // Maximum size in bytes for a cluster. This variable is a guideline and
- // some clusters may have a larger size. Default is 0 which signifies that
- // the muxer will decide the size.
- uint64 max_cluster_size_;
-
- // The mode that segment is in. If set to |kLive| the writer must not
- // seek backwards.
- Mode mode_;
-
- // Flag telling the muxer that a new cue point should be added.
- bool new_cuepoint_;
-
- // TODO(fgalligan): Should we add support for more than one Cues element?
- // 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_;
- IMkvWriter* writer_header_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Segment);
-};
-
-} // end namespace mkvmuxer
-
-#endif // MKVMUXER_HPP
--- /dev/null
+++ b/third_party/libwebm/mkvmuxer/mkvmuxer.cc
@@ -1,0 +1,3769 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#include "mkvmuxer/mkvmuxer.h"
+
+#include <cfloat>
+#include <climits>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <memory>
+#include <new>
+#include <vector>
+
+#include "common/webmids.h"
+#include "mkvmuxer/mkvmuxerutil.h"
+#include "mkvmuxer/mkvwriter.h"
+#include "mkvparser/mkvparser.h"
+
+namespace mkvmuxer {
+
+const float MasteringMetadata::kValueNotPresent = FLT_MAX;
+const uint64_t Colour::kValueNotPresent = UINT64_MAX;
+
+namespace {
+// Deallocate the string designated by |dst|, and then copy the |src|
+// string to |dst|. The caller owns both the |src| string and the
+// |dst| copy (hence the caller is responsible for eventually
+// deallocating the strings, either directly, or indirectly via
+// StrCpy). Returns true if the source string was successfully copied
+// to the destination.
+bool StrCpy(const char* src, char** dst_ptr) {
+ if (dst_ptr == NULL)
+ return false;
+
+ char*& dst = *dst_ptr;
+
+ delete[] dst;
+ dst = NULL;
+
+ if (src == NULL)
+ return true;
+
+ const size_t size = strlen(src) + 1;
+
+ dst = new (std::nothrow) char[size]; // NOLINT
+ if (dst == NULL)
+ return false;
+
+ strcpy(dst, src); // NOLINT
+ return true;
+}
+
+typedef std::auto_ptr<PrimaryChromaticity> PrimaryChromaticityPtr;
+bool CopyChromaticity(const PrimaryChromaticity* src,
+ PrimaryChromaticityPtr* dst) {
+ if (!dst)
+ return false;
+
+ dst->reset(new (std::nothrow) PrimaryChromaticity(src->x, src->y));
+ if (!dst->get())
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////
+//
+// IMkvWriter Class
+
+IMkvWriter::IMkvWriter() {}
+
+IMkvWriter::~IMkvWriter() {}
+
+bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) {
+ // Level 0
+ uint64_t size = EbmlElementSize(libwebm::kMkvEBMLVersion, UINT64_C(1));
+ size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, UINT64_C(1));
+ size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, UINT64_C(4));
+ size += EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, UINT64_C(8));
+ size += EbmlElementSize(libwebm::kMkvDocType, "webm");
+ size += EbmlElementSize(libwebm::kMkvDocTypeVersion, doc_type_version);
+ size += EbmlElementSize(libwebm::kMkvDocTypeReadVersion, UINT64_C(2));
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion, UINT64_C(1)))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion, UINT64_C(1)))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength, UINT64_C(4)))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength, UINT64_C(8)))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvDocType, "webm"))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion, doc_type_version))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion, UINT64_C(2)))
+ return false;
+
+ return true;
+}
+
+bool WriteEbmlHeader(IMkvWriter* writer) {
+ return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion);
+}
+
+bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst,
+ int64_t start, int64_t size) {
+ // TODO(vigneshv): Check if this is a reasonable value.
+ const uint32_t kBufSize = 2048;
+ uint8_t* buf = new uint8_t[kBufSize];
+ int64_t offset = start;
+ while (size > 0) {
+ const int64_t read_len = (size > kBufSize) ? kBufSize : size;
+ if (source->Read(offset, static_cast<long>(read_len), buf))
+ return false;
+ dst->Write(buf, static_cast<uint32_t>(read_len));
+ offset += read_len;
+ size -= read_len;
+ }
+ delete[] buf;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Frame Class
+
+Frame::Frame()
+ : add_id_(0),
+ additional_(NULL),
+ additional_length_(0),
+ duration_(0),
+ duration_set_(false),
+ frame_(NULL),
+ is_key_(false),
+ length_(0),
+ track_number_(0),
+ timestamp_(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();
+ duration_set_ = frame.duration_set();
+ is_key_ = frame.is_key();
+ track_number_ = frame.track_number();
+ timestamp_ = frame.timestamp();
+ discard_padding_ = frame.discard_padding();
+ reference_block_timestamp_ = frame.reference_block_timestamp();
+ reference_block_timestamp_set_ = frame.reference_block_timestamp_set();
+ return true;
+}
+
+bool Frame::Init(const uint8_t* frame, uint64_t length) {
+ uint8_t* const data =
+ new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
+ if (!data)
+ return false;
+
+ delete[] frame_;
+ frame_ = data;
+ length_ = length;
+
+ memcpy(frame_, frame, static_cast<size_t>(length_));
+ return true;
+}
+
+bool Frame::AddAdditionalData(const uint8_t* additional, uint64_t length,
+ uint64_t add_id) {
+ uint8_t* const data =
+ new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
+ if (!data)
+ return false;
+
+ delete[] additional_;
+ additional_ = data;
+ additional_length_ = length;
+ add_id_ = add_id;
+
+ memcpy(additional_, additional, static_cast<size_t>(additional_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_duration(uint64_t duration) {
+ duration_ = duration;
+ duration_set_ = true;
+}
+
+void Frame::set_reference_block_timestamp(int64_t reference_block_timestamp) {
+ reference_block_timestamp_ = reference_block_timestamp;
+ reference_block_timestamp_set_ = true;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// CuePoint Class
+
+CuePoint::CuePoint()
+ : time_(0),
+ track_(0),
+ cluster_pos_(0),
+ block_number_(1),
+ output_block_number_(true) {}
+
+CuePoint::~CuePoint() {}
+
+bool CuePoint::Write(IMkvWriter* writer) const {
+ if (!writer || track_ < 1 || cluster_pos_ < 1)
+ return false;
+
+ uint64_t size =
+ EbmlElementSize(libwebm::kMkvCueClusterPosition, cluster_pos_);
+ size += EbmlElementSize(libwebm::kMkvCueTrack, track_);
+ if (output_block_number_ && block_number_ > 1)
+ size += EbmlElementSize(libwebm::kMkvCueBlockNumber, block_number_);
+ const uint64_t track_pos_size =
+ EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
+ const uint64_t payload_size =
+ EbmlElementSize(libwebm::kMkvCueTime, time_) + track_pos_size;
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvCuePoint, payload_size))
+ return false;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvCueTime, time_))
+ return false;
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack, track_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition, cluster_pos_))
+ return false;
+ if (output_block_number_ && block_number_ > 1)
+ if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber, block_number_))
+ return false;
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0)
+ return false;
+
+ if (stop_position - payload_position != static_cast<int64_t>(payload_size))
+ return false;
+
+ return true;
+}
+
+uint64_t CuePoint::PayloadSize() const {
+ uint64_t size =
+ EbmlElementSize(libwebm::kMkvCueClusterPosition, cluster_pos_);
+ size += EbmlElementSize(libwebm::kMkvCueTrack, track_);
+ if (output_block_number_ && block_number_ > 1)
+ size += EbmlElementSize(libwebm::kMkvCueBlockNumber, block_number_);
+ const uint64_t track_pos_size =
+ EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
+ const uint64_t payload_size =
+ EbmlElementSize(libwebm::kMkvCueTime, time_) + track_pos_size;
+
+ return payload_size;
+}
+
+uint64_t CuePoint::Size() const {
+ const uint64_t payload_size = PayloadSize();
+ return EbmlMasterElementSize(libwebm::kMkvCuePoint, payload_size) +
+ payload_size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Cues Class
+
+Cues::Cues()
+ : cue_entries_capacity_(0),
+ cue_entries_size_(0),
+ cue_entries_(NULL),
+ output_block_number_(true) {}
+
+Cues::~Cues() {
+ if (cue_entries_) {
+ for (int32_t i = 0; i < cue_entries_size_; ++i) {
+ CuePoint* const cue = cue_entries_[i];
+ delete cue;
+ }
+ delete[] cue_entries_;
+ }
+}
+
+bool Cues::AddCue(CuePoint* cue) {
+ if (!cue)
+ return false;
+
+ if ((cue_entries_size_ + 1) > cue_entries_capacity_) {
+ // Add more CuePoints.
+ const int32_t new_capacity =
+ (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2;
+
+ if (new_capacity < 1)
+ return false;
+
+ CuePoint** const cues =
+ new (std::nothrow) CuePoint*[new_capacity]; // NOLINT
+ if (!cues)
+ return false;
+
+ for (int32_t i = 0; i < cue_entries_size_; ++i) {
+ cues[i] = cue_entries_[i];
+ }
+
+ delete[] cue_entries_;
+
+ cue_entries_ = cues;
+ cue_entries_capacity_ = new_capacity;
+ }
+
+ cue->set_output_block_number(output_block_number_);
+ cue_entries_[cue_entries_size_++] = cue;
+ return true;
+}
+
+CuePoint* Cues::GetCueByIndex(int32_t index) const {
+ if (cue_entries_ == NULL)
+ return NULL;
+
+ if (index >= cue_entries_size_)
+ return NULL;
+
+ return cue_entries_[index];
+}
+
+uint64_t Cues::Size() {
+ uint64_t size = 0;
+ for (int32_t i = 0; i < cue_entries_size_; ++i)
+ size += GetCueByIndex(i)->Size();
+ size += EbmlMasterElementSize(libwebm::kMkvCues, size);
+ return size;
+}
+
+bool Cues::Write(IMkvWriter* writer) const {
+ if (!writer)
+ return false;
+
+ uint64_t size = 0;
+ for (int32_t i = 0; i < cue_entries_size_; ++i) {
+ const CuePoint* const cue = GetCueByIndex(i);
+
+ if (!cue)
+ return false;
+
+ size += cue->Size();
+ }
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvCues, size))
+ return false;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ for (int32_t i = 0; i < cue_entries_size_; ++i) {
+ const CuePoint* const cue = GetCueByIndex(i);
+
+ if (!cue->Write(writer))
+ return false;
+ }
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0)
+ return false;
+
+ if (stop_position - payload_position != static_cast<int64_t>(size))
+ return false;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// ContentEncAESSettings Class
+
+ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {}
+
+uint64_t ContentEncAESSettings::Size() const {
+ const uint64_t payload = PayloadSize();
+ const uint64_t size =
+ EbmlMasterElementSize(libwebm::kMkvContentEncAESSettings, payload) +
+ payload;
+ return size;
+}
+
+bool ContentEncAESSettings::Write(IMkvWriter* writer) const {
+ const uint64_t payload = PayloadSize();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncAESSettings,
+ payload))
+ return false;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvAESSettingsCipherMode,
+ cipher_mode_))
+ return false;
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(payload))
+ return false;
+
+ return true;
+}
+
+uint64_t ContentEncAESSettings::PayloadSize() const {
+ uint64_t size =
+ EbmlElementSize(libwebm::kMkvAESSettingsCipherMode, cipher_mode_);
+ return size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// ContentEncoding Class
+
+ContentEncoding::ContentEncoding()
+ : enc_algo_(5),
+ enc_key_id_(NULL),
+ encoding_order_(0),
+ encoding_scope_(1),
+ encoding_type_(1),
+ enc_key_id_length_(0) {}
+
+ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; }
+
+bool ContentEncoding::SetEncryptionID(const uint8_t* id, uint64_t length) {
+ if (!id || length < 1)
+ return false;
+
+ delete[] enc_key_id_;
+
+ enc_key_id_ =
+ new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
+ if (!enc_key_id_)
+ return false;
+
+ memcpy(enc_key_id_, id, static_cast<size_t>(length));
+ enc_key_id_length_ = length;
+
+ return true;
+}
+
+uint64_t ContentEncoding::Size() const {
+ const uint64_t encryption_size = EncryptionSize();
+ const uint64_t encoding_size = EncodingSize(0, encryption_size);
+ const uint64_t encodings_size =
+ EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
+ encoding_size;
+
+ return encodings_size;
+}
+
+bool ContentEncoding::Write(IMkvWriter* writer) const {
+ const uint64_t encryption_size = EncryptionSize();
+ const uint64_t encoding_size = EncodingSize(0, encryption_size);
+ const uint64_t size =
+ EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) +
+ encoding_size;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncoding,
+ encoding_size))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingOrder,
+ encoding_order_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope,
+ encoding_scope_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType,
+ encoding_type_))
+ return false;
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption,
+ encryption_size))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo, enc_algo_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvContentEncKeyID, enc_key_id_,
+ enc_key_id_length_))
+ return false;
+
+ if (!enc_aes_settings_.Write(writer))
+ return false;
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(size))
+ return false;
+
+ return true;
+}
+
+uint64_t ContentEncoding::EncodingSize(uint64_t compresion_size,
+ uint64_t encryption_size) const {
+ // TODO(fgalligan): Add support for compression settings.
+ if (compresion_size != 0)
+ return 0;
+
+ uint64_t encoding_size = 0;
+
+ if (encryption_size > 0) {
+ encoding_size +=
+ EbmlMasterElementSize(libwebm::kMkvContentEncryption, encryption_size) +
+ encryption_size;
+ }
+ encoding_size +=
+ EbmlElementSize(libwebm::kMkvContentEncodingType, encoding_type_);
+ encoding_size +=
+ EbmlElementSize(libwebm::kMkvContentEncodingScope, encoding_scope_);
+ encoding_size +=
+ EbmlElementSize(libwebm::kMkvContentEncodingOrder, encoding_order_);
+
+ return encoding_size;
+}
+
+uint64_t ContentEncoding::EncryptionSize() const {
+ const uint64_t aes_size = enc_aes_settings_.Size();
+
+ uint64_t encryption_size = EbmlElementSize(libwebm::kMkvContentEncKeyID,
+ enc_key_id_, enc_key_id_length_);
+ encryption_size += EbmlElementSize(libwebm::kMkvContentEncAlgo, enc_algo_);
+
+ return encryption_size + aes_size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Track Class
+
+Track::Track(unsigned int* seed)
+ : codec_id_(NULL),
+ codec_private_(NULL),
+ language_(NULL),
+ max_block_additional_id_(0),
+ name_(NULL),
+ number_(0),
+ type_(0),
+ uid_(MakeUID(seed)),
+ codec_delay_(0),
+ seek_pre_roll_(0),
+ default_duration_(0),
+ codec_private_length_(0),
+ content_encoding_entries_(NULL),
+ content_encoding_entries_size_(0) {}
+
+Track::~Track() {
+ delete[] codec_id_;
+ delete[] codec_private_;
+ delete[] language_;
+ delete[] name_;
+
+ if (content_encoding_entries_) {
+ for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
+ ContentEncoding* const encoding = content_encoding_entries_[i];
+ delete encoding;
+ }
+ delete[] content_encoding_entries_;
+ }
+}
+
+bool Track::AddContentEncoding() {
+ const uint32_t count = content_encoding_entries_size_ + 1;
+
+ ContentEncoding** const content_encoding_entries =
+ new (std::nothrow) ContentEncoding*[count]; // NOLINT
+ if (!content_encoding_entries)
+ return false;
+
+ ContentEncoding* const content_encoding =
+ new (std::nothrow) ContentEncoding(); // NOLINT
+ if (!content_encoding) {
+ delete[] content_encoding_entries;
+ return false;
+ }
+
+ for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
+ content_encoding_entries[i] = content_encoding_entries_[i];
+ }
+
+ delete[] content_encoding_entries_;
+
+ content_encoding_entries_ = content_encoding_entries;
+ content_encoding_entries_[content_encoding_entries_size_] = content_encoding;
+ content_encoding_entries_size_ = count;
+ return true;
+}
+
+ContentEncoding* Track::GetContentEncodingByIndex(uint32_t index) const {
+ if (content_encoding_entries_ == NULL)
+ return NULL;
+
+ if (index >= content_encoding_entries_size_)
+ return NULL;
+
+ return content_encoding_entries_[index];
+}
+
+uint64_t Track::PayloadSize() const {
+ uint64_t size = EbmlElementSize(libwebm::kMkvTrackNumber, number_);
+ size += EbmlElementSize(libwebm::kMkvTrackUID, uid_);
+ size += EbmlElementSize(libwebm::kMkvTrackType, type_);
+ if (codec_id_)
+ size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
+ if (codec_private_)
+ size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
+ codec_private_length_);
+ if (language_)
+ size += EbmlElementSize(libwebm::kMkvLanguage, language_);
+ if (name_)
+ size += EbmlElementSize(libwebm::kMkvName, name_);
+ if (max_block_additional_id_)
+ size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
+ max_block_additional_id_);
+ if (codec_delay_)
+ size += EbmlElementSize(libwebm::kMkvCodecDelay, codec_delay_);
+ if (seek_pre_roll_)
+ size += EbmlElementSize(libwebm::kMkvSeekPreRoll, seek_pre_roll_);
+ if (default_duration_)
+ size += EbmlElementSize(libwebm::kMkvDefaultDuration, default_duration_);
+
+ if (content_encoding_entries_size_ > 0) {
+ uint64_t content_encodings_size = 0;
+ for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
+ ContentEncoding* const encoding = content_encoding_entries_[i];
+ content_encodings_size += encoding->Size();
+ }
+
+ size += EbmlMasterElementSize(libwebm::kMkvContentEncodings,
+ content_encodings_size) +
+ content_encodings_size;
+ }
+
+ return size;
+}
+
+uint64_t Track::Size() const {
+ uint64_t size = PayloadSize();
+ size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size);
+ return size;
+}
+
+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_t payload_size = PayloadSize();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvTrackEntry, payload_size))
+ return false;
+
+ uint64_t size = EbmlElementSize(libwebm::kMkvTrackNumber, number_);
+ size += EbmlElementSize(libwebm::kMkvTrackUID, uid_);
+ size += EbmlElementSize(libwebm::kMkvTrackType, type_);
+ if (codec_id_)
+ size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
+ if (codec_private_)
+ size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
+ codec_private_length_);
+ if (language_)
+ size += EbmlElementSize(libwebm::kMkvLanguage, language_);
+ if (name_)
+ size += EbmlElementSize(libwebm::kMkvName, name_);
+ if (max_block_additional_id_)
+ size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID,
+ max_block_additional_id_);
+ if (codec_delay_)
+ size += EbmlElementSize(libwebm::kMkvCodecDelay, codec_delay_);
+ if (seek_pre_roll_)
+ size += EbmlElementSize(libwebm::kMkvSeekPreRoll, seek_pre_roll_);
+ if (default_duration_)
+ size += EbmlElementSize(libwebm::kMkvDefaultDuration, default_duration_);
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber, number_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID, uid_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvTrackType, type_))
+ return false;
+ if (max_block_additional_id_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID,
+ max_block_additional_id_)) {
+ return false;
+ }
+ }
+ if (codec_delay_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay, codec_delay_))
+ return false;
+ }
+ if (seek_pre_roll_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll, seek_pre_roll_))
+ return false;
+ }
+ if (default_duration_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration,
+ default_duration_))
+ return false;
+ }
+ if (codec_id_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvCodecID, codec_id_))
+ return false;
+ }
+ if (codec_private_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvCodecPrivate, codec_private_,
+ codec_private_length_))
+ return false;
+ }
+ if (language_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvLanguage, language_))
+ return false;
+ }
+ if (name_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvName, name_))
+ return false;
+ }
+
+ int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(size))
+ return false;
+
+ if (content_encoding_entries_size_ > 0) {
+ uint64_t content_encodings_size = 0;
+ for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
+ ContentEncoding* const encoding = content_encoding_entries_[i];
+ content_encodings_size += encoding->Size();
+ }
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncodings,
+ content_encodings_size))
+ return false;
+
+ for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) {
+ ContentEncoding* const encoding = content_encoding_entries_[i];
+ if (!encoding->Write(writer))
+ return false;
+ }
+ }
+
+ stop_position = writer->Position();
+ if (stop_position < 0)
+ return false;
+ return true;
+}
+
+bool Track::SetCodecPrivate(const uint8_t* codec_private, uint64_t length) {
+ if (!codec_private || length < 1)
+ return false;
+
+ delete[] codec_private_;
+
+ codec_private_ =
+ new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT
+ if (!codec_private_)
+ return false;
+
+ memcpy(codec_private_, codec_private, static_cast<size_t>(length));
+ codec_private_length_ = length;
+
+ return true;
+}
+
+void Track::set_codec_id(const char* codec_id) {
+ if (codec_id) {
+ delete[] codec_id_;
+
+ const size_t length = strlen(codec_id) + 1;
+ codec_id_ = new (std::nothrow) char[length]; // NOLINT
+ if (codec_id_) {
+#ifdef _MSC_VER
+ strcpy_s(codec_id_, length, codec_id);
+#else
+ strcpy(codec_id_, codec_id);
+#endif
+ }
+ }
+}
+
+// TODO(fgalligan): Vet the language parameter.
+void Track::set_language(const char* language) {
+ if (language) {
+ delete[] language_;
+
+ const size_t length = strlen(language) + 1;
+ language_ = new (std::nothrow) char[length]; // NOLINT
+ if (language_) {
+#ifdef _MSC_VER
+ strcpy_s(language_, length, language);
+#else
+ strcpy(language_, language);
+#endif
+ }
+ }
+}
+
+void Track::set_name(const char* name) {
+ if (name) {
+ delete[] name_;
+
+ const size_t length = strlen(name) + 1;
+ name_ = new (std::nothrow) char[length]; // NOLINT
+ if (name_) {
+#ifdef _MSC_VER
+ strcpy_s(name_, length, name);
+#else
+ strcpy(name_, name);
+#endif
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Colour and its child elements
+
+uint64_t PrimaryChromaticity::PrimaryChromaticityPayloadSize(
+ libwebm::MkvId x_id, libwebm::MkvId y_id) const {
+ return EbmlElementSize(x_id, x) + EbmlElementSize(y_id, y);
+}
+
+bool PrimaryChromaticity::Write(IMkvWriter* writer, libwebm::MkvId x_id,
+ libwebm::MkvId y_id) const {
+ return WriteEbmlElement(writer, x_id, x) && WriteEbmlElement(writer, y_id, y);
+}
+
+uint64_t MasteringMetadata::MasteringMetadataSize() const {
+ uint64_t size = PayloadSize();
+
+ if (size > 0)
+ size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size);
+
+ return size;
+}
+
+bool MasteringMetadata::Write(IMkvWriter* writer) const {
+ const uint64_t size = PayloadSize();
+
+ // Don't write an empty element.
+ if (size == 0)
+ return true;
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvMasteringMetadata, size))
+ return false;
+ if (luminance_max != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvLuminanceMax, luminance_max)) {
+ return false;
+ }
+ if (luminance_min != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvLuminanceMin, luminance_min)) {
+ return false;
+ }
+ if (r_ &&
+ !r_->Write(writer, libwebm::kMkvPrimaryRChromaticityX,
+ libwebm::kMkvPrimaryRChromaticityY)) {
+ return false;
+ }
+ if (g_ &&
+ !g_->Write(writer, libwebm::kMkvPrimaryGChromaticityX,
+ libwebm::kMkvPrimaryGChromaticityY)) {
+ return false;
+ }
+ if (b_ &&
+ !b_->Write(writer, libwebm::kMkvPrimaryBChromaticityX,
+ libwebm::kMkvPrimaryBChromaticityY)) {
+ return false;
+ }
+ if (white_point_ &&
+ !white_point_->Write(writer, libwebm::kMkvWhitePointChromaticityX,
+ libwebm::kMkvWhitePointChromaticityY)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool MasteringMetadata::SetChromaticity(
+ const PrimaryChromaticity* r, const PrimaryChromaticity* g,
+ const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) {
+ PrimaryChromaticityPtr r_ptr(NULL);
+ if (r) {
+ if (!CopyChromaticity(r, &r_ptr))
+ return false;
+ }
+ PrimaryChromaticityPtr g_ptr(NULL);
+ if (g) {
+ if (!CopyChromaticity(g, &g_ptr))
+ return false;
+ }
+ PrimaryChromaticityPtr b_ptr(NULL);
+ if (b) {
+ if (!CopyChromaticity(b, &b_ptr))
+ return false;
+ }
+ PrimaryChromaticityPtr wp_ptr(NULL);
+ if (white_point) {
+ if (!CopyChromaticity(white_point, &wp_ptr))
+ return false;
+ }
+
+ r_ = r_ptr.release();
+ g_ = g_ptr.release();
+ b_ = b_ptr.release();
+ white_point_ = wp_ptr.release();
+ return true;
+}
+
+uint64_t MasteringMetadata::PayloadSize() const {
+ uint64_t size = 0;
+
+ if (luminance_max != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvLuminanceMax, luminance_max);
+ if (luminance_min != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvLuminanceMin, luminance_min);
+
+ if (r_) {
+ size += r_->PrimaryChromaticityPayloadSize(
+ libwebm::kMkvPrimaryRChromaticityX, libwebm::kMkvPrimaryRChromaticityY);
+ }
+ if (g_) {
+ size += g_->PrimaryChromaticityPayloadSize(
+ libwebm::kMkvPrimaryGChromaticityX, libwebm::kMkvPrimaryGChromaticityY);
+ }
+ if (b_) {
+ size += b_->PrimaryChromaticityPayloadSize(
+ libwebm::kMkvPrimaryBChromaticityX, libwebm::kMkvPrimaryBChromaticityY);
+ }
+ if (white_point_) {
+ size += white_point_->PrimaryChromaticityPayloadSize(
+ libwebm::kMkvWhitePointChromaticityX,
+ libwebm::kMkvWhitePointChromaticityY);
+ }
+
+ return size;
+}
+
+uint64_t Colour::ColourSize() const {
+ uint64_t size = PayloadSize();
+
+ if (size > 0)
+ size += EbmlMasterElementSize(libwebm::kMkvColour, size);
+
+ return size;
+}
+
+bool Colour::Write(IMkvWriter* writer) const {
+ const uint64_t size = PayloadSize();
+
+ // Don't write an empty element.
+ if (size == 0)
+ return true;
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size))
+ return false;
+
+ if (matrix_coefficients != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients,
+ matrix_coefficients)) {
+ return false;
+ }
+ if (bits_per_channel != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel,
+ bits_per_channel)) {
+ return false;
+ }
+ if (chroma_subsampling_horz != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz,
+ chroma_subsampling_horz)) {
+ return false;
+ }
+ if (chroma_subsampling_vert != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert,
+ chroma_subsampling_vert)) {
+ return false;
+ }
+
+ if (cb_subsampling_horz != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz,
+ cb_subsampling_horz)) {
+ return false;
+ }
+ if (cb_subsampling_vert != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert,
+ cb_subsampling_vert)) {
+ return false;
+ }
+ if (chroma_siting_horz != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz,
+ chroma_siting_horz)) {
+ return false;
+ }
+ if (chroma_siting_vert != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert,
+ chroma_siting_vert)) {
+ return false;
+ }
+ if (range != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvRange, range)) {
+ return false;
+ }
+ if (transfer_characteristics != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics,
+ transfer_characteristics)) {
+ return false;
+ }
+ if (primaries != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvPrimaries, primaries)) {
+ return false;
+ }
+ if (max_cll != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvMaxCLL, max_cll)) {
+ return false;
+ }
+ if (max_fall != kValueNotPresent &&
+ !WriteEbmlElement(writer, libwebm::kMkvMaxFALL, max_fall)) {
+ return false;
+ }
+
+ if (mastering_metadata_ && !mastering_metadata_->Write(writer))
+ return false;
+
+ return true;
+}
+
+bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) {
+ std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
+ if (!mm_ptr.get())
+ return false;
+
+ mm_ptr->luminance_max = mastering_metadata.luminance_max;
+ mm_ptr->luminance_min = mastering_metadata.luminance_min;
+
+ if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(),
+ mastering_metadata.b(),
+ mastering_metadata.white_point())) {
+ return false;
+ }
+
+ delete mastering_metadata_;
+ mastering_metadata_ = mm_ptr.release();
+ return true;
+}
+
+uint64_t Colour::PayloadSize() const {
+ uint64_t size = 0;
+
+ if (matrix_coefficients != kValueNotPresent)
+ size +=
+ EbmlElementSize(libwebm::kMkvMatrixCoefficients, matrix_coefficients);
+ if (bits_per_channel != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvBitsPerChannel, bits_per_channel);
+ if (chroma_subsampling_horz != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz,
+ chroma_subsampling_horz);
+ if (chroma_subsampling_vert != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert,
+ chroma_subsampling_vert);
+ if (cb_subsampling_horz != kValueNotPresent)
+ size +=
+ EbmlElementSize(libwebm::kMkvCbSubsamplingHorz, cb_subsampling_horz);
+ if (cb_subsampling_vert != kValueNotPresent)
+ size +=
+ EbmlElementSize(libwebm::kMkvCbSubsamplingVert, cb_subsampling_vert);
+ if (chroma_siting_horz != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvChromaSitingHorz, chroma_siting_horz);
+ if (chroma_siting_vert != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvChromaSitingVert, chroma_siting_vert);
+ if (range != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvRange, range);
+ if (transfer_characteristics != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvTransferCharacteristics,
+ transfer_characteristics);
+ if (primaries != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvPrimaries, primaries);
+ if (max_cll != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvMaxCLL, max_cll);
+ if (max_fall != kValueNotPresent)
+ size += EbmlElementSize(libwebm::kMkvMaxFALL, max_fall);
+
+ if (mastering_metadata_)
+ size += mastering_metadata_->MasteringMetadataSize();
+
+ return size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// VideoTrack Class
+
+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),
+ alpha_mode_(0),
+ width_(0),
+ colour_(NULL) {}
+
+VideoTrack::~VideoTrack() { delete colour_; }
+
+bool VideoTrack::SetStereoMode(uint64_t stereo_mode) {
+ if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst &&
+ stereo_mode != kTopBottomRightIsFirst &&
+ stereo_mode != kTopBottomLeftIsFirst &&
+ stereo_mode != kSideBySideRightIsFirst)
+ return false;
+
+ stereo_mode_ = stereo_mode;
+ return true;
+}
+
+bool VideoTrack::SetAlphaMode(uint64_t alpha_mode) {
+ if (alpha_mode != kNoAlpha && alpha_mode != kAlpha)
+ return false;
+
+ alpha_mode_ = alpha_mode;
+ return true;
+}
+
+uint64_t VideoTrack::PayloadSize() const {
+ const uint64_t parent_size = Track::PayloadSize();
+
+ uint64_t size = VideoPayloadSize();
+ size += EbmlMasterElementSize(libwebm::kMkvVideo, size);
+
+ return parent_size + size;
+}
+
+bool VideoTrack::Write(IMkvWriter* writer) const {
+ if (!Track::Write(writer))
+ return false;
+
+ const uint64_t size = VideoPayloadSize();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvVideo, size))
+ return false;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvPixelWidth, width_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvPixelHeight, height_))
+ return false;
+ if (display_width_ > 0) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth, display_width_))
+ return false;
+ }
+ if (display_height_ > 0) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight, display_height_))
+ return false;
+ }
+ if (crop_left_ > 0) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft, crop_left_))
+ return false;
+ }
+ if (crop_right_ > 0) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight, crop_right_))
+ return false;
+ }
+ if (crop_top_ > 0) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop, crop_top_))
+ return false;
+ }
+ if (crop_bottom_ > 0) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom, crop_bottom_))
+ return false;
+ }
+ if (stereo_mode_ > kMono) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode, stereo_mode_))
+ return false;
+ }
+ if (alpha_mode_ > kNoAlpha) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode, alpha_mode_))
+ return false;
+ }
+ if (frame_rate_ > 0.0) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvFrameRate,
+ static_cast<float>(frame_rate_))) {
+ return false;
+ }
+ }
+ if (colour_) {
+ if (!colour_->Write(writer))
+ return false;
+ }
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(size)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool VideoTrack::SetColour(const Colour& colour) {
+ std::auto_ptr<Colour> colour_ptr(new Colour());
+ if (!colour_ptr.get())
+ return false;
+
+ if (colour.mastering_metadata()) {
+ if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata()))
+ return false;
+ }
+
+ colour_ptr->matrix_coefficients = colour.matrix_coefficients;
+ colour_ptr->bits_per_channel = colour.bits_per_channel;
+ colour_ptr->chroma_subsampling_horz = colour.chroma_subsampling_horz;
+ colour_ptr->chroma_subsampling_vert = colour.chroma_subsampling_vert;
+ colour_ptr->cb_subsampling_horz = colour.cb_subsampling_horz;
+ colour_ptr->cb_subsampling_vert = colour.cb_subsampling_vert;
+ colour_ptr->chroma_siting_horz = colour.chroma_siting_horz;
+ colour_ptr->chroma_siting_vert = colour.chroma_siting_vert;
+ colour_ptr->range = colour.range;
+ colour_ptr->transfer_characteristics = colour.transfer_characteristics;
+ colour_ptr->primaries = colour.primaries;
+ colour_ptr->max_cll = colour.max_cll;
+ colour_ptr->max_fall = colour.max_fall;
+ colour_ = colour_ptr.release();
+ return true;
+}
+
+uint64_t VideoTrack::VideoPayloadSize() const {
+ uint64_t size = EbmlElementSize(libwebm::kMkvPixelWidth, width_);
+ size += EbmlElementSize(libwebm::kMkvPixelHeight, height_);
+ if (display_width_ > 0)
+ size += EbmlElementSize(libwebm::kMkvDisplayWidth, display_width_);
+ if (display_height_ > 0)
+ size += EbmlElementSize(libwebm::kMkvDisplayHeight, display_height_);
+ if (crop_left_ > 0)
+ size += EbmlElementSize(libwebm::kMkvPixelCropLeft, crop_left_);
+ if (crop_right_ > 0)
+ size += EbmlElementSize(libwebm::kMkvPixelCropRight, crop_right_);
+ if (crop_top_ > 0)
+ size += EbmlElementSize(libwebm::kMkvPixelCropTop, crop_top_);
+ if (crop_bottom_ > 0)
+ size += EbmlElementSize(libwebm::kMkvPixelCropBottom, crop_bottom_);
+ if (stereo_mode_ > kMono)
+ size += EbmlElementSize(libwebm::kMkvStereoMode, stereo_mode_);
+ if (alpha_mode_ > kNoAlpha)
+ size += EbmlElementSize(libwebm::kMkvAlphaMode, alpha_mode_);
+ if (frame_rate_ > 0.0)
+ size += EbmlElementSize(libwebm::kMkvFrameRate,
+ static_cast<float>(frame_rate_));
+ if (colour_)
+ size += colour_->ColourSize();
+
+ return size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// AudioTrack Class
+
+AudioTrack::AudioTrack(unsigned int* seed)
+ : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {}
+
+AudioTrack::~AudioTrack() {}
+
+uint64_t AudioTrack::PayloadSize() const {
+ const uint64_t parent_size = Track::PayloadSize();
+
+ uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
+ static_cast<float>(sample_rate_));
+ size += EbmlElementSize(libwebm::kMkvChannels, channels_);
+ if (bit_depth_ > 0)
+ size += EbmlElementSize(libwebm::kMkvBitDepth, bit_depth_);
+ size += EbmlMasterElementSize(libwebm::kMkvAudio, size);
+
+ return parent_size + size;
+}
+
+bool AudioTrack::Write(IMkvWriter* writer) const {
+ if (!Track::Write(writer))
+ return false;
+
+ // Calculate AudioSettings size.
+ uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency,
+ static_cast<float>(sample_rate_));
+ size += EbmlElementSize(libwebm::kMkvChannels, channels_);
+ if (bit_depth_ > 0)
+ size += EbmlElementSize(libwebm::kMkvBitDepth, bit_depth_);
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvAudio, size))
+ return false;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvSamplingFrequency,
+ static_cast<float>(sample_rate_)))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvChannels, channels_))
+ return false;
+ if (bit_depth_ > 0)
+ if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth, bit_depth_))
+ return false;
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(size))
+ return false;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Tracks Class
+
+const char Tracks::kOpusCodecId[] = "A_OPUS";
+const char Tracks::kVorbisCodecId[] = "A_VORBIS";
+const char Tracks::kVp8CodecId[] = "V_VP8";
+const char Tracks::kVp9CodecId[] = "V_VP9";
+const char Tracks::kVp10CodecId[] = "V_VP10";
+
+Tracks::Tracks()
+ : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {}
+
+Tracks::~Tracks() {
+ if (track_entries_) {
+ for (uint32_t i = 0; i < track_entries_size_; ++i) {
+ Track* const track = track_entries_[i];
+ delete track;
+ }
+ delete[] track_entries_;
+ }
+}
+
+bool Tracks::AddTrack(Track* track, int32_t number) {
+ if (number < 0 || wrote_tracks_)
+ return false;
+
+ // This muxer only supports track numbers in the range [1, 126], in
+ // order to be able (to use Matroska integer representation) to
+ // serialize the block header (of which the track number is a part)
+ // for a frame using exactly 4 bytes.
+
+ if (number > 0x7E)
+ return false;
+
+ uint32_t track_num = number;
+
+ if (track_num > 0) {
+ // Check to make sure a track does not already have |track_num|.
+ for (uint32_t i = 0; i < track_entries_size_; ++i) {
+ if (track_entries_[i]->number() == track_num)
+ return false;
+ }
+ }
+
+ const uint32_t count = track_entries_size_ + 1;
+
+ Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT
+ if (!track_entries)
+ return false;
+
+ for (uint32_t i = 0; i < track_entries_size_; ++i) {
+ track_entries[i] = track_entries_[i];
+ }
+
+ delete[] track_entries_;
+
+ // Find the lowest availible track number > 0.
+ if (track_num == 0) {
+ track_num = count;
+
+ // Check to make sure a track does not already have |track_num|.
+ bool exit = false;
+ do {
+ exit = true;
+ for (uint32_t i = 0; i < track_entries_size_; ++i) {
+ if (track_entries[i]->number() == track_num) {
+ track_num++;
+ exit = false;
+ break;
+ }
+ }
+ } while (!exit);
+ }
+ track->set_number(track_num);
+
+ track_entries_ = track_entries;
+ track_entries_[track_entries_size_] = track;
+ track_entries_size_ = count;
+ return true;
+}
+
+const Track* Tracks::GetTrackByIndex(uint32_t index) const {
+ if (track_entries_ == NULL)
+ return NULL;
+
+ if (index >= track_entries_size_)
+ return NULL;
+
+ return track_entries_[index];
+}
+
+Track* Tracks::GetTrackByNumber(uint64_t track_number) const {
+ const int32_t count = track_entries_size();
+ for (int32_t i = 0; i < count; ++i) {
+ if (track_entries_[i]->number() == track_number)
+ return track_entries_[i];
+ }
+
+ return NULL;
+}
+
+bool Tracks::TrackIsAudio(uint64_t track_number) const {
+ const Track* const track = GetTrackByNumber(track_number);
+
+ if (track->type() == kAudio)
+ return true;
+
+ return false;
+}
+
+bool Tracks::TrackIsVideo(uint64_t track_number) const {
+ const Track* const track = GetTrackByNumber(track_number);
+
+ if (track->type() == kVideo)
+ return true;
+
+ return false;
+}
+
+bool Tracks::Write(IMkvWriter* writer) const {
+ uint64_t size = 0;
+ const int32_t count = track_entries_size();
+ for (int32_t i = 0; i < count; ++i) {
+ const Track* const track = GetTrackByIndex(i);
+
+ if (!track)
+ return false;
+
+ size += track->Size();
+ }
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvTracks, size))
+ return false;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ for (int32_t i = 0; i < count; ++i) {
+ const Track* const track = GetTrackByIndex(i);
+ if (!track->Write(writer))
+ return false;
+ }
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(size))
+ return false;
+
+ wrote_tracks_ = true;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Chapter Class
+
+bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); }
+
+void Chapter::set_time(const Segment& segment, uint64_t start_ns,
+ uint64_t end_ns) {
+ const SegmentInfo* const info = segment.GetSegmentInfo();
+ const uint64_t timecode_scale = info->timecode_scale();
+ start_timecode_ = start_ns / timecode_scale;
+ end_timecode_ = end_ns / timecode_scale;
+}
+
+bool Chapter::add_string(const char* title, const char* language,
+ const char* country) {
+ if (!ExpandDisplaysArray())
+ return false;
+
+ Display& d = displays_[displays_count_++];
+ d.Init();
+
+ if (!d.set_title(title))
+ return false;
+
+ if (!d.set_language(language))
+ return false;
+
+ if (!d.set_country(country))
+ return false;
+
+ return true;
+}
+
+Chapter::Chapter() {
+ // This ctor only constructs the object. Proper initialization is
+ // done in Init() (called in Chapters::AddChapter()). The only
+ // reason we bother implementing this ctor is because we had to
+ // declare it as private (along with the dtor), in order to prevent
+ // clients from creating Chapter instances (a privelege we grant
+ // only to the Chapters class). Doing no initialization here also
+ // means that creating arrays of chapter objects is more efficient,
+ // because we only initialize each new chapter object as it becomes
+ // active on the array.
+}
+
+Chapter::~Chapter() {}
+
+void Chapter::Init(unsigned int* seed) {
+ id_ = NULL;
+ start_timecode_ = 0;
+ end_timecode_ = 0;
+ displays_ = NULL;
+ displays_size_ = 0;
+ displays_count_ = 0;
+ uid_ = MakeUID(seed);
+}
+
+void Chapter::ShallowCopy(Chapter* dst) const {
+ dst->id_ = id_;
+ dst->start_timecode_ = start_timecode_;
+ dst->end_timecode_ = end_timecode_;
+ dst->uid_ = uid_;
+ dst->displays_ = displays_;
+ dst->displays_size_ = displays_size_;
+ dst->displays_count_ = displays_count_;
+}
+
+void Chapter::Clear() {
+ StrCpy(NULL, &id_);
+
+ while (displays_count_ > 0) {
+ Display& d = displays_[--displays_count_];
+ d.Clear();
+ }
+
+ delete[] displays_;
+ displays_ = NULL;
+
+ displays_size_ = 0;
+}
+
+bool Chapter::ExpandDisplaysArray() {
+ if (displays_size_ > displays_count_)
+ return true; // nothing to do yet
+
+ const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_;
+
+ Display* const displays = new (std::nothrow) Display[size]; // NOLINT
+ if (displays == NULL)
+ return false;
+
+ for (int idx = 0; idx < displays_count_; ++idx) {
+ displays[idx] = displays_[idx]; // shallow copy
+ }
+
+ delete[] displays_;
+
+ displays_ = displays;
+ displays_size_ = size;
+
+ return true;
+}
+
+uint64_t Chapter::WriteAtom(IMkvWriter* writer) const {
+ uint64_t payload_size =
+ EbmlElementSize(libwebm::kMkvChapterStringUID, id_) +
+ EbmlElementSize(libwebm::kMkvChapterUID, uid_) +
+ EbmlElementSize(libwebm::kMkvChapterTimeStart, start_timecode_) +
+ EbmlElementSize(libwebm::kMkvChapterTimeEnd, end_timecode_);
+
+ for (int idx = 0; idx < displays_count_; ++idx) {
+ const Display& d = displays_[idx];
+ payload_size += d.WriteDisplay(NULL);
+ }
+
+ const uint64_t atom_size =
+ EbmlMasterElementSize(libwebm::kMkvChapterAtom, payload_size) +
+ payload_size;
+
+ if (writer == NULL)
+ return atom_size;
+
+ const int64_t start = writer->Position();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterAtom, payload_size))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvChapterStringUID, id_))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvChapterUID, uid_))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart, start_timecode_))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd, end_timecode_))
+ return 0;
+
+ for (int idx = 0; idx < displays_count_; ++idx) {
+ const Display& d = displays_[idx];
+
+ if (!d.WriteDisplay(writer))
+ return 0;
+ }
+
+ const int64_t stop = writer->Position();
+
+ if (stop >= start && uint64_t(stop - start) != atom_size)
+ return 0;
+
+ return atom_size;
+}
+
+void Chapter::Display::Init() {
+ title_ = NULL;
+ language_ = NULL;
+ country_ = NULL;
+}
+
+void Chapter::Display::Clear() {
+ StrCpy(NULL, &title_);
+ StrCpy(NULL, &language_);
+ StrCpy(NULL, &country_);
+}
+
+bool Chapter::Display::set_title(const char* title) {
+ return StrCpy(title, &title_);
+}
+
+bool Chapter::Display::set_language(const char* language) {
+ return StrCpy(language, &language_);
+}
+
+bool Chapter::Display::set_country(const char* country) {
+ return StrCpy(country, &country_);
+}
+
+uint64_t Chapter::Display::WriteDisplay(IMkvWriter* writer) const {
+ uint64_t payload_size = EbmlElementSize(libwebm::kMkvChapString, title_);
+
+ if (language_)
+ payload_size += EbmlElementSize(libwebm::kMkvChapLanguage, language_);
+
+ if (country_)
+ payload_size += EbmlElementSize(libwebm::kMkvChapCountry, country_);
+
+ const uint64_t display_size =
+ EbmlMasterElementSize(libwebm::kMkvChapterDisplay, payload_size) +
+ payload_size;
+
+ if (writer == NULL)
+ return display_size;
+
+ const int64_t start = writer->Position();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterDisplay,
+ payload_size))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvChapString, title_))
+ return 0;
+
+ if (language_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvChapLanguage, language_))
+ return 0;
+ }
+
+ if (country_) {
+ if (!WriteEbmlElement(writer, libwebm::kMkvChapCountry, country_))
+ return 0;
+ }
+
+ const int64_t stop = writer->Position();
+
+ if (stop >= start && uint64_t(stop - start) != display_size)
+ return 0;
+
+ return display_size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Chapters Class
+
+Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {}
+
+Chapters::~Chapters() {
+ while (chapters_count_ > 0) {
+ Chapter& chapter = chapters_[--chapters_count_];
+ chapter.Clear();
+ }
+
+ delete[] chapters_;
+ chapters_ = NULL;
+}
+
+int Chapters::Count() const { return chapters_count_; }
+
+Chapter* Chapters::AddChapter(unsigned int* seed) {
+ if (!ExpandChaptersArray())
+ return NULL;
+
+ Chapter& chapter = chapters_[chapters_count_++];
+ chapter.Init(seed);
+
+ return &chapter;
+}
+
+bool Chapters::Write(IMkvWriter* writer) const {
+ if (writer == NULL)
+ return false;
+
+ const uint64_t payload_size = WriteEdition(NULL); // return size only
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapters, payload_size))
+ return false;
+
+ const int64_t start = writer->Position();
+
+ if (WriteEdition(writer) == 0) // error
+ return false;
+
+ const int64_t stop = writer->Position();
+
+ if (stop >= start && uint64_t(stop - start) != payload_size)
+ return false;
+
+ return true;
+}
+
+bool Chapters::ExpandChaptersArray() {
+ if (chapters_size_ > chapters_count_)
+ return true; // nothing to do yet
+
+ const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_;
+
+ Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT
+ if (chapters == NULL)
+ return false;
+
+ for (int idx = 0; idx < chapters_count_; ++idx) {
+ const Chapter& src = chapters_[idx];
+ Chapter* const dst = chapters + idx;
+ src.ShallowCopy(dst);
+ }
+
+ delete[] chapters_;
+
+ chapters_ = chapters;
+ chapters_size_ = size;
+
+ return true;
+}
+
+uint64_t Chapters::WriteEdition(IMkvWriter* writer) const {
+ uint64_t payload_size = 0;
+
+ for (int idx = 0; idx < chapters_count_; ++idx) {
+ const Chapter& chapter = chapters_[idx];
+ payload_size += chapter.WriteAtom(NULL);
+ }
+
+ const uint64_t edition_size =
+ EbmlMasterElementSize(libwebm::kMkvEditionEntry, payload_size) +
+ payload_size;
+
+ if (writer == NULL) // return size only
+ return edition_size;
+
+ const int64_t start = writer->Position();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvEditionEntry, payload_size))
+ return 0; // error
+
+ for (int idx = 0; idx < chapters_count_; ++idx) {
+ const Chapter& chapter = chapters_[idx];
+
+ const uint64_t chapter_size = chapter.WriteAtom(writer);
+ if (chapter_size == 0) // error
+ return 0;
+ }
+
+ const int64_t stop = writer->Position();
+
+ if (stop >= start && uint64_t(stop - start) != edition_size)
+ return 0;
+
+ 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_t Tag::Write(IMkvWriter* writer) const {
+ uint64_t 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_t tag_size =
+ EbmlMasterElementSize(libwebm::kMkvTag, payload_size) + payload_size;
+
+ if (writer == NULL)
+ return tag_size;
+
+ const int64_t start = writer->Position();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::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_t stop = writer->Position();
+
+ if (stop >= start && uint64_t(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_t Tag::SimpleTag::Write(IMkvWriter* writer) const {
+ uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_);
+
+ payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_);
+
+ const uint64_t simple_tag_size =
+ EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) +
+ payload_size;
+
+ if (writer == NULL)
+ return simple_tag_size;
+
+ const int64_t start = writer->Position();
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_))
+ return 0;
+
+ const int64_t stop = writer->Position();
+
+ if (stop >= start && uint64_t(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_t payload_size = 0;
+
+ for (int idx = 0; idx < tags_count_; ++idx) {
+ const Tag& tag = tags_[idx];
+ payload_size += tag.Write(NULL);
+ }
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvTags, payload_size))
+ return false;
+
+ const int64_t start = writer->Position();
+
+ for (int idx = 0; idx < tags_count_; ++idx) {
+ const Tag& tag = tags_[idx];
+
+ const uint64_t tag_size = tag.Write(writer);
+ if (tag_size == 0) // error
+ return 0;
+ }
+
+ const int64_t stop = writer->Position();
+
+ if (stop >= start && uint64_t(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_t timecode, int64_t cues_pos, uint64_t timecode_scale,
+ bool write_last_frame_with_duration, bool fixed_size_timecode)
+ : blocks_added_(0),
+ finalized_(false),
+ fixed_size_timecode_(fixed_size_timecode),
+ header_written_(false),
+ payload_size_(0),
+ position_for_cues_(cues_pos),
+ size_position_(-1),
+ timecode_(timecode),
+ timecode_scale_(timecode_scale),
+ write_last_frame_with_duration_(write_last_frame_with_duration),
+ writer_(NULL) {}
+
+Cluster::~Cluster() {}
+
+bool Cluster::Init(IMkvWriter* ptr_writer) {
+ if (!ptr_writer) {
+ return false;
+ }
+ writer_ = ptr_writer;
+ return true;
+}
+
+bool Cluster::AddFrame(const Frame* const frame) {
+ return QueueOrWriteFrame(frame);
+}
+
+bool Cluster::AddFrame(const uint8_t* data, uint64_t length,
+ uint64_t track_number, uint64_t abs_timecode,
+ bool is_key) {
+ 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 QueueOrWriteFrame(&frame);
+}
+
+bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
+ const uint8_t* additional,
+ uint64_t additional_length,
+ uint64_t add_id, uint64_t track_number,
+ uint64_t abs_timecode, bool is_key) {
+ 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 QueueOrWriteFrame(&frame);
+}
+
+bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
+ int64_t discard_padding,
+ uint64_t track_number,
+ uint64_t abs_timecode, bool is_key) {
+ 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 QueueOrWriteFrame(&frame);
+}
+
+bool Cluster::AddMetadata(const uint8_t* data, uint64_t length,
+ uint64_t track_number, uint64_t abs_timecode,
+ uint64_t 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 QueueOrWriteFrame(&frame);
+}
+
+void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; }
+
+bool Cluster::Finalize() {
+ return !write_last_frame_with_duration_ && Finalize(false, 0);
+}
+
+bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) {
+ if (!writer_ || finalized_)
+ return false;
+
+ if (write_last_frame_with_duration_) {
+ // Write out held back Frames. This essentially performs a k-way merge
+ // across all tracks in the increasing order of timestamps.
+ while (!stored_frames_.empty()) {
+ Frame* frame = stored_frames_.begin()->second.front();
+
+ // Get the next frame to write (frame with least timestamp across all
+ // tracks).
+ for (FrameMapIterator frames_iterator = ++stored_frames_.begin();
+ frames_iterator != stored_frames_.end(); ++frames_iterator) {
+ if (frames_iterator->second.front()->timestamp() < frame->timestamp()) {
+ frame = frames_iterator->second.front();
+ }
+ }
+
+ // Set the duration if it's the last frame for the track.
+ if (set_last_frame_duration &&
+ stored_frames_[frame->track_number()].size() == 1 &&
+ !frame->duration_set()) {
+ frame->set_duration(duration - frame->timestamp());
+ if (!frame->is_key() && !frame->reference_block_timestamp_set()) {
+ frame->set_reference_block_timestamp(
+ last_block_timestamp_[frame->track_number()]);
+ }
+ }
+
+ // Write the frame and remove it from |stored_frames_|.
+ const bool wrote_frame = DoWriteFrame(frame);
+ stored_frames_[frame->track_number()].pop_front();
+ if (stored_frames_[frame->track_number()].empty()) {
+ stored_frames_.erase(frame->track_number());
+ }
+ delete frame;
+ if (!wrote_frame)
+ return false;
+ }
+ }
+
+ if (size_position_ == -1)
+ return false;
+
+ if (writer_->Seekable()) {
+ const int64_t pos = writer_->Position();
+
+ if (writer_->Position(size_position_))
+ return false;
+
+ if (WriteUIntSize(writer_, payload_size(), 8))
+ return false;
+
+ if (writer_->Position(pos))
+ return false;
+ }
+
+ finalized_ = true;
+
+ return true;
+}
+
+uint64_t Cluster::Size() const {
+ const uint64_t element_size =
+ EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) +
+ payload_size_;
+ return element_size;
+}
+
+bool Cluster::PreWriteBlock() {
+ if (finalized_)
+ return false;
+
+ if (!header_written_) {
+ if (!WriteClusterHeader())
+ return false;
+ }
+
+ return true;
+}
+
+void Cluster::PostWriteBlock(uint64_t element_size) {
+ AddPayloadSize(element_size);
+ ++blocks_added_;
+}
+
+int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const {
+ const int64_t cluster_timecode = this->Cluster::timecode();
+ const int64_t rel_timecode =
+ static_cast<int64_t>(abs_timecode) - cluster_timecode;
+
+ if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode)
+ return -1;
+
+ return rel_timecode;
+}
+
+bool Cluster::DoWriteFrame(const Frame* const frame) {
+ if (!frame || !frame->IsValid())
+ return false;
+
+ if (!PreWriteBlock())
+ return false;
+
+ const uint64_t element_size = WriteFrame(writer_, frame, this);
+ if (element_size == 0)
+ return false;
+
+ PostWriteBlock(element_size);
+ last_block_timestamp_[frame->track_number()] = frame->timestamp();
+ return true;
+}
+
+bool Cluster::QueueOrWriteFrame(const Frame* const frame) {
+ if (!frame || !frame->IsValid())
+ return false;
+
+ // If |write_last_frame_with_duration_| is not set, then write the frame right
+ // away.
+ if (!write_last_frame_with_duration_) {
+ return DoWriteFrame(frame);
+ }
+
+ // Queue the current frame.
+ uint64_t track_number = frame->track_number();
+ Frame* const frame_to_store = new Frame();
+ frame_to_store->CopyFrom(*frame);
+ stored_frames_[track_number].push_back(frame_to_store);
+
+ // Iterate through all queued frames in the current track except the last one
+ // and write it if it is okay to do so (i.e.) no other track has an held back
+ // frame with timestamp <= the timestamp of the frame in question.
+ std::vector<std::list<Frame*>::iterator> frames_to_erase;
+ for (std::list<Frame *>::iterator
+ current_track_iterator = stored_frames_[track_number].begin(),
+ end = --stored_frames_[track_number].end();
+ current_track_iterator != end; ++current_track_iterator) {
+ const Frame* const frame_to_write = *current_track_iterator;
+ bool okay_to_write = true;
+ for (FrameMapIterator track_iterator = stored_frames_.begin();
+ track_iterator != stored_frames_.end(); ++track_iterator) {
+ if (track_iterator->first == track_number) {
+ continue;
+ }
+ if (track_iterator->second.front()->timestamp() <
+ frame_to_write->timestamp()) {
+ okay_to_write = false;
+ break;
+ }
+ }
+ if (okay_to_write) {
+ const bool wrote_frame = DoWriteFrame(frame_to_write);
+ delete frame_to_write;
+ if (!wrote_frame)
+ return false;
+ frames_to_erase.push_back(current_track_iterator);
+ } else {
+ break;
+ }
+ }
+ for (std::vector<std::list<Frame*>::iterator>::iterator iterator =
+ frames_to_erase.begin();
+ iterator != frames_to_erase.end(); ++iterator) {
+ stored_frames_[track_number].erase(*iterator);
+ }
+ return true;
+}
+
+bool Cluster::WriteClusterHeader() {
+ if (finalized_)
+ return false;
+
+ if (WriteID(writer_, libwebm::kMkvCluster))
+ return false;
+
+ // Save for later.
+ size_position_ = writer_->Position();
+
+ // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8
+ // bytes because we do not know how big our cluster will be.
+ if (SerializeInt(writer_, kEbmlUnknownValue, 8))
+ return false;
+
+ if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode(),
+ fixed_size_timecode_ ? 8 : 0)) {
+ return false;
+ }
+ AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode(),
+ fixed_size_timecode_ ? 8 : 0));
+ header_written_ = true;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// SeekHead Class
+
+SeekHead::SeekHead() : start_pos_(0ULL) {
+ for (int32_t i = 0; i < kSeekEntryCount; ++i) {
+ seek_entry_id_[i] = 0;
+ seek_entry_pos_[i] = 0;
+ }
+}
+
+SeekHead::~SeekHead() {}
+
+bool SeekHead::Finalize(IMkvWriter* writer) const {
+ if (writer->Seekable()) {
+ if (start_pos_ == -1)
+ return false;
+
+ uint64_t payload_size = 0;
+ uint64_t entry_size[kSeekEntryCount];
+
+ for (int32_t i = 0; i < kSeekEntryCount; ++i) {
+ if (seek_entry_id_[i] != 0) {
+ entry_size[i] = EbmlElementSize(
+ libwebm::kMkvSeekID, static_cast<uint64_t>(seek_entry_id_[i]));
+ entry_size[i] +=
+ EbmlElementSize(libwebm::kMkvSeekPosition, seek_entry_pos_[i]);
+
+ payload_size +=
+ EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) +
+ entry_size[i];
+ }
+ }
+
+ // No SeekHead elements
+ if (payload_size == 0)
+ return true;
+
+ const int64_t pos = writer->Position();
+ if (writer->Position(start_pos_))
+ return false;
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size))
+ return false;
+
+ for (int32_t i = 0; i < kSeekEntryCount; ++i) {
+ if (seek_entry_id_[i] != 0) {
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i]))
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvSeekID,
+ static_cast<uint64_t>(seek_entry_id_[i])))
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition,
+ seek_entry_pos_[i]))
+ return false;
+ }
+ }
+
+ const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize();
+ const uint64_t total_size =
+ EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) +
+ total_entry_size;
+ const int64_t size_left = total_size - (writer->Position() - start_pos_);
+
+ const uint64_t bytes_written = WriteVoidElement(writer, size_left);
+ if (!bytes_written)
+ return false;
+
+ if (writer->Position(pos))
+ return false;
+ }
+
+ return true;
+}
+
+bool SeekHead::Write(IMkvWriter* writer) {
+ const uint64_t entry_size = kSeekEntryCount * MaxEntrySize();
+ const uint64_t size =
+ EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size);
+
+ start_pos_ = writer->Position();
+
+ const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size);
+ if (!bytes_written)
+ return false;
+
+ return true;
+}
+
+bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) {
+ for (int32_t i = 0; i < kSeekEntryCount; ++i) {
+ if (seek_entry_id_[i] == 0) {
+ seek_entry_id_[i] = id;
+ seek_entry_pos_[i] = pos;
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t SeekHead::GetId(int index) const {
+ if (index < 0 || index >= kSeekEntryCount)
+ return UINT_MAX;
+ return seek_entry_id_[index];
+}
+
+uint64_t SeekHead::GetPosition(int index) const {
+ if (index < 0 || index >= kSeekEntryCount)
+ return ULLONG_MAX;
+ return seek_entry_pos_[index];
+}
+
+bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) {
+ if (index < 0 || index >= kSeekEntryCount)
+ return false;
+ seek_entry_id_[index] = id;
+ seek_entry_pos_[index] = position;
+ return true;
+}
+
+uint64_t SeekHead::MaxEntrySize() const {
+ const uint64_t max_entry_payload_size =
+ EbmlElementSize(libwebm::kMkvSeekID, UINT64_C(0xffffffff)) +
+ EbmlElementSize(libwebm::kMkvSeekPosition, UINT64_C(0xffffffffffffffff));
+ const uint64_t max_entry_size =
+ EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) +
+ max_entry_payload_size;
+
+ return max_entry_size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// SegmentInfo Class
+
+SegmentInfo::SegmentInfo()
+ : duration_(-1.0),
+ muxing_app_(NULL),
+ timecode_scale_(1000000ULL),
+ writing_app_(NULL),
+ date_utc_(LLONG_MIN),
+ duration_pos_(-1) {}
+
+SegmentInfo::~SegmentInfo() {
+ delete[] muxing_app_;
+ delete[] writing_app_;
+}
+
+bool SegmentInfo::Init() {
+ int32_t major;
+ int32_t minor;
+ int32_t build;
+ int32_t revision;
+ GetVersion(&major, &minor, &build, &revision);
+ char temp[256];
+#ifdef _MSC_VER
+ sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
+ minor, build, revision);
+#else
+ snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major,
+ minor, build, revision);
+#endif
+
+ const size_t app_len = strlen(temp) + 1;
+
+ delete[] muxing_app_;
+
+ muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT
+ if (!muxing_app_)
+ return false;
+
+#ifdef _MSC_VER
+ strcpy_s(muxing_app_, app_len, temp);
+#else
+ strcpy(muxing_app_, temp);
+#endif
+
+ set_writing_app(temp);
+ if (!writing_app_)
+ return false;
+ return true;
+}
+
+bool SegmentInfo::Finalize(IMkvWriter* writer) const {
+ if (!writer)
+ return false;
+
+ if (duration_ > 0.0) {
+ if (writer->Seekable()) {
+ if (duration_pos_ == -1)
+ return false;
+
+ const int64_t pos = writer->Position();
+
+ if (writer->Position(duration_pos_))
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
+ static_cast<float>(duration_)))
+ return false;
+
+ if (writer->Position(pos))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SegmentInfo::Write(IMkvWriter* writer) {
+ if (!writer || !muxing_app_ || !writing_app_)
+ return false;
+
+ uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale, timecode_scale_);
+ if (duration_ > 0.0)
+ size +=
+ EbmlElementSize(libwebm::kMkvDuration, static_cast<float>(duration_));
+ if (date_utc_ != LLONG_MIN)
+ size += EbmlDateElementSize(libwebm::kMkvDateUTC);
+ size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_);
+ size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_);
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size))
+ return false;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return false;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale, timecode_scale_))
+ return false;
+
+ if (duration_ > 0.0) {
+ // Save for later
+ duration_pos_ = writer->Position();
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvDuration,
+ static_cast<float>(duration_)))
+ return false;
+ }
+
+ if (date_utc_ != LLONG_MIN)
+ WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_);
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_))
+ return false;
+ if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_))
+ return false;
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(size))
+ return false;
+
+ return true;
+}
+
+void SegmentInfo::set_muxing_app(const char* app) {
+ if (app) {
+ const size_t length = strlen(app) + 1;
+ char* temp_str = new (std::nothrow) char[length]; // NOLINT
+ if (!temp_str)
+ return;
+
+#ifdef _MSC_VER
+ strcpy_s(temp_str, length, app);
+#else
+ strcpy(temp_str, app);
+#endif
+
+ delete[] muxing_app_;
+ muxing_app_ = temp_str;
+ }
+}
+
+void SegmentInfo::set_writing_app(const char* app) {
+ if (app) {
+ const size_t length = strlen(app) + 1;
+ char* temp_str = new (std::nothrow) char[length]; // NOLINT
+ if (!temp_str)
+ return;
+
+#ifdef _MSC_VER
+ strcpy_s(temp_str, length, app);
+#else
+ strcpy(temp_str, app);
+#endif
+
+ delete[] writing_app_;
+ writing_app_ = temp_str;
+ }
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Segment Class
+
+Segment::Segment()
+ : chunk_count_(0),
+ chunk_name_(NULL),
+ chunk_writer_cluster_(NULL),
+ chunk_writer_cues_(NULL),
+ chunk_writer_header_(NULL),
+ chunking_(false),
+ chunking_base_name_(NULL),
+ cluster_list_(NULL),
+ cluster_list_capacity_(0),
+ cluster_list_size_(0),
+ cues_position_(kAfterClusters),
+ cues_track_(0),
+ force_new_cluster_(false),
+ frames_(NULL),
+ frames_capacity_(0),
+ frames_size_(0),
+ has_video_(false),
+ header_written_(false),
+ last_block_duration_(0),
+ last_timestamp_(0),
+ max_cluster_duration_(kDefaultMaxClusterDuration),
+ max_cluster_size_(0),
+ mode_(kFile),
+ new_cuepoint_(false),
+ output_cues_(true),
+ accurate_cluster_duration_(false),
+ fixed_size_cluster_timecode_(false),
+ payload_pos_(0),
+ size_position_(0),
+ doc_type_version_(kDefaultDocTypeVersion),
+ doc_type_version_written_(0),
+ writer_cluster_(NULL),
+ writer_cues_(NULL),
+ writer_header_(NULL) {
+ const time_t curr_time = time(NULL);
+ seed_ = static_cast<unsigned int>(curr_time);
+#ifdef _WIN32
+ srand(seed_);
+#endif
+}
+
+Segment::~Segment() {
+ if (cluster_list_) {
+ for (int32_t i = 0; i < cluster_list_size_; ++i) {
+ Cluster* const cluster = cluster_list_[i];
+ delete cluster;
+ }
+ delete[] cluster_list_;
+ }
+
+ if (frames_) {
+ for (int32_t i = 0; i < frames_size_; ++i) {
+ Frame* const frame = frames_[i];
+ delete frame;
+ }
+ delete[] frames_;
+ }
+
+ delete[] chunk_name_;
+ delete[] chunking_base_name_;
+
+ if (chunk_writer_cluster_) {
+ chunk_writer_cluster_->Close();
+ delete chunk_writer_cluster_;
+ }
+ if (chunk_writer_cues_) {
+ chunk_writer_cues_->Close();
+ delete chunk_writer_cues_;
+ }
+ if (chunk_writer_header_) {
+ chunk_writer_header_->Close();
+ delete chunk_writer_header_;
+ }
+}
+
+void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index,
+ uint64_t* cues_size) {
+ CuePoint* const cue_point = cues_.GetCueByIndex(index);
+ if (cue_point == NULL)
+ return;
+ const uint64_t old_cue_point_size = cue_point->Size();
+ const uint64_t 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 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_t cue_point_size_diff = cue_point->Size() - old_cue_point_size;
+ const uint64_t cue_size_diff =
+ GetCodedUIntSize(*cues_size + cue_point_size_diff) -
+ GetCodedUIntSize(*cues_size);
+ *cues_size += cue_point_size_diff;
+ diff = cue_size_diff + cue_point_size_diff;
+ if (diff > 0) {
+ for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) {
+ MoveCuesBeforeClustersHelper(diff, i, cues_size);
+ }
+ }
+}
+
+void Segment::MoveCuesBeforeClusters() {
+ const uint64_t current_cue_size = cues_.Size();
+ uint64_t cue_size = 0;
+ for (int32_t i = 0; i < cues_.cue_entries_size(); ++i)
+ cue_size += cues_.GetCueByIndex(i)->Size();
+ for (int32_t 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
+ // of Cluster and Cues
+ int32_t cluster_index = 0;
+ int32_t cues_index = 0;
+ for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) {
+ if (seek_head_.GetId(i) == libwebm::kMkvCluster)
+ cluster_index = i;
+ if (seek_head_.GetId(i) == libwebm::kMkvCues)
+ cues_index = i;
+ }
+ seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues,
+ seek_head_.GetPosition(cluster_index));
+ seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster,
+ cues_.Size() + seek_head_.GetPosition(cues_index));
+}
+
+bool Segment::Init(IMkvWriter* ptr_writer) {
+ if (!ptr_writer) {
+ return false;
+ }
+ writer_cluster_ = ptr_writer;
+ writer_cues_ = ptr_writer;
+ writer_header_ = ptr_writer;
+ return segment_info_.Init();
+}
+
+bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
+ IMkvWriter* writer) {
+ if (!writer->Seekable() || chunking_)
+ return false;
+ const int64_t cluster_offset =
+ cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster);
+
+ // Copy the headers.
+ if (!ChunkedCopy(reader, writer, 0, cluster_offset))
+ return false;
+
+ // Recompute cue positions and seek entries.
+ MoveCuesBeforeClusters();
+
+ // Write cues and seek entries.
+ // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the
+ // second time with a different writer object. But the name Finalize() doesn't
+ // indicate something we want to call more than once. So consider renaming it
+ // to write() or some such.
+ if (!cues_.Write(writer) || !seek_head_.Finalize(writer))
+ return false;
+
+ // Copy the Clusters.
+ if (!ChunkedCopy(reader, writer, cluster_offset,
+ cluster_end_offset_ - cluster_offset))
+ return false;
+
+ // Update the Segment size in case the Cues size has changed.
+ const int64_t pos = writer->Position();
+ const int64_t segment_size = writer->Position() - payload_pos_;
+ if (writer->Position(size_position_) ||
+ WriteUIntSize(writer, segment_size, 8) || writer->Position(pos))
+ return false;
+ return true;
+}
+
+bool Segment::Finalize() {
+ if (WriteFramesAll() < 0)
+ return false;
+
+ if (cluster_list_size_ > 0) {
+ // Update last cluster's size
+ Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
+
+ // For the last frame of the last Cluster, we don't write it as a BlockGroup
+ // with Duration unless the frame itself has duration set explicitly.
+ if (!old_cluster || !old_cluster->Finalize(false, 0))
+ return false;
+ }
+
+ if (mode_ == kFile) {
+ if (chunking_ && chunk_writer_cluster_) {
+ chunk_writer_cluster_->Close();
+ chunk_count_++;
+ }
+
+ const double duration =
+ (static_cast<double>(last_timestamp_) + last_block_duration_) /
+ segment_info_.timecode_scale();
+ segment_info_.set_duration(duration);
+ if (!segment_info_.Finalize(writer_header_))
+ return false;
+
+ if (output_cues_)
+ if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset()))
+ return false;
+
+ if (chunking_) {
+ if (!chunk_writer_cues_)
+ return false;
+
+ char* name = NULL;
+ if (!UpdateChunkName("cues", &name))
+ return false;
+
+ const bool cues_open = chunk_writer_cues_->Open(name);
+ delete[] name;
+ if (!cues_open)
+ return false;
+ }
+
+ cluster_end_offset_ = writer_cluster_->Position();
+
+ // Write the seek headers and cues
+ if (output_cues_)
+ if (!cues_.Write(writer_cues_))
+ return false;
+
+ if (!seek_head_.Finalize(writer_header_))
+ return false;
+
+ if (writer_header_->Seekable()) {
+ if (size_position_ == -1)
+ return false;
+
+ const int64_t segment_size = MaxOffset();
+ if (segment_size < 1)
+ return false;
+
+ const int64_t 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;
+
+ if (WriteUIntSize(writer_header_, segment_size, 8))
+ return false;
+
+ if (writer_header_->Position(pos))
+ return false;
+ }
+
+ if (chunking_) {
+ // Do not close any writers until the segment size has been written,
+ // otherwise the size may be off.
+ if (!chunk_writer_cues_ || !chunk_writer_header_)
+ return false;
+
+ chunk_writer_cues_->Close();
+ chunk_writer_header_->Close();
+ }
+ }
+
+ return true;
+}
+
+Track* Segment::AddTrack(int32_t number) {
+ Track* const track = new (std::nothrow) Track(&seed_); // NOLINT
+
+ if (!track)
+ return NULL;
+
+ if (!tracks_.AddTrack(track, number)) {
+ delete track;
+ return NULL;
+ }
+
+ return track;
+}
+
+Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
+
+Tag* Segment::AddTag() { return tags_.AddTag(); }
+
+uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) {
+ VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
+ if (!track)
+ return 0;
+
+ track->set_type(Tracks::kVideo);
+ track->set_codec_id(Tracks::kVp8CodecId);
+ track->set_width(width);
+ track->set_height(height);
+
+ tracks_.AddTrack(track, number);
+ has_video_ = true;
+
+ return track->number();
+}
+
+bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) {
+ if (cluster_list_size_ < 1)
+ return false;
+
+ const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
+ if (!cluster)
+ return false;
+
+ CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT
+ if (!cue)
+ return false;
+
+ cue->set_time(timestamp / segment_info_.timecode_scale());
+ cue->set_block_number(cluster->blocks_added());
+ cue->set_cluster_pos(cluster->position_for_cues());
+ cue->set_track(track);
+ if (!cues_.AddCue(cue))
+ return false;
+
+ new_cuepoint_ = false;
+ return true;
+}
+
+uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels,
+ int32_t number) {
+ AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT
+ if (!track)
+ return 0;
+
+ track->set_type(Tracks::kAudio);
+ track->set_codec_id(Tracks::kVorbisCodecId);
+ track->set_sample_rate(sample_rate);
+ track->set_channels(channels);
+
+ tracks_.AddTrack(track, number);
+
+ return track->number();
+}
+
+bool Segment::AddFrame(const uint8_t* data, uint64_t length,
+ uint64_t track_number, uint64_t timestamp, bool is_key) {
+ if (!data)
+ return false;
+
+ Frame frame;
+ if (!frame.Init(data, length))
+ return false;
+ frame.set_track_number(track_number);
+ frame.set_timestamp(timestamp);
+ frame.set_is_key(is_key);
+ return AddGenericFrame(&frame);
+}
+
+bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length,
+ const uint8_t* additional,
+ uint64_t additional_length,
+ uint64_t add_id, uint64_t track_number,
+ uint64_t timestamp, bool is_key) {
+ if (!data || !additional)
+ 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(timestamp);
+ frame.set_is_key(is_key);
+ return AddGenericFrame(&frame);
+}
+
+bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
+ int64_t discard_padding,
+ uint64_t track_number,
+ uint64_t timestamp, bool is_key) {
+ if (!data)
+ return false;
+
+ 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);
+}
+
+bool Segment::AddMetadata(const uint8_t* data, uint64_t length,
+ uint64_t track_number, uint64_t timestamp_ns,
+ uint64_t duration_ns) {
+ if (!data)
+ return false;
+
+ Frame frame;
+ if (!frame.Init(data, length))
+ return false;
+ 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::AddGenericFrame(const Frame* frame) {
+ if (!frame)
+ return false;
+
+ if (!CheckHeaderInfo())
+ return false;
+
+ // Check for non-monotonically increasing timestamps.
+ 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(frame->track_number()) &&
+ !force_new_cluster_) {
+ Frame* const new_frame = new (std::nothrow) Frame();
+ if (!new_frame || !new_frame->CopyFrom(*frame))
+ return false;
+ return QueueFrame(new_frame);
+ }
+
+ if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
+ frame->is_key())) {
+ return false;
+ }
+
+ if (cluster_list_size_ < 1)
+ return false;
+
+ Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
+ if (!cluster)
+ return false;
+
+ // If the Frame is not a SimpleBlock, then set the reference_block_timestamp
+ // if it is not set already.
+ bool frame_created = false;
+ if (!frame->CanBeSimpleBlock() && !frame->is_key() &&
+ !frame->reference_block_timestamp_set()) {
+ Frame* const new_frame = new (std::nothrow) Frame();
+ if (!new_frame->CopyFrom(*frame))
+ return false;
+ new_frame->set_reference_block_timestamp(
+ last_track_timestamp_[frame->track_number() - 1]);
+ frame = new_frame;
+ frame_created = true;
+ }
+
+ if (!cluster->AddFrame(frame))
+ return false;
+
+ if (new_cuepoint_ && cues_track_ == frame->track_number()) {
+ if (!AddCuePoint(frame->timestamp(), cues_track_))
+ return false;
+ }
+
+ last_timestamp_ = frame->timestamp();
+ last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
+ last_block_duration_ = frame->duration();
+
+ if (frame_created)
+ delete frame;
+
+ return true;
+}
+
+void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; }
+
+void Segment::AccurateClusterDuration(bool accurate_cluster_duration) {
+ accurate_cluster_duration_ = accurate_cluster_duration;
+}
+
+void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) {
+ fixed_size_cluster_timecode_ = fixed_size_cluster_timecode;
+}
+
+bool Segment::SetChunking(bool chunking, const char* filename) {
+ if (chunk_count_ > 0)
+ return false;
+
+ if (chunking) {
+ if (!filename)
+ return false;
+
+ // Check if we are being set to what is already set.
+ if (chunking_ && !strcmp(filename, chunking_base_name_))
+ return true;
+
+ const size_t name_length = strlen(filename) + 1;
+ char* const temp = new (std::nothrow) char[name_length]; // NOLINT
+ if (!temp)
+ return false;
+
+#ifdef _MSC_VER
+ strcpy_s(temp, name_length, filename);
+#else
+ strcpy(temp, filename);
+#endif
+
+ delete[] chunking_base_name_;
+ chunking_base_name_ = temp;
+
+ if (!UpdateChunkName("chk", &chunk_name_))
+ return false;
+
+ if (!chunk_writer_cluster_) {
+ chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT
+ if (!chunk_writer_cluster_)
+ return false;
+ }
+
+ if (!chunk_writer_cues_) {
+ chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT
+ if (!chunk_writer_cues_)
+ return false;
+ }
+
+ if (!chunk_writer_header_) {
+ chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT
+ if (!chunk_writer_header_)
+ return false;
+ }
+
+ if (!chunk_writer_cluster_->Open(chunk_name_))
+ return false;
+
+ const size_t header_length = strlen(filename) + strlen(".hdr") + 1;
+ char* const header = new (std::nothrow) char[header_length]; // NOLINT
+ if (!header)
+ return false;
+
+#ifdef _MSC_VER
+ strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_);
+ strcat_s(header, header_length, ".hdr");
+#else
+ strcpy(header, chunking_base_name_);
+ strcat(header, ".hdr");
+#endif
+ if (!chunk_writer_header_->Open(header)) {
+ delete[] header;
+ return false;
+ }
+
+ writer_cluster_ = chunk_writer_cluster_;
+ writer_cues_ = chunk_writer_cues_;
+ writer_header_ = chunk_writer_header_;
+
+ delete[] header;
+ }
+
+ chunking_ = chunking;
+
+ return true;
+}
+
+bool Segment::CuesTrack(uint64_t track_number) {
+ const Track* const track = GetTrackByNumber(track_number);
+ if (!track)
+ return false;
+
+ cues_track_ = track_number;
+ return true;
+}
+
+void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; }
+
+Track* Segment::GetTrackByNumber(uint64_t track_number) const {
+ return tracks_.GetTrackByNumber(track_number);
+}
+
+bool Segment::WriteSegmentHeader() {
+ UpdateDocTypeVersion();
+
+ // TODO(fgalligan): Support more than one segment.
+ if (!WriteEbmlHeader(writer_header_, doc_type_version_))
+ return false;
+ doc_type_version_written_ = doc_type_version_;
+ ebml_header_size_ = static_cast<int32_t>(writer_header_->Position());
+
+ // Write "unknown" (-1) as segment size value. If mode is kFile, Segment
+ // will write over duration when the file is finalized.
+ if (WriteID(writer_header_, libwebm::kMkvSegment))
+ return false;
+
+ // Save for later.
+ size_position_ = writer_header_->Position();
+
+ // Write "unknown" (EBML coded -1) as segment size value. We need to write 8
+ // bytes because if we are going to overwrite the segment size later we do
+ // not know how big our segment will be.
+ if (SerializeInt(writer_header_, kEbmlUnknownValue, 8))
+ return false;
+
+ payload_pos_ = writer_header_->Position();
+
+ if (mode_ == kFile && writer_header_->Seekable()) {
+ // Set the duration > 0.0 so SegmentInfo will write out the duration. When
+ // the muxer is done writing we will set the correct duration and have
+ // SegmentInfo upadte it.
+ segment_info_.set_duration(1.0);
+
+ if (!seek_head_.Write(writer_header_))
+ return false;
+ }
+
+ if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset()))
+ return false;
+ if (!segment_info_.Write(writer_header_))
+ return false;
+
+ if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset()))
+ return false;
+ if (!tracks_.Write(writer_header_))
+ return false;
+
+ if (chapters_.Count() > 0) {
+ if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset()))
+ return false;
+ if (!chapters_.Write(writer_header_))
+ return false;
+ }
+
+ if (tags_.Count() > 0) {
+ if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset()))
+ return false;
+ if (!tags_.Write(writer_header_))
+ return false;
+ }
+
+ if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
+ if (!chunk_writer_header_)
+ return false;
+
+ chunk_writer_header_->Close();
+ }
+
+ header_written_ = true;
+
+ return true;
+}
+
+// Here we are testing whether to create a new cluster, given a frame
+// having time frame_timestamp_ns.
+//
+int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns,
+ bool is_key) const {
+ if (force_new_cluster_)
+ return 1;
+
+ // If no clusters have been created yet, then create a new cluster
+ // and write this frame immediately, in the new cluster. This path
+ // should only be followed once, the first time we attempt to write
+ // a frame.
+
+ if (cluster_list_size_ <= 0)
+ return 1;
+
+ // There exists at least one cluster. We must compare the frame to
+ // the last cluster, in order to determine whether the frame is
+ // written to the existing cluster, or that a new cluster should be
+ // created.
+
+ const uint64_t timecode_scale = segment_info_.timecode_scale();
+ const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
+
+ const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
+ const uint64_t last_cluster_timecode = last_cluster->timecode();
+
+ // For completeness we test for the case when the frame's timecode
+ // is less than the cluster's timecode. Although in principle that
+ // is allowed, this muxer doesn't actually write clusters like that,
+ // so this indicates a bug somewhere in our algorithm.
+
+ if (frame_timecode < last_cluster_timecode) // should never happen
+ return -1;
+
+ // If the frame has a timestamp significantly larger than the last
+ // cluster (in Matroska, cluster-relative timestamps are serialized
+ // using a 16-bit signed integer), then we cannot write this frame
+ // to that cluster, and so we must create a new cluster.
+
+ const int64_t delta_timecode = frame_timecode - last_cluster_timecode;
+
+ if (delta_timecode > kMaxBlockTimecode)
+ return 2;
+
+ // We decide to create a new cluster when we have a video keyframe.
+ // This will flush queued (audio) frames, and write the keyframe
+ // immediately, in the newly-created cluster.
+
+ if (is_key && tracks_.TrackIsVideo(track_number))
+ return 1;
+
+ // Create a new cluster if we have accumulated too many frames
+ // already, where "too many" is defined as "the total time of frames
+ // in the cluster exceeds a threshold".
+
+ const uint64_t delta_ns = delta_timecode * timecode_scale;
+
+ if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_)
+ return 1;
+
+ // This is similar to the case above, with the difference that a new
+ // cluster is created when the size of the current cluster exceeds a
+ // threshold.
+
+ const uint64_t cluster_size = last_cluster->payload_size();
+
+ if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_)
+ return 1;
+
+ // There's no need to create a new cluster, so emit this frame now.
+
+ return 0;
+}
+
+bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) {
+ const int32_t new_size = cluster_list_size_ + 1;
+
+ if (new_size > cluster_list_capacity_) {
+ // Add more clusters.
+ const int32_t new_capacity =
+ (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2;
+ Cluster** const clusters =
+ new (std::nothrow) Cluster*[new_capacity]; // NOLINT
+ if (!clusters)
+ return false;
+
+ for (int32_t i = 0; i < cluster_list_size_; ++i) {
+ clusters[i] = cluster_list_[i];
+ }
+
+ delete[] cluster_list_;
+
+ cluster_list_ = clusters;
+ cluster_list_capacity_ = new_capacity;
+ }
+
+ if (!WriteFramesLessThan(frame_timestamp_ns))
+ return false;
+
+ if (cluster_list_size_ > 0) {
+ // Update old cluster's size
+ Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];
+
+ if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns))
+ return false;
+ }
+
+ if (output_cues_)
+ new_cuepoint_ = true;
+
+ if (chunking_ && cluster_list_size_ > 0) {
+ chunk_writer_cluster_->Close();
+ chunk_count_++;
+
+ if (!UpdateChunkName("chk", &chunk_name_))
+ return false;
+ if (!chunk_writer_cluster_->Open(chunk_name_))
+ return false;
+ }
+
+ const uint64_t timecode_scale = segment_info_.timecode_scale();
+ const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale;
+
+ uint64_t cluster_timecode = frame_timecode;
+
+ if (frames_size_ > 0) {
+ const Frame* const f = frames_[0]; // earliest queued frame
+ const uint64_t ns = f->timestamp();
+ const uint64_t tc = ns / timecode_scale;
+
+ if (tc < cluster_timecode)
+ cluster_timecode = tc;
+ }
+
+ Cluster*& cluster = cluster_list_[cluster_list_size_];
+ const int64_t offset = MaxOffset();
+ cluster = new (std::nothrow)
+ Cluster(cluster_timecode, offset, segment_info_.timecode_scale(),
+ accurate_cluster_duration_, fixed_size_cluster_timecode_);
+ if (!cluster)
+ return false;
+
+ if (!cluster->Init(writer_cluster_))
+ return false;
+
+ cluster_list_size_ = new_size;
+ return true;
+}
+
+bool Segment::DoNewClusterProcessing(uint64_t track_number,
+ uint64_t frame_timestamp_ns, bool is_key) {
+ for (;;) {
+ // Based on the characteristics of the current frame and current
+ // cluster, decide whether to create a new cluster.
+ const int result = TestFrame(track_number, frame_timestamp_ns, is_key);
+ if (result < 0) // error
+ return false;
+
+ // Always set force_new_cluster_ to false after TestFrame.
+ force_new_cluster_ = false;
+
+ // A non-zero result means create a new cluster.
+ if (result > 0 && !MakeNewCluster(frame_timestamp_ns))
+ return false;
+
+ // Write queued (audio) frames.
+ const int frame_count = WriteFramesAll();
+ if (frame_count < 0) // error
+ return false;
+
+ // Write the current frame to the current cluster (if TestFrame
+ // returns 0) or to a newly created cluster (TestFrame returns 1).
+ if (result <= 1)
+ return true;
+
+ // TestFrame returned 2, which means there was a large time
+ // difference between the cluster and the frame itself. Do the
+ // test again, comparing the frame to the new cluster.
+ }
+}
+
+bool Segment::CheckHeaderInfo() {
+ if (!header_written_) {
+ if (!WriteSegmentHeader())
+ return false;
+
+ if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset()))
+ return false;
+
+ if (output_cues_ && cues_track_ == 0) {
+ // Check for a video track
+ for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) {
+ const Track* const track = tracks_.GetTrackByIndex(i);
+ if (!track)
+ return false;
+
+ if (tracks_.TrackIsVideo(track->number())) {
+ cues_track_ = track->number();
+ break;
+ }
+ }
+
+ // Set first track found
+ if (cues_track_ == 0) {
+ const Track* const track = tracks_.GetTrackByIndex(0);
+ if (!track)
+ return false;
+
+ cues_track_ = track->number();
+ }
+ }
+ }
+ return true;
+}
+
+void Segment::UpdateDocTypeVersion() {
+ for (uint32_t 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;
+
+ char ext_chk[64];
+#ifdef _MSC_VER
+ sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
+#else
+ snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext);
+#endif
+
+ const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1;
+ char* const str = new (std::nothrow) char[length]; // NOLINT
+ if (!str)
+ return false;
+
+#ifdef _MSC_VER
+ strcpy_s(str, length - strlen(ext_chk), chunking_base_name_);
+ strcat_s(str, length, ext_chk);
+#else
+ strcpy(str, chunking_base_name_);
+ strcat(str, ext_chk);
+#endif
+
+ delete[] * name;
+ *name = str;
+
+ return true;
+}
+
+int64_t Segment::MaxOffset() {
+ if (!writer_header_)
+ return -1;
+
+ int64_t offset = writer_header_->Position() - payload_pos_;
+
+ if (chunking_) {
+ for (int32_t i = 0; i < cluster_list_size_; ++i) {
+ Cluster* const cluster = cluster_list_[i];
+ offset += cluster->Size();
+ }
+
+ if (writer_cues_)
+ offset += writer_cues_->Position();
+ }
+
+ return offset;
+}
+
+bool Segment::QueueFrame(Frame* frame) {
+ const int32_t new_size = frames_size_ + 1;
+
+ if (new_size > frames_capacity_) {
+ // Add more frames.
+ const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2;
+
+ if (new_capacity < 1)
+ return false;
+
+ Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT
+ if (!frames)
+ return false;
+
+ for (int32_t i = 0; i < frames_size_; ++i) {
+ frames[i] = frames_[i];
+ }
+
+ delete[] frames_;
+ frames_ = frames;
+ frames_capacity_ = new_capacity;
+ }
+
+ frames_[frames_size_++] = frame;
+
+ return true;
+}
+
+int Segment::WriteFramesAll() {
+ if (frames_ == NULL)
+ return 0;
+
+ if (cluster_list_size_ < 1)
+ return -1;
+
+ Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
+
+ if (!cluster)
+ return -1;
+
+ for (int32_t i = 0; i < frames_size_; ++i) {
+ Frame*& frame = frames_[i];
+ // 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_))
+ return -1;
+ }
+
+ if (frame->timestamp() > last_timestamp_) {
+ last_timestamp_ = frame->timestamp();
+ last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
+ }
+
+ delete frame;
+ frame = NULL;
+ }
+
+ const int result = frames_size_;
+ frames_size_ = 0;
+
+ return result;
+}
+
+bool Segment::WriteFramesLessThan(uint64_t timestamp) {
+ // Check |cluster_list_size_| to see if this is the first cluster. If it is
+ // the first cluster the audio frames that are less than the first video
+ // timesatmp will be written in a later step.
+ if (frames_size_ > 0 && cluster_list_size_ > 0) {
+ if (!frames_)
+ return false;
+
+ Cluster* const cluster = cluster_list_[cluster_list_size_ - 1];
+ if (!cluster)
+ return false;
+
+ int32_t shift_left = 0;
+
+ // TODO(fgalligan): Change this to use the durations of frames instead of
+ // the next frame's start time if the duration is accurate.
+ for (int32_t i = 1; i < frames_size_; ++i) {
+ const Frame* const frame_curr = frames_[i];
+
+ if (frame_curr->timestamp() > timestamp)
+ break;
+
+ const Frame* const frame_prev = frames_[i - 1];
+ 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_prev->timestamp(), cues_track_))
+ return false;
+ }
+
+ ++shift_left;
+ 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;
+ }
+
+ if (shift_left > 0) {
+ if (shift_left >= frames_size_)
+ return false;
+
+ const int32_t new_frames_size = frames_size_ - shift_left;
+ for (int32_t i = 0; i < new_frames_size; ++i) {
+ frames_[i] = frames_[i + shift_left];
+ }
+
+ frames_size_ = new_frames_size;
+ }
+ }
+
+ return true;
+}
+
+} // namespace mkvmuxer
--- /dev/null
+++ b/third_party/libwebm/mkvmuxer/mkvmuxer.h
@@ -1,0 +1,1695 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef MKVMUXER_MKVMUXER_H_
+#define MKVMUXER_MKVMUXER_H_
+
+#include <stdint.h>
+
+#include <cstddef>
+#include <list>
+#include <map>
+
+#include "common/webmids.h"
+#include "mkvmuxer/mkvmuxertypes.h"
+
+// For a description of the WebM elements see
+// http://www.webmproject.org/code/specs/container/.
+
+namespace mkvparser {
+class IMkvReader;
+} // namespace mkvparser
+
+namespace mkvmuxer {
+
+class MkvWriter;
+class Segment;
+
+const uint64_t kMaxTrackNumber = 126;
+
+///////////////////////////////////////////////////////////////
+// Interface used by the mkvmuxer to write out the Mkv data.
+class IMkvWriter {
+ public:
+ // Writes out |len| bytes of |buf|. Returns 0 on success.
+ virtual int32_t Write(const void* buf, uint32_t len) = 0;
+
+ // Returns the offset of the output position from the beginning of the
+ // output.
+ virtual int64_t Position() const = 0;
+
+ // Set the current File position. Returns 0 on success.
+ virtual int32_t Position(int64_t position) = 0;
+
+ // Returns true if the writer is seekable.
+ virtual bool Seekable() const = 0;
+
+ // Element start notification. Called whenever an element identifier is about
+ // to be written to the stream. |element_id| is the element identifier, and
+ // |position| is the location in the WebM stream where the first octet of the
+ // element identifier will be written.
+ // Note: the |MkvId| enumeration in webmids.hpp defines element values.
+ virtual void ElementStartNotify(uint64_t element_id, int64_t position) = 0;
+
+ protected:
+ IMkvWriter();
+ virtual ~IMkvWriter();
+
+ private:
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(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_t 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
+bool ChunkedCopy(mkvparser::IMkvReader* source, IMkvWriter* dst, int64_t start,
+ int64_t size);
+
+///////////////////////////////////////////////////////////////
+// Class to hold data the will be written to a block.
+class Frame {
+ public:
+ 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_t* frame, uint64_t length);
+
+ // Copies |additional| data into |additional_|. Returns true on success.
+ bool AddAdditionalData(const uint8_t* additional, uint64_t length,
+ uint64_t 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_t add_id() const { return add_id_; }
+ const uint8_t* additional() const { return additional_; }
+ uint64_t additional_length() const { return additional_length_; }
+ void set_duration(uint64_t duration);
+ uint64_t duration() const { return duration_; }
+ bool duration_set() const { return duration_set_; }
+ const uint8_t* frame() const { return frame_; }
+ void set_is_key(bool key) { is_key_ = key; }
+ bool is_key() const { return is_key_; }
+ uint64_t length() const { return length_; }
+ void set_track_number(uint64_t track_number) { track_number_ = track_number; }
+ uint64_t track_number() const { return track_number_; }
+ void set_timestamp(uint64_t timestamp) { timestamp_ = timestamp; }
+ uint64_t timestamp() const { return timestamp_; }
+ void set_discard_padding(int64_t discard_padding) {
+ discard_padding_ = discard_padding;
+ }
+ int64_t discard_padding() const { return discard_padding_; }
+ void set_reference_block_timestamp(int64_t reference_block_timestamp);
+ int64_t 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.
+ uint64_t add_id_;
+
+ // Pointer to additional data. Owned by this class.
+ uint8_t* additional_;
+
+ // Length of the additional data.
+ uint64_t additional_length_;
+
+ // Duration of the frame in nanoseconds.
+ uint64_t duration_;
+
+ // Flag indicating that |duration_| has been set. Setting duration causes the
+ // frame to be written out as a Block with BlockDuration instead of as a
+ // SimpleBlock.
+ bool duration_set_;
+
+ // Pointer to the data. Owned by this class.
+ uint8_t* frame_;
+
+ // Flag telling if the data should set the key flag of a block.
+ bool is_key_;
+
+ // Length of the data.
+ uint64_t length_;
+
+ // Mkv track number the data is associated with.
+ uint64_t track_number_;
+
+ // Timestamp of the data in nanoseconds.
+ uint64_t timestamp_;
+
+ // Discard padding for the frame.
+ int64_t discard_padding_;
+
+ // Reference block timestamp.
+ int64_t reference_block_timestamp_;
+
+ // Flag indicating if |reference_block_timestamp_| has been set.
+ bool reference_block_timestamp_set_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Frame);
+};
+
+///////////////////////////////////////////////////////////////
+// Class to hold one cue point in a Cues element.
+class CuePoint {
+ public:
+ CuePoint();
+ ~CuePoint();
+
+ // Returns the size in bytes for the entire CuePoint element.
+ uint64_t Size() const;
+
+ // Output the CuePoint element to the writer. Returns true on success.
+ bool Write(IMkvWriter* writer) const;
+
+ void set_time(uint64_t time) { time_ = time; }
+ uint64_t time() const { return time_; }
+ void set_track(uint64_t track) { track_ = track; }
+ uint64_t track() const { return track_; }
+ void set_cluster_pos(uint64_t cluster_pos) { cluster_pos_ = cluster_pos; }
+ uint64_t cluster_pos() const { return cluster_pos_; }
+ void set_block_number(uint64_t block_number) { block_number_ = block_number; }
+ uint64_t block_number() const { return block_number_; }
+ void set_output_block_number(bool output_block_number) {
+ output_block_number_ = output_block_number;
+ }
+ bool output_block_number() const { return output_block_number_; }
+
+ private:
+ // Returns the size in bytes for the payload of the CuePoint element.
+ uint64_t PayloadSize() const;
+
+ // Absolute timecode according to the segment time base.
+ uint64_t time_;
+
+ // The Track element associated with the CuePoint.
+ uint64_t track_;
+
+ // The position of the Cluster containing the Block.
+ uint64_t cluster_pos_;
+
+ // Number of the Block within the Cluster, starting from 1.
+ uint64_t block_number_;
+
+ // If true the muxer will write out the block number for the cue if the
+ // block number is different than the default of 1. Default is set to true.
+ bool output_block_number_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(CuePoint);
+};
+
+///////////////////////////////////////////////////////////////
+// Cues element.
+class Cues {
+ public:
+ Cues();
+ ~Cues();
+
+ // Adds a cue point to the Cues element. Returns true on success.
+ bool AddCue(CuePoint* cue);
+
+ // Returns the cue point by index. Returns NULL if there is no cue point
+ // match.
+ CuePoint* GetCueByIndex(int32_t index) const;
+
+ // Returns the total size of the Cues element
+ uint64_t Size();
+
+ // Output the Cues element to the writer. Returns true on success.
+ bool Write(IMkvWriter* writer) const;
+
+ int32_t cue_entries_size() const { return cue_entries_size_; }
+ void set_output_block_number(bool output_block_number) {
+ output_block_number_ = output_block_number;
+ }
+ bool output_block_number() const { return output_block_number_; }
+
+ private:
+ // Number of allocated elements in |cue_entries_|.
+ int32_t cue_entries_capacity_;
+
+ // Number of CuePoints in |cue_entries_|.
+ int32_t cue_entries_size_;
+
+ // CuePoint list.
+ CuePoint** cue_entries_;
+
+ // If true the muxer will write out the block number for the cue if the
+ // block number is different than the default of 1. Default is set to true.
+ bool output_block_number_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cues);
+};
+
+///////////////////////////////////////////////////////////////
+// ContentEncAESSettings element
+class ContentEncAESSettings {
+ public:
+ enum { kCTR = 1 };
+
+ ContentEncAESSettings();
+ ~ContentEncAESSettings() {}
+
+ // Returns the size in bytes for the ContentEncAESSettings element.
+ uint64_t Size() const;
+
+ // Writes out the ContentEncAESSettings element to |writer|. Returns true on
+ // success.
+ bool Write(IMkvWriter* writer) const;
+
+ uint64_t cipher_mode() const { return cipher_mode_; }
+
+ private:
+ // Returns the size in bytes for the payload of the ContentEncAESSettings
+ // element.
+ uint64_t PayloadSize() const;
+
+ // Sub elements
+ uint64_t cipher_mode_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncAESSettings);
+};
+
+///////////////////////////////////////////////////////////////
+// ContentEncoding element
+// Elements used to describe if the track data has been encrypted or
+// compressed with zlib or header stripping.
+// Currently only whole frames can be encrypted with AES. This dictates that
+// ContentEncodingOrder will be 0, ContentEncodingScope will be 1,
+// ContentEncodingType will be 1, and ContentEncAlgo will be 5.
+class ContentEncoding {
+ public:
+ ContentEncoding();
+ ~ContentEncoding();
+
+ // Sets the content encryption id. Copies |length| bytes from |id| to
+ // |enc_key_id_|. Returns true on success.
+ bool SetEncryptionID(const uint8_t* id, uint64_t length);
+
+ // Returns the size in bytes for the ContentEncoding element.
+ uint64_t Size() const;
+
+ // Writes out the ContentEncoding element to |writer|. Returns true on
+ // success.
+ bool Write(IMkvWriter* writer) const;
+
+ uint64_t enc_algo() const { return enc_algo_; }
+ uint64_t encoding_order() const { return encoding_order_; }
+ uint64_t encoding_scope() const { return encoding_scope_; }
+ uint64_t encoding_type() const { return encoding_type_; }
+ ContentEncAESSettings* enc_aes_settings() { return &enc_aes_settings_; }
+
+ private:
+ // Returns the size in bytes for the encoding elements.
+ uint64_t EncodingSize(uint64_t compresion_size,
+ uint64_t encryption_size) const;
+
+ // Returns the size in bytes for the encryption elements.
+ uint64_t EncryptionSize() const;
+
+ // Track element names
+ uint64_t enc_algo_;
+ uint8_t* enc_key_id_;
+ uint64_t encoding_order_;
+ uint64_t encoding_scope_;
+ uint64_t encoding_type_;
+
+ // ContentEncAESSettings element.
+ ContentEncAESSettings enc_aes_settings_;
+
+ // Size of the ContentEncKeyID data in bytes.
+ uint64_t enc_key_id_length_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
+};
+
+///////////////////////////////////////////////////////////////
+// Colour element.
+struct PrimaryChromaticity {
+ PrimaryChromaticity(float x_val, float y_val) : x(x_val), y(y_val) {}
+ PrimaryChromaticity() : x(0), y(0) {}
+ ~PrimaryChromaticity() {}
+ uint64_t PrimaryChromaticityPayloadSize(libwebm::MkvId x_id,
+ libwebm::MkvId y_id) const;
+ bool Write(IMkvWriter* writer, libwebm::MkvId x_id,
+ libwebm::MkvId y_id) const;
+
+ float x;
+ float y;
+};
+
+class MasteringMetadata {
+ public:
+ static const float kValueNotPresent;
+
+ MasteringMetadata()
+ : luminance_max(kValueNotPresent),
+ luminance_min(kValueNotPresent),
+ r_(NULL),
+ g_(NULL),
+ b_(NULL),
+ white_point_(NULL) {}
+ ~MasteringMetadata() {
+ delete r_;
+ delete g_;
+ delete b_;
+ delete white_point_;
+ }
+
+ // Returns total size of the MasteringMetadata element.
+ uint64_t MasteringMetadataSize() const;
+ bool Write(IMkvWriter* writer) const;
+
+ // Copies non-null chromaticity.
+ bool SetChromaticity(const PrimaryChromaticity* r,
+ const PrimaryChromaticity* g,
+ const PrimaryChromaticity* b,
+ const PrimaryChromaticity* white_point);
+ const PrimaryChromaticity* r() const { return r_; }
+ const PrimaryChromaticity* g() const { return g_; }
+ const PrimaryChromaticity* b() const { return b_; }
+ const PrimaryChromaticity* white_point() const { return white_point_; }
+
+ float luminance_max;
+ float luminance_min;
+
+ private:
+ // Returns size of MasteringMetadata child elements.
+ uint64_t PayloadSize() const;
+
+ PrimaryChromaticity* r_;
+ PrimaryChromaticity* g_;
+ PrimaryChromaticity* b_;
+ PrimaryChromaticity* white_point_;
+};
+
+class Colour {
+ public:
+ static const uint64_t kValueNotPresent;
+ Colour()
+ : matrix_coefficients(kValueNotPresent),
+ bits_per_channel(kValueNotPresent),
+ chroma_subsampling_horz(kValueNotPresent),
+ chroma_subsampling_vert(kValueNotPresent),
+ cb_subsampling_horz(kValueNotPresent),
+ cb_subsampling_vert(kValueNotPresent),
+ chroma_siting_horz(kValueNotPresent),
+ chroma_siting_vert(kValueNotPresent),
+ range(kValueNotPresent),
+ transfer_characteristics(kValueNotPresent),
+ primaries(kValueNotPresent),
+ max_cll(kValueNotPresent),
+ max_fall(kValueNotPresent),
+ mastering_metadata_(NULL) {}
+ ~Colour() { delete mastering_metadata_; }
+
+ // Returns total size of the Colour element.
+ uint64_t ColourSize() const;
+ bool Write(IMkvWriter* writer) const;
+
+ // Deep copies |mastering_metadata|.
+ bool SetMasteringMetadata(const MasteringMetadata& mastering_metadata);
+
+ const MasteringMetadata* mastering_metadata() const {
+ return mastering_metadata_;
+ }
+
+ uint64_t matrix_coefficients;
+ uint64_t bits_per_channel;
+ uint64_t chroma_subsampling_horz;
+ uint64_t chroma_subsampling_vert;
+ uint64_t cb_subsampling_horz;
+ uint64_t cb_subsampling_vert;
+ uint64_t chroma_siting_horz;
+ uint64_t chroma_siting_vert;
+ uint64_t range;
+ uint64_t transfer_characteristics;
+ uint64_t primaries;
+ uint64_t max_cll;
+ uint64_t max_fall;
+
+ private:
+ // Returns size of Colour child elements.
+ uint64_t PayloadSize() const;
+
+ MasteringMetadata* mastering_metadata_;
+};
+
+///////////////////////////////////////////////////////////////
+// Track element.
+class Track {
+ public:
+ // The |seed| parameter is used to synthesize a UID for the track.
+ explicit Track(unsigned int* seed);
+ virtual ~Track();
+
+ // Adds a ContentEncoding element to the Track. Returns true on success.
+ virtual bool AddContentEncoding();
+
+ // Returns the ContentEncoding by index. Returns NULL if there is no
+ // ContentEncoding match.
+ ContentEncoding* GetContentEncodingByIndex(uint32_t index) const;
+
+ // Returns the size in bytes for the payload of the Track element.
+ virtual uint64_t PayloadSize() const;
+
+ // Returns the size in bytes of the Track element.
+ virtual uint64_t Size() const;
+
+ // Output the Track element to the writer. Returns true on success.
+ virtual bool Write(IMkvWriter* writer) const;
+
+ // Sets the CodecPrivate element of the Track element. Copies |length|
+ // bytes from |codec_private| to |codec_private_|. Returns true on success.
+ bool SetCodecPrivate(const uint8_t* codec_private, uint64_t length);
+
+ void set_codec_id(const char* codec_id);
+ const char* codec_id() const { return codec_id_; }
+ const uint8_t* codec_private() const { return codec_private_; }
+ void set_language(const char* language);
+ const char* language() const { return language_; }
+ void set_max_block_additional_id(uint64_t max_block_additional_id) {
+ max_block_additional_id_ = max_block_additional_id;
+ }
+ uint64_t max_block_additional_id() const { return max_block_additional_id_; }
+ void set_name(const char* name);
+ const char* name() const { return name_; }
+ void set_number(uint64_t number) { number_ = number; }
+ uint64_t number() const { return number_; }
+ void set_type(uint64_t type) { type_ = type; }
+ uint64_t type() const { return type_; }
+ void set_uid(uint64_t uid) { uid_ = uid; }
+ uint64_t uid() const { return uid_; }
+ void set_codec_delay(uint64_t codec_delay) { codec_delay_ = codec_delay; }
+ uint64_t codec_delay() const { return codec_delay_; }
+ void set_seek_pre_roll(uint64_t seek_pre_roll) {
+ seek_pre_roll_ = seek_pre_roll;
+ }
+ uint64_t seek_pre_roll() const { return seek_pre_roll_; }
+ void set_default_duration(uint64_t default_duration) {
+ default_duration_ = default_duration;
+ }
+ uint64_t default_duration() const { return default_duration_; }
+
+ uint64_t codec_private_length() const { return codec_private_length_; }
+ uint32_t content_encoding_entries_size() const {
+ return content_encoding_entries_size_;
+ }
+
+ private:
+ // Track element names.
+ char* codec_id_;
+ uint8_t* codec_private_;
+ char* language_;
+ uint64_t max_block_additional_id_;
+ char* name_;
+ uint64_t number_;
+ uint64_t type_;
+ uint64_t uid_;
+ uint64_t codec_delay_;
+ uint64_t seek_pre_roll_;
+ uint64_t default_duration_;
+
+ // Size of the CodecPrivate data in bytes.
+ uint64_t codec_private_length_;
+
+ // ContentEncoding element list.
+ ContentEncoding** content_encoding_entries_;
+
+ // Number of ContentEncoding elements added.
+ uint32_t content_encoding_entries_size_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Track);
+};
+
+///////////////////////////////////////////////////////////////
+// Track that has video specific elements.
+class VideoTrack : public Track {
+ public:
+ // Supported modes for stereo 3D.
+ enum StereoMode {
+ kMono = 0,
+ kSideBySideLeftIsFirst = 1,
+ kTopBottomRightIsFirst = 2,
+ kTopBottomLeftIsFirst = 3,
+ kSideBySideRightIsFirst = 11
+ };
+
+ enum AlphaMode { kNoAlpha = 0, kAlpha = 1 };
+
+ // The |seed| parameter is used to synthesize a UID for the track.
+ explicit VideoTrack(unsigned int* seed);
+ virtual ~VideoTrack();
+
+ // Returns the size in bytes for the payload of the Track element plus the
+ // video specific elements.
+ virtual uint64_t PayloadSize() const;
+
+ // Output the VideoTrack element to the writer. Returns true on success.
+ virtual bool Write(IMkvWriter* writer) const;
+
+ // Sets the video's stereo mode. Returns true on success.
+ bool SetStereoMode(uint64_t stereo_mode);
+
+ // Sets the video's alpha mode. Returns true on success.
+ bool SetAlphaMode(uint64_t alpha_mode);
+
+ void set_display_height(uint64_t height) { display_height_ = height; }
+ uint64_t display_height() const { return display_height_; }
+ void set_display_width(uint64_t width) { display_width_ = width; }
+ uint64_t display_width() const { return display_width_; }
+
+ void set_crop_left(uint64_t crop_left) { crop_left_ = crop_left; }
+ uint64_t crop_left() const { return crop_left_; }
+ void set_crop_right(uint64_t crop_right) { crop_right_ = crop_right; }
+ uint64_t crop_right() const { return crop_right_; }
+ void set_crop_top(uint64_t crop_top) { crop_top_ = crop_top; }
+ uint64_t crop_top() const { return crop_top_; }
+ void set_crop_bottom(uint64_t crop_bottom) { crop_bottom_ = crop_bottom; }
+ uint64_t 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_t height) { height_ = height; }
+ uint64_t height() const { return height_; }
+ uint64_t stereo_mode() { return stereo_mode_; }
+ uint64_t alpha_mode() { return alpha_mode_; }
+ void set_width(uint64_t width) { width_ = width; }
+ uint64_t width() const { return width_; }
+
+ Colour* colour() { return colour_; }
+
+ // Deep copies |colour|.
+ bool SetColour(const Colour& colour);
+
+ private:
+ // Returns the size in bytes of the Video element.
+ uint64_t VideoPayloadSize() const;
+
+ // Video track element names.
+ uint64_t display_height_;
+ uint64_t display_width_;
+ uint64_t crop_left_;
+ uint64_t crop_right_;
+ uint64_t crop_top_;
+ uint64_t crop_bottom_;
+ double frame_rate_;
+ uint64_t height_;
+ uint64_t stereo_mode_;
+ uint64_t alpha_mode_;
+ uint64_t width_;
+
+ Colour* colour_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack);
+};
+
+///////////////////////////////////////////////////////////////
+// Track that has audio specific elements.
+class AudioTrack : public Track {
+ public:
+ // The |seed| parameter is used to synthesize a UID for the track.
+ explicit AudioTrack(unsigned int* seed);
+ virtual ~AudioTrack();
+
+ // Returns the size in bytes for the payload of the Track element plus the
+ // audio specific elements.
+ virtual uint64_t PayloadSize() const;
+
+ // Output the AudioTrack element to the writer. Returns true on success.
+ virtual bool Write(IMkvWriter* writer) const;
+
+ void set_bit_depth(uint64_t bit_depth) { bit_depth_ = bit_depth; }
+ uint64_t bit_depth() const { return bit_depth_; }
+ void set_channels(uint64_t channels) { channels_ = channels; }
+ uint64_t channels() const { return channels_; }
+ void set_sample_rate(double sample_rate) { sample_rate_ = sample_rate; }
+ double sample_rate() const { return sample_rate_; }
+
+ private:
+ // Audio track element names.
+ uint64_t bit_depth_;
+ uint64_t channels_;
+ double sample_rate_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(AudioTrack);
+};
+
+///////////////////////////////////////////////////////////////
+// Tracks element
+class Tracks {
+ public:
+ // Audio and video type defined by the Matroska specs.
+ enum { kVideo = 0x1, kAudio = 0x2 };
+
+ static const char kOpusCodecId[];
+ static const char kVorbisCodecId[];
+ static const char kVp8CodecId[];
+ static const char kVp9CodecId[];
+ static const char kVp10CodecId[];
+
+ Tracks();
+ ~Tracks();
+
+ // Adds a Track element to the Tracks object. |track| will be owned and
+ // deleted by the Tracks object. Returns true on success. |number| is the
+ // number to use for the track. |number| must be >= 0. If |number| == 0
+ // then the muxer will decide on the track number.
+ bool AddTrack(Track* track, int32_t number);
+
+ // Returns the track by index. Returns NULL if there is no track match.
+ const Track* GetTrackByIndex(uint32_t idx) const;
+
+ // Search the Tracks and return the track that matches |tn|. Returns NULL
+ // if there is no track match.
+ Track* GetTrackByNumber(uint64_t track_number) const;
+
+ // Returns true if the track number is an audio track.
+ bool TrackIsAudio(uint64_t track_number) const;
+
+ // Returns true if the track number is a video track.
+ bool TrackIsVideo(uint64_t track_number) const;
+
+ // Output the Tracks element to the writer. Returns true on success.
+ bool Write(IMkvWriter* writer) const;
+
+ uint32_t track_entries_size() const { return track_entries_size_; }
+
+ private:
+ // Track element list.
+ Track** track_entries_;
+
+ // Number of Track elements added.
+ uint32_t track_entries_size_;
+
+ // Whether or not Tracks element has already been written via IMkvWriter.
+ mutable bool wrote_tracks_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tracks);
+};
+
+///////////////////////////////////////////////////////////////
+// Chapter element
+//
+class Chapter {
+ public:
+ // Set the identifier for this chapter. (This corresponds to the
+ // Cue Identifier line in WebVTT.)
+ // TODO(matthewjheaney): the actual serialization of this item in
+ // MKV is pending.
+ bool set_id(const char* id);
+
+ // Converts the nanosecond start and stop times of this chapter to
+ // their corresponding timecode values, and stores them that way.
+ void set_time(const Segment& segment, uint64_t start_time_ns,
+ uint64_t end_time_ns);
+
+ // Sets the uid for this chapter. Primarily used to enable
+ // deterministic output from the muxer.
+ void set_uid(const uint64_t uid) { uid_ = uid; }
+
+ // Add a title string to this chapter, per the semantics described
+ // here:
+ // http://www.matroska.org/technical/specs/index.html
+ //
+ // The title ("chapter string") is a UTF-8 string.
+ //
+ // The language has ISO 639-2 representation, described here:
+ // http://www.loc.gov/standards/iso639-2/englangn.html
+ // http://www.loc.gov/standards/iso639-2/php/English_list.php
+ // If you specify NULL as the language value, this implies
+ // English ("eng").
+ //
+ // The country value corresponds to the codes listed here:
+ // http://www.iana.org/domains/root/db/
+ //
+ // The function returns false if the string could not be allocated.
+ bool add_string(const char* title, const char* language, const char* country);
+
+ private:
+ friend class Chapters;
+
+ // For storage of chapter titles that differ by language.
+ class Display {
+ public:
+ // Establish representation invariant for new Display object.
+ void Init();
+
+ // Reclaim resources, in anticipation of destruction.
+ void Clear();
+
+ // Copies the title to the |title_| member. Returns false on
+ // error.
+ bool set_title(const char* title);
+
+ // Copies the language to the |language_| member. Returns false
+ // on error.
+ bool set_language(const char* language);
+
+ // Copies the country to the |country_| member. Returns false on
+ // error.
+ bool set_country(const char* country);
+
+ // If |writer| is non-NULL, serialize the Display sub-element of
+ // the Atom into the stream. Returns the Display element size on
+ // success, 0 if error.
+ uint64_t WriteDisplay(IMkvWriter* writer) const;
+
+ private:
+ char* title_;
+ char* language_;
+ char* country_;
+ };
+
+ Chapter();
+ ~Chapter();
+
+ // Establish the representation invariant for a newly-created
+ // Chapter object. The |seed| parameter is used to create the UID
+ // for this chapter atom.
+ void Init(unsigned int* seed);
+
+ // Copies this Chapter object to a different one. This is used when
+ // expanding a plain array of Chapter objects (see Chapters).
+ void ShallowCopy(Chapter* dst) const;
+
+ // Reclaim resources used by this Chapter object, pending its
+ // destruction.
+ void Clear();
+
+ // If there is no storage remaining on the |displays_| array for a
+ // new display object, creates a new, longer array and copies the
+ // existing Display objects to the new array. Returns false if the
+ // array cannot be expanded.
+ bool ExpandDisplaysArray();
+
+ // If |writer| is non-NULL, serialize the Atom sub-element into the
+ // stream. Returns the total size of the element on success, 0 if
+ // error.
+ uint64_t WriteAtom(IMkvWriter* writer) const;
+
+ // The string identifier for this chapter (corresponds to WebVTT cue
+ // identifier).
+ char* id_;
+
+ // Start timecode of the chapter.
+ uint64_t start_timecode_;
+
+ // Stop timecode of the chapter.
+ uint64_t end_timecode_;
+
+ // The binary identifier for this chapter.
+ uint64_t uid_;
+
+ // The Atom element can contain multiple Display sub-elements, as
+ // the same logical title can be rendered in different languages.
+ Display* displays_;
+
+ // The physical length (total size) of the |displays_| array.
+ int displays_size_;
+
+ // The logical length (number of active elements) on the |displays_|
+ // array.
+ int displays_count_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapter);
+};
+
+///////////////////////////////////////////////////////////////
+// Chapters element
+//
+class Chapters {
+ public:
+ Chapters();
+ ~Chapters();
+
+ Chapter* AddChapter(unsigned int* seed);
+
+ // Returns the number of chapters that have been added.
+ int Count() const;
+
+ // Output the Chapters element to the writer. Returns true on success.
+ bool Write(IMkvWriter* writer) const;
+
+ private:
+ // Expands the chapters_ array if there is not enough space to contain
+ // another chapter object. Returns true on success.
+ bool ExpandChaptersArray();
+
+ // If |writer| is non-NULL, serialize the Edition sub-element of the
+ // Chapters element into the stream. Returns the Edition element
+ // size on success, 0 if error.
+ uint64_t WriteEdition(IMkvWriter* writer) const;
+
+ // Total length of the chapters_ array.
+ int chapters_size_;
+
+ // Number of active chapters on the chapters_ array.
+ int chapters_count_;
+
+ // Array for storage of chapter objects.
+ Chapter* chapters_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(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_t 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_t 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:
+ // |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. |timecode_scale| is the timecode scale of the segment.
+ Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale,
+ bool write_last_frame_with_duration = false,
+ bool fixed_size_timecode = false);
+ ~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:
+ // 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_t* data, uint64_t length, uint64_t track_number,
+ uint64_t 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:
+ // 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. The range of allowed values is [1, 126].
+ // abs_timecode: Absolute (not relative to cluster) timestamp of the
+ // frame, expressed in timecode units.
+ // is_key: Flag telling whether or not this frame is a key frame.
+ bool AddFrameWithAdditional(const uint8_t* data, uint64_t length,
+ const uint8_t* additional,
+ uint64_t additional_length, uint64_t add_id,
+ uint64_t track_number, uint64_t abs_timecode,
+ bool is_key);
+
+ // Adds a frame to be output in the file. The frame is written out through
+ // |writer_| if successful. Returns true on success.
+ // Inputs:
+ // 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
+ // functions. The range of allowed values is [1, 126].
+ // abs_timecode: Absolute (not relative to cluster) timestamp of the
+ // frame, expressed in timecode units.
+ // is_key: Flag telling whether or not this frame is a key frame.
+ bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
+ int64_t discard_padding,
+ uint64_t track_number, uint64_t abs_timecode,
+ bool is_key);
+
+ // Writes a frame of metadata to the output medium; returns true on
+ // success.
+ // 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. The range of allowed values is [1, 126].
+ // timecode: Absolute (not relative to cluster) timestamp of the
+ // metadata frame, expressed in timecode units.
+ // duration: Duration of metadata frame, in timecode units.
+ //
+ // 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_t* data, uint64_t length, uint64_t track_number,
+ uint64_t timecode, uint64_t duration);
+
+ // Increments the size of the cluster's data in bytes.
+ void AddPayloadSize(uint64_t size);
+
+ // Closes the cluster so no more data can be written to it. Will update the
+ // cluster's size if |writer_| is seekable. Returns true on success. This
+ // variant of Finalize() fails when |write_last_frame_with_duration_| is set
+ // to true.
+ bool Finalize();
+
+ // Closes the cluster so no more data can be written to it. Will update the
+ // cluster's size if |writer_| is seekable. Returns true on success.
+ // Inputs:
+ // set_last_frame_duration: Boolean indicating whether or not the duration
+ // of the last frame should be set. If set to
+ // false, the |duration| value is ignored and
+ // |write_last_frame_with_duration_| will not be
+ // honored.
+ // duration: Duration of the Cluster in timecode scale.
+ bool Finalize(bool set_last_frame_duration, uint64_t duration);
+
+ // Returns the size in bytes for the entire Cluster element.
+ uint64_t Size() const;
+
+ // Given |abs_timecode|, calculates timecode relative to most recent timecode.
+ // Returns -1 on failure, or a relative timecode.
+ int64_t GetRelativeTimecode(int64_t abs_timecode) const;
+
+ int64_t size_position() const { return size_position_; }
+ int32_t blocks_added() const { return blocks_added_; }
+ uint64_t payload_size() const { return payload_size_; }
+ int64_t position_for_cues() const { return position_for_cues_; }
+ uint64_t timecode() const { return timecode_; }
+ uint64_t timecode_scale() const { return timecode_scale_; }
+ void set_write_last_frame_with_duration(bool write_last_frame_with_duration) {
+ write_last_frame_with_duration_ = write_last_frame_with_duration;
+ }
+ bool write_last_frame_with_duration() const {
+ return write_last_frame_with_duration_;
+ }
+
+ private:
+ // Iterator type for the |stored_frames_| map.
+ typedef std::map<uint64_t, std::list<Frame*> >::iterator FrameMapIterator;
+
+ // Utility method that confirms that blocks can still be added, and that the
+ // cluster header has been written. Used by |DoWriteFrame*|. Returns true
+ // when successful.
+ bool PreWriteBlock();
+
+ // Utility method used by the |DoWriteFrame*| methods that handles the book
+ // keeping required after each block is written.
+ void PostWriteBlock(uint64_t element_size);
+
+ // Does some verification and calls WriteFrame.
+ bool DoWriteFrame(const Frame* const frame);
+
+ // Either holds back the given frame, or writes it out depending on whether or
+ // not |write_last_frame_with_duration_| is set.
+ bool QueueOrWriteFrame(const Frame* const frame);
+
+ // Outputs the Cluster header to |writer_|. Returns true on success.
+ bool WriteClusterHeader();
+
+ // Number of blocks added to the cluster.
+ int32_t blocks_added_;
+
+ // Flag telling if the cluster has been closed.
+ bool finalized_;
+
+ // Flag indicating whether the cluster's timecode will always be written out
+ // using 8 bytes.
+ bool fixed_size_timecode_;
+
+ // Flag telling if the cluster's header has been written.
+ bool header_written_;
+
+ // The size of the cluster elements in bytes.
+ uint64_t payload_size_;
+
+ // The file position used for cue points.
+ const int64_t position_for_cues_;
+
+ // The file position of the cluster's size element.
+ int64_t size_position_;
+
+ // The absolute timecode of the cluster.
+ const uint64_t timecode_;
+
+ // The timecode scale of the Segment containing the cluster.
+ const uint64_t timecode_scale_;
+
+ // Flag indicating whether the last frame of the cluster should be written as
+ // a Block with Duration. If set to true, then it will result in holding back
+ // of frames and the parameterized version of Finalize() must be called to
+ // finish writing the Cluster.
+ bool write_last_frame_with_duration_;
+
+ // Map used to hold back frames, if required. Track number is the key.
+ std::map<uint64_t, std::list<Frame*> > stored_frames_;
+
+ // Map from track number to the timestamp of the last block written for that
+ // track.
+ std::map<uint64_t, uint64_t> last_block_timestamp_;
+
+ // Pointer to the writer object. Not owned by this class.
+ IMkvWriter* writer_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cluster);
+};
+
+///////////////////////////////////////////////////////////////
+// SeekHead element
+class SeekHead {
+ public:
+ SeekHead();
+ ~SeekHead();
+
+ // TODO(fgalligan): Change this to reserve a certain size. Then check how
+ // big the seek entry to be added is as not every seek entry will be the
+ // maximum size it could be.
+ // Adds a seek entry to be written out when the element is finalized. |id|
+ // must be the coded mkv element id. |pos| is the file position of the
+ // element. Returns true on success.
+ bool AddSeekEntry(uint32_t id, uint64_t pos);
+
+ // Writes out SeekHead and SeekEntry elements. Returns true on success.
+ bool Finalize(IMkvWriter* writer) const;
+
+ // Returns the id of the Seek Entry at the given index. Returns -1 if index is
+ // out of range.
+ uint32_t GetId(int index) const;
+
+ // Returns the position of the Seek Entry at the given index. Returns -1 if
+ // index is out of range.
+ uint64_t GetPosition(int index) const;
+
+ // Sets the Seek Entry id and position at given index.
+ // Returns true on success.
+ bool SetSeekEntry(int index, uint32_t id, uint64_t position);
+
+ // Reserves space by writing out a Void element which will be updated with
+ // a SeekHead element later. Returns true on success.
+ bool Write(IMkvWriter* writer);
+
+ // We are going to put a cap on the number of Seek Entries.
+ const static int32_t kSeekEntryCount = 5;
+
+ private:
+ // Returns the maximum size in bytes of one seek entry.
+ uint64_t MaxEntrySize() const;
+
+ // Seek entry id element list.
+ uint32_t seek_entry_id_[kSeekEntryCount];
+
+ // Seek entry pos element list.
+ uint64_t seek_entry_pos_[kSeekEntryCount];
+
+ // The file position of SeekHead element.
+ int64_t start_pos_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SeekHead);
+};
+
+///////////////////////////////////////////////////////////////
+// Segment Information element
+class SegmentInfo {
+ public:
+ SegmentInfo();
+ ~SegmentInfo();
+
+ // Will update the duration if |duration_| is > 0.0. Returns true on success.
+ bool Finalize(IMkvWriter* writer) const;
+
+ // Sets |muxing_app_| and |writing_app_|.
+ bool Init();
+
+ // Output the Segment Information element to the writer. Returns true on
+ // success.
+ bool Write(IMkvWriter* writer);
+
+ void set_duration(double duration) { duration_ = duration; }
+ double duration() const { return duration_; }
+ void set_muxing_app(const char* app);
+ const char* muxing_app() const { return muxing_app_; }
+ void set_timecode_scale(uint64_t scale) { timecode_scale_ = scale; }
+ uint64_t timecode_scale() const { return timecode_scale_; }
+ void set_writing_app(const char* app);
+ const char* writing_app() const { return writing_app_; }
+ void set_date_utc(int64_t date_utc) { date_utc_ = date_utc; }
+ int64_t date_utc() const { return date_utc_; }
+
+ private:
+ // Segment Information element names.
+ // Initially set to -1 to signify that a duration has not been set and should
+ // not be written out.
+ double duration_;
+ // Set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
+ char* muxing_app_;
+ uint64_t timecode_scale_;
+ // Initially set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
+ char* writing_app_;
+ // LLONG_MIN when DateUTC is not set.
+ int64_t date_utc_;
+
+ // The file position of the duration element.
+ int64_t duration_pos_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SegmentInfo);
+};
+
+///////////////////////////////////////////////////////////////
+// This class represents the main segment in a WebM file. Currently only
+// supports one Segment element.
+//
+// Notes:
+// |Init| must be called before any other method in this class.
+class Segment {
+ public:
+ enum Mode { kLive = 0x1, kFile = 0x2 };
+
+ enum CuesPosition {
+ kAfterClusters = 0x0, // Position Cues after Clusters - Default
+ kBeforeClusters = 0x1 // Position Cues before Clusters
+ };
+
+ const static uint32_t kDefaultDocTypeVersion = 2;
+ const static uint64_t kDefaultMaxClusterDuration = 30000000000ULL;
+
+ Segment();
+ ~Segment();
+
+ // Initializes |SegmentInfo| and returns result. Always returns false when
+ // |ptr_writer| is NULL.
+ bool Init(IMkvWriter* ptr_writer);
+
+ // Adds a generic track to the segment. Returns the newly-allocated
+ // track object (which is owned by the segment) on success, NULL on
+ // error. |number| is the number to use for the track. |number|
+ // must be >= 0. If |number| == 0 then the muxer will decide on the
+ // track number.
+ Track* AddTrack(int32_t number);
+
+ // Adds a Vorbis audio track to the segment. Returns the number of the track
+ // on success, 0 on error. |number| is the number to use for the audio track.
+ // |number| must be >= 0. If |number| == 0 then the muxer will decide on
+ // the track number.
+ uint64_t AddAudioTrack(int32_t sample_rate, int32_t channels, int32_t number);
+
+ // Adds an empty chapter to the chapters of this segment. Returns
+ // non-NULL on success. After adding the chapter, the caller should
+ // 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
+ // BlockNumber for the CuePoint. Returns true on success.
+ bool AddCuePoint(uint64_t timestamp, uint64_t track);
+
+ // Adds a frame to be output in the file. Returns true on success.
+ // 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.
+ // 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_t* data, uint64_t length, uint64_t track_number,
+ uint64_t timestamp_ns, bool is_key);
+
+ // Writes a frame of metadata to the output medium; returns true on
+ // success.
+ // 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.
+ // timecode: Absolute timestamp of the metadata frame, expressed
+ // in nanosecond units.
+ // duration: Duration of metadata frame, in nanosecond units.
+ //
+ // 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_t* data, uint64_t length, uint64_t track_number,
+ uint64_t timestamp_ns, uint64_t duration_ns);
+
+ // Writes a frame with additional data to the output medium; returns true on
+ // success.
+ // Inputs:
+ // data: Pointer to the data.
+ // length: Length of the data.
+ // additional: Pointer to additional data.
+ // additional_length: Length of additional data.
+ // add_id: Additional ID which identifies the type of additional data.
+ // track_number: Track to add the data to. Value returned by Add track
+ // functions.
+ // timestamp: Absolute timestamp of the frame, expressed in nanosecond
+ // units.
+ // is_key: Flag telling whether or not this frame is a key frame.
+ bool AddFrameWithAdditional(const uint8_t* data, uint64_t length,
+ const uint8_t* additional,
+ uint64_t additional_length, uint64_t add_id,
+ uint64_t track_number, uint64_t timestamp,
+ bool is_key);
+
+ // Writes a frame with DiscardPadding to the output medium; returns true on
+ // success.
+ // Inputs:
+ // 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
+ // functions.
+ // timestamp: Absolute timestamp of the frame, expressed in nanosecond
+ // units.
+ // is_key: Flag telling whether or not this frame is a key frame.
+ bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
+ int64_t discard_padding,
+ uint64_t track_number, uint64_t timestamp,
+ bool is_key);
+
+ // Writes a Frame to the output medium. Chooses the correct way of writing
+ // the frame (Block vs SimpleBlock) based on the parameters passed.
+ // Inputs:
+ // frame: frame object
+ bool AddGenericFrame(const Frame* frame);
+
+ // Adds a VP8 video track to the segment. Returns the number of the track on
+ // success, 0 on error. |number| is the number to use for the video track.
+ // |number| must be >= 0. If |number| == 0 then the muxer will decide on
+ // the track number.
+ uint64_t AddVideoTrack(int32_t width, int32_t height, int32_t number);
+
+ // This function must be called after Finalize() if you need a copy of the
+ // output with Cues written before the Clusters. It will return false if the
+ // writer is not seekable of if chunking is set to true.
+ // Input parameters:
+ // reader - an IMkvReader object created with the same underlying file of the
+ // current writer object. Make sure to close the existing writer
+ // object before creating this so that all the data is properly
+ // flushed and available for reading.
+ // writer - an IMkvWriter object pointing to a *different* file than the one
+ // pointed by the current writer object. This file will contain the
+ // Cues element before the Clusters.
+ bool CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
+ IMkvWriter* writer);
+
+ // Sets which track to use for the Cues element. Must have added the track
+ // before calling this function. Returns true on success. |track_number| is
+ // returned by the Add track functions.
+ bool CuesTrack(uint64_t track_number);
+
+ // This will force the muxer to create a new Cluster when the next frame is
+ // added.
+ void ForceNewClusterOnNextFrame();
+
+ // Writes out any frames that have not been written out. Finalizes the last
+ // cluster. May update the size and duration of the segment. May output the
+ // Cues element. May finalize the SeekHead element. Returns true on success.
+ bool Finalize();
+
+ // Returns the Cues object.
+ Cues* GetCues() { return &cues_; }
+
+ // Returns the Segment Information object.
+ const SegmentInfo* GetSegmentInfo() const { return &segment_info_; }
+ SegmentInfo* GetSegmentInfo() { return &segment_info_; }
+
+ // Search the Tracks and return the track that matches |track_number|.
+ // Returns NULL if there is no track match.
+ Track* GetTrackByNumber(uint64_t track_number) const;
+
+ // Toggles whether to output a cues element.
+ void OutputCues(bool output_cues);
+
+ // Toggles whether to write the last frame in each Cluster with Duration.
+ void AccurateClusterDuration(bool accurate_cluster_duration);
+
+ // Toggles whether to write the Cluster Timecode using exactly 8 bytes.
+ void UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode);
+
+ // Sets if the muxer will output files in chunks or not. |chunking| is a
+ // flag telling whether or not to turn on chunking. |filename| is the base
+ // filename for the chunk files. The header chunk file will be named
+ // |filename|.hdr and the data chunks will be named
+ // |filename|_XXXXXX.chk. Chunking implies that the muxer will be writing
+ // to files so the muxer will use the default MkvWriter class to control
+ // what data is written to what files. Returns true on success.
+ // TODO: Should we change the IMkvWriter Interface to add Open and Close?
+ // That will force the interface to be dependent on files.
+ bool SetChunking(bool chunking, const char* filename);
+
+ bool chunking() const { return chunking_; }
+ uint64_t cues_track() const { return cues_track_; }
+ void set_max_cluster_duration(uint64_t max_cluster_duration) {
+ max_cluster_duration_ = max_cluster_duration;
+ }
+ uint64_t max_cluster_duration() const { return max_cluster_duration_; }
+ void set_max_cluster_size(uint64_t max_cluster_size) {
+ max_cluster_size_ = max_cluster_size;
+ }
+ uint64_t max_cluster_size() const { return max_cluster_size_; }
+ void set_mode(Mode mode) { mode_ = mode; }
+ Mode mode() const { return mode_; }
+ CuesPosition cues_position() const { return cues_position_; }
+ bool output_cues() const { return output_cues_; }
+ const SegmentInfo* segment_info() const { return &segment_info_; }
+
+ private:
+ // Checks if header information has been output and initialized. If not it
+ // will output the Segment element and initialize the SeekHead elment and
+ // 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.
+ bool UpdateChunkName(const char* ext, char** name) const;
+
+ // Returns the maximum offset within the segment's payload. When chunking
+ // this function is needed to determine offsets of elements within the
+ // chunked files. Returns -1 on error.
+ int64_t MaxOffset();
+
+ // Adds the frame to our frame array.
+ bool QueueFrame(Frame* frame);
+
+ // Output all frames that are queued. Returns -1 on error, otherwise
+ // it returns the number of frames written.
+ int WriteFramesAll();
+
+ // Output all frames that are queued that have an end time that is less
+ // then |timestamp|. Returns true on success and if there are no frames
+ // queued.
+ bool WriteFramesLessThan(uint64_t timestamp);
+
+ // Outputs the segment header, Segment Information element, SeekHead element,
+ // and Tracks element to |writer_|.
+ bool WriteSegmentHeader();
+
+ // Given a frame with the specified timestamp (nanosecond units) and
+ // keyframe status, determine whether a new cluster should be
+ // created, before writing enqueued frames and the frame itself. The
+ // function returns one of the following values:
+ // -1 = error: an out-of-order frame was detected
+ // 0 = do not create a new cluster, and write frame to the existing cluster
+ // 1 = create a new cluster, and write frame to that new cluster
+ // 2 = create a new cluster, and re-run test
+ int TestFrame(uint64_t track_num, uint64_t timestamp_ns, bool key) const;
+
+ // Create a new cluster, using the earlier of the first enqueued
+ // frame, or the indicated time. Returns true on success.
+ bool MakeNewCluster(uint64_t timestamp_ns);
+
+ // Checks whether a new cluster needs to be created, and if so
+ // creates a new cluster. Returns false if creation of a new cluster
+ // was necessary but creation was not successful.
+ bool DoNewClusterProcessing(uint64_t track_num, uint64_t timestamp_ns,
+ bool key);
+
+ // Adjusts Cue Point values (to place Cues before Clusters) so that they
+ // reflect the correct offsets.
+ void MoveCuesBeforeClusters();
+
+ // This function recursively computes the correct cluster offsets (this is
+ // done to move the Cues before Clusters). It recursively updates the change
+ // in size (which indicates a change in cluster offset) until no sizes change.
+ // Parameters:
+ // 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 - sum of size of all the CuePoint elements.
+ void MoveCuesBeforeClustersHelper(uint64_t diff, int index,
+ uint64_t* cue_size);
+
+ // Seeds the random number generator used to make UIDs.
+ unsigned int seed_;
+
+ // WebM elements
+ Cues cues_;
+ SeekHead seek_head_;
+ SegmentInfo segment_info_;
+ Tracks tracks_;
+ Chapters chapters_;
+ Tags tags_;
+
+ // Number of chunks written.
+ int chunk_count_;
+
+ // Current chunk filename.
+ char* chunk_name_;
+
+ // Default MkvWriter object created by this class used for writing clusters
+ // out in separate files.
+ MkvWriter* chunk_writer_cluster_;
+
+ // Default MkvWriter object created by this class used for writing Cues
+ // element out to a file.
+ MkvWriter* chunk_writer_cues_;
+
+ // Default MkvWriter object created by this class used for writing the
+ // Matroska header out to a file.
+ MkvWriter* chunk_writer_header_;
+
+ // Flag telling whether or not the muxer is chunking output to multiple
+ // files.
+ bool chunking_;
+
+ // Base filename for the chunked files.
+ char* chunking_base_name_;
+
+ // File position offset where the Clusters end.
+ int64_t cluster_end_offset_;
+
+ // List of clusters.
+ Cluster** cluster_list_;
+
+ // Number of cluster pointers allocated in the cluster list.
+ int32_t cluster_list_capacity_;
+
+ // Number of clusters in the cluster list.
+ int32_t cluster_list_size_;
+
+ // Indicates whether Cues should be written before or after Clusters
+ CuesPosition cues_position_;
+
+ // Track number that is associated with the cues element for this segment.
+ uint64_t cues_track_;
+
+ // Tells the muxer to force a new cluster on the next Block.
+ bool force_new_cluster_;
+
+ // List of stored audio frames. These variables are used to store frames so
+ // the muxer can follow the guideline "Audio blocks that contain the video
+ // key frame's timecode should be in the same cluster as the video key frame
+ // block."
+ Frame** frames_;
+
+ // Number of frame pointers allocated in the frame list.
+ int32_t frames_capacity_;
+
+ // Number of frames in the frame list.
+ int32_t frames_size_;
+
+ // Flag telling if a video track has been added to the segment.
+ bool has_video_;
+
+ // Flag telling if the segment's header has been written.
+ bool header_written_;
+
+ // Duration of the last block in nanoseconds.
+ uint64_t last_block_duration_;
+
+ // Last timestamp in nanoseconds added to a cluster.
+ uint64_t last_timestamp_;
+
+ // Last timestamp in nanoseconds by track number added to a cluster.
+ uint64_t 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.
+ uint64_t max_cluster_duration_;
+
+ // Maximum size in bytes for a cluster. This variable is a guideline and
+ // some clusters may have a larger size. Default is 0 which signifies that
+ // the muxer will decide the size.
+ uint64_t max_cluster_size_;
+
+ // The mode that segment is in. If set to |kLive| the writer must not
+ // seek backwards.
+ Mode mode_;
+
+ // Flag telling the muxer that a new cue point should be added.
+ bool new_cuepoint_;
+
+ // TODO(fgalligan): Should we add support for more than one Cues element?
+ // Flag whether or not the muxer should output a Cues element.
+ bool output_cues_;
+
+ // Flag whether or not the last frame in each Cluster will have a Duration
+ // element in it.
+ bool accurate_cluster_duration_;
+
+ // Flag whether or not to write the Cluster Timecode using exactly 8 bytes.
+ bool fixed_size_cluster_timecode_;
+
+ // The size of the EBML header, used to validate the header if
+ // WriteEbmlHeader() is called more than once.
+ int32_t ebml_header_size_;
+
+ // The file position of the segment's payload.
+ int64_t payload_pos_;
+
+ // The file position of the element's size.
+ int64_t 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_t doc_type_version_;
+ uint32_t doc_type_version_written_;
+
+ // Pointer to the writer objects. Not owned by this class.
+ IMkvWriter* writer_cluster_;
+ IMkvWriter* writer_cues_;
+ IMkvWriter* writer_header_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Segment);
+};
+
+} // namespace mkvmuxer
+
+#endif // MKVMUXER_MKVMUXER_H_
--- /dev/null
+++ b/third_party/libwebm/mkvmuxer/mkvmuxertypes.h
@@ -1,0 +1,19 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef MKVMUXER_MKVMUXERTYPES_H_
+#define MKVMUXER_MKVMUXERTYPES_H_
+
+// Copied from Chromium basictypes.h
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+#endif // MKVMUXER_MKVMUXERTYPES_HPP_
--- /dev/null
+++ b/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc
@@ -1,0 +1,649 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#include "mkvmuxer/mkvmuxerutil.h"
+
+#ifdef __ANDROID__
+#include <fcntl.h>
+#endif
+
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <new>
+
+#include "common/webmids.h"
+#include "mkvmuxer/mkvmuxer.h"
+#include "mkvmuxer/mkvwriter.h"
+
+namespace mkvmuxer {
+
+namespace {
+
+// Date elements are always 8 octets in size.
+const int kDateElementSize = 8;
+
+uint64_t WriteBlock(IMkvWriter* writer, const Frame* const frame,
+ int64_t timecode, uint64_t timecode_scale) {
+ uint64_t block_additional_elem_size = 0;
+ uint64_t block_addid_elem_size = 0;
+ uint64_t block_more_payload_size = 0;
+ uint64_t block_more_elem_size = 0;
+ uint64_t block_additions_payload_size = 0;
+ uint64_t block_additions_elem_size = 0;
+ if (frame->additional()) {
+ block_additional_elem_size =
+ EbmlElementSize(libwebm::kMkvBlockAdditional, frame->additional(),
+ frame->additional_length());
+ block_addid_elem_size =
+ EbmlElementSize(libwebm::kMkvBlockAddID, frame->add_id());
+
+ block_more_payload_size =
+ block_addid_elem_size + block_additional_elem_size;
+ block_more_elem_size =
+ EbmlMasterElementSize(libwebm::kMkvBlockMore, block_more_payload_size) +
+ block_more_payload_size;
+ block_additions_payload_size = block_more_elem_size;
+ block_additions_elem_size =
+ EbmlMasterElementSize(libwebm::kMkvBlockAdditions,
+ block_additions_payload_size) +
+ block_additions_payload_size;
+ }
+
+ uint64_t discard_padding_elem_size = 0;
+ if (frame->discard_padding() != 0) {
+ discard_padding_elem_size =
+ EbmlElementSize(libwebm::kMkvDiscardPadding, frame->discard_padding());
+ }
+
+ const uint64_t reference_block_timestamp =
+ frame->reference_block_timestamp() / timecode_scale;
+ uint64_t reference_block_elem_size = 0;
+ if (!frame->is_key()) {
+ reference_block_elem_size =
+ EbmlElementSize(libwebm::kMkvReferenceBlock, reference_block_timestamp);
+ }
+
+ const uint64_t duration = frame->duration() / timecode_scale;
+ uint64_t block_duration_elem_size = 0;
+ if (duration > 0)
+ block_duration_elem_size =
+ EbmlElementSize(libwebm::kMkvBlockDuration, duration);
+
+ const uint64_t block_payload_size = 4 + frame->length();
+ const uint64_t block_elem_size =
+ EbmlMasterElementSize(libwebm::kMkvBlock, block_payload_size) +
+ block_payload_size;
+
+ const uint64_t 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, libwebm::kMkvBlockGroup,
+ block_group_payload_size)) {
+ return 0;
+ }
+
+ if (!WriteEbmlMasterElement(writer, libwebm::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_t>(frame->length())))
+ return 0;
+
+ if (frame->additional()) {
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockAdditions,
+ block_additions_payload_size)) {
+ return 0;
+ }
+
+ if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockMore,
+ block_more_payload_size))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvBlockAddID, frame->add_id()))
+ return 0;
+
+ if (!WriteEbmlElement(writer, libwebm::kMkvBlockAdditional,
+ frame->additional(), frame->additional_length())) {
+ return 0;
+ }
+ }
+
+ if (frame->discard_padding() != 0 &&
+ !WriteEbmlElement(writer, libwebm::kMkvDiscardPadding,
+ frame->discard_padding())) {
+ return false;
+ }
+
+ if (!frame->is_key() &&
+ !WriteEbmlElement(writer, libwebm::kMkvReferenceBlock,
+ reference_block_timestamp)) {
+ return false;
+ }
+
+ if (duration > 0 &&
+ !WriteEbmlElement(writer, libwebm::kMkvBlockDuration, duration)) {
+ return false;
+ }
+ return EbmlMasterElementSize(libwebm::kMkvBlockGroup,
+ block_group_payload_size) +
+ block_group_payload_size;
+}
+
+uint64_t WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame,
+ int64_t timecode) {
+ if (WriteID(writer, libwebm::kMkvSimpleBlock))
+ return 0;
+
+ const int32_t size = static_cast<int32_t>(frame->length()) + 4;
+ if (WriteUInt(writer, size))
+ return 0;
+
+ if (WriteUInt(writer, static_cast<uint64_t>(frame->track_number())))
+ return 0;
+
+ if (SerializeInt(writer, timecode, 2))
+ return 0;
+
+ uint64_t flags = 0;
+ if (frame->is_key())
+ flags |= 0x80;
+
+ if (SerializeInt(writer, flags, 1))
+ return 0;
+
+ if (writer->Write(frame->frame(), static_cast<uint32_t>(frame->length())))
+ return 0;
+
+ return GetUIntSize(libwebm::kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 +
+ frame->length();
+}
+
+} // namespace
+
+int32_t GetCodedUIntSize(uint64_t value) {
+ if (value < 0x000000000000007FULL)
+ return 1;
+ else if (value < 0x0000000000003FFFULL)
+ return 2;
+ else if (value < 0x00000000001FFFFFULL)
+ return 3;
+ else if (value < 0x000000000FFFFFFFULL)
+ return 4;
+ else if (value < 0x00000007FFFFFFFFULL)
+ return 5;
+ else if (value < 0x000003FFFFFFFFFFULL)
+ return 6;
+ else if (value < 0x0001FFFFFFFFFFFFULL)
+ return 7;
+ return 8;
+}
+
+int32_t GetUIntSize(uint64_t value) {
+ if (value < 0x0000000000000100ULL)
+ return 1;
+ else if (value < 0x0000000000010000ULL)
+ return 2;
+ else if (value < 0x0000000001000000ULL)
+ return 3;
+ else if (value < 0x0000000100000000ULL)
+ return 4;
+ else if (value < 0x0000010000000000ULL)
+ return 5;
+ else if (value < 0x0001000000000000ULL)
+ return 6;
+ else if (value < 0x0100000000000000ULL)
+ return 7;
+ return 8;
+}
+
+int32_t GetIntSize(int64_t 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_t v = (value < 0) ? value ^ -1LL : value;
+ return GetUIntSize(2 * v);
+}
+
+uint64_t EbmlMasterElementSize(uint64_t type, uint64_t value) {
+ // Size of EBML ID
+ int32_t ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += GetCodedUIntSize(value);
+
+ return ebml_size;
+}
+
+uint64_t EbmlElementSize(uint64_t type, int64_t value) {
+ // Size of EBML ID
+ int32_t ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += GetIntSize(value);
+
+ // Size of Datasize
+ ebml_size++;
+
+ return ebml_size;
+}
+
+uint64_t EbmlElementSize(uint64_t type, uint64_t value) {
+ return EbmlElementSize(type, value, 0);
+}
+
+uint64_t EbmlElementSize(uint64_t type, uint64_t value, uint64_t fixed_size) {
+ // Size of EBML ID
+ int32_t ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += (fixed_size > 0) ? fixed_size : GetUIntSize(value);
+
+ // Size of Datasize
+ ebml_size++;
+
+ return ebml_size;
+}
+
+uint64_t EbmlElementSize(uint64_t type, float /* value */) {
+ // Size of EBML ID
+ uint64_t ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += sizeof(float);
+
+ // Size of Datasize
+ ebml_size++;
+
+ return ebml_size;
+}
+
+uint64_t EbmlElementSize(uint64_t type, const char* value) {
+ if (!value)
+ return 0;
+
+ // Size of EBML ID
+ uint64_t ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += strlen(value);
+
+ // Size of Datasize
+ ebml_size++;
+
+ return ebml_size;
+}
+
+uint64_t EbmlElementSize(uint64_t type, const uint8_t* value, uint64_t size) {
+ if (!value)
+ return 0;
+
+ // Size of EBML ID
+ uint64_t ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += size;
+
+ // Size of Datasize
+ ebml_size += GetCodedUIntSize(size);
+
+ return ebml_size;
+}
+
+uint64_t EbmlDateElementSize(uint64_t type) {
+ // Size of EBML ID
+ uint64_t ebml_size = GetUIntSize(type);
+
+ // Datasize
+ ebml_size += kDateElementSize;
+
+ // Size of Datasize
+ ebml_size++;
+
+ return ebml_size;
+}
+
+int32_t SerializeInt(IMkvWriter* writer, int64_t value, int32_t size) {
+ if (!writer || size < 1 || size > 8)
+ return -1;
+
+ for (int32_t i = 1; i <= size; ++i) {
+ const int32_t byte_count = size - i;
+ const int32_t bit_count = byte_count * 8;
+
+ const int64_t bb = value >> bit_count;
+ const uint8_t b = static_cast<uint8_t>(bb);
+
+ const int32_t status = writer->Write(&b, 1);
+
+ if (status < 0)
+ return status;
+ }
+
+ return 0;
+}
+
+int32_t SerializeFloat(IMkvWriter* writer, float f) {
+ if (!writer)
+ return -1;
+
+ assert(sizeof(uint32_t) == sizeof(float));
+ // This union is merely used to avoid a reinterpret_cast from float& to
+ // uint32& which will result in violation of strict aliasing.
+ union U32 {
+ uint32_t u32;
+ float f;
+ } value;
+ value.f = f;
+
+ for (int32_t i = 1; i <= 4; ++i) {
+ const int32_t byte_count = 4 - i;
+ const int32_t bit_count = byte_count * 8;
+
+ const uint8_t byte = static_cast<uint8_t>(value.u32 >> bit_count);
+
+ const int32_t status = writer->Write(&byte, 1);
+
+ if (status < 0)
+ return status;
+ }
+
+ return 0;
+}
+
+int32_t WriteUInt(IMkvWriter* writer, uint64_t value) {
+ if (!writer)
+ return -1;
+
+ int32_t size = GetCodedUIntSize(value);
+
+ return WriteUIntSize(writer, value, size);
+}
+
+int32_t WriteUIntSize(IMkvWriter* writer, uint64_t value, int32_t size) {
+ if (!writer || size < 0 || size > 8)
+ return -1;
+
+ if (size > 0) {
+ const uint64_t bit = 1LL << (size * 7);
+
+ if (value > (bit - 2))
+ return -1;
+
+ value |= bit;
+ } else {
+ size = 1;
+ int64_t bit;
+
+ for (;;) {
+ bit = 1LL << (size * 7);
+ const uint64_t max = bit - 2;
+
+ if (value <= max)
+ break;
+
+ ++size;
+ }
+
+ if (size > 8)
+ return false;
+
+ value |= bit;
+ }
+
+ return SerializeInt(writer, value, size);
+}
+
+int32_t WriteID(IMkvWriter* writer, uint64_t type) {
+ if (!writer)
+ return -1;
+
+ writer->ElementStartNotify(type, writer->Position());
+
+ const int32_t size = GetUIntSize(type);
+
+ return SerializeInt(writer, type, size);
+}
+
+bool WriteEbmlMasterElement(IMkvWriter* writer, uint64_t type, uint64_t size) {
+ if (!writer)
+ return false;
+
+ if (WriteID(writer, type))
+ return false;
+
+ if (WriteUInt(writer, size))
+ return false;
+
+ return true;
+}
+
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, uint64_t value) {
+ return WriteEbmlElement(writer, type, value, 0);
+}
+
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, uint64_t value,
+ uint64_t fixed_size) {
+ if (!writer)
+ return false;
+
+ if (WriteID(writer, type))
+ return false;
+
+ uint64_t size = GetUIntSize(value);
+ if (fixed_size > 0) {
+ if (size > fixed_size)
+ return false;
+ size = fixed_size;
+ }
+ if (WriteUInt(writer, size))
+ return false;
+
+ if (SerializeInt(writer, value, static_cast<int32_t>(size)))
+ return false;
+
+ return true;
+}
+
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, int64_t value) {
+ if (!writer)
+ return false;
+
+ if (WriteID(writer, type))
+ return 0;
+
+ const uint64_t size = GetIntSize(value);
+ if (WriteUInt(writer, size))
+ return false;
+
+ if (SerializeInt(writer, value, static_cast<int32_t>(size)))
+ return false;
+
+ return true;
+}
+
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, float value) {
+ if (!writer)
+ return false;
+
+ if (WriteID(writer, type))
+ return false;
+
+ if (WriteUInt(writer, 4))
+ return false;
+
+ if (SerializeFloat(writer, value))
+ return false;
+
+ return true;
+}
+
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, const char* value) {
+ if (!writer || !value)
+ return false;
+
+ if (WriteID(writer, type))
+ return false;
+
+ const uint64_t length = strlen(value);
+ if (WriteUInt(writer, length))
+ return false;
+
+ if (writer->Write(value, static_cast<const uint32_t>(length)))
+ return false;
+
+ return true;
+}
+
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, const uint8_t* value,
+ uint64_t size) {
+ if (!writer || !value || size < 1)
+ return false;
+
+ if (WriteID(writer, type))
+ return false;
+
+ if (WriteUInt(writer, size))
+ return false;
+
+ if (writer->Write(value, static_cast<uint32_t>(size)))
+ return false;
+
+ return true;
+}
+
+bool WriteEbmlDateElement(IMkvWriter* writer, uint64_t type, int64_t value) {
+ if (!writer)
+ return false;
+
+ if (WriteID(writer, type))
+ return false;
+
+ if (WriteUInt(writer, kDateElementSize))
+ return false;
+
+ if (SerializeInt(writer, value, kDateElementSize))
+ return false;
+
+ return true;
+}
+
+uint64_t WriteFrame(IMkvWriter* writer, const Frame* const frame,
+ Cluster* cluster) {
+ if (!writer || !frame || !frame->IsValid() || !cluster ||
+ !cluster->timecode_scale())
+ return 0;
+
+ // 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_t relative_timecode = cluster->GetRelativeTimecode(
+ frame->timestamp() / cluster->timecode_scale());
+ if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode)
+ return 0;
+
+ return frame->CanBeSimpleBlock() ?
+ WriteSimpleBlock(writer, frame, relative_timecode) :
+ WriteBlock(writer, frame, relative_timecode,
+ cluster->timecode_scale());
+}
+
+uint64_t WriteVoidElement(IMkvWriter* writer, uint64_t size) {
+ if (!writer)
+ return false;
+
+ // Subtract one for the void ID and the coded size.
+ uint64_t void_entry_size = size - 1 - GetCodedUIntSize(size - 1);
+ uint64_t void_size =
+ EbmlMasterElementSize(libwebm::kMkvVoid, void_entry_size) +
+ void_entry_size;
+
+ if (void_size != size)
+ return 0;
+
+ const int64_t payload_position = writer->Position();
+ if (payload_position < 0)
+ return 0;
+
+ if (WriteID(writer, libwebm::kMkvVoid))
+ return 0;
+
+ if (WriteUInt(writer, void_entry_size))
+ return 0;
+
+ const uint8_t value = 0;
+ for (int32_t i = 0; i < static_cast<int32_t>(void_entry_size); ++i) {
+ if (writer->Write(&value, 1))
+ return 0;
+ }
+
+ const int64_t stop_position = writer->Position();
+ if (stop_position < 0 ||
+ stop_position - payload_position != static_cast<int64_t>(void_size))
+ return 0;
+
+ return void_size;
+}
+
+void GetVersion(int32_t* major, int32_t* minor, int32_t* build,
+ int32_t* revision) {
+ *major = 0;
+ *minor = 2;
+ *build = 1;
+ *revision = 0;
+}
+
+uint64_t MakeUID(unsigned int* seed) {
+ uint64_t uid = 0;
+
+#ifdef __MINGW32__
+ srand(*seed);
+#endif
+
+ for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values
+ uid <<= 8;
+
+// TODO(fgalligan): Move random number generation to platform specific code.
+#ifdef _MSC_VER
+ (void)seed;
+ const int32_t nn = rand();
+#elif __ANDROID__
+ int32_t temp_num = 1;
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd != -1) {
+ read(fd, &temp_num, sizeof(temp_num));
+ close(fd);
+ }
+ const int32_t nn = temp_num;
+#elif defined __MINGW32__
+ const int32_t nn = rand();
+#else
+ const int32_t nn = rand_r(seed);
+#endif
+ const int32_t n = 0xFF & (nn >> 4); // throw away low-order bits
+
+ uid |= n;
+ }
+
+ return uid;
+}
+
+} // namespace mkvmuxer
--- /dev/null
+++ b/third_party/libwebm/mkvmuxer/mkvmuxerutil.h
@@ -1,0 +1,95 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#ifndef MKVMUXER_MKVMUXERUTIL_H_
+#define MKVMUXER_MKVMUXERUTIL_H_
+
+#include <stdint.h>
+
+namespace mkvmuxer {
+class Cluster;
+class Frame;
+class IMkvWriter;
+
+const uint64_t kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL;
+const int64_t kMaxBlockTimecode = 0x07FFFLL;
+
+// Writes out |value| in Big Endian order. Returns 0 on success.
+int32_t SerializeInt(IMkvWriter* writer, int64_t value, int32_t size);
+
+// Returns the size in bytes of the element.
+int32_t GetUIntSize(uint64_t value);
+int32_t GetIntSize(int64_t value);
+int32_t GetCodedUIntSize(uint64_t value);
+uint64_t EbmlMasterElementSize(uint64_t type, uint64_t value);
+uint64_t EbmlElementSize(uint64_t type, int64_t value);
+uint64_t EbmlElementSize(uint64_t type, uint64_t value);
+uint64_t EbmlElementSize(uint64_t type, float value);
+uint64_t EbmlElementSize(uint64_t type, const char* value);
+uint64_t EbmlElementSize(uint64_t type, const uint8_t* value, uint64_t size);
+uint64_t EbmlDateElementSize(uint64_t type);
+
+// Returns the size in bytes of the element assuming that the element was
+// written using |fixed_size| bytes. If |fixed_size| is set to zero, then it
+// computes the necessary number of bytes based on |value|.
+uint64_t EbmlElementSize(uint64_t type, uint64_t value, uint64_t fixed_size);
+
+// 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
+// be in a coded form. Returns 0 on success.
+int32_t WriteUInt(IMkvWriter* writer, uint64_t value);
+
+// Creates an EBML coded number from |value| and writes it out. The size of
+// the coded number is determined by the value of |size|. |value| must not
+// be in a coded form. Returns 0 on success.
+int32_t WriteUIntSize(IMkvWriter* writer, uint64_t value, int32_t size);
+
+// Output an Mkv master element. Returns true if the element was written.
+bool WriteEbmlMasterElement(IMkvWriter* writer, uint64_t value, uint64_t size);
+
+// Outputs an Mkv ID, calls |IMkvWriter::ElementStartNotify|, and passes the
+// ID to |SerializeInt|. Returns 0 on success.
+int32_t WriteID(IMkvWriter* writer, uint64_t type);
+
+// Output an Mkv non-master element. Returns true if the element was written.
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, uint64_t value);
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, int64_t value);
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, float value);
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, const char* value);
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, const uint8_t* value,
+ uint64_t size);
+bool WriteEbmlDateElement(IMkvWriter* writer, uint64_t type, int64_t value);
+
+// Output an Mkv non-master element using fixed size. The element will be
+// written out using exactly |fixed_size| bytes. If |fixed_size| is set to zero
+// then it computes the necessary number of bytes based on |value|. Returns true
+// if the element was written.
+bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, uint64_t value,
+ uint64_t fixed_size);
+
+// Output a Mkv Frame. It decides the correct element to write (Block vs
+// SimpleBlock) based on the parameters of the Frame.
+uint64_t 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
+// it from |size|.
+uint64_t WriteVoidElement(IMkvWriter* writer, uint64_t size);
+
+// Returns the version number of the muxer in |major|, |minor|, |build|,
+// and |revision|.
+void GetVersion(int32_t* major, int32_t* minor, int32_t* build,
+ int32_t* revision);
+
+// Returns a random number to be used for UID, using |seed| to seed
+// the random-number generator (see POSIX rand_r() for semantics).
+uint64_t MakeUID(unsigned int* seed);
+
+} // namespace mkvmuxer
+
+#endif // MKVMUXER_MKVMUXERUTIL_H_
--- /dev/null
+++ b/third_party/libwebm/mkvmuxer/mkvwriter.cc
@@ -1,0 +1,88 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#include "mkvmuxer/mkvwriter.h"
+
+#ifdef _MSC_VER
+#include <share.h> // for _SH_DENYWR
+#endif
+
+namespace mkvmuxer {
+
+MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
+
+MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {}
+
+MkvWriter::~MkvWriter() { Close(); }
+
+int32_t MkvWriter::Write(const void* buffer, uint32_t length) {
+ if (!file_)
+ return -1;
+
+ if (length == 0)
+ return 0;
+
+ if (buffer == NULL)
+ return -1;
+
+ const size_t bytes_written = fwrite(buffer, 1, length, file_);
+
+ return (bytes_written == length) ? 0 : -1;
+}
+
+bool MkvWriter::Open(const char* filename) {
+ if (filename == NULL)
+ return false;
+
+ if (file_)
+ return false;
+
+#ifdef _MSC_VER
+ file_ = _fsopen(filename, "wb", _SH_DENYWR);
+#else
+ file_ = fopen(filename, "wb");
+#endif
+ if (file_ == NULL)
+ return false;
+ return true;
+}
+
+void MkvWriter::Close() {
+ if (file_ && writer_owns_file_) {
+ fclose(file_);
+ }
+ file_ = NULL;
+}
+
+int64_t MkvWriter::Position() const {
+ if (!file_)
+ return 0;
+
+#ifdef _MSC_VER
+ return _ftelli64(file_);
+#else
+ return ftell(file_);
+#endif
+}
+
+int32_t MkvWriter::Position(int64_t position) {
+ if (!file_)
+ return -1;
+
+#ifdef _MSC_VER
+ return _fseeki64(file_, position, SEEK_SET);
+#else
+ return fseek(file_, position, SEEK_SET);
+#endif
+}
+
+bool MkvWriter::Seekable() const { return true; }
+
+void MkvWriter::ElementStartNotify(uint64_t, int64_t) {}
+
+} // namespace mkvmuxer
--- /dev/null
+++ b/third_party/libwebm/mkvmuxer/mkvwriter.h
@@ -1,0 +1,51 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef MKVMUXER_MKVWRITER_H_
+#define MKVMUXER_MKVWRITER_H_
+
+#include <stdio.h>
+
+#include "mkvmuxer/mkvmuxer.h"
+#include "mkvmuxer/mkvmuxertypes.h"
+
+namespace mkvmuxer {
+
+// Default implementation of the IMkvWriter interface on Windows.
+class MkvWriter : public IMkvWriter {
+ public:
+ MkvWriter();
+ explicit MkvWriter(FILE* fp);
+ virtual ~MkvWriter();
+
+ // IMkvWriter interface
+ virtual int64_t Position() const;
+ virtual int32_t Position(int64_t position);
+ virtual bool Seekable() const;
+ virtual int32_t Write(const void* buffer, uint32_t length);
+ virtual void ElementStartNotify(uint64_t element_id, int64_t position);
+
+ // Creates and opens a file for writing. |filename| is the name of the file
+ // to open. This function will overwrite the contents of |filename|. Returns
+ // true on success.
+ bool Open(const char* filename);
+
+ // Closes an opened file.
+ void Close();
+
+ private:
+ // File handle to output file.
+ FILE* file_;
+ bool writer_owns_file_;
+
+ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
+};
+
+} // namespace mkvmuxer
+
+#endif // MKVMUXER_MKVWRITER_H_
--- a/third_party/libwebm/mkvmuxertypes.hpp
+++ /dev/null
@@ -1,30 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef MKVMUXERTYPES_HPP
-#define MKVMUXERTYPES_HPP
-
-// Copied from Chromium basictypes.h
-// A macro to disallow the copy constructor and operator= functions
-// This should be used in the private: declarations for a class
-#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
-namespace mkvmuxer {
-
-typedef unsigned char uint8;
-typedef short int16;
-typedef int int32;
-typedef unsigned int uint32;
-typedef long long int64;
-typedef unsigned long long uint64;
-
-} // end namespace mkvmuxer
-
-#endif // MKVMUXERTYPES_HPP
--- a/third_party/libwebm/mkvmuxerutil.cpp
+++ /dev/null
@@ -1,629 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#include "mkvmuxerutil.hpp"
-
-#ifdef __ANDROID__
-#include <fcntl.h>
-#endif
-
-#include <cassert>
-#include <cmath>
-#include <cstdio>
-#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 {
-
-// 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) {
- if (value < 0x000000000000007FULL)
- return 1;
- else if (value < 0x0000000000003FFFULL)
- return 2;
- else if (value < 0x00000000001FFFFFULL)
- return 3;
- else if (value < 0x000000000FFFFFFFULL)
- return 4;
- else if (value < 0x00000007FFFFFFFFULL)
- return 5;
- else if (value < 0x000003FFFFFFFFFFULL)
- return 6;
- else if (value < 0x0001FFFFFFFFFFFFULL)
- return 7;
- return 8;
-}
-
-int32 GetUIntSize(uint64 value) {
- if (value < 0x0000000000000100ULL)
- return 1;
- else if (value < 0x0000000000010000ULL)
- return 2;
- else if (value < 0x0000000001000000ULL)
- return 3;
- else if (value < 0x0000000100000000ULL)
- return 4;
- else if (value < 0x0000010000000000ULL)
- return 5;
- else if (value < 0x0001000000000000ULL)
- return 6;
- else if (value < 0x0100000000000000ULL)
- return 7;
- 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);
-
- // Datasize
- ebml_size += GetCodedUIntSize(value);
-
- return ebml_size;
-}
-
-uint64 EbmlElementSize(uint64 type, int64 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) {
- // Size of EBML ID
- int32 ebml_size = GetUIntSize(type);
-
- // Datasize
- ebml_size += GetUIntSize(value);
-
- // Size of Datasize
- ebml_size++;
-
- return ebml_size;
-}
-
-uint64 EbmlElementSize(uint64 type, float /* value */) {
- // Size of EBML ID
- uint64 ebml_size = GetUIntSize(type);
-
- // Datasize
- ebml_size += sizeof(float);
-
- // Size of Datasize
- ebml_size++;
-
- return ebml_size;
-}
-
-uint64 EbmlElementSize(uint64 type, const char* value) {
- if (!value)
- return 0;
-
- // Size of EBML ID
- uint64 ebml_size = GetUIntSize(type);
-
- // Datasize
- ebml_size += strlen(value);
-
- // Size of Datasize
- ebml_size++;
-
- return ebml_size;
-}
-
-uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) {
- if (!value)
- return 0;
-
- // Size of EBML ID
- uint64 ebml_size = GetUIntSize(type);
-
- // Datasize
- ebml_size += size;
-
- // Size of Datasize
- ebml_size += GetCodedUIntSize(size);
-
- return ebml_size;
-}
-
-uint64 EbmlDateElementSize(uint64 type) {
- // Size of EBML ID
- uint64 ebml_size = GetUIntSize(type);
-
- // Datasize
- ebml_size += kDateElementSize;
-
- // Size of Datasize
- ebml_size++;
-
- return ebml_size;
-}
-
-int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size) {
- if (!writer || size < 1 || size > 8)
- return -1;
-
- for (int32 i = 1; i <= size; ++i) {
- const int32 byte_count = size - i;
- const int32 bit_count = byte_count * 8;
-
- const int64 bb = value >> bit_count;
- const uint8 b = static_cast<uint8>(bb);
-
- const int32 status = writer->Write(&b, 1);
-
- if (status < 0)
- return status;
- }
-
- return 0;
-}
-
-int32 SerializeFloat(IMkvWriter* writer, float f) {
- if (!writer)
- return -1;
-
- assert(sizeof(uint32) == sizeof(float));
- // This union is merely used to avoid a reinterpret_cast from float& to
- // uint32& which will result in violation of strict aliasing.
- union U32 {
- uint32 u32;
- float f;
- } value;
- value.f = f;
-
- for (int32 i = 1; i <= 4; ++i) {
- const int32 byte_count = 4 - i;
- const int32 bit_count = byte_count * 8;
-
- const uint8 byte = static_cast<uint8>(value.u32 >> bit_count);
-
- const int32 status = writer->Write(&byte, 1);
-
- if (status < 0)
- return status;
- }
-
- return 0;
-}
-
-int32 WriteUInt(IMkvWriter* writer, uint64 value) {
- if (!writer)
- return -1;
-
- int32 size = GetCodedUIntSize(value);
-
- return WriteUIntSize(writer, value, size);
-}
-
-int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size) {
- if (!writer || size < 0 || size > 8)
- return -1;
-
- if (size > 0) {
- const uint64 bit = 1LL << (size * 7);
-
- if (value > (bit - 2))
- return -1;
-
- value |= bit;
- } else {
- size = 1;
- int64 bit;
-
- for (;;) {
- bit = 1LL << (size * 7);
- const uint64 max = bit - 2;
-
- if (value <= max)
- break;
-
- ++size;
- }
-
- if (size > 8)
- return false;
-
- value |= bit;
- }
-
- return SerializeInt(writer, value, size);
-}
-
-int32 WriteID(IMkvWriter* writer, uint64 type) {
- if (!writer)
- return -1;
-
- writer->ElementStartNotify(type, writer->Position());
-
- const int32 size = GetUIntSize(type);
-
- return SerializeInt(writer, type, size);
-}
-
-bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 type, uint64 size) {
- if (!writer)
- return false;
-
- if (WriteID(writer, type))
- return false;
-
- if (WriteUInt(writer, size))
- return false;
-
- return true;
-}
-
-bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) {
- if (!writer)
- return false;
-
- if (WriteID(writer, type))
- return false;
-
- const uint64 size = GetUIntSize(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, 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;
-
- if (WriteID(writer, type))
- return false;
-
- if (WriteUInt(writer, 4))
- return false;
-
- if (SerializeFloat(writer, value))
- return false;
-
- return true;
-}
-
-bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value) {
- if (!writer || !value)
- return false;
-
- if (WriteID(writer, type))
- return false;
-
- const uint64 length = strlen(value);
- if (WriteUInt(writer, length))
- return false;
-
- if (writer->Write(value, static_cast<const uint32>(length)))
- return false;
-
- return true;
-}
-
-bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
- uint64 size) {
- if (!writer || !value || size < 1)
- return false;
-
- if (WriteID(writer, type))
- return false;
-
- if (WriteUInt(writer, size))
- return false;
-
- if (writer->Write(value, static_cast<uint32>(size)))
- return false;
-
- return true;
-}
-
-bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) {
- if (!writer)
- return false;
-
- if (WriteID(writer, type))
- return false;
-
- if (WriteUInt(writer, kDateElementSize))
- return false;
-
- if (SerializeInt(writer, value, kDateElementSize))
- return false;
-
- return true;
-}
-
-uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
- Cluster* cluster) {
- if (!writer || !frame || !frame->IsValid() || !cluster ||
- !cluster->timecode_scale())
- return 0;
-
- // 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;
-
- return frame->CanBeSimpleBlock() ?
- WriteSimpleBlock(writer, frame, relative_timecode) :
- WriteBlock(writer, frame, relative_timecode,
- cluster->timecode_scale());
-}
-
-uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
- if (!writer)
- return false;
-
- // Subtract one for the void ID and the coded size.
- uint64 void_entry_size = size - 1 - GetCodedUIntSize(size - 1);
- uint64 void_size =
- EbmlMasterElementSize(kMkvVoid, void_entry_size) + void_entry_size;
-
- if (void_size != size)
- return 0;
-
- const int64 payload_position = writer->Position();
- if (payload_position < 0)
- return 0;
-
- if (WriteID(writer, kMkvVoid))
- return 0;
-
- if (WriteUInt(writer, void_entry_size))
- return 0;
-
- const uint8 value = 0;
- for (int32 i = 0; i < static_cast<int32>(void_entry_size); ++i) {
- if (writer->Write(&value, 1))
- return 0;
- }
-
- const int64 stop_position = writer->Position();
- if (stop_position < 0 ||
- stop_position - payload_position != static_cast<int64>(void_size))
- return 0;
-
- return void_size;
-}
-
-void GetVersion(int32* major, int32* minor, int32* build, int32* revision) {
- *major = 0;
- *minor = 2;
- *build = 1;
- *revision = 0;
-}
-
-} // namespace mkvmuxer
-
-mkvmuxer::uint64 mkvmuxer::MakeUID(unsigned int* seed) {
- uint64 uid = 0;
-
-#ifdef __MINGW32__
- srand(*seed);
-#endif
-
- for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values
- uid <<= 8;
-
-// TODO(fgalligan): Move random number generation to platform specific code.
-#ifdef _MSC_VER
- (void)seed;
- const int32 nn = rand();
-#elif __ANDROID__
- int32 temp_num = 1;
- int fd = open("/dev/urandom", O_RDONLY);
- if (fd != -1) {
- read(fd, &temp_num, sizeof(int32));
- close(fd);
- }
- const int32 nn = temp_num;
-#elif defined __MINGW32__
- const int32 nn = rand();
-#else
- const int32 nn = rand_r(seed);
-#endif
- const int32 n = 0xFF & (nn >> 4); // throw away low-order bits
-
- uid |= n;
- }
-
- return uid;
-}
--- a/third_party/libwebm/mkvmuxerutil.hpp
+++ /dev/null
@@ -1,83 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef MKVMUXERUTIL_HPP
-#define MKVMUXERUTIL_HPP
-
-#include "mkvmuxer.hpp"
-#include "mkvmuxertypes.hpp"
-
-namespace mkvmuxer {
-
-class IMkvWriter;
-
-const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL;
-const int64 kMaxBlockTimecode = 0x07FFFLL;
-
-// Writes out |value| in Big Endian order. Returns 0 on success.
-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);
-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);
-
-// 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
-// be in a coded form. Returns 0 on success.
-int32 WriteUInt(IMkvWriter* writer, uint64 value);
-
-// Creates an EBML coded number from |value| and writes it out. The size of
-// the coded number is determined by the value of |size|. |value| must not
-// be in a coded form. Returns 0 on success.
-int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size);
-
-// Output an Mkv master element. Returns true if the element was written.
-bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 value, uint64 size);
-
-// Outputs an Mkv ID, calls |IMkvWriter::ElementStartNotify|, and passes the
-// ID to |SerializeInt|. Returns 0 on success.
-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 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
-// it from |size|.
-uint64 WriteVoidElement(IMkvWriter* writer, uint64 size);
-
-// Returns the version number of the muxer in |major|, |minor|, |build|,
-// and |revision|.
-void GetVersion(int32* major, int32* minor, int32* build, int32* revision);
-
-// Returns a random number to be used for UID, using |seed| to seed
-// the random-number generator (see POSIX rand_r() for semantics).
-uint64 MakeUID(unsigned int* seed);
-
-} // end namespace mkvmuxer
-
-#endif // MKVMUXERUTIL_HPP
--- a/third_party/libwebm/mkvparser.cpp
+++ /dev/null
@@ -1,7724 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#include "mkvparser.hpp"
-
-#if defined(_MSC_VER) && _MSC_VER < 1800
-#include <float.h> // _isnan() / _finite()
-#define MSC_COMPAT
-#endif
-
-#include <cassert>
-#include <climits>
-#include <cmath>
-#include <cstring>
-#include <new>
-
-#include "webmids.hpp"
-
-#ifdef _MSC_VER
-// Disable MSVC warnings that suggest making code non-portable.
-#pragma warning(disable : 4996)
-#endif
-
-namespace mkvparser {
-
-#ifdef MSC_COMPAT
-inline bool isnan(double val) { return !!_isnan(val); }
-inline bool isinf(double val) { return !_finite(val); }
-#else
-inline bool isnan(double val) { return std::isnan(val); }
-inline bool isinf(double val) { return std::isinf(val); }
-#endif // MSC_COMPAT
-
-IMkvReader::~IMkvReader() {}
-
-template<typename Type> Type* SafeArrayAlloc(unsigned long long num_elements,
- unsigned long long element_size) {
- if (num_elements == 0 || element_size == 0)
- return NULL;
-
- const size_t kMaxAllocSize = 0x80000000; // 2GiB
- const unsigned long long num_bytes = num_elements * element_size;
- if (element_size > (kMaxAllocSize / num_elements))
- return NULL;
- if (num_bytes != static_cast<size_t>(num_bytes))
- return NULL;
-
- return new (std::nothrow) Type[static_cast<size_t>(num_bytes)];
-}
-
-void GetVersion(int& major, int& minor, int& build, int& revision) {
- major = 1;
- minor = 0;
- build = 0;
- revision = 30;
-}
-
-long long ReadUInt(IMkvReader* pReader, long long pos, long& len) {
- if (!pReader || pos < 0)
- return E_FILE_FORMAT_INVALID;
-
- len = 1;
- unsigned char b;
- int status = pReader->Read(pos, 1, &b);
-
- if (status < 0) // error or underflow
- return status;
-
- if (status > 0) // interpreted as "underflow"
- return E_BUFFER_NOT_FULL;
-
- if (b == 0) // we can't handle u-int values larger than 8 bytes
- return E_FILE_FORMAT_INVALID;
-
- unsigned char m = 0x80;
-
- while (!(b & m)) {
- m >>= 1;
- ++len;
- }
-
- long long result = b & (~m);
- ++pos;
-
- for (int i = 1; i < len; ++i) {
- status = pReader->Read(pos, 1, &b);
-
- if (status < 0) {
- len = 1;
- return status;
- }
-
- if (status > 0) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
-}
-
-// Reads an EBML ID and returns it.
-// An ID must at least 1 byte long, cannot exceed 4, and its value must be
-// greater than 0.
-// See known EBML values and EBMLMaxIDLength:
-// http://www.matroska.org/technical/specs/index.html
-// Returns the ID, or a value less than 0 to report an error while reading the
-// ID.
-long long ReadID(IMkvReader* pReader, long long pos, long& len) {
- if (pReader == NULL || pos < 0)
- return E_FILE_FORMAT_INVALID;
-
- // Read the first byte. The length in bytes of the ID is determined by
- // finding the first set bit in the first byte of the ID.
- unsigned char temp_byte = 0;
- int read_status = pReader->Read(pos, 1, &temp_byte);
-
- if (read_status < 0)
- return E_FILE_FORMAT_INVALID;
- else if (read_status > 0) // No data to read.
- return E_BUFFER_NOT_FULL;
-
- if (temp_byte == 0) // ID length > 8 bytes; invalid file.
- return E_FILE_FORMAT_INVALID;
-
- int bit_pos = 0;
- const int kMaxIdLengthInBytes = 4;
- const int kCheckByte = 0x80;
-
- // Find the first bit that's set.
- bool found_bit = false;
- for (; bit_pos < kMaxIdLengthInBytes; ++bit_pos) {
- if ((kCheckByte >> bit_pos) & temp_byte) {
- found_bit = true;
- break;
- }
- }
-
- if (!found_bit) {
- // The value is too large to be a valid ID.
- return E_FILE_FORMAT_INVALID;
- }
-
- // Read the remaining bytes of the ID (if any).
- const int id_length = bit_pos + 1;
- long long ebml_id = temp_byte;
- for (int i = 1; i < id_length; ++i) {
- ebml_id <<= 8;
- read_status = pReader->Read(pos + i, 1, &temp_byte);
-
- if (read_status < 0)
- return E_FILE_FORMAT_INVALID;
- else if (read_status > 0)
- return E_BUFFER_NOT_FULL;
-
- ebml_id |= temp_byte;
- }
-
- len = id_length;
- return ebml_id;
-}
-
-long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) {
- if (!pReader || pos < 0)
- return E_FILE_FORMAT_INVALID;
-
- long long total, available;
-
- int status = pReader->Length(&total, &available);
- if (status < 0 || (total >= 0 && available > total))
- return E_FILE_FORMAT_INVALID;
-
- len = 1;
-
- if (pos >= available)
- return pos; // too few bytes available
-
- unsigned char b;
-
- status = pReader->Read(pos, 1, &b);
-
- if (status != 0)
- return status;
-
- if (b == 0) // we can't handle u-int values larger than 8 bytes
- return E_FILE_FORMAT_INVALID;
-
- unsigned char m = 0x80;
-
- while (!(b & m)) {
- m >>= 1;
- ++len;
- }
-
- return 0; // success
-}
-
-// TODO(vigneshv): This function assumes that unsigned values never have their
-// high bit set.
-long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) {
- if (!pReader || pos < 0 || (size <= 0) || (size > 8))
- return E_FILE_FORMAT_INVALID;
-
- long long result = 0;
-
- for (long long i = 0; i < size; ++i) {
- unsigned char b;
-
- const long status = pReader->Read(pos, 1, &b);
-
- if (status < 0)
- return status;
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
-}
-
-long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
- double& result) {
- if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8)))
- return E_FILE_FORMAT_INVALID;
-
- const long size = static_cast<long>(size_);
-
- unsigned char buf[8];
-
- const int status = pReader->Read(pos, size, buf);
-
- if (status < 0) // error
- return status;
-
- if (size == 4) {
- union {
- float f;
- unsigned long ff;
- };
-
- ff = 0;
-
- for (int i = 0;;) {
- ff |= buf[i];
-
- if (++i >= 4)
- break;
-
- ff <<= 8;
- }
-
- result = f;
- } else {
- union {
- double d;
- unsigned long long dd;
- };
-
- dd = 0;
-
- for (int i = 0;;) {
- dd |= buf[i];
-
- if (++i >= 8)
- break;
-
- dd <<= 8;
- }
-
- result = d;
- }
-
- if (mkvparser::isinf(result) || mkvparser::isnan(result))
- return E_FILE_FORMAT_INVALID;
-
- return 0;
-}
-
-long UnserializeInt(IMkvReader* pReader, long long pos, long long size,
- long long& result_ref) {
- if (!pReader || pos < 0 || size < 1 || size > 8)
- return E_FILE_FORMAT_INVALID;
-
- signed char first_byte = 0;
- const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte);
-
- if (status < 0)
- return status;
-
- unsigned long long result = first_byte;
- ++pos;
-
- for (long i = 1; i < size; ++i) {
- unsigned char b;
-
- const long status = pReader->Read(pos, 1, &b);
-
- if (status < 0)
- return status;
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- result_ref = static_cast<long long>(result);
- return 0;
-}
-
-long UnserializeString(IMkvReader* pReader, long long pos, long long size,
- char*& str) {
- delete[] str;
- str = NULL;
-
- if (size >= LONG_MAX || size < 0)
- return E_FILE_FORMAT_INVALID;
-
- // +1 for '\0' terminator
- const long required_size = static_cast<long>(size) + 1;
-
- str = SafeArrayAlloc<char>(1, required_size);
- if (str == NULL)
- return E_FILE_FORMAT_INVALID;
-
- unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
-
- const long status = pReader->Read(pos, static_cast<long>(size), buf);
-
- if (status) {
- delete[] str;
- str = NULL;
-
- return status;
- }
-
- str[required_size - 1] = '\0';
- return 0;
-}
-
-long ParseElementHeader(IMkvReader* pReader, long long& pos,
- long long stop, long long& id,
- long long& size) {
- if (stop >= 0 && pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- long len;
-
- id = ReadID(pReader, pos, len);
-
- if (id < 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume id
-
- if (stop >= 0 && pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- size = ReadUInt(pReader, pos, len);
-
- if (size < 0 || len < 1 || len > 8) {
- // Invalid: Negative payload size, negative or 0 length integer, or integer
- // larger than 64 bits (libwebm cannot handle them).
- return E_FILE_FORMAT_INVALID;
- }
-
- // Avoid rolling over pos when very close to LLONG_MAX.
- const unsigned long long rollover_check =
- static_cast<unsigned long long>(pos) + len;
- if (rollover_check > LLONG_MAX)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume length of size
-
- // pos now designates payload
-
- if (stop >= 0 && pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- return 0; // success
-}
-
-bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
- long long& val) {
- if (!pReader || pos < 0)
- return false;
-
- long long total = 0;
- long long available = 0;
-
- const long status = pReader->Length(&total, &available);
- if (status < 0 || (total >= 0 && available > total))
- return false;
-
- long len = 0;
-
- const long long id = ReadID(pReader, pos, len);
- if (id < 0 || (available - pos) > len)
- return false;
-
- if (static_cast<unsigned long>(id) != expected_id)
- return false;
-
- pos += len; // consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len)
- return false;
-
- pos += len; // consume length of size of payload
-
- val = UnserializeUInt(pReader, pos, size);
- if (val < 0)
- return false;
-
- pos += size; // consume size of payload
-
- return true;
-}
-
-bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
- unsigned char*& buf, size_t& buflen) {
- if (!pReader || pos < 0)
- return false;
-
- long long total = 0;
- long long available = 0;
-
- long status = pReader->Length(&total, &available);
- if (status < 0 || (total >= 0 && available > total))
- return false;
-
- long len = 0;
- const long long id = ReadID(pReader, pos, len);
- if (id < 0 || (available - pos) > len)
- return false;
-
- if (static_cast<unsigned long>(id) != expected_id)
- return false;
-
- pos += len; // consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- if (size < 0 || len <= 0 || len > 8 || (available - pos) > len)
- return false;
-
- unsigned long long rollover_check =
- static_cast<unsigned long long>(pos) + len;
- if (rollover_check > LLONG_MAX)
- return false;
-
- pos += len; // consume length of size of payload
-
- rollover_check = static_cast<unsigned long long>(pos) + size;
- if (rollover_check > LLONG_MAX)
- return false;
-
- if ((pos + size) > available)
- return false;
-
- if (size >= LONG_MAX)
- return false;
-
- const long buflen_ = static_cast<long>(size);
-
- buf = SafeArrayAlloc<unsigned char>(1, buflen_);
- if (!buf)
- return false;
-
- status = pReader->Read(pos, buflen_, buf);
- if (status != 0)
- return false;
-
- buflen = buflen_;
-
- pos += size; // consume size of payload
- return true;
-}
-
-EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); }
-
-EBMLHeader::~EBMLHeader() { delete[] m_docType; }
-
-void EBMLHeader::Init() {
- m_version = 1;
- m_readVersion = 1;
- m_maxIdLength = 4;
- m_maxSizeLength = 8;
-
- if (m_docType) {
- delete[] m_docType;
- m_docType = NULL;
- }
-
- m_docTypeVersion = 1;
- m_docTypeReadVersion = 1;
-}
-
-long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) {
- if (!pReader)
- return E_FILE_FORMAT_INVALID;
-
- long long total, available;
-
- long status = pReader->Length(&total, &available);
-
- if (status < 0) // error
- return status;
-
- pos = 0;
- long long end = (available >= 1024) ? 1024 : available;
-
- // Scan until we find what looks like the first byte of the EBML header.
- const long long kMaxScanBytes = (available >= 1024) ? 1024 : available;
- const unsigned char kEbmlByte0 = 0x1A;
- unsigned char scan_byte = 0;
-
- while (pos < kMaxScanBytes) {
- status = pReader->Read(pos, 1, &scan_byte);
-
- if (status < 0) // error
- return status;
- else if (status > 0)
- return E_BUFFER_NOT_FULL;
-
- if (scan_byte == kEbmlByte0)
- break;
-
- ++pos;
- }
-
- long len = 0;
- const long long ebml_id = ReadID(pReader, pos, len);
-
- // TODO(tomfinegan): Move Matroska ID constants into a common namespace.
- if (len != 4 || ebml_id != mkvmuxer::kMkvEBML)
- return E_FILE_FORMAT_INVALID;
-
- // Move read pos forward to the EBML header size field.
- pos += 4;
-
- // Read length of size field.
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return E_FILE_FORMAT_INVALID;
- else if (result > 0) // need more data
- return E_BUFFER_NOT_FULL;
-
- if (len < 1 || len > 8)
- return E_FILE_FORMAT_INVALID;
-
- if ((total >= 0) && ((total - pos) < len))
- return E_FILE_FORMAT_INVALID;
-
- if ((available - pos) < len)
- return pos + len; // try again later
-
- // Read the EBML header size.
- result = ReadUInt(pReader, pos, len);
-
- if (result < 0) // error
- return result;
-
- pos += len; // consume size field
-
- // pos now designates start of payload
-
- if ((total >= 0) && ((total - pos) < result))
- return E_FILE_FORMAT_INVALID;
-
- if ((available - pos) < result)
- return pos + result;
-
- end = pos + result;
-
- Init();
-
- while (pos < end) {
- long long id, size;
-
- status = ParseElementHeader(pReader, pos, end, id, size);
-
- if (status < 0) // error
- return status;
-
- if (size == 0)
- return E_FILE_FORMAT_INVALID;
-
- if (id == mkvmuxer::kMkvEBMLVersion) {
- m_version = UnserializeUInt(pReader, pos, size);
-
- if (m_version <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvEBMLReadVersion) {
- m_readVersion = UnserializeUInt(pReader, pos, size);
-
- if (m_readVersion <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvEBMLMaxIDLength) {
- m_maxIdLength = UnserializeUInt(pReader, pos, size);
-
- if (m_maxIdLength <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvEBMLMaxSizeLength) {
- m_maxSizeLength = UnserializeUInt(pReader, pos, size);
-
- if (m_maxSizeLength <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvDocType) {
- if (m_docType)
- return E_FILE_FORMAT_INVALID;
-
- status = UnserializeString(pReader, pos, size, m_docType);
-
- if (status) // error
- return status;
- } else if (id == mkvmuxer::kMkvDocTypeVersion) {
- m_docTypeVersion = UnserializeUInt(pReader, pos, size);
-
- if (m_docTypeVersion <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvDocTypeReadVersion) {
- m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
-
- if (m_docTypeReadVersion <= 0)
- return E_FILE_FORMAT_INVALID;
- }
-
- pos += size;
- }
-
- if (pos != end)
- return E_FILE_FORMAT_INVALID;
-
- // Make sure DocType, DocTypeReadVersion, and DocTypeVersion are valid.
- if (m_docType == NULL || m_docTypeReadVersion <= 0 || m_docTypeVersion <= 0)
- return E_FILE_FORMAT_INVALID;
-
- // Make sure EBMLMaxIDLength and EBMLMaxSizeLength are valid.
- if (m_maxIdLength <= 0 || m_maxIdLength > 4 ||
- m_maxSizeLength <= 0 || m_maxSizeLength > 8)
- return E_FILE_FORMAT_INVALID;
-
- return 0;
-}
-
-Segment::Segment(IMkvReader* pReader, long long elem_start,
- // long long elem_size,
- long long start, long long size)
- : m_pReader(pReader),
- m_element_start(elem_start),
- // m_element_size(elem_size),
- m_start(start),
- m_size(size),
- m_pos(start),
- m_pUnknownSize(0),
- m_pSeekHead(NULL),
- m_pInfo(NULL),
- m_pTracks(NULL),
- m_pCues(NULL),
- m_pChapters(NULL),
- m_pTags(NULL),
- m_clusters(NULL),
- m_clusterCount(0),
- m_clusterPreloadCount(0),
- m_clusterSize(0) {}
-
-Segment::~Segment() {
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- Cluster** i = m_clusters;
- Cluster** j = m_clusters + count;
-
- while (i != j) {
- Cluster* const p = *i++;
- delete p;
- }
-
- delete[] m_clusters;
-
- delete m_pTracks;
- delete m_pInfo;
- delete m_pCues;
- delete m_pChapters;
- delete m_pTags;
- delete m_pSeekHead;
-}
-
-long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
- Segment*& pSegment) {
- if (pReader == NULL || pos < 0)
- return E_PARSE_FAILED;
-
- pSegment = NULL;
-
- long long total, available;
-
- const long status = pReader->Length(&total, &available);
-
- if (status < 0) // error
- return status;
-
- if (available < 0)
- return -1;
-
- if ((total >= 0) && (available > total))
- return -1;
-
- // I would assume that in practice this loop would execute
- // exactly once, but we allow for other elements (e.g. Void)
- // to immediately follow the EBML header. This is fine for
- // the source filter case (since the entire file is available),
- // but in the splitter case over a network we should probably
- // just give up early. We could for example decide only to
- // execute this loop a maximum of, say, 10 times.
- // TODO:
- // There is an implied "give up early" by only parsing up
- // to the available limit. We do do that, but only if the
- // total file size is unknown. We could decide to always
- // use what's available as our limit (irrespective of whether
- // we happen to know the total file length). This would have
- // as its sense "parse this much of the file before giving up",
- // which a slightly different sense from "try to parse up to
- // 10 EMBL elements before giving up".
-
- for (;;) {
- if ((total >= 0) && (pos >= total))
- return E_FILE_FORMAT_INVALID;
-
- // Read ID
- long len;
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result) // error, or too few available bytes
- return result;
-
- if ((total >= 0) && ((pos + len) > total))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long idpos = pos;
- const long long id = ReadID(pReader, pos, len);
-
- if (id < 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume ID
-
- // Read Size
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result) // error, or too few available bytes
- return result;
-
- if ((total >= 0) && ((pos + len) > total))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) // error
- return size;
-
- pos += len; // consume length of size of element
-
- // Pos now points to start of payload
-
- // Handle "unknown size" for live streaming of webm files.
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (id == mkvmuxer::kMkvSegment) {
- if (size == unknown_size)
- size = -1;
-
- else if (total < 0)
- size = -1;
-
- else if ((pos + size) > total)
- size = -1;
-
- pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size);
- if (pSegment == NULL)
- return E_PARSE_FAILED;
-
- return 0; // success
- }
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- if ((total >= 0) && ((pos + size) > total))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + size) > available)
- return pos + size;
-
- pos += size; // consume payload
- }
-}
-
-long long Segment::ParseHeaders() {
- // Outermost (level 0) segment object has been constructed,
- // and pos designates start of payload. We need to find the
- // inner (level 1) elements.
- long long total, available;
-
- const int status = m_pReader->Length(&total, &available);
-
- if (status < 0) // error
- return status;
-
- if (total > 0 && available > total)
- return E_FILE_FORMAT_INVALID;
-
- const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
-
- if ((segment_stop >= 0 && total >= 0 && segment_stop > total) ||
- (segment_stop >= 0 && m_pos > segment_stop)) {
- return E_FILE_FORMAT_INVALID;
- }
-
- for (;;) {
- if ((total >= 0) && (m_pos >= total))
- break;
-
- if ((segment_stop >= 0) && (m_pos >= segment_stop))
- break;
-
- long long pos = m_pos;
- const long long element_start = pos;
-
- // Avoid rolling over pos when very close to LLONG_MAX.
- unsigned long long rollover_check = pos + 1ULL;
- if (rollover_check > LLONG_MAX)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + 1) > available)
- return (pos + 1);
-
- long len;
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) // error
- return result;
-
- if (result > 0) {
- // MkvReader doesn't have enough data to satisfy this read attempt.
- return (pos + 1);
- }
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long idpos = pos;
- const long long id = ReadID(m_pReader, idpos, len);
-
- if (id < 0)
- return E_FILE_FORMAT_INVALID;
-
- if (id == mkvmuxer::kMkvCluster)
- break;
-
- pos += len; // consume ID
-
- if ((pos + 1) > available)
- return (pos + 1);
-
- // Read Size
- result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) // error
- return result;
-
- if (result > 0) {
- // MkvReader doesn't have enough data to satisfy this read attempt.
- return (pos + 1);
- }
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0 || len < 1 || len > 8) {
- // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or
- // len > 8 is true instead of checking this _everywhere_.
- return size;
- }
-
- pos += len; // consume length of size of element
-
- // Avoid rolling over pos when very close to LLONG_MAX.
- rollover_check = static_cast<unsigned long long>(pos) + size;
- if (rollover_check > LLONG_MAX)
- return E_FILE_FORMAT_INVALID;
-
- const long long element_size = size + pos - element_start;
-
- // Pos now points to start of payload
-
- if ((segment_stop >= 0) && ((pos + size) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- // We read EBML elements either in total or nothing at all.
-
- if ((pos + size) > available)
- return pos + size;
-
- if (id == mkvmuxer::kMkvInfo) {
- if (m_pInfo)
- return E_FILE_FORMAT_INVALID;
-
- m_pInfo = new (std::nothrow)
- SegmentInfo(this, pos, size, element_start, element_size);
-
- if (m_pInfo == NULL)
- return -1;
-
- const long status = m_pInfo->Parse();
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvTracks) {
- if (m_pTracks)
- return E_FILE_FORMAT_INVALID;
-
- m_pTracks = new (std::nothrow)
- Tracks(this, pos, size, element_start, element_size);
-
- if (m_pTracks == NULL)
- return -1;
-
- const long status = m_pTracks->Parse();
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvCues) {
- if (m_pCues == NULL) {
- m_pCues = new (std::nothrow)
- Cues(this, pos, size, element_start, element_size);
-
- if (m_pCues == NULL)
- return -1;
- }
- } else if (id == mkvmuxer::kMkvSeekHead) {
- if (m_pSeekHead == NULL) {
- m_pSeekHead = new (std::nothrow)
- SeekHead(this, pos, size, element_start, element_size);
-
- if (m_pSeekHead == NULL)
- return -1;
-
- const long status = m_pSeekHead->Parse();
-
- if (status)
- return status;
- }
- } else if (id == mkvmuxer::kMkvChapters) {
- if (m_pChapters == NULL) {
- m_pChapters = new (std::nothrow)
- Chapters(this, pos, size, element_start, element_size);
-
- if (m_pChapters == NULL)
- return -1;
-
- const long status = m_pChapters->Parse();
-
- if (status)
- return status;
- }
- } else if (id == mkvmuxer::kMkvTags) {
- 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
- }
-
- if (segment_stop >= 0 && m_pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
-
- if (m_pInfo == NULL) // TODO: liberalize this behavior
- return E_FILE_FORMAT_INVALID;
-
- if (m_pTracks == NULL)
- return E_FILE_FORMAT_INVALID;
-
- return 0; // success
-}
-
-long Segment::LoadCluster(long long& pos, long& len) {
- for (;;) {
- const long result = DoLoadCluster(pos, len);
-
- if (result <= 1)
- return result;
- }
-}
-
-long Segment::DoLoadCluster(long long& pos, long& len) {
- if (m_pos < 0)
- return DoLoadClusterUnknownSize(pos, len);
-
- long long total, avail;
-
- long status = m_pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return status;
-
- if (total >= 0 && avail > total)
- return E_FILE_FORMAT_INVALID;
-
- const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
-
- long long cluster_off = -1; // offset relative to start of segment
- long long cluster_size = -1; // size of cluster payload
-
- for (;;) {
- if ((total >= 0) && (m_pos >= total))
- return 1; // no more clusters
-
- if ((segment_stop >= 0) && (m_pos >= segment_stop))
- return 1; // no more clusters
-
- pos = m_pos;
-
- // 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)
- 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 = ReadID(m_pReader, idpos, len);
-
- if (id < 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume ID
-
- // 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)
- 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 length of size of element
-
- // pos now points to start of payload
-
- if (size == 0) {
- // Missing element payload: move on.
- m_pos = pos;
- continue;
- }
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if ((segment_stop >= 0) && (size != unknown_size) &&
- ((pos + size) > segment_stop)) {
- return E_FILE_FORMAT_INVALID;
- }
-
- if (id == mkvmuxer::kMkvCues) {
- if (size == unknown_size) {
- // Cues element of unknown size: Not supported.
- return E_FILE_FORMAT_INVALID;
- }
-
- if (m_pCues == NULL) {
- const long long element_size = (pos - idpos) + size;
-
- m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
- if (m_pCues == NULL)
- return -1;
- }
-
- m_pos = pos + size; // consume payload
- continue;
- }
-
- if (id != mkvmuxer::kMkvCluster) {
- // Besides the Segment, Libwebm allows only cluster elements of unknown
- // size. Fail the parse upon encountering a non-cluster element reporting
- // unknown size.
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- m_pos = pos + size; // consume payload
- continue;
- }
-
- // We have a cluster.
-
- cluster_off = idpos - m_start; // relative pos
-
- if (size != unknown_size)
- cluster_size = size;
-
- break;
- }
-
- if (cluster_off < 0) {
- // No cluster, die.
- return E_FILE_FORMAT_INVALID;
- }
-
- long long pos_;
- long len_;
-
- status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
-
- if (status < 0) { // error, or underflow
- pos = pos_;
- len = len_;
-
- return status;
- }
-
- // status == 0 means "no block entries found"
- // status > 0 means "found at least one block entry"
-
- // TODO:
- // The issue here is that the segment increments its own
- // pos ptr past the most recent cluster parsed, and then
- // starts from there to parse the next cluster. If we
- // don't know the size of the current cluster, then we
- // must either parse its payload (as we do below), looking
- // for the cluster (or cues) ID to terminate the parse.
- // This isn't really what we want: rather, we really need
- // a way to create the curr cluster object immediately.
- // The pity is that cluster::parse can determine its own
- // boundary, and we largely duplicate that same logic here.
- //
- // Maybe we need to get rid of our look-ahead preloading
- // in source::parse???
- //
- // As we're parsing the blocks in the curr cluster
- //(in cluster::parse), we should have some way to signal
- // to the segment that we have determined the boundary,
- // so it can adjust its own segment::m_pos member.
- //
- // The problem is that we're asserting in asyncreadinit,
- // because we adjust the pos down to the curr seek pos,
- // and the resulting adjusted len is > 2GB. I'm suspicious
- // that this is even correct, but even if it is, we can't
- // be loading that much data in the cache anyway.
-
- const long idx = m_clusterCount;
-
- if (m_clusterPreloadCount > 0) {
- if (idx >= m_clusterSize)
- return E_FILE_FORMAT_INVALID;
-
- Cluster* const pCluster = m_clusters[idx];
- if (pCluster == NULL || pCluster->m_index >= 0)
- return E_FILE_FORMAT_INVALID;
-
- const long long off = pCluster->GetPosition();
- if (off < 0)
- return E_FILE_FORMAT_INVALID;
-
- if (off == cluster_off) { // preloaded already
- if (status == 0) // no entries found
- return E_FILE_FORMAT_INVALID;
-
- if (cluster_size >= 0)
- pos += cluster_size;
- else {
- const long long element_size = pCluster->GetElementSize();
-
- if (element_size <= 0)
- return E_FILE_FORMAT_INVALID; // TODO: handle this case
-
- pos = pCluster->m_element_start + element_size;
- }
-
- pCluster->m_index = idx; // move from preloaded to loaded
- ++m_clusterCount;
- --m_clusterPreloadCount;
-
- m_pos = pos; // consume payload
- if (segment_stop >= 0 && m_pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
-
- return 0; // success
- }
- }
-
- if (status == 0) { // no entries found
- if (cluster_size >= 0)
- pos += cluster_size;
-
- if ((total >= 0) && (pos >= total)) {
- m_pos = total;
- return 1; // no more clusters
- }
-
- if ((segment_stop >= 0) && (pos >= segment_stop)) {
- m_pos = segment_stop;
- return 1; // no more clusters
- }
-
- m_pos = pos;
- return 2; // try again
- }
-
- // status > 0 means we have an entry
-
- Cluster* const pCluster = Cluster::Create(this, idx, cluster_off);
- if (pCluster == NULL)
- return -1;
-
- if (!AppendCluster(pCluster)) {
- delete pCluster;
- return -1;
- }
-
- if (cluster_size >= 0) {
- pos += cluster_size;
-
- m_pos = pos;
-
- if (segment_stop > 0 && m_pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
-
- return 0;
- }
-
- m_pUnknownSize = pCluster;
- m_pos = -pos;
-
- 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)
-}
-
-long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
- if (m_pos >= 0 || m_pUnknownSize == NULL)
- return E_PARSE_FAILED;
-
- const long status = m_pUnknownSize->Parse(pos, len);
-
- if (status < 0) // error or underflow
- return status;
-
- if (status == 0) // parsed a block
- return 2; // continue parsing
-
- const long long start = m_pUnknownSize->m_element_start;
- const long long size = m_pUnknownSize->GetElementSize();
-
- if (size < 0)
- return E_FILE_FORMAT_INVALID;
-
- pos = start + size;
- m_pos = pos;
-
- m_pUnknownSize = 0;
-
- return 2; // continue parsing
-}
-
-bool Segment::AppendCluster(Cluster* pCluster) {
- if (pCluster == NULL || pCluster->m_index < 0)
- return false;
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- long& size = m_clusterSize;
- const long idx = pCluster->m_index;
-
- if (size < count || idx != m_clusterCount)
- return false;
-
- if (count >= size) {
- const long n = (size <= 0) ? 2048 : 2 * size;
-
- Cluster** const qq = new (std::nothrow) Cluster*[n];
- if (qq == NULL)
- return false;
-
- Cluster** q = qq;
- Cluster** p = m_clusters;
- Cluster** const pp = p + count;
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_clusters;
-
- m_clusters = qq;
- size = n;
- }
-
- if (m_clusterPreloadCount > 0) {
- Cluster** const p = m_clusters + m_clusterCount;
- if (*p == NULL || (*p)->m_index >= 0)
- return false;
-
- Cluster** q = p + m_clusterPreloadCount;
- if (q >= (m_clusters + size))
- return false;
-
- for (;;) {
- Cluster** const qq = q - 1;
- if ((*qq)->m_index >= 0)
- return false;
-
- *q = *qq;
- q = qq;
-
- if (q == p)
- break;
- }
- }
-
- m_clusters[idx] = pCluster;
- ++m_clusterCount;
- return true;
-}
-
-bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
- if (pCluster == NULL || pCluster->m_index >= 0 || idx < m_clusterCount)
- return false;
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- long& size = m_clusterSize;
- if (size < count)
- return false;
-
- if (count >= size) {
- const long n = (size <= 0) ? 2048 : 2 * size;
-
- Cluster** const qq = new (std::nothrow) Cluster*[n];
- if (qq == NULL)
- return false;
- Cluster** q = qq;
-
- Cluster** p = m_clusters;
- Cluster** const pp = p + count;
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_clusters;
-
- m_clusters = qq;
- size = n;
- }
-
- if (m_clusters == NULL)
- return false;
-
- Cluster** const p = m_clusters + idx;
-
- Cluster** q = m_clusters + count;
- if (q < p || q >= (m_clusters + size))
- return false;
-
- while (q > p) {
- Cluster** const qq = q - 1;
-
- if ((*qq)->m_index >= 0)
- return false;
-
- *q = *qq;
- q = qq;
- }
-
- m_clusters[idx] = pCluster;
- ++m_clusterPreloadCount;
- return true;
-}
-
-long Segment::Load() {
- if (m_clusters != NULL || m_clusterSize != 0 || m_clusterCount != 0)
- return E_PARSE_FAILED;
-
- // Outermost (level 0) segment object has been constructed,
- // and pos designates start of payload. We need to find the
- // inner (level 1) elements.
-
- const long long header_status = ParseHeaders();
-
- if (header_status < 0) // error
- return static_cast<long>(header_status);
-
- if (header_status > 0) // underflow
- return E_BUFFER_NOT_FULL;
-
- if (m_pInfo == NULL || m_pTracks == NULL)
- return E_FILE_FORMAT_INVALID;
-
- for (;;) {
- const int status = LoadCluster();
-
- if (status < 0) // error
- return status;
-
- if (status >= 1) // no more clusters
- return 0;
- }
-}
-
-SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
- long long element_start, long long element_size)
- : m_pSegment(pSegment),
- m_start(start),
- m_size(size_),
- m_element_start(element_start),
- m_element_size(element_size),
- m_entries(0),
- m_entry_count(0),
- m_void_elements(0),
- m_void_element_count(0) {}
-
-SeekHead::~SeekHead() {
- delete[] m_entries;
- delete[] m_void_elements;
-}
-
-long SeekHead::Parse() {
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long pos = m_start;
- const long long stop = m_start + m_size;
-
- // first count the seek head entries
-
- int entry_count = 0;
- int void_element_count = 0;
-
- while (pos < stop) {
- long long id, size;
-
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
-
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvSeek)
- ++entry_count;
- else if (id == mkvmuxer::kMkvVoid)
- ++void_element_count;
-
- pos += size; // consume payload
-
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- m_entries = new (std::nothrow) Entry[entry_count];
-
- if (m_entries == NULL)
- return -1;
-
- m_void_elements = new (std::nothrow) VoidElement[void_element_count];
-
- if (m_void_elements == NULL)
- return -1;
-
- // now parse the entries and void elements
-
- Entry* pEntry = m_entries;
- VoidElement* pVoidElement = m_void_elements;
-
- pos = m_start;
-
- while (pos < stop) {
- const long long idpos = pos;
-
- long long id, size;
-
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
-
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvSeek) {
- if (ParseEntry(pReader, pos, size, pEntry)) {
- Entry& e = *pEntry++;
-
- e.element_start = idpos;
- e.element_size = (pos + size) - idpos;
- }
- } else if (id == mkvmuxer::kMkvVoid) {
- VoidElement& e = *pVoidElement++;
-
- e.element_start = idpos;
- e.element_size = (pos + size) - idpos;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
- assert(count_ >= 0);
- assert(count_ <= entry_count);
-
- m_entry_count = static_cast<int>(count_);
-
- count_ = ptrdiff_t(pVoidElement - m_void_elements);
- assert(count_ >= 0);
- assert(count_ <= void_element_count);
-
- m_void_element_count = static_cast<int>(count_);
-
- return 0;
-}
-
-int SeekHead::GetCount() const { return m_entry_count; }
-
-const SeekHead::Entry* SeekHead::GetEntry(int idx) const {
- if (idx < 0)
- return 0;
-
- if (idx >= m_entry_count)
- return 0;
-
- return m_entries + idx;
-}
-
-int SeekHead::GetVoidElementCount() const { return m_void_element_count; }
-
-const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
- if (idx < 0)
- return 0;
-
- if (idx >= m_void_element_count)
- return 0;
-
- return m_void_elements + idx;
-}
-
-long Segment::ParseCues(long long off, long long& pos, long& len) {
- if (m_pCues)
- return 0; // success
-
- if (off < 0)
- return -1;
-
- long long total, avail;
-
- const int status = m_pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return status;
-
- assert((total < 0) || (avail <= total));
-
- pos = m_start + off;
-
- if ((total < 0) || (pos >= total))
- return 1; // don't bother parsing cues
-
- const long long element_start = pos;
- const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
-
- 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) // underflow (weird)
- {
- len = 1;
- 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 = ReadID(m_pReader, idpos, len);
-
- if (id != mkvmuxer::kMkvCues)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume ID
- assert((segment_stop < 0) || (pos <= segment_stop));
-
- // 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) // underflow (weird)
- {
- len = 1;
- 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);
-
- if (size == 0) // weird, although technically not illegal
- return 1; // done
-
- pos += len; // consume length of size of element
- assert((segment_stop < 0) || (pos <= segment_stop));
-
- // Pos now points to start of payload
-
- const long long element_stop = pos + size;
-
- if ((segment_stop >= 0) && (element_stop > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((total >= 0) && (element_stop > total))
- return 1; // don't bother parsing anymore
-
- len = static_cast<long>(size);
-
- if (element_stop > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long element_size = element_stop - element_start;
-
- m_pCues =
- new (std::nothrow) Cues(this, pos, size, element_start, element_size);
- if (m_pCues == NULL)
- return -1;
-
- return 0; // success
-}
-
-bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
- Entry* pEntry) {
- if (size_ <= 0)
- return false;
-
- long long pos = start;
- const long long stop = start + size_;
-
- long len;
-
- // parse the container for the level-1 element ID
-
- const long long seekIdId = ReadID(pReader, pos, len);
- if (seekIdId < 0)
- return false;
-
- if (seekIdId != mkvmuxer::kMkvSeekID)
- return false;
-
- if ((pos + len) > stop)
- return false;
-
- pos += len; // consume SeekID id
-
- const long long seekIdSize = ReadUInt(pReader, pos, len);
-
- if (seekIdSize <= 0)
- return false;
-
- if ((pos + len) > stop)
- return false;
-
- pos += len; // consume size of field
-
- if ((pos + seekIdSize) > stop)
- return false;
-
- // Note that the SeekId payload really is serialized
- // as a "Matroska integer", not as a plain binary value.
- // In fact, Matroska requires that ID values in the
- // stream exactly match the binary representation as listed
- // in the Matroska specification.
- //
- // This parser is more liberal, and permits IDs to have
- // any width. (This could make the representation in the stream
- // different from what's in the spec, but it doesn't matter here,
- // since we always normalize "Matroska integer" values.)
-
- pEntry->id = ReadUInt(pReader, pos, len); // payload
-
- if (pEntry->id <= 0)
- return false;
-
- if (len != seekIdSize)
- return false;
-
- pos += seekIdSize; // consume SeekID payload
-
- const long long seekPosId = ReadID(pReader, pos, len);
-
- if (seekPosId != mkvmuxer::kMkvSeekPosition)
- return false;
-
- if ((pos + len) > stop)
- return false;
-
- pos += len; // consume id
-
- const long long seekPosSize = ReadUInt(pReader, pos, len);
-
- if (seekPosSize <= 0)
- return false;
-
- if ((pos + len) > stop)
- return false;
-
- pos += len; // consume size
-
- if ((pos + seekPosSize) > stop)
- return false;
-
- pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
-
- if (pEntry->pos < 0)
- return false;
-
- pos += seekPosSize; // consume payload
-
- if (pos != stop)
- return false;
-
- return true;
-}
-
-Cues::Cues(Segment* pSegment, long long start_, long long size_,
- long long element_start, long long element_size)
- : m_pSegment(pSegment),
- m_start(start_),
- m_size(size_),
- m_element_start(element_start),
- m_element_size(element_size),
- m_cue_points(NULL),
- m_count(0),
- m_preload_count(0),
- m_pos(start_) {}
-
-Cues::~Cues() {
- const long n = m_count + m_preload_count;
-
- CuePoint** p = m_cue_points;
- CuePoint** const q = p + n;
-
- while (p != q) {
- CuePoint* const pCP = *p++;
- assert(pCP);
-
- delete pCP;
- }
-
- delete[] m_cue_points;
-}
-
-long Cues::GetCount() const {
- if (m_cue_points == NULL)
- return -1;
-
- return m_count; // TODO: really ignore preload count?
-}
-
-bool Cues::DoneParsing() const {
- const long long stop = m_start + m_size;
- return (m_pos >= stop);
-}
-
-bool Cues::Init() const {
- if (m_cue_points)
- return true;
-
- if (m_count != 0 || m_preload_count != 0)
- return false;
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- const long long stop = m_start + m_size;
- long long pos = m_start;
-
- long cue_points_size = 0;
-
- while (pos < stop) {
- const long long idpos = pos;
-
- long len;
-
- const long long id = ReadID(pReader, pos, len);
- if (id < 0 || (pos + len) > stop) {
- return false;
- }
-
- pos += len; // consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- if (size < 0 || (pos + len > stop)) {
- return false;
- }
-
- pos += len; // consume Size field
- if (pos + size > stop) {
- return false;
- }
-
- if (id == mkvmuxer::kMkvCuePoint) {
- if (!PreloadCuePoint(cue_points_size, idpos))
- return false;
- }
-
- pos += size; // skip payload
- }
- return true;
-}
-
-bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
- if (m_count != 0)
- return false;
-
- if (m_preload_count >= cue_points_size) {
- const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
-
- CuePoint** const qq = new (std::nothrow) CuePoint*[n];
- if (qq == NULL)
- return false;
-
- CuePoint** q = qq; // beginning of target
-
- CuePoint** p = m_cue_points; // beginning of source
- CuePoint** const pp = p + m_preload_count; // end of source
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_cue_points;
-
- m_cue_points = qq;
- cue_points_size = n;
- }
-
- CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
- if (pCP == NULL)
- return false;
-
- m_cue_points[m_preload_count++] = pCP;
- return true;
-}
-
-bool Cues::LoadCuePoint() const {
- const long long stop = m_start + m_size;
-
- if (m_pos >= stop)
- return false; // nothing else to do
-
- if (!Init()) {
- m_pos = stop;
- return false;
- }
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- while (m_pos < stop) {
- const long long idpos = m_pos;
-
- long len;
-
- const long long id = ReadID(pReader, m_pos, len);
- if (id < 0 || (m_pos + len) > stop)
- return false;
-
- m_pos += len; // consume ID
-
- const long long size = ReadUInt(pReader, m_pos, len);
- if (size < 0 || (m_pos + len) > stop)
- return false;
-
- m_pos += len; // consume Size field
- if ((m_pos + size) > stop)
- return false;
-
- if (id != mkvmuxer::kMkvCuePoint) {
- m_pos += size; // consume payload
- if (m_pos > stop)
- return false;
-
- continue;
- }
-
- if (m_preload_count < 1)
- return false;
-
- CuePoint* const pCP = m_cue_points[m_count];
- if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos)))
- return false;
-
- if (!pCP->Load(pReader)) {
- m_pos = stop;
- return false;
- }
- ++m_count;
- --m_preload_count;
-
- m_pos += size; // consume payload
- if (m_pos > stop)
- return false;
-
- return true; // yes, we loaded a cue point
- }
-
- return false; // no, we did not load a cue point
-}
-
-bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
- const CuePoint::TrackPosition*& pTP) const {
- if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0)
- return false;
-
- CuePoint** const ii = m_cue_points;
- CuePoint** i = ii;
-
- CuePoint** const jj = ii + m_count;
- CuePoint** j = jj;
-
- pCP = *i;
- if (pCP == NULL)
- return false;
-
- if (time_ns <= pCP->GetTime(m_pSegment)) {
- pTP = pCP->Find(pTrack);
- return (pTP != NULL);
- }
-
- while (i < j) {
- // INVARIANT:
- //[ii, i) <= time_ns
- //[i, j) ?
- //[j, jj) > time_ns
-
- CuePoint** const k = i + (j - i) / 2;
- if (k >= jj)
- return false;
-
- CuePoint* const pCP = *k;
- if (pCP == NULL)
- return false;
-
- const long long t = pCP->GetTime(m_pSegment);
-
- if (t <= time_ns)
- i = k + 1;
- else
- j = k;
-
- if (i > j)
- return false;
- }
-
- if (i != j || i > jj || i <= ii)
- return false;
-
- pCP = *--i;
-
- if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns)
- return false;
-
- // TODO: here and elsewhere, it's probably not correct to search
- // for the cue point with this time, and then search for a matching
- // track. In principle, the matching track could be on some earlier
- // cue point, and with our current algorithm, we'd miss it. To make
- // this bullet-proof, we'd need to create a secondary structure,
- // with a list of cue points that apply to a track, and then search
- // that track-based structure for a matching cue point.
-
- pTP = pCP->Find(pTrack);
- return (pTP != NULL);
-}
-
-const CuePoint* Cues::GetFirst() const {
- if (m_cue_points == NULL || m_count == 0)
- return NULL;
-
- CuePoint* const* const pp = m_cue_points;
- if (pp == NULL)
- return NULL;
-
- CuePoint* const pCP = pp[0];
- if (pCP == NULL || pCP->GetTimeCode() < 0)
- return NULL;
-
- return pCP;
-}
-
-const CuePoint* Cues::GetLast() const {
- if (m_cue_points == NULL || m_count <= 0)
- return NULL;
-
- const long index = m_count - 1;
-
- CuePoint* const* const pp = m_cue_points;
- if (pp == NULL)
- return NULL;
-
- CuePoint* const pCP = pp[index];
- if (pCP == NULL || pCP->GetTimeCode() < 0)
- return NULL;
-
- return pCP;
-}
-
-const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
- if (pCurr == NULL || pCurr->GetTimeCode() < 0 ||
- m_cue_points == NULL || m_count < 1) {
- return NULL;
- }
-
- long index = pCurr->m_index;
- if (index >= m_count)
- return NULL;
-
- CuePoint* const* const pp = m_cue_points;
- if (pp == NULL || pp[index] != pCurr)
- return NULL;
-
- ++index;
-
- if (index >= m_count)
- return NULL;
-
- CuePoint* const pNext = pp[index];
-
- if (pNext == NULL || pNext->GetTimeCode() < 0)
- return NULL;
-
- return pNext;
-}
-
-const BlockEntry* Cues::GetBlock(const CuePoint* pCP,
- const CuePoint::TrackPosition* pTP) const {
- if (pCP == NULL || pTP == NULL)
- return NULL;
-
- return m_pSegment->GetBlock(*pCP, *pTP);
-}
-
-const BlockEntry* Segment::GetBlock(const CuePoint& cp,
- const CuePoint::TrackPosition& tp) {
- Cluster** const ii = m_clusters;
- Cluster** i = ii;
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- Cluster** const jj = ii + count;
- Cluster** j = jj;
-
- while (i < j) {
- // INVARIANT:
- //[ii, i) < pTP->m_pos
- //[i, j) ?
- //[j, jj) > pTP->m_pos
-
- Cluster** const k = i + (j - i) / 2;
- assert(k < jj);
-
- Cluster* const pCluster = *k;
- assert(pCluster);
-
- // const long long pos_ = pCluster->m_pos;
- // assert(pos_);
- // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
-
- const long long pos = pCluster->GetPosition();
- assert(pos >= 0);
-
- if (pos < tp.m_pos)
- i = k + 1;
- else if (pos > tp.m_pos)
- j = k;
- else
- return pCluster->GetEntry(cp, tp);
- }
-
- assert(i == j);
- // assert(Cluster::HasBlockEntries(this, tp.m_pos));
-
- Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos); //, -1);
- if (pCluster == NULL)
- return NULL;
-
- const ptrdiff_t idx = i - m_clusters;
-
- if (!PreloadCluster(pCluster, idx)) {
- delete pCluster;
- return NULL;
- }
- assert(m_clusters);
- assert(m_clusterPreloadCount > 0);
- assert(m_clusters[idx] == pCluster);
-
- return pCluster->GetEntry(cp, tp);
-}
-
-const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) {
- if (requested_pos < 0)
- return 0;
-
- Cluster** const ii = m_clusters;
- Cluster** i = ii;
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- Cluster** const jj = ii + count;
- Cluster** j = jj;
-
- while (i < j) {
- // INVARIANT:
- //[ii, i) < pTP->m_pos
- //[i, j) ?
- //[j, jj) > pTP->m_pos
-
- Cluster** const k = i + (j - i) / 2;
- assert(k < jj);
-
- Cluster* const pCluster = *k;
- assert(pCluster);
-
- // const long long pos_ = pCluster->m_pos;
- // assert(pos_);
- // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
-
- const long long pos = pCluster->GetPosition();
- assert(pos >= 0);
-
- if (pos < requested_pos)
- i = k + 1;
- else if (pos > requested_pos)
- j = k;
- else
- return pCluster;
- }
-
- assert(i == j);
- // assert(Cluster::HasBlockEntries(this, tp.m_pos));
-
- Cluster* const pCluster = Cluster::Create(this, -1, requested_pos);
- if (pCluster == NULL)
- return NULL;
-
- const ptrdiff_t idx = i - m_clusters;
-
- if (!PreloadCluster(pCluster, idx)) {
- delete pCluster;
- return NULL;
- }
- assert(m_clusters);
- assert(m_clusterPreloadCount > 0);
- assert(m_clusters[idx] == pCluster);
-
- return pCluster;
-}
-
-CuePoint::CuePoint(long idx, long long pos)
- : m_element_start(0),
- m_element_size(0),
- m_index(idx),
- m_timecode(-1 * pos),
- m_track_positions(NULL),
- m_track_positions_count(0) {
- assert(pos > 0);
-}
-
-CuePoint::~CuePoint() { delete[] m_track_positions; }
-
-bool CuePoint::Load(IMkvReader* pReader) {
- // odbgstream os;
- // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
-
- if (m_timecode >= 0) // already loaded
- return true;
-
- assert(m_track_positions == NULL);
- assert(m_track_positions_count == 0);
-
- long long pos_ = -m_timecode;
- const long long element_start = pos_;
-
- long long stop;
-
- {
- long len;
-
- const long long id = ReadID(pReader, pos_, len);
- if (id != mkvmuxer::kMkvCuePoint)
- return false;
-
- pos_ += len; // consume ID
-
- const long long size = ReadUInt(pReader, pos_, len);
- assert(size >= 0);
-
- pos_ += len; // consume Size field
- // pos_ now points to start of payload
-
- stop = pos_ + size;
- }
-
- const long long element_size = stop - element_start;
-
- long long pos = pos_;
-
- // First count number of track positions
-
- while (pos < stop) {
- long len;
-
- const long long id = ReadID(pReader, pos, len);
- if ((id < 0) || (pos + len > stop)) {
- return false;
- }
-
- pos += len; // consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- if ((size < 0) || (pos + len > stop)) {
- return false;
- }
-
- pos += len; // consume Size field
- if ((pos + size) > stop) {
- return false;
- }
-
- if (id == mkvmuxer::kMkvCueTime)
- m_timecode = UnserializeUInt(pReader, pos, size);
-
- else if (id == mkvmuxer::kMkvCueTrackPositions)
- ++m_track_positions_count;
-
- pos += size; // consume payload
- }
-
- if (m_timecode < 0 || m_track_positions_count <= 0) {
- return false;
- }
-
- // os << "CuePoint::Load(cont'd): idpos=" << idpos
- // << " timecode=" << m_timecode
- // << endl;
-
- m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
- if (m_track_positions == NULL)
- return false;
-
- // Now parse track positions
-
- TrackPosition* p = m_track_positions;
- pos = pos_;
-
- while (pos < stop) {
- long len;
-
- const long long id = ReadID(pReader, pos, len);
- 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);
-
- pos += len; // consume Size field
- assert((pos + size) <= stop);
-
- if (id == mkvmuxer::kMkvCueTrackPositions) {
- TrackPosition& tp = *p++;
- if (!tp.Parse(pReader, pos, size)) {
- return false;
- }
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return false;
- }
-
- assert(size_t(p - m_track_positions) == m_track_positions_count);
-
- m_element_start = element_start;
- m_element_size = element_size;
-
- return true;
-}
-
-bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
- long long size_) {
- const long long stop = start_ + size_;
- long long pos = start_;
-
- m_track = -1;
- m_pos = -1;
- m_block = 1; // default
-
- while (pos < stop) {
- long len;
-
- const long long id = ReadID(pReader, pos, len);
- if ((id < 0) || ((pos + len) > stop)) {
- return false;
- }
-
- pos += len; // consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- if ((size < 0) || ((pos + len) > stop)) {
- return false;
- }
-
- pos += len; // consume Size field
- if ((pos + size) > stop) {
- return false;
- }
-
- if (id == mkvmuxer::kMkvCueTrack)
- m_track = UnserializeUInt(pReader, pos, size);
- else if (id == mkvmuxer::kMkvCueClusterPosition)
- m_pos = UnserializeUInt(pReader, pos, size);
- else if (id == mkvmuxer::kMkvCueBlockNumber)
- m_block = UnserializeUInt(pReader, pos, size);
-
- pos += size; // consume payload
- }
-
- if ((m_pos < 0) || (m_track <= 0)) {
- return false;
- }
-
- return true;
-}
-
-const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
- assert(pTrack);
-
- const long long n = pTrack->GetNumber();
-
- const TrackPosition* i = m_track_positions;
- const TrackPosition* const j = i + m_track_positions_count;
-
- while (i != j) {
- const TrackPosition& p = *i++;
-
- if (p.m_track == n)
- return &p;
- }
-
- return NULL; // no matching track number found
-}
-
-long long CuePoint::GetTimeCode() const { return m_timecode; }
-
-long long CuePoint::GetTime(const Segment* pSegment) const {
- assert(pSegment);
- assert(m_timecode >= 0);
-
- const SegmentInfo* const pInfo = pSegment->GetInfo();
- assert(pInfo);
-
- const long long scale = pInfo->GetTimeCodeScale();
- assert(scale >= 1);
-
- const long long time = scale * m_timecode;
-
- return time;
-}
-
-bool Segment::DoneParsing() const {
- if (m_size < 0) {
- long long total, avail;
-
- const int status = m_pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return true; // must assume done
-
- if (total < 0)
- return false; // assume live stream
-
- return (m_pos >= total);
- }
-
- const long long stop = m_start + m_size;
-
- return (m_pos >= stop);
-}
-
-const Cluster* Segment::GetFirst() const {
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return &m_eos;
-
- Cluster* const pCluster = m_clusters[0];
- assert(pCluster);
-
- return pCluster;
-}
-
-const Cluster* Segment::GetLast() const {
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return &m_eos;
-
- const long idx = m_clusterCount - 1;
-
- Cluster* const pCluster = m_clusters[idx];
- assert(pCluster);
-
- return pCluster;
-}
-
-unsigned long Segment::GetCount() const { return m_clusterCount; }
-
-const Cluster* Segment::GetNext(const Cluster* pCurr) {
- assert(pCurr);
- assert(pCurr != &m_eos);
- assert(m_clusters);
-
- long idx = pCurr->m_index;
-
- if (idx >= 0) {
- assert(m_clusterCount > 0);
- assert(idx < m_clusterCount);
- assert(pCurr == m_clusters[idx]);
-
- ++idx;
-
- if (idx >= m_clusterCount)
- return &m_eos; // caller will LoadCluster as desired
-
- Cluster* const pNext = m_clusters[idx];
- assert(pNext);
- assert(pNext->m_index >= 0);
- assert(pNext->m_index == idx);
-
- return pNext;
- }
-
- assert(m_clusterPreloadCount > 0);
-
- long long pos = pCurr->m_element_start;
-
- assert(m_size >= 0); // TODO
- const long long stop = m_start + m_size; // end of segment
-
- {
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0);
- assert((pos + len) <= stop); // TODO
- if (result != 0)
- return NULL;
-
- const long long id = ReadID(m_pReader, pos, len);
- if (id != mkvmuxer::kMkvCluster)
- return NULL;
-
- pos += len; // consume ID
-
- // Read Size
- result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0); // TODO
- assert((pos + len) <= stop); // TODO
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size > 0); // TODO
- // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
-
- pos += len; // consume length of size of element
- assert((pos + size) <= stop); // TODO
-
- // Pos now points to start of payload
-
- pos += size; // consume payload
- }
-
- long long off_next = 0;
-
- while (pos < stop) {
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0);
- assert((pos + len) <= stop); // TODO
- if (result != 0)
- return NULL;
-
- const long long idpos = pos; // pos of next (potential) cluster
-
- const long long id = ReadID(m_pReader, idpos, len);
- if (id < 0)
- return NULL;
-
- pos += len; // consume ID
-
- // Read Size
- result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0); // TODO
- assert((pos + len) <= stop); // TODO
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size >= 0); // TODO
-
- pos += len; // consume length of size of element
- assert((pos + size) <= stop); // TODO
-
- // Pos now points to start of payload
-
- if (size == 0) // weird
- continue;
-
- if (id == mkvmuxer::kMkvCluster) {
- const long long off_next_ = idpos - m_start;
-
- long long pos_;
- long len_;
-
- const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_);
-
- assert(status >= 0);
-
- if (status > 0) {
- off_next = off_next_;
- break;
- }
- }
-
- pos += size; // consume payload
- }
-
- if (off_next <= 0)
- return 0;
-
- Cluster** const ii = m_clusters + m_clusterCount;
- Cluster** i = ii;
-
- Cluster** const jj = ii + m_clusterPreloadCount;
- Cluster** j = jj;
-
- while (i < j) {
- // INVARIANT:
- //[0, i) < pos_next
- //[i, j) ?
- //[j, jj) > pos_next
-
- Cluster** const k = i + (j - i) / 2;
- assert(k < jj);
-
- Cluster* const pNext = *k;
- assert(pNext);
- assert(pNext->m_index < 0);
-
- // const long long pos_ = pNext->m_pos;
- // assert(pos_);
- // pos = pos_ * ((pos_ < 0) ? -1 : 1);
-
- pos = pNext->GetPosition();
-
- if (pos < off_next)
- i = k + 1;
- else if (pos > off_next)
- j = k;
- else
- return pNext;
- }
-
- assert(i == j);
-
- Cluster* const pNext = Cluster::Create(this, -1, off_next);
- if (pNext == NULL)
- return NULL;
-
- const ptrdiff_t idx_next = i - m_clusters; // insertion position
-
- if (!PreloadCluster(pNext, idx_next)) {
- delete pNext;
- return NULL;
- }
- assert(m_clusters);
- assert(idx_next < m_clusterSize);
- assert(m_clusters[idx_next] == pNext);
-
- return pNext;
-}
-
-long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult,
- long long& pos, long& len) {
- assert(pCurr);
- assert(!pCurr->EOS());
- assert(m_clusters);
-
- pResult = 0;
-
- if (pCurr->m_index >= 0) { // loaded (not merely preloaded)
- assert(m_clusters[pCurr->m_index] == pCurr);
-
- const long next_idx = pCurr->m_index + 1;
-
- if (next_idx < m_clusterCount) {
- pResult = m_clusters[next_idx];
- return 0; // success
- }
-
- // curr cluster is last among loaded
-
- const long result = LoadCluster(pos, len);
-
- if (result < 0) // error or underflow
- return result;
-
- if (result > 0) // no more clusters
- {
- // pResult = &m_eos;
- return 1;
- }
-
- pResult = GetLast();
- return 0; // success
- }
-
- assert(m_pos > 0);
-
- 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;
-
- // interrogate curr cluster
-
- pos = pCurr->m_element_start;
-
- if (pCurr->m_element_size >= 0)
- pos += pCurr->m_element_size;
- else {
- 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 id = ReadUInt(m_pReader, pos, len);
-
- if (id != mkvmuxer::kMkvCluster)
- return -1;
-
- pos += len; // consume ID
-
- // 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
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size) // TODO: should never happen
- return E_FILE_FORMAT_INVALID; // TODO: resolve this
-
- // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
-
- if ((segment_stop >= 0) && ((pos + size) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- // Pos now points to start of payload
-
- pos += size; // consume payload (that is, the current cluster)
- if (segment_stop >= 0 && pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
-
- // By consuming the payload, we are assuming that the curr
- // cluster isn't interesting. That is, we don't bother checking
- // whether the payload of the curr cluster is less than what
- // happens to be available (obtained via IMkvReader::Length).
- // Presumably the caller has already dispensed with the current
- // cluster, and really does want the next cluster.
- }
-
- // pos now points to just beyond the last fully-loaded cluster
-
- for (;;) {
- const long status = DoParseNext(pResult, pos, len);
-
- if (status <= 1)
- return status;
- }
-}
-
-long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
- 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;
-
- // Parse next cluster. This is strictly a parsing activity.
- // Creation of a new cluster object happens later, after the
- // parsing is done.
-
- long long off_next = 0;
- long long cluster_size = -1;
-
- for (;;) {
- if ((total >= 0) && (pos >= total))
- return 1; // EOF
-
- if ((segment_stop >= 0) && (pos >= segment_stop))
- return 1; // EOF
-
- 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; // absolute
- const long long idoff = pos - m_start; // relative
-
- const long long id = ReadID(m_pReader, idpos, len); // absolute
-
- if (id < 0) // error
- return static_cast<long>(id);
-
- if (id == 0) // weird
- return -1; // generic error
-
- pos += len; // consume ID
-
- // 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 length of size of element
-
- // Pos now points to start of payload
-
- if (size == 0) // weird
- continue;
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if ((segment_stop >= 0) && (size != unknown_size) &&
- ((pos + size) > segment_stop)) {
- return E_FILE_FORMAT_INVALID;
- }
-
- if (id == mkvmuxer::kMkvCues) {
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- const long long element_stop = pos + size;
-
- if ((segment_stop >= 0) && (element_stop > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- const long long element_start = idpos;
- const long long element_size = element_stop - element_start;
-
- if (m_pCues == NULL) {
- m_pCues = new (std::nothrow)
- Cues(this, pos, size, element_start, element_size);
- if (m_pCues == NULL)
- return false;
- }
-
- pos += size; // consume payload
- if (segment_stop >= 0 && pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
-
- continue;
- }
-
- if (id != mkvmuxer::kMkvCluster) { // not a Cluster ID
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- pos += size; // consume payload
- if (segment_stop >= 0 && pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
-
- continue;
- }
-
- // We have a cluster.
- off_next = idoff;
-
- if (size != unknown_size)
- cluster_size = size;
-
- break;
- }
-
- assert(off_next > 0); // have cluster
-
- // We have parsed the next cluster.
- // We have not created a cluster object yet. What we need
- // to do now is determine whether it has already be preloaded
- //(in which case, an object for this cluster has already been
- // created), and if not, create a new cluster object.
-
- Cluster** const ii = m_clusters + m_clusterCount;
- Cluster** i = ii;
-
- Cluster** const jj = ii + m_clusterPreloadCount;
- Cluster** j = jj;
-
- while (i < j) {
- // INVARIANT:
- //[0, i) < pos_next
- //[i, j) ?
- //[j, jj) > pos_next
-
- Cluster** const k = i + (j - i) / 2;
- assert(k < jj);
-
- const Cluster* const pNext = *k;
- assert(pNext);
- assert(pNext->m_index < 0);
-
- pos = pNext->GetPosition();
- assert(pos >= 0);
-
- if (pos < off_next)
- i = k + 1;
- else if (pos > off_next)
- j = k;
- else {
- pResult = pNext;
- return 0; // success
- }
- }
-
- assert(i == j);
-
- long long pos_;
- long len_;
-
- status = Cluster::HasBlockEntries(this, off_next, pos_, len_);
-
- if (status < 0) { // error or underflow
- pos = pos_;
- len = len_;
-
- return status;
- }
-
- if (status > 0) { // means "found at least one block entry"
- Cluster* const pNext = Cluster::Create(this,
- -1, // preloaded
- off_next);
- if (pNext == NULL)
- return -1;
-
- const ptrdiff_t idx_next = i - m_clusters; // insertion position
-
- if (!PreloadCluster(pNext, idx_next)) {
- delete pNext;
- return -1;
- }
- assert(m_clusters);
- assert(idx_next < m_clusterSize);
- assert(m_clusters[idx_next] == pNext);
-
- pResult = pNext;
- return 0; // success
- }
-
- // status == 0 means "no block entries found"
-
- 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 = ReadID(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 == mkvmuxer::kMkvCluster || id == mkvmuxer::kMkvCues)
- 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
- if (segment_stop >= 0 && pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
- } // determine cluster size
-
- cluster_size = pos - payload_pos;
- assert(cluster_size >= 0); // TODO: handle cluster_size = 0
-
- pos = payload_pos; // reset and re-parse original cluster
- }
-
- pos += cluster_size; // consume payload
- if (segment_stop >= 0 && pos > segment_stop)
- return E_FILE_FORMAT_INVALID;
-
- return 2; // try to find a cluster that follows next
-}
-
-const Cluster* Segment::FindCluster(long long time_ns) const {
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return &m_eos;
-
- {
- Cluster* const pCluster = m_clusters[0];
- assert(pCluster);
- assert(pCluster->m_index == 0);
-
- if (time_ns <= pCluster->GetTime())
- return pCluster;
- }
-
- // Binary search of cluster array
-
- long i = 0;
- long j = m_clusterCount;
-
- while (i < j) {
- // INVARIANT:
- //[0, i) <= time_ns
- //[i, j) ?
- //[j, m_clusterCount) > time_ns
-
- const long k = i + (j - i) / 2;
- assert(k < m_clusterCount);
-
- Cluster* const pCluster = m_clusters[k];
- assert(pCluster);
- assert(pCluster->m_index == k);
-
- const long long t = pCluster->GetTime();
-
- if (t <= time_ns)
- i = k + 1;
- else
- j = k;
-
- assert(i <= j);
- }
-
- assert(i == j);
- assert(i > 0);
- assert(i <= m_clusterCount);
-
- const long k = i - 1;
-
- Cluster* const pCluster = m_clusters[k];
- assert(pCluster);
- assert(pCluster->m_index == k);
- assert(pCluster->GetTime() <= time_ns);
-
- return pCluster;
-}
-
-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 {
- assert(m_pInfo);
- return m_pInfo->GetDuration();
-}
-
-Chapters::Chapters(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_editions(NULL),
- m_editions_size(0),
- m_editions_count(0) {}
-
-Chapters::~Chapters() {
- while (m_editions_count > 0) {
- Edition& e = m_editions[--m_editions_count];
- e.Clear();
- }
- delete[] m_editions;
-}
-
-long Chapters::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) // error
- return status;
-
- if (size == 0) // weird
- continue;
-
- if (id == mkvmuxer::kMkvEditionEntry) {
- status = ParseEdition(pos, size);
-
- if (status < 0) // error
- return status;
- }
-
- pos += size;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- return 0;
-}
-
-int Chapters::GetEditionCount() const { return m_editions_count; }
-
-const Chapters::Edition* Chapters::GetEdition(int idx) const {
- if (idx < 0)
- return NULL;
-
- if (idx >= m_editions_count)
- return NULL;
-
- return m_editions + idx;
-}
-
-bool Chapters::ExpandEditionsArray() {
- if (m_editions_size > m_editions_count)
- return true; // nothing else to do
-
- const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
-
- Edition* const editions = new (std::nothrow) Edition[size];
-
- if (editions == NULL)
- return false;
-
- for (int idx = 0; idx < m_editions_count; ++idx) {
- m_editions[idx].ShallowCopy(editions[idx]);
- }
-
- delete[] m_editions;
- m_editions = editions;
-
- m_editions_size = size;
- return true;
-}
-
-long Chapters::ParseEdition(long long pos, long long size) {
- if (!ExpandEditionsArray())
- return -1;
-
- Edition& e = m_editions[m_editions_count++];
- e.Init();
-
- return e.Parse(m_pSegment->m_pReader, pos, size);
-}
-
-Chapters::Edition::Edition() {}
-
-Chapters::Edition::~Edition() {}
-
-int Chapters::Edition::GetAtomCount() const { return m_atoms_count; }
-
-const Chapters::Atom* Chapters::Edition::GetAtom(int index) const {
- if (index < 0)
- return NULL;
-
- if (index >= m_atoms_count)
- return NULL;
-
- return m_atoms + index;
-}
-
-void Chapters::Edition::Init() {
- m_atoms = NULL;
- m_atoms_size = 0;
- m_atoms_count = 0;
-}
-
-void Chapters::Edition::ShallowCopy(Edition& rhs) const {
- rhs.m_atoms = m_atoms;
- rhs.m_atoms_size = m_atoms_size;
- rhs.m_atoms_count = m_atoms_count;
-}
-
-void Chapters::Edition::Clear() {
- while (m_atoms_count > 0) {
- Atom& a = m_atoms[--m_atoms_count];
- a.Clear();
- }
-
- delete[] m_atoms;
- m_atoms = NULL;
-
- m_atoms_size = 0;
-}
-
-long Chapters::Edition::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)
- continue;
-
- if (id == mkvmuxer::kMkvChapterAtom) {
- status = ParseAtom(pReader, pos, size);
-
- if (status < 0) // error
- return status;
- }
-
- pos += size;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- return 0;
-}
-
-long Chapters::Edition::ParseAtom(IMkvReader* pReader, long long pos,
- long long size) {
- if (!ExpandAtomsArray())
- return -1;
-
- Atom& a = m_atoms[m_atoms_count++];
- a.Init();
-
- return a.Parse(pReader, pos, size);
-}
-
-bool Chapters::Edition::ExpandAtomsArray() {
- if (m_atoms_size > m_atoms_count)
- return true; // nothing else to do
-
- const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
-
- Atom* const atoms = new (std::nothrow) Atom[size];
-
- if (atoms == NULL)
- return false;
-
- for (int idx = 0; idx < m_atoms_count; ++idx) {
- m_atoms[idx].ShallowCopy(atoms[idx]);
- }
-
- delete[] m_atoms;
- m_atoms = atoms;
-
- m_atoms_size = size;
- return true;
-}
-
-Chapters::Atom::Atom() {}
-
-Chapters::Atom::~Atom() {}
-
-unsigned long long Chapters::Atom::GetUID() const { return m_uid; }
-
-const char* Chapters::Atom::GetStringUID() const { return m_string_uid; }
-
-long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; }
-
-long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; }
-
-long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const {
- return GetTime(pChapters, m_start_timecode);
-}
-
-long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const {
- return GetTime(pChapters, m_stop_timecode);
-}
-
-int Chapters::Atom::GetDisplayCount() const { return m_displays_count; }
-
-const Chapters::Display* Chapters::Atom::GetDisplay(int index) const {
- if (index < 0)
- return NULL;
-
- if (index >= m_displays_count)
- return NULL;
-
- return m_displays + index;
-}
-
-void Chapters::Atom::Init() {
- m_string_uid = NULL;
- m_uid = 0;
- m_start_timecode = -1;
- m_stop_timecode = -1;
-
- m_displays = NULL;
- m_displays_size = 0;
- m_displays_count = 0;
-}
-
-void Chapters::Atom::ShallowCopy(Atom& rhs) const {
- rhs.m_string_uid = m_string_uid;
- rhs.m_uid = m_uid;
- rhs.m_start_timecode = m_start_timecode;
- rhs.m_stop_timecode = m_stop_timecode;
-
- rhs.m_displays = m_displays;
- rhs.m_displays_size = m_displays_size;
- rhs.m_displays_count = m_displays_count;
-}
-
-void Chapters::Atom::Clear() {
- delete[] m_string_uid;
- m_string_uid = NULL;
-
- while (m_displays_count > 0) {
- Display& d = m_displays[--m_displays_count];
- d.Clear();
- }
-
- delete[] m_displays;
- m_displays = NULL;
-
- m_displays_size = 0;
-}
-
-long Chapters::Atom::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) // 0 length payload, skip.
- continue;
-
- if (id == mkvmuxer::kMkvChapterDisplay) {
- status = ParseDisplay(pReader, pos, size);
-
- if (status < 0) // error
- return status;
- } else if (id == mkvmuxer::kMkvChapterStringUID) {
- status = UnserializeString(pReader, pos, size, m_string_uid);
-
- if (status < 0) // error
- return status;
- } else if (id == mkvmuxer::kMkvChapterUID) {
- long long val;
- status = UnserializeInt(pReader, pos, size, val);
-
- if (status < 0) // error
- return status;
-
- m_uid = static_cast<unsigned long long>(val);
- } else if (id == mkvmuxer::kMkvChapterTimeStart) {
- const long long val = UnserializeUInt(pReader, pos, size);
-
- if (val < 0) // error
- return static_cast<long>(val);
-
- m_start_timecode = val;
- } else if (id == mkvmuxer::kMkvChapterTimeEnd) {
- const long long val = UnserializeUInt(pReader, pos, size);
-
- if (val < 0) // error
- return static_cast<long>(val);
-
- m_stop_timecode = val;
- }
-
- pos += size;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- return 0;
-}
-
-long long Chapters::Atom::GetTime(const Chapters* pChapters,
- long long timecode) {
- if (pChapters == NULL)
- return -1;
-
- Segment* const pSegment = pChapters->m_pSegment;
-
- if (pSegment == NULL) // weird
- return -1;
-
- const SegmentInfo* const pInfo = pSegment->GetInfo();
-
- if (pInfo == NULL)
- return -1;
-
- const long long timecode_scale = pInfo->GetTimeCodeScale();
-
- if (timecode_scale < 1) // weird
- return -1;
-
- if (timecode < 0)
- return -1;
-
- const long long result = timecode_scale * timecode;
-
- return result;
-}
-
-long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
- long long size) {
- if (!ExpandDisplaysArray())
- return -1;
-
- Display& d = m_displays[m_displays_count++];
- d.Init();
-
- return d.Parse(pReader, pos, size);
-}
-
-bool Chapters::Atom::ExpandDisplaysArray() {
- if (m_displays_size > m_displays_count)
- return true; // nothing else to do
-
- const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
-
- Display* const displays = new (std::nothrow) Display[size];
-
- if (displays == NULL)
- return false;
-
- for (int idx = 0; idx < m_displays_count; ++idx) {
- m_displays[idx].ShallowCopy(displays[idx]);
- }
-
- delete[] m_displays;
- m_displays = displays;
-
- m_displays_size = size;
- return true;
-}
-
-Chapters::Display::Display() {}
-
-Chapters::Display::~Display() {}
-
-const char* Chapters::Display::GetString() const { return m_string; }
-
-const char* Chapters::Display::GetLanguage() const { return m_language; }
-
-const char* Chapters::Display::GetCountry() const { return m_country; }
-
-void Chapters::Display::Init() {
- m_string = NULL;
- m_language = NULL;
- m_country = NULL;
-}
-
-void Chapters::Display::ShallowCopy(Display& rhs) const {
- rhs.m_string = m_string;
- rhs.m_language = m_language;
- rhs.m_country = m_country;
-}
-
-void Chapters::Display::Clear() {
- delete[] m_string;
- m_string = NULL;
-
- delete[] m_language;
- m_language = NULL;
-
- delete[] m_country;
- m_country = NULL;
-}
-
-long Chapters::Display::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) // No payload.
- continue;
-
- if (id == mkvmuxer::kMkvChapString) {
- status = UnserializeString(pReader, pos, size, m_string);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvChapLanguage) {
- status = UnserializeString(pReader, pos, size, m_language);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvChapCountry) {
- status = UnserializeString(pReader, pos, size, m_country);
-
- if (status)
- return status;
- }
-
- pos += size;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- 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 == mkvmuxer::kMkvTag) {
- status = ParseTag(pos, size);
-
- if (status < 0)
- return status;
- }
-
- pos += size;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- 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 == mkvmuxer::kMkvSimpleTag) {
- status = ParseSimpleTag(pReader, pos, size);
-
- if (status < 0)
- return status;
- }
-
- pos += size;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- 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 == mkvmuxer::kMkvTagName) {
- status = UnserializeString(pReader, pos, size, m_tag_name);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvTagString) {
- status = UnserializeString(pReader, pos, size, m_tag_string);
-
- if (status)
- return status;
- }
-
- pos += size;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- return 0;
-}
-
-SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
- long long element_start, long long element_size)
- : m_pSegment(pSegment),
- m_start(start),
- m_size(size_),
- m_element_start(element_start),
- m_element_size(element_size),
- m_pMuxingAppAsUTF8(NULL),
- m_pWritingAppAsUTF8(NULL),
- m_pTitleAsUTF8(NULL) {}
-
-SegmentInfo::~SegmentInfo() {
- delete[] m_pMuxingAppAsUTF8;
- m_pMuxingAppAsUTF8 = NULL;
-
- delete[] m_pWritingAppAsUTF8;
- m_pWritingAppAsUTF8 = NULL;
-
- delete[] m_pTitleAsUTF8;
- m_pTitleAsUTF8 = NULL;
-}
-
-long SegmentInfo::Parse() {
- assert(m_pMuxingAppAsUTF8 == NULL);
- assert(m_pWritingAppAsUTF8 == NULL);
- assert(m_pTitleAsUTF8 == NULL);
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long pos = m_start;
- const long long stop = m_start + m_size;
-
- m_timecodeScale = 1000000;
- m_duration = -1;
-
- while (pos < stop) {
- long long id, size;
-
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
-
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvTimecodeScale) {
- m_timecodeScale = UnserializeUInt(pReader, pos, size);
-
- if (m_timecodeScale <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvDuration) {
- const long status = UnserializeFloat(pReader, pos, size, m_duration);
-
- if (status < 0)
- return status;
-
- if (m_duration < 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvMuxingApp) {
- const long status =
- UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvWritingApp) {
- const long status =
- UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvTitle) {
- const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8);
-
- if (status)
- return status;
- }
-
- pos += size;
-
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- const double rollover_check = m_duration * m_timecodeScale;
- if (rollover_check > LLONG_MAX)
- return E_FILE_FORMAT_INVALID;
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- return 0;
-}
-
-long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; }
-
-long long SegmentInfo::GetDuration() const {
- if (m_duration < 0)
- return -1;
-
- assert(m_timecodeScale >= 1);
-
- const double dd = double(m_duration) * double(m_timecodeScale);
- const long long d = static_cast<long long>(dd);
-
- return d;
-}
-
-const char* SegmentInfo::GetMuxingAppAsUTF8() const {
- return m_pMuxingAppAsUTF8;
-}
-
-const char* SegmentInfo::GetWritingAppAsUTF8() const {
- return m_pWritingAppAsUTF8;
-}
-
-const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; }
-
-///////////////////////////////////////////////////////////////
-// ContentEncoding element
-ContentEncoding::ContentCompression::ContentCompression()
- : algo(0), settings(NULL), settings_len(0) {}
-
-ContentEncoding::ContentCompression::~ContentCompression() {
- delete[] settings;
-}
-
-ContentEncoding::ContentEncryption::ContentEncryption()
- : algo(0),
- key_id(NULL),
- key_id_len(0),
- signature(NULL),
- signature_len(0),
- sig_key_id(NULL),
- sig_key_id_len(0),
- sig_algo(0),
- sig_hash_algo(0) {}
-
-ContentEncoding::ContentEncryption::~ContentEncryption() {
- delete[] key_id;
- delete[] signature;
- delete[] sig_key_id;
-}
-
-ContentEncoding::ContentEncoding()
- : compression_entries_(NULL),
- compression_entries_end_(NULL),
- encryption_entries_(NULL),
- encryption_entries_end_(NULL),
- encoding_order_(0),
- encoding_scope_(1),
- encoding_type_(0) {}
-
-ContentEncoding::~ContentEncoding() {
- ContentCompression** comp_i = compression_entries_;
- ContentCompression** const comp_j = compression_entries_end_;
-
- while (comp_i != comp_j) {
- ContentCompression* const comp = *comp_i++;
- delete comp;
- }
-
- delete[] compression_entries_;
-
- ContentEncryption** enc_i = encryption_entries_;
- ContentEncryption** const enc_j = encryption_entries_end_;
-
- while (enc_i != enc_j) {
- ContentEncryption* const enc = *enc_i++;
- delete enc;
- }
-
- delete[] encryption_entries_;
-}
-
-const ContentEncoding::ContentCompression*
- ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
- const ptrdiff_t count = compression_entries_end_ - compression_entries_;
- assert(count >= 0);
-
- if (idx >= static_cast<unsigned long>(count))
- return NULL;
-
- return compression_entries_[idx];
-}
-
-unsigned long ContentEncoding::GetCompressionCount() const {
- const ptrdiff_t count = compression_entries_end_ - compression_entries_;
- assert(count >= 0);
-
- return static_cast<unsigned long>(count);
-}
-
-const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex(
- unsigned long idx) const {
- const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
- assert(count >= 0);
-
- if (idx >= static_cast<unsigned long>(count))
- return NULL;
-
- return encryption_entries_[idx];
-}
-
-unsigned long ContentEncoding::GetEncryptionCount() const {
- const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
- assert(count >= 0);
-
- return static_cast<unsigned long>(count);
-}
-
-long ContentEncoding::ParseContentEncAESSettingsEntry(
- long long start, long long size, IMkvReader* pReader,
- ContentEncAESSettings* aes) {
- assert(pReader);
- assert(aes);
-
- long long pos = start;
- const long long stop = start + size;
-
- while (pos < stop) {
- long long id, size;
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvAESSettingsCipherMode) {
- aes->cipher_mode = UnserializeUInt(pReader, pos, size);
- if (aes->cipher_mode != 1)
- return E_FILE_FORMAT_INVALID;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- return 0;
-}
-
-long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
- IMkvReader* pReader) {
- assert(pReader);
-
- long long pos = start;
- const long long stop = start + size;
-
- // Count ContentCompression and ContentEncryption elements.
- int compression_count = 0;
- int encryption_count = 0;
-
- while (pos < stop) {
- long long id, size;
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvContentCompression)
- ++compression_count;
-
- if (id == mkvmuxer::kMkvContentEncryption)
- ++encryption_count;
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (compression_count <= 0 && encryption_count <= 0)
- return -1;
-
- if (compression_count > 0) {
- compression_entries_ =
- new (std::nothrow) ContentCompression*[compression_count];
- if (!compression_entries_)
- return -1;
- compression_entries_end_ = compression_entries_;
- }
-
- if (encryption_count > 0) {
- encryption_entries_ =
- new (std::nothrow) ContentEncryption*[encryption_count];
- if (!encryption_entries_) {
- delete[] compression_entries_;
- return -1;
- }
- encryption_entries_end_ = encryption_entries_;
- }
-
- pos = start;
- while (pos < stop) {
- long long id, size;
- long status = ParseElementHeader(pReader, pos, stop, id, size);
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvContentEncodingOrder) {
- encoding_order_ = UnserializeUInt(pReader, pos, size);
- } else if (id == mkvmuxer::kMkvContentEncodingScope) {
- encoding_scope_ = UnserializeUInt(pReader, pos, size);
- if (encoding_scope_ < 1)
- return -1;
- } else if (id == mkvmuxer::kMkvContentEncodingType) {
- encoding_type_ = UnserializeUInt(pReader, pos, size);
- } else if (id == mkvmuxer::kMkvContentCompression) {
- ContentCompression* const compression =
- new (std::nothrow) ContentCompression();
- if (!compression)
- return -1;
-
- status = ParseCompressionEntry(pos, size, pReader, compression);
- if (status) {
- delete compression;
- return status;
- }
- *compression_entries_end_++ = compression;
- } else if (id == mkvmuxer::kMkvContentEncryption) {
- ContentEncryption* const encryption =
- new (std::nothrow) ContentEncryption();
- if (!encryption)
- return -1;
-
- status = ParseEncryptionEntry(pos, size, pReader, encryption);
- if (status) {
- delete encryption;
- return status;
- }
- *encryption_entries_end_++ = encryption;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- return 0;
-}
-
-long ContentEncoding::ParseCompressionEntry(long long start, long long size,
- IMkvReader* pReader,
- ContentCompression* compression) {
- assert(pReader);
- assert(compression);
-
- long long pos = start;
- const long long stop = start + size;
-
- bool valid = false;
-
- while (pos < stop) {
- long long id, size;
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvContentCompAlgo) {
- long long algo = UnserializeUInt(pReader, pos, size);
- if (algo < 0)
- return E_FILE_FORMAT_INVALID;
- compression->algo = algo;
- valid = true;
- } else if (id == mkvmuxer::kMkvContentCompSettings) {
- if (size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- const size_t buflen = static_cast<size_t>(size);
- unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
- if (buf == NULL)
- return -1;
-
- const int read_status =
- pReader->Read(pos, static_cast<long>(buflen), buf);
- if (read_status) {
- delete[] buf;
- return status;
- }
-
- compression->settings = buf;
- compression->settings_len = buflen;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- // ContentCompAlgo is mandatory
- if (!valid)
- return E_FILE_FORMAT_INVALID;
-
- return 0;
-}
-
-long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
- IMkvReader* pReader,
- ContentEncryption* encryption) {
- assert(pReader);
- assert(encryption);
-
- long long pos = start;
- const long long stop = start + size;
-
- while (pos < stop) {
- long long id, size;
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvContentEncAlgo) {
- encryption->algo = UnserializeUInt(pReader, pos, size);
- if (encryption->algo != 5)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvContentEncKeyID) {
- delete[] encryption->key_id;
- encryption->key_id = NULL;
- encryption->key_id_len = 0;
-
- if (size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- const size_t buflen = static_cast<size_t>(size);
- unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
- if (buf == NULL)
- return -1;
-
- const int read_status =
- pReader->Read(pos, static_cast<long>(buflen), buf);
- if (read_status) {
- delete[] buf;
- return status;
- }
-
- encryption->key_id = buf;
- encryption->key_id_len = buflen;
- } else if (id == mkvmuxer::kMkvContentSignature) {
- delete[] encryption->signature;
- encryption->signature = NULL;
- encryption->signature_len = 0;
-
- if (size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- const size_t buflen = static_cast<size_t>(size);
- unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
- if (buf == NULL)
- return -1;
-
- const int read_status =
- pReader->Read(pos, static_cast<long>(buflen), buf);
- if (read_status) {
- delete[] buf;
- return status;
- }
-
- encryption->signature = buf;
- encryption->signature_len = buflen;
- } else if (id == mkvmuxer::kMkvContentSigKeyID) {
- delete[] encryption->sig_key_id;
- encryption->sig_key_id = NULL;
- encryption->sig_key_id_len = 0;
-
- if (size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- const size_t buflen = static_cast<size_t>(size);
- unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
- if (buf == NULL)
- return -1;
-
- const int read_status =
- pReader->Read(pos, static_cast<long>(buflen), buf);
- if (read_status) {
- delete[] buf;
- return status;
- }
-
- encryption->sig_key_id = buf;
- encryption->sig_key_id_len = buflen;
- } else if (id == mkvmuxer::kMkvContentSigAlgo) {
- encryption->sig_algo = UnserializeUInt(pReader, pos, size);
- } else if (id == mkvmuxer::kMkvContentSigHashAlgo) {
- encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
- } else if (id == mkvmuxer::kMkvContentEncAESSettings) {
- const long status = ParseContentEncAESSettingsEntry(
- pos, size, pReader, &encryption->aes_settings);
- if (status)
- return status;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- return 0;
-}
-
-Track::Track(Segment* pSegment, long long element_start, long long element_size)
- : m_pSegment(pSegment),
- m_element_start(element_start),
- m_element_size(element_size),
- content_encoding_entries_(NULL),
- content_encoding_entries_end_(NULL) {}
-
-Track::~Track() {
- Info& info = const_cast<Info&>(m_info);
- info.Clear();
-
- ContentEncoding** i = content_encoding_entries_;
- ContentEncoding** const j = content_encoding_entries_end_;
-
- while (i != j) {
- ContentEncoding* const encoding = *i++;
- delete encoding;
- }
-
- delete[] content_encoding_entries_;
-}
-
-long Track::Create(Segment* pSegment, const Info& info, long long element_start,
- long long element_size, Track*& pResult) {
- if (pResult)
- return -1;
-
- Track* const pTrack =
- new (std::nothrow) Track(pSegment, element_start, element_size);
-
- if (pTrack == NULL)
- return -1; // generic error
-
- const int status = info.Copy(pTrack->m_info);
-
- if (status) { // error
- delete pTrack;
- return status;
- }
-
- pResult = pTrack;
- return 0; // success
-}
-
-Track::Info::Info()
- : uid(0),
- defaultDuration(0),
- codecDelay(0),
- seekPreRoll(0),
- nameAsUTF8(NULL),
- language(NULL),
- codecId(NULL),
- codecNameAsUTF8(NULL),
- codecPrivate(NULL),
- codecPrivateSize(0),
- lacing(false) {}
-
-Track::Info::~Info() { Clear(); }
-
-void Track::Info::Clear() {
- delete[] nameAsUTF8;
- nameAsUTF8 = NULL;
-
- delete[] language;
- language = NULL;
-
- delete[] codecId;
- codecId = NULL;
-
- delete[] codecPrivate;
- codecPrivate = NULL;
- codecPrivateSize = 0;
-
- delete[] codecNameAsUTF8;
- codecNameAsUTF8 = NULL;
-}
-
-int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
- if (str == static_cast<char * Info::*>(NULL))
- return -1;
-
- char*& dst = dst_.*str;
-
- if (dst) // should be NULL already
- return -1;
-
- const char* const src = this->*str;
-
- if (src == NULL)
- return 0;
-
- const size_t len = strlen(src);
-
- dst = SafeArrayAlloc<char>(1, len + 1);
-
- if (dst == NULL)
- return -1;
-
- strcpy(dst, src);
-
- return 0;
-}
-
-int Track::Info::Copy(Info& dst) const {
- if (&dst == this)
- return 0;
-
- dst.type = type;
- dst.number = number;
- dst.defaultDuration = defaultDuration;
- dst.codecDelay = codecDelay;
- dst.seekPreRoll = seekPreRoll;
- dst.uid = uid;
- dst.lacing = lacing;
- dst.settings = settings;
-
- // We now copy the string member variables from src to dst.
- // This involves memory allocation so in principle the operation
- // can fail (indeed, that's why we have Info::Copy), so we must
- // report this to the caller. An error return from this function
- // therefore implies that the copy was only partially successful.
-
- if (int status = CopyStr(&Info::nameAsUTF8, dst))
- return status;
-
- if (int status = CopyStr(&Info::language, dst))
- return status;
-
- if (int status = CopyStr(&Info::codecId, dst))
- return status;
-
- if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
- return status;
-
- if (codecPrivateSize > 0) {
- if (codecPrivate == NULL)
- return -1;
-
- if (dst.codecPrivate)
- return -1;
-
- if (dst.codecPrivateSize != 0)
- return -1;
-
- dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
-
- if (dst.codecPrivate == NULL)
- return -1;
-
- memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
- dst.codecPrivateSize = codecPrivateSize;
- }
-
- return 0;
-}
-
-const BlockEntry* Track::GetEOS() const { return &m_eos; }
-
-long Track::GetType() const { return m_info.type; }
-
-long Track::GetNumber() const { return m_info.number; }
-
-unsigned long long Track::GetUid() const { return m_info.uid; }
-
-const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
-
-const char* Track::GetLanguage() const { return m_info.language; }
-
-const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
-
-const char* Track::GetCodecId() const { return m_info.codecId; }
-
-const unsigned char* Track::GetCodecPrivate(size_t& size) const {
- size = m_info.codecPrivateSize;
- return m_info.codecPrivate;
-}
-
-bool Track::GetLacing() const { return m_info.lacing; }
-
-unsigned long long Track::GetDefaultDuration() const {
- return m_info.defaultDuration;
-}
-
-unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
-
-unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
-
-long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
- const Cluster* pCluster = m_pSegment->GetFirst();
-
- for (int i = 0;;) {
- if (pCluster == NULL) {
- pBlockEntry = GetEOS();
- return 1;
- }
-
- if (pCluster->EOS()) {
- if (m_pSegment->DoneParsing()) {
- pBlockEntry = GetEOS();
- return 1;
- }
-
- pBlockEntry = 0;
- return E_BUFFER_NOT_FULL;
- }
-
- long status = pCluster->GetFirst(pBlockEntry);
-
- if (status < 0) // error
- return status;
-
- if (pBlockEntry == 0) { // empty cluster
- pCluster = m_pSegment->GetNext(pCluster);
- continue;
- }
-
- for (;;) {
- const Block* const pBlock = pBlockEntry->GetBlock();
- assert(pBlock);
-
- const long long tn = pBlock->GetTrackNumber();
-
- if ((tn == m_info.number) && VetEntry(pBlockEntry))
- return 0;
-
- const BlockEntry* pNextEntry;
-
- status = pCluster->GetNext(pBlockEntry, pNextEntry);
-
- if (status < 0) // error
- return status;
-
- if (pNextEntry == 0)
- break;
-
- pBlockEntry = pNextEntry;
- }
-
- ++i;
-
- if (i >= 100)
- break;
-
- pCluster = m_pSegment->GetNext(pCluster);
- }
-
- // NOTE: if we get here, it means that we didn't find a block with
- // a matching track number. We interpret that as an error (which
- // might be too conservative).
-
- pBlockEntry = GetEOS(); // so we can return a non-NULL value
- return 1;
-}
-
-long Track::GetNext(const BlockEntry* pCurrEntry,
- const BlockEntry*& pNextEntry) const {
- assert(pCurrEntry);
- assert(!pCurrEntry->EOS()); //?
-
- const Block* const pCurrBlock = pCurrEntry->GetBlock();
- assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
- if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
- return -1;
-
- const Cluster* pCluster = pCurrEntry->GetCluster();
- assert(pCluster);
- assert(!pCluster->EOS());
-
- long status = pCluster->GetNext(pCurrEntry, pNextEntry);
-
- if (status < 0) // error
- return status;
-
- for (int i = 0;;) {
- while (pNextEntry) {
- const Block* const pNextBlock = pNextEntry->GetBlock();
- assert(pNextBlock);
-
- if (pNextBlock->GetTrackNumber() == m_info.number)
- return 0;
-
- pCurrEntry = pNextEntry;
-
- status = pCluster->GetNext(pCurrEntry, pNextEntry);
-
- if (status < 0) // error
- return status;
- }
-
- pCluster = m_pSegment->GetNext(pCluster);
-
- if (pCluster == NULL) {
- pNextEntry = GetEOS();
- return 1;
- }
-
- if (pCluster->EOS()) {
- if (m_pSegment->DoneParsing()) {
- pNextEntry = GetEOS();
- return 1;
- }
-
- // 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
- // calls GetNext again, which repeats the same search. This is
- // a pathological case, since the only way it can happen is if
- // there exists a long sequence of clusters none of which contain a
- // block from this track. One way around this problem is for the
- // caller to be smarter when he loads another cluster: don't call
- // us back until you have a cluster that contains a block from this
- // track. (Of course, that's not cheap either, since our caller
- // would have to scan the each cluster as it's loaded, so that
- // would just push back the problem.)
-
- pNextEntry = NULL;
- return E_BUFFER_NOT_FULL;
- }
-
- status = pCluster->GetFirst(pNextEntry);
-
- if (status < 0) // error
- return status;
-
- if (pNextEntry == NULL) // empty cluster
- continue;
-
- ++i;
-
- if (i >= 100)
- break;
- }
-
- // NOTE: if we get here, it means that we didn't find a block with
- // a matching track number after lots of searching, so we give
- // up trying.
-
- pNextEntry = GetEOS(); // so we can return a non-NULL value
- return 1;
-}
-
-bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
- assert(pBlockEntry);
- const Block* const pBlock = pBlockEntry->GetBlock();
- assert(pBlock);
- assert(pBlock->GetTrackNumber() == m_info.number);
- if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
- return false;
-
- // This function is used during a seek to determine whether the
- // frame is a valid seek target. This default function simply
- // returns true, which means all frames are valid seek targets.
- // It gets overridden by the VideoTrack class, because only video
- // keyframes can be used as seek target.
-
- return true;
-}
-
-long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
- const long status = GetFirst(pResult);
-
- if (status < 0) // buffer underflow, etc
- return status;
-
- assert(pResult);
-
- if (pResult->EOS())
- return 0;
-
- const Cluster* pCluster = pResult->GetCluster();
- assert(pCluster);
- assert(pCluster->GetIndex() >= 0);
-
- if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
- return 0;
-
- Cluster** const clusters = m_pSegment->m_clusters;
- assert(clusters);
-
- const long count = m_pSegment->GetCount(); // loaded only, not preloaded
- assert(count > 0);
-
- Cluster** const i = clusters + pCluster->GetIndex();
- assert(i);
- assert(*i == pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- Cluster** const j = clusters + count;
-
- 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);
-
- pCluster = *mid;
- assert(pCluster);
- assert(pCluster->GetIndex() >= 0);
- assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
-
- 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) {
- pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- pResult = pCluster->GetEntry(this);
-
- if ((pResult != 0) && !pResult->EOS())
- return 0;
-
- // landed on empty cluster (no entries)
- }
-
- pResult = GetEOS(); // weird
- return 0;
-}
-
-const ContentEncoding* Track::GetContentEncodingByIndex(
- unsigned long idx) const {
- const ptrdiff_t count =
- content_encoding_entries_end_ - content_encoding_entries_;
- assert(count >= 0);
-
- if (idx >= static_cast<unsigned long>(count))
- return NULL;
-
- return content_encoding_entries_[idx];
-}
-
-unsigned long Track::GetContentEncodingCount() const {
- const ptrdiff_t count =
- content_encoding_entries_end_ - content_encoding_entries_;
- assert(count >= 0);
-
- return static_cast<unsigned long>(count);
-}
-
-long Track::ParseContentEncodingsEntry(long long start, long long size) {
- IMkvReader* const pReader = m_pSegment->m_pReader;
- assert(pReader);
-
- long long pos = start;
- const long long stop = start + size;
-
- // Count ContentEncoding elements.
- int count = 0;
- while (pos < stop) {
- long long id, size;
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
- if (status < 0) // error
- return status;
-
- // pos now designates start of element
- if (id == mkvmuxer::kMkvContentEncoding)
- ++count;
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (count <= 0)
- return -1;
-
- content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
- if (!content_encoding_entries_)
- return -1;
-
- content_encoding_entries_end_ = content_encoding_entries_;
-
- pos = start;
- while (pos < stop) {
- long long id, size;
- long status = ParseElementHeader(pReader, pos, stop, id, size);
- if (status < 0) // error
- return status;
-
- // pos now designates start of element
- if (id == mkvmuxer::kMkvContentEncoding) {
- ContentEncoding* const content_encoding =
- new (std::nothrow) ContentEncoding();
- if (!content_encoding)
- return -1;
-
- status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
- if (status) {
- delete content_encoding;
- return status;
- }
-
- *content_encoding_entries_end_++ = content_encoding;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- return 0;
-}
-
-Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
-
-BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
-
-const Block* Track::EOSBlock::GetBlock() const { return NULL; }
-
-VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
- long long element_size)
- : Track(pSegment, element_start, element_size) {}
-
-long VideoTrack::Parse(Segment* pSegment, const Info& info,
- long long element_start, long long element_size,
- VideoTrack*& pResult) {
- if (pResult)
- return -1;
-
- if (info.type != Track::kVideo)
- return -1;
-
- 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;
-
- const Settings& s = info.settings;
- assert(s.start >= 0);
- assert(s.size >= 0);
-
- long long pos = s.start;
- assert(pos >= 0);
-
- const long long stop = pos + s.size;
-
- while (pos < stop) {
- long long id, size;
-
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
-
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvPixelWidth) {
- width = UnserializeUInt(pReader, pos, size);
-
- if (width <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvPixelHeight) {
- height = UnserializeUInt(pReader, pos, size);
-
- if (height <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvDisplayWidth) {
- display_width = UnserializeUInt(pReader, pos, size);
-
- if (display_width <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvDisplayHeight) {
- display_height = UnserializeUInt(pReader, pos, size);
-
- if (display_height <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvDisplayUnit) {
- display_unit = UnserializeUInt(pReader, pos, size);
-
- if (display_unit < 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvStereoMode) {
- stereo_mode = UnserializeUInt(pReader, pos, size);
-
- if (stereo_mode < 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvFrameRate) {
- const long status = UnserializeFloat(pReader, pos, size, rate);
-
- if (status < 0)
- return status;
-
- if (rate <= 0)
- return E_FILE_FORMAT_INVALID;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- VideoTrack* const pTrack =
- new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
-
- if (pTrack == NULL)
- return -1; // generic error
-
- const int status = info.Copy(pTrack->m_info);
-
- if (status) { // error
- delete pTrack;
- return status;
- }
-
- 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;
- return 0; // success
-}
-
-bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
- return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
-}
-
-long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
- const long status = GetFirst(pResult);
-
- if (status < 0) // buffer underflow, etc
- return status;
-
- assert(pResult);
-
- if (pResult->EOS())
- return 0;
-
- const Cluster* pCluster = pResult->GetCluster();
- assert(pCluster);
- assert(pCluster->GetIndex() >= 0);
-
- if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
- return 0;
-
- Cluster** const clusters = m_pSegment->m_clusters;
- assert(clusters);
-
- const long count = m_pSegment->GetCount(); // loaded only, not pre-loaded
- assert(count > 0);
-
- Cluster** const i = clusters + pCluster->GetIndex();
- assert(i);
- assert(*i == pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- Cluster** const j = clusters + count;
-
- 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);
-
- pCluster = *mid;
- assert(pCluster);
- assert(pCluster->GetIndex() >= 0);
- assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
-
- 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);
-
- pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- pResult = pCluster->GetEntry(this, time_ns);
-
- if ((pResult != 0) && !pResult->EOS()) // found a keyframe
- return 0;
-
- while (lo != i) {
- pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- pResult = pCluster->GetEntry(this, time_ns);
-
- if ((pResult != 0) && !pResult->EOS())
- return 0;
- }
-
- // weird: we're on the first cluster, but no keyframe found
- // should never happen but we must return something anyway
-
- pResult = GetEOS();
- return 0;
-}
-
-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,
- long long element_size)
- : Track(pSegment, element_start, element_size) {}
-
-long AudioTrack::Parse(Segment* pSegment, const Info& info,
- long long element_start, long long element_size,
- AudioTrack*& pResult) {
- if (pResult)
- return -1;
-
- if (info.type != Track::kAudio)
- return -1;
-
- IMkvReader* const pReader = pSegment->m_pReader;
-
- const Settings& s = info.settings;
- assert(s.start >= 0);
- assert(s.size >= 0);
-
- long long pos = s.start;
- assert(pos >= 0);
-
- const long long stop = pos + s.size;
-
- double rate = 8000.0; // MKV default
- long long channels = 1;
- long long bit_depth = 0;
-
- while (pos < stop) {
- long long id, size;
-
- long status = ParseElementHeader(pReader, pos, stop, id, size);
-
- if (status < 0) // error
- return status;
-
- if (id == mkvmuxer::kMkvSamplingFrequency) {
- status = UnserializeFloat(pReader, pos, size, rate);
-
- if (status < 0)
- return status;
-
- if (rate <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvChannels) {
- channels = UnserializeUInt(pReader, pos, size);
-
- if (channels <= 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvBitDepth) {
- bit_depth = UnserializeUInt(pReader, pos, size);
-
- if (bit_depth <= 0)
- return E_FILE_FORMAT_INVALID;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- AudioTrack* const pTrack =
- new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
-
- if (pTrack == NULL)
- return -1; // generic error
-
- const int status = info.Copy(pTrack->m_info);
-
- if (status) {
- delete pTrack;
- return status;
- }
-
- pTrack->m_rate = rate;
- pTrack->m_channels = channels;
- pTrack->m_bitDepth = bit_depth;
-
- pResult = pTrack;
- return 0; // success
-}
-
-double AudioTrack::GetSamplingRate() const { return m_rate; }
-
-long long AudioTrack::GetChannels() const { return m_channels; }
-
-long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
-
-Tracks::Tracks(Segment* pSegment, long long start, long long size_,
- long long element_start, long long element_size)
- : m_pSegment(pSegment),
- m_start(start),
- m_size(size_),
- m_element_start(element_start),
- m_element_size(element_size),
- m_trackEntries(NULL),
- m_trackEntriesEnd(NULL) {}
-
-long Tracks::Parse() {
- assert(m_trackEntries == NULL);
- assert(m_trackEntriesEnd == NULL);
-
- const long long stop = m_start + m_size;
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- int count = 0;
- long long pos = m_start;
-
- while (pos < stop) {
- long long id, size;
-
- const long status = ParseElementHeader(pReader, pos, stop, id, size);
-
- if (status < 0) // error
- return status;
-
- if (size == 0) // weird
- continue;
-
- if (id == mkvmuxer::kMkvTrackEntry)
- ++count;
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- if (count <= 0)
- return 0; // success
-
- m_trackEntries = new (std::nothrow) Track*[count];
-
- if (m_trackEntries == NULL)
- return -1;
-
- m_trackEntriesEnd = m_trackEntries;
-
- pos = m_start;
-
- while (pos < stop) {
- const long long element_start = pos;
-
- long long id, payload_size;
-
- const long status =
- ParseElementHeader(pReader, pos, stop, id, payload_size);
-
- if (status < 0) // error
- return status;
-
- if (payload_size == 0) // weird
- continue;
-
- const long long payload_stop = pos + payload_size;
- assert(payload_stop <= stop); // checked in ParseElement
-
- const long long element_size = payload_stop - element_start;
-
- if (id == mkvmuxer::kMkvTrackEntry) {
- Track*& pTrack = *m_trackEntriesEnd;
- pTrack = NULL;
-
- const long status = ParseTrackEntry(pos, payload_size, element_start,
- element_size, pTrack);
- if (status)
- return status;
-
- if (pTrack)
- ++m_trackEntriesEnd;
- }
-
- pos = payload_stop;
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- return 0; // success
-}
-
-unsigned long Tracks::GetTracksCount() const {
- const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
- assert(result >= 0);
-
- return static_cast<unsigned long>(result);
-}
-
-long Tracks::ParseTrackEntry(long long track_start, long long track_size,
- long long element_start, long long element_size,
- Track*& pResult) const {
- if (pResult)
- return -1;
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long pos = track_start;
- const long long track_stop = track_start + track_size;
-
- Track::Info info;
-
- info.type = 0;
- info.number = 0;
- info.uid = 0;
- info.defaultDuration = 0;
-
- Track::Settings v;
- v.start = -1;
- v.size = -1;
-
- Track::Settings a;
- a.start = -1;
- a.size = -1;
-
- Track::Settings e; // content_encodings_settings;
- e.start = -1;
- e.size = -1;
-
- long long lacing = 1; // default is true
-
- while (pos < track_stop) {
- long long id, size;
-
- const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
-
- if (status < 0) // error
- return status;
-
- if (size < 0)
- return E_FILE_FORMAT_INVALID;
-
- const long long start = pos;
-
- if (id == mkvmuxer::kMkvVideo) {
- v.start = start;
- v.size = size;
- } else if (id == mkvmuxer::kMkvAudio) {
- a.start = start;
- a.size = size;
- } else if (id == mkvmuxer::kMkvContentEncodings) {
- e.start = start;
- e.size = size;
- } else if (id == mkvmuxer::kMkvTrackUID) {
- if (size > 8)
- return E_FILE_FORMAT_INVALID;
-
- info.uid = 0;
-
- long long pos_ = start;
- const long long pos_end = start + size;
-
- while (pos_ != pos_end) {
- unsigned char b;
-
- const int status = pReader->Read(pos_, 1, &b);
-
- if (status)
- return status;
-
- info.uid <<= 8;
- info.uid |= b;
-
- ++pos_;
- }
- } else if (id == mkvmuxer::kMkvTrackNumber) {
- const long long num = UnserializeUInt(pReader, pos, size);
-
- if ((num <= 0) || (num > 127))
- return E_FILE_FORMAT_INVALID;
-
- info.number = static_cast<long>(num);
- } else if (id == mkvmuxer::kMkvTrackType) {
- const long long type = UnserializeUInt(pReader, pos, size);
-
- if ((type <= 0) || (type > 254))
- return E_FILE_FORMAT_INVALID;
-
- info.type = static_cast<long>(type);
- } else if (id == mkvmuxer::kMkvName) {
- const long status =
- UnserializeString(pReader, pos, size, info.nameAsUTF8);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvLanguage) {
- const long status = UnserializeString(pReader, pos, size, info.language);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvDefaultDuration) {
- const long long duration = UnserializeUInt(pReader, pos, size);
-
- if (duration < 0)
- return E_FILE_FORMAT_INVALID;
-
- info.defaultDuration = static_cast<unsigned long long>(duration);
- } else if (id == mkvmuxer::kMkvCodecID) {
- const long status = UnserializeString(pReader, pos, size, info.codecId);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvFlagLacing) {
- lacing = UnserializeUInt(pReader, pos, size);
-
- if ((lacing < 0) || (lacing > 1))
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvCodecPrivate) {
- delete[] info.codecPrivate;
- info.codecPrivate = NULL;
- info.codecPrivateSize = 0;
-
- const size_t buflen = static_cast<size_t>(size);
-
- if (buflen) {
- unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
-
- if (buf == NULL)
- return -1;
-
- const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
-
- if (status) {
- delete[] buf;
- return status;
- }
-
- info.codecPrivate = buf;
- info.codecPrivateSize = buflen;
- }
- } else if (id == mkvmuxer::kMkvCodecName) {
- const long status =
- UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
-
- if (status)
- return status;
- } else if (id == mkvmuxer::kMkvCodecDelay) {
- info.codecDelay = UnserializeUInt(pReader, pos, size);
- } else if (id == mkvmuxer::kMkvSeekPreRoll) {
- info.seekPreRoll = UnserializeUInt(pReader, pos, size);
- }
-
- pos += size; // consume payload
- if (pos > track_stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != track_stop)
- return E_FILE_FORMAT_INVALID;
-
- if (info.number <= 0) // not specified
- return E_FILE_FORMAT_INVALID;
-
- if (GetTrackByNumber(info.number))
- return E_FILE_FORMAT_INVALID;
-
- if (info.type <= 0) // not specified
- return E_FILE_FORMAT_INVALID;
-
- info.lacing = (lacing > 0) ? true : false;
-
- if (info.type == Track::kVideo) {
- if (v.start < 0)
- return E_FILE_FORMAT_INVALID;
-
- if (a.start >= 0)
- return E_FILE_FORMAT_INVALID;
-
- info.settings = v;
-
- VideoTrack* pTrack = NULL;
-
- const long status = VideoTrack::Parse(m_pSegment, info, element_start,
- element_size, pTrack);
-
- if (status)
- return status;
-
- pResult = pTrack;
- assert(pResult);
-
- if (e.start >= 0)
- pResult->ParseContentEncodingsEntry(e.start, e.size);
- } else if (info.type == Track::kAudio) {
- if (a.start < 0)
- return E_FILE_FORMAT_INVALID;
-
- if (v.start >= 0)
- return E_FILE_FORMAT_INVALID;
-
- info.settings = a;
-
- AudioTrack* pTrack = NULL;
-
- const long status = AudioTrack::Parse(m_pSegment, info, element_start,
- element_size, pTrack);
-
- if (status)
- return status;
-
- pResult = pTrack;
- assert(pResult);
-
- if (e.start >= 0)
- pResult->ParseContentEncodingsEntry(e.start, e.size);
- } else {
- // neither video nor audio - probably metadata or subtitles
-
- if (a.start >= 0)
- return E_FILE_FORMAT_INVALID;
-
- if (v.start >= 0)
- return E_FILE_FORMAT_INVALID;
-
- if (info.type == Track::kMetadata && e.start >= 0)
- return E_FILE_FORMAT_INVALID;
-
- info.settings.start = -1;
- info.settings.size = 0;
-
- Track* pTrack = NULL;
-
- const long status =
- Track::Create(m_pSegment, info, element_start, element_size, pTrack);
-
- if (status)
- return status;
-
- pResult = pTrack;
- assert(pResult);
- }
-
- return 0; // success
-}
-
-Tracks::~Tracks() {
- Track** i = m_trackEntries;
- Track** const j = m_trackEntriesEnd;
-
- while (i != j) {
- Track* const pTrack = *i++;
- delete pTrack;
- }
-
- delete[] m_trackEntries;
-}
-
-const Track* Tracks::GetTrackByNumber(long tn) const {
- if (tn < 0)
- return NULL;
-
- Track** i = m_trackEntries;
- Track** const j = m_trackEntriesEnd;
-
- while (i != j) {
- Track* const pTrack = *i++;
-
- if (pTrack == NULL)
- continue;
-
- if (tn == pTrack->GetNumber())
- return pTrack;
- }
-
- return NULL; // not found
-}
-
-const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
- const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
-
- if (idx >= static_cast<unsigned long>(count))
- return NULL;
-
- return m_trackEntries[idx];
-}
-
-long Cluster::Load(long long& pos, long& len) const {
- if (m_pSegment == NULL)
- return E_PARSE_FAILED;
-
- if (m_timecode >= 0) // at least partially loaded
- return 0;
-
- if (m_pos != m_element_start || m_element_size >= 0)
- return E_PARSE_FAILED;
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
- long long total, avail;
- const int status = pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return status;
-
- if (total >= 0 && (avail > total || m_pos > total))
- return E_FILE_FORMAT_INVALID;
-
- pos = m_pos;
-
- long long cluster_size = -1;
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error or underflow
- return static_cast<long>(result);
-
- if (result > 0)
- return E_BUFFER_NOT_FULL;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long id_ = ReadID(pReader, pos, len);
-
- if (id_ < 0) // error
- return static_cast<long>(id_);
-
- if (id_ != mkvmuxer::kMkvCluster)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume id
-
- // read cluster size
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0)
- return E_BUFFER_NOT_FULL;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) // error
- return static_cast<long>(cluster_size);
-
- if (size == 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume length of size of element
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size != unknown_size)
- cluster_size = size;
-
- // pos points to start of payload
- long long timecode = -1;
- long long new_pos = -1;
- bool bBlock = false;
-
- long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
-
- for (;;) {
- if ((cluster_stop >= 0) && (pos >= cluster_stop))
- break;
-
- // Parse ID
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0)
- return E_BUFFER_NOT_FULL;
-
- if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long id = ReadID(pReader, pos, len);
-
- if (id < 0) // error
- return static_cast<long>(id);
-
- if (id == 0)
- return E_FILE_FORMAT_INVALID;
-
- // 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 == mkvmuxer::kMkvCluster)
- break;
-
- if (id == mkvmuxer::kMkvCues)
- break;
-
- pos += len; // consume ID field
-
- // Parse Size
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0)
- return E_BUFFER_NOT_FULL;
-
- if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) // error
- return static_cast<long>(size);
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume size field
-
- if ((cluster_stop >= 0) && (pos > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- // pos now points to start of payload
-
- if (size == 0)
- continue;
-
- if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if (id == mkvmuxer::kMkvTimecode) {
- len = static_cast<long>(size);
-
- if ((pos + size) > avail)
- return E_BUFFER_NOT_FULL;
-
- timecode = UnserializeUInt(pReader, pos, size);
-
- if (timecode < 0) // error (or underflow)
- return static_cast<long>(timecode);
-
- new_pos = pos + size;
-
- if (bBlock)
- break;
- } else if (id == mkvmuxer::kMkvBlockGroup) {
- bBlock = true;
- break;
- } else if (id == mkvmuxer::kMkvSimpleBlock) {
- bBlock = true;
- break;
- }
-
- pos += size; // consume payload
- if (cluster_stop >= 0 && pos > cluster_stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (cluster_stop >= 0 && pos > cluster_stop)
- return E_FILE_FORMAT_INVALID;
-
- if (timecode < 0) // no timecode found
- return E_FILE_FORMAT_INVALID;
-
- if (!bBlock)
- return E_FILE_FORMAT_INVALID;
-
- m_pos = new_pos; // designates position just beyond timecode payload
- m_timecode = timecode; // m_timecode >= 0 means we're partially loaded
-
- if (cluster_size >= 0)
- m_element_size = cluster_stop - m_element_start;
-
- return 0;
-}
-
-long Cluster::Parse(long long& pos, long& len) const {
- long status = Load(pos, len);
-
- if (status < 0)
- return status;
-
- if (m_pos < m_element_start || m_timecode < 0)
- return E_PARSE_FAILED;
-
- const long long cluster_stop =
- (m_element_size < 0) ? -1 : m_element_start + m_element_size;
-
- if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
- return 1; // nothing else to do
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long total, avail;
-
- status = pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return status;
-
- if (total >= 0 && avail > total)
- return E_FILE_FORMAT_INVALID;
-
- pos = m_pos;
-
- for (;;) {
- if ((cluster_stop >= 0) && (pos >= cluster_stop))
- break;
-
- if ((total >= 0) && (pos >= total)) {
- if (m_element_size < 0)
- m_element_size = pos - m_element_start;
-
- break;
- }
-
- // Parse ID
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0)
- return E_BUFFER_NOT_FULL;
-
- if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long id = ReadID(pReader, pos, len);
-
- if (id < 0)
- return E_FILE_FORMAT_INVALID;
-
- // 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 == mkvmuxer::kMkvCluster) || (id == mkvmuxer::kMkvCues)) {
- if (m_element_size < 0)
- m_element_size = pos - m_element_start;
-
- break;
- }
-
- pos += len; // consume ID field
-
- // Parse Size
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0)
- return E_BUFFER_NOT_FULL;
-
- if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) // error
- return static_cast<long>(size);
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume size field
-
- if ((cluster_stop >= 0) && (pos > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- // pos now points to start of payload
-
- if (size == 0)
- continue;
-
- // const long long block_start = pos;
- const long long block_stop = pos + size;
-
- if (cluster_stop >= 0) {
- if (block_stop > cluster_stop) {
- if (id == mkvmuxer::kMkvBlockGroup ||
- id == mkvmuxer::kMkvSimpleBlock) {
- return E_FILE_FORMAT_INVALID;
- }
-
- pos = cluster_stop;
- break;
- }
- } else if ((total >= 0) && (block_stop > total)) {
- m_element_size = total - m_element_start;
- pos = total;
- break;
- } else if (block_stop > avail) {
- len = static_cast<long>(size);
- return E_BUFFER_NOT_FULL;
- }
-
- Cluster* const this_ = const_cast<Cluster*>(this);
-
- if (id == mkvmuxer::kMkvBlockGroup)
- return this_->ParseBlockGroup(size, pos, len);
-
- if (id == mkvmuxer::kMkvSimpleBlock)
- return this_->ParseSimpleBlock(size, pos, len);
-
- pos += size; // consume payload
- if (cluster_stop >= 0 && pos > cluster_stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (m_element_size < 1)
- return E_FILE_FORMAT_INVALID;
-
- m_pos = pos;
- if (cluster_stop >= 0 && m_pos > cluster_stop)
- return E_FILE_FORMAT_INVALID;
-
- if (m_entries_count > 0) {
- const long idx = m_entries_count - 1;
-
- const BlockEntry* const pLast = m_entries[idx];
- if (pLast == NULL)
- return E_PARSE_FAILED;
-
- const Block* const pBlock = pLast->GetBlock();
- if (pBlock == NULL)
- return E_PARSE_FAILED;
-
- const long long start = pBlock->m_start;
-
- if ((total >= 0) && (start > total))
- return E_PARSE_FAILED; // defend against trucated stream
-
- const long long size = pBlock->m_size;
-
- const long long stop = start + size;
- if (cluster_stop >= 0 && stop > cluster_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((total >= 0) && (stop > total))
- return E_PARSE_FAILED; // defend against trucated stream
- }
-
- return 1; // no more entries
-}
-
-long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
- long& len) {
- const long long block_start = pos;
- const long long block_stop = pos + block_size;
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long total, avail;
-
- long status = pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return status;
-
- assert((total < 0) || (avail <= total));
-
- // parse track number
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0) // weird
- return E_BUFFER_NOT_FULL;
-
- if ((pos + len) > block_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long track = ReadUInt(pReader, pos, len);
-
- if (track < 0) // error
- return static_cast<long>(track);
-
- if (track == 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume track number
-
- if ((pos + 2) > block_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + 2) > avail) {
- len = 2;
- return E_BUFFER_NOT_FULL;
- }
-
- pos += 2; // consume timecode
-
- if ((pos + 1) > block_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- unsigned char flags;
-
- status = pReader->Read(pos, 1, &flags);
-
- if (status < 0) { // error or underflow
- len = 1;
- return status;
- }
-
- ++pos; // consume flags byte
- assert(pos <= avail);
-
- if (pos >= block_stop)
- return E_FILE_FORMAT_INVALID;
-
- const int lacing = int(flags & 0x06) >> 1;
-
- if ((lacing != 0) && (block_stop > avail)) {
- len = static_cast<long>(block_stop - pos);
- return E_BUFFER_NOT_FULL;
- }
-
- status = CreateBlock(mkvmuxer::kMkvSimpleBlock,
- block_start, block_size,
- 0); // DiscardPadding
-
- if (status != 0)
- return status;
-
- m_pos = block_stop;
-
- return 0; // success
-}
-
-long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
- long& len) {
- const long long payload_start = pos;
- const long long payload_stop = pos + payload_size;
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long total, avail;
-
- long status = pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return status;
-
- assert((total < 0) || (avail <= total));
-
- if ((total >= 0) && (payload_stop > total))
- return E_FILE_FORMAT_INVALID;
-
- if (payload_stop > avail) {
- len = static_cast<long>(payload_size);
- return E_BUFFER_NOT_FULL;
- }
-
- long long discard_padding = 0;
-
- while (pos < payload_stop) {
- // parse sub-block element ID
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0) // weird
- return E_BUFFER_NOT_FULL;
-
- if ((pos + len) > payload_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long id = ReadID(pReader, pos, len);
-
- if (id < 0) // error
- return static_cast<long>(id);
-
- if (id == 0) // not a valid ID
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume ID field
-
- // Parse Size
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0) // weird
- return E_BUFFER_NOT_FULL;
-
- if ((pos + len) > payload_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) // error
- return static_cast<long>(size);
-
- pos += len; // consume size field
-
- // pos now points to start of sub-block group payload
-
- if (pos > payload_stop)
- return E_FILE_FORMAT_INVALID;
-
- if (size == 0) // weird
- continue;
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID;
-
- if (id == mkvmuxer::kMkvDiscardPadding) {
- status = UnserializeInt(pReader, pos, size, discard_padding);
-
- if (status < 0) // error
- return status;
- }
-
- if (id != mkvmuxer::kMkvBlock) {
- pos += size; // consume sub-part of block group
-
- if (pos > payload_stop)
- return E_FILE_FORMAT_INVALID;
-
- continue;
- }
-
- const long long block_stop = pos + size;
-
- if (block_stop > payload_stop)
- return E_FILE_FORMAT_INVALID;
-
- // parse track number
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0) // weird
- return E_BUFFER_NOT_FULL;
-
- if ((pos + len) > block_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long track = ReadUInt(pReader, pos, len);
-
- if (track < 0) // error
- return static_cast<long>(track);
-
- if (track == 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume track number
-
- if ((pos + 2) > block_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + 2) > avail) {
- len = 2;
- return E_BUFFER_NOT_FULL;
- }
-
- pos += 2; // consume timecode
-
- if ((pos + 1) > block_stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- unsigned char flags;
-
- status = pReader->Read(pos, 1, &flags);
-
- if (status < 0) { // error or underflow
- len = 1;
- return status;
- }
-
- ++pos; // consume flags byte
- assert(pos <= avail);
-
- if (pos >= block_stop)
- return E_FILE_FORMAT_INVALID;
-
- const int lacing = int(flags & 0x06) >> 1;
-
- if ((lacing != 0) && (block_stop > avail)) {
- len = static_cast<long>(block_stop - pos);
- return E_BUFFER_NOT_FULL;
- }
-
- pos = block_stop; // consume block-part of block group
- if (pos > payload_stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- if (pos != payload_stop)
- return E_FILE_FORMAT_INVALID;
-
- status = CreateBlock(mkvmuxer::kMkvBlockGroup,
- payload_start, payload_size, discard_padding);
- if (status != 0)
- return status;
-
- m_pos = payload_stop;
-
- return 0; // success
-}
-
-long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
- assert(m_pos >= m_element_start);
-
- pEntry = NULL;
-
- if (index < 0)
- return -1; // generic error
-
- if (m_entries_count < 0)
- return E_BUFFER_NOT_FULL;
-
- assert(m_entries);
- assert(m_entries_size > 0);
- assert(m_entries_count <= m_entries_size);
-
- if (index < m_entries_count) {
- pEntry = m_entries[index];
- assert(pEntry);
-
- return 1; // found entry
- }
-
- if (m_element_size < 0) // we don't know cluster end yet
- return E_BUFFER_NOT_FULL; // underflow
-
- const long long element_stop = m_element_start + m_element_size;
-
- if (m_pos >= element_stop)
- return 0; // nothing left to parse
-
- return E_BUFFER_NOT_FULL; // underflow, since more remains to be parsed
-}
-
-Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
- if (!pSegment || off < 0)
- return NULL;
-
- const long long element_start = pSegment->m_start + off;
-
- Cluster* const pCluster =
- new (std::nothrow) Cluster(pSegment, idx, element_start);
-
- return pCluster;
-}
-
-Cluster::Cluster()
- : m_pSegment(NULL),
- m_element_start(0),
- m_index(0),
- m_pos(0),
- m_element_size(0),
- m_timecode(0),
- m_entries(NULL),
- m_entries_size(0),
- m_entries_count(0) // means "no entries"
-{}
-
-Cluster::Cluster(Segment* pSegment, long idx, long long element_start
- /* long long element_size */)
- : m_pSegment(pSegment),
- m_element_start(element_start),
- m_index(idx),
- m_pos(element_start),
- m_element_size(-1 /* element_size */),
- m_timecode(-1),
- m_entries(NULL),
- m_entries_size(0),
- m_entries_count(-1) // means "has not been parsed yet"
-{}
-
-Cluster::~Cluster() {
- if (m_entries_count <= 0)
- return;
-
- BlockEntry** i = m_entries;
- BlockEntry** const j = m_entries + m_entries_count;
-
- while (i != j) {
- BlockEntry* p = *i++;
- assert(p);
-
- delete p;
- }
-
- delete[] m_entries;
-}
-
-bool Cluster::EOS() const { return (m_pSegment == NULL); }
-
-long Cluster::GetIndex() const { return m_index; }
-
-long long Cluster::GetPosition() const {
- const long long pos = m_element_start - m_pSegment->m_start;
- assert(pos >= 0);
-
- return pos;
-}
-
-long long Cluster::GetElementSize() const { return m_element_size; }
-
-long Cluster::HasBlockEntries(
- const Segment* pSegment,
- long long off, // relative to start of segment payload
- long long& pos, long& len) {
- assert(pSegment);
- assert(off >= 0); // relative to segment
-
- IMkvReader* const pReader = pSegment->m_pReader;
-
- long long total, avail;
-
- long status = pReader->Length(&total, &avail);
-
- if (status < 0) // error
- return status;
-
- assert((total < 0) || (avail <= total));
-
- pos = pSegment->m_start + off; // absolute
-
- if ((total >= 0) && (pos >= total))
- return 0; // we don't even have a complete cluster
-
- const long long segment_stop =
- (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
-
- long long cluster_stop = -1; // interpreted later to mean "unknown size"
-
- {
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0) // need more data
- return E_BUFFER_NOT_FULL;
-
- if ((segment_stop >= 0) && ((pos + len) > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((total >= 0) && ((pos + len) > total))
- return 0;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long id = ReadID(pReader, pos, len);
-
- if (id < 0) // error
- return static_cast<long>(id);
-
- if (id != mkvmuxer::kMkvCluster)
- return E_PARSE_FAILED;
-
- pos += len; // consume Cluster ID field
-
- // read size field
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(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 ((total >= 0) && ((pos + len) > total))
- return 0;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) // error
- return static_cast<long>(size);
-
- if (size == 0)
- return 0; // cluster does not have entries
-
- pos += len; // consume size field
-
- // pos now points to start of payload
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size != unknown_size) {
- cluster_stop = pos + size;
- assert(cluster_stop >= 0);
-
- if ((segment_stop >= 0) && (cluster_stop > segment_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((total >= 0) && (cluster_stop > total))
- // return E_FILE_FORMAT_INVALID; //too conservative
- return 0; // cluster does not have any entries
- }
- }
-
- for (;;) {
- if ((cluster_stop >= 0) && (pos >= cluster_stop))
- return 0; // no entries detected
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0) // need more data
- return E_BUFFER_NOT_FULL;
-
- if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long id = ReadID(pReader, pos, len);
-
- if (id < 0) // error
- 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 == mkvmuxer::kMkvCluster)
- return 0; // no entries found
-
- if (id == mkvmuxer::kMkvCues)
- return 0; // no entries found
-
- pos += len; // consume id field
-
- if ((cluster_stop >= 0) && (pos >= cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- // read size field
-
- if ((pos + 1) > avail) {
- len = 1;
- return E_BUFFER_NOT_FULL;
- }
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) // error
- return static_cast<long>(result);
-
- if (result > 0) // underflow
- return E_BUFFER_NOT_FULL;
-
- if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > avail)
- return E_BUFFER_NOT_FULL;
-
- const long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0) // error
- return static_cast<long>(size);
-
- pos += len; // consume size field
-
- // pos now points to start of payload
-
- if ((cluster_stop >= 0) && (pos > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if (size == 0) // weird
- continue;
-
- const long long unknown_size = (1LL << (7 * len)) - 1;
-
- if (size == unknown_size)
- return E_FILE_FORMAT_INVALID; // not supported inside cluster
-
- if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
- return E_FILE_FORMAT_INVALID;
-
- if (id == mkvmuxer::kMkvBlockGroup)
- return 1; // have at least one entry
-
- if (id == mkvmuxer::kMkvSimpleBlock)
- return 1; // have at least one entry
-
- pos += size; // consume payload
- if (cluster_stop >= 0 && pos > cluster_stop)
- return E_FILE_FORMAT_INVALID;
- }
-}
-
-long long Cluster::GetTimeCode() const {
- long long pos;
- long len;
-
- const long status = Load(pos, len);
-
- if (status < 0) // error
- return status;
-
- return m_timecode;
-}
-
-long long Cluster::GetTime() const {
- const long long tc = GetTimeCode();
-
- if (tc < 0)
- return tc;
-
- const SegmentInfo* const pInfo = m_pSegment->GetInfo();
- assert(pInfo);
-
- const long long scale = pInfo->GetTimeCodeScale();
- assert(scale >= 1);
-
- const long long t = m_timecode * scale;
-
- return t;
-}
-
-long long Cluster::GetFirstTime() const {
- const BlockEntry* pEntry;
-
- const long status = GetFirst(pEntry);
-
- if (status < 0) // error
- return status;
-
- if (pEntry == NULL) // empty cluster
- return GetTime();
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- return pBlock->GetTime(this);
-}
-
-long long Cluster::GetLastTime() const {
- const BlockEntry* pEntry;
-
- const long status = GetLast(pEntry);
-
- if (status < 0) // error
- return status;
-
- if (pEntry == NULL) // empty cluster
- return GetTime();
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- return pBlock->GetTime(this);
-}
-
-long Cluster::CreateBlock(long long id,
- long long pos, // absolute pos of payload
- long long size, long long discard_padding) {
- if (id != mkvmuxer::kMkvBlockGroup && id != mkvmuxer::kMkvSimpleBlock)
- return E_PARSE_FAILED;
-
- if (m_entries_count < 0) { // haven't parsed anything yet
- assert(m_entries == NULL);
- assert(m_entries_size == 0);
-
- m_entries_size = 1024;
- m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
- if (m_entries == NULL)
- return -1;
-
- m_entries_count = 0;
- } else {
- assert(m_entries);
- assert(m_entries_size > 0);
- assert(m_entries_count <= m_entries_size);
-
- if (m_entries_count >= m_entries_size) {
- const long entries_size = 2 * m_entries_size;
-
- BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
- if (entries == NULL)
- return -1;
-
- BlockEntry** src = m_entries;
- BlockEntry** const src_end = src + m_entries_count;
-
- BlockEntry** dst = entries;
-
- while (src != src_end)
- *dst++ = *src++;
-
- delete[] m_entries;
-
- m_entries = entries;
- m_entries_size = entries_size;
- }
- }
-
- if (id == mkvmuxer::kMkvBlockGroup)
- return CreateBlockGroup(pos, size, discard_padding);
- else
- return CreateSimpleBlock(pos, size);
-}
-
-long Cluster::CreateBlockGroup(long long start_offset, long long size,
- long long discard_padding) {
- assert(m_entries);
- assert(m_entries_size > 0);
- assert(m_entries_count >= 0);
- assert(m_entries_count < m_entries_size);
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long pos = start_offset;
- const long long stop = start_offset + size;
-
- // For WebM files, there is a bias towards previous reference times
- //(in order to support alt-ref frames, which refer back to the previous
- // keyframe). Normally a 0 value is not possible, but here we tenatively
- // allow 0 as the value of a reference frame, with the interpretation
- // that this is a "previous" reference time.
-
- long long prev = 1; // nonce
- long long next = 0; // nonce
- long long duration = -1; // really, this is unsigned
-
- long long bpos = -1;
- long long bsize = -1;
-
- while (pos < stop) {
- long len;
- const long long id = ReadID(pReader, pos, len);
- if (id < 0 || (pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- 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 == mkvmuxer::kMkvBlock) {
- if (bpos < 0) { // Block ID
- bpos = pos;
- bsize = size;
- }
- } else if (id == mkvmuxer::kMkvBlockDuration) {
- if (size > 8)
- return E_FILE_FORMAT_INVALID;
-
- duration = UnserializeUInt(pReader, pos, size);
-
- if (duration < 0)
- return E_FILE_FORMAT_INVALID;
- } else if (id == mkvmuxer::kMkvReferenceBlock) {
- if (size > 8 || size <= 0)
- return E_FILE_FORMAT_INVALID;
- const long size_ = static_cast<long>(size);
-
- long long time;
-
- long status = UnserializeInt(pReader, pos, size_, time);
- assert(status == 0);
- if (status != 0)
- return -1;
-
- if (time <= 0) // see note above
- prev = time;
- else
- next = time;
- }
-
- pos += size; // consume payload
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
- }
- if (bpos < 0)
- return E_FILE_FORMAT_INVALID;
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- assert(bsize >= 0);
-
- const long idx = m_entries_count;
-
- BlockEntry** const ppEntry = m_entries + idx;
- BlockEntry*& pEntry = *ppEntry;
-
- pEntry = new (std::nothrow)
- BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
-
- if (pEntry == NULL)
- return -1; // generic error
-
- BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
-
- const long status = p->Parse();
-
- if (status == 0) { // success
- ++m_entries_count;
- return 0;
- }
-
- delete pEntry;
- pEntry = 0;
-
- return status;
-}
-
-long Cluster::CreateSimpleBlock(long long st, long long sz) {
- assert(m_entries);
- assert(m_entries_size > 0);
- assert(m_entries_count >= 0);
- assert(m_entries_count < m_entries_size);
-
- const long idx = m_entries_count;
-
- BlockEntry** const ppEntry = m_entries + idx;
- BlockEntry*& pEntry = *ppEntry;
-
- pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
-
- if (pEntry == NULL)
- return -1; // generic error
-
- SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
-
- const long status = p->Parse();
-
- if (status == 0) {
- ++m_entries_count;
- return 0;
- }
-
- delete pEntry;
- pEntry = 0;
-
- return status;
-}
-
-long Cluster::GetFirst(const BlockEntry*& pFirst) const {
- if (m_entries_count <= 0) {
- long long pos;
- long len;
-
- const long status = Parse(pos, len);
-
- if (status < 0) { // error
- pFirst = NULL;
- return status;
- }
-
- if (m_entries_count <= 0) { // empty cluster
- pFirst = NULL;
- return 0;
- }
- }
-
- assert(m_entries);
-
- pFirst = m_entries[0];
- assert(pFirst);
-
- return 0; // success
-}
-
-long Cluster::GetLast(const BlockEntry*& pLast) const {
- for (;;) {
- long long pos;
- long len;
-
- const long status = Parse(pos, len);
-
- if (status < 0) { // error
- pLast = NULL;
- return status;
- }
-
- if (status > 0) // no new block
- break;
- }
-
- if (m_entries_count <= 0) {
- pLast = NULL;
- return 0;
- }
-
- assert(m_entries);
-
- const long idx = m_entries_count - 1;
-
- pLast = m_entries[idx];
- assert(pLast);
-
- return 0;
-}
-
-long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
- assert(pCurr);
- assert(m_entries);
- assert(m_entries_count > 0);
-
- size_t idx = pCurr->GetIndex();
- assert(idx < size_t(m_entries_count));
- assert(m_entries[idx] == pCurr);
-
- ++idx;
-
- if (idx >= size_t(m_entries_count)) {
- long long pos;
- long len;
-
- const long status = Parse(pos, len);
-
- if (status < 0) { // error
- pNext = NULL;
- return status;
- }
-
- if (status > 0) {
- pNext = NULL;
- return 0;
- }
-
- assert(m_entries);
- assert(m_entries_count > 0);
- assert(idx < size_t(m_entries_count));
- }
-
- pNext = m_entries[idx];
- assert(pNext);
-
- return 0;
-}
-
-long Cluster::GetEntryCount() const { return m_entries_count; }
-
-const BlockEntry* Cluster::GetEntry(const Track* pTrack,
- long long time_ns) const {
- assert(pTrack);
-
- if (m_pSegment == NULL) // this is the special EOS cluster
- return pTrack->GetEOS();
-
- const BlockEntry* pResult = pTrack->GetEOS();
-
- long index = 0;
-
- for (;;) {
- if (index >= m_entries_count) {
- long long pos;
- long len;
-
- const long status = Parse(pos, len);
- assert(status >= 0);
-
- if (status > 0) // completely parsed, and no more entries
- return pResult;
-
- if (status < 0) // should never happen
- return 0;
-
- assert(m_entries);
- assert(index < m_entries_count);
- }
-
- const BlockEntry* const pEntry = m_entries[index];
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
- ++index;
- 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)
- return pResult;
-
- pResult = pEntry; // have a candidate
- } else if (time_ns >= 0) {
- const long long ns = pBlock->GetTime(this);
-
- if (ns > time_ns)
- return pResult;
- }
-
- ++index;
- }
-}
-
-const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
- const CuePoint::TrackPosition& tp) const {
- assert(m_pSegment);
- const long long tc = cp.GetTimeCode();
-
- if (tp.m_block > 0) {
- const long block = static_cast<long>(tp.m_block);
- const long index = block - 1;
-
- while (index >= m_entries_count) {
- long long pos;
- long len;
-
- const long status = Parse(pos, len);
-
- if (status < 0) // TODO: can this happen?
- return NULL;
-
- if (status > 0) // nothing remains to be parsed
- return NULL;
- }
-
- 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;
- }
- }
-
- long index = 0;
-
- for (;;) {
- if (index >= m_entries_count) {
- long long pos;
- long len;
-
- const long status = Parse(pos, len);
-
- if (status < 0) // TODO: can this happen?
- return NULL;
-
- if (status > 0) // nothing remains to be parsed
- return NULL;
-
- assert(m_entries);
- assert(index < m_entries_count);
- }
-
- 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) {
- ++index;
- continue;
- }
-
- const long long tc_ = pBlock->GetTimeCode(this);
-
- if (tc_ < tc) {
- ++index;
- 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;
- }
-}
-
-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,
- long long size)
- : 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,
- long long block_size, long long prev, long long next,
- long long duration, long long discard_padding)
- : BlockEntry(pCluster, idx),
- m_block(block_start, block_size, discard_padding),
- m_prev(prev),
- m_next(next),
- m_duration(duration) {}
-
-long BlockGroup::Parse() {
- const long status = m_block.Parse(m_pCluster);
-
- if (status)
- return status;
-
- m_block.SetKey((m_prev > 0) && (m_next <= 0));
-
- return 0;
-}
-
-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)
- : m_start(start),
- m_size(size_),
- m_track(0),
- m_timecode(-1),
- m_flags(0),
- m_frames(NULL),
- m_frame_count(-1),
- m_discard_padding(discard_padding) {}
-
-Block::~Block() { delete[] m_frames; }
-
-long Block::Parse(const Cluster* pCluster) {
- if (pCluster == NULL)
- return -1;
-
- if (pCluster->m_pSegment == NULL)
- return -1;
-
- assert(m_start >= 0);
- assert(m_size >= 0);
- assert(m_track <= 0);
- assert(m_frames == NULL);
- assert(m_frame_count <= 0);
-
- long long pos = m_start;
- const long long stop = m_start + m_size;
-
- long len;
-
- IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
-
- m_track = ReadUInt(pReader, pos, len);
-
- if (m_track <= 0)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume track number
-
- if ((stop - pos) < 2)
- return E_FILE_FORMAT_INVALID;
-
- long status;
- long long value;
-
- status = UnserializeInt(pReader, pos, 2, value);
-
- if (status)
- return E_FILE_FORMAT_INVALID;
-
- if (value < SHRT_MIN)
- return E_FILE_FORMAT_INVALID;
-
- if (value > SHRT_MAX)
- return E_FILE_FORMAT_INVALID;
-
- m_timecode = static_cast<short>(value);
-
- pos += 2;
-
- if ((stop - pos) <= 0)
- return E_FILE_FORMAT_INVALID;
-
- status = pReader->Read(pos, 1, &m_flags);
-
- if (status)
- return E_FILE_FORMAT_INVALID;
-
- const int lacing = int(m_flags & 0x06) >> 1;
-
- ++pos; // consume flags byte
-
- if (lacing == 0) { // no lacing
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
-
- m_frame_count = 1;
- m_frames = new (std::nothrow) Frame[m_frame_count];
- if (m_frames == NULL)
- return -1;
-
- Frame& f = m_frames[0];
- f.pos = pos;
-
- const long long frame_size = stop - pos;
-
- if (frame_size > LONG_MAX || frame_size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- f.len = static_cast<long>(frame_size);
-
- return 0; // success
- }
-
- if (pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- unsigned char biased_count;
-
- status = pReader->Read(pos, 1, &biased_count);
-
- if (status)
- return E_FILE_FORMAT_INVALID;
-
- ++pos; // consume frame count
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
-
- m_frame_count = int(biased_count) + 1;
-
- m_frames = new (std::nothrow) Frame[m_frame_count];
- if (m_frames == NULL)
- return -1;
-
- if (!m_frames)
- return E_FILE_FORMAT_INVALID;
-
- if (lacing == 1) { // Xiph
- Frame* pf = m_frames;
- Frame* const pf_end = pf + m_frame_count;
-
- long long size = 0;
- int frame_count = m_frame_count;
-
- while (frame_count > 1) {
- long frame_size = 0;
-
- for (;;) {
- unsigned char val;
-
- if (pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- status = pReader->Read(pos, 1, &val);
-
- if (status)
- return E_FILE_FORMAT_INVALID;
-
- ++pos; // consume xiph size byte
-
- frame_size += val;
-
- if (val < 255)
- break;
- }
-
- Frame& f = *pf++;
- assert(pf < pf_end);
- if (pf >= pf_end)
- return E_FILE_FORMAT_INVALID;
-
- 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
-
- --frame_count;
- }
-
- if (pf >= pf_end || pos > stop)
- return E_FILE_FORMAT_INVALID;
-
- {
- Frame& f = *pf++;
-
- if (pf != pf_end)
- return E_FILE_FORMAT_INVALID;
-
- f.pos = 0; // patch later
-
- const long long total_size = stop - pos;
-
- if (total_size < size)
- return E_FILE_FORMAT_INVALID;
-
- const long long frame_size = total_size - size;
-
- if (frame_size > LONG_MAX || frame_size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- f.len = static_cast<long>(frame_size);
- }
-
- pf = m_frames;
- while (pf != pf_end) {
- Frame& f = *pf++;
- assert((pos + f.len) <= stop);
-
- if ((pos + f.len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- f.pos = pos;
- pos += f.len;
- }
-
- assert(pos == stop);
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- } 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)
- return E_FILE_FORMAT_INVALID;
-
- const long long frame_size = total_size / m_frame_count;
-
- if (frame_size > LONG_MAX || frame_size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- Frame* pf = m_frames;
- Frame* const pf_end = pf + m_frame_count;
-
- while (pf != pf_end) {
- assert((pos + frame_size) <= stop);
- if ((pos + frame_size) > stop)
- return E_FILE_FORMAT_INVALID;
-
- Frame& f = *pf++;
-
- f.pos = pos;
- f.len = static_cast<long>(frame_size);
-
- pos += frame_size;
- }
-
- assert(pos == stop);
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
-
- } else {
- assert(lacing == 3); // EBML lacing
-
- if (pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- long long size = 0;
- int frame_count = m_frame_count;
-
- long long frame_size = ReadUInt(pReader, pos, len);
-
- if (frame_size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- if (frame_size > LONG_MAX)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume length of size of first frame
-
- if ((pos + frame_size) > stop)
- return E_FILE_FORMAT_INVALID;
-
- Frame* pf = m_frames;
- Frame* const pf_end = pf + m_frame_count;
-
- {
- Frame& curr = *pf;
-
- curr.pos = 0; // patch later
-
- curr.len = static_cast<long>(frame_size);
- size += curr.len; // contribution of this frame
- }
-
- --frame_count;
-
- while (frame_count > 1) {
- if (pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- assert(pf < pf_end);
- if (pf >= pf_end)
- return E_FILE_FORMAT_INVALID;
-
-
- const Frame& prev = *pf++;
- assert(prev.len == frame_size);
- if (prev.len != frame_size)
- return E_FILE_FORMAT_INVALID;
-
- assert(pf < pf_end);
- if (pf >= pf_end)
- return E_FILE_FORMAT_INVALID;
-
- Frame& curr = *pf;
-
- curr.pos = 0; // patch later
-
- const long long delta_size_ = ReadUInt(pReader, pos, len);
-
- if (delta_size_ < 0)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; // consume length of (delta) size
- if (pos > stop)
- return E_FILE_FORMAT_INVALID;
-
- const int exp = 7 * len - 1;
- const long long bias = (1LL << exp) - 1LL;
- const long long delta_size = delta_size_ - bias;
-
- frame_size += delta_size;
-
- if (frame_size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- if (frame_size > LONG_MAX)
- return E_FILE_FORMAT_INVALID;
-
- curr.len = static_cast<long>(frame_size);
- size += curr.len; // contribution of this frame
-
- --frame_count;
- }
-
- // parse last frame
- if (frame_count > 0) {
- if (pos > stop || pf >= pf_end)
- return E_FILE_FORMAT_INVALID;
-
- const Frame& prev = *pf++;
- assert(prev.len == frame_size);
- if (prev.len != frame_size)
- return E_FILE_FORMAT_INVALID;
-
- if (pf >= pf_end)
- return E_FILE_FORMAT_INVALID;
-
- Frame& curr = *pf++;
- if (pf != pf_end)
- return E_FILE_FORMAT_INVALID;
-
- curr.pos = 0; // patch later
-
- const long long total_size = stop - pos;
-
- if (total_size < size)
- return E_FILE_FORMAT_INVALID;
-
- frame_size = total_size - size;
-
- if (frame_size > LONG_MAX || frame_size <= 0)
- return E_FILE_FORMAT_INVALID;
-
- curr.len = static_cast<long>(frame_size);
- }
-
- pf = m_frames;
- while (pf != pf_end) {
- Frame& f = *pf++;
- assert((pos + f.len) <= stop);
- if ((pos + f.len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- f.pos = pos;
- pos += f.len;
- }
-
- if (pos != stop)
- return E_FILE_FORMAT_INVALID;
- }
-
- return 0; // success
-}
-
-long long Block::GetTimeCode(const Cluster* pCluster) const {
- if (pCluster == 0)
- return m_timecode;
-
- const long long tc0 = pCluster->GetTimeCode();
- assert(tc0 >= 0);
-
- const long long tc = tc0 + m_timecode;
-
- return tc; // unscaled timecode units
-}
-
-long long Block::GetTime(const Cluster* pCluster) const {
- assert(pCluster);
-
- const long long tc = GetTimeCode(pCluster);
-
- const Segment* const pSegment = pCluster->m_pSegment;
- const SegmentInfo* const pInfo = pSegment->GetInfo();
- assert(pInfo);
-
- const long long scale = pInfo->GetTimeCodeScale();
- assert(scale >= 1);
-
- const long long ns = tc * scale;
-
- return ns;
-}
-
-long long Block::GetTrackNumber() const { return m_track; }
-
-bool Block::IsKey() const {
- return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
-}
-
-void Block::SetKey(bool bKey) {
- if (bKey)
- m_flags |= static_cast<unsigned char>(1 << 7);
- else
- m_flags &= 0x7F;
-}
-
-bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
-
-Block::Lacing Block::GetLacing() const {
- const int value = int(m_flags & 0x06) >> 1;
- return static_cast<Lacing>(value);
-}
-
-int Block::GetFrameCount() const { return m_frame_count; }
-
-const Block::Frame& Block::GetFrame(int idx) const {
- assert(idx >= 0);
- assert(idx < m_frame_count);
-
- const Frame& f = m_frames[idx];
- assert(f.pos > 0);
- assert(f.len > 0);
-
- return f;
-}
-
-long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
- assert(pReader);
- assert(buf);
-
- const long status = pReader->Read(pos, len, buf);
- return status;
-}
-
-long long Block::GetDiscardPadding() const { return m_discard_padding; }
-
-} // end namespace mkvparser
--- a/third_party/libwebm/mkvparser.hpp
+++ /dev/null
@@ -1,1025 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef MKVPARSER_HPP
-#define MKVPARSER_HPP
-
-#include <cstddef>
-#include <cstdio>
-#include <cstdlib>
-
-namespace mkvparser {
-
-const int E_PARSE_FAILED = -1;
-const int E_FILE_FORMAT_INVALID = -2;
-const int E_BUFFER_NOT_FULL = -3;
-
-class IMkvReader {
- public:
- virtual int Read(long long pos, long len, unsigned char* buf) = 0;
- virtual int Length(long long* total, long long* available) = 0;
-
- protected:
- virtual ~IMkvReader();
-};
-
-template<typename Type> Type* SafeArrayAlloc(unsigned long long num_elements,
- unsigned long long element_size);
-long long GetUIntLength(IMkvReader*, long long, long&);
-long long ReadUInt(IMkvReader*, long long, long&);
-long long ReadID(IMkvReader* pReader, long long pos, long& len);
-long long UnserializeUInt(IMkvReader*, long long pos, long long size);
-
-long UnserializeFloat(IMkvReader*, long long pos, long long size, double&);
-long UnserializeInt(IMkvReader*, long long pos, long long size,
- long long& result);
-
-long UnserializeString(IMkvReader*, long long pos, long long size, char*& str);
-
-long ParseElementHeader(IMkvReader* pReader,
- long long& pos, // consume id and size fields
- long long stop, // if you know size of element's parent
- long long& id, long long& size);
-
-bool Match(IMkvReader*, long long&, unsigned long, long long&);
-bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&);
-
-void GetVersion(int& major, int& minor, int& build, int& revision);
-
-struct EBMLHeader {
- EBMLHeader();
- ~EBMLHeader();
- long long m_version;
- long long m_readVersion;
- long long m_maxIdLength;
- long long m_maxSizeLength;
- char* m_docType;
- long long m_docTypeVersion;
- long long m_docTypeReadVersion;
-
- long long Parse(IMkvReader*, long long&);
- void Init();
-};
-
-class Segment;
-class Track;
-class Cluster;
-
-class Block {
- Block(const Block&);
- Block& operator=(const Block&);
-
- public:
- const long long m_start;
- const long long m_size;
-
- Block(long long start, long long size, long long discard_padding);
- ~Block();
-
- long Parse(const Cluster*);
-
- long long GetTrackNumber() const;
- long long GetTimeCode(const Cluster*) const; // absolute, but not scaled
- long long GetTime(const Cluster*) const; // absolute, and scaled (ns)
- bool IsKey() const;
- void SetKey(bool);
- bool IsInvisible() const;
-
- enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml };
- Lacing GetLacing() const;
-
- int GetFrameCount() const; // to index frames: [0, count)
-
- struct Frame {
- long long pos; // absolute offset
- long len;
-
- long Read(IMkvReader*, unsigned char*) const;
- };
-
- const Frame& GetFrame(int frame_index) const;
-
- long long GetDiscardPadding() const;
-
- private:
- long long m_track; // Track::Number()
- short m_timecode; // relative to cluster
- unsigned char m_flags;
-
- Frame* m_frames;
- int m_frame_count;
-
- protected:
- const long long m_discard_padding;
-};
-
-class BlockEntry {
- BlockEntry(const BlockEntry&);
- BlockEntry& operator=(const BlockEntry&);
-
- protected:
- BlockEntry(Cluster*, long index);
-
- public:
- virtual ~BlockEntry();
-
- bool EOS() const;
- const Cluster* GetCluster() const;
- long GetIndex() const;
- virtual const Block* GetBlock() const = 0;
-
- enum Kind { kBlockEOS, kBlockSimple, kBlockGroup };
- virtual Kind GetKind() const = 0;
-
- protected:
- Cluster* const m_pCluster;
- const long m_index;
-};
-
-class SimpleBlock : public BlockEntry {
- SimpleBlock(const SimpleBlock&);
- SimpleBlock& operator=(const SimpleBlock&);
-
- public:
- SimpleBlock(Cluster*, long index, long long start, long long size);
- long Parse();
-
- Kind GetKind() const;
- const Block* GetBlock() const;
-
- protected:
- Block m_block;
-};
-
-class BlockGroup : public BlockEntry {
- BlockGroup(const BlockGroup&);
- BlockGroup& operator=(const BlockGroup&);
-
- public:
- BlockGroup(Cluster*, long index,
- long long block_start, // absolute pos of block's payload
- long long block_size, // size of block's payload
- long long prev, long long next, long long duration,
- long long discard_padding);
-
- long Parse();
-
- Kind GetKind() const;
- const Block* GetBlock() const;
-
- long long GetPrevTimeCode() const; // relative to block's time
- long long GetNextTimeCode() const; // as above
- long long GetDurationTimeCode() const;
-
- private:
- Block m_block;
- const long long m_prev;
- const long long m_next;
- const long long m_duration;
-};
-
-///////////////////////////////////////////////////////////////
-// ContentEncoding element
-// Elements used to describe if the track data has been encrypted or
-// compressed with zlib or header stripping.
-class ContentEncoding {
- public:
- enum { kCTR = 1 };
-
- ContentEncoding();
- ~ContentEncoding();
-
- // ContentCompression element names
- struct ContentCompression {
- ContentCompression();
- ~ContentCompression();
-
- unsigned long long algo;
- unsigned char* settings;
- long long settings_len;
- };
-
- // ContentEncAESSettings element names
- struct ContentEncAESSettings {
- ContentEncAESSettings() : cipher_mode(kCTR) {}
- ~ContentEncAESSettings() {}
-
- unsigned long long cipher_mode;
- };
-
- // ContentEncryption element names
- struct ContentEncryption {
- ContentEncryption();
- ~ContentEncryption();
-
- unsigned long long algo;
- unsigned char* key_id;
- long long key_id_len;
- unsigned char* signature;
- long long signature_len;
- unsigned char* sig_key_id;
- long long sig_key_id_len;
- unsigned long long sig_algo;
- unsigned long long sig_hash_algo;
-
- ContentEncAESSettings aes_settings;
- };
-
- // Returns ContentCompression represented by |idx|. Returns NULL if |idx|
- // is out of bounds.
- const ContentCompression* GetCompressionByIndex(unsigned long idx) const;
-
- // Returns number of ContentCompression elements in this ContentEncoding
- // element.
- unsigned long GetCompressionCount() const;
-
- // Parses the ContentCompression element from |pReader|. |start| is the
- // starting offset of the ContentCompression payload. |size| is the size in
- // bytes of the ContentCompression payload. |compression| is where the parsed
- // values will be stored.
- long ParseCompressionEntry(long long start, long long size,
- IMkvReader* pReader,
- ContentCompression* compression);
-
- // Returns ContentEncryption represented by |idx|. Returns NULL if |idx|
- // is out of bounds.
- const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const;
-
- // Returns number of ContentEncryption elements in this ContentEncoding
- // element.
- unsigned long GetEncryptionCount() const;
-
- // Parses the ContentEncAESSettings element from |pReader|. |start| is the
- // starting offset of the ContentEncAESSettings payload. |size| is the
- // size in bytes of the ContentEncAESSettings payload. |encryption| is
- // where the parsed values will be stored.
- long ParseContentEncAESSettingsEntry(long long start, long long size,
- IMkvReader* pReader,
- ContentEncAESSettings* aes);
-
- // Parses the ContentEncoding element from |pReader|. |start| is the
- // starting offset of the ContentEncoding payload. |size| is the size in
- // bytes of the ContentEncoding payload. Returns true on success.
- long ParseContentEncodingEntry(long long start, long long size,
- IMkvReader* pReader);
-
- // Parses the ContentEncryption element from |pReader|. |start| is the
- // starting offset of the ContentEncryption payload. |size| is the size in
- // bytes of the ContentEncryption payload. |encryption| is where the parsed
- // values will be stored.
- long ParseEncryptionEntry(long long start, long long size,
- IMkvReader* pReader, ContentEncryption* encryption);
-
- unsigned long long encoding_order() const { return encoding_order_; }
- unsigned long long encoding_scope() const { return encoding_scope_; }
- unsigned long long encoding_type() const { return encoding_type_; }
-
- private:
- // Member variables for list of ContentCompression elements.
- ContentCompression** compression_entries_;
- ContentCompression** compression_entries_end_;
-
- // Member variables for list of ContentEncryption elements.
- ContentEncryption** encryption_entries_;
- ContentEncryption** encryption_entries_end_;
-
- // ContentEncoding element names
- unsigned long long encoding_order_;
- unsigned long long encoding_scope_;
- unsigned long long encoding_type_;
-
- // LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
- ContentEncoding(const ContentEncoding&);
- ContentEncoding& operator=(const ContentEncoding&);
-};
-
-class Track {
- Track(const Track&);
- Track& operator=(const Track&);
-
- public:
- class Info;
- static long Create(Segment*, const Info&, long long element_start,
- long long element_size, Track*&);
-
- enum Type { kVideo = 1, kAudio = 2, kSubtitle = 0x11, kMetadata = 0x21 };
-
- Segment* const m_pSegment;
- const long long m_element_start;
- const long long m_element_size;
- virtual ~Track();
-
- long GetType() const;
- long GetNumber() const;
- unsigned long long GetUid() const;
- const char* GetNameAsUTF8() const;
- const char* GetLanguage() const;
- const char* GetCodecNameAsUTF8() const;
- const char* GetCodecId() const;
- const unsigned char* GetCodecPrivate(size_t&) const;
- bool GetLacing() const;
- unsigned long long GetDefaultDuration() const;
- unsigned long long GetCodecDelay() const;
- unsigned long long GetSeekPreRoll() const;
-
- const BlockEntry* GetEOS() const;
-
- struct Settings {
- long long start;
- long long size;
- };
-
- class Info {
- public:
- Info();
- ~Info();
- int Copy(Info&) const;
- void Clear();
- long type;
- long number;
- unsigned long long uid;
- unsigned long long defaultDuration;
- unsigned long long codecDelay;
- unsigned long long seekPreRoll;
- char* nameAsUTF8;
- char* language;
- char* codecId;
- char* codecNameAsUTF8;
- unsigned char* codecPrivate;
- size_t codecPrivateSize;
- bool lacing;
- Settings settings;
-
- private:
- Info(const Info&);
- Info& operator=(const Info&);
- int CopyStr(char* Info::*str, Info&) const;
- };
-
- long GetFirst(const BlockEntry*&) const;
- long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
- virtual bool VetEntry(const BlockEntry*) const;
- virtual long Seek(long long time_ns, const BlockEntry*&) const;
-
- const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const;
- unsigned long GetContentEncodingCount() const;
-
- long ParseContentEncodingsEntry(long long start, long long size);
-
- protected:
- Track(Segment*, long long element_start, long long element_size);
-
- Info m_info;
-
- class EOSBlock : public BlockEntry {
- public:
- EOSBlock();
-
- Kind GetKind() const;
- const Block* GetBlock() const;
- };
-
- EOSBlock m_eos;
-
- private:
- ContentEncoding** content_encoding_entries_;
- ContentEncoding** content_encoding_entries_end_;
-};
-
-class VideoTrack : public Track {
- VideoTrack(const VideoTrack&);
- VideoTrack& operator=(const VideoTrack&);
-
- VideoTrack(Segment*, long long element_start, long long element_size);
-
- public:
- static long Parse(Segment*, const Info&, long long element_start,
- long long element_size, VideoTrack*&);
-
- 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;
- long Seek(long long time_ns, const BlockEntry*&) const;
-
- 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;
-};
-
-class AudioTrack : public Track {
- AudioTrack(const AudioTrack&);
- AudioTrack& operator=(const AudioTrack&);
-
- AudioTrack(Segment*, long long element_start, long long element_size);
-
- public:
- static long Parse(Segment*, const Info&, long long element_start,
- long long element_size, AudioTrack*&);
-
- double GetSamplingRate() const;
- long long GetChannels() const;
- long long GetBitDepth() const;
-
- private:
- double m_rate;
- long long m_channels;
- long long m_bitDepth;
-};
-
-class Tracks {
- Tracks(const Tracks&);
- Tracks& operator=(const Tracks&);
-
- 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;
-
- Tracks(Segment*, long long start, long long size, long long element_start,
- long long element_size);
-
- ~Tracks();
-
- long Parse();
-
- unsigned long GetTracksCount() const;
-
- const Track* GetTrackByNumber(long tn) const;
- const Track* GetTrackByIndex(unsigned long idx) const;
-
- private:
- Track** m_trackEntries;
- Track** m_trackEntriesEnd;
-
- long ParseTrackEntry(long long payload_start, long long payload_size,
- long long element_start, long long element_size,
- Track*&) const;
-};
-
-class Chapters {
- Chapters(const Chapters&);
- Chapters& operator=(const Chapters&);
-
- 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;
-
- Chapters(Segment*, long long payload_start, long long payload_size,
- long long element_start, long long element_size);
-
- ~Chapters();
-
- long Parse();
-
- class Atom;
- class Edition;
-
- class Display {
- friend class Atom;
- Display();
- Display(const Display&);
- ~Display();
- Display& operator=(const Display&);
-
- public:
- const char* GetString() const;
- const char* GetLanguage() const;
- const char* GetCountry() const;
-
- private:
- void Init();
- void ShallowCopy(Display&) const;
- void Clear();
- long Parse(IMkvReader*, long long pos, long long size);
-
- char* m_string;
- char* m_language;
- char* m_country;
- };
-
- class Atom {
- friend class Edition;
- Atom();
- Atom(const Atom&);
- ~Atom();
- Atom& operator=(const Atom&);
-
- public:
- unsigned long long GetUID() const;
- const char* GetStringUID() const;
-
- long long GetStartTimecode() const;
- long long GetStopTimecode() const;
-
- long long GetStartTime(const Chapters*) const;
- long long GetStopTime(const Chapters*) const;
-
- int GetDisplayCount() const;
- const Display* GetDisplay(int index) const;
-
- private:
- void Init();
- void ShallowCopy(Atom&) const;
- void Clear();
- long Parse(IMkvReader*, long long pos, long long size);
- static long long GetTime(const Chapters*, long long timecode);
-
- long ParseDisplay(IMkvReader*, long long pos, long long size);
- bool ExpandDisplaysArray();
-
- char* m_string_uid;
- unsigned long long m_uid;
- long long m_start_timecode;
- long long m_stop_timecode;
-
- Display* m_displays;
- int m_displays_size;
- int m_displays_count;
- };
-
- class Edition {
- friend class Chapters;
- Edition();
- Edition(const Edition&);
- ~Edition();
- Edition& operator=(const Edition&);
-
- public:
- int GetAtomCount() const;
- const Atom* GetAtom(int index) const;
-
- private:
- void Init();
- void ShallowCopy(Edition&) const;
- void Clear();
- long Parse(IMkvReader*, long long pos, long long size);
-
- long ParseAtom(IMkvReader*, long long pos, long long size);
- bool ExpandAtomsArray();
-
- Atom* m_atoms;
- int m_atoms_size;
- int m_atoms_count;
- };
-
- int GetEditionCount() const;
- const Edition* GetEdition(int index) const;
-
- private:
- long ParseEdition(long long pos, long long size);
- bool ExpandEditionsArray();
-
- Edition* m_editions;
- int m_editions_size;
- 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&);
-
- 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;
-
- SegmentInfo(Segment*, long long start, long long size,
- long long element_start, long long element_size);
-
- ~SegmentInfo();
-
- long Parse();
-
- long long GetTimeCodeScale() const;
- long long GetDuration() const; // scaled
- const char* GetMuxingAppAsUTF8() const;
- const char* GetWritingAppAsUTF8() const;
- const char* GetTitleAsUTF8() const;
-
- private:
- long long m_timecodeScale;
- double m_duration;
- char* m_pMuxingAppAsUTF8;
- char* m_pWritingAppAsUTF8;
- char* m_pTitleAsUTF8;
-};
-
-class SeekHead {
- SeekHead(const SeekHead&);
- SeekHead& operator=(const SeekHead&);
-
- 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;
-
- SeekHead(Segment*, long long start, long long size, long long element_start,
- long long element_size);
-
- ~SeekHead();
-
- long Parse();
-
- struct Entry {
- // the SeekHead entry payload
- long long id;
- long long pos;
-
- // absolute pos of SeekEntry ID
- long long element_start;
-
- // SeekEntry ID size + size size + payload
- long long element_size;
- };
-
- int GetCount() const;
- const Entry* GetEntry(int idx) const;
-
- struct VoidElement {
- // absolute pos of Void ID
- long long element_start;
-
- // ID size + size size + payload size
- long long element_size;
- };
-
- int GetVoidElementCount() const;
- const VoidElement* GetVoidElement(int idx) const;
-
- private:
- Entry* m_entries;
- int m_entry_count;
-
- VoidElement* m_void_elements;
- int m_void_element_count;
-
- static bool ParseEntry(IMkvReader*,
- long long pos, // payload
- long long size, Entry*);
-};
-
-class Cues;
-class CuePoint {
- friend class Cues;
-
- CuePoint(long, long long);
- ~CuePoint();
-
- CuePoint(const CuePoint&);
- CuePoint& operator=(const CuePoint&);
-
- public:
- long long m_element_start;
- long long m_element_size;
-
- bool Load(IMkvReader*);
-
- long long GetTimeCode() const; // absolute but unscaled
- long long GetTime(const Segment*) const; // absolute and scaled (ns units)
-
- struct TrackPosition {
- long long m_track;
- long long m_pos; // of cluster
- long long m_block;
- // codec_state //defaults to 0
- // reference = clusters containing req'd referenced blocks
- // reftime = timecode of the referenced block
-
- bool Parse(IMkvReader*, long long, long long);
- };
-
- const TrackPosition* Find(const Track*) const;
-
- private:
- const long m_index;
- long long m_timecode;
- TrackPosition* m_track_positions;
- size_t m_track_positions_count;
-};
-
-class Cues {
- friend class Segment;
-
- Cues(Segment*, long long start, long long size, long long element_start,
- long long element_size);
- ~Cues();
-
- Cues(const Cues&);
- Cues& operator=(const Cues&);
-
- 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;
-
- bool Find( // lower bound of time_ns
- long long time_ns, const Track*, const CuePoint*&,
- const CuePoint::TrackPosition*&) const;
-
- const CuePoint* GetFirst() const;
- const CuePoint* GetLast() const;
- const CuePoint* GetNext(const CuePoint*) const;
-
- const BlockEntry* GetBlock(const CuePoint*,
- const CuePoint::TrackPosition*) const;
-
- bool LoadCuePoint() const;
- long GetCount() const; // loaded only
- // long GetTotal() const; //loaded + preloaded
- bool DoneParsing() const;
-
- private:
- bool Init() const;
- bool PreloadCuePoint(long&, long long) const;
-
- mutable CuePoint** m_cue_points;
- mutable long m_count;
- mutable long m_preload_count;
- mutable long long m_pos;
-};
-
-class Cluster {
- friend class Segment;
-
- Cluster(const Cluster&);
- Cluster& operator=(const Cluster&);
-
- public:
- Segment* const m_pSegment;
-
- public:
- static Cluster* Create(Segment*,
- long index, // index in segment
- long long off); // offset relative to segment
- // long long element_size);
-
- Cluster(); // EndOfStream
- ~Cluster();
-
- bool EOS() const;
-
- long long GetTimeCode() const; // absolute, but not scaled
- long long GetTime() const; // absolute, and scaled (nanosecond units)
- long long GetFirstTime() const; // time (ns) of first (earliest) block
- long long GetLastTime() const; // time (ns) of last (latest) block
-
- long GetFirst(const BlockEntry*&) const;
- long GetLast(const BlockEntry*&) const;
- long GetNext(const BlockEntry* curr, const BlockEntry*& next) const;
-
- const BlockEntry* GetEntry(const Track*, long long ns = -1) const;
- const BlockEntry* GetEntry(const CuePoint&,
- const CuePoint::TrackPosition&) const;
- // const BlockEntry* GetMaxKey(const VideoTrack*) const;
-
- // static bool HasBlockEntries(const Segment*, long long);
-
- static long HasBlockEntries(const Segment*, long long idoff, long long& pos,
- long& size);
-
- long GetEntryCount() const;
-
- long Load(long long& pos, long& size) const;
-
- long Parse(long long& pos, long& size) const;
- long GetEntry(long index, const mkvparser::BlockEntry*&) const;
-
- protected:
- Cluster(Segment*, long index, long long element_start);
- // long long element_size);
-
- public:
- const long long m_element_start;
- long long GetPosition() const; // offset relative to segment
-
- long GetIndex() const;
- long long GetElementSize() const;
- // long long GetPayloadSize() const;
-
- // long long Unparsed() const;
-
- private:
- long m_index;
- mutable long long m_pos;
- // mutable long long m_size;
- mutable long long m_element_size;
- mutable long long m_timecode;
- mutable BlockEntry** m_entries;
- mutable long m_entries_size;
- mutable long m_entries_count;
-
- long ParseSimpleBlock(long long, long long&, long&);
- long ParseBlockGroup(long long, long long&, long&);
-
- long CreateBlock(long long id, long long pos, long long size,
- long long discard_padding);
- long CreateBlockGroup(long long start_offset, long long size,
- long long discard_padding);
- long CreateSimpleBlock(long long, long long);
-};
-
-class Segment {
- friend class Cues;
- friend class Track;
- friend class VideoTrack;
-
- Segment(const Segment&);
- Segment& operator=(const Segment&);
-
- private:
- Segment(IMkvReader*, long long elem_start,
- // long long elem_size,
- long long pos, long long size);
-
- public:
- IMkvReader* const m_pReader;
- const long long m_element_start;
- // const long long m_element_size;
- const long long m_start; // posn of segment payload
- const long long m_size; // size of segment payload
- Cluster m_eos; // TODO: make private?
-
- static long long CreateInstance(IMkvReader*, long long, Segment*&);
- ~Segment();
-
- long Load(); // loads headers and all clusters
-
- // for incremental loading
- // long long Unparsed() const;
- bool DoneParsing() const;
- long long ParseHeaders(); // stops when first cluster is found
- // long FindNextCluster(long long& pos, long& size) const;
- long LoadCluster(long long& pos, long& size); // load one cluster
- long LoadCluster();
-
- long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos,
- long& size);
-
- 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;
-
- unsigned long GetCount() const;
- const Cluster* GetFirst() const;
- const Cluster* GetLast() const;
- const Cluster* GetNext(const Cluster*);
-
- const Cluster* FindCluster(long long time_nanoseconds) const;
- // const BlockEntry* Seek(long long time_nanoseconds, const Track*) const;
-
- const Cluster* FindOrPreloadCluster(long long pos);
-
- long ParseCues(long long cues_off, // offset relative to start of segment
- long long& parse_pos, long& parse_len);
-
- private:
- long long m_pos; // absolute file posn; what has been consumed so far
- Cluster* m_pUnknownSize;
-
- SeekHead* m_pSeekHead;
- SegmentInfo* m_pInfo;
- 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
- long m_clusterSize; // array size
-
- long DoLoadCluster(long long&, long&);
- long DoLoadClusterUnknownSize(long long&, long&);
- long DoParseNext(const Cluster*&, long long&, long&);
-
- bool AppendCluster(Cluster*);
- bool PreloadCluster(Cluster*, ptrdiff_t);
-
- // void ParseSeekHead(long long pos, long long size);
- // void ParseSeekEntry(long long pos, long long size);
- // void ParseCues(long long);
-
- const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&);
-};
-
-} // end namespace mkvparser
-
-inline long mkvparser::Segment::LoadCluster() {
- long long pos;
- long size;
-
- return LoadCluster(pos, size);
-}
-
-#endif // MKVPARSER_HPP
--- /dev/null
+++ b/third_party/libwebm/mkvparser/mkvparser.cc
@@ -1,0 +1,7940 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#include "mkvparser/mkvparser.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#include <float.h> // _isnan() / _finite()
+#define MSC_COMPAT
+#endif
+
+#include <cassert>
+#include <cfloat>
+#include <climits>
+#include <cmath>
+#include <cstring>
+#include <memory>
+#include <new>
+
+#include "common/webmids.h"
+
+namespace mkvparser {
+const float MasteringMetadata::kValueNotPresent = FLT_MAX;
+const long long Colour::kValueNotPresent = LLONG_MAX;
+
+#ifdef MSC_COMPAT
+inline bool isnan(double val) { return !!_isnan(val); }
+inline bool isinf(double val) { return !_finite(val); }
+#else
+inline bool isnan(double val) { return std::isnan(val); }
+inline bool isinf(double val) { return std::isinf(val); }
+#endif // MSC_COMPAT
+
+IMkvReader::~IMkvReader() {}
+
+template <typename Type>
+Type* SafeArrayAlloc(unsigned long long num_elements,
+ unsigned long long element_size) {
+ if (num_elements == 0 || element_size == 0)
+ return NULL;
+
+ const size_t kMaxAllocSize = 0x80000000; // 2GiB
+ const unsigned long long num_bytes = num_elements * element_size;
+ if (element_size > (kMaxAllocSize / num_elements))
+ return NULL;
+ if (num_bytes != static_cast<size_t>(num_bytes))
+ return NULL;
+
+ return new (std::nothrow) Type[static_cast<size_t>(num_bytes)];
+}
+
+void GetVersion(int& major, int& minor, int& build, int& revision) {
+ major = 1;
+ minor = 0;
+ build = 0;
+ revision = 30;
+}
+
+long long ReadUInt(IMkvReader* pReader, long long pos, long& len) {
+ if (!pReader || pos < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ len = 1;
+ unsigned char b;
+ int status = pReader->Read(pos, 1, &b);
+
+ if (status < 0) // error or underflow
+ return status;
+
+ if (status > 0) // interpreted as "underflow"
+ return E_BUFFER_NOT_FULL;
+
+ if (b == 0) // we can't handle u-int values larger than 8 bytes
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char m = 0x80;
+
+ while (!(b & m)) {
+ m >>= 1;
+ ++len;
+ }
+
+ long long result = b & (~m);
+ ++pos;
+
+ for (int i = 1; i < len; ++i) {
+ status = pReader->Read(pos, 1, &b);
+
+ if (status < 0) {
+ len = 1;
+ return status;
+ }
+
+ if (status > 0) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+// Reads an EBML ID and returns it.
+// An ID must at least 1 byte long, cannot exceed 4, and its value must be
+// greater than 0.
+// See known EBML values and EBMLMaxIDLength:
+// http://www.matroska.org/technical/specs/index.html
+// Returns the ID, or a value less than 0 to report an error while reading the
+// ID.
+long long ReadID(IMkvReader* pReader, long long pos, long& len) {
+ if (pReader == NULL || pos < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ // Read the first byte. The length in bytes of the ID is determined by
+ // finding the first set bit in the first byte of the ID.
+ unsigned char temp_byte = 0;
+ int read_status = pReader->Read(pos, 1, &temp_byte);
+
+ if (read_status < 0)
+ return E_FILE_FORMAT_INVALID;
+ else if (read_status > 0) // No data to read.
+ return E_BUFFER_NOT_FULL;
+
+ if (temp_byte == 0) // ID length > 8 bytes; invalid file.
+ return E_FILE_FORMAT_INVALID;
+
+ int bit_pos = 0;
+ const int kMaxIdLengthInBytes = 4;
+ const int kCheckByte = 0x80;
+
+ // Find the first bit that's set.
+ bool found_bit = false;
+ for (; bit_pos < kMaxIdLengthInBytes; ++bit_pos) {
+ if ((kCheckByte >> bit_pos) & temp_byte) {
+ found_bit = true;
+ break;
+ }
+ }
+
+ if (!found_bit) {
+ // The value is too large to be a valid ID.
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ // Read the remaining bytes of the ID (if any).
+ const int id_length = bit_pos + 1;
+ long long ebml_id = temp_byte;
+ for (int i = 1; i < id_length; ++i) {
+ ebml_id <<= 8;
+ read_status = pReader->Read(pos + i, 1, &temp_byte);
+
+ if (read_status < 0)
+ return E_FILE_FORMAT_INVALID;
+ else if (read_status > 0)
+ return E_BUFFER_NOT_FULL;
+
+ ebml_id |= temp_byte;
+ }
+
+ len = id_length;
+ return ebml_id;
+}
+
+long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) {
+ if (!pReader || pos < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ long long total, available;
+
+ int status = pReader->Length(&total, &available);
+ if (status < 0 || (total >= 0 && available > total))
+ return E_FILE_FORMAT_INVALID;
+
+ len = 1;
+
+ if (pos >= available)
+ return pos; // too few bytes available
+
+ unsigned char b;
+
+ status = pReader->Read(pos, 1, &b);
+
+ if (status != 0)
+ return status;
+
+ if (b == 0) // we can't handle u-int values larger than 8 bytes
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char m = 0x80;
+
+ while (!(b & m)) {
+ m >>= 1;
+ ++len;
+ }
+
+ return 0; // success
+}
+
+// TODO(vigneshv): This function assumes that unsigned values never have their
+// high bit set.
+long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) {
+ if (!pReader || pos < 0 || (size <= 0) || (size > 8))
+ return E_FILE_FORMAT_INVALID;
+
+ long long result = 0;
+
+ for (long long i = 0; i < size; ++i) {
+ unsigned char b;
+
+ const long status = pReader->Read(pos, 1, &b);
+
+ if (status < 0)
+ return status;
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
+ double& result) {
+ if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8)))
+ return E_FILE_FORMAT_INVALID;
+
+ const long size = static_cast<long>(size_);
+
+ unsigned char buf[8];
+
+ const int status = pReader->Read(pos, size, buf);
+
+ if (status < 0) // error
+ return status;
+
+ if (size == 4) {
+ union {
+ float f;
+ unsigned long ff;
+ };
+
+ ff = 0;
+
+ for (int i = 0;;) {
+ ff |= buf[i];
+
+ if (++i >= 4)
+ break;
+
+ ff <<= 8;
+ }
+
+ result = f;
+ } else {
+ union {
+ double d;
+ unsigned long long dd;
+ };
+
+ dd = 0;
+
+ for (int i = 0;;) {
+ dd |= buf[i];
+
+ if (++i >= 8)
+ break;
+
+ dd <<= 8;
+ }
+
+ result = d;
+ }
+
+ if (mkvparser::isinf(result) || mkvparser::isnan(result))
+ return E_FILE_FORMAT_INVALID;
+
+ return 0;
+}
+
+long UnserializeInt(IMkvReader* pReader, long long pos, long long size,
+ long long& result_ref) {
+ if (!pReader || pos < 0 || size < 1 || size > 8)
+ return E_FILE_FORMAT_INVALID;
+
+ signed char first_byte = 0;
+ const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte);
+
+ if (status < 0)
+ return status;
+
+ unsigned long long result = first_byte;
+ ++pos;
+
+ for (long i = 1; i < size; ++i) {
+ unsigned char b;
+
+ const long status = pReader->Read(pos, 1, &b);
+
+ if (status < 0)
+ return status;
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ result_ref = static_cast<long long>(result);
+ return 0;
+}
+
+long UnserializeString(IMkvReader* pReader, long long pos, long long size,
+ char*& str) {
+ delete[] str;
+ str = NULL;
+
+ if (size >= LONG_MAX || size < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ // +1 for '\0' terminator
+ const long required_size = static_cast<long>(size) + 1;
+
+ str = SafeArrayAlloc<char>(1, required_size);
+ if (str == NULL)
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
+
+ const long status = pReader->Read(pos, static_cast<long>(size), buf);
+
+ if (status) {
+ delete[] str;
+ str = NULL;
+
+ return status;
+ }
+
+ str[required_size - 1] = '\0';
+ return 0;
+}
+
+long ParseElementHeader(IMkvReader* pReader, long long& pos, long long stop,
+ long long& id, long long& size) {
+ if (stop >= 0 && pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ long len;
+
+ id = ReadID(pReader, pos, len);
+
+ if (id < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume id
+
+ if (stop >= 0 && pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ size = ReadUInt(pReader, pos, len);
+
+ if (size < 0 || len < 1 || len > 8) {
+ // Invalid: Negative payload size, negative or 0 length integer, or integer
+ // larger than 64 bits (libwebm cannot handle them).
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ // Avoid rolling over pos when very close to LLONG_MAX.
+ const unsigned long long rollover_check =
+ static_cast<unsigned long long>(pos) + len;
+ if (rollover_check > LLONG_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume length of size
+
+ // pos now designates payload
+
+ if (stop >= 0 && pos > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0; // success
+}
+
+bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
+ long long& val) {
+ if (!pReader || pos < 0)
+ return false;
+
+ long long total = 0;
+ long long available = 0;
+
+ const long status = pReader->Length(&total, &available);
+ if (status < 0 || (total >= 0 && available > total))
+ return false;
+
+ long len = 0;
+
+ const long long id = ReadID(pReader, pos, len);
+ if (id < 0 || (available - pos) > len)
+ return false;
+
+ if (static_cast<unsigned long>(id) != expected_id)
+ return false;
+
+ pos += len; // consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len)
+ return false;
+
+ pos += len; // consume length of size of payload
+
+ val = UnserializeUInt(pReader, pos, size);
+ if (val < 0)
+ return false;
+
+ pos += size; // consume size of payload
+
+ return true;
+}
+
+bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
+ unsigned char*& buf, size_t& buflen) {
+ if (!pReader || pos < 0)
+ return false;
+
+ long long total = 0;
+ long long available = 0;
+
+ long status = pReader->Length(&total, &available);
+ if (status < 0 || (total >= 0 && available > total))
+ return false;
+
+ long len = 0;
+ const long long id = ReadID(pReader, pos, len);
+ if (id < 0 || (available - pos) > len)
+ return false;
+
+ if (static_cast<unsigned long>(id) != expected_id)
+ return false;
+
+ pos += len; // consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ if (size < 0 || len <= 0 || len > 8 || (available - pos) > len)
+ return false;
+
+ unsigned long long rollover_check =
+ static_cast<unsigned long long>(pos) + len;
+ if (rollover_check > LLONG_MAX)
+ return false;
+
+ pos += len; // consume length of size of payload
+
+ rollover_check = static_cast<unsigned long long>(pos) + size;
+ if (rollover_check > LLONG_MAX)
+ return false;
+
+ if ((pos + size) > available)
+ return false;
+
+ if (size >= LONG_MAX)
+ return false;
+
+ const long buflen_ = static_cast<long>(size);
+
+ buf = SafeArrayAlloc<unsigned char>(1, buflen_);
+ if (!buf)
+ return false;
+
+ status = pReader->Read(pos, buflen_, buf);
+ if (status != 0)
+ return false;
+
+ buflen = buflen_;
+
+ pos += size; // consume size of payload
+ return true;
+}
+
+EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); }
+
+EBMLHeader::~EBMLHeader() { delete[] m_docType; }
+
+void EBMLHeader::Init() {
+ m_version = 1;
+ m_readVersion = 1;
+ m_maxIdLength = 4;
+ m_maxSizeLength = 8;
+
+ if (m_docType) {
+ delete[] m_docType;
+ m_docType = NULL;
+ }
+
+ m_docTypeVersion = 1;
+ m_docTypeReadVersion = 1;
+}
+
+long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) {
+ if (!pReader)
+ return E_FILE_FORMAT_INVALID;
+
+ long long total, available;
+
+ long status = pReader->Length(&total, &available);
+
+ if (status < 0) // error
+ return status;
+
+ pos = 0;
+
+ // Scan until we find what looks like the first byte of the EBML header.
+ const long long kMaxScanBytes = (available >= 1024) ? 1024 : available;
+ const unsigned char kEbmlByte0 = 0x1A;
+ unsigned char scan_byte = 0;
+
+ while (pos < kMaxScanBytes) {
+ status = pReader->Read(pos, 1, &scan_byte);
+
+ if (status < 0) // error
+ return status;
+ else if (status > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if (scan_byte == kEbmlByte0)
+ break;
+
+ ++pos;
+ }
+
+ long len = 0;
+ const long long ebml_id = ReadID(pReader, pos, len);
+
+ if (ebml_id == E_BUFFER_NOT_FULL)
+ return E_BUFFER_NOT_FULL;
+
+ if (len != 4 || ebml_id != libwebm::kMkvEBML)
+ return E_FILE_FORMAT_INVALID;
+
+ // Move read pos forward to the EBML header size field.
+ pos += 4;
+
+ // Read length of size field.
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return E_FILE_FORMAT_INVALID;
+ else if (result > 0) // need more data
+ return E_BUFFER_NOT_FULL;
+
+ if (len < 1 || len > 8)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((total >= 0) && ((total - pos) < len))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((available - pos) < len)
+ return pos + len; // try again later
+
+ // Read the EBML header size.
+ result = ReadUInt(pReader, pos, len);
+
+ if (result < 0) // error
+ return result;
+
+ pos += len; // consume size field
+
+ // pos now designates start of payload
+
+ if ((total >= 0) && ((total - pos) < result))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((available - pos) < result)
+ return pos + result;
+
+ const long long end = pos + result;
+
+ Init();
+
+ while (pos < end) {
+ long long id, size;
+
+ status = ParseElementHeader(pReader, pos, end, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (size == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == libwebm::kMkvEBMLVersion) {
+ m_version = UnserializeUInt(pReader, pos, size);
+
+ if (m_version <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvEBMLReadVersion) {
+ m_readVersion = UnserializeUInt(pReader, pos, size);
+
+ if (m_readVersion <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvEBMLMaxIDLength) {
+ m_maxIdLength = UnserializeUInt(pReader, pos, size);
+
+ if (m_maxIdLength <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvEBMLMaxSizeLength) {
+ m_maxSizeLength = UnserializeUInt(pReader, pos, size);
+
+ if (m_maxSizeLength <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvDocType) {
+ if (m_docType)
+ return E_FILE_FORMAT_INVALID;
+
+ status = UnserializeString(pReader, pos, size, m_docType);
+
+ if (status) // error
+ return status;
+ } else if (id == libwebm::kMkvDocTypeVersion) {
+ m_docTypeVersion = UnserializeUInt(pReader, pos, size);
+
+ if (m_docTypeVersion <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvDocTypeReadVersion) {
+ m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
+
+ if (m_docTypeReadVersion <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos += size;
+ }
+
+ if (pos != end)
+ return E_FILE_FORMAT_INVALID;
+
+ // Make sure DocType, DocTypeReadVersion, and DocTypeVersion are valid.
+ if (m_docType == NULL || m_docTypeReadVersion <= 0 || m_docTypeVersion <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ // Make sure EBMLMaxIDLength and EBMLMaxSizeLength are valid.
+ if (m_maxIdLength <= 0 || m_maxIdLength > 4 || m_maxSizeLength <= 0 ||
+ m_maxSizeLength > 8)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0;
+}
+
+Segment::Segment(IMkvReader* pReader, long long elem_start,
+ // long long elem_size,
+ long long start, long long size)
+ : m_pReader(pReader),
+ m_element_start(elem_start),
+ // m_element_size(elem_size),
+ m_start(start),
+ m_size(size),
+ m_pos(start),
+ m_pUnknownSize(0),
+ m_pSeekHead(NULL),
+ m_pInfo(NULL),
+ m_pTracks(NULL),
+ m_pCues(NULL),
+ m_pChapters(NULL),
+ m_pTags(NULL),
+ m_clusters(NULL),
+ m_clusterCount(0),
+ m_clusterPreloadCount(0),
+ m_clusterSize(0) {}
+
+Segment::~Segment() {
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ Cluster** i = m_clusters;
+ Cluster** j = m_clusters + count;
+
+ while (i != j) {
+ Cluster* const p = *i++;
+ delete p;
+ }
+
+ delete[] m_clusters;
+
+ delete m_pTracks;
+ delete m_pInfo;
+ delete m_pCues;
+ delete m_pChapters;
+ delete m_pTags;
+ delete m_pSeekHead;
+}
+
+long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
+ Segment*& pSegment) {
+ if (pReader == NULL || pos < 0)
+ return E_PARSE_FAILED;
+
+ pSegment = NULL;
+
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+
+ if (status < 0) // error
+ return status;
+
+ if (available < 0)
+ return -1;
+
+ if ((total >= 0) && (available > total))
+ return -1;
+
+ // I would assume that in practice this loop would execute
+ // exactly once, but we allow for other elements (e.g. Void)
+ // to immediately follow the EBML header. This is fine for
+ // the source filter case (since the entire file is available),
+ // but in the splitter case over a network we should probably
+ // just give up early. We could for example decide only to
+ // execute this loop a maximum of, say, 10 times.
+ // TODO:
+ // There is an implied "give up early" by only parsing up
+ // to the available limit. We do do that, but only if the
+ // total file size is unknown. We could decide to always
+ // use what's available as our limit (irrespective of whether
+ // we happen to know the total file length). This would have
+ // as its sense "parse this much of the file before giving up",
+ // which a slightly different sense from "try to parse up to
+ // 10 EMBL elements before giving up".
+
+ for (;;) {
+ if ((total >= 0) && (pos >= total))
+ return E_FILE_FORMAT_INVALID;
+
+ // Read ID
+ long len;
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result) // error, or too few available bytes
+ return result;
+
+ if ((total >= 0) && ((pos + len) > total))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long idpos = pos;
+ const long long id = ReadID(pReader, pos, len);
+
+ if (id < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume ID
+
+ // Read Size
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result) // error, or too few available bytes
+ return result;
+
+ if ((total >= 0) && ((pos + len) > total))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) // error
+ return size;
+
+ pos += len; // consume length of size of element
+
+ // Pos now points to start of payload
+
+ // Handle "unknown size" for live streaming of webm files.
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (id == libwebm::kMkvSegment) {
+ if (size == unknown_size)
+ size = -1;
+
+ else if (total < 0)
+ size = -1;
+
+ else if ((pos + size) > total)
+ size = -1;
+
+ pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size);
+ if (pSegment == NULL)
+ return E_PARSE_FAILED;
+
+ return 0; // success
+ }
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((total >= 0) && ((pos + size) > total))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + size) > available)
+ return pos + size;
+
+ pos += size; // consume payload
+ }
+}
+
+long long Segment::ParseHeaders() {
+ // Outermost (level 0) segment object has been constructed,
+ // and pos designates start of payload. We need to find the
+ // inner (level 1) elements.
+ long long total, available;
+
+ const int status = m_pReader->Length(&total, &available);
+
+ if (status < 0) // error
+ return status;
+
+ if (total > 0 && available > total)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+ if ((segment_stop >= 0 && total >= 0 && segment_stop > total) ||
+ (segment_stop >= 0 && m_pos > segment_stop)) {
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ for (;;) {
+ if ((total >= 0) && (m_pos >= total))
+ break;
+
+ if ((segment_stop >= 0) && (m_pos >= segment_stop))
+ break;
+
+ long long pos = m_pos;
+ const long long element_start = pos;
+
+ // Avoid rolling over pos when very close to LLONG_MAX.
+ unsigned long long rollover_check = pos + 1ULL;
+ if (rollover_check > LLONG_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + 1) > available)
+ return (pos + 1);
+
+ long len;
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) // error
+ return result;
+
+ if (result > 0) {
+ // MkvReader doesn't have enough data to satisfy this read attempt.
+ return (pos + 1);
+ }
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long idpos = pos;
+ const long long id = ReadID(m_pReader, idpos, len);
+
+ if (id < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == libwebm::kMkvCluster)
+ break;
+
+ pos += len; // consume ID
+
+ if ((pos + 1) > available)
+ return (pos + 1);
+
+ // Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) // error
+ return result;
+
+ if (result > 0) {
+ // MkvReader doesn't have enough data to satisfy this read attempt.
+ return (pos + 1);
+ }
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0 || len < 1 || len > 8) {
+ // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or
+ // len > 8 is true instead of checking this _everywhere_.
+ return size;
+ }
+
+ pos += len; // consume length of size of element
+
+ // Avoid rolling over pos when very close to LLONG_MAX.
+ rollover_check = static_cast<unsigned long long>(pos) + size;
+ if (rollover_check > LLONG_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long element_size = size + pos - element_start;
+
+ // Pos now points to start of payload
+
+ if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ // We read EBML elements either in total or nothing at all.
+
+ if ((pos + size) > available)
+ return pos + size;
+
+ if (id == libwebm::kMkvInfo) {
+ if (m_pInfo)
+ return E_FILE_FORMAT_INVALID;
+
+ m_pInfo = new (std::nothrow)
+ SegmentInfo(this, pos, size, element_start, element_size);
+
+ if (m_pInfo == NULL)
+ return -1;
+
+ const long status = m_pInfo->Parse();
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvTracks) {
+ if (m_pTracks)
+ return E_FILE_FORMAT_INVALID;
+
+ m_pTracks = new (std::nothrow)
+ Tracks(this, pos, size, element_start, element_size);
+
+ if (m_pTracks == NULL)
+ return -1;
+
+ const long status = m_pTracks->Parse();
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvCues) {
+ if (m_pCues == NULL) {
+ m_pCues = new (std::nothrow)
+ Cues(this, pos, size, element_start, element_size);
+
+ if (m_pCues == NULL)
+ return -1;
+ }
+ } else if (id == libwebm::kMkvSeekHead) {
+ if (m_pSeekHead == NULL) {
+ m_pSeekHead = new (std::nothrow)
+ SeekHead(this, pos, size, element_start, element_size);
+
+ if (m_pSeekHead == NULL)
+ return -1;
+
+ const long status = m_pSeekHead->Parse();
+
+ if (status)
+ return status;
+ }
+ } else if (id == libwebm::kMkvChapters) {
+ if (m_pChapters == NULL) {
+ m_pChapters = new (std::nothrow)
+ Chapters(this, pos, size, element_start, element_size);
+
+ if (m_pChapters == NULL)
+ return -1;
+
+ const long status = m_pChapters->Parse();
+
+ if (status)
+ return status;
+ }
+ } else if (id == libwebm::kMkvTags) {
+ 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
+ }
+
+ if (segment_stop >= 0 && m_pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (m_pInfo == NULL) // TODO: liberalize this behavior
+ return E_FILE_FORMAT_INVALID;
+
+ if (m_pTracks == NULL)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0; // success
+}
+
+long Segment::LoadCluster(long long& pos, long& len) {
+ for (;;) {
+ const long result = DoLoadCluster(pos, len);
+
+ if (result <= 1)
+ return result;
+ }
+}
+
+long Segment::DoLoadCluster(long long& pos, long& len) {
+ if (m_pos < 0)
+ return DoLoadClusterUnknownSize(pos, len);
+
+ long long total, avail;
+
+ long status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return status;
+
+ if (total >= 0 && avail > total)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+ long long cluster_off = -1; // offset relative to start of segment
+ long long cluster_size = -1; // size of cluster payload
+
+ for (;;) {
+ if ((total >= 0) && (m_pos >= total))
+ return 1; // no more clusters
+
+ if ((segment_stop >= 0) && (m_pos >= segment_stop))
+ return 1; // no more clusters
+
+ pos = m_pos;
+
+ // 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)
+ 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 = ReadID(m_pReader, idpos, len);
+
+ if (id < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume ID
+
+ // 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)
+ 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 length of size of element
+
+ // pos now points to start of payload
+
+ if (size == 0) {
+ // Missing element payload: move on.
+ m_pos = pos;
+ continue;
+ }
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if ((segment_stop >= 0) && (size != unknown_size) &&
+ ((pos + size) > segment_stop)) {
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (id == libwebm::kMkvCues) {
+ if (size == unknown_size) {
+ // Cues element of unknown size: Not supported.
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (m_pCues == NULL) {
+ const long long element_size = (pos - idpos) + size;
+
+ m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
+ if (m_pCues == NULL)
+ return -1;
+ }
+
+ m_pos = pos + size; // consume payload
+ continue;
+ }
+
+ if (id != libwebm::kMkvCluster) {
+ // Besides the Segment, Libwebm allows only cluster elements of unknown
+ // size. Fail the parse upon encountering a non-cluster element reporting
+ // unknown size.
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ m_pos = pos + size; // consume payload
+ continue;
+ }
+
+ // We have a cluster.
+
+ cluster_off = idpos - m_start; // relative pos
+
+ if (size != unknown_size)
+ cluster_size = size;
+
+ break;
+ }
+
+ if (cluster_off < 0) {
+ // No cluster, die.
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ long long pos_;
+ long len_;
+
+ status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
+
+ if (status < 0) { // error, or underflow
+ pos = pos_;
+ len = len_;
+
+ return status;
+ }
+
+ // status == 0 means "no block entries found"
+ // status > 0 means "found at least one block entry"
+
+ // TODO:
+ // The issue here is that the segment increments its own
+ // pos ptr past the most recent cluster parsed, and then
+ // starts from there to parse the next cluster. If we
+ // don't know the size of the current cluster, then we
+ // must either parse its payload (as we do below), looking
+ // for the cluster (or cues) ID to terminate the parse.
+ // This isn't really what we want: rather, we really need
+ // a way to create the curr cluster object immediately.
+ // The pity is that cluster::parse can determine its own
+ // boundary, and we largely duplicate that same logic here.
+ //
+ // Maybe we need to get rid of our look-ahead preloading
+ // in source::parse???
+ //
+ // As we're parsing the blocks in the curr cluster
+ //(in cluster::parse), we should have some way to signal
+ // to the segment that we have determined the boundary,
+ // so it can adjust its own segment::m_pos member.
+ //
+ // The problem is that we're asserting in asyncreadinit,
+ // because we adjust the pos down to the curr seek pos,
+ // and the resulting adjusted len is > 2GB. I'm suspicious
+ // that this is even correct, but even if it is, we can't
+ // be loading that much data in the cache anyway.
+
+ const long idx = m_clusterCount;
+
+ if (m_clusterPreloadCount > 0) {
+ if (idx >= m_clusterSize)
+ return E_FILE_FORMAT_INVALID;
+
+ Cluster* const pCluster = m_clusters[idx];
+ if (pCluster == NULL || pCluster->m_index >= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long off = pCluster->GetPosition();
+ if (off < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (off == cluster_off) { // preloaded already
+ if (status == 0) // no entries found
+ return E_FILE_FORMAT_INVALID;
+
+ if (cluster_size >= 0)
+ pos += cluster_size;
+ else {
+ const long long element_size = pCluster->GetElementSize();
+
+ if (element_size <= 0)
+ return E_FILE_FORMAT_INVALID; // TODO: handle this case
+
+ pos = pCluster->m_element_start + element_size;
+ }
+
+ pCluster->m_index = idx; // move from preloaded to loaded
+ ++m_clusterCount;
+ --m_clusterPreloadCount;
+
+ m_pos = pos; // consume payload
+ if (segment_stop >= 0 && m_pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0; // success
+ }
+ }
+
+ if (status == 0) { // no entries found
+ if (cluster_size >= 0)
+ pos += cluster_size;
+
+ if ((total >= 0) && (pos >= total)) {
+ m_pos = total;
+ return 1; // no more clusters
+ }
+
+ if ((segment_stop >= 0) && (pos >= segment_stop)) {
+ m_pos = segment_stop;
+ return 1; // no more clusters
+ }
+
+ m_pos = pos;
+ return 2; // try again
+ }
+
+ // status > 0 means we have an entry
+
+ Cluster* const pCluster = Cluster::Create(this, idx, cluster_off);
+ if (pCluster == NULL)
+ return -1;
+
+ if (!AppendCluster(pCluster)) {
+ delete pCluster;
+ return -1;
+ }
+
+ if (cluster_size >= 0) {
+ pos += cluster_size;
+
+ m_pos = pos;
+
+ if (segment_stop > 0 && m_pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0;
+ }
+
+ m_pUnknownSize = pCluster;
+ m_pos = -pos;
+
+ 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)
+}
+
+long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
+ if (m_pos >= 0 || m_pUnknownSize == NULL)
+ return E_PARSE_FAILED;
+
+ const long status = m_pUnknownSize->Parse(pos, len);
+
+ if (status < 0) // error or underflow
+ return status;
+
+ if (status == 0) // parsed a block
+ return 2; // continue parsing
+
+ const long long start = m_pUnknownSize->m_element_start;
+ const long long size = m_pUnknownSize->GetElementSize();
+
+ if (size < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos = start + size;
+ m_pos = pos;
+
+ m_pUnknownSize = 0;
+
+ return 2; // continue parsing
+}
+
+bool Segment::AppendCluster(Cluster* pCluster) {
+ if (pCluster == NULL || pCluster->m_index < 0)
+ return false;
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ long& size = m_clusterSize;
+ const long idx = pCluster->m_index;
+
+ if (size < count || idx != m_clusterCount)
+ return false;
+
+ if (count >= size) {
+ const long n = (size <= 0) ? 2048 : 2 * size;
+
+ Cluster** const qq = new (std::nothrow) Cluster*[n];
+ if (qq == NULL)
+ return false;
+
+ Cluster** q = qq;
+ Cluster** p = m_clusters;
+ Cluster** const pp = p + count;
+
+ while (p != pp)
+ *q++ = *p++;
+
+ delete[] m_clusters;
+
+ m_clusters = qq;
+ size = n;
+ }
+
+ if (m_clusterPreloadCount > 0) {
+ Cluster** const p = m_clusters + m_clusterCount;
+ if (*p == NULL || (*p)->m_index >= 0)
+ return false;
+
+ Cluster** q = p + m_clusterPreloadCount;
+ if (q >= (m_clusters + size))
+ return false;
+
+ for (;;) {
+ Cluster** const qq = q - 1;
+ if ((*qq)->m_index >= 0)
+ return false;
+
+ *q = *qq;
+ q = qq;
+
+ if (q == p)
+ break;
+ }
+ }
+
+ m_clusters[idx] = pCluster;
+ ++m_clusterCount;
+ return true;
+}
+
+bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
+ if (pCluster == NULL || pCluster->m_index >= 0 || idx < m_clusterCount)
+ return false;
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ long& size = m_clusterSize;
+ if (size < count)
+ return false;
+
+ if (count >= size) {
+ const long n = (size <= 0) ? 2048 : 2 * size;
+
+ Cluster** const qq = new (std::nothrow) Cluster*[n];
+ if (qq == NULL)
+ return false;
+ Cluster** q = qq;
+
+ Cluster** p = m_clusters;
+ Cluster** const pp = p + count;
+
+ while (p != pp)
+ *q++ = *p++;
+
+ delete[] m_clusters;
+
+ m_clusters = qq;
+ size = n;
+ }
+
+ if (m_clusters == NULL)
+ return false;
+
+ Cluster** const p = m_clusters + idx;
+
+ Cluster** q = m_clusters + count;
+ if (q < p || q >= (m_clusters + size))
+ return false;
+
+ while (q > p) {
+ Cluster** const qq = q - 1;
+
+ if ((*qq)->m_index >= 0)
+ return false;
+
+ *q = *qq;
+ q = qq;
+ }
+
+ m_clusters[idx] = pCluster;
+ ++m_clusterPreloadCount;
+ return true;
+}
+
+long Segment::Load() {
+ if (m_clusters != NULL || m_clusterSize != 0 || m_clusterCount != 0)
+ return E_PARSE_FAILED;
+
+ // Outermost (level 0) segment object has been constructed,
+ // and pos designates start of payload. We need to find the
+ // inner (level 1) elements.
+
+ const long long header_status = ParseHeaders();
+
+ if (header_status < 0) // error
+ return static_cast<long>(header_status);
+
+ if (header_status > 0) // underflow
+ return E_BUFFER_NOT_FULL;
+
+ if (m_pInfo == NULL || m_pTracks == NULL)
+ return E_FILE_FORMAT_INVALID;
+
+ for (;;) {
+ const int status = LoadCluster();
+
+ if (status < 0) // error
+ return status;
+
+ if (status >= 1) // no more clusters
+ return 0;
+ }
+}
+
+SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
+ long long element_start, long long element_size)
+ : m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ m_entries(0),
+ m_entry_count(0),
+ m_void_elements(0),
+ m_void_element_count(0) {}
+
+SeekHead::~SeekHead() {
+ delete[] m_entries;
+ delete[] m_void_elements;
+}
+
+long SeekHead::Parse() {
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = m_start;
+ const long long stop = m_start + m_size;
+
+ // first count the seek head entries
+
+ int entry_count = 0;
+ int void_element_count = 0;
+
+ while (pos < stop) {
+ long long id, size;
+
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvSeek)
+ ++entry_count;
+ else if (id == libwebm::kMkvVoid)
+ ++void_element_count;
+
+ pos += size; // consume payload
+
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ m_entries = new (std::nothrow) Entry[entry_count];
+
+ if (m_entries == NULL)
+ return -1;
+
+ m_void_elements = new (std::nothrow) VoidElement[void_element_count];
+
+ if (m_void_elements == NULL)
+ return -1;
+
+ // now parse the entries and void elements
+
+ Entry* pEntry = m_entries;
+ VoidElement* pVoidElement = m_void_elements;
+
+ pos = m_start;
+
+ while (pos < stop) {
+ const long long idpos = pos;
+
+ long long id, size;
+
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvSeek) {
+ if (ParseEntry(pReader, pos, size, pEntry)) {
+ Entry& e = *pEntry++;
+
+ e.element_start = idpos;
+ e.element_size = (pos + size) - idpos;
+ }
+ } else if (id == libwebm::kMkvVoid) {
+ VoidElement& e = *pVoidElement++;
+
+ e.element_start = idpos;
+ e.element_size = (pos + size) - idpos;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
+ assert(count_ >= 0);
+ assert(count_ <= entry_count);
+
+ m_entry_count = static_cast<int>(count_);
+
+ count_ = ptrdiff_t(pVoidElement - m_void_elements);
+ assert(count_ >= 0);
+ assert(count_ <= void_element_count);
+
+ m_void_element_count = static_cast<int>(count_);
+
+ return 0;
+}
+
+int SeekHead::GetCount() const { return m_entry_count; }
+
+const SeekHead::Entry* SeekHead::GetEntry(int idx) const {
+ if (idx < 0)
+ return 0;
+
+ if (idx >= m_entry_count)
+ return 0;
+
+ return m_entries + idx;
+}
+
+int SeekHead::GetVoidElementCount() const { return m_void_element_count; }
+
+const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
+ if (idx < 0)
+ return 0;
+
+ if (idx >= m_void_element_count)
+ return 0;
+
+ return m_void_elements + idx;
+}
+
+long Segment::ParseCues(long long off, long long& pos, long& len) {
+ if (m_pCues)
+ return 0; // success
+
+ if (off < 0)
+ return -1;
+
+ long long total, avail;
+
+ const int status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ pos = m_start + off;
+
+ if ((total < 0) || (pos >= total))
+ return 1; // don't bother parsing cues
+
+ const long long element_start = pos;
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+ 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) // underflow (weird)
+ {
+ len = 1;
+ 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 = ReadID(m_pReader, idpos, len);
+
+ if (id != libwebm::kMkvCues)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume ID
+ assert((segment_stop < 0) || (pos <= segment_stop));
+
+ // 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) // underflow (weird)
+ {
+ len = 1;
+ 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);
+
+ if (size == 0) // weird, although technically not illegal
+ return 1; // done
+
+ pos += len; // consume length of size of element
+ assert((segment_stop < 0) || (pos <= segment_stop));
+
+ // Pos now points to start of payload
+
+ const long long element_stop = pos + size;
+
+ if ((segment_stop >= 0) && (element_stop > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((total >= 0) && (element_stop > total))
+ return 1; // don't bother parsing anymore
+
+ len = static_cast<long>(size);
+
+ if (element_stop > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long element_size = element_stop - element_start;
+
+ m_pCues =
+ new (std::nothrow) Cues(this, pos, size, element_start, element_size);
+ if (m_pCues == NULL)
+ return -1;
+
+ return 0; // success
+}
+
+bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
+ Entry* pEntry) {
+ if (size_ <= 0)
+ return false;
+
+ long long pos = start;
+ const long long stop = start + size_;
+
+ long len;
+
+ // parse the container for the level-1 element ID
+
+ const long long seekIdId = ReadID(pReader, pos, len);
+ if (seekIdId < 0)
+ return false;
+
+ if (seekIdId != libwebm::kMkvSeekID)
+ return false;
+
+ if ((pos + len) > stop)
+ return false;
+
+ pos += len; // consume SeekID id
+
+ const long long seekIdSize = ReadUInt(pReader, pos, len);
+
+ if (seekIdSize <= 0)
+ return false;
+
+ if ((pos + len) > stop)
+ return false;
+
+ pos += len; // consume size of field
+
+ if ((pos + seekIdSize) > stop)
+ return false;
+
+ // Note that the SeekId payload really is serialized
+ // as a "Matroska integer", not as a plain binary value.
+ // In fact, Matroska requires that ID values in the
+ // stream exactly match the binary representation as listed
+ // in the Matroska specification.
+ //
+ // This parser is more liberal, and permits IDs to have
+ // any width. (This could make the representation in the stream
+ // different from what's in the spec, but it doesn't matter here,
+ // since we always normalize "Matroska integer" values.)
+
+ pEntry->id = ReadUInt(pReader, pos, len); // payload
+
+ if (pEntry->id <= 0)
+ return false;
+
+ if (len != seekIdSize)
+ return false;
+
+ pos += seekIdSize; // consume SeekID payload
+
+ const long long seekPosId = ReadID(pReader, pos, len);
+
+ if (seekPosId != libwebm::kMkvSeekPosition)
+ return false;
+
+ if ((pos + len) > stop)
+ return false;
+
+ pos += len; // consume id
+
+ const long long seekPosSize = ReadUInt(pReader, pos, len);
+
+ if (seekPosSize <= 0)
+ return false;
+
+ if ((pos + len) > stop)
+ return false;
+
+ pos += len; // consume size
+
+ if ((pos + seekPosSize) > stop)
+ return false;
+
+ pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
+
+ if (pEntry->pos < 0)
+ return false;
+
+ pos += seekPosSize; // consume payload
+
+ if (pos != stop)
+ return false;
+
+ return true;
+}
+
+Cues::Cues(Segment* pSegment, long long start_, long long size_,
+ long long element_start, long long element_size)
+ : m_pSegment(pSegment),
+ m_start(start_),
+ m_size(size_),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ m_cue_points(NULL),
+ m_count(0),
+ m_preload_count(0),
+ m_pos(start_) {}
+
+Cues::~Cues() {
+ const long n = m_count + m_preload_count;
+
+ CuePoint** p = m_cue_points;
+ CuePoint** const q = p + n;
+
+ while (p != q) {
+ CuePoint* const pCP = *p++;
+ assert(pCP);
+
+ delete pCP;
+ }
+
+ delete[] m_cue_points;
+}
+
+long Cues::GetCount() const {
+ if (m_cue_points == NULL)
+ return -1;
+
+ return m_count; // TODO: really ignore preload count?
+}
+
+bool Cues::DoneParsing() const {
+ const long long stop = m_start + m_size;
+ return (m_pos >= stop);
+}
+
+bool Cues::Init() const {
+ if (m_cue_points)
+ return true;
+
+ if (m_count != 0 || m_preload_count != 0)
+ return false;
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ const long long stop = m_start + m_size;
+ long long pos = m_start;
+
+ long cue_points_size = 0;
+
+ while (pos < stop) {
+ const long long idpos = pos;
+
+ long len;
+
+ const long long id = ReadID(pReader, pos, len);
+ if (id < 0 || (pos + len) > stop) {
+ return false;
+ }
+
+ pos += len; // consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ if (size < 0 || (pos + len > stop)) {
+ return false;
+ }
+
+ pos += len; // consume Size field
+ if (pos + size > stop) {
+ return false;
+ }
+
+ if (id == libwebm::kMkvCuePoint) {
+ if (!PreloadCuePoint(cue_points_size, idpos))
+ return false;
+ }
+
+ pos += size; // skip payload
+ }
+ return true;
+}
+
+bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
+ if (m_count != 0)
+ return false;
+
+ if (m_preload_count >= cue_points_size) {
+ const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
+
+ CuePoint** const qq = new (std::nothrow) CuePoint*[n];
+ if (qq == NULL)
+ return false;
+
+ CuePoint** q = qq; // beginning of target
+
+ CuePoint** p = m_cue_points; // beginning of source
+ CuePoint** const pp = p + m_preload_count; // end of source
+
+ while (p != pp)
+ *q++ = *p++;
+
+ delete[] m_cue_points;
+
+ m_cue_points = qq;
+ cue_points_size = n;
+ }
+
+ CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
+ if (pCP == NULL)
+ return false;
+
+ m_cue_points[m_preload_count++] = pCP;
+ return true;
+}
+
+bool Cues::LoadCuePoint() const {
+ const long long stop = m_start + m_size;
+
+ if (m_pos >= stop)
+ return false; // nothing else to do
+
+ if (!Init()) {
+ m_pos = stop;
+ return false;
+ }
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ while (m_pos < stop) {
+ const long long idpos = m_pos;
+
+ long len;
+
+ const long long id = ReadID(pReader, m_pos, len);
+ if (id < 0 || (m_pos + len) > stop)
+ return false;
+
+ m_pos += len; // consume ID
+
+ const long long size = ReadUInt(pReader, m_pos, len);
+ if (size < 0 || (m_pos + len) > stop)
+ return false;
+
+ m_pos += len; // consume Size field
+ if ((m_pos + size) > stop)
+ return false;
+
+ if (id != libwebm::kMkvCuePoint) {
+ m_pos += size; // consume payload
+ if (m_pos > stop)
+ return false;
+
+ continue;
+ }
+
+ if (m_preload_count < 1)
+ return false;
+
+ CuePoint* const pCP = m_cue_points[m_count];
+ if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos)))
+ return false;
+
+ if (!pCP->Load(pReader)) {
+ m_pos = stop;
+ return false;
+ }
+ ++m_count;
+ --m_preload_count;
+
+ m_pos += size; // consume payload
+ if (m_pos > stop)
+ return false;
+
+ return true; // yes, we loaded a cue point
+ }
+
+ return false; // no, we did not load a cue point
+}
+
+bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
+ const CuePoint::TrackPosition*& pTP) const {
+ if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0)
+ return false;
+
+ CuePoint** const ii = m_cue_points;
+ CuePoint** i = ii;
+
+ CuePoint** const jj = ii + m_count;
+ CuePoint** j = jj;
+
+ pCP = *i;
+ if (pCP == NULL)
+ return false;
+
+ if (time_ns <= pCP->GetTime(m_pSegment)) {
+ pTP = pCP->Find(pTrack);
+ return (pTP != NULL);
+ }
+
+ while (i < j) {
+ // INVARIANT:
+ //[ii, i) <= time_ns
+ //[i, j) ?
+ //[j, jj) > time_ns
+
+ CuePoint** const k = i + (j - i) / 2;
+ if (k >= jj)
+ return false;
+
+ CuePoint* const pCP = *k;
+ if (pCP == NULL)
+ return false;
+
+ const long long t = pCP->GetTime(m_pSegment);
+
+ if (t <= time_ns)
+ i = k + 1;
+ else
+ j = k;
+
+ if (i > j)
+ return false;
+ }
+
+ if (i != j || i > jj || i <= ii)
+ return false;
+
+ pCP = *--i;
+
+ if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns)
+ return false;
+
+ // TODO: here and elsewhere, it's probably not correct to search
+ // for the cue point with this time, and then search for a matching
+ // track. In principle, the matching track could be on some earlier
+ // cue point, and with our current algorithm, we'd miss it. To make
+ // this bullet-proof, we'd need to create a secondary structure,
+ // with a list of cue points that apply to a track, and then search
+ // that track-based structure for a matching cue point.
+
+ pTP = pCP->Find(pTrack);
+ return (pTP != NULL);
+}
+
+const CuePoint* Cues::GetFirst() const {
+ if (m_cue_points == NULL || m_count == 0)
+ return NULL;
+
+ CuePoint* const* const pp = m_cue_points;
+ if (pp == NULL)
+ return NULL;
+
+ CuePoint* const pCP = pp[0];
+ if (pCP == NULL || pCP->GetTimeCode() < 0)
+ return NULL;
+
+ return pCP;
+}
+
+const CuePoint* Cues::GetLast() const {
+ if (m_cue_points == NULL || m_count <= 0)
+ return NULL;
+
+ const long index = m_count - 1;
+
+ CuePoint* const* const pp = m_cue_points;
+ if (pp == NULL)
+ return NULL;
+
+ CuePoint* const pCP = pp[index];
+ if (pCP == NULL || pCP->GetTimeCode() < 0)
+ return NULL;
+
+ return pCP;
+}
+
+const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
+ if (pCurr == NULL || pCurr->GetTimeCode() < 0 || m_cue_points == NULL ||
+ m_count < 1) {
+ return NULL;
+ }
+
+ long index = pCurr->m_index;
+ if (index >= m_count)
+ return NULL;
+
+ CuePoint* const* const pp = m_cue_points;
+ if (pp == NULL || pp[index] != pCurr)
+ return NULL;
+
+ ++index;
+
+ if (index >= m_count)
+ return NULL;
+
+ CuePoint* const pNext = pp[index];
+
+ if (pNext == NULL || pNext->GetTimeCode() < 0)
+ return NULL;
+
+ return pNext;
+}
+
+const BlockEntry* Cues::GetBlock(const CuePoint* pCP,
+ const CuePoint::TrackPosition* pTP) const {
+ if (pCP == NULL || pTP == NULL)
+ return NULL;
+
+ return m_pSegment->GetBlock(*pCP, *pTP);
+}
+
+const BlockEntry* Segment::GetBlock(const CuePoint& cp,
+ const CuePoint::TrackPosition& tp) {
+ Cluster** const ii = m_clusters;
+ Cluster** i = ii;
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ Cluster** const jj = ii + count;
+ Cluster** j = jj;
+
+ while (i < j) {
+ // INVARIANT:
+ //[ii, i) < pTP->m_pos
+ //[i, j) ?
+ //[j, jj) > pTP->m_pos
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ Cluster* const pCluster = *k;
+ assert(pCluster);
+
+ // const long long pos_ = pCluster->m_pos;
+ // assert(pos_);
+ // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+ const long long pos = pCluster->GetPosition();
+ assert(pos >= 0);
+
+ if (pos < tp.m_pos)
+ i = k + 1;
+ else if (pos > tp.m_pos)
+ j = k;
+ else
+ return pCluster->GetEntry(cp, tp);
+ }
+
+ assert(i == j);
+ // assert(Cluster::HasBlockEntries(this, tp.m_pos));
+
+ Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos); //, -1);
+ if (pCluster == NULL)
+ return NULL;
+
+ const ptrdiff_t idx = i - m_clusters;
+
+ if (!PreloadCluster(pCluster, idx)) {
+ delete pCluster;
+ return NULL;
+ }
+ assert(m_clusters);
+ assert(m_clusterPreloadCount > 0);
+ assert(m_clusters[idx] == pCluster);
+
+ return pCluster->GetEntry(cp, tp);
+}
+
+const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) {
+ if (requested_pos < 0)
+ return 0;
+
+ Cluster** const ii = m_clusters;
+ Cluster** i = ii;
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ Cluster** const jj = ii + count;
+ Cluster** j = jj;
+
+ while (i < j) {
+ // INVARIANT:
+ //[ii, i) < pTP->m_pos
+ //[i, j) ?
+ //[j, jj) > pTP->m_pos
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ Cluster* const pCluster = *k;
+ assert(pCluster);
+
+ // const long long pos_ = pCluster->m_pos;
+ // assert(pos_);
+ // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+ const long long pos = pCluster->GetPosition();
+ assert(pos >= 0);
+
+ if (pos < requested_pos)
+ i = k + 1;
+ else if (pos > requested_pos)
+ j = k;
+ else
+ return pCluster;
+ }
+
+ assert(i == j);
+ // assert(Cluster::HasBlockEntries(this, tp.m_pos));
+
+ Cluster* const pCluster = Cluster::Create(this, -1, requested_pos);
+ if (pCluster == NULL)
+ return NULL;
+
+ const ptrdiff_t idx = i - m_clusters;
+
+ if (!PreloadCluster(pCluster, idx)) {
+ delete pCluster;
+ return NULL;
+ }
+ assert(m_clusters);
+ assert(m_clusterPreloadCount > 0);
+ assert(m_clusters[idx] == pCluster);
+
+ return pCluster;
+}
+
+CuePoint::CuePoint(long idx, long long pos)
+ : m_element_start(0),
+ m_element_size(0),
+ m_index(idx),
+ m_timecode(-1 * pos),
+ m_track_positions(NULL),
+ m_track_positions_count(0) {
+ assert(pos > 0);
+}
+
+CuePoint::~CuePoint() { delete[] m_track_positions; }
+
+bool CuePoint::Load(IMkvReader* pReader) {
+ // odbgstream os;
+ // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
+
+ if (m_timecode >= 0) // already loaded
+ return true;
+
+ assert(m_track_positions == NULL);
+ assert(m_track_positions_count == 0);
+
+ long long pos_ = -m_timecode;
+ const long long element_start = pos_;
+
+ long long stop;
+
+ {
+ long len;
+
+ const long long id = ReadID(pReader, pos_, len);
+ if (id != libwebm::kMkvCuePoint)
+ return false;
+
+ pos_ += len; // consume ID
+
+ const long long size = ReadUInt(pReader, pos_, len);
+ assert(size >= 0);
+
+ pos_ += len; // consume Size field
+ // pos_ now points to start of payload
+
+ stop = pos_ + size;
+ }
+
+ const long long element_size = stop - element_start;
+
+ long long pos = pos_;
+
+ // First count number of track positions
+
+ while (pos < stop) {
+ long len;
+
+ const long long id = ReadID(pReader, pos, len);
+ if ((id < 0) || (pos + len > stop)) {
+ return false;
+ }
+
+ pos += len; // consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ if ((size < 0) || (pos + len > stop)) {
+ return false;
+ }
+
+ pos += len; // consume Size field
+ if ((pos + size) > stop) {
+ return false;
+ }
+
+ if (id == libwebm::kMkvCueTime)
+ m_timecode = UnserializeUInt(pReader, pos, size);
+
+ else if (id == libwebm::kMkvCueTrackPositions)
+ ++m_track_positions_count;
+
+ pos += size; // consume payload
+ }
+
+ if (m_timecode < 0 || m_track_positions_count <= 0) {
+ return false;
+ }
+
+ // os << "CuePoint::Load(cont'd): idpos=" << idpos
+ // << " timecode=" << m_timecode
+ // << endl;
+
+ m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
+ if (m_track_positions == NULL)
+ return false;
+
+ // Now parse track positions
+
+ TrackPosition* p = m_track_positions;
+ pos = pos_;
+
+ while (pos < stop) {
+ long len;
+
+ const long long id = ReadID(pReader, pos, len);
+ 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);
+
+ pos += len; // consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == libwebm::kMkvCueTrackPositions) {
+ TrackPosition& tp = *p++;
+ if (!tp.Parse(pReader, pos, size)) {
+ return false;
+ }
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return false;
+ }
+
+ assert(size_t(p - m_track_positions) == m_track_positions_count);
+
+ m_element_start = element_start;
+ m_element_size = element_size;
+
+ return true;
+}
+
+bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
+ long long size_) {
+ const long long stop = start_ + size_;
+ long long pos = start_;
+
+ m_track = -1;
+ m_pos = -1;
+ m_block = 1; // default
+
+ while (pos < stop) {
+ long len;
+
+ const long long id = ReadID(pReader, pos, len);
+ if ((id < 0) || ((pos + len) > stop)) {
+ return false;
+ }
+
+ pos += len; // consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ if ((size < 0) || ((pos + len) > stop)) {
+ return false;
+ }
+
+ pos += len; // consume Size field
+ if ((pos + size) > stop) {
+ return false;
+ }
+
+ if (id == libwebm::kMkvCueTrack)
+ m_track = UnserializeUInt(pReader, pos, size);
+ else if (id == libwebm::kMkvCueClusterPosition)
+ m_pos = UnserializeUInt(pReader, pos, size);
+ else if (id == libwebm::kMkvCueBlockNumber)
+ m_block = UnserializeUInt(pReader, pos, size);
+
+ pos += size; // consume payload
+ }
+
+ if ((m_pos < 0) || (m_track <= 0)) {
+ return false;
+ }
+
+ return true;
+}
+
+const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
+ assert(pTrack);
+
+ const long long n = pTrack->GetNumber();
+
+ const TrackPosition* i = m_track_positions;
+ const TrackPosition* const j = i + m_track_positions_count;
+
+ while (i != j) {
+ const TrackPosition& p = *i++;
+
+ if (p.m_track == n)
+ return &p;
+ }
+
+ return NULL; // no matching track number found
+}
+
+long long CuePoint::GetTimeCode() const { return m_timecode; }
+
+long long CuePoint::GetTime(const Segment* pSegment) const {
+ assert(pSegment);
+ assert(m_timecode >= 0);
+
+ const SegmentInfo* const pInfo = pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long time = scale * m_timecode;
+
+ return time;
+}
+
+bool Segment::DoneParsing() const {
+ if (m_size < 0) {
+ long long total, avail;
+
+ const int status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return true; // must assume done
+
+ if (total < 0)
+ return false; // assume live stream
+
+ return (m_pos >= total);
+ }
+
+ const long long stop = m_start + m_size;
+
+ return (m_pos >= stop);
+}
+
+const Cluster* Segment::GetFirst() const {
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ Cluster* const pCluster = m_clusters[0];
+ assert(pCluster);
+
+ return pCluster;
+}
+
+const Cluster* Segment::GetLast() const {
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ const long idx = m_clusterCount - 1;
+
+ Cluster* const pCluster = m_clusters[idx];
+ assert(pCluster);
+
+ return pCluster;
+}
+
+unsigned long Segment::GetCount() const { return m_clusterCount; }
+
+const Cluster* Segment::GetNext(const Cluster* pCurr) {
+ assert(pCurr);
+ assert(pCurr != &m_eos);
+ assert(m_clusters);
+
+ long idx = pCurr->m_index;
+
+ if (idx >= 0) {
+ assert(m_clusterCount > 0);
+ assert(idx < m_clusterCount);
+ assert(pCurr == m_clusters[idx]);
+
+ ++idx;
+
+ if (idx >= m_clusterCount)
+ return &m_eos; // caller will LoadCluster as desired
+
+ Cluster* const pNext = m_clusters[idx];
+ assert(pNext);
+ assert(pNext->m_index >= 0);
+ assert(pNext->m_index == idx);
+
+ return pNext;
+ }
+
+ assert(m_clusterPreloadCount > 0);
+
+ long long pos = pCurr->m_element_start;
+
+ assert(m_size >= 0); // TODO
+ const long long stop = m_start + m_size; // end of segment
+
+ {
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0);
+ assert((pos + len) <= stop); // TODO
+ if (result != 0)
+ return NULL;
+
+ const long long id = ReadID(m_pReader, pos, len);
+ if (id != libwebm::kMkvCluster)
+ return NULL;
+
+ pos += len; // consume ID
+
+ // Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0); // TODO
+ assert((pos + len) <= stop); // TODO
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size > 0); // TODO
+ // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
+
+ pos += len; // consume length of size of element
+ assert((pos + size) <= stop); // TODO
+
+ // Pos now points to start of payload
+
+ pos += size; // consume payload
+ }
+
+ long long off_next = 0;
+
+ while (pos < stop) {
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0);
+ assert((pos + len) <= stop); // TODO
+ if (result != 0)
+ return NULL;
+
+ const long long idpos = pos; // pos of next (potential) cluster
+
+ const long long id = ReadID(m_pReader, idpos, len);
+ if (id < 0)
+ return NULL;
+
+ pos += len; // consume ID
+
+ // Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0); // TODO
+ assert((pos + len) <= stop); // TODO
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size >= 0); // TODO
+
+ pos += len; // consume length of size of element
+ assert((pos + size) <= stop); // TODO
+
+ // Pos now points to start of payload
+
+ if (size == 0) // weird
+ continue;
+
+ if (id == libwebm::kMkvCluster) {
+ const long long off_next_ = idpos - m_start;
+
+ long long pos_;
+ long len_;
+
+ const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_);
+
+ assert(status >= 0);
+
+ if (status > 0) {
+ off_next = off_next_;
+ break;
+ }
+ }
+
+ pos += size; // consume payload
+ }
+
+ if (off_next <= 0)
+ return 0;
+
+ Cluster** const ii = m_clusters + m_clusterCount;
+ Cluster** i = ii;
+
+ Cluster** const jj = ii + m_clusterPreloadCount;
+ Cluster** j = jj;
+
+ while (i < j) {
+ // INVARIANT:
+ //[0, i) < pos_next
+ //[i, j) ?
+ //[j, jj) > pos_next
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ Cluster* const pNext = *k;
+ assert(pNext);
+ assert(pNext->m_index < 0);
+
+ // const long long pos_ = pNext->m_pos;
+ // assert(pos_);
+ // pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+ pos = pNext->GetPosition();
+
+ if (pos < off_next)
+ i = k + 1;
+ else if (pos > off_next)
+ j = k;
+ else
+ return pNext;
+ }
+
+ assert(i == j);
+
+ Cluster* const pNext = Cluster::Create(this, -1, off_next);
+ if (pNext == NULL)
+ return NULL;
+
+ const ptrdiff_t idx_next = i - m_clusters; // insertion position
+
+ if (!PreloadCluster(pNext, idx_next)) {
+ delete pNext;
+ return NULL;
+ }
+ assert(m_clusters);
+ assert(idx_next < m_clusterSize);
+ assert(m_clusters[idx_next] == pNext);
+
+ return pNext;
+}
+
+long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult,
+ long long& pos, long& len) {
+ assert(pCurr);
+ assert(!pCurr->EOS());
+ assert(m_clusters);
+
+ pResult = 0;
+
+ if (pCurr->m_index >= 0) { // loaded (not merely preloaded)
+ assert(m_clusters[pCurr->m_index] == pCurr);
+
+ const long next_idx = pCurr->m_index + 1;
+
+ if (next_idx < m_clusterCount) {
+ pResult = m_clusters[next_idx];
+ return 0; // success
+ }
+
+ // curr cluster is last among loaded
+
+ const long result = LoadCluster(pos, len);
+
+ if (result < 0) // error or underflow
+ return result;
+
+ if (result > 0) // no more clusters
+ {
+ // pResult = &m_eos;
+ return 1;
+ }
+
+ pResult = GetLast();
+ return 0; // success
+ }
+
+ assert(m_pos > 0);
+
+ 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;
+
+ // interrogate curr cluster
+
+ pos = pCurr->m_element_start;
+
+ if (pCurr->m_element_size >= 0)
+ pos += pCurr->m_element_size;
+ else {
+ 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 id = ReadUInt(m_pReader, pos, len);
+
+ if (id != libwebm::kMkvCluster)
+ return -1;
+
+ pos += len; // consume ID
+
+ // 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
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size) // TODO: should never happen
+ return E_FILE_FORMAT_INVALID; // TODO: resolve this
+
+ // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
+
+ if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ // Pos now points to start of payload
+
+ pos += size; // consume payload (that is, the current cluster)
+ if (segment_stop >= 0 && pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ // By consuming the payload, we are assuming that the curr
+ // cluster isn't interesting. That is, we don't bother checking
+ // whether the payload of the curr cluster is less than what
+ // happens to be available (obtained via IMkvReader::Length).
+ // Presumably the caller has already dispensed with the current
+ // cluster, and really does want the next cluster.
+ }
+
+ // pos now points to just beyond the last fully-loaded cluster
+
+ for (;;) {
+ const long status = DoParseNext(pResult, pos, len);
+
+ if (status <= 1)
+ return status;
+ }
+}
+
+long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
+ 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;
+
+ // Parse next cluster. This is strictly a parsing activity.
+ // Creation of a new cluster object happens later, after the
+ // parsing is done.
+
+ long long off_next = 0;
+ long long cluster_size = -1;
+
+ for (;;) {
+ if ((total >= 0) && (pos >= total))
+ return 1; // EOF
+
+ if ((segment_stop >= 0) && (pos >= segment_stop))
+ return 1; // EOF
+
+ 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; // absolute
+ const long long idoff = pos - m_start; // relative
+
+ const long long id = ReadID(m_pReader, idpos, len); // absolute
+
+ if (id < 0) // error
+ return static_cast<long>(id);
+
+ if (id == 0) // weird
+ return -1; // generic error
+
+ pos += len; // consume ID
+
+ // 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 length of size of element
+
+ // Pos now points to start of payload
+
+ if (size == 0) // weird
+ continue;
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if ((segment_stop >= 0) && (size != unknown_size) &&
+ ((pos + size) > segment_stop)) {
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (id == libwebm::kMkvCues) {
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long element_stop = pos + size;
+
+ if ((segment_stop >= 0) && (element_stop > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ const long long element_start = idpos;
+ const long long element_size = element_stop - element_start;
+
+ if (m_pCues == NULL) {
+ m_pCues = new (std::nothrow)
+ Cues(this, pos, size, element_start, element_size);
+ if (m_pCues == NULL)
+ return false;
+ }
+
+ pos += size; // consume payload
+ if (segment_stop >= 0 && pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ continue;
+ }
+
+ if (id != libwebm::kMkvCluster) { // not a Cluster ID
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += size; // consume payload
+ if (segment_stop >= 0 && pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ continue;
+ }
+
+ // We have a cluster.
+ off_next = idoff;
+
+ if (size != unknown_size)
+ cluster_size = size;
+
+ break;
+ }
+
+ assert(off_next > 0); // have cluster
+
+ // We have parsed the next cluster.
+ // We have not created a cluster object yet. What we need
+ // to do now is determine whether it has already be preloaded
+ //(in which case, an object for this cluster has already been
+ // created), and if not, create a new cluster object.
+
+ Cluster** const ii = m_clusters + m_clusterCount;
+ Cluster** i = ii;
+
+ Cluster** const jj = ii + m_clusterPreloadCount;
+ Cluster** j = jj;
+
+ while (i < j) {
+ // INVARIANT:
+ //[0, i) < pos_next
+ //[i, j) ?
+ //[j, jj) > pos_next
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ const Cluster* const pNext = *k;
+ assert(pNext);
+ assert(pNext->m_index < 0);
+
+ pos = pNext->GetPosition();
+ assert(pos >= 0);
+
+ if (pos < off_next)
+ i = k + 1;
+ else if (pos > off_next)
+ j = k;
+ else {
+ pResult = pNext;
+ return 0; // success
+ }
+ }
+
+ assert(i == j);
+
+ long long pos_;
+ long len_;
+
+ status = Cluster::HasBlockEntries(this, off_next, pos_, len_);
+
+ if (status < 0) { // error or underflow
+ pos = pos_;
+ len = len_;
+
+ return status;
+ }
+
+ if (status > 0) { // means "found at least one block entry"
+ Cluster* const pNext = Cluster::Create(this,
+ -1, // preloaded
+ off_next);
+ if (pNext == NULL)
+ return -1;
+
+ const ptrdiff_t idx_next = i - m_clusters; // insertion position
+
+ if (!PreloadCluster(pNext, idx_next)) {
+ delete pNext;
+ return -1;
+ }
+ assert(m_clusters);
+ assert(idx_next < m_clusterSize);
+ assert(m_clusters[idx_next] == pNext);
+
+ pResult = pNext;
+ return 0; // success
+ }
+
+ // status == 0 means "no block entries found"
+
+ 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 = ReadID(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 == libwebm::kMkvCluster || id == libwebm::kMkvCues)
+ 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
+ if (segment_stop >= 0 && pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+ } // determine cluster size
+
+ cluster_size = pos - payload_pos;
+ assert(cluster_size >= 0); // TODO: handle cluster_size = 0
+
+ pos = payload_pos; // reset and re-parse original cluster
+ }
+
+ pos += cluster_size; // consume payload
+ if (segment_stop >= 0 && pos > segment_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ return 2; // try to find a cluster that follows next
+}
+
+const Cluster* Segment::FindCluster(long long time_ns) const {
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ {
+ Cluster* const pCluster = m_clusters[0];
+ assert(pCluster);
+ assert(pCluster->m_index == 0);
+
+ if (time_ns <= pCluster->GetTime())
+ return pCluster;
+ }
+
+ // Binary search of cluster array
+
+ long i = 0;
+ long j = m_clusterCount;
+
+ while (i < j) {
+ // INVARIANT:
+ //[0, i) <= time_ns
+ //[i, j) ?
+ //[j, m_clusterCount) > time_ns
+
+ const long k = i + (j - i) / 2;
+ assert(k < m_clusterCount);
+
+ Cluster* const pCluster = m_clusters[k];
+ assert(pCluster);
+ assert(pCluster->m_index == k);
+
+ const long long t = pCluster->GetTime();
+
+ if (t <= time_ns)
+ i = k + 1;
+ else
+ j = k;
+
+ assert(i <= j);
+ }
+
+ assert(i == j);
+ assert(i > 0);
+ assert(i <= m_clusterCount);
+
+ const long k = i - 1;
+
+ Cluster* const pCluster = m_clusters[k];
+ assert(pCluster);
+ assert(pCluster->m_index == k);
+ assert(pCluster->GetTime() <= time_ns);
+
+ return pCluster;
+}
+
+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 {
+ assert(m_pInfo);
+ return m_pInfo->GetDuration();
+}
+
+Chapters::Chapters(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_editions(NULL),
+ m_editions_size(0),
+ m_editions_count(0) {}
+
+Chapters::~Chapters() {
+ while (m_editions_count > 0) {
+ Edition& e = m_editions[--m_editions_count];
+ e.Clear();
+ }
+ delete[] m_editions;
+}
+
+long Chapters::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) // error
+ return status;
+
+ if (size == 0) // weird
+ continue;
+
+ if (id == libwebm::kMkvEditionEntry) {
+ status = ParseEdition(pos, size);
+
+ if (status < 0) // error
+ return status;
+ }
+
+ pos += size;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ return 0;
+}
+
+int Chapters::GetEditionCount() const { return m_editions_count; }
+
+const Chapters::Edition* Chapters::GetEdition(int idx) const {
+ if (idx < 0)
+ return NULL;
+
+ if (idx >= m_editions_count)
+ return NULL;
+
+ return m_editions + idx;
+}
+
+bool Chapters::ExpandEditionsArray() {
+ if (m_editions_size > m_editions_count)
+ return true; // nothing else to do
+
+ const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
+
+ Edition* const editions = new (std::nothrow) Edition[size];
+
+ if (editions == NULL)
+ return false;
+
+ for (int idx = 0; idx < m_editions_count; ++idx) {
+ m_editions[idx].ShallowCopy(editions[idx]);
+ }
+
+ delete[] m_editions;
+ m_editions = editions;
+
+ m_editions_size = size;
+ return true;
+}
+
+long Chapters::ParseEdition(long long pos, long long size) {
+ if (!ExpandEditionsArray())
+ return -1;
+
+ Edition& e = m_editions[m_editions_count++];
+ e.Init();
+
+ return e.Parse(m_pSegment->m_pReader, pos, size);
+}
+
+Chapters::Edition::Edition() {}
+
+Chapters::Edition::~Edition() {}
+
+int Chapters::Edition::GetAtomCount() const { return m_atoms_count; }
+
+const Chapters::Atom* Chapters::Edition::GetAtom(int index) const {
+ if (index < 0)
+ return NULL;
+
+ if (index >= m_atoms_count)
+ return NULL;
+
+ return m_atoms + index;
+}
+
+void Chapters::Edition::Init() {
+ m_atoms = NULL;
+ m_atoms_size = 0;
+ m_atoms_count = 0;
+}
+
+void Chapters::Edition::ShallowCopy(Edition& rhs) const {
+ rhs.m_atoms = m_atoms;
+ rhs.m_atoms_size = m_atoms_size;
+ rhs.m_atoms_count = m_atoms_count;
+}
+
+void Chapters::Edition::Clear() {
+ while (m_atoms_count > 0) {
+ Atom& a = m_atoms[--m_atoms_count];
+ a.Clear();
+ }
+
+ delete[] m_atoms;
+ m_atoms = NULL;
+
+ m_atoms_size = 0;
+}
+
+long Chapters::Edition::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)
+ continue;
+
+ if (id == libwebm::kMkvChapterAtom) {
+ status = ParseAtom(pReader, pos, size);
+
+ if (status < 0) // error
+ return status;
+ }
+
+ pos += size;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ return 0;
+}
+
+long Chapters::Edition::ParseAtom(IMkvReader* pReader, long long pos,
+ long long size) {
+ if (!ExpandAtomsArray())
+ return -1;
+
+ Atom& a = m_atoms[m_atoms_count++];
+ a.Init();
+
+ return a.Parse(pReader, pos, size);
+}
+
+bool Chapters::Edition::ExpandAtomsArray() {
+ if (m_atoms_size > m_atoms_count)
+ return true; // nothing else to do
+
+ const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
+
+ Atom* const atoms = new (std::nothrow) Atom[size];
+
+ if (atoms == NULL)
+ return false;
+
+ for (int idx = 0; idx < m_atoms_count; ++idx) {
+ m_atoms[idx].ShallowCopy(atoms[idx]);
+ }
+
+ delete[] m_atoms;
+ m_atoms = atoms;
+
+ m_atoms_size = size;
+ return true;
+}
+
+Chapters::Atom::Atom() {}
+
+Chapters::Atom::~Atom() {}
+
+unsigned long long Chapters::Atom::GetUID() const { return m_uid; }
+
+const char* Chapters::Atom::GetStringUID() const { return m_string_uid; }
+
+long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; }
+
+long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; }
+
+long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const {
+ return GetTime(pChapters, m_start_timecode);
+}
+
+long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const {
+ return GetTime(pChapters, m_stop_timecode);
+}
+
+int Chapters::Atom::GetDisplayCount() const { return m_displays_count; }
+
+const Chapters::Display* Chapters::Atom::GetDisplay(int index) const {
+ if (index < 0)
+ return NULL;
+
+ if (index >= m_displays_count)
+ return NULL;
+
+ return m_displays + index;
+}
+
+void Chapters::Atom::Init() {
+ m_string_uid = NULL;
+ m_uid = 0;
+ m_start_timecode = -1;
+ m_stop_timecode = -1;
+
+ m_displays = NULL;
+ m_displays_size = 0;
+ m_displays_count = 0;
+}
+
+void Chapters::Atom::ShallowCopy(Atom& rhs) const {
+ rhs.m_string_uid = m_string_uid;
+ rhs.m_uid = m_uid;
+ rhs.m_start_timecode = m_start_timecode;
+ rhs.m_stop_timecode = m_stop_timecode;
+
+ rhs.m_displays = m_displays;
+ rhs.m_displays_size = m_displays_size;
+ rhs.m_displays_count = m_displays_count;
+}
+
+void Chapters::Atom::Clear() {
+ delete[] m_string_uid;
+ m_string_uid = NULL;
+
+ while (m_displays_count > 0) {
+ Display& d = m_displays[--m_displays_count];
+ d.Clear();
+ }
+
+ delete[] m_displays;
+ m_displays = NULL;
+
+ m_displays_size = 0;
+}
+
+long Chapters::Atom::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) // 0 length payload, skip.
+ continue;
+
+ if (id == libwebm::kMkvChapterDisplay) {
+ status = ParseDisplay(pReader, pos, size);
+
+ if (status < 0) // error
+ return status;
+ } else if (id == libwebm::kMkvChapterStringUID) {
+ status = UnserializeString(pReader, pos, size, m_string_uid);
+
+ if (status < 0) // error
+ return status;
+ } else if (id == libwebm::kMkvChapterUID) {
+ long long val;
+ status = UnserializeInt(pReader, pos, size, val);
+
+ if (status < 0) // error
+ return status;
+
+ m_uid = static_cast<unsigned long long>(val);
+ } else if (id == libwebm::kMkvChapterTimeStart) {
+ const long long val = UnserializeUInt(pReader, pos, size);
+
+ if (val < 0) // error
+ return static_cast<long>(val);
+
+ m_start_timecode = val;
+ } else if (id == libwebm::kMkvChapterTimeEnd) {
+ const long long val = UnserializeUInt(pReader, pos, size);
+
+ if (val < 0) // error
+ return static_cast<long>(val);
+
+ m_stop_timecode = val;
+ }
+
+ pos += size;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ return 0;
+}
+
+long long Chapters::Atom::GetTime(const Chapters* pChapters,
+ long long timecode) {
+ if (pChapters == NULL)
+ return -1;
+
+ Segment* const pSegment = pChapters->m_pSegment;
+
+ if (pSegment == NULL) // weird
+ return -1;
+
+ const SegmentInfo* const pInfo = pSegment->GetInfo();
+
+ if (pInfo == NULL)
+ return -1;
+
+ const long long timecode_scale = pInfo->GetTimeCodeScale();
+
+ if (timecode_scale < 1) // weird
+ return -1;
+
+ if (timecode < 0)
+ return -1;
+
+ const long long result = timecode_scale * timecode;
+
+ return result;
+}
+
+long Chapters::Atom::ParseDisplay(IMkvReader* pReader, long long pos,
+ long long size) {
+ if (!ExpandDisplaysArray())
+ return -1;
+
+ Display& d = m_displays[m_displays_count++];
+ d.Init();
+
+ return d.Parse(pReader, pos, size);
+}
+
+bool Chapters::Atom::ExpandDisplaysArray() {
+ if (m_displays_size > m_displays_count)
+ return true; // nothing else to do
+
+ const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
+
+ Display* const displays = new (std::nothrow) Display[size];
+
+ if (displays == NULL)
+ return false;
+
+ for (int idx = 0; idx < m_displays_count; ++idx) {
+ m_displays[idx].ShallowCopy(displays[idx]);
+ }
+
+ delete[] m_displays;
+ m_displays = displays;
+
+ m_displays_size = size;
+ return true;
+}
+
+Chapters::Display::Display() {}
+
+Chapters::Display::~Display() {}
+
+const char* Chapters::Display::GetString() const { return m_string; }
+
+const char* Chapters::Display::GetLanguage() const { return m_language; }
+
+const char* Chapters::Display::GetCountry() const { return m_country; }
+
+void Chapters::Display::Init() {
+ m_string = NULL;
+ m_language = NULL;
+ m_country = NULL;
+}
+
+void Chapters::Display::ShallowCopy(Display& rhs) const {
+ rhs.m_string = m_string;
+ rhs.m_language = m_language;
+ rhs.m_country = m_country;
+}
+
+void Chapters::Display::Clear() {
+ delete[] m_string;
+ m_string = NULL;
+
+ delete[] m_language;
+ m_language = NULL;
+
+ delete[] m_country;
+ m_country = NULL;
+}
+
+long Chapters::Display::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) // No payload.
+ continue;
+
+ if (id == libwebm::kMkvChapString) {
+ status = UnserializeString(pReader, pos, size, m_string);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvChapLanguage) {
+ status = UnserializeString(pReader, pos, size, m_language);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvChapCountry) {
+ status = UnserializeString(pReader, pos, size, m_country);
+
+ if (status)
+ return status;
+ }
+
+ pos += size;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ 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 == libwebm::kMkvTag) {
+ status = ParseTag(pos, size);
+
+ if (status < 0)
+ return status;
+ }
+
+ pos += size;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ 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 == libwebm::kMkvSimpleTag) {
+ status = ParseSimpleTag(pReader, pos, size);
+
+ if (status < 0)
+ return status;
+ }
+
+ pos += size;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ 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 == libwebm::kMkvTagName) {
+ status = UnserializeString(pReader, pos, size, m_tag_name);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvTagString) {
+ status = UnserializeString(pReader, pos, size, m_tag_string);
+
+ if (status)
+ return status;
+ }
+
+ pos += size;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ return 0;
+}
+
+SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
+ long long element_start, long long element_size)
+ : m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ m_pMuxingAppAsUTF8(NULL),
+ m_pWritingAppAsUTF8(NULL),
+ m_pTitleAsUTF8(NULL) {}
+
+SegmentInfo::~SegmentInfo() {
+ delete[] m_pMuxingAppAsUTF8;
+ m_pMuxingAppAsUTF8 = NULL;
+
+ delete[] m_pWritingAppAsUTF8;
+ m_pWritingAppAsUTF8 = NULL;
+
+ delete[] m_pTitleAsUTF8;
+ m_pTitleAsUTF8 = NULL;
+}
+
+long SegmentInfo::Parse() {
+ assert(m_pMuxingAppAsUTF8 == NULL);
+ assert(m_pWritingAppAsUTF8 == NULL);
+ assert(m_pTitleAsUTF8 == NULL);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = m_start;
+ const long long stop = m_start + m_size;
+
+ m_timecodeScale = 1000000;
+ m_duration = -1;
+
+ while (pos < stop) {
+ long long id, size;
+
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvTimecodeScale) {
+ m_timecodeScale = UnserializeUInt(pReader, pos, size);
+
+ if (m_timecodeScale <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvDuration) {
+ const long status = UnserializeFloat(pReader, pos, size, m_duration);
+
+ if (status < 0)
+ return status;
+
+ if (m_duration < 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvMuxingApp) {
+ const long status =
+ UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvWritingApp) {
+ const long status =
+ UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvTitle) {
+ const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8);
+
+ if (status)
+ return status;
+ }
+
+ pos += size;
+
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ const double rollover_check = m_duration * m_timecodeScale;
+ if (rollover_check > LLONG_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0;
+}
+
+long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; }
+
+long long SegmentInfo::GetDuration() const {
+ if (m_duration < 0)
+ return -1;
+
+ assert(m_timecodeScale >= 1);
+
+ const double dd = double(m_duration) * double(m_timecodeScale);
+ const long long d = static_cast<long long>(dd);
+
+ return d;
+}
+
+const char* SegmentInfo::GetMuxingAppAsUTF8() const {
+ return m_pMuxingAppAsUTF8;
+}
+
+const char* SegmentInfo::GetWritingAppAsUTF8() const {
+ return m_pWritingAppAsUTF8;
+}
+
+const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; }
+
+///////////////////////////////////////////////////////////////
+// ContentEncoding element
+ContentEncoding::ContentCompression::ContentCompression()
+ : algo(0), settings(NULL), settings_len(0) {}
+
+ContentEncoding::ContentCompression::~ContentCompression() {
+ delete[] settings;
+}
+
+ContentEncoding::ContentEncryption::ContentEncryption()
+ : algo(0),
+ key_id(NULL),
+ key_id_len(0),
+ signature(NULL),
+ signature_len(0),
+ sig_key_id(NULL),
+ sig_key_id_len(0),
+ sig_algo(0),
+ sig_hash_algo(0) {}
+
+ContentEncoding::ContentEncryption::~ContentEncryption() {
+ delete[] key_id;
+ delete[] signature;
+ delete[] sig_key_id;
+}
+
+ContentEncoding::ContentEncoding()
+ : compression_entries_(NULL),
+ compression_entries_end_(NULL),
+ encryption_entries_(NULL),
+ encryption_entries_end_(NULL),
+ encoding_order_(0),
+ encoding_scope_(1),
+ encoding_type_(0) {}
+
+ContentEncoding::~ContentEncoding() {
+ ContentCompression** comp_i = compression_entries_;
+ ContentCompression** const comp_j = compression_entries_end_;
+
+ while (comp_i != comp_j) {
+ ContentCompression* const comp = *comp_i++;
+ delete comp;
+ }
+
+ delete[] compression_entries_;
+
+ ContentEncryption** enc_i = encryption_entries_;
+ ContentEncryption** const enc_j = encryption_entries_end_;
+
+ while (enc_i != enc_j) {
+ ContentEncryption* const enc = *enc_i++;
+ delete enc;
+ }
+
+ delete[] encryption_entries_;
+}
+
+const ContentEncoding::ContentCompression*
+ ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
+ const ptrdiff_t count = compression_entries_end_ - compression_entries_;
+ assert(count >= 0);
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return compression_entries_[idx];
+}
+
+unsigned long ContentEncoding::GetCompressionCount() const {
+ const ptrdiff_t count = compression_entries_end_ - compression_entries_;
+ assert(count >= 0);
+
+ return static_cast<unsigned long>(count);
+}
+
+const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex(
+ unsigned long idx) const {
+ const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
+ assert(count >= 0);
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return encryption_entries_[idx];
+}
+
+unsigned long ContentEncoding::GetEncryptionCount() const {
+ const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
+ assert(count >= 0);
+
+ return static_cast<unsigned long>(count);
+}
+
+long ContentEncoding::ParseContentEncAESSettingsEntry(
+ long long start, long long size, IMkvReader* pReader,
+ ContentEncAESSettings* aes) {
+ assert(pReader);
+ assert(aes);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ while (pos < stop) {
+ long long id, size;
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvAESSettingsCipherMode) {
+ aes->cipher_mode = UnserializeUInt(pReader, pos, size);
+ if (aes->cipher_mode != 1)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ return 0;
+}
+
+long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
+ IMkvReader* pReader) {
+ assert(pReader);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ // Count ContentCompression and ContentEncryption elements.
+ int compression_count = 0;
+ int encryption_count = 0;
+
+ while (pos < stop) {
+ long long id, size;
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvContentCompression)
+ ++compression_count;
+
+ if (id == libwebm::kMkvContentEncryption)
+ ++encryption_count;
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (compression_count <= 0 && encryption_count <= 0)
+ return -1;
+
+ if (compression_count > 0) {
+ compression_entries_ =
+ new (std::nothrow) ContentCompression*[compression_count];
+ if (!compression_entries_)
+ return -1;
+ compression_entries_end_ = compression_entries_;
+ }
+
+ if (encryption_count > 0) {
+ encryption_entries_ =
+ new (std::nothrow) ContentEncryption*[encryption_count];
+ if (!encryption_entries_) {
+ delete[] compression_entries_;
+ return -1;
+ }
+ encryption_entries_end_ = encryption_entries_;
+ }
+
+ pos = start;
+ while (pos < stop) {
+ long long id, size;
+ long status = ParseElementHeader(pReader, pos, stop, id, size);
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvContentEncodingOrder) {
+ encoding_order_ = UnserializeUInt(pReader, pos, size);
+ } else if (id == libwebm::kMkvContentEncodingScope) {
+ encoding_scope_ = UnserializeUInt(pReader, pos, size);
+ if (encoding_scope_ < 1)
+ return -1;
+ } else if (id == libwebm::kMkvContentEncodingType) {
+ encoding_type_ = UnserializeUInt(pReader, pos, size);
+ } else if (id == libwebm::kMkvContentCompression) {
+ ContentCompression* const compression =
+ new (std::nothrow) ContentCompression();
+ if (!compression)
+ return -1;
+
+ status = ParseCompressionEntry(pos, size, pReader, compression);
+ if (status) {
+ delete compression;
+ return status;
+ }
+ *compression_entries_end_++ = compression;
+ } else if (id == libwebm::kMkvContentEncryption) {
+ ContentEncryption* const encryption =
+ new (std::nothrow) ContentEncryption();
+ if (!encryption)
+ return -1;
+
+ status = ParseEncryptionEntry(pos, size, pReader, encryption);
+ if (status) {
+ delete encryption;
+ return status;
+ }
+ *encryption_entries_end_++ = encryption;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ return 0;
+}
+
+long ContentEncoding::ParseCompressionEntry(long long start, long long size,
+ IMkvReader* pReader,
+ ContentCompression* compression) {
+ assert(pReader);
+ assert(compression);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ bool valid = false;
+
+ while (pos < stop) {
+ long long id, size;
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvContentCompAlgo) {
+ long long algo = UnserializeUInt(pReader, pos, size);
+ if (algo < 0)
+ return E_FILE_FORMAT_INVALID;
+ compression->algo = algo;
+ valid = true;
+ } else if (id == libwebm::kMkvContentCompSettings) {
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ const size_t buflen = static_cast<size_t>(size);
+ unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+ if (buf == NULL)
+ return -1;
+
+ const int read_status =
+ pReader->Read(pos, static_cast<long>(buflen), buf);
+ if (read_status) {
+ delete[] buf;
+ return status;
+ }
+
+ compression->settings = buf;
+ compression->settings_len = buflen;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ // ContentCompAlgo is mandatory
+ if (!valid)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0;
+}
+
+long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
+ IMkvReader* pReader,
+ ContentEncryption* encryption) {
+ assert(pReader);
+ assert(encryption);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ while (pos < stop) {
+ long long id, size;
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvContentEncAlgo) {
+ encryption->algo = UnserializeUInt(pReader, pos, size);
+ if (encryption->algo != 5)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvContentEncKeyID) {
+ delete[] encryption->key_id;
+ encryption->key_id = NULL;
+ encryption->key_id_len = 0;
+
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ const size_t buflen = static_cast<size_t>(size);
+ unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+ if (buf == NULL)
+ return -1;
+
+ const int read_status =
+ pReader->Read(pos, static_cast<long>(buflen), buf);
+ if (read_status) {
+ delete[] buf;
+ return status;
+ }
+
+ encryption->key_id = buf;
+ encryption->key_id_len = buflen;
+ } else if (id == libwebm::kMkvContentSignature) {
+ delete[] encryption->signature;
+ encryption->signature = NULL;
+ encryption->signature_len = 0;
+
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ const size_t buflen = static_cast<size_t>(size);
+ unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+ if (buf == NULL)
+ return -1;
+
+ const int read_status =
+ pReader->Read(pos, static_cast<long>(buflen), buf);
+ if (read_status) {
+ delete[] buf;
+ return status;
+ }
+
+ encryption->signature = buf;
+ encryption->signature_len = buflen;
+ } else if (id == libwebm::kMkvContentSigKeyID) {
+ delete[] encryption->sig_key_id;
+ encryption->sig_key_id = NULL;
+ encryption->sig_key_id_len = 0;
+
+ if (size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ const size_t buflen = static_cast<size_t>(size);
+ unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+ if (buf == NULL)
+ return -1;
+
+ const int read_status =
+ pReader->Read(pos, static_cast<long>(buflen), buf);
+ if (read_status) {
+ delete[] buf;
+ return status;
+ }
+
+ encryption->sig_key_id = buf;
+ encryption->sig_key_id_len = buflen;
+ } else if (id == libwebm::kMkvContentSigAlgo) {
+ encryption->sig_algo = UnserializeUInt(pReader, pos, size);
+ } else if (id == libwebm::kMkvContentSigHashAlgo) {
+ encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
+ } else if (id == libwebm::kMkvContentEncAESSettings) {
+ const long status = ParseContentEncAESSettingsEntry(
+ pos, size, pReader, &encryption->aes_settings);
+ if (status)
+ return status;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ return 0;
+}
+
+Track::Track(Segment* pSegment, long long element_start, long long element_size)
+ : m_pSegment(pSegment),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ content_encoding_entries_(NULL),
+ content_encoding_entries_end_(NULL) {}
+
+Track::~Track() {
+ Info& info = const_cast<Info&>(m_info);
+ info.Clear();
+
+ ContentEncoding** i = content_encoding_entries_;
+ ContentEncoding** const j = content_encoding_entries_end_;
+
+ while (i != j) {
+ ContentEncoding* const encoding = *i++;
+ delete encoding;
+ }
+
+ delete[] content_encoding_entries_;
+}
+
+long Track::Create(Segment* pSegment, const Info& info, long long element_start,
+ long long element_size, Track*& pResult) {
+ if (pResult)
+ return -1;
+
+ Track* const pTrack =
+ new (std::nothrow) Track(pSegment, element_start, element_size);
+
+ if (pTrack == NULL)
+ return -1; // generic error
+
+ const int status = info.Copy(pTrack->m_info);
+
+ if (status) { // error
+ delete pTrack;
+ return status;
+ }
+
+ pResult = pTrack;
+ return 0; // success
+}
+
+Track::Info::Info()
+ : uid(0),
+ defaultDuration(0),
+ codecDelay(0),
+ seekPreRoll(0),
+ nameAsUTF8(NULL),
+ language(NULL),
+ codecId(NULL),
+ codecNameAsUTF8(NULL),
+ codecPrivate(NULL),
+ codecPrivateSize(0),
+ lacing(false) {}
+
+Track::Info::~Info() { Clear(); }
+
+void Track::Info::Clear() {
+ delete[] nameAsUTF8;
+ nameAsUTF8 = NULL;
+
+ delete[] language;
+ language = NULL;
+
+ delete[] codecId;
+ codecId = NULL;
+
+ delete[] codecPrivate;
+ codecPrivate = NULL;
+ codecPrivateSize = 0;
+
+ delete[] codecNameAsUTF8;
+ codecNameAsUTF8 = NULL;
+}
+
+int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
+ if (str == static_cast<char * Info::*>(NULL))
+ return -1;
+
+ char*& dst = dst_.*str;
+
+ if (dst) // should be NULL already
+ return -1;
+
+ const char* const src = this->*str;
+
+ if (src == NULL)
+ return 0;
+
+ const size_t len = strlen(src);
+
+ dst = SafeArrayAlloc<char>(1, len + 1);
+
+ if (dst == NULL)
+ return -1;
+
+ strcpy(dst, src);
+
+ return 0;
+}
+
+int Track::Info::Copy(Info& dst) const {
+ if (&dst == this)
+ return 0;
+
+ dst.type = type;
+ dst.number = number;
+ dst.defaultDuration = defaultDuration;
+ dst.codecDelay = codecDelay;
+ dst.seekPreRoll = seekPreRoll;
+ dst.uid = uid;
+ dst.lacing = lacing;
+ dst.settings = settings;
+
+ // We now copy the string member variables from src to dst.
+ // This involves memory allocation so in principle the operation
+ // can fail (indeed, that's why we have Info::Copy), so we must
+ // report this to the caller. An error return from this function
+ // therefore implies that the copy was only partially successful.
+
+ if (int status = CopyStr(&Info::nameAsUTF8, dst))
+ return status;
+
+ if (int status = CopyStr(&Info::language, dst))
+ return status;
+
+ if (int status = CopyStr(&Info::codecId, dst))
+ return status;
+
+ if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
+ return status;
+
+ if (codecPrivateSize > 0) {
+ if (codecPrivate == NULL)
+ return -1;
+
+ if (dst.codecPrivate)
+ return -1;
+
+ if (dst.codecPrivateSize != 0)
+ return -1;
+
+ dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
+
+ if (dst.codecPrivate == NULL)
+ return -1;
+
+ memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
+ dst.codecPrivateSize = codecPrivateSize;
+ }
+
+ return 0;
+}
+
+const BlockEntry* Track::GetEOS() const { return &m_eos; }
+
+long Track::GetType() const { return m_info.type; }
+
+long Track::GetNumber() const { return m_info.number; }
+
+unsigned long long Track::GetUid() const { return m_info.uid; }
+
+const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
+
+const char* Track::GetLanguage() const { return m_info.language; }
+
+const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
+
+const char* Track::GetCodecId() const { return m_info.codecId; }
+
+const unsigned char* Track::GetCodecPrivate(size_t& size) const {
+ size = m_info.codecPrivateSize;
+ return m_info.codecPrivate;
+}
+
+bool Track::GetLacing() const { return m_info.lacing; }
+
+unsigned long long Track::GetDefaultDuration() const {
+ return m_info.defaultDuration;
+}
+
+unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
+
+unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
+
+long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
+ const Cluster* pCluster = m_pSegment->GetFirst();
+
+ for (int i = 0;;) {
+ if (pCluster == NULL) {
+ pBlockEntry = GetEOS();
+ return 1;
+ }
+
+ if (pCluster->EOS()) {
+ if (m_pSegment->DoneParsing()) {
+ pBlockEntry = GetEOS();
+ return 1;
+ }
+
+ pBlockEntry = 0;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long status = pCluster->GetFirst(pBlockEntry);
+
+ if (status < 0) // error
+ return status;
+
+ if (pBlockEntry == 0) { // empty cluster
+ pCluster = m_pSegment->GetNext(pCluster);
+ continue;
+ }
+
+ for (;;) {
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+
+ const long long tn = pBlock->GetTrackNumber();
+
+ if ((tn == m_info.number) && VetEntry(pBlockEntry))
+ return 0;
+
+ const BlockEntry* pNextEntry;
+
+ status = pCluster->GetNext(pBlockEntry, pNextEntry);
+
+ if (status < 0) // error
+ return status;
+
+ if (pNextEntry == 0)
+ break;
+
+ pBlockEntry = pNextEntry;
+ }
+
+ ++i;
+
+ if (i >= 100)
+ break;
+
+ pCluster = m_pSegment->GetNext(pCluster);
+ }
+
+ // NOTE: if we get here, it means that we didn't find a block with
+ // a matching track number. We interpret that as an error (which
+ // might be too conservative).
+
+ pBlockEntry = GetEOS(); // so we can return a non-NULL value
+ return 1;
+}
+
+long Track::GetNext(const BlockEntry* pCurrEntry,
+ const BlockEntry*& pNextEntry) const {
+ assert(pCurrEntry);
+ assert(!pCurrEntry->EOS()); //?
+
+ const Block* const pCurrBlock = pCurrEntry->GetBlock();
+ assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
+ if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
+ return -1;
+
+ const Cluster* pCluster = pCurrEntry->GetCluster();
+ assert(pCluster);
+ assert(!pCluster->EOS());
+
+ long status = pCluster->GetNext(pCurrEntry, pNextEntry);
+
+ if (status < 0) // error
+ return status;
+
+ for (int i = 0;;) {
+ while (pNextEntry) {
+ const Block* const pNextBlock = pNextEntry->GetBlock();
+ assert(pNextBlock);
+
+ if (pNextBlock->GetTrackNumber() == m_info.number)
+ return 0;
+
+ pCurrEntry = pNextEntry;
+
+ status = pCluster->GetNext(pCurrEntry, pNextEntry);
+
+ if (status < 0) // error
+ return status;
+ }
+
+ pCluster = m_pSegment->GetNext(pCluster);
+
+ if (pCluster == NULL) {
+ pNextEntry = GetEOS();
+ return 1;
+ }
+
+ if (pCluster->EOS()) {
+ if (m_pSegment->DoneParsing()) {
+ pNextEntry = GetEOS();
+ return 1;
+ }
+
+ // 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
+ // calls GetNext again, which repeats the same search. This is
+ // a pathological case, since the only way it can happen is if
+ // there exists a long sequence of clusters none of which contain a
+ // block from this track. One way around this problem is for the
+ // caller to be smarter when he loads another cluster: don't call
+ // us back until you have a cluster that contains a block from this
+ // track. (Of course, that's not cheap either, since our caller
+ // would have to scan the each cluster as it's loaded, so that
+ // would just push back the problem.)
+
+ pNextEntry = NULL;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ status = pCluster->GetFirst(pNextEntry);
+
+ if (status < 0) // error
+ return status;
+
+ if (pNextEntry == NULL) // empty cluster
+ continue;
+
+ ++i;
+
+ if (i >= 100)
+ break;
+ }
+
+ // NOTE: if we get here, it means that we didn't find a block with
+ // a matching track number after lots of searching, so we give
+ // up trying.
+
+ pNextEntry = GetEOS(); // so we can return a non-NULL value
+ return 1;
+}
+
+bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
+ assert(pBlockEntry);
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+ assert(pBlock->GetTrackNumber() == m_info.number);
+ if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
+ return false;
+
+ // This function is used during a seek to determine whether the
+ // frame is a valid seek target. This default function simply
+ // returns true, which means all frames are valid seek targets.
+ // It gets overridden by the VideoTrack class, because only video
+ // keyframes can be used as seek target.
+
+ return true;
+}
+
+long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
+ const long status = GetFirst(pResult);
+
+ if (status < 0) // buffer underflow, etc
+ return status;
+
+ assert(pResult);
+
+ if (pResult->EOS())
+ return 0;
+
+ const Cluster* pCluster = pResult->GetCluster();
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+
+ if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
+ return 0;
+
+ Cluster** const clusters = m_pSegment->m_clusters;
+ assert(clusters);
+
+ const long count = m_pSegment->GetCount(); // loaded only, not preloaded
+ assert(count > 0);
+
+ Cluster** const i = clusters + pCluster->GetIndex();
+ assert(i);
+ assert(*i == pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ Cluster** const j = clusters + count;
+
+ 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);
+
+ pCluster = *mid;
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+ assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
+
+ 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) {
+ pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ pResult = pCluster->GetEntry(this);
+
+ if ((pResult != 0) && !pResult->EOS())
+ return 0;
+
+ // landed on empty cluster (no entries)
+ }
+
+ pResult = GetEOS(); // weird
+ return 0;
+}
+
+const ContentEncoding* Track::GetContentEncodingByIndex(
+ unsigned long idx) const {
+ const ptrdiff_t count =
+ content_encoding_entries_end_ - content_encoding_entries_;
+ assert(count >= 0);
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return content_encoding_entries_[idx];
+}
+
+unsigned long Track::GetContentEncodingCount() const {
+ const ptrdiff_t count =
+ content_encoding_entries_end_ - content_encoding_entries_;
+ assert(count >= 0);
+
+ return static_cast<unsigned long>(count);
+}
+
+long Track::ParseContentEncodingsEntry(long long start, long long size) {
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+ assert(pReader);
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ // Count ContentEncoding elements.
+ int count = 0;
+ while (pos < stop) {
+ long long id, size;
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+ if (status < 0) // error
+ return status;
+
+ // pos now designates start of element
+ if (id == libwebm::kMkvContentEncoding)
+ ++count;
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (count <= 0)
+ return -1;
+
+ content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
+ if (!content_encoding_entries_)
+ return -1;
+
+ content_encoding_entries_end_ = content_encoding_entries_;
+
+ pos = start;
+ while (pos < stop) {
+ long long id, size;
+ long status = ParseElementHeader(pReader, pos, stop, id, size);
+ if (status < 0) // error
+ return status;
+
+ // pos now designates start of element
+ if (id == libwebm::kMkvContentEncoding) {
+ ContentEncoding* const content_encoding =
+ new (std::nothrow) ContentEncoding();
+ if (!content_encoding)
+ return -1;
+
+ status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
+ if (status) {
+ delete content_encoding;
+ return status;
+ }
+
+ *content_encoding_entries_end_++ = content_encoding;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0;
+}
+
+Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
+
+BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
+
+const Block* Track::EOSBlock::GetBlock() const { return NULL; }
+
+bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos,
+ long long value_size, bool is_x,
+ PrimaryChromaticity** chromaticity) {
+ if (!reader)
+ return false;
+
+ std::auto_ptr<PrimaryChromaticity> chromaticity_ptr;
+
+ if (!*chromaticity) {
+ chromaticity_ptr.reset(new PrimaryChromaticity());
+ } else {
+ chromaticity_ptr.reset(*chromaticity);
+ }
+
+ if (!chromaticity_ptr.get())
+ return false;
+
+ float* value = is_x ? &chromaticity_ptr->x : &chromaticity_ptr->y;
+
+ double parser_value = 0;
+ const long long value_parse_status =
+ UnserializeFloat(reader, read_pos, value_size, parser_value);
+
+ *value = static_cast<float>(parser_value);
+
+ if (value_parse_status < 0 || *value < 0.0 || *value > 1.0)
+ return false;
+
+ *chromaticity = chromaticity_ptr.release();
+ return true;
+}
+
+bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
+ long long mm_size, MasteringMetadata** mm) {
+ if (!reader || *mm)
+ return false;
+
+ std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
+ if (!mm_ptr.get())
+ return false;
+
+ const long long mm_end = mm_start + mm_size;
+ long long read_pos = mm_start;
+
+ while (read_pos < mm_end) {
+ long long child_id = 0;
+ long long child_size = 0;
+
+ const long long status =
+ ParseElementHeader(reader, read_pos, mm_end, child_id, child_size);
+ if (status < 0)
+ return false;
+
+ if (child_id == libwebm::kMkvLuminanceMax) {
+ double value = 0;
+ const long long value_parse_status =
+ UnserializeFloat(reader, read_pos, child_size, value);
+ mm_ptr->luminance_max = static_cast<float>(value);
+ if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 ||
+ mm_ptr->luminance_max > 9999.99) {
+ return false;
+ }
+ } else if (child_id == libwebm::kMkvLuminanceMin) {
+ double value = 0;
+ const long long value_parse_status =
+ UnserializeFloat(reader, read_pos, child_size, value);
+ mm_ptr->luminance_min = static_cast<float>(value);
+ if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 ||
+ mm_ptr->luminance_min > 999.9999) {
+ return false;
+ }
+ } else {
+ bool is_x = false;
+ PrimaryChromaticity** chromaticity;
+ switch (child_id) {
+ case libwebm::kMkvPrimaryRChromaticityX:
+ case libwebm::kMkvPrimaryRChromaticityY:
+ is_x = child_id == libwebm::kMkvPrimaryRChromaticityX;
+ chromaticity = &mm_ptr->r;
+ break;
+ case libwebm::kMkvPrimaryGChromaticityX:
+ case libwebm::kMkvPrimaryGChromaticityY:
+ is_x = child_id == libwebm::kMkvPrimaryGChromaticityX;
+ chromaticity = &mm_ptr->g;
+ break;
+ case libwebm::kMkvPrimaryBChromaticityX:
+ case libwebm::kMkvPrimaryBChromaticityY:
+ is_x = child_id == libwebm::kMkvPrimaryBChromaticityX;
+ chromaticity = &mm_ptr->b;
+ break;
+ case libwebm::kMkvWhitePointChromaticityX:
+ case libwebm::kMkvWhitePointChromaticityY:
+ is_x = child_id == libwebm::kMkvWhitePointChromaticityX;
+ chromaticity = &mm_ptr->white_point;
+ break;
+ default:
+ return false;
+ }
+ const bool value_parse_status = PrimaryChromaticity::Parse(
+ reader, read_pos, child_size, is_x, chromaticity);
+ if (!value_parse_status)
+ return false;
+ }
+
+ read_pos += child_size;
+ if (read_pos > mm_end)
+ return false;
+ }
+
+ *mm = mm_ptr.release();
+ return true;
+}
+
+bool Colour::Parse(IMkvReader* reader, long long colour_start,
+ long long colour_size, Colour** colour) {
+ if (!reader || *colour)
+ return false;
+
+ std::auto_ptr<Colour> colour_ptr(new Colour());
+ if (!colour_ptr.get())
+ return false;
+
+ const long long colour_end = colour_start + colour_size;
+ long long read_pos = colour_start;
+
+ while (read_pos < colour_end) {
+ long long child_id = 0;
+ long long child_size = 0;
+
+ const long status =
+ ParseElementHeader(reader, read_pos, colour_end, child_id, child_size);
+ if (status < 0)
+ return false;
+
+ if (child_id == libwebm::kMkvMatrixCoefficients) {
+ colour_ptr->matrix_coefficients =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->matrix_coefficients < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvBitsPerChannel) {
+ colour_ptr->bits_per_channel =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->bits_per_channel < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvChromaSubsamplingHorz) {
+ colour_ptr->chroma_subsampling_horz =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->chroma_subsampling_horz < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvChromaSubsamplingVert) {
+ colour_ptr->chroma_subsampling_vert =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->chroma_subsampling_vert < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvCbSubsamplingHorz) {
+ colour_ptr->cb_subsampling_horz =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->cb_subsampling_horz < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvCbSubsamplingVert) {
+ colour_ptr->cb_subsampling_vert =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->cb_subsampling_vert < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvChromaSitingHorz) {
+ colour_ptr->chroma_siting_horz =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->chroma_siting_horz < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvChromaSitingVert) {
+ colour_ptr->chroma_siting_vert =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->chroma_siting_vert < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvRange) {
+ colour_ptr->range = UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->range < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvTransferCharacteristics) {
+ colour_ptr->transfer_characteristics =
+ UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->transfer_characteristics < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvPrimaries) {
+ colour_ptr->primaries = UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->primaries < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvMaxCLL) {
+ colour_ptr->max_cll = UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->max_cll < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvMaxFALL) {
+ colour_ptr->max_fall = UnserializeUInt(reader, read_pos, child_size);
+ if (colour_ptr->max_fall < 0)
+ return false;
+ } else if (child_id == libwebm::kMkvMasteringMetadata) {
+ if (!MasteringMetadata::Parse(reader, read_pos, child_size,
+ &colour_ptr->mastering_metadata))
+ return false;
+ } else {
+ return false;
+ }
+
+ read_pos += child_size;
+ if (read_pos > colour_end)
+ return false;
+ }
+ *colour = colour_ptr.release();
+ return true;
+}
+
+VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
+ long long element_size)
+ : Track(pSegment, element_start, element_size), m_colour(NULL) {}
+
+VideoTrack::~VideoTrack() { delete m_colour; }
+
+long VideoTrack::Parse(Segment* pSegment, const Info& info,
+ long long element_start, long long element_size,
+ VideoTrack*& pResult) {
+ if (pResult)
+ return -1;
+
+ if (info.type != Track::kVideo)
+ return -1;
+
+ 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;
+
+ const Settings& s = info.settings;
+ assert(s.start >= 0);
+ assert(s.size >= 0);
+
+ long long pos = s.start;
+ assert(pos >= 0);
+
+ const long long stop = pos + s.size;
+
+ Colour* colour = NULL;
+
+ while (pos < stop) {
+ long long id, size;
+
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvPixelWidth) {
+ width = UnserializeUInt(pReader, pos, size);
+
+ if (width <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvPixelHeight) {
+ height = UnserializeUInt(pReader, pos, size);
+
+ if (height <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvDisplayWidth) {
+ display_width = UnserializeUInt(pReader, pos, size);
+
+ if (display_width <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvDisplayHeight) {
+ display_height = UnserializeUInt(pReader, pos, size);
+
+ if (display_height <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvDisplayUnit) {
+ display_unit = UnserializeUInt(pReader, pos, size);
+
+ if (display_unit < 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvStereoMode) {
+ stereo_mode = UnserializeUInt(pReader, pos, size);
+
+ if (stereo_mode < 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvFrameRate) {
+ const long status = UnserializeFloat(pReader, pos, size, rate);
+
+ if (status < 0)
+ return status;
+
+ if (rate <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvColour) {
+ if (!Colour::Parse(pReader, pos, size, &colour))
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ VideoTrack* const pTrack =
+ new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
+
+ if (pTrack == NULL)
+ return -1; // generic error
+
+ const int status = info.Copy(pTrack->m_info);
+
+ if (status) { // error
+ delete pTrack;
+ return status;
+ }
+
+ 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;
+ pTrack->m_colour = colour;
+
+ pResult = pTrack;
+ return 0; // success
+}
+
+bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
+ return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
+}
+
+long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
+ const long status = GetFirst(pResult);
+
+ if (status < 0) // buffer underflow, etc
+ return status;
+
+ assert(pResult);
+
+ if (pResult->EOS())
+ return 0;
+
+ const Cluster* pCluster = pResult->GetCluster();
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+
+ if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
+ return 0;
+
+ Cluster** const clusters = m_pSegment->m_clusters;
+ assert(clusters);
+
+ const long count = m_pSegment->GetCount(); // loaded only, not pre-loaded
+ assert(count > 0);
+
+ Cluster** const i = clusters + pCluster->GetIndex();
+ assert(i);
+ assert(*i == pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ Cluster** const j = clusters + count;
+
+ 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);
+
+ pCluster = *mid;
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+ assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
+
+ 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);
+
+ pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ pResult = pCluster->GetEntry(this, time_ns);
+
+ if ((pResult != 0) && !pResult->EOS()) // found a keyframe
+ return 0;
+
+ while (lo != i) {
+ pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ pResult = pCluster->GetEntry(this, time_ns);
+
+ if ((pResult != 0) && !pResult->EOS())
+ return 0;
+ }
+
+ // weird: we're on the first cluster, but no keyframe found
+ // should never happen but we must return something anyway
+
+ pResult = GetEOS();
+ return 0;
+}
+
+Colour* VideoTrack::GetColour() const { return m_colour; }
+
+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,
+ long long element_size)
+ : Track(pSegment, element_start, element_size) {}
+
+long AudioTrack::Parse(Segment* pSegment, const Info& info,
+ long long element_start, long long element_size,
+ AudioTrack*& pResult) {
+ if (pResult)
+ return -1;
+
+ if (info.type != Track::kAudio)
+ return -1;
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ const Settings& s = info.settings;
+ assert(s.start >= 0);
+ assert(s.size >= 0);
+
+ long long pos = s.start;
+ assert(pos >= 0);
+
+ const long long stop = pos + s.size;
+
+ double rate = 8000.0; // MKV default
+ long long channels = 1;
+ long long bit_depth = 0;
+
+ while (pos < stop) {
+ long long id, size;
+
+ long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (id == libwebm::kMkvSamplingFrequency) {
+ status = UnserializeFloat(pReader, pos, size, rate);
+
+ if (status < 0)
+ return status;
+
+ if (rate <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvChannels) {
+ channels = UnserializeUInt(pReader, pos, size);
+
+ if (channels <= 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvBitDepth) {
+ bit_depth = UnserializeUInt(pReader, pos, size);
+
+ if (bit_depth <= 0)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ AudioTrack* const pTrack =
+ new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
+
+ if (pTrack == NULL)
+ return -1; // generic error
+
+ const int status = info.Copy(pTrack->m_info);
+
+ if (status) {
+ delete pTrack;
+ return status;
+ }
+
+ pTrack->m_rate = rate;
+ pTrack->m_channels = channels;
+ pTrack->m_bitDepth = bit_depth;
+
+ pResult = pTrack;
+ return 0; // success
+}
+
+double AudioTrack::GetSamplingRate() const { return m_rate; }
+
+long long AudioTrack::GetChannels() const { return m_channels; }
+
+long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
+
+Tracks::Tracks(Segment* pSegment, long long start, long long size_,
+ long long element_start, long long element_size)
+ : m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ m_trackEntries(NULL),
+ m_trackEntriesEnd(NULL) {}
+
+long Tracks::Parse() {
+ assert(m_trackEntries == NULL);
+ assert(m_trackEntriesEnd == NULL);
+
+ const long long stop = m_start + m_size;
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ int count = 0;
+ long long pos = m_start;
+
+ while (pos < stop) {
+ long long id, size;
+
+ const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (size == 0) // weird
+ continue;
+
+ if (id == libwebm::kMkvTrackEntry)
+ ++count;
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (count <= 0)
+ return 0; // success
+
+ m_trackEntries = new (std::nothrow) Track*[count];
+
+ if (m_trackEntries == NULL)
+ return -1;
+
+ m_trackEntriesEnd = m_trackEntries;
+
+ pos = m_start;
+
+ while (pos < stop) {
+ const long long element_start = pos;
+
+ long long id, payload_size;
+
+ const long status =
+ ParseElementHeader(pReader, pos, stop, id, payload_size);
+
+ if (status < 0) // error
+ return status;
+
+ if (payload_size == 0) // weird
+ continue;
+
+ const long long payload_stop = pos + payload_size;
+ assert(payload_stop <= stop); // checked in ParseElement
+
+ const long long element_size = payload_stop - element_start;
+
+ if (id == libwebm::kMkvTrackEntry) {
+ Track*& pTrack = *m_trackEntriesEnd;
+ pTrack = NULL;
+
+ const long status = ParseTrackEntry(pos, payload_size, element_start,
+ element_size, pTrack);
+ if (status)
+ return status;
+
+ if (pTrack)
+ ++m_trackEntriesEnd;
+ }
+
+ pos = payload_stop;
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0; // success
+}
+
+unsigned long Tracks::GetTracksCount() const {
+ const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
+ assert(result >= 0);
+
+ return static_cast<unsigned long>(result);
+}
+
+long Tracks::ParseTrackEntry(long long track_start, long long track_size,
+ long long element_start, long long element_size,
+ Track*& pResult) const {
+ if (pResult)
+ return -1;
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = track_start;
+ const long long track_stop = track_start + track_size;
+
+ Track::Info info;
+
+ info.type = 0;
+ info.number = 0;
+ info.uid = 0;
+ info.defaultDuration = 0;
+
+ Track::Settings v;
+ v.start = -1;
+ v.size = -1;
+
+ Track::Settings a;
+ a.start = -1;
+ a.size = -1;
+
+ Track::Settings e; // content_encodings_settings;
+ e.start = -1;
+ e.size = -1;
+
+ long long lacing = 1; // default is true
+
+ while (pos < track_stop) {
+ long long id, size;
+
+ const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
+
+ if (status < 0) // error
+ return status;
+
+ if (size < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long start = pos;
+
+ if (id == libwebm::kMkvVideo) {
+ v.start = start;
+ v.size = size;
+ } else if (id == libwebm::kMkvAudio) {
+ a.start = start;
+ a.size = size;
+ } else if (id == libwebm::kMkvContentEncodings) {
+ e.start = start;
+ e.size = size;
+ } else if (id == libwebm::kMkvTrackUID) {
+ if (size > 8)
+ return E_FILE_FORMAT_INVALID;
+
+ info.uid = 0;
+
+ long long pos_ = start;
+ const long long pos_end = start + size;
+
+ while (pos_ != pos_end) {
+ unsigned char b;
+
+ const int status = pReader->Read(pos_, 1, &b);
+
+ if (status)
+ return status;
+
+ info.uid <<= 8;
+ info.uid |= b;
+
+ ++pos_;
+ }
+ } else if (id == libwebm::kMkvTrackNumber) {
+ const long long num = UnserializeUInt(pReader, pos, size);
+
+ if ((num <= 0) || (num > 127))
+ return E_FILE_FORMAT_INVALID;
+
+ info.number = static_cast<long>(num);
+ } else if (id == libwebm::kMkvTrackType) {
+ const long long type = UnserializeUInt(pReader, pos, size);
+
+ if ((type <= 0) || (type > 254))
+ return E_FILE_FORMAT_INVALID;
+
+ info.type = static_cast<long>(type);
+ } else if (id == libwebm::kMkvName) {
+ const long status =
+ UnserializeString(pReader, pos, size, info.nameAsUTF8);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvLanguage) {
+ const long status = UnserializeString(pReader, pos, size, info.language);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvDefaultDuration) {
+ const long long duration = UnserializeUInt(pReader, pos, size);
+
+ if (duration < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ info.defaultDuration = static_cast<unsigned long long>(duration);
+ } else if (id == libwebm::kMkvCodecID) {
+ const long status = UnserializeString(pReader, pos, size, info.codecId);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvFlagLacing) {
+ lacing = UnserializeUInt(pReader, pos, size);
+
+ if ((lacing < 0) || (lacing > 1))
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvCodecPrivate) {
+ delete[] info.codecPrivate;
+ info.codecPrivate = NULL;
+ info.codecPrivateSize = 0;
+
+ const size_t buflen = static_cast<size_t>(size);
+
+ if (buflen) {
+ unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+
+ if (buf == NULL)
+ return -1;
+
+ const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
+
+ if (status) {
+ delete[] buf;
+ return status;
+ }
+
+ info.codecPrivate = buf;
+ info.codecPrivateSize = buflen;
+ }
+ } else if (id == libwebm::kMkvCodecName) {
+ const long status =
+ UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
+
+ if (status)
+ return status;
+ } else if (id == libwebm::kMkvCodecDelay) {
+ info.codecDelay = UnserializeUInt(pReader, pos, size);
+ } else if (id == libwebm::kMkvSeekPreRoll) {
+ info.seekPreRoll = UnserializeUInt(pReader, pos, size);
+ }
+
+ pos += size; // consume payload
+ if (pos > track_stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != track_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (info.number <= 0) // not specified
+ return E_FILE_FORMAT_INVALID;
+
+ if (GetTrackByNumber(info.number))
+ return E_FILE_FORMAT_INVALID;
+
+ if (info.type <= 0) // not specified
+ return E_FILE_FORMAT_INVALID;
+
+ info.lacing = (lacing > 0) ? true : false;
+
+ if (info.type == Track::kVideo) {
+ if (v.start < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (a.start >= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ info.settings = v;
+
+ VideoTrack* pTrack = NULL;
+
+ const long status = VideoTrack::Parse(m_pSegment, info, element_start,
+ element_size, pTrack);
+
+ if (status)
+ return status;
+
+ pResult = pTrack;
+ assert(pResult);
+
+ if (e.start >= 0)
+ pResult->ParseContentEncodingsEntry(e.start, e.size);
+ } else if (info.type == Track::kAudio) {
+ if (a.start < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (v.start >= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ info.settings = a;
+
+ AudioTrack* pTrack = NULL;
+
+ const long status = AudioTrack::Parse(m_pSegment, info, element_start,
+ element_size, pTrack);
+
+ if (status)
+ return status;
+
+ pResult = pTrack;
+ assert(pResult);
+
+ if (e.start >= 0)
+ pResult->ParseContentEncodingsEntry(e.start, e.size);
+ } else {
+ // neither video nor audio - probably metadata or subtitles
+
+ if (a.start >= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (v.start >= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (info.type == Track::kMetadata && e.start >= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ info.settings.start = -1;
+ info.settings.size = 0;
+
+ Track* pTrack = NULL;
+
+ const long status =
+ Track::Create(m_pSegment, info, element_start, element_size, pTrack);
+
+ if (status)
+ return status;
+
+ pResult = pTrack;
+ assert(pResult);
+ }
+
+ return 0; // success
+}
+
+Tracks::~Tracks() {
+ Track** i = m_trackEntries;
+ Track** const j = m_trackEntriesEnd;
+
+ while (i != j) {
+ Track* const pTrack = *i++;
+ delete pTrack;
+ }
+
+ delete[] m_trackEntries;
+}
+
+const Track* Tracks::GetTrackByNumber(long tn) const {
+ if (tn < 0)
+ return NULL;
+
+ Track** i = m_trackEntries;
+ Track** const j = m_trackEntriesEnd;
+
+ while (i != j) {
+ Track* const pTrack = *i++;
+
+ if (pTrack == NULL)
+ continue;
+
+ if (tn == pTrack->GetNumber())
+ return pTrack;
+ }
+
+ return NULL; // not found
+}
+
+const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
+ const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return m_trackEntries[idx];
+}
+
+long Cluster::Load(long long& pos, long& len) const {
+ if (m_pSegment == NULL)
+ return E_PARSE_FAILED;
+
+ if (m_timecode >= 0) // at least partially loaded
+ return 0;
+
+ if (m_pos != m_element_start || m_element_size >= 0)
+ return E_PARSE_FAILED;
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+ long long total, avail;
+ const int status = pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return status;
+
+ if (total >= 0 && (avail > total || m_pos > total))
+ return E_FILE_FORMAT_INVALID;
+
+ pos = m_pos;
+
+ long long cluster_size = -1;
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error or underflow
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id_ = ReadID(pReader, pos, len);
+
+ if (id_ < 0) // error
+ return static_cast<long>(id_);
+
+ if (id_ != libwebm::kMkvCluster)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume id
+
+ // read cluster size
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) // error
+ return static_cast<long>(cluster_size);
+
+ if (size == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume length of size of element
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size != unknown_size)
+ cluster_size = size;
+
+ // pos points to start of payload
+ long long timecode = -1;
+ long long new_pos = -1;
+ bool bBlock = false;
+
+ long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
+
+ for (;;) {
+ if ((cluster_stop >= 0) && (pos >= cluster_stop))
+ break;
+
+ // Parse ID
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadID(pReader, pos, len);
+
+ if (id < 0) // error
+ return static_cast<long>(id);
+
+ if (id == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ // 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 == libwebm::kMkvCluster)
+ break;
+
+ if (id == libwebm::kMkvCues)
+ break;
+
+ pos += len; // consume ID field
+
+ // Parse Size
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) // error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume size field
+
+ if ((cluster_stop >= 0) && (pos > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ // pos now points to start of payload
+
+ if (size == 0)
+ continue;
+
+ if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == libwebm::kMkvTimecode) {
+ len = static_cast<long>(size);
+
+ if ((pos + size) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ timecode = UnserializeUInt(pReader, pos, size);
+
+ if (timecode < 0) // error (or underflow)
+ return static_cast<long>(timecode);
+
+ new_pos = pos + size;
+
+ if (bBlock)
+ break;
+ } else if (id == libwebm::kMkvBlockGroup) {
+ bBlock = true;
+ break;
+ } else if (id == libwebm::kMkvSimpleBlock) {
+ bBlock = true;
+ break;
+ }
+
+ pos += size; // consume payload
+ if (cluster_stop >= 0 && pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (cluster_stop >= 0 && pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (timecode < 0) // no timecode found
+ return E_FILE_FORMAT_INVALID;
+
+ if (!bBlock)
+ return E_FILE_FORMAT_INVALID;
+
+ m_pos = new_pos; // designates position just beyond timecode payload
+ m_timecode = timecode; // m_timecode >= 0 means we're partially loaded
+
+ if (cluster_size >= 0)
+ m_element_size = cluster_stop - m_element_start;
+
+ return 0;
+}
+
+long Cluster::Parse(long long& pos, long& len) const {
+ long status = Load(pos, len);
+
+ if (status < 0)
+ return status;
+
+ if (m_pos < m_element_start || m_timecode < 0)
+ return E_PARSE_FAILED;
+
+ const long long cluster_stop =
+ (m_element_size < 0) ? -1 : m_element_start + m_element_size;
+
+ if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
+ return 1; // nothing else to do
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long total, avail;
+
+ status = pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return status;
+
+ if (total >= 0 && avail > total)
+ return E_FILE_FORMAT_INVALID;
+
+ pos = m_pos;
+
+ for (;;) {
+ if ((cluster_stop >= 0) && (pos >= cluster_stop))
+ break;
+
+ if ((total >= 0) && (pos >= total)) {
+ if (m_element_size < 0)
+ m_element_size = pos - m_element_start;
+
+ break;
+ }
+
+ // Parse ID
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadID(pReader, pos, len);
+
+ if (id < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ // 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 == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) {
+ if (m_element_size < 0)
+ m_element_size = pos - m_element_start;
+
+ break;
+ }
+
+ pos += len; // consume ID field
+
+ // Parse Size
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) // error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume size field
+
+ if ((cluster_stop >= 0) && (pos > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ // pos now points to start of payload
+
+ if (size == 0)
+ continue;
+
+ // const long long block_start = pos;
+ const long long block_stop = pos + size;
+
+ if (cluster_stop >= 0) {
+ if (block_stop > cluster_stop) {
+ if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) {
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ pos = cluster_stop;
+ break;
+ }
+ } else if ((total >= 0) && (block_stop > total)) {
+ m_element_size = total - m_element_start;
+ pos = total;
+ break;
+ } else if (block_stop > avail) {
+ len = static_cast<long>(size);
+ return E_BUFFER_NOT_FULL;
+ }
+
+ Cluster* const this_ = const_cast<Cluster*>(this);
+
+ if (id == libwebm::kMkvBlockGroup)
+ return this_->ParseBlockGroup(size, pos, len);
+
+ if (id == libwebm::kMkvSimpleBlock)
+ return this_->ParseSimpleBlock(size, pos, len);
+
+ pos += size; // consume payload
+ if (cluster_stop >= 0 && pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (m_element_size < 1)
+ return E_FILE_FORMAT_INVALID;
+
+ m_pos = pos;
+ if (cluster_stop >= 0 && m_pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (m_entries_count > 0) {
+ const long idx = m_entries_count - 1;
+
+ const BlockEntry* const pLast = m_entries[idx];
+ if (pLast == NULL)
+ return E_PARSE_FAILED;
+
+ const Block* const pBlock = pLast->GetBlock();
+ if (pBlock == NULL)
+ return E_PARSE_FAILED;
+
+ const long long start = pBlock->m_start;
+
+ if ((total >= 0) && (start > total))
+ return E_PARSE_FAILED; // defend against trucated stream
+
+ const long long size = pBlock->m_size;
+
+ const long long stop = start + size;
+ if (cluster_stop >= 0 && stop > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((total >= 0) && (stop > total))
+ return E_PARSE_FAILED; // defend against trucated stream
+ }
+
+ return 1; // no more entries
+}
+
+long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
+ long& len) {
+ const long long block_start = pos;
+ const long long block_stop = pos + block_size;
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long total, avail;
+
+ long status = pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ // parse track number
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0) // weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long track = ReadUInt(pReader, pos, len);
+
+ if (track < 0) // error
+ return static_cast<long>(track);
+
+ if (track == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume track number
+
+ if ((pos + 2) > block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + 2) > avail) {
+ len = 2;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ pos += 2; // consume timecode
+
+ if ((pos + 1) > block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ unsigned char flags;
+
+ status = pReader->Read(pos, 1, &flags);
+
+ if (status < 0) { // error or underflow
+ len = 1;
+ return status;
+ }
+
+ ++pos; // consume flags byte
+ assert(pos <= avail);
+
+ if (pos >= block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const int lacing = int(flags & 0x06) >> 1;
+
+ if ((lacing != 0) && (block_stop > avail)) {
+ len = static_cast<long>(block_stop - pos);
+ return E_BUFFER_NOT_FULL;
+ }
+
+ status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size,
+ 0); // DiscardPadding
+
+ if (status != 0)
+ return status;
+
+ m_pos = block_stop;
+
+ return 0; // success
+}
+
+long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
+ long& len) {
+ const long long payload_start = pos;
+ const long long payload_stop = pos + payload_size;
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long total, avail;
+
+ long status = pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ if ((total >= 0) && (payload_stop > total))
+ return E_FILE_FORMAT_INVALID;
+
+ if (payload_stop > avail) {
+ len = static_cast<long>(payload_size);
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long discard_padding = 0;
+
+ while (pos < payload_stop) {
+ // parse sub-block element ID
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0) // weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > payload_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadID(pReader, pos, len);
+
+ if (id < 0) // error
+ return static_cast<long>(id);
+
+ if (id == 0) // not a valid ID
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume ID field
+
+ // Parse Size
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0) // weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > payload_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) // error
+ return static_cast<long>(size);
+
+ pos += len; // consume size field
+
+ // pos now points to start of sub-block group payload
+
+ if (pos > payload_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (size == 0) // weird
+ continue;
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == libwebm::kMkvDiscardPadding) {
+ status = UnserializeInt(pReader, pos, size, discard_padding);
+
+ if (status < 0) // error
+ return status;
+ }
+
+ if (id != libwebm::kMkvBlock) {
+ pos += size; // consume sub-part of block group
+
+ if (pos > payload_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ continue;
+ }
+
+ const long long block_stop = pos + size;
+
+ if (block_stop > payload_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ // parse track number
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0) // weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long track = ReadUInt(pReader, pos, len);
+
+ if (track < 0) // error
+ return static_cast<long>(track);
+
+ if (track == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume track number
+
+ if ((pos + 2) > block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + 2) > avail) {
+ len = 2;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ pos += 2; // consume timecode
+
+ if ((pos + 1) > block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ unsigned char flags;
+
+ status = pReader->Read(pos, 1, &flags);
+
+ if (status < 0) { // error or underflow
+ len = 1;
+ return status;
+ }
+
+ ++pos; // consume flags byte
+ assert(pos <= avail);
+
+ if (pos >= block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const int lacing = int(flags & 0x06) >> 1;
+
+ if ((lacing != 0) && (block_stop > avail)) {
+ len = static_cast<long>(block_stop - pos);
+ return E_BUFFER_NOT_FULL;
+ }
+
+ pos = block_stop; // consume block-part of block group
+ if (pos > payload_stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ if (pos != payload_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size,
+ discard_padding);
+ if (status != 0)
+ return status;
+
+ m_pos = payload_stop;
+
+ return 0; // success
+}
+
+long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
+ assert(m_pos >= m_element_start);
+
+ pEntry = NULL;
+
+ if (index < 0)
+ return -1; // generic error
+
+ if (m_entries_count < 0)
+ return E_BUFFER_NOT_FULL;
+
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count <= m_entries_size);
+
+ if (index < m_entries_count) {
+ pEntry = m_entries[index];
+ assert(pEntry);
+
+ return 1; // found entry
+ }
+
+ if (m_element_size < 0) // we don't know cluster end yet
+ return E_BUFFER_NOT_FULL; // underflow
+
+ const long long element_stop = m_element_start + m_element_size;
+
+ if (m_pos >= element_stop)
+ return 0; // nothing left to parse
+
+ return E_BUFFER_NOT_FULL; // underflow, since more remains to be parsed
+}
+
+Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
+ if (!pSegment || off < 0)
+ return NULL;
+
+ const long long element_start = pSegment->m_start + off;
+
+ Cluster* const pCluster =
+ new (std::nothrow) Cluster(pSegment, idx, element_start);
+
+ return pCluster;
+}
+
+Cluster::Cluster()
+ : m_pSegment(NULL),
+ m_element_start(0),
+ m_index(0),
+ m_pos(0),
+ m_element_size(0),
+ m_timecode(0),
+ m_entries(NULL),
+ m_entries_size(0),
+ m_entries_count(0) // means "no entries"
+{}
+
+Cluster::Cluster(Segment* pSegment, long idx, long long element_start
+ /* long long element_size */)
+ : m_pSegment(pSegment),
+ m_element_start(element_start),
+ m_index(idx),
+ m_pos(element_start),
+ m_element_size(-1 /* element_size */),
+ m_timecode(-1),
+ m_entries(NULL),
+ m_entries_size(0),
+ m_entries_count(-1) // means "has not been parsed yet"
+{}
+
+Cluster::~Cluster() {
+ if (m_entries_count <= 0)
+ return;
+
+ BlockEntry** i = m_entries;
+ BlockEntry** const j = m_entries + m_entries_count;
+
+ while (i != j) {
+ BlockEntry* p = *i++;
+ assert(p);
+
+ delete p;
+ }
+
+ delete[] m_entries;
+}
+
+bool Cluster::EOS() const { return (m_pSegment == NULL); }
+
+long Cluster::GetIndex() const { return m_index; }
+
+long long Cluster::GetPosition() const {
+ const long long pos = m_element_start - m_pSegment->m_start;
+ assert(pos >= 0);
+
+ return pos;
+}
+
+long long Cluster::GetElementSize() const { return m_element_size; }
+
+long Cluster::HasBlockEntries(
+ const Segment* pSegment,
+ long long off, // relative to start of segment payload
+ long long& pos, long& len) {
+ assert(pSegment);
+ assert(off >= 0); // relative to segment
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ long long total, avail;
+
+ long status = pReader->Length(&total, &avail);
+
+ if (status < 0) // error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ pos = pSegment->m_start + off; // absolute
+
+ if ((total >= 0) && (pos >= total))
+ return 0; // we don't even have a complete cluster
+
+ const long long segment_stop =
+ (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
+
+ long long cluster_stop = -1; // interpreted later to mean "unknown size"
+
+ {
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0) // need more data
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((total >= 0) && ((pos + len) > total))
+ return 0;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadID(pReader, pos, len);
+
+ if (id < 0) // error
+ return static_cast<long>(id);
+
+ if (id != libwebm::kMkvCluster)
+ return E_PARSE_FAILED;
+
+ pos += len; // consume Cluster ID field
+
+ // read size field
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(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 ((total >= 0) && ((pos + len) > total))
+ return 0;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) // error
+ return static_cast<long>(size);
+
+ if (size == 0)
+ return 0; // cluster does not have entries
+
+ pos += len; // consume size field
+
+ // pos now points to start of payload
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size != unknown_size) {
+ cluster_stop = pos + size;
+ assert(cluster_stop >= 0);
+
+ if ((segment_stop >= 0) && (cluster_stop > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((total >= 0) && (cluster_stop > total))
+ // return E_FILE_FORMAT_INVALID; //too conservative
+ return 0; // cluster does not have any entries
+ }
+ }
+
+ for (;;) {
+ if ((cluster_stop >= 0) && (pos >= cluster_stop))
+ return 0; // no entries detected
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0) // need more data
+ return E_BUFFER_NOT_FULL;
+
+ if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadID(pReader, pos, len);
+
+ if (id < 0) // error
+ 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 == libwebm::kMkvCluster)
+ return 0; // no entries found
+
+ if (id == libwebm::kMkvCues)
+ return 0; // no entries found
+
+ pos += len; // consume id field
+
+ if ((cluster_stop >= 0) && (pos >= cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ // read size field
+
+ if ((pos + 1) > avail) {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) // error
+ return static_cast<long>(result);
+
+ if (result > 0) // underflow
+ return E_BUFFER_NOT_FULL;
+
+ if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) // error
+ return static_cast<long>(size);
+
+ pos += len; // consume size field
+
+ // pos now points to start of payload
+
+ if ((cluster_stop >= 0) && (pos > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if (size == 0) // weird
+ continue;
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID; // not supported inside cluster
+
+ if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == libwebm::kMkvBlockGroup)
+ return 1; // have at least one entry
+
+ if (id == libwebm::kMkvSimpleBlock)
+ return 1; // have at least one entry
+
+ pos += size; // consume payload
+ if (cluster_stop >= 0 && pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+}
+
+long long Cluster::GetTimeCode() const {
+ long long pos;
+ long len;
+
+ const long status = Load(pos, len);
+
+ if (status < 0) // error
+ return status;
+
+ return m_timecode;
+}
+
+long long Cluster::GetTime() const {
+ const long long tc = GetTimeCode();
+
+ if (tc < 0)
+ return tc;
+
+ const SegmentInfo* const pInfo = m_pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long t = m_timecode * scale;
+
+ return t;
+}
+
+long long Cluster::GetFirstTime() const {
+ const BlockEntry* pEntry;
+
+ const long status = GetFirst(pEntry);
+
+ if (status < 0) // error
+ return status;
+
+ if (pEntry == NULL) // empty cluster
+ return GetTime();
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ return pBlock->GetTime(this);
+}
+
+long long Cluster::GetLastTime() const {
+ const BlockEntry* pEntry;
+
+ const long status = GetLast(pEntry);
+
+ if (status < 0) // error
+ return status;
+
+ if (pEntry == NULL) // empty cluster
+ return GetTime();
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ return pBlock->GetTime(this);
+}
+
+long Cluster::CreateBlock(long long id,
+ long long pos, // absolute pos of payload
+ long long size, long long discard_padding) {
+ if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock)
+ return E_PARSE_FAILED;
+
+ if (m_entries_count < 0) { // haven't parsed anything yet
+ assert(m_entries == NULL);
+ assert(m_entries_size == 0);
+
+ m_entries_size = 1024;
+ m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
+ if (m_entries == NULL)
+ return -1;
+
+ m_entries_count = 0;
+ } else {
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count <= m_entries_size);
+
+ if (m_entries_count >= m_entries_size) {
+ const long entries_size = 2 * m_entries_size;
+
+ BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
+ if (entries == NULL)
+ return -1;
+
+ BlockEntry** src = m_entries;
+ BlockEntry** const src_end = src + m_entries_count;
+
+ BlockEntry** dst = entries;
+
+ while (src != src_end)
+ *dst++ = *src++;
+
+ delete[] m_entries;
+
+ m_entries = entries;
+ m_entries_size = entries_size;
+ }
+ }
+
+ if (id == libwebm::kMkvBlockGroup)
+ return CreateBlockGroup(pos, size, discard_padding);
+ else
+ return CreateSimpleBlock(pos, size);
+}
+
+long Cluster::CreateBlockGroup(long long start_offset, long long size,
+ long long discard_padding) {
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count >= 0);
+ assert(m_entries_count < m_entries_size);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = start_offset;
+ const long long stop = start_offset + size;
+
+ // For WebM files, there is a bias towards previous reference times
+ //(in order to support alt-ref frames, which refer back to the previous
+ // keyframe). Normally a 0 value is not possible, but here we tenatively
+ // allow 0 as the value of a reference frame, with the interpretation
+ // that this is a "previous" reference time.
+
+ long long prev = 1; // nonce
+ long long next = 0; // nonce
+ long long duration = -1; // really, this is unsigned
+
+ long long bpos = -1;
+ long long bsize = -1;
+
+ while (pos < stop) {
+ long len;
+ const long long id = ReadID(pReader, pos, len);
+ if (id < 0 || (pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ 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 == libwebm::kMkvBlock) {
+ if (bpos < 0) { // Block ID
+ bpos = pos;
+ bsize = size;
+ }
+ } else if (id == libwebm::kMkvBlockDuration) {
+ if (size > 8)
+ return E_FILE_FORMAT_INVALID;
+
+ duration = UnserializeUInt(pReader, pos, size);
+
+ if (duration < 0)
+ return E_FILE_FORMAT_INVALID;
+ } else if (id == libwebm::kMkvReferenceBlock) {
+ if (size > 8 || size <= 0)
+ return E_FILE_FORMAT_INVALID;
+ const long size_ = static_cast<long>(size);
+
+ long long time;
+
+ long status = UnserializeInt(pReader, pos, size_, time);
+ assert(status == 0);
+ if (status != 0)
+ return -1;
+
+ if (time <= 0) // see note above
+ prev = time;
+ else
+ next = time;
+ }
+
+ pos += size; // consume payload
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+ if (bpos < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ assert(bsize >= 0);
+
+ const long idx = m_entries_count;
+
+ BlockEntry** const ppEntry = m_entries + idx;
+ BlockEntry*& pEntry = *ppEntry;
+
+ pEntry = new (std::nothrow)
+ BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
+
+ if (pEntry == NULL)
+ return -1; // generic error
+
+ BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
+
+ const long status = p->Parse();
+
+ if (status == 0) { // success
+ ++m_entries_count;
+ return 0;
+ }
+
+ delete pEntry;
+ pEntry = 0;
+
+ return status;
+}
+
+long Cluster::CreateSimpleBlock(long long st, long long sz) {
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count >= 0);
+ assert(m_entries_count < m_entries_size);
+
+ const long idx = m_entries_count;
+
+ BlockEntry** const ppEntry = m_entries + idx;
+ BlockEntry*& pEntry = *ppEntry;
+
+ pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
+
+ if (pEntry == NULL)
+ return -1; // generic error
+
+ SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
+
+ const long status = p->Parse();
+
+ if (status == 0) {
+ ++m_entries_count;
+ return 0;
+ }
+
+ delete pEntry;
+ pEntry = 0;
+
+ return status;
+}
+
+long Cluster::GetFirst(const BlockEntry*& pFirst) const {
+ if (m_entries_count <= 0) {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) { // error
+ pFirst = NULL;
+ return status;
+ }
+
+ if (m_entries_count <= 0) { // empty cluster
+ pFirst = NULL;
+ return 0;
+ }
+ }
+
+ assert(m_entries);
+
+ pFirst = m_entries[0];
+ assert(pFirst);
+
+ return 0; // success
+}
+
+long Cluster::GetLast(const BlockEntry*& pLast) const {
+ for (;;) {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) { // error
+ pLast = NULL;
+ return status;
+ }
+
+ if (status > 0) // no new block
+ break;
+ }
+
+ if (m_entries_count <= 0) {
+ pLast = NULL;
+ return 0;
+ }
+
+ assert(m_entries);
+
+ const long idx = m_entries_count - 1;
+
+ pLast = m_entries[idx];
+ assert(pLast);
+
+ return 0;
+}
+
+long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
+ assert(pCurr);
+ assert(m_entries);
+ assert(m_entries_count > 0);
+
+ size_t idx = pCurr->GetIndex();
+ assert(idx < size_t(m_entries_count));
+ assert(m_entries[idx] == pCurr);
+
+ ++idx;
+
+ if (idx >= size_t(m_entries_count)) {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) { // error
+ pNext = NULL;
+ return status;
+ }
+
+ if (status > 0) {
+ pNext = NULL;
+ return 0;
+ }
+
+ assert(m_entries);
+ assert(m_entries_count > 0);
+ assert(idx < size_t(m_entries_count));
+ }
+
+ pNext = m_entries[idx];
+ assert(pNext);
+
+ return 0;
+}
+
+long Cluster::GetEntryCount() const { return m_entries_count; }
+
+const BlockEntry* Cluster::GetEntry(const Track* pTrack,
+ long long time_ns) const {
+ assert(pTrack);
+
+ if (m_pSegment == NULL) // this is the special EOS cluster
+ return pTrack->GetEOS();
+
+ const BlockEntry* pResult = pTrack->GetEOS();
+
+ long index = 0;
+
+ for (;;) {
+ if (index >= m_entries_count) {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+ assert(status >= 0);
+
+ if (status > 0) // completely parsed, and no more entries
+ return pResult;
+
+ if (status < 0) // should never happen
+ return 0;
+
+ assert(m_entries);
+ assert(index < m_entries_count);
+ }
+
+ const BlockEntry* const pEntry = m_entries[index];
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
+ ++index;
+ 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)
+ return pResult;
+
+ pResult = pEntry; // have a candidate
+ } else if (time_ns >= 0) {
+ const long long ns = pBlock->GetTime(this);
+
+ if (ns > time_ns)
+ return pResult;
+ }
+
+ ++index;
+ }
+}
+
+const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
+ const CuePoint::TrackPosition& tp) const {
+ assert(m_pSegment);
+ const long long tc = cp.GetTimeCode();
+
+ if (tp.m_block > 0) {
+ const long block = static_cast<long>(tp.m_block);
+ const long index = block - 1;
+
+ while (index >= m_entries_count) {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) // TODO: can this happen?
+ return NULL;
+
+ if (status > 0) // nothing remains to be parsed
+ return NULL;
+ }
+
+ 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;
+ }
+ }
+
+ long index = 0;
+
+ for (;;) {
+ if (index >= m_entries_count) {
+ long long pos;
+ long len;
+
+ const long status = Parse(pos, len);
+
+ if (status < 0) // TODO: can this happen?
+ return NULL;
+
+ if (status > 0) // nothing remains to be parsed
+ return NULL;
+
+ assert(m_entries);
+ assert(index < m_entries_count);
+ }
+
+ 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) {
+ ++index;
+ continue;
+ }
+
+ const long long tc_ = pBlock->GetTimeCode(this);
+
+ if (tc_ < tc) {
+ ++index;
+ 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;
+ }
+}
+
+BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
+BlockEntry::~BlockEntry() {}
+const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
+long BlockEntry::GetIndex() const { return m_index; }
+
+SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
+ long long size)
+ : 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,
+ long long block_size, long long prev, long long next,
+ long long duration, long long discard_padding)
+ : BlockEntry(pCluster, idx),
+ m_block(block_start, block_size, discard_padding),
+ m_prev(prev),
+ m_next(next),
+ m_duration(duration) {}
+
+long BlockGroup::Parse() {
+ const long status = m_block.Parse(m_pCluster);
+
+ if (status)
+ return status;
+
+ m_block.SetKey((m_prev > 0) && (m_next <= 0));
+
+ return 0;
+}
+
+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)
+ : m_start(start),
+ m_size(size_),
+ m_track(0),
+ m_timecode(-1),
+ m_flags(0),
+ m_frames(NULL),
+ m_frame_count(-1),
+ m_discard_padding(discard_padding) {}
+
+Block::~Block() { delete[] m_frames; }
+
+long Block::Parse(const Cluster* pCluster) {
+ if (pCluster == NULL)
+ return -1;
+
+ if (pCluster->m_pSegment == NULL)
+ return -1;
+
+ assert(m_start >= 0);
+ assert(m_size >= 0);
+ assert(m_track <= 0);
+ assert(m_frames == NULL);
+ assert(m_frame_count <= 0);
+
+ long long pos = m_start;
+ const long long stop = m_start + m_size;
+
+ long len;
+
+ IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
+
+ m_track = ReadUInt(pReader, pos, len);
+
+ if (m_track <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume track number
+
+ if ((stop - pos) < 2)
+ return E_FILE_FORMAT_INVALID;
+
+ long status;
+ long long value;
+
+ status = UnserializeInt(pReader, pos, 2, value);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
+
+ if (value < SHRT_MIN)
+ return E_FILE_FORMAT_INVALID;
+
+ if (value > SHRT_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ m_timecode = static_cast<short>(value);
+
+ pos += 2;
+
+ if ((stop - pos) <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ status = pReader->Read(pos, 1, &m_flags);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
+
+ const int lacing = int(m_flags & 0x06) >> 1;
+
+ ++pos; // consume flags byte
+
+ if (lacing == 0) { // no lacing
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ m_frame_count = 1;
+ m_frames = new (std::nothrow) Frame[m_frame_count];
+ if (m_frames == NULL)
+ return -1;
+
+ Frame& f = m_frames[0];
+ f.pos = pos;
+
+ const long long frame_size = stop - pos;
+
+ if (frame_size > LONG_MAX || frame_size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ f.len = static_cast<long>(frame_size);
+
+ return 0; // success
+ }
+
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char biased_count;
+
+ status = pReader->Read(pos, 1, &biased_count);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
+
+ ++pos; // consume frame count
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ m_frame_count = int(biased_count) + 1;
+
+ m_frames = new (std::nothrow) Frame[m_frame_count];
+ if (m_frames == NULL)
+ return -1;
+
+ if (!m_frames)
+ return E_FILE_FORMAT_INVALID;
+
+ if (lacing == 1) { // Xiph
+ Frame* pf = m_frames;
+ Frame* const pf_end = pf + m_frame_count;
+
+ long long size = 0;
+ int frame_count = m_frame_count;
+
+ while (frame_count > 1) {
+ long frame_size = 0;
+
+ for (;;) {
+ unsigned char val;
+
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ status = pReader->Read(pos, 1, &val);
+
+ if (status)
+ return E_FILE_FORMAT_INVALID;
+
+ ++pos; // consume xiph size byte
+
+ frame_size += val;
+
+ if (val < 255)
+ break;
+ }
+
+ Frame& f = *pf++;
+ assert(pf < pf_end);
+ if (pf >= pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ 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
+
+ --frame_count;
+ }
+
+ if (pf >= pf_end || pos > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ {
+ Frame& f = *pf++;
+
+ if (pf != pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ f.pos = 0; // patch later
+
+ const long long total_size = stop - pos;
+
+ if (total_size < size)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long frame_size = total_size - size;
+
+ if (frame_size > LONG_MAX || frame_size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ f.len = static_cast<long>(frame_size);
+ }
+
+ pf = m_frames;
+ while (pf != pf_end) {
+ Frame& f = *pf++;
+ assert((pos + f.len) <= stop);
+
+ if ((pos + f.len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ f.pos = pos;
+ pos += f.len;
+ }
+
+ assert(pos == stop);
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ } 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)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long frame_size = total_size / m_frame_count;
+
+ if (frame_size > LONG_MAX || frame_size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ Frame* pf = m_frames;
+ Frame* const pf_end = pf + m_frame_count;
+
+ while (pf != pf_end) {
+ assert((pos + frame_size) <= stop);
+ if ((pos + frame_size) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ Frame& f = *pf++;
+
+ f.pos = pos;
+ f.len = static_cast<long>(frame_size);
+
+ pos += frame_size;
+ }
+
+ assert(pos == stop);
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+
+ } else {
+ assert(lacing == 3); // EBML lacing
+
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ long long size = 0;
+ int frame_count = m_frame_count;
+
+ long long frame_size = ReadUInt(pReader, pos, len);
+
+ if (frame_size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume length of size of first frame
+
+ if ((pos + frame_size) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ Frame* pf = m_frames;
+ Frame* const pf_end = pf + m_frame_count;
+
+ {
+ Frame& curr = *pf;
+
+ curr.pos = 0; // patch later
+
+ curr.len = static_cast<long>(frame_size);
+ size += curr.len; // contribution of this frame
+ }
+
+ --frame_count;
+
+ while (frame_count > 1) {
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ assert(pf < pf_end);
+ if (pf >= pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ const Frame& prev = *pf++;
+ assert(prev.len == frame_size);
+ if (prev.len != frame_size)
+ return E_FILE_FORMAT_INVALID;
+
+ assert(pf < pf_end);
+ if (pf >= pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ Frame& curr = *pf;
+
+ curr.pos = 0; // patch later
+
+ const long long delta_size_ = ReadUInt(pReader, pos, len);
+
+ if (delta_size_ < 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; // consume length of (delta) size
+ if (pos > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const int exp = 7 * len - 1;
+ const long long bias = (1LL << exp) - 1LL;
+ const long long delta_size = delta_size_ - bias;
+
+ frame_size += delta_size;
+
+ if (frame_size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ if (frame_size > LONG_MAX)
+ return E_FILE_FORMAT_INVALID;
+
+ curr.len = static_cast<long>(frame_size);
+ size += curr.len; // contribution of this frame
+
+ --frame_count;
+ }
+
+ // parse last frame
+ if (frame_count > 0) {
+ if (pos > stop || pf >= pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ const Frame& prev = *pf++;
+ assert(prev.len == frame_size);
+ if (prev.len != frame_size)
+ return E_FILE_FORMAT_INVALID;
+
+ if (pf >= pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ Frame& curr = *pf++;
+ if (pf != pf_end)
+ return E_FILE_FORMAT_INVALID;
+
+ curr.pos = 0; // patch later
+
+ const long long total_size = stop - pos;
+
+ if (total_size < size)
+ return E_FILE_FORMAT_INVALID;
+
+ frame_size = total_size - size;
+
+ if (frame_size > LONG_MAX || frame_size <= 0)
+ return E_FILE_FORMAT_INVALID;
+
+ curr.len = static_cast<long>(frame_size);
+ }
+
+ pf = m_frames;
+ while (pf != pf_end) {
+ Frame& f = *pf++;
+ assert((pos + f.len) <= stop);
+ if ((pos + f.len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ f.pos = pos;
+ pos += f.len;
+ }
+
+ if (pos != stop)
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ return 0; // success
+}
+
+long long Block::GetTimeCode(const Cluster* pCluster) const {
+ if (pCluster == 0)
+ return m_timecode;
+
+ const long long tc0 = pCluster->GetTimeCode();
+ assert(tc0 >= 0);
+
+ const long long tc = tc0 + m_timecode;
+
+ return tc; // unscaled timecode units
+}
+
+long long Block::GetTime(const Cluster* pCluster) const {
+ assert(pCluster);
+
+ const long long tc = GetTimeCode(pCluster);
+
+ const Segment* const pSegment = pCluster->m_pSegment;
+ const SegmentInfo* const pInfo = pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long ns = tc * scale;
+
+ return ns;
+}
+
+long long Block::GetTrackNumber() const { return m_track; }
+
+bool Block::IsKey() const {
+ return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
+}
+
+void Block::SetKey(bool bKey) {
+ if (bKey)
+ m_flags |= static_cast<unsigned char>(1 << 7);
+ else
+ m_flags &= 0x7F;
+}
+
+bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
+
+Block::Lacing Block::GetLacing() const {
+ const int value = int(m_flags & 0x06) >> 1;
+ return static_cast<Lacing>(value);
+}
+
+int Block::GetFrameCount() const { return m_frame_count; }
+
+const Block::Frame& Block::GetFrame(int idx) const {
+ assert(idx >= 0);
+ assert(idx < m_frame_count);
+
+ const Frame& f = m_frames[idx];
+ assert(f.pos > 0);
+ assert(f.len > 0);
+
+ return f;
+}
+
+long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
+ assert(pReader);
+ assert(buf);
+
+ const long status = pReader->Read(pos, len, buf);
+ return status;
+}
+
+long long Block::GetDiscardPadding() const { return m_discard_padding; }
+
+} // namespace mkvparser
--- /dev/null
+++ b/third_party/libwebm/mkvparser/mkvparser.h
@@ -1,0 +1,1112 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#ifndef MKVPARSER_MKVPARSER_H_
+#define MKVPARSER_MKVPARSER_H_
+
+#include <cstddef>
+
+namespace mkvparser {
+
+const int E_PARSE_FAILED = -1;
+const int E_FILE_FORMAT_INVALID = -2;
+const int E_BUFFER_NOT_FULL = -3;
+
+class IMkvReader {
+ public:
+ virtual int Read(long long pos, long len, unsigned char* buf) = 0;
+ virtual int Length(long long* total, long long* available) = 0;
+
+ protected:
+ virtual ~IMkvReader();
+};
+
+template <typename Type>
+Type* SafeArrayAlloc(unsigned long long num_elements,
+ unsigned long long element_size);
+long long GetUIntLength(IMkvReader*, long long, long&);
+long long ReadUInt(IMkvReader*, long long, long&);
+long long ReadID(IMkvReader* pReader, long long pos, long& len);
+long long UnserializeUInt(IMkvReader*, long long pos, long long size);
+
+long UnserializeFloat(IMkvReader*, long long pos, long long size, double&);
+long UnserializeInt(IMkvReader*, long long pos, long long size,
+ long long& result);
+
+long UnserializeString(IMkvReader*, long long pos, long long size, char*& str);
+
+long ParseElementHeader(IMkvReader* pReader,
+ long long& pos, // consume id and size fields
+ long long stop, // if you know size of element's parent
+ long long& id, long long& size);
+
+bool Match(IMkvReader*, long long&, unsigned long, long long&);
+bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&);
+
+void GetVersion(int& major, int& minor, int& build, int& revision);
+
+struct EBMLHeader {
+ EBMLHeader();
+ ~EBMLHeader();
+ long long m_version;
+ long long m_readVersion;
+ long long m_maxIdLength;
+ long long m_maxSizeLength;
+ char* m_docType;
+ long long m_docTypeVersion;
+ long long m_docTypeReadVersion;
+
+ long long Parse(IMkvReader*, long long&);
+ void Init();
+};
+
+class Segment;
+class Track;
+class Cluster;
+
+class Block {
+ Block(const Block&);
+ Block& operator=(const Block&);
+
+ public:
+ const long long m_start;
+ const long long m_size;
+
+ Block(long long start, long long size, long long discard_padding);
+ ~Block();
+
+ long Parse(const Cluster*);
+
+ long long GetTrackNumber() const;
+ long long GetTimeCode(const Cluster*) const; // absolute, but not scaled
+ long long GetTime(const Cluster*) const; // absolute, and scaled (ns)
+ bool IsKey() const;
+ void SetKey(bool);
+ bool IsInvisible() const;
+
+ enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml };
+ Lacing GetLacing() const;
+
+ int GetFrameCount() const; // to index frames: [0, count)
+
+ struct Frame {
+ long long pos; // absolute offset
+ long len;
+
+ long Read(IMkvReader*, unsigned char*) const;
+ };
+
+ const Frame& GetFrame(int frame_index) const;
+
+ long long GetDiscardPadding() const;
+
+ private:
+ long long m_track; // Track::Number()
+ short m_timecode; // relative to cluster
+ unsigned char m_flags;
+
+ Frame* m_frames;
+ int m_frame_count;
+
+ protected:
+ const long long m_discard_padding;
+};
+
+class BlockEntry {
+ BlockEntry(const BlockEntry&);
+ BlockEntry& operator=(const BlockEntry&);
+
+ protected:
+ BlockEntry(Cluster*, long index);
+
+ public:
+ virtual ~BlockEntry();
+
+ bool EOS() const { return (GetKind() == kBlockEOS); }
+ const Cluster* GetCluster() const;
+ long GetIndex() const;
+ virtual const Block* GetBlock() const = 0;
+
+ enum Kind { kBlockEOS, kBlockSimple, kBlockGroup };
+ virtual Kind GetKind() const = 0;
+
+ protected:
+ Cluster* const m_pCluster;
+ const long m_index;
+};
+
+class SimpleBlock : public BlockEntry {
+ SimpleBlock(const SimpleBlock&);
+ SimpleBlock& operator=(const SimpleBlock&);
+
+ public:
+ SimpleBlock(Cluster*, long index, long long start, long long size);
+ long Parse();
+
+ Kind GetKind() const;
+ const Block* GetBlock() const;
+
+ protected:
+ Block m_block;
+};
+
+class BlockGroup : public BlockEntry {
+ BlockGroup(const BlockGroup&);
+ BlockGroup& operator=(const BlockGroup&);
+
+ public:
+ BlockGroup(Cluster*, long index,
+ long long block_start, // absolute pos of block's payload
+ long long block_size, // size of block's payload
+ long long prev, long long next, long long duration,
+ long long discard_padding);
+
+ long Parse();
+
+ Kind GetKind() const;
+ const Block* GetBlock() const;
+
+ long long GetPrevTimeCode() const; // relative to block's time
+ long long GetNextTimeCode() const; // as above
+ long long GetDurationTimeCode() const;
+
+ private:
+ Block m_block;
+ const long long m_prev;
+ const long long m_next;
+ const long long m_duration;
+};
+
+///////////////////////////////////////////////////////////////
+// ContentEncoding element
+// Elements used to describe if the track data has been encrypted or
+// compressed with zlib or header stripping.
+class ContentEncoding {
+ public:
+ enum { kCTR = 1 };
+
+ ContentEncoding();
+ ~ContentEncoding();
+
+ // ContentCompression element names
+ struct ContentCompression {
+ ContentCompression();
+ ~ContentCompression();
+
+ unsigned long long algo;
+ unsigned char* settings;
+ long long settings_len;
+ };
+
+ // ContentEncAESSettings element names
+ struct ContentEncAESSettings {
+ ContentEncAESSettings() : cipher_mode(kCTR) {}
+ ~ContentEncAESSettings() {}
+
+ unsigned long long cipher_mode;
+ };
+
+ // ContentEncryption element names
+ struct ContentEncryption {
+ ContentEncryption();
+ ~ContentEncryption();
+
+ unsigned long long algo;
+ unsigned char* key_id;
+ long long key_id_len;
+ unsigned char* signature;
+ long long signature_len;
+ unsigned char* sig_key_id;
+ long long sig_key_id_len;
+ unsigned long long sig_algo;
+ unsigned long long sig_hash_algo;
+
+ ContentEncAESSettings aes_settings;
+ };
+
+ // Returns ContentCompression represented by |idx|. Returns NULL if |idx|
+ // is out of bounds.
+ const ContentCompression* GetCompressionByIndex(unsigned long idx) const;
+
+ // Returns number of ContentCompression elements in this ContentEncoding
+ // element.
+ unsigned long GetCompressionCount() const;
+
+ // Parses the ContentCompression element from |pReader|. |start| is the
+ // starting offset of the ContentCompression payload. |size| is the size in
+ // bytes of the ContentCompression payload. |compression| is where the parsed
+ // values will be stored.
+ long ParseCompressionEntry(long long start, long long size,
+ IMkvReader* pReader,
+ ContentCompression* compression);
+
+ // Returns ContentEncryption represented by |idx|. Returns NULL if |idx|
+ // is out of bounds.
+ const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const;
+
+ // Returns number of ContentEncryption elements in this ContentEncoding
+ // element.
+ unsigned long GetEncryptionCount() const;
+
+ // Parses the ContentEncAESSettings element from |pReader|. |start| is the
+ // starting offset of the ContentEncAESSettings payload. |size| is the
+ // size in bytes of the ContentEncAESSettings payload. |encryption| is
+ // where the parsed values will be stored.
+ long ParseContentEncAESSettingsEntry(long long start, long long size,
+ IMkvReader* pReader,
+ ContentEncAESSettings* aes);
+
+ // Parses the ContentEncoding element from |pReader|. |start| is the
+ // starting offset of the ContentEncoding payload. |size| is the size in
+ // bytes of the ContentEncoding payload. Returns true on success.
+ long ParseContentEncodingEntry(long long start, long long size,
+ IMkvReader* pReader);
+
+ // Parses the ContentEncryption element from |pReader|. |start| is the
+ // starting offset of the ContentEncryption payload. |size| is the size in
+ // bytes of the ContentEncryption payload. |encryption| is where the parsed
+ // values will be stored.
+ long ParseEncryptionEntry(long long start, long long size,
+ IMkvReader* pReader, ContentEncryption* encryption);
+
+ unsigned long long encoding_order() const { return encoding_order_; }
+ unsigned long long encoding_scope() const { return encoding_scope_; }
+ unsigned long long encoding_type() const { return encoding_type_; }
+
+ private:
+ // Member variables for list of ContentCompression elements.
+ ContentCompression** compression_entries_;
+ ContentCompression** compression_entries_end_;
+
+ // Member variables for list of ContentEncryption elements.
+ ContentEncryption** encryption_entries_;
+ ContentEncryption** encryption_entries_end_;
+
+ // ContentEncoding element names
+ unsigned long long encoding_order_;
+ unsigned long long encoding_scope_;
+ unsigned long long encoding_type_;
+
+ // LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
+ ContentEncoding(const ContentEncoding&);
+ ContentEncoding& operator=(const ContentEncoding&);
+};
+
+class Track {
+ Track(const Track&);
+ Track& operator=(const Track&);
+
+ public:
+ class Info;
+ static long Create(Segment*, const Info&, long long element_start,
+ long long element_size, Track*&);
+
+ enum Type { kVideo = 1, kAudio = 2, kSubtitle = 0x11, kMetadata = 0x21 };
+
+ Segment* const m_pSegment;
+ const long long m_element_start;
+ const long long m_element_size;
+ virtual ~Track();
+
+ long GetType() const;
+ long GetNumber() const;
+ unsigned long long GetUid() const;
+ const char* GetNameAsUTF8() const;
+ const char* GetLanguage() const;
+ const char* GetCodecNameAsUTF8() const;
+ const char* GetCodecId() const;
+ const unsigned char* GetCodecPrivate(size_t&) const;
+ bool GetLacing() const;
+ unsigned long long GetDefaultDuration() const;
+ unsigned long long GetCodecDelay() const;
+ unsigned long long GetSeekPreRoll() const;
+
+ const BlockEntry* GetEOS() const;
+
+ struct Settings {
+ long long start;
+ long long size;
+ };
+
+ class Info {
+ public:
+ Info();
+ ~Info();
+ int Copy(Info&) const;
+ void Clear();
+ long type;
+ long number;
+ unsigned long long uid;
+ unsigned long long defaultDuration;
+ unsigned long long codecDelay;
+ unsigned long long seekPreRoll;
+ char* nameAsUTF8;
+ char* language;
+ char* codecId;
+ char* codecNameAsUTF8;
+ unsigned char* codecPrivate;
+ size_t codecPrivateSize;
+ bool lacing;
+ Settings settings;
+
+ private:
+ Info(const Info&);
+ Info& operator=(const Info&);
+ int CopyStr(char* Info::*str, Info&) const;
+ };
+
+ long GetFirst(const BlockEntry*&) const;
+ long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
+ virtual bool VetEntry(const BlockEntry*) const;
+ virtual long Seek(long long time_ns, const BlockEntry*&) const;
+
+ const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const;
+ unsigned long GetContentEncodingCount() const;
+
+ long ParseContentEncodingsEntry(long long start, long long size);
+
+ protected:
+ Track(Segment*, long long element_start, long long element_size);
+
+ Info m_info;
+
+ class EOSBlock : public BlockEntry {
+ public:
+ EOSBlock();
+
+ Kind GetKind() const;
+ const Block* GetBlock() const;
+ };
+
+ EOSBlock m_eos;
+
+ private:
+ ContentEncoding** content_encoding_entries_;
+ ContentEncoding** content_encoding_entries_end_;
+};
+
+struct PrimaryChromaticity {
+ PrimaryChromaticity() : x(0), y(0) {}
+ ~PrimaryChromaticity() {}
+ static bool Parse(IMkvReader* reader, long long read_pos,
+ long long value_size, bool is_x,
+ PrimaryChromaticity** chromaticity);
+ float x;
+ float y;
+};
+
+struct MasteringMetadata {
+ static const float kValueNotPresent;
+
+ MasteringMetadata()
+ : r(NULL),
+ g(NULL),
+ b(NULL),
+ white_point(NULL),
+ luminance_max(kValueNotPresent),
+ luminance_min(kValueNotPresent) {}
+ ~MasteringMetadata() {
+ delete r;
+ delete g;
+ delete b;
+ delete white_point;
+ }
+
+ static bool Parse(IMkvReader* reader, long long element_start,
+ long long element_size,
+ MasteringMetadata** mastering_metadata);
+
+ PrimaryChromaticity* r;
+ PrimaryChromaticity* g;
+ PrimaryChromaticity* b;
+ PrimaryChromaticity* white_point;
+ float luminance_max;
+ float luminance_min;
+};
+
+struct Colour {
+ static const long long kValueNotPresent;
+
+ // Unless otherwise noted all values assigned upon construction are the
+ // equivalent of unspecified/default.
+ Colour()
+ : matrix_coefficients(kValueNotPresent),
+ bits_per_channel(kValueNotPresent),
+ chroma_subsampling_horz(kValueNotPresent),
+ chroma_subsampling_vert(kValueNotPresent),
+ cb_subsampling_horz(kValueNotPresent),
+ cb_subsampling_vert(kValueNotPresent),
+ chroma_siting_horz(kValueNotPresent),
+ chroma_siting_vert(kValueNotPresent),
+ range(kValueNotPresent),
+ transfer_characteristics(kValueNotPresent),
+ primaries(kValueNotPresent),
+ max_cll(kValueNotPresent),
+ max_fall(kValueNotPresent),
+ mastering_metadata(NULL) {}
+ ~Colour() {
+ delete mastering_metadata;
+ mastering_metadata = NULL;
+ }
+
+ static bool Parse(IMkvReader* reader, long long element_start,
+ long long element_size, Colour** colour);
+
+ long long matrix_coefficients;
+ long long bits_per_channel;
+ long long chroma_subsampling_horz;
+ long long chroma_subsampling_vert;
+ long long cb_subsampling_horz;
+ long long cb_subsampling_vert;
+ long long chroma_siting_horz;
+ long long chroma_siting_vert;
+ long long range;
+ long long transfer_characteristics;
+ long long primaries;
+ long long max_cll;
+ long long max_fall;
+
+ MasteringMetadata* mastering_metadata;
+};
+
+class VideoTrack : public Track {
+ VideoTrack(const VideoTrack&);
+ VideoTrack& operator=(const VideoTrack&);
+
+ VideoTrack(Segment*, long long element_start, long long element_size);
+
+ public:
+ virtual ~VideoTrack();
+ static long Parse(Segment*, const Info&, long long element_start,
+ long long element_size, VideoTrack*&);
+
+ 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;
+ long Seek(long long time_ns, const BlockEntry*&) const;
+
+ Colour* GetColour() const;
+
+ 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;
+
+ Colour* m_colour;
+};
+
+class AudioTrack : public Track {
+ AudioTrack(const AudioTrack&);
+ AudioTrack& operator=(const AudioTrack&);
+
+ AudioTrack(Segment*, long long element_start, long long element_size);
+
+ public:
+ static long Parse(Segment*, const Info&, long long element_start,
+ long long element_size, AudioTrack*&);
+
+ double GetSamplingRate() const;
+ long long GetChannels() const;
+ long long GetBitDepth() const;
+
+ private:
+ double m_rate;
+ long long m_channels;
+ long long m_bitDepth;
+};
+
+class Tracks {
+ Tracks(const Tracks&);
+ Tracks& operator=(const Tracks&);
+
+ 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;
+
+ Tracks(Segment*, long long start, long long size, long long element_start,
+ long long element_size);
+
+ ~Tracks();
+
+ long Parse();
+
+ unsigned long GetTracksCount() const;
+
+ const Track* GetTrackByNumber(long tn) const;
+ const Track* GetTrackByIndex(unsigned long idx) const;
+
+ private:
+ Track** m_trackEntries;
+ Track** m_trackEntriesEnd;
+
+ long ParseTrackEntry(long long payload_start, long long payload_size,
+ long long element_start, long long element_size,
+ Track*&) const;
+};
+
+class Chapters {
+ Chapters(const Chapters&);
+ Chapters& operator=(const Chapters&);
+
+ 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;
+
+ Chapters(Segment*, long long payload_start, long long payload_size,
+ long long element_start, long long element_size);
+
+ ~Chapters();
+
+ long Parse();
+
+ class Atom;
+ class Edition;
+
+ class Display {
+ friend class Atom;
+ Display();
+ Display(const Display&);
+ ~Display();
+ Display& operator=(const Display&);
+
+ public:
+ const char* GetString() const;
+ const char* GetLanguage() const;
+ const char* GetCountry() const;
+
+ private:
+ void Init();
+ void ShallowCopy(Display&) const;
+ void Clear();
+ long Parse(IMkvReader*, long long pos, long long size);
+
+ char* m_string;
+ char* m_language;
+ char* m_country;
+ };
+
+ class Atom {
+ friend class Edition;
+ Atom();
+ Atom(const Atom&);
+ ~Atom();
+ Atom& operator=(const Atom&);
+
+ public:
+ unsigned long long GetUID() const;
+ const char* GetStringUID() const;
+
+ long long GetStartTimecode() const;
+ long long GetStopTimecode() const;
+
+ long long GetStartTime(const Chapters*) const;
+ long long GetStopTime(const Chapters*) const;
+
+ int GetDisplayCount() const;
+ const Display* GetDisplay(int index) const;
+
+ private:
+ void Init();
+ void ShallowCopy(Atom&) const;
+ void Clear();
+ long Parse(IMkvReader*, long long pos, long long size);
+ static long long GetTime(const Chapters*, long long timecode);
+
+ long ParseDisplay(IMkvReader*, long long pos, long long size);
+ bool ExpandDisplaysArray();
+
+ char* m_string_uid;
+ unsigned long long m_uid;
+ long long m_start_timecode;
+ long long m_stop_timecode;
+
+ Display* m_displays;
+ int m_displays_size;
+ int m_displays_count;
+ };
+
+ class Edition {
+ friend class Chapters;
+ Edition();
+ Edition(const Edition&);
+ ~Edition();
+ Edition& operator=(const Edition&);
+
+ public:
+ int GetAtomCount() const;
+ const Atom* GetAtom(int index) const;
+
+ private:
+ void Init();
+ void ShallowCopy(Edition&) const;
+ void Clear();
+ long Parse(IMkvReader*, long long pos, long long size);
+
+ long ParseAtom(IMkvReader*, long long pos, long long size);
+ bool ExpandAtomsArray();
+
+ Atom* m_atoms;
+ int m_atoms_size;
+ int m_atoms_count;
+ };
+
+ int GetEditionCount() const;
+ const Edition* GetEdition(int index) const;
+
+ private:
+ long ParseEdition(long long pos, long long size);
+ bool ExpandEditionsArray();
+
+ Edition* m_editions;
+ int m_editions_size;
+ 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&);
+
+ 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;
+
+ SegmentInfo(Segment*, long long start, long long size,
+ long long element_start, long long element_size);
+
+ ~SegmentInfo();
+
+ long Parse();
+
+ long long GetTimeCodeScale() const;
+ long long GetDuration() const; // scaled
+ const char* GetMuxingAppAsUTF8() const;
+ const char* GetWritingAppAsUTF8() const;
+ const char* GetTitleAsUTF8() const;
+
+ private:
+ long long m_timecodeScale;
+ double m_duration;
+ char* m_pMuxingAppAsUTF8;
+ char* m_pWritingAppAsUTF8;
+ char* m_pTitleAsUTF8;
+};
+
+class SeekHead {
+ SeekHead(const SeekHead&);
+ SeekHead& operator=(const SeekHead&);
+
+ 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;
+
+ SeekHead(Segment*, long long start, long long size, long long element_start,
+ long long element_size);
+
+ ~SeekHead();
+
+ long Parse();
+
+ struct Entry {
+ // the SeekHead entry payload
+ long long id;
+ long long pos;
+
+ // absolute pos of SeekEntry ID
+ long long element_start;
+
+ // SeekEntry ID size + size size + payload
+ long long element_size;
+ };
+
+ int GetCount() const;
+ const Entry* GetEntry(int idx) const;
+
+ struct VoidElement {
+ // absolute pos of Void ID
+ long long element_start;
+
+ // ID size + size size + payload size
+ long long element_size;
+ };
+
+ int GetVoidElementCount() const;
+ const VoidElement* GetVoidElement(int idx) const;
+
+ private:
+ Entry* m_entries;
+ int m_entry_count;
+
+ VoidElement* m_void_elements;
+ int m_void_element_count;
+
+ static bool ParseEntry(IMkvReader*,
+ long long pos, // payload
+ long long size, Entry*);
+};
+
+class Cues;
+class CuePoint {
+ friend class Cues;
+
+ CuePoint(long, long long);
+ ~CuePoint();
+
+ CuePoint(const CuePoint&);
+ CuePoint& operator=(const CuePoint&);
+
+ public:
+ long long m_element_start;
+ long long m_element_size;
+
+ bool Load(IMkvReader*);
+
+ long long GetTimeCode() const; // absolute but unscaled
+ long long GetTime(const Segment*) const; // absolute and scaled (ns units)
+
+ struct TrackPosition {
+ long long m_track;
+ long long m_pos; // of cluster
+ long long m_block;
+ // codec_state //defaults to 0
+ // reference = clusters containing req'd referenced blocks
+ // reftime = timecode of the referenced block
+
+ bool Parse(IMkvReader*, long long, long long);
+ };
+
+ const TrackPosition* Find(const Track*) const;
+
+ private:
+ const long m_index;
+ long long m_timecode;
+ TrackPosition* m_track_positions;
+ size_t m_track_positions_count;
+};
+
+class Cues {
+ friend class Segment;
+
+ Cues(Segment*, long long start, long long size, long long element_start,
+ long long element_size);
+ ~Cues();
+
+ Cues(const Cues&);
+ Cues& operator=(const Cues&);
+
+ 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;
+
+ bool Find( // lower bound of time_ns
+ long long time_ns, const Track*, const CuePoint*&,
+ const CuePoint::TrackPosition*&) const;
+
+ const CuePoint* GetFirst() const;
+ const CuePoint* GetLast() const;
+ const CuePoint* GetNext(const CuePoint*) const;
+
+ const BlockEntry* GetBlock(const CuePoint*,
+ const CuePoint::TrackPosition*) const;
+
+ bool LoadCuePoint() const;
+ long GetCount() const; // loaded only
+ // long GetTotal() const; //loaded + preloaded
+ bool DoneParsing() const;
+
+ private:
+ bool Init() const;
+ bool PreloadCuePoint(long&, long long) const;
+
+ mutable CuePoint** m_cue_points;
+ mutable long m_count;
+ mutable long m_preload_count;
+ mutable long long m_pos;
+};
+
+class Cluster {
+ friend class Segment;
+
+ Cluster(const Cluster&);
+ Cluster& operator=(const Cluster&);
+
+ public:
+ Segment* const m_pSegment;
+
+ public:
+ static Cluster* Create(Segment*,
+ long index, // index in segment
+ long long off); // offset relative to segment
+ // long long element_size);
+
+ Cluster(); // EndOfStream
+ ~Cluster();
+
+ bool EOS() const;
+
+ long long GetTimeCode() const; // absolute, but not scaled
+ long long GetTime() const; // absolute, and scaled (nanosecond units)
+ long long GetFirstTime() const; // time (ns) of first (earliest) block
+ long long GetLastTime() const; // time (ns) of last (latest) block
+
+ long GetFirst(const BlockEntry*&) const;
+ long GetLast(const BlockEntry*&) const;
+ long GetNext(const BlockEntry* curr, const BlockEntry*& next) const;
+
+ const BlockEntry* GetEntry(const Track*, long long ns = -1) const;
+ const BlockEntry* GetEntry(const CuePoint&,
+ const CuePoint::TrackPosition&) const;
+ // const BlockEntry* GetMaxKey(const VideoTrack*) const;
+
+ // static bool HasBlockEntries(const Segment*, long long);
+
+ static long HasBlockEntries(const Segment*, long long idoff, long long& pos,
+ long& size);
+
+ long GetEntryCount() const;
+
+ long Load(long long& pos, long& size) const;
+
+ long Parse(long long& pos, long& size) const;
+ long GetEntry(long index, const mkvparser::BlockEntry*&) const;
+
+ protected:
+ Cluster(Segment*, long index, long long element_start);
+ // long long element_size);
+
+ public:
+ const long long m_element_start;
+ long long GetPosition() const; // offset relative to segment
+
+ long GetIndex() const;
+ long long GetElementSize() const;
+ // long long GetPayloadSize() const;
+
+ // long long Unparsed() const;
+
+ private:
+ long m_index;
+ mutable long long m_pos;
+ // mutable long long m_size;
+ mutable long long m_element_size;
+ mutable long long m_timecode;
+ mutable BlockEntry** m_entries;
+ mutable long m_entries_size;
+ mutable long m_entries_count;
+
+ long ParseSimpleBlock(long long, long long&, long&);
+ long ParseBlockGroup(long long, long long&, long&);
+
+ long CreateBlock(long long id, long long pos, long long size,
+ long long discard_padding);
+ long CreateBlockGroup(long long start_offset, long long size,
+ long long discard_padding);
+ long CreateSimpleBlock(long long, long long);
+};
+
+class Segment {
+ friend class Cues;
+ friend class Track;
+ friend class VideoTrack;
+
+ Segment(const Segment&);
+ Segment& operator=(const Segment&);
+
+ private:
+ Segment(IMkvReader*, long long elem_start,
+ // long long elem_size,
+ long long pos, long long size);
+
+ public:
+ IMkvReader* const m_pReader;
+ const long long m_element_start;
+ // const long long m_element_size;
+ const long long m_start; // posn of segment payload
+ const long long m_size; // size of segment payload
+ Cluster m_eos; // TODO: make private?
+
+ static long long CreateInstance(IMkvReader*, long long, Segment*&);
+ ~Segment();
+
+ long Load(); // loads headers and all clusters
+
+ // for incremental loading
+ // long long Unparsed() const;
+ bool DoneParsing() const;
+ long long ParseHeaders(); // stops when first cluster is found
+ // long FindNextCluster(long long& pos, long& size) const;
+ long LoadCluster(long long& pos, long& size); // load one cluster
+ long LoadCluster();
+
+ long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos,
+ long& size);
+
+ 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;
+
+ unsigned long GetCount() const;
+ const Cluster* GetFirst() const;
+ const Cluster* GetLast() const;
+ const Cluster* GetNext(const Cluster*);
+
+ const Cluster* FindCluster(long long time_nanoseconds) const;
+ // const BlockEntry* Seek(long long time_nanoseconds, const Track*) const;
+
+ const Cluster* FindOrPreloadCluster(long long pos);
+
+ long ParseCues(long long cues_off, // offset relative to start of segment
+ long long& parse_pos, long& parse_len);
+
+ private:
+ long long m_pos; // absolute file posn; what has been consumed so far
+ Cluster* m_pUnknownSize;
+
+ SeekHead* m_pSeekHead;
+ SegmentInfo* m_pInfo;
+ 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
+ long m_clusterSize; // array size
+
+ long DoLoadCluster(long long&, long&);
+ long DoLoadClusterUnknownSize(long long&, long&);
+ long DoParseNext(const Cluster*&, long long&, long&);
+
+ bool AppendCluster(Cluster*);
+ bool PreloadCluster(Cluster*, ptrdiff_t);
+
+ // void ParseSeekHead(long long pos, long long size);
+ // void ParseSeekEntry(long long pos, long long size);
+ // void ParseCues(long long);
+
+ const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&);
+};
+
+} // namespace mkvparser
+
+inline long mkvparser::Segment::LoadCluster() {
+ long long pos;
+ long size;
+
+ return LoadCluster(pos, size);
+}
+
+#endif // MKVPARSER_MKVPARSER_H_
--- /dev/null
+++ b/third_party/libwebm/mkvparser/mkvreader.cc
@@ -1,0 +1,131 @@
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#include "mkvparser/mkvreader.h"
+
+#include <cassert>
+
+namespace mkvparser {
+
+MkvReader::MkvReader() : m_file(NULL), reader_owns_file_(true) {}
+
+MkvReader::MkvReader(FILE* fp) : m_file(fp), reader_owns_file_(false) {
+ GetFileSize();
+}
+
+MkvReader::~MkvReader() {
+ if (reader_owns_file_)
+ Close();
+ m_file = NULL;
+}
+
+int MkvReader::Open(const char* fileName) {
+ if (fileName == NULL)
+ return -1;
+
+ if (m_file)
+ return -1;
+
+#ifdef _MSC_VER
+ const errno_t e = fopen_s(&m_file, fileName, "rb");
+
+ if (e)
+ return -1; // error
+#else
+ m_file = fopen(fileName, "rb");
+
+ if (m_file == NULL)
+ return -1;
+#endif
+ return !GetFileSize();
+}
+
+bool MkvReader::GetFileSize() {
+ if (m_file == NULL)
+ return false;
+#ifdef _MSC_VER
+ int status = _fseeki64(m_file, 0L, SEEK_END);
+
+ if (status)
+ return false; // error
+
+ m_length = _ftelli64(m_file);
+#else
+ fseek(m_file, 0L, SEEK_END);
+ m_length = ftell(m_file);
+#endif
+ assert(m_length >= 0);
+
+ if (m_length < 0)
+ return false;
+
+#ifdef _MSC_VER
+ status = _fseeki64(m_file, 0L, SEEK_SET);
+
+ if (status)
+ return false; // error
+#else
+ fseek(m_file, 0L, SEEK_SET);
+#endif
+
+ return true;
+}
+
+void MkvReader::Close() {
+ if (m_file != NULL) {
+ fclose(m_file);
+ m_file = NULL;
+ }
+}
+
+int MkvReader::Length(long long* total, long long* available) {
+ if (m_file == NULL)
+ return -1;
+
+ if (total)
+ *total = m_length;
+
+ if (available)
+ *available = m_length;
+
+ return 0;
+}
+
+int MkvReader::Read(long long offset, long len, unsigned char* buffer) {
+ if (m_file == NULL)
+ return -1;
+
+ if (offset < 0)
+ return -1;
+
+ if (len < 0)
+ return -1;
+
+ if (len == 0)
+ return 0;
+
+ if (offset >= m_length)
+ return -1;
+
+#ifdef _MSC_VER
+ const int status = _fseeki64(m_file, offset, SEEK_SET);
+
+ if (status)
+ return -1; // error
+#else
+ fseek(m_file, offset, SEEK_SET);
+#endif
+
+ const size_t size = fread(buffer, 1, len, m_file);
+
+ if (size < size_t(len))
+ return -1; // error
+
+ return 0; // success
+}
+
+} // namespace mkvparser
\ No newline at end of file
--- /dev/null
+++ b/third_party/libwebm/mkvparser/mkvreader.h
@@ -1,0 +1,45 @@
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#ifndef MKVPARSER_MKVREADER_H_
+#define MKVPARSER_MKVREADER_H_
+
+#include <cstdio>
+
+#include "mkvparser/mkvparser.h"
+
+namespace mkvparser {
+
+class MkvReader : public IMkvReader {
+ public:
+ MkvReader();
+ explicit MkvReader(FILE* fp);
+ virtual ~MkvReader();
+
+ int Open(const char*);
+ void Close();
+
+ virtual int Read(long long position, long length, unsigned char* buffer);
+ virtual int Length(long long* total, long long* available);
+
+ private:
+ MkvReader(const MkvReader&);
+ MkvReader& operator=(const MkvReader&);
+
+ // Determines the size of the file. This is called either by the constructor
+ // or by the Open function depending on file ownership. Returns true on
+ // success.
+ bool GetFileSize();
+
+ long long m_length;
+ FILE* m_file;
+ bool reader_owns_file_;
+};
+
+} // namespace mkvparser
+
+#endif // MKVPARSER_MKVREADER_H_
--- a/third_party/libwebm/mkvreader.cpp
+++ /dev/null
@@ -1,132 +1,0 @@
-// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#include "mkvreader.hpp"
-
-#include <cassert>
-
-namespace mkvparser {
-
-MkvReader::MkvReader() : m_file(NULL), reader_owns_file_(true) {}
-
-MkvReader::MkvReader(FILE* fp) : m_file(fp), reader_owns_file_(false) {
- GetFileSize();
-}
-
-MkvReader::~MkvReader() {
- if (reader_owns_file_)
- Close();
- m_file = NULL;
-}
-
-int MkvReader::Open(const char* fileName) {
- if (fileName == NULL)
- return -1;
-
- if (m_file)
- return -1;
-
-#ifdef _MSC_VER
- const errno_t e = fopen_s(&m_file, fileName, "rb");
-
- if (e)
- return -1; // error
-#else
- m_file = fopen(fileName, "rb");
-
- if (m_file == NULL)
- return -1;
-#endif
- return !GetFileSize();
-}
-
-bool MkvReader::GetFileSize() {
- if (m_file == NULL)
- return false;
-#ifdef _MSC_VER
- int status = _fseeki64(m_file, 0L, SEEK_END);
-
- if (status)
- return false; // error
-
- m_length = _ftelli64(m_file);
-#else
- fseek(m_file, 0L, SEEK_END);
- m_length = ftell(m_file);
-#endif
- assert(m_length >= 0);
-
- if (m_length < 0)
- return false;
-
-#ifdef _MSC_VER
- status = _fseeki64(m_file, 0L, SEEK_SET);
-
- if (status)
- return false; // error
-#else
- fseek(m_file, 0L, SEEK_SET);
-#endif
-
- return true;
-}
-
-void MkvReader::Close() {
- if (m_file != NULL) {
- fclose(m_file);
- m_file = NULL;
- }
-}
-
-int MkvReader::Length(long long* total, long long* available) {
- if (m_file == NULL)
- return -1;
-
- if (total)
- *total = m_length;
-
- if (available)
- *available = m_length;
-
- return 0;
-}
-
-int MkvReader::Read(long long offset, long len, unsigned char* buffer) {
- if (m_file == NULL)
- return -1;
-
- if (offset < 0)
- return -1;
-
- if (len < 0)
- return -1;
-
- if (len == 0)
- return 0;
-
- if (offset >= m_length)
- return -1;
-
-#ifdef _MSC_VER
- const int status = _fseeki64(m_file, offset, SEEK_SET);
-
- if (status)
- return -1; // error
-#else
- fseek(m_file, offset, SEEK_SET);
-#endif
-
- const size_t size = fread(buffer, 1, len, m_file);
-
- if (size < size_t(len))
- return -1; // error
-
- return 0; // success
-}
-
-} // end namespace mkvparser
--- a/third_party/libwebm/mkvreader.hpp
+++ /dev/null
@@ -1,45 +1,0 @@
-// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef MKVREADER_HPP
-#define MKVREADER_HPP
-
-#include "mkvparser.hpp"
-#include <cstdio>
-
-namespace mkvparser {
-
-class MkvReader : public IMkvReader {
- public:
- MkvReader();
- explicit MkvReader(FILE* fp);
- virtual ~MkvReader();
-
- int Open(const char*);
- void Close();
-
- virtual int Read(long long position, long length, unsigned char* buffer);
- virtual int Length(long long* total, long long* available);
-
- private:
- MkvReader(const MkvReader&);
- MkvReader& operator=(const MkvReader&);
-
- // Determines the size of the file. This is called either by the constructor
- // or by the Open function depending on file ownership. Returns true on
- // success.
- bool GetFileSize();
-
- long long m_length;
- FILE* m_file;
- bool reader_owns_file_;
-};
-
-} // end namespace mkvparser
-
-#endif // MKVREADER_HPP
--- a/third_party/libwebm/mkvwriter.cpp
+++ /dev/null
@@ -1,90 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#include "mkvwriter.hpp"
-
-#ifdef _MSC_VER
-#include <share.h> // for _SH_DENYWR
-#endif
-
-#include <new>
-
-namespace mkvmuxer {
-
-MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
-
-MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {}
-
-MkvWriter::~MkvWriter() { Close(); }
-
-int32 MkvWriter::Write(const void* buffer, uint32 length) {
- if (!file_)
- return -1;
-
- if (length == 0)
- return 0;
-
- if (buffer == NULL)
- return -1;
-
- const size_t bytes_written = fwrite(buffer, 1, length, file_);
-
- return (bytes_written == length) ? 0 : -1;
-}
-
-bool MkvWriter::Open(const char* filename) {
- if (filename == NULL)
- return false;
-
- if (file_)
- return false;
-
-#ifdef _MSC_VER
- file_ = _fsopen(filename, "wb", _SH_DENYWR);
-#else
- file_ = fopen(filename, "wb");
-#endif
- if (file_ == NULL)
- return false;
- return true;
-}
-
-void MkvWriter::Close() {
- if (file_ && writer_owns_file_) {
- fclose(file_);
- }
- file_ = NULL;
-}
-
-int64 MkvWriter::Position() const {
- if (!file_)
- return 0;
-
-#ifdef _MSC_VER
- return _ftelli64(file_);
-#else
- return ftell(file_);
-#endif
-}
-
-int32 MkvWriter::Position(int64 position) {
- if (!file_)
- return -1;
-
-#ifdef _MSC_VER
- return _fseeki64(file_, position, SEEK_SET);
-#else
- return fseek(file_, position, SEEK_SET);
-#endif
-}
-
-bool MkvWriter::Seekable() const { return true; }
-
-void MkvWriter::ElementStartNotify(uint64, int64) {}
-
-} // namespace mkvmuxer
--- a/third_party/libwebm/mkvwriter.hpp
+++ /dev/null
@@ -1,51 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef MKVWRITER_HPP
-#define MKVWRITER_HPP
-
-#include <stdio.h>
-
-#include "mkvmuxer.hpp"
-#include "mkvmuxertypes.hpp"
-
-namespace mkvmuxer {
-
-// Default implementation of the IMkvWriter interface on Windows.
-class MkvWriter : public IMkvWriter {
- public:
- MkvWriter();
- explicit MkvWriter(FILE* fp);
- virtual ~MkvWriter();
-
- // IMkvWriter interface
- virtual int64 Position() const;
- virtual int32 Position(int64 position);
- virtual bool Seekable() const;
- virtual int32 Write(const void* buffer, uint32 length);
- virtual void ElementStartNotify(uint64 element_id, int64 position);
-
- // Creates and opens a file for writing. |filename| is the name of the file
- // to open. This function will overwrite the contents of |filename|. Returns
- // true on success.
- bool Open(const char* filename);
-
- // Closes an opened file.
- void Close();
-
- private:
- // File handle to output file.
- FILE* file_;
- bool writer_owns_file_;
-
- LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
-};
-
-} // end namespace mkvmuxer
-
-#endif // MKVWRITER_HPP
--- a/third_party/libwebm/webmids.hpp
+++ /dev/null
@@ -1,155 +1,0 @@
-// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef WEBMIDS_HPP
-#define WEBMIDS_HPP
-
-namespace mkvmuxer {
-
-enum MkvId {
- kMkvEBML = 0x1A45DFA3,
- kMkvEBMLVersion = 0x4286,
- kMkvEBMLReadVersion = 0x42F7,
- kMkvEBMLMaxIDLength = 0x42F2,
- kMkvEBMLMaxSizeLength = 0x42F3,
- kMkvDocType = 0x4282,
- kMkvDocTypeVersion = 0x4287,
- kMkvDocTypeReadVersion = 0x4285,
- kMkvVoid = 0xEC,
- kMkvSignatureSlot = 0x1B538667,
- kMkvSignatureAlgo = 0x7E8A,
- kMkvSignatureHash = 0x7E9A,
- kMkvSignaturePublicKey = 0x7EA5,
- kMkvSignature = 0x7EB5,
- kMkvSignatureElements = 0x7E5B,
- kMkvSignatureElementList = 0x7E7B,
- kMkvSignedElement = 0x6532,
- // segment
- kMkvSegment = 0x18538067,
- // Meta Seek Information
- kMkvSeekHead = 0x114D9B74,
- kMkvSeek = 0x4DBB,
- kMkvSeekID = 0x53AB,
- kMkvSeekPosition = 0x53AC,
- // Segment Information
- kMkvInfo = 0x1549A966,
- kMkvTimecodeScale = 0x2AD7B1,
- kMkvDuration = 0x4489,
- kMkvDateUTC = 0x4461,
- kMkvTitle = 0x7BA9,
- kMkvMuxingApp = 0x4D80,
- kMkvWritingApp = 0x5741,
- // Cluster
- kMkvCluster = 0x1F43B675,
- kMkvTimecode = 0xE7,
- kMkvPrevSize = 0xAB,
- kMkvBlockGroup = 0xA0,
- kMkvBlock = 0xA1,
- kMkvBlockDuration = 0x9B,
- kMkvReferenceBlock = 0xFB,
- kMkvLaceNumber = 0xCC,
- kMkvSimpleBlock = 0xA3,
- kMkvBlockAdditions = 0x75A1,
- kMkvBlockMore = 0xA6,
- kMkvBlockAddID = 0xEE,
- kMkvBlockAdditional = 0xA5,
- kMkvDiscardPadding = 0x75A2,
- // Track
- kMkvTracks = 0x1654AE6B,
- kMkvTrackEntry = 0xAE,
- kMkvTrackNumber = 0xD7,
- kMkvTrackUID = 0x73C5,
- kMkvTrackType = 0x83,
- kMkvFlagEnabled = 0xB9,
- kMkvFlagDefault = 0x88,
- kMkvFlagForced = 0x55AA,
- kMkvFlagLacing = 0x9C,
- kMkvDefaultDuration = 0x23E383,
- kMkvMaxBlockAdditionID = 0x55EE,
- kMkvName = 0x536E,
- kMkvLanguage = 0x22B59C,
- kMkvCodecID = 0x86,
- kMkvCodecPrivate = 0x63A2,
- kMkvCodecName = 0x258688,
- kMkvCodecDelay = 0x56AA,
- kMkvSeekPreRoll = 0x56BB,
- // video
- kMkvVideo = 0xE0,
- kMkvFlagInterlaced = 0x9A,
- kMkvStereoMode = 0x53B8,
- kMkvAlphaMode = 0x53C0,
- kMkvPixelWidth = 0xB0,
- kMkvPixelHeight = 0xBA,
- kMkvPixelCropBottom = 0x54AA,
- kMkvPixelCropTop = 0x54BB,
- kMkvPixelCropLeft = 0x54CC,
- kMkvPixelCropRight = 0x54DD,
- kMkvDisplayWidth = 0x54B0,
- kMkvDisplayHeight = 0x54BA,
- kMkvDisplayUnit = 0x54B2,
- kMkvAspectRatioType = 0x54B3,
- kMkvFrameRate = 0x2383E3,
- // end video
- // audio
- kMkvAudio = 0xE1,
- kMkvSamplingFrequency = 0xB5,
- kMkvOutputSamplingFrequency = 0x78B5,
- kMkvChannels = 0x9F,
- kMkvBitDepth = 0x6264,
- // end audio
- // ContentEncodings
- kMkvContentEncodings = 0x6D80,
- kMkvContentEncoding = 0x6240,
- kMkvContentEncodingOrder = 0x5031,
- kMkvContentEncodingScope = 0x5032,
- kMkvContentEncodingType = 0x5033,
- kMkvContentCompression = 0x5034,
- kMkvContentCompAlgo = 0x4254,
- kMkvContentCompSettings = 0x4255,
- kMkvContentEncryption = 0x5035,
- kMkvContentEncAlgo = 0x47E1,
- kMkvContentEncKeyID = 0x47E2,
- kMkvContentSignature = 0x47E3,
- kMkvContentSigKeyID = 0x47E4,
- kMkvContentSigAlgo = 0x47E5,
- kMkvContentSigHashAlgo = 0x47E6,
- kMkvContentEncAESSettings = 0x47E7,
- kMkvAESSettingsCipherMode = 0x47E8,
- kMkvAESSettingsCipherInitData = 0x47E9,
- // end ContentEncodings
- // Cueing Data
- kMkvCues = 0x1C53BB6B,
- kMkvCuePoint = 0xBB,
- kMkvCueTime = 0xB3,
- kMkvCueTrackPositions = 0xB7,
- kMkvCueTrack = 0xF7,
- kMkvCueClusterPosition = 0xF1,
- kMkvCueBlockNumber = 0x5378,
- // Chapters
- kMkvChapters = 0x1043A770,
- kMkvEditionEntry = 0x45B9,
- kMkvChapterAtom = 0xB6,
- kMkvChapterUID = 0x73C4,
- kMkvChapterStringUID = 0x5654,
- kMkvChapterTimeStart = 0x91,
- kMkvChapterTimeEnd = 0x92,
- kMkvChapterDisplay = 0x80,
- kMkvChapString = 0x85,
- kMkvChapLanguage = 0x437C,
- kMkvChapCountry = 0x437E,
- // Tags
- kMkvTags = 0x1254C367,
- kMkvTag = 0x7373,
- kMkvSimpleTag = 0x67C8,
- kMkvTagName = 0x45A3,
- kMkvTagString = 0x4487
-};
-
-} // end namespace mkvmuxer
-
-#endif // WEBMIDS_HPP
--- a/webmdec.cc
+++ b/webmdec.cc
@@ -13,8 +13,8 @@
#include <cstring>
#include <cstdio>
-#include "third_party/libwebm/mkvparser.hpp"
-#include "third_party/libwebm/mkvreader.hpp"
+#include "third_party/libwebm/mkvparser/mkvparser.h"
+#include "third_party/libwebm/mkvparser/mkvreader.h"
namespace {
--- a/webmenc.cc
+++ b/webmenc.cc
@@ -11,9 +11,9 @@
#include <string>
-#include "third_party/libwebm/mkvmuxer.hpp"
-#include "third_party/libwebm/mkvmuxerutil.hpp"
-#include "third_party/libwebm/mkvwriter.hpp"
+#include "third_party/libwebm/mkvmuxer/mkvmuxer.h"
+#include "third_party/libwebm/mkvmuxer/mkvmuxerutil.h"
+#include "third_party/libwebm/mkvmuxer/mkvwriter.h"
namespace {
const uint64_t kDebugTrackUid = 0xDEADBEEF;