third_party: Roll libwebm snapshot. 32d5ac4 mkvmuxerutil: Fix MSVC build. 6397597 vpxpes_parser: Fix MSVC build. 784b6fe mkvmuxer: Revert changes to IMkvWriter types. 030518e webm_info: Fix implicit conversion warnings. 46d5dee sample_muxer: Fix implicit conversion warnings. 22b0845 webmts: Fix implicit conversion warnings. ef464c2 mkvparser: Fix implicit conversion warnings. 0e9767e Merge "rename mingw64_toolchain.cmake to mingw-w64_toolchain" f47cbd5 cmake: Move cxx flag testing/setup into its own file. 87443a6 rename mingw64_toolchain.cmake to mingw-w64_toolchain 2aee04f msvc_runtime.cmake: Check for MSVC, not WIN32. 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: I542bd5e34586f29d4d15771ec4bd728defe0769e
diff --git a/examples.mk b/examples.mk index f10bec6..c891a54 100644 --- a/examples.mk +++ b/examples.mk
@@ -36,21 +36,30 @@ 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. @@ -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
diff --git a/libs.mk b/libs.mk index e6fb068..c2a4725 100644 --- 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)
diff --git a/test/test.mk b/test/test.mk index 0a4b69c..6931853 100644 --- 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
diff --git a/third_party/libwebm/Android.mk b/third_party/libwebm/Android.mk index be9d77d..8149a08 100644 --- 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)
diff --git a/third_party/libwebm/README.libvpx b/third_party/libwebm/README.libvpx index 2989d3d..73f8303 100644 --- a/third_party/libwebm/README.libvpx +++ b/third_party/libwebm/README.libvpx
@@ -1,5 +1,5 @@ URL: https://chromium.googlesource.com/webm/libwebm -Version: 476366249e1fda7710a389cd41c57db42305e0d4 +Version: 32d5ac49414a8914ec1e1f285f3f927c6e8ec29d License: BSD License File: LICENSE.txt
diff --git a/third_party/libwebm/RELEASE.TXT b/third_party/libwebm/RELEASE.TXT deleted file mode 100644 index a7e9f03..0000000 --- a/third_party/libwebm/RELEASE.TXT +++ /dev/null
@@ -1,34 +0,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
diff --git a/third_party/libwebm/common/file_util.cc b/third_party/libwebm/common/file_util.cc new file mode 100644 index 0000000..4f91318 --- /dev/null +++ b/third_party/libwebm/common/file_util.cc
@@ -0,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
diff --git a/third_party/libwebm/common/file_util.h b/third_party/libwebm/common/file_util.h new file mode 100644 index 0000000..0e71eac --- /dev/null +++ b/third_party/libwebm/common/file_util.h
@@ -0,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
diff --git a/third_party/libwebm/common/hdr_util.cc b/third_party/libwebm/common/hdr_util.cc new file mode 100644 index 0000000..e1a9842 --- /dev/null +++ b/third_party/libwebm/common/hdr_util.cc
@@ -0,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
diff --git a/third_party/libwebm/common/hdr_util.h b/third_party/libwebm/common/hdr_util.h new file mode 100644 index 0000000..d30c2b9 --- /dev/null +++ b/third_party/libwebm/common/hdr_util.h
@@ -0,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_
diff --git a/third_party/libwebm/webmids.hpp b/third_party/libwebm/common/webmids.h similarity index 79% rename from third_party/libwebm/webmids.hpp rename to third_party/libwebm/common/webmids.h index ad4ab57..32a0c5f 100644 --- a/third_party/libwebm/webmids.hpp +++ b/third_party/libwebm/common/webmids.h
@@ -6,10 +6,10 @@ // 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 +#ifndef COMMON_WEBMIDS_H_ +#define COMMON_WEBMIDS_H_ -namespace mkvmuxer { +namespace libwebm { enum MkvId { kMkvEBML = 0x1A45DFA3, @@ -95,6 +95,35 @@ 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, @@ -150,6 +179,6 @@ kMkvTagString = 0x4487 }; -} // end namespace mkvmuxer +} // namespace libwebm -#endif // WEBMIDS_HPP +#endif // COMMON_WEBMIDS_H_
diff --git a/third_party/libwebm/mkvmuxer.cpp b/third_party/libwebm/mkvmuxer.cpp deleted file mode 100644 index 9be3119..0000000 --- a/third_party/libwebm/mkvmuxer.cpp +++ /dev/null
@@ -1,3277 +0,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
diff --git a/third_party/libwebm/mkvmuxer/mkvmuxer.cc b/third_party/libwebm/mkvmuxer/mkvmuxer.cc new file mode 100644 index 0000000..c79ce24 --- /dev/null +++ b/third_party/libwebm/mkvmuxer/mkvmuxer.cc
@@ -0,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
diff --git a/third_party/libwebm/mkvmuxer.hpp b/third_party/libwebm/mkvmuxer/mkvmuxer.h similarity index 68% rename from third_party/libwebm/mkvmuxer.hpp rename to third_party/libwebm/mkvmuxer/mkvmuxer.h index 03a002c..55ba071 100644 --- a/third_party/libwebm/mkvmuxer.hpp +++ b/third_party/libwebm/mkvmuxer/mkvmuxer.h
@@ -6,24 +6,31 @@ // 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 +#ifndef MKVMUXER_MKVMUXER_H_ +#define MKVMUXER_MKVMUXER_H_ -#include "mkvmuxertypes.hpp" +#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; -} // end namespace +} // namespace mkvparser namespace mkvmuxer { class MkvWriter; class Segment; -const uint64 kMaxTrackNumber = 126; +const uint64_t kMaxTrackNumber = 126; /////////////////////////////////////////////////////////////// // Interface used by the mkvmuxer to write out the Mkv data. @@ -59,15 +66,15 @@ // 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); +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 start, - int64 size); +bool ChunkedCopy(mkvparser::IMkvReader* source, IMkvWriter* dst, int64_t start, + int64_t size); /////////////////////////////////////////////////////////////// // Class to hold data the will be written to a block. @@ -81,10 +88,11 @@ bool CopyFrom(const Frame& frame); // Copies |frame| data into |frame_|. Returns true on success. - bool Init(const uint8* frame, uint64 length); + bool Init(const uint8_t* frame, uint64_t length); // Copies |additional| data into |additional_|. Returns true on success. - bool AddAdditionalData(const uint8* additional, uint64 length, uint64 add_id); + bool AddAdditionalData(const uint8_t* additional, uint64_t length, + uint64_t add_id); // Returns true if the frame has valid parameters. bool IsValid() const; @@ -93,62 +101,70 @@ // 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_; } + 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 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) { + 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 discard_padding() const { return discard_padding_; } - void set_reference_block_timestamp(int64 reference_block_timestamp); - int64 reference_block_timestamp() const { return reference_block_timestamp_; } + 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 add_id_; + uint64_t add_id_; // Pointer to additional data. Owned by this class. - uint8* additional_; + uint8_t* additional_; // Length of the additional data. - uint64 additional_length_; + uint64_t additional_length_; // Duration of the frame in nanoseconds. - uint64 duration_; + 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* frame_; + uint8_t* frame_; // Flag telling if the data should set the key flag of a block. bool is_key_; // Length of the data. - uint64 length_; + uint64_t length_; // Mkv track number the data is associated with. - uint64 track_number_; + uint64_t track_number_; // Timestamp of the data in nanoseconds. - uint64 timestamp_; + uint64_t timestamp_; // Discard padding for the frame. - int64 discard_padding_; + int64_t discard_padding_; // Reference block timestamp. - int64 reference_block_timestamp_; + int64_t reference_block_timestamp_; // Flag indicating if |reference_block_timestamp_| has been set. bool reference_block_timestamp_set_; @@ -164,19 +180,19 @@ ~CuePoint(); // Returns the size in bytes for the entire CuePoint element. - uint64 Size() const; + uint64_t 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_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; } @@ -184,19 +200,19 @@ private: // Returns the size in bytes for the payload of the CuePoint element. - uint64 PayloadSize() const; + uint64_t PayloadSize() const; // Absolute timecode according to the segment time base. - uint64 time_; + uint64_t time_; // The Track element associated with the CuePoint. - uint64 track_; + uint64_t track_; // The position of the Cluster containing the Block. - uint64 cluster_pos_; + uint64_t cluster_pos_; // Number of the Block within the Cluster, starting from 1. - uint64 block_number_; + 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. @@ -217,15 +233,15 @@ // Returns the cue point by index. Returns NULL if there is no cue point // match. - CuePoint* GetCueByIndex(int32 index) const; + CuePoint* GetCueByIndex(int32_t index) const; // Returns the total size of the Cues element - uint64 Size(); + uint64_t 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_; } + 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; } @@ -233,10 +249,10 @@ private: // Number of allocated elements in |cue_entries_|. - int32 cue_entries_capacity_; + int32_t cue_entries_capacity_; // Number of CuePoints in |cue_entries_|. - int32 cue_entries_size_; + int32_t cue_entries_size_; // CuePoint list. CuePoint** cue_entries_; @@ -258,21 +274,21 @@ ~ContentEncAESSettings() {} // Returns the size in bytes for the ContentEncAESSettings element. - uint64 Size() const; + uint64_t 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_; } + uint64_t cipher_mode() const { return cipher_mode_; } private: // Returns the size in bytes for the payload of the ContentEncAESSettings // element. - uint64 PayloadSize() const; + uint64_t PayloadSize() const; // Sub elements - uint64 cipher_mode_; + uint64_t cipher_mode_; LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncAESSettings); }; @@ -291,45 +307,158 @@ // 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); + bool SetEncryptionID(const uint8_t* id, uint64_t length); // Returns the size in bytes for the ContentEncoding element. - uint64 Size() const; + uint64_t 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_; } + 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 EncodingSize(uint64 compresion_size, uint64 encryption_size) const; + uint64_t EncodingSize(uint64_t compresion_size, + uint64_t encryption_size) const; // Returns the size in bytes for the encryption elements. - uint64 EncryptionSize() const; + uint64_t EncryptionSize() const; // Track element names - uint64 enc_algo_; - uint8* enc_key_id_; - uint64 encoding_order_; - uint64 encoding_scope_; - uint64 encoding_type_; + 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 enc_key_id_length_; + 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: @@ -342,76 +471,76 @@ // Returns the ContentEncoding by index. Returns NULL if there is no // ContentEncoding match. - ContentEncoding* GetContentEncodingByIndex(uint32 index) const; + ContentEncoding* GetContentEncodingByIndex(uint32_t index) const; // Returns the size in bytes for the payload of the Track element. - virtual uint64 PayloadSize() const; + virtual uint64_t PayloadSize() const; // Returns the size in bytes of the Track element. - virtual uint64 Size() const; + 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* codec_private, uint64 length); + 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* codec_private() const { return codec_private_; } + 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 max_block_additional_id) { + void set_max_block_additional_id(uint64_t max_block_additional_id) { max_block_additional_id_ = max_block_additional_id; } - uint64 max_block_additional_id() const { return 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 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) { + 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 seek_pre_roll() const { return seek_pre_roll_; } - void set_default_duration(uint64 default_duration) { + uint64_t seek_pre_roll() const { return seek_pre_roll_; } + void set_default_duration(uint64_t default_duration) { default_duration_ = default_duration; } - uint64 default_duration() const { return default_duration_; } + uint64_t default_duration() const { return default_duration_; } - uint64 codec_private_length() const { return codec_private_length_; } - uint32 content_encoding_entries_size() const { + 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* codec_private_; + uint8_t* codec_private_; char* language_; - uint64 max_block_additional_id_; + uint64_t max_block_additional_id_; char* name_; - uint64 number_; - uint64 type_; - uint64 uid_; - uint64 codec_delay_; - uint64 seek_pre_roll_; - uint64 default_duration_; + 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 codec_private_length_; + uint64_t codec_private_length_; // ContentEncoding element list. ContentEncoding** content_encoding_entries_; // Number of ContentEncoding elements added. - uint32 content_encoding_entries_size_; + uint32_t content_encoding_entries_size_; LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Track); }; @@ -437,56 +566,63 @@ // Returns the size in bytes for the payload of the Track element plus the // video specific elements. - virtual uint64 PayloadSize() const; + 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 stereo_mode); + bool SetStereoMode(uint64_t stereo_mode); // Sets the video's alpha mode. Returns true on success. - bool SetAlphaMode(uint64 alpha_mode); + bool SetAlphaMode(uint64_t 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_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 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_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 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_; } + 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 VideoPayloadSize() const; + uint64_t VideoPayloadSize() const; // Video track element names. - uint64 display_height_; - uint64 display_width_; - uint64 crop_left_; - uint64 crop_right_; - uint64 crop_top_; - uint64 crop_bottom_; + 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 height_; - uint64 stereo_mode_; - uint64 alpha_mode_; - uint64 width_; + uint64_t height_; + uint64_t stereo_mode_; + uint64_t alpha_mode_; + uint64_t width_; + + Colour* colour_; LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack); }; @@ -501,22 +637,22 @@ // Returns the size in bytes for the payload of the Track element plus the // audio specific elements. - virtual uint64 PayloadSize() const; + 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 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_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 bit_depth_; - uint64 channels_; + uint64_t bit_depth_; + uint64_t channels_; double sample_rate_; LIBWEBM_DISALLOW_COPY_AND_ASSIGN(AudioTrack); @@ -542,32 +678,35 @@ // 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); + bool AddTrack(Track* track, int32_t number); // Returns the track by index. Returns NULL if there is no track match. - const Track* GetTrackByIndex(uint32 idx) const; + 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 track_number) const; + Track* GetTrackByNumber(uint64_t track_number) const; // Returns true if the track number is an audio track. - bool TrackIsAudio(uint64 track_number) const; + bool TrackIsAudio(uint64_t track_number) const; // Returns true if the track number is a video track. - bool TrackIsVideo(uint64 track_number) const; + bool TrackIsVideo(uint64_t 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_; } + uint32_t track_entries_size() const { return track_entries_size_; } private: // Track element list. Track** track_entries_; // Number of Track elements added. - uint32 track_entries_size_; + 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); }; @@ -585,12 +724,12 @@ // 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); + 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 uid) { uid_ = uid; } + void set_uid(const uint64_t uid) { uid_ = uid; } // Add a title string to this chapter, per the semantics described // here: @@ -637,7 +776,7 @@ // 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; + uint64_t WriteDisplay(IMkvWriter* writer) const; private: char* title_; @@ -670,20 +809,20 @@ // 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; + 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 start_timecode_; + uint64_t start_timecode_; // Stop timecode of the chapter. - uint64 end_timecode_; + uint64_t end_timecode_; // The binary identifier for this chapter. - uint64 uid_; + uint64_t uid_; // The Atom element can contain multiple Display sub-elements, as // the same logical title can be rendered in different languages. @@ -723,7 +862,7 @@ // 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; + uint64_t WriteEdition(IMkvWriter* writer) const; // Total length of the chapters_ array. int chapters_size_; @@ -768,7 +907,7 @@ // 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; + uint64_t Write(IMkvWriter* writer) const; private: char* tag_name_; @@ -795,7 +934,7 @@ // 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; + uint64_t Write(IMkvWriter* writer) const; // The Atom element can contain multiple SimpleTag sub-elements SimpleTag* simple_tags_; @@ -853,7 +992,9 @@ // |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(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); @@ -872,8 +1013,8 @@ // 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 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 @@ -889,10 +1030,11 @@ // 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); + 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. @@ -905,9 +1047,10 @@ // 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); + 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. @@ -923,31 +1066,53 @@ // 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); + 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 size); + 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. - bool Finalize(); + // 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 Size() const; + uint64_t 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_t GetRelativeTimecode(int64_t 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_; } + 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. @@ -955,37 +1120,58 @@ // Utility method used by the |DoWriteFrame*| methods that handles the book // keeping required after each block is written. - void PostWriteBlock(uint64 element_size); + 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 blocks_added_; + 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 payload_size_; + uint64_t payload_size_; // The file position used for cue points. - const int64 position_for_cues_; + const int64_t position_for_cues_; // The file position of the cluster's size element. - int64 size_position_; + int64_t size_position_; // The absolute timecode of the cluster. - const uint64 timecode_; + const uint64_t timecode_; // The timecode scale of the Segment containing the cluster. - const uint64 timecode_scale_; + 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_; @@ -1006,42 +1192,42 @@ // 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); + 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 GetId(int index) const; + 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 GetPosition(int index) const; + 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 id, uint64 position); + 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 kSeekEntryCount = 5; + const static int32_t kSeekEntryCount = 5; private: // Returns the maximum size in bytes of one seek entry. - uint64 MaxEntrySize() const; + uint64_t MaxEntrySize() const; // Seek entry id element list. - uint32 seek_entry_id_[kSeekEntryCount]; + uint32_t seek_entry_id_[kSeekEntryCount]; // Seek entry pos element list. - uint64 seek_entry_pos_[kSeekEntryCount]; + uint64_t seek_entry_pos_[kSeekEntryCount]; // The file position of SeekHead element. - int64 start_pos_; + int64_t start_pos_; LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SeekHead); }; @@ -1067,12 +1253,12 @@ 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_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 date_utc) { date_utc_ = date_utc; } - int64 date_utc() const { return date_utc_; } + 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. @@ -1081,14 +1267,14 @@ double duration_; // Set to libwebm-%d.%d.%d.%d, major, minor, build, revision. char* muxing_app_; - uint64 timecode_scale_; + 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 date_utc_; + int64_t date_utc_; // The file position of the duration element. - int64 duration_pos_; + int64_t duration_pos_; LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SegmentInfo); }; @@ -1108,8 +1294,8 @@ kBeforeClusters = 0x1 // Position Cues before Clusters }; - const static uint32 kDefaultDocTypeVersion = 2; - const static uint64 kDefaultMaxClusterDuration = 30000000000ULL; + const static uint32_t kDefaultDocTypeVersion = 2; + const static uint64_t kDefaultMaxClusterDuration = 30000000000ULL; Segment(); ~Segment(); @@ -1123,13 +1309,13 @@ // 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); + 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 AddAudioTrack(int32 sample_rate, int32 channels, int32 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 @@ -1145,7 +1331,7 @@ // 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); + bool AddCuePoint(uint64_t timestamp, uint64_t track); // Adds a frame to be output in the file. Returns true on success. // Inputs: @@ -1155,8 +1341,8 @@ // 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); + 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. @@ -1172,8 +1358,8 @@ // 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); + 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. @@ -1188,10 +1374,11 @@ // 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); + 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. @@ -1204,9 +1391,10 @@ // 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); + 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. @@ -1218,7 +1406,7 @@ // 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); + 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 @@ -1237,7 +1425,7 @@ // 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); + bool CuesTrack(uint64_t track_number); // This will force the muxer to create a new Cluster when the next frame is // added. @@ -1257,11 +1445,17 @@ // 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; + 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 @@ -1274,15 +1468,15 @@ 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) { + 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 max_cluster_duration() const { return max_cluster_duration_; } - void set_max_cluster_size(uint64 max_cluster_size) { + 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 max_cluster_size() const { return 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_; } @@ -1306,7 +1500,7 @@ // 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(); + int64_t MaxOffset(); // Adds the frame to our frame array. bool QueueFrame(Frame* frame); @@ -1318,7 +1512,7 @@ // 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); + bool WriteFramesLessThan(uint64_t timestamp); // Outputs the segment header, Segment Information element, SeekHead element, // and Tracks element to |writer_|. @@ -1332,16 +1526,17 @@ // 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; + 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 timestamp_ns); + 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 track_num, uint64 timestamp_ns, bool key); + 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. @@ -1355,7 +1550,8 @@ // 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); + void MoveCuesBeforeClustersHelper(uint64_t diff, int index, + uint64_t* cue_size); // Seeds the random number generator used to make UIDs. unsigned int seed_; @@ -1394,22 +1590,22 @@ char* chunking_base_name_; // File position offset where the Clusters end. - int64 cluster_end_offset_; + int64_t cluster_end_offset_; // List of clusters. Cluster** cluster_list_; // Number of cluster pointers allocated in the cluster list. - int32 cluster_list_capacity_; + int32_t cluster_list_capacity_; // Number of clusters in the cluster list. - int32 cluster_list_size_; + 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 cues_track_; + uint64_t cues_track_; // Tells the muxer to force a new cluster on the next Block. bool force_new_cluster_; @@ -1421,10 +1617,10 @@ Frame** frames_; // Number of frame pointers allocated in the frame list. - int32 frames_capacity_; + int32_t frames_capacity_; // Number of frames in the frame list. - int32 frames_size_; + int32_t frames_size_; // Flag telling if a video track has been added to the segment. bool has_video_; @@ -1433,23 +1629,23 @@ bool header_written_; // Duration of the last block in nanoseconds. - uint64 last_block_duration_; + uint64_t last_block_duration_; // Last timestamp in nanoseconds added to a cluster. - uint64 last_timestamp_; + uint64_t last_timestamp_; // Last timestamp in nanoseconds by track number added to a cluster. - uint64 last_track_timestamp_[kMaxTrackNumber]; + 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 max_cluster_duration_; + 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 max_cluster_size_; + uint64_t max_cluster_size_; // The mode that segment is in. If set to |kLive| the writer must not // seek backwards. @@ -1462,22 +1658,29 @@ // 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 ebml_header_size_; + int32_t ebml_header_size_; // The file position of the segment's payload. - int64 payload_pos_; + int64_t payload_pos_; // The file position of the element's size. - int64 size_position_; + 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 doc_type_version_; - uint32 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_; @@ -1487,6 +1690,6 @@ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Segment); }; -} // end namespace mkvmuxer +} // namespace mkvmuxer -#endif // MKVMUXER_HPP +#endif // MKVMUXER_MKVMUXER_H_
diff --git a/third_party/libwebm/mkvmuxertypes.hpp b/third_party/libwebm/mkvmuxer/mkvmuxertypes.h similarity index 86% rename from third_party/libwebm/mkvmuxertypes.hpp rename to third_party/libwebm/mkvmuxer/mkvmuxertypes.h index d0fc9fe..e5db121 100644 --- a/third_party/libwebm/mkvmuxertypes.hpp +++ b/third_party/libwebm/mkvmuxer/mkvmuxertypes.h
@@ -6,8 +6,17 @@ // 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 +#ifndef MKVMUXER_MKVMUXERTYPES_H_ +#define MKVMUXER_MKVMUXERTYPES_H_ + +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; +} // namespace mkvmuxer // Copied from Chromium basictypes.h // A macro to disallow the copy constructor and operator= functions @@ -16,15 +25,4 @@ 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 +#endif // MKVMUXER_MKVMUXERTYPES_HPP_
diff --git a/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc b/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc new file mode 100644 index 0000000..3562b8a --- /dev/null +++ b/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc
@@ -0,0 +1,650 @@ +// 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 static_cast<uint64_t>(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 static_cast<uint64_t>(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 static_cast<uint64_t>(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 + uint64_t ebml_size = static_cast<uint64_t>(GetUIntSize(type)); + + // Datasize + ebml_size += + (fixed_size > 0) ? fixed_size : static_cast<uint64_t>(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 = static_cast<uint64_t>(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 = static_cast<uint64_t>(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 = static_cast<uint64_t>(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 = static_cast<uint64_t>(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 = static_cast<uint64_t>(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
diff --git a/third_party/libwebm/mkvmuxer/mkvmuxerutil.h b/third_party/libwebm/mkvmuxer/mkvmuxerutil.h new file mode 100644 index 0000000..0e21a2dc --- /dev/null +++ b/third_party/libwebm/mkvmuxer/mkvmuxerutil.h
@@ -0,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_
diff --git a/third_party/libwebm/mkvwriter.cpp b/third_party/libwebm/mkvmuxer/mkvwriter.cc similarity index 96% rename from third_party/libwebm/mkvwriter.cpp rename to third_party/libwebm/mkvmuxer/mkvwriter.cc index 75d4350..ca48e14 100644 --- a/third_party/libwebm/mkvwriter.cpp +++ b/third_party/libwebm/mkvmuxer/mkvwriter.cc
@@ -6,14 +6,12 @@ // 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" +#include "mkvmuxer/mkvwriter.h" #ifdef _MSC_VER #include <share.h> // for _SH_DENYWR #endif -#include <new> - namespace mkvmuxer { MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
diff --git a/third_party/libwebm/mkvwriter.hpp b/third_party/libwebm/mkvmuxer/mkvwriter.h similarity index 86% rename from third_party/libwebm/mkvwriter.hpp rename to third_party/libwebm/mkvmuxer/mkvwriter.h index 684560c..4227c63 100644 --- a/third_party/libwebm/mkvwriter.hpp +++ b/third_party/libwebm/mkvmuxer/mkvwriter.h
@@ -6,13 +6,13 @@ // 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 +#ifndef MKVMUXER_MKVWRITER_H_ +#define MKVMUXER_MKVWRITER_H_ #include <stdio.h> -#include "mkvmuxer.hpp" -#include "mkvmuxertypes.hpp" +#include "mkvmuxer/mkvmuxer.h" +#include "mkvmuxer/mkvmuxertypes.h" namespace mkvmuxer { @@ -46,6 +46,6 @@ LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter); }; -} // end namespace mkvmuxer +} // namespace mkvmuxer -#endif // MKVWRITER_HPP +#endif // MKVMUXER_MKVWRITER_H_
diff --git a/third_party/libwebm/mkvmuxerutil.cpp b/third_party/libwebm/mkvmuxerutil.cpp deleted file mode 100644 index 27ab15d..0000000 --- a/third_party/libwebm/mkvmuxerutil.cpp +++ /dev/null
@@ -1,629 +0,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; -}
diff --git a/third_party/libwebm/mkvmuxerutil.hpp b/third_party/libwebm/mkvmuxerutil.hpp deleted file mode 100644 index e318576..0000000 --- a/third_party/libwebm/mkvmuxerutil.hpp +++ /dev/null
@@ -1,83 +0,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
diff --git a/third_party/libwebm/mkvparser.cpp b/third_party/libwebm/mkvparser/mkvparser.cc similarity index 91% rename from third_party/libwebm/mkvparser.cpp rename to third_party/libwebm/mkvparser/mkvparser.cc index f2855d5..2180115 100644 --- a/third_party/libwebm/mkvparser.cpp +++ b/third_party/libwebm/mkvparser/mkvparser.cc
@@ -5,8 +5,7 @@ // 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" +#include "mkvparser/mkvparser.h" #if defined(_MSC_VER) && _MSC_VER < 1800 #include <float.h> // _isnan() / _finite() @@ -14,19 +13,18 @@ #endif #include <cassert> +#include <cfloat> #include <climits> #include <cmath> #include <cstring> +#include <memory> #include <new> -#include "webmids.hpp" - -#ifdef _MSC_VER -// Disable MSVC warnings that suggest making code non-portable. -#pragma warning(disable : 4996) -#endif +#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); } @@ -38,8 +36,9 @@ IMkvReader::~IMkvReader() {} -template<typename Type> Type* SafeArrayAlloc(unsigned long long num_elements, - unsigned long long element_size) { +template <typename Type> +Type* SafeArrayAlloc(unsigned long long num_elements, + unsigned long long element_size) { if (num_elements == 0 || element_size == 0) return NULL; @@ -350,9 +349,8 @@ return 0; } -long ParseElementHeader(IMkvReader* pReader, long long& pos, - long long stop, long long& id, - long long& size) { +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; @@ -386,7 +384,7 @@ // pos now designates payload - if (stop >= 0 && pos >= stop) + if (stop >= 0 && pos > stop) return E_FILE_FORMAT_INVALID; return 0; // success @@ -520,7 +518,6 @@ 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; @@ -544,8 +541,10 @@ 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) + 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. @@ -584,7 +583,7 @@ if ((available - pos) < result) return pos + result; - end = pos + result; + const long long end = pos + result; Init(); @@ -599,27 +598,27 @@ if (size == 0) return E_FILE_FORMAT_INVALID; - if (id == mkvmuxer::kMkvEBMLVersion) { + if (id == libwebm::kMkvEBMLVersion) { m_version = UnserializeUInt(pReader, pos, size); if (m_version <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvEBMLReadVersion) { + } else if (id == libwebm::kMkvEBMLReadVersion) { m_readVersion = UnserializeUInt(pReader, pos, size); if (m_readVersion <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvEBMLMaxIDLength) { + } else if (id == libwebm::kMkvEBMLMaxIDLength) { m_maxIdLength = UnserializeUInt(pReader, pos, size); if (m_maxIdLength <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvEBMLMaxSizeLength) { + } else if (id == libwebm::kMkvEBMLMaxSizeLength) { m_maxSizeLength = UnserializeUInt(pReader, pos, size); if (m_maxSizeLength <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvDocType) { + } else if (id == libwebm::kMkvDocType) { if (m_docType) return E_FILE_FORMAT_INVALID; @@ -627,12 +626,12 @@ if (status) // error return status; - } else if (id == mkvmuxer::kMkvDocTypeVersion) { + } else if (id == libwebm::kMkvDocTypeVersion) { m_docTypeVersion = UnserializeUInt(pReader, pos, size); if (m_docTypeVersion <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvDocTypeReadVersion) { + } else if (id == libwebm::kMkvDocTypeReadVersion) { m_docTypeReadVersion = UnserializeUInt(pReader, pos, size); if (m_docTypeReadVersion <= 0) @@ -650,8 +649,8 @@ 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) + if (m_maxIdLength <= 0 || m_maxIdLength > 4 || m_maxSizeLength <= 0 || + m_maxSizeLength > 8) return E_FILE_FORMAT_INVALID; return 0; @@ -786,7 +785,7 @@ // Handle "unknown size" for live streaming of webm files. const long long unknown_size = (1LL << (7 * len)) - 1; - if (id == mkvmuxer::kMkvSegment) { + if (id == libwebm::kMkvSegment) { if (size == unknown_size) size = -1; @@ -878,7 +877,7 @@ if (id < 0) return E_FILE_FORMAT_INVALID; - if (id == mkvmuxer::kMkvCluster) + if (id == libwebm::kMkvCluster) break; pos += len; // consume ID @@ -930,7 +929,7 @@ if ((pos + size) > available) return pos + size; - if (id == mkvmuxer::kMkvInfo) { + if (id == libwebm::kMkvInfo) { if (m_pInfo) return E_FILE_FORMAT_INVALID; @@ -944,7 +943,7 @@ if (status) return status; - } else if (id == mkvmuxer::kMkvTracks) { + } else if (id == libwebm::kMkvTracks) { if (m_pTracks) return E_FILE_FORMAT_INVALID; @@ -958,7 +957,7 @@ if (status) return status; - } else if (id == mkvmuxer::kMkvCues) { + } else if (id == libwebm::kMkvCues) { if (m_pCues == NULL) { m_pCues = new (std::nothrow) Cues(this, pos, size, element_start, element_size); @@ -966,7 +965,7 @@ if (m_pCues == NULL) return -1; } - } else if (id == mkvmuxer::kMkvSeekHead) { + } else if (id == libwebm::kMkvSeekHead) { if (m_pSeekHead == NULL) { m_pSeekHead = new (std::nothrow) SeekHead(this, pos, size, element_start, element_size); @@ -979,7 +978,7 @@ if (status) return status; } - } else if (id == mkvmuxer::kMkvChapters) { + } else if (id == libwebm::kMkvChapters) { if (m_pChapters == NULL) { m_pChapters = new (std::nothrow) Chapters(this, pos, size, element_start, element_size); @@ -992,7 +991,7 @@ if (status) return status; } - } else if (id == mkvmuxer::kMkvTags) { + } else if (id == libwebm::kMkvTags) { if (m_pTags == NULL) { m_pTags = new (std::nothrow) Tags(this, pos, size, element_start, element_size); @@ -1131,7 +1130,7 @@ return E_FILE_FORMAT_INVALID; } - if (id == mkvmuxer::kMkvCues) { + if (id == libwebm::kMkvCues) { if (size == unknown_size) { // Cues element of unknown size: Not supported. return E_FILE_FORMAT_INVALID; @@ -1149,7 +1148,7 @@ continue; } - if (id != mkvmuxer::kMkvCluster) { + 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. @@ -1466,7 +1465,7 @@ return E_FILE_FORMAT_INVALID; for (;;) { - const int status = LoadCluster(); + const long status = LoadCluster(); if (status < 0) // error return status; @@ -1512,9 +1511,9 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvSeek) + if (id == libwebm::kMkvSeek) ++entry_count; - else if (id == mkvmuxer::kMkvVoid) + else if (id == libwebm::kMkvVoid) ++void_element_count; pos += size; // consume payload @@ -1553,14 +1552,14 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvSeek) { + 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 == mkvmuxer::kMkvVoid) { + } else if (id == libwebm::kMkvVoid) { VoidElement& e = *pVoidElement++; e.element_start = idpos; @@ -1664,7 +1663,7 @@ const long long id = ReadID(m_pReader, idpos, len); - if (id != mkvmuxer::kMkvCues) + if (id != libwebm::kMkvCues) return E_FILE_FORMAT_INVALID; pos += len; // consume ID @@ -1746,7 +1745,7 @@ if (seekIdId < 0) return false; - if (seekIdId != mkvmuxer::kMkvSeekID) + if (seekIdId != libwebm::kMkvSeekID) return false; if ((pos + len) > stop) @@ -1790,7 +1789,7 @@ const long long seekPosId = ReadID(pReader, pos, len); - if (seekPosId != mkvmuxer::kMkvSeekPosition) + if (seekPosId != libwebm::kMkvSeekPosition) return false; if ((pos + len) > stop) @@ -1900,7 +1899,7 @@ return false; } - if (id == mkvmuxer::kMkvCuePoint) { + if (id == libwebm::kMkvCuePoint) { if (!PreloadCuePoint(cue_points_size, idpos)) return false; } @@ -1975,7 +1974,7 @@ if ((m_pos + size) > stop) return false; - if (id != mkvmuxer::kMkvCuePoint) { + if (id != libwebm::kMkvCuePoint) { m_pos += size; // consume payload if (m_pos > stop) return false; @@ -2105,8 +2104,8 @@ } const CuePoint* Cues::GetNext(const CuePoint* pCurr) const { - if (pCurr == NULL || pCurr->GetTimeCode() < 0 || - m_cue_points == NULL || m_count < 1) { + if (pCurr == NULL || pCurr->GetTimeCode() < 0 || m_cue_points == NULL || + m_count < 1) { return NULL; } @@ -2286,7 +2285,7 @@ long len; const long long id = ReadID(pReader, pos_, len); - if (id != mkvmuxer::kMkvCuePoint) + if (id != libwebm::kMkvCuePoint) return false; pos_ += len; // consume ID @@ -2326,10 +2325,10 @@ return false; } - if (id == mkvmuxer::kMkvCueTime) + if (id == libwebm::kMkvCueTime) m_timecode = UnserializeUInt(pReader, pos, size); - else if (id == mkvmuxer::kMkvCueTrackPositions) + else if (id == libwebm::kMkvCueTrackPositions) ++m_track_positions_count; pos += size; // consume payload @@ -2368,7 +2367,7 @@ pos += len; // consume Size field assert((pos + size) <= stop); - if (id == mkvmuxer::kMkvCueTrackPositions) { + if (id == libwebm::kMkvCueTrackPositions) { TrackPosition& tp = *p++; if (!tp.Parse(pReader, pos, size)) { return false; @@ -2417,11 +2416,11 @@ return false; } - if (id == mkvmuxer::kMkvCueTrack) + if (id == libwebm::kMkvCueTrack) m_track = UnserializeUInt(pReader, pos, size); - else if (id == mkvmuxer::kMkvCueClusterPosition) + else if (id == libwebm::kMkvCueClusterPosition) m_pos = UnserializeUInt(pReader, pos, size); - else if (id == mkvmuxer::kMkvCueBlockNumber) + else if (id == libwebm::kMkvCueBlockNumber) m_block = UnserializeUInt(pReader, pos, size); pos += size; // consume payload @@ -2555,7 +2554,7 @@ return NULL; const long long id = ReadID(m_pReader, pos, len); - if (id != mkvmuxer::kMkvCluster) + if (id != libwebm::kMkvCluster) return NULL; pos += len; // consume ID @@ -2612,7 +2611,7 @@ if (size == 0) // weird continue; - if (id == mkvmuxer::kMkvCluster) { + if (id == libwebm::kMkvCluster) { const long long off_next_ = idpos - m_start; long long pos_; @@ -2762,7 +2761,7 @@ const long long id = ReadUInt(m_pReader, pos, len); - if (id != mkvmuxer::kMkvCluster) + if (id != libwebm::kMkvCluster) return -1; pos += len; // consume ID @@ -2927,7 +2926,7 @@ return E_FILE_FORMAT_INVALID; } - if (id == mkvmuxer::kMkvCues) { + if (id == libwebm::kMkvCues) { if (size == unknown_size) return E_FILE_FORMAT_INVALID; @@ -2953,7 +2952,7 @@ continue; } - if (id != mkvmuxer::kMkvCluster) { // not a Cluster ID + if (id != libwebm::kMkvCluster) { // not a Cluster ID if (size == unknown_size) return E_FILE_FORMAT_INVALID; @@ -3091,7 +3090,7 @@ // that we have exhausted the sub-element's inside the cluster // whose ID we parsed earlier. - if (id == mkvmuxer::kMkvCluster || id == mkvmuxer::kMkvCues) + if (id == libwebm::kMkvCluster || id == libwebm::kMkvCues) break; pos += len; // consume ID (of sub-element) @@ -3259,7 +3258,7 @@ if (size == 0) // weird continue; - if (id == mkvmuxer::kMkvEditionEntry) { + if (id == libwebm::kMkvEditionEntry) { status = ParseEdition(pos, size); if (status < 0) // error @@ -3375,7 +3374,7 @@ if (size == 0) continue; - if (id == mkvmuxer::kMkvChapterAtom) { + if (id == libwebm::kMkvChapterAtom) { status = ParseAtom(pReader, pos, size); if (status < 0) // error @@ -3508,17 +3507,17 @@ if (size == 0) // 0 length payload, skip. continue; - if (id == mkvmuxer::kMkvChapterDisplay) { + if (id == libwebm::kMkvChapterDisplay) { status = ParseDisplay(pReader, pos, size); if (status < 0) // error return status; - } else if (id == mkvmuxer::kMkvChapterStringUID) { + } else if (id == libwebm::kMkvChapterStringUID) { status = UnserializeString(pReader, pos, size, m_string_uid); if (status < 0) // error return status; - } else if (id == mkvmuxer::kMkvChapterUID) { + } else if (id == libwebm::kMkvChapterUID) { long long val; status = UnserializeInt(pReader, pos, size, val); @@ -3526,14 +3525,14 @@ return status; m_uid = static_cast<unsigned long long>(val); - } else if (id == mkvmuxer::kMkvChapterTimeStart) { + } 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 == mkvmuxer::kMkvChapterTimeEnd) { + } else if (id == libwebm::kMkvChapterTimeEnd) { const long long val = UnserializeUInt(pReader, pos, size); if (val < 0) // error @@ -3661,17 +3660,17 @@ if (size == 0) // No payload. continue; - if (id == mkvmuxer::kMkvChapString) { + if (id == libwebm::kMkvChapString) { status = UnserializeString(pReader, pos, size, m_string); if (status) return status; - } else if (id == mkvmuxer::kMkvChapLanguage) { + } else if (id == libwebm::kMkvChapLanguage) { status = UnserializeString(pReader, pos, size, m_language); if (status) return status; - } else if (id == mkvmuxer::kMkvChapCountry) { + } else if (id == libwebm::kMkvChapCountry) { status = UnserializeString(pReader, pos, size, m_country); if (status) @@ -3724,7 +3723,7 @@ if (size == 0) // 0 length tag, read another continue; - if (id == mkvmuxer::kMkvTag) { + if (id == libwebm::kMkvTag) { status = ParseTag(pos, size); if (status < 0) @@ -3840,7 +3839,7 @@ if (size == 0) // 0 length tag, read another continue; - if (id == mkvmuxer::kMkvSimpleTag) { + if (id == libwebm::kMkvSimpleTag) { status = ParseSimpleTag(pReader, pos, size); if (status < 0) @@ -3931,12 +3930,12 @@ if (size == 0) // weird continue; - if (id == mkvmuxer::kMkvTagName) { + if (id == libwebm::kMkvTagName) { status = UnserializeString(pReader, pos, size, m_tag_name); if (status) return status; - } else if (id == mkvmuxer::kMkvTagString) { + } else if (id == libwebm::kMkvTagString) { status = UnserializeString(pReader, pos, size, m_tag_string); if (status) @@ -3996,12 +3995,12 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvTimecodeScale) { + if (id == libwebm::kMkvTimecodeScale) { m_timecodeScale = UnserializeUInt(pReader, pos, size); if (m_timecodeScale <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvDuration) { + } else if (id == libwebm::kMkvDuration) { const long status = UnserializeFloat(pReader, pos, size, m_duration); if (status < 0) @@ -4009,19 +4008,19 @@ if (m_duration < 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvMuxingApp) { + } else if (id == libwebm::kMkvMuxingApp) { const long status = UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8); if (status) return status; - } else if (id == mkvmuxer::kMkvWritingApp) { + } else if (id == libwebm::kMkvWritingApp) { const long status = UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8); if (status) return status; - } else if (id == mkvmuxer::kMkvTitle) { + } else if (id == libwebm::kMkvTitle) { const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8); if (status) @@ -4176,7 +4175,7 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvAESSettingsCipherMode) { + if (id == libwebm::kMkvAESSettingsCipherMode) { aes->cipher_mode = UnserializeUInt(pReader, pos, size); if (aes->cipher_mode != 1) return E_FILE_FORMAT_INVALID; @@ -4207,10 +4206,10 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvContentCompression) + if (id == libwebm::kMkvContentCompression) ++compression_count; - if (id == mkvmuxer::kMkvContentEncryption) + if (id == libwebm::kMkvContentEncryption) ++encryption_count; pos += size; // consume payload @@ -4246,15 +4245,15 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvContentEncodingOrder) { + if (id == libwebm::kMkvContentEncodingOrder) { encoding_order_ = UnserializeUInt(pReader, pos, size); - } else if (id == mkvmuxer::kMkvContentEncodingScope) { + } else if (id == libwebm::kMkvContentEncodingScope) { encoding_scope_ = UnserializeUInt(pReader, pos, size); if (encoding_scope_ < 1) return -1; - } else if (id == mkvmuxer::kMkvContentEncodingType) { + } else if (id == libwebm::kMkvContentEncodingType) { encoding_type_ = UnserializeUInt(pReader, pos, size); - } else if (id == mkvmuxer::kMkvContentCompression) { + } else if (id == libwebm::kMkvContentCompression) { ContentCompression* const compression = new (std::nothrow) ContentCompression(); if (!compression) @@ -4266,7 +4265,7 @@ return status; } *compression_entries_end_++ = compression; - } else if (id == mkvmuxer::kMkvContentEncryption) { + } else if (id == libwebm::kMkvContentEncryption) { ContentEncryption* const encryption = new (std::nothrow) ContentEncryption(); if (!encryption) @@ -4307,13 +4306,13 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvContentCompAlgo) { + 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 == mkvmuxer::kMkvContentCompSettings) { + } else if (id == libwebm::kMkvContentCompSettings) { if (size <= 0) return E_FILE_FORMAT_INVALID; @@ -4360,11 +4359,11 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvContentEncAlgo) { + if (id == libwebm::kMkvContentEncAlgo) { encryption->algo = UnserializeUInt(pReader, pos, size); if (encryption->algo != 5) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvContentEncKeyID) { + } else if (id == libwebm::kMkvContentEncKeyID) { delete[] encryption->key_id; encryption->key_id = NULL; encryption->key_id_len = 0; @@ -4386,7 +4385,7 @@ encryption->key_id = buf; encryption->key_id_len = buflen; - } else if (id == mkvmuxer::kMkvContentSignature) { + } else if (id == libwebm::kMkvContentSignature) { delete[] encryption->signature; encryption->signature = NULL; encryption->signature_len = 0; @@ -4408,7 +4407,7 @@ encryption->signature = buf; encryption->signature_len = buflen; - } else if (id == mkvmuxer::kMkvContentSigKeyID) { + } else if (id == libwebm::kMkvContentSigKeyID) { delete[] encryption->sig_key_id; encryption->sig_key_id = NULL; encryption->sig_key_id_len = 0; @@ -4430,11 +4429,11 @@ encryption->sig_key_id = buf; encryption->sig_key_id_len = buflen; - } else if (id == mkvmuxer::kMkvContentSigAlgo) { + } else if (id == libwebm::kMkvContentSigAlgo) { encryption->sig_algo = UnserializeUInt(pReader, pos, size); - } else if (id == mkvmuxer::kMkvContentSigHashAlgo) { + } else if (id == libwebm::kMkvContentSigHashAlgo) { encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size); - } else if (id == mkvmuxer::kMkvContentEncAESSettings) { + } else if (id == libwebm::kMkvContentEncAESSettings) { const long status = ParseContentEncAESSettingsEntry( pos, size, pReader, &encryption->aes_settings); if (status) @@ -4921,7 +4920,7 @@ return status; // pos now designates start of element - if (id == mkvmuxer::kMkvContentEncoding) + if (id == libwebm::kMkvContentEncoding) ++count; pos += size; // consume payload @@ -4946,7 +4945,7 @@ return status; // pos now designates start of element - if (id == mkvmuxer::kMkvContentEncoding) { + if (id == libwebm::kMkvContentEncoding) { ContentEncoding* const content_encoding = new (std::nothrow) ContentEncoding(); if (!content_encoding) @@ -4978,9 +4977,222 @@ 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) {} + : 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, @@ -5011,6 +5223,8 @@ const long long stop = pos + s.size; + Colour* colour = NULL; + while (pos < stop) { long long id, size; @@ -5019,37 +5233,37 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvPixelWidth) { + if (id == libwebm::kMkvPixelWidth) { width = UnserializeUInt(pReader, pos, size); if (width <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvPixelHeight) { + } else if (id == libwebm::kMkvPixelHeight) { height = UnserializeUInt(pReader, pos, size); if (height <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvDisplayWidth) { + } else if (id == libwebm::kMkvDisplayWidth) { display_width = UnserializeUInt(pReader, pos, size); if (display_width <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvDisplayHeight) { + } else if (id == libwebm::kMkvDisplayHeight) { display_height = UnserializeUInt(pReader, pos, size); if (display_height <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvDisplayUnit) { + } else if (id == libwebm::kMkvDisplayUnit) { display_unit = UnserializeUInt(pReader, pos, size); if (display_unit < 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvStereoMode) { + } else if (id == libwebm::kMkvStereoMode) { stereo_mode = UnserializeUInt(pReader, pos, size); if (stereo_mode < 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvFrameRate) { + } else if (id == libwebm::kMkvFrameRate) { const long status = UnserializeFloat(pReader, pos, size, rate); if (status < 0) @@ -5057,6 +5271,9 @@ 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 @@ -5087,6 +5304,7 @@ 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 @@ -5185,6 +5403,8 @@ 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; } @@ -5239,7 +5459,7 @@ if (status < 0) // error return status; - if (id == mkvmuxer::kMkvSamplingFrequency) { + if (id == libwebm::kMkvSamplingFrequency) { status = UnserializeFloat(pReader, pos, size, rate); if (status < 0) @@ -5247,12 +5467,12 @@ if (rate <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvChannels) { + } else if (id == libwebm::kMkvChannels) { channels = UnserializeUInt(pReader, pos, size); if (channels <= 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvBitDepth) { + } else if (id == libwebm::kMkvBitDepth) { bit_depth = UnserializeUInt(pReader, pos, size); if (bit_depth <= 0) @@ -5325,7 +5545,7 @@ if (size == 0) // weird continue; - if (id == mkvmuxer::kMkvTrackEntry) + if (id == libwebm::kMkvTrackEntry) ++count; pos += size; // consume payload @@ -5367,7 +5587,7 @@ const long long element_size = payload_stop - element_start; - if (id == mkvmuxer::kMkvTrackEntry) { + if (id == libwebm::kMkvTrackEntry) { Track*& pTrack = *m_trackEntriesEnd; pTrack = NULL; @@ -5443,16 +5663,16 @@ const long long start = pos; - if (id == mkvmuxer::kMkvVideo) { + if (id == libwebm::kMkvVideo) { v.start = start; v.size = size; - } else if (id == mkvmuxer::kMkvAudio) { + } else if (id == libwebm::kMkvAudio) { a.start = start; a.size = size; - } else if (id == mkvmuxer::kMkvContentEncodings) { + } else if (id == libwebm::kMkvContentEncodings) { e.start = start; e.size = size; - } else if (id == mkvmuxer::kMkvTrackUID) { + } else if (id == libwebm::kMkvTrackUID) { if (size > 8) return E_FILE_FORMAT_INVALID; @@ -5474,49 +5694,49 @@ ++pos_; } - } else if (id == mkvmuxer::kMkvTrackNumber) { + } 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 == mkvmuxer::kMkvTrackType) { + } 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 == mkvmuxer::kMkvName) { + } else if (id == libwebm::kMkvName) { const long status = UnserializeString(pReader, pos, size, info.nameAsUTF8); if (status) return status; - } else if (id == mkvmuxer::kMkvLanguage) { + } else if (id == libwebm::kMkvLanguage) { const long status = UnserializeString(pReader, pos, size, info.language); if (status) return status; - } else if (id == mkvmuxer::kMkvDefaultDuration) { + } 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 == mkvmuxer::kMkvCodecID) { + } else if (id == libwebm::kMkvCodecID) { const long status = UnserializeString(pReader, pos, size, info.codecId); if (status) return status; - } else if (id == mkvmuxer::kMkvFlagLacing) { + } else if (id == libwebm::kMkvFlagLacing) { lacing = UnserializeUInt(pReader, pos, size); if ((lacing < 0) || (lacing > 1)) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvCodecPrivate) { + } else if (id == libwebm::kMkvCodecPrivate) { delete[] info.codecPrivate; info.codecPrivate = NULL; info.codecPrivateSize = 0; @@ -5539,15 +5759,15 @@ info.codecPrivate = buf; info.codecPrivateSize = buflen; } - } else if (id == mkvmuxer::kMkvCodecName) { + } else if (id == libwebm::kMkvCodecName) { const long status = UnserializeString(pReader, pos, size, info.codecNameAsUTF8); if (status) return status; - } else if (id == mkvmuxer::kMkvCodecDelay) { + } else if (id == libwebm::kMkvCodecDelay) { info.codecDelay = UnserializeUInt(pReader, pos, size); - } else if (id == mkvmuxer::kMkvSeekPreRoll) { + } else if (id == libwebm::kMkvSeekPreRoll) { info.seekPreRoll = UnserializeUInt(pReader, pos, size); } @@ -5730,7 +5950,7 @@ if (id_ < 0) // error return static_cast<long>(id_); - if (id_ != mkvmuxer::kMkvCluster) + if (id_ != libwebm::kMkvCluster) return E_FILE_FORMAT_INVALID; pos += len; // consume id @@ -5812,10 +6032,10 @@ // that we have exhausted the sub-element's inside the cluster // whose ID we parsed earlier. - if (id == mkvmuxer::kMkvCluster) + if (id == libwebm::kMkvCluster) break; - if (id == mkvmuxer::kMkvCues) + if (id == libwebm::kMkvCues) break; pos += len; // consume ID field @@ -5864,7 +6084,7 @@ if ((cluster_stop >= 0) && ((pos + size) > cluster_stop)) return E_FILE_FORMAT_INVALID; - if (id == mkvmuxer::kMkvTimecode) { + if (id == libwebm::kMkvTimecode) { len = static_cast<long>(size); if ((pos + size) > avail) @@ -5879,10 +6099,10 @@ if (bBlock) break; - } else if (id == mkvmuxer::kMkvBlockGroup) { + } else if (id == libwebm::kMkvBlockGroup) { bBlock = true; break; - } else if (id == mkvmuxer::kMkvSimpleBlock) { + } else if (id == libwebm::kMkvSimpleBlock) { bBlock = true; break; } @@ -5980,7 +6200,7 @@ // that we have exhausted the sub-element's inside the cluster // whose ID we parsed earlier. - if ((id == mkvmuxer::kMkvCluster) || (id == mkvmuxer::kMkvCues)) { + if ((id == libwebm::kMkvCluster) || (id == libwebm::kMkvCues)) { if (m_element_size < 0) m_element_size = pos - m_element_start; @@ -6035,8 +6255,7 @@ if (cluster_stop >= 0) { if (block_stop > cluster_stop) { - if (id == mkvmuxer::kMkvBlockGroup || - id == mkvmuxer::kMkvSimpleBlock) { + if (id == libwebm::kMkvBlockGroup || id == libwebm::kMkvSimpleBlock) { return E_FILE_FORMAT_INVALID; } @@ -6054,10 +6273,10 @@ Cluster* const this_ = const_cast<Cluster*>(this); - if (id == mkvmuxer::kMkvBlockGroup) + if (id == libwebm::kMkvBlockGroup) return this_->ParseBlockGroup(size, pos, len); - if (id == mkvmuxer::kMkvSimpleBlock) + if (id == libwebm::kMkvSimpleBlock) return this_->ParseSimpleBlock(size, pos, len); pos += size; // consume payload @@ -6188,8 +6407,7 @@ return E_BUFFER_NOT_FULL; } - status = CreateBlock(mkvmuxer::kMkvSimpleBlock, - block_start, block_size, + status = CreateBlock(libwebm::kMkvSimpleBlock, block_start, block_size, 0); // DiscardPadding if (status != 0) @@ -6299,14 +6517,14 @@ if (size == unknown_size) return E_FILE_FORMAT_INVALID; - if (id == mkvmuxer::kMkvDiscardPadding) { + if (id == libwebm::kMkvDiscardPadding) { status = UnserializeInt(pReader, pos, size, discard_padding); if (status < 0) // error return status; } - if (id != mkvmuxer::kMkvBlock) { + if (id != libwebm::kMkvBlock) { pos += size; // consume sub-part of block group if (pos > payload_stop) @@ -6399,8 +6617,8 @@ if (pos != payload_stop) return E_FILE_FORMAT_INVALID; - status = CreateBlock(mkvmuxer::kMkvBlockGroup, - payload_start, payload_size, discard_padding); + status = CreateBlock(libwebm::kMkvBlockGroup, payload_start, payload_size, + discard_padding); if (status != 0) return status; @@ -6565,7 +6783,7 @@ if (id < 0) // error return static_cast<long>(id); - if (id != mkvmuxer::kMkvCluster) + if (id != libwebm::kMkvCluster) return E_PARSE_FAILED; pos += len; // consume Cluster ID field @@ -6653,10 +6871,10 @@ // that we have exhausted the sub-element's inside the cluster // whose ID we parsed earlier. - if (id == mkvmuxer::kMkvCluster) + if (id == libwebm::kMkvCluster) return 0; // no entries found - if (id == mkvmuxer::kMkvCues) + if (id == libwebm::kMkvCues) return 0; // no entries found pos += len; // consume id field @@ -6708,10 +6926,10 @@ if ((cluster_stop >= 0) && ((pos + size) > cluster_stop)) return E_FILE_FORMAT_INVALID; - if (id == mkvmuxer::kMkvBlockGroup) + if (id == libwebm::kMkvBlockGroup) return 1; // have at least one entry - if (id == mkvmuxer::kMkvSimpleBlock) + if (id == libwebm::kMkvSimpleBlock) return 1; // have at least one entry pos += size; // consume payload @@ -6786,7 +7004,7 @@ 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) + if (id != libwebm::kMkvBlockGroup && id != libwebm::kMkvSimpleBlock) return E_PARSE_FAILED; if (m_entries_count < 0) { // haven't parsed anything yet @@ -6826,7 +7044,7 @@ } } - if (id == mkvmuxer::kMkvBlockGroup) + if (id == libwebm::kMkvBlockGroup) return CreateBlockGroup(pos, size, discard_padding); else return CreateSimpleBlock(pos, size); @@ -6871,12 +7089,12 @@ pos += len; // consume size - if (id == mkvmuxer::kMkvBlock) { + if (id == libwebm::kMkvBlock) { if (bpos < 0) { // Block ID bpos = pos; bsize = size; } - } else if (id == mkvmuxer::kMkvBlockDuration) { + } else if (id == libwebm::kMkvBlockDuration) { if (size > 8) return E_FILE_FORMAT_INVALID; @@ -6884,7 +7102,7 @@ if (duration < 0) return E_FILE_FORMAT_INVALID; - } else if (id == mkvmuxer::kMkvReferenceBlock) { + } else if (id == libwebm::kMkvReferenceBlock) { if (size > 8 || size <= 0) return E_FILE_FORMAT_INVALID; const long size_ = static_cast<long>(size); @@ -7231,7 +7449,6 @@ 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; } @@ -7555,7 +7772,6 @@ if (pf >= pf_end) return E_FILE_FORMAT_INVALID; - const Frame& prev = *pf++; assert(prev.len == frame_size); if (prev.len != frame_size) @@ -7581,7 +7797,7 @@ if (pos > stop) return E_FILE_FORMAT_INVALID; - const int exp = 7 * len - 1; + const long exp = 7 * len - 1; const long long bias = (1LL << exp) - 1LL; const long long delta_size = delta_size_ - bias; @@ -7721,4 +7937,4 @@ long long Block::GetDiscardPadding() const { return m_discard_padding; } -} // end namespace mkvparser +} // namespace mkvparser
diff --git a/third_party/libwebm/mkvparser.hpp b/third_party/libwebm/mkvparser/mkvparser.h similarity index 89% rename from third_party/libwebm/mkvparser.hpp rename to third_party/libwebm/mkvparser/mkvparser.h index 75ef69d..42e6e88 100644 --- a/third_party/libwebm/mkvparser.hpp +++ b/third_party/libwebm/mkvparser/mkvparser.h
@@ -5,13 +5,10 @@ // 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 +#ifndef MKVPARSER_MKVPARSER_H_ +#define MKVPARSER_MKVPARSER_H_ #include <cstddef> -#include <cstdio> -#include <cstdlib> namespace mkvparser { @@ -28,8 +25,9 @@ virtual ~IMkvReader(); }; -template<typename Type> Type* SafeArrayAlloc(unsigned long long num_elements, - unsigned long long element_size); +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); @@ -128,7 +126,7 @@ public: virtual ~BlockEntry(); - bool EOS() const; + bool EOS() const { return (GetKind() == kBlockEOS); } const Cluster* GetCluster() const; long GetIndex() const; virtual const Block* GetBlock() const = 0; @@ -391,6 +389,90 @@ 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&); @@ -398,6 +480,7 @@ 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*&); @@ -412,6 +495,8 @@ 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; @@ -421,6 +506,8 @@ long long m_stereo_mode; double m_rate; + + Colour* m_colour; }; class AudioTrack : public Track { @@ -1013,7 +1100,7 @@ const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&); }; -} // end namespace mkvparser +} // namespace mkvparser inline long mkvparser::Segment::LoadCluster() { long long pos; @@ -1022,4 +1109,4 @@ return LoadCluster(pos, size); } -#endif // MKVPARSER_HPP +#endif // MKVPARSER_MKVPARSER_H_
diff --git a/third_party/libwebm/mkvreader.cpp b/third_party/libwebm/mkvparser/mkvreader.cc similarity index 96% rename from third_party/libwebm/mkvreader.cpp rename to third_party/libwebm/mkvparser/mkvreader.cc index eaf9e0a..9f90d8c 100644 --- a/third_party/libwebm/mkvreader.cpp +++ b/third_party/libwebm/mkvparser/mkvreader.cc
@@ -5,8 +5,7 @@ // 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 "mkvparser/mkvreader.h" #include <cassert> @@ -129,4 +128,4 @@ return 0; // success } -} // end namespace mkvparser +} // namespace mkvparser \ No newline at end of file
diff --git a/third_party/libwebm/mkvreader.hpp b/third_party/libwebm/mkvparser/mkvreader.h similarity index 86% rename from third_party/libwebm/mkvreader.hpp rename to third_party/libwebm/mkvparser/mkvreader.h index 82ebad5..9831ecf 100644 --- a/third_party/libwebm/mkvreader.hpp +++ b/third_party/libwebm/mkvparser/mkvreader.h
@@ -5,13 +5,13 @@ // 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_ -#ifndef MKVREADER_HPP -#define MKVREADER_HPP - -#include "mkvparser.hpp" #include <cstdio> +#include "mkvparser/mkvparser.h" + namespace mkvparser { class MkvReader : public IMkvReader { @@ -40,6 +40,6 @@ bool reader_owns_file_; }; -} // end namespace mkvparser +} // namespace mkvparser -#endif // MKVREADER_HPP +#endif // MKVPARSER_MKVREADER_H_
diff --git a/webmdec.cc b/webmdec.cc index c4539a0..81150aa 100644 --- 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 {
diff --git a/webmenc.cc b/webmenc.cc index d41e700..caf4391 100644 --- 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;