Merge remote-tracking branch 'origin/upstream-master' am: 2efb1cd57a

Original change: https://android-review.googlesource.com/c/platform/external/libwebm/+/1504154

Change-Id: I1edc0db1e552c633cc882dd16570be02ef2b90c3
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..7ce4e92
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,152 @@
+---
+Language:        Cpp
+# BasedOnStyle:  Google
+# Generated with clang-format 7.0.1
+AccessModifierOffset: -1
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: true
+AlignOperands:   true
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass:      false
+  AfterControlStatement: false
+  AfterEnum:       false
+  AfterFunction:   false
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  AfterExternBlock: false
+  BeforeCatch:     false
+  BeforeElse:      false
+  IndentBraces:    false
+  SplitEmptyFunction: true
+  SplitEmptyRecord: true
+  SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeTernaryOperators: false
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeCategories:
+FixNamespaceComments: true
+ForEachMacros:
+  - foreach
+  - Q_FOREACH
+  - BOOST_FOREACH
+IncludeBlocks:   Preserve
+IncludeCategories:
+   - Regex:           '^<ext/.*\.h>'
+     Priority:        2
+   - Regex:           '^<.*\.h>'
+     Priority:        1
+   - Regex:           '^<.*'
+     Priority:        2
+   - Regex:           '.*'
+     Priority:        3
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IndentCaseLabels: true
+IndentPPDirectives: None
+IndentWidth:     2
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Never
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Left
+RawStringFormats:
+  - Language:        Cpp
+    Delimiters:
+      - cc
+      - CC
+      - cpp
+      - Cpp
+      - CPP
+      - 'c++'
+      - 'C++'
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
+  - Language:        TextProto
+    Delimiters:
+      - pb
+      - PB
+      - proto
+      - PROTO
+    EnclosingFunctions:
+      - EqualsProto
+      - EquivToProto
+      - PARSE_PARTIAL_TEXT_PROTO
+      - PARSE_TEST_PROTO
+      - PARSE_TEXT_PROTO
+      - ParseTextOrDie
+      - ParseTextProtoOrDie
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
+ReflowComments:  true
+SortIncludes:    true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Auto
+TabWidth:        8
+UseTab:          Never
+...
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..2b5cda5
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,6 @@
+*.sln           eol=crlf
+*.vcproj        eol=crlf
+*.vsprops       eol=crlf
+*.vcxproj       eol=crlf
+*.mkv           -text -diff
+*.webm          -text -diff
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2abbaa6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+*.mkv
+*.MKV
+core
+*.a
+*.d
+*.so*
+*.o
+*~
+*.swp
+*.ncb
+*.user
+*.suo
+*.exe
+/*.webm
+Debug
+Release
+*.sdf
+*.opensdf
+ipch
+dumpvtt
+mkvmuxer_sample
+mkvparser_sample
+vttdemux
+mkvmuxer_tests
+mkvparser_tests
+webm_info
+webm2pes
+webm2pes_tests
+webm2ts
+vp9_header_parser_tests
+vp9_level_stats_tests
+Makefile
+CMakeFiles
+CMakeCache.txt
+*.cmake
diff --git a/AUTHORS.TXT b/AUTHORS.TXT
new file mode 100644
index 0000000..9686ac1
--- /dev/null
+++ b/AUTHORS.TXT
@@ -0,0 +1,4 @@
+# Names should be added to this file like so:
+# Name or Organization <email address>
+
+Google Inc.
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..b46ba10
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libwebm
+LOCAL_CPPFLAGS:=-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
+LOCAL_CPPFLAGS+=-D__STDC_LIMIT_MACROS -std=c++11
+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/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9fa5a53
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,452 @@
+##  Copyright (c) 2015 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.
+cmake_minimum_required(VERSION 3.2)
+project(LIBWEBM CXX)
+
+include(GNUInstallDirs)
+include("${CMAKE_CURRENT_SOURCE_DIR}/build/cxx_flags.cmake")
+
+if (NOT BUILD_SHARED_LIBS)
+  include("${CMAKE_CURRENT_SOURCE_DIR}/build/msvc_runtime.cmake")
+endif ()
+
+set(LIBWEBM_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+
+# Build/test configuration flags.
+option(ENABLE_WEBMTS "Enables WebM PES/TS support." ON)
+option(ENABLE_WEBMINFO "Enables building webm_info." ON)
+option(ENABLE_TESTS "Enables tests." OFF)
+option(ENABLE_IWYU "Enables include-what-you-use support." OFF)
+option(ENABLE_WERROR "Enable warnings as errors." OFF)
+option(ENABLE_WEBM_PARSER "Enables new parser API." OFF)
+
+if(WIN32)
+  require_cxx_flag_nomsvc("-std=gnu++11")
+else()
+  require_cxx_flag_nomsvc("-std=c++11")
+endif()
+
+add_cxx_preproc_definition("__STDC_CONSTANT_MACROS")
+add_cxx_preproc_definition("__STDC_FORMAT_MACROS")
+add_cxx_preproc_definition("__STDC_LIMIT_MACROS")
+
+# Set up compiler flags and build properties.
+include_directories("${LIBWEBM_SRC_DIR}")
+
+if (MSVC)
+  add_cxx_flag_if_supported("/W4")
+  # Disable MSVC warnings that suggest making code non-portable.
+  add_cxx_flag_if_supported("/wd4996")
+  if (ENABLE_WERROR)
+    add_cxx_flag_if_supported("/WX")
+  endif ()
+else ()
+  add_cxx_flag_if_supported("-Wall")
+  add_cxx_flag_if_supported("-Wextra")
+  add_cxx_flag_if_supported("-Wnarrowing")
+  add_cxx_flag_if_supported("-Wno-deprecated")
+  add_cxx_flag_if_supported("-Wshorten-64-to-32")
+  if (ENABLE_WERROR)
+    add_cxx_flag_if_supported("-Werror")
+  endif ()
+endif ()
+
+# Source list variables.
+set(dumpvtt_sources "${LIBWEBM_SRC_DIR}/dumpvtt.cc")
+
+set(libwebm_common_sources
+    "${LIBWEBM_SRC_DIR}/common/file_util.cc"
+    "${LIBWEBM_SRC_DIR}/common/file_util.h"
+    "${LIBWEBM_SRC_DIR}/common/hdr_util.cc"
+    "${LIBWEBM_SRC_DIR}/common/hdr_util.h"
+    "${LIBWEBM_SRC_DIR}/common/webmids.h")
+
+set(mkvmuxer_sources
+    "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxer.cc"
+    "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxer.h"
+    "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxertypes.h"
+    "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxerutil.cc"
+    "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxerutil.h"
+    "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvwriter.cc"
+    "${LIBWEBM_SRC_DIR}/mkvmuxer/mkvwriter.h"
+    "${LIBWEBM_SRC_DIR}/common/webmids.h")
+
+set(mkvmuxer_sample_sources
+    "${LIBWEBM_SRC_DIR}/mkvmuxer_sample.cc"
+    "${LIBWEBM_SRC_DIR}/sample_muxer_metadata.cc"
+    "${LIBWEBM_SRC_DIR}/sample_muxer_metadata.h")
+
+set(mkvmuxer_tests_sources
+    "${LIBWEBM_SRC_DIR}/testing/mkvmuxer_tests.cc"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.cc"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.h")
+
+set(mkvparser_sources
+    "${LIBWEBM_SRC_DIR}/mkvparser/mkvparser.cc"
+    "${LIBWEBM_SRC_DIR}/mkvparser/mkvparser.h"
+    "${LIBWEBM_SRC_DIR}/mkvparser/mkvreader.cc"
+    "${LIBWEBM_SRC_DIR}/mkvparser/mkvreader.h"
+    "${LIBWEBM_SRC_DIR}/common/webmids.h")
+
+set(mkvparser_sample_sources "${LIBWEBM_SRC_DIR}/mkvparser_sample.cc")
+set(mkvparser_tests_sources
+    "${LIBWEBM_SRC_DIR}/testing/mkvparser_tests.cc"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.cc"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.h")
+
+set(vp9_header_parser_tests_sources
+    "${LIBWEBM_SRC_DIR}/common/vp9_header_parser_tests.cc"
+    "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc"
+    "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.cc"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.h")
+
+set(vp9_level_stats_tests_sources
+    "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc"
+    "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h"
+    "${LIBWEBM_SRC_DIR}/common/vp9_level_stats_tests.cc"
+    "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.cc"
+    "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.h"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.cc"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.h")
+
+set(vttdemux_sources
+    "${LIBWEBM_SRC_DIR}/vttdemux.cc"
+    "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.cc"
+    "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.h")
+
+set(webm_parser_public_headers
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/buffer_reader.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/callback.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/dom_types.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/element.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/file_reader.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/id.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/istream_reader.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/reader.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/status.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/include/webm/webm_parser.h")
+
+set(webm_parser_sources
+    ${webm_parser_public_headers}
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/ancestory.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/ancestory.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/audio_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/bit_utils.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/bit_utils.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/block_additions_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/block_group_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/block_header_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/block_header_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/block_more_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/block_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/block_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/bool_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/buffer_reader.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/byte_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/callback.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/chapter_atom_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/chapter_display_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/chapters_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/cluster_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/colour_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/content_enc_aes_settings_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/content_encoding_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/content_encodings_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/content_encryption_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/cue_point_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/cue_track_positions_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/cues_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/date_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/date_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/ebml_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/edition_entry_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/element_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/file_reader.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/float_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/float_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/id_element_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/id_element_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/id_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/id_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/info_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/int_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/istream_reader.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/master_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/master_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/master_value_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/mastering_metadata_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/parser_utils.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/parser_utils.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/projection_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/recursive_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/seek_head_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/seek_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/segment_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/segment_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/simple_tag_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/size_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/size_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/skip_callback.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/skip_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/skip_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/slices_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/tag_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/tags_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/targets_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/time_slice_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/track_entry_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/tracks_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/unknown_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/unknown_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/var_int_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/var_int_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/video_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/virtual_block_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/virtual_block_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/void_parser.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/void_parser.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/src/webm_parser.cc")
+
+set(webm_parser_demo_sources "${LIBWEBM_SRC_DIR}/webm_parser/demo/demo.cc")
+set(webm_parser_tests_sources
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/audio_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/bit_utils_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_additions_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_group_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_header_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_more_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/block_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/bool_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/buffer_reader_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/byte_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/callback_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/chapter_atom_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/chapter_display_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/chapters_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/cluster_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/colour_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_enc_aes_settings_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_encoding_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_encodings_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/content_encryption_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/cue_point_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/cue_track_positions_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/cues_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/date_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/ebml_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/edition_entry_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/element_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/float_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/id_element_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/id_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/info_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/int_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/istream_reader_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/limited_reader_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/master_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/master_value_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/mastering_metadata_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/parser_utils_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/projection_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/recursive_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/seek_head_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/seek_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/segment_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/simple_tag_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/size_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/skip_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/slices_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/tag_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/tags_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/targets_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/time_slice_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/track_entry_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/tracks_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/unknown_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/var_int_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/video_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/virtual_block_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/void_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/webm_parser_test.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/element_parser_test.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/limited_reader.cc"
+    "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/limited_reader.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/mock_callback.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/test_utils/parser_test.h"
+    "${LIBWEBM_SRC_DIR}/webm_parser/tests/webm_parser_tests.cc")
+
+set(webm_info_sources
+    "${LIBWEBM_SRC_DIR}/common/indent.cc"
+    "${LIBWEBM_SRC_DIR}/common/indent.h"
+    "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc"
+    "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h"
+    "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.cc"
+    "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.h"
+    "${LIBWEBM_SRC_DIR}/common/webm_constants.h"
+    "${LIBWEBM_SRC_DIR}/common/webm_endian.cc"
+    "${LIBWEBM_SRC_DIR}/common/webm_endian.h"
+    "${LIBWEBM_SRC_DIR}/webm_info.cc")
+
+set(webmts_sources
+    "${LIBWEBM_SRC_DIR}/common/libwebm_util.cc"
+    "${LIBWEBM_SRC_DIR}/common/libwebm_util.h"
+    "${LIBWEBM_SRC_DIR}/common/video_frame.cc"
+    "${LIBWEBM_SRC_DIR}/common/video_frame.h"
+    "${LIBWEBM_SRC_DIR}/m2ts/vpxpes2ts.cc"
+    "${LIBWEBM_SRC_DIR}/m2ts/vpxpes2ts.h"
+    "${LIBWEBM_SRC_DIR}/m2ts/vpxpes_parser.cc"
+    "${LIBWEBM_SRC_DIR}/m2ts/vpxpes_parser.h"
+    "${LIBWEBM_SRC_DIR}/m2ts/webm2pes.cc"
+    "${LIBWEBM_SRC_DIR}/m2ts/webm2pes.h")
+
+set(webm2pes_sources "${LIBWEBM_SRC_DIR}/m2ts/webm2pes_main.cc")
+set(webm2pes_tests_sources
+    "${LIBWEBM_SRC_DIR}/testing/test_util.cc"
+    "${LIBWEBM_SRC_DIR}/testing/test_util.h"
+    "${LIBWEBM_SRC_DIR}/testing/video_frame_tests.cc"
+    "${LIBWEBM_SRC_DIR}/m2ts/tests/webm2pes_tests.cc")
+set(webm2ts_sources "${LIBWEBM_SRC_DIR}/m2ts/vpxpes2ts_main.cc")
+
+set(webvtt_common_sources
+    "${LIBWEBM_SRC_DIR}/webvtt/vttreader.cc"
+    "${LIBWEBM_SRC_DIR}/webvtt/vttreader.h"
+    "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.cc"
+    "${LIBWEBM_SRC_DIR}/webvtt/webvttparser.h")
+
+# Targets.
+add_library(mkvmuxer OBJECT ${mkvmuxer_sources})
+add_library(mkvparser OBJECT ${mkvparser_sources})
+add_library(webvtt_common OBJECT ${webvtt_common_sources})
+
+add_library(webm ${libwebm_common_sources}
+            $<TARGET_OBJECTS:mkvmuxer>
+            $<TARGET_OBJECTS:mkvparser>)
+
+if (WIN32)
+  # Use libwebm and libwebm.lib for project and library name on Windows (instead
+  # webm and webm.lib).
+  set_target_properties(webm PROPERTIES PROJECT_LABEL libwebm)
+  set_target_properties(webm PROPERTIES PREFIX lib)
+endif ()
+
+add_executable(mkvparser_sample ${mkvparser_sample_sources})
+target_link_libraries(mkvparser_sample LINK_PUBLIC webm)
+
+add_executable(mkvmuxer_sample ${mkvmuxer_sample_sources}
+               $<TARGET_OBJECTS:webvtt_common>)
+target_link_libraries(mkvmuxer_sample LINK_PUBLIC webm)
+
+add_executable(dumpvtt ${dumpvtt_sources} $<TARGET_OBJECTS:webvtt_common>)
+target_link_libraries(dumpvtt LINK_PUBLIC webm)
+
+add_executable(vttdemux ${vttdemux_sources})
+target_link_libraries(vttdemux LINK_PUBLIC webm)
+
+if (ENABLE_WEBMINFO)
+  add_executable(webm_info ${webm_info_sources})
+  target_link_libraries(webm_info LINK_PUBLIC webm)
+endif ()
+
+if (ENABLE_WEBM_PARSER)
+  include_directories(webm_parser webm_parser/include)
+  add_library(webm_parser OBJECT ${webm_parser_sources})
+  target_sources(webm PUBLIC $<TARGET_OBJECTS:webm_parser>)
+  set_target_properties(webm PROPERTIES PUBLIC_HEADER
+                        "${webm_parser_public_headers}")
+
+  add_executable(webm_parser_demo ${webm_parser_demo_sources})
+  target_link_libraries(webm_parser_demo LINK_PUBLIC webm)
+
+  install(TARGETS webm
+          ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+          LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+          RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+          PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webm)
+endif ()
+
+if (ENABLE_WEBMTS)
+  add_library(webmts OBJECT ${webmts_sources})
+
+  add_executable(webm2pes ${webm2pes_sources} $<TARGET_OBJECTS:webmts>)
+  target_link_libraries(webm2pes LINK_PUBLIC webm)
+
+  add_executable(webm2ts ${webm2ts_sources} $<TARGET_OBJECTS:webmts>)
+  target_link_libraries(webm2ts LINK_PUBLIC webm)
+endif ()
+
+if (ENABLE_TESTS)
+  set(GTEST_SRC_DIR "${LIBWEBM_SRC_DIR}/../googletest" CACHE PATH
+      "Path to Googletest git repository.")
+  # This directory is where libwebm will build googletest dependencies.
+  set(GTEST_BUILD_DIR "${CMAKE_BINARY_DIR}/googletest_build")
+
+  if (LIBWEBM_DISABLE_GTEST_CMAKE)
+    add_library(gtest STATIC "${GTEST_SRC_DIR}/googletest/src/gtest-all.cc")
+    include_directories("${GTEST_SRC_DIR}/googletest")
+  else ()
+    add_subdirectory("${GTEST_SRC_DIR}" "${GTEST_BUILD_DIR}")
+  endif ()
+  include_directories("${GTEST_SRC_DIR}/googletest/include")
+
+  add_executable(mkvmuxer_tests ${mkvmuxer_tests_sources})
+  target_link_libraries(mkvmuxer_tests LINK_PUBLIC gtest webm)
+
+  add_executable(mkvparser_tests ${mkvparser_tests_sources})
+  target_link_libraries(mkvparser_tests LINK_PUBLIC gtest webm)
+
+  add_executable(vp9_header_parser_tests ${vp9_header_parser_tests_sources})
+  target_link_libraries(vp9_header_parser_tests LINK_PUBLIC gtest webm)
+
+  add_executable(vp9_level_stats_tests ${vp9_level_stats_tests_sources})
+  target_link_libraries(vp9_level_stats_tests LINK_PUBLIC gtest webm)
+
+  if (ENABLE_WEBMTS)
+    add_executable(webm2pes_tests ${webm2pes_tests_sources}
+                   $<TARGET_OBJECTS:webmts>)
+    target_link_libraries(webm2pes_tests LINK_PUBLIC gtest webm)
+  endif ()
+
+  if (ENABLE_WEBM_PARSER)
+    include_directories("${GTEST_SRC_DIR}/googlemock/include")
+    add_executable(webm_parser_tests ${webm_parser_tests_sources})
+    target_link_libraries(webm_parser_tests LINK_PUBLIC gmock gtest webm)
+  endif ()
+endif ()
+
+# Include-what-you-use.
+if (ENABLE_IWYU)
+  # Make sure all the tools necessary for IWYU are present.
+  find_program(iwyu_path NAMES include-what-you-use)
+  find_program(iwyu_tool_path NAMES iwyu_tool.py)
+
+  # Some odd behavior on cmake's part: PYTHON_EXECUTABLE and PYTHON_VERSION_*
+  # are set by cmake when it does its internal python check, but
+  # PYTHONINTERP_FOUND is empty without explicitly looking for it.
+  find_package(PythonInterp)
+
+  if (iwyu_path AND iwyu_tool_path AND PYTHONINTERP_FOUND)
+    # Enable compilation command export (needed for iwyu_tool.py)
+    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+    # Add a custom target to run iwyu across all targets.
+    add_custom_target(iwyu
+                      ALL
+                      COMMAND "${PYTHON_EXECUTABLE}" "${iwyu_tool_path}" -p
+                          "${CMAKE_BINARY_DIR}"
+                      COMMENT "Running include-what-you-use..."
+                      VERBATIM)
+  else ()
+    message(STATUS "Ignoring ENABLE_IWYU because reasons:")
+    message(STATUS "  iwyu_path=" ${iwyu_path})
+    message(STATUS "  iwyu_tool_path=" ${iwyu_tool_path})
+    message(STATUS "  PYTHONINTERP_FOUND=" ${PYTHONINTERP_FOUND})
+    message(STATUS "  See README.libwebm for more information.")
+  endif ()
+endif ()
diff --git a/LICENSE.TXT b/LICENSE.TXT
new file mode 100644
index 0000000..7a6f995
--- /dev/null
+++ b/LICENSE.TXT
@@ -0,0 +1,30 @@
+Copyright (c) 2010, Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+  * Neither the name of Google nor the names of its contributors may
+    be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/Makefile.unix b/Makefile.unix
new file mode 100644
index 0000000..0430d17
--- /dev/null
+++ b/Makefile.unix
@@ -0,0 +1,57 @@
+CXX       := g++
+DEFINES   := -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
+DEFINES   += -D__STDC_LIMIT_MACROS
+INCLUDES  := -I.
+CXXFLAGS  := -W -Wall -g -std=c++11
+ALL_CXXFLAGS := -MMD -MP $(DEFINES) $(INCLUDES) $(CXXFLAGS)
+LIBWEBMA  := libwebm.a
+LIBWEBMSO := libwebm.so
+WEBMOBJS  := mkvmuxer/mkvmuxer.o mkvmuxer/mkvmuxerutil.o mkvmuxer/mkvwriter.o
+WEBMOBJS  += mkvparser/mkvparser.o mkvparser/mkvreader.o
+WEBMOBJS  += common/file_util.o common/hdr_util.o
+OBJSA     := $(WEBMOBJS:.o=_a.o)
+OBJSSO    := $(WEBMOBJS:.o=_so.o)
+VTTOBJS   := webvtt/vttreader.o webvtt/webvttparser.o sample_muxer_metadata.o
+EXEOBJS   := mkvmuxer_sample.o mkvparser_sample.o dumpvtt.o vttdemux.o
+EXES      := mkvparser_sample mkvmuxer_sample dumpvtt vttdemux
+DEPS      := $(WEBMOBJS:.o=.d) $(OBJECTS1:.o=.d) $(OBJECTS2:.o=.d)
+DEPS      += $(OBJECTS3:.o=.d) $(OBJECTS4:.o=.d) $(OBJSA:.o=.d) $(OBJSSO:.o=.d)
+DEPS      += $(VTTOBJS:.o=.d) $(EXEOBJS:.o=.d)
+CLEAN     := $(EXEOBJS) $(VTTOBJS) $(WEBMOBJS) $(OBJSA) $(OBJSSO) $(LIBWEBMA)
+CLEAN     += $(LIBWEBMSO) $(EXES) $(DEPS) $(INFOOBJS)
+
+all: $(EXES)
+
+mkvparser_sample: mkvparser_sample.o $(LIBWEBMA)
+	$(CXX) $^ -o $@
+
+mkvmuxer_sample: mkvmuxer_sample.o $(VTTOBJS) $(LIBWEBMA)
+	$(CXX) $^ -o $@
+
+dumpvtt: dumpvtt.o $(VTTOBJS) $(WEBMOBJS)
+	$(CXX) $^ -o $@
+
+vttdemux: vttdemux.o $(VTTOBJS) $(LIBWEBMA)
+	$(CXX) $^ -o $@
+
+shared: $(LIBWEBMSO)
+
+libwebm.a: $(OBJSA)
+	$(AR) rcs $@ $^
+
+libwebm.so: $(OBJSSO)
+	$(CXX) $(ALL_CXXFLAGS) -shared $(OBJSSO) -o $(LIBWEBMSO)
+
+%.o: %.cc
+	$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
+%_a.o: %.cc
+	$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
+%_so.o: %.cc
+	$(CXX) -c $(ALL_CXXFLAGS) -fPIC $< -o $@
+
+clean:
+	$(RM) -f $(CLEAN) Makefile.bak
+
+ifneq ($(MAKECMDGOALS), clean)
+  -include $(DEPS)
+endif
diff --git a/PATENTS.TXT b/PATENTS.TXT
new file mode 100644
index 0000000..caedf60
--- /dev/null
+++ b/PATENTS.TXT
@@ -0,0 +1,23 @@
+Additional IP Rights Grant (Patents)
+------------------------------------
+
+"These implementations" means the copyrightable works that implement the WebM
+codecs distributed by Google as part of the WebM Project.
+
+Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
+royalty-free, irrevocable (except as stated in this section) patent license to
+make, have made, use, offer to sell, sell, import, transfer, and otherwise
+run, modify and propagate the contents of these implementations of WebM, where
+such license applies only to those patent claims, both currently owned by
+Google and acquired in the future, licensable by Google that are necessarily
+infringed by these implementations of WebM. This grant does not include claims
+that would be infringed only as a consequence of further modification of these
+implementations. If you or your agent or exclusive licensee institute or order
+or agree to the institution of patent litigation or any other patent
+enforcement activity against any entity (including a cross-claim or
+counterclaim in a lawsuit) alleging that any of these implementations of WebM
+or any code incorporated within any of these implementations of WebM
+constitute direct or contributory patent infringement, or inducement of
+patent infringement, then any patent rights granted to you under this License
+for these implementations of WebM shall terminate as of the date such
+litigation is filed.
diff --git a/README.libwebm b/README.libwebm
new file mode 100644
index 0000000..3406f80
--- /dev/null
+++ b/README.libwebm
@@ -0,0 +1,143 @@
+Building Libwebm
+
+To build libwebm you must first create project files. To do this run cmake
+and pass it the path to your libwebm repo.
+
+Makefile.unix can be used as a fallback on systems that cmake does not
+support.
+
+
+CMake Basics
+
+To generate project/make files for the default toolchain on your system simply
+run cmake with the path to the libwebm repo:
+
+$ cmake path/to/libwebm
+
+On Windows the above command will produce Visual Studio project files for the
+newest Visual Studio detected on the system. On Mac OS X and Linux systems, the
+above command will produce a makefile.
+
+To control what types of projects are generated the -G parameter is added to
+the cmake command line. This argument must be followed by the name of a
+generator. Running cmake with the --help argument will list the available
+generators for your system.
+
+On Mac OS X you would run the following command to generate Xcode projects:
+
+$ cmake path/to/libwebm -G Xcode
+
+On a Windows box you would run the following command to generate Visual Studio
+2013 projects:
+
+$ cmake path/to/libwebm -G "Visual Studio 12"
+
+To generate 64-bit Windows Visual Studio 2013 projects:
+
+$ cmake path/to/libwebm "Visual Studio 12 Win64"
+
+
+CMake Makefiles: Debugging and Optimization
+
+Unlike Visual Studio and Xcode projects, the build configuration for make builds
+is controlled when you run cmake. The following examples demonstrate various
+build configurations.
+
+Omitting the build type produces makefiles that use build flags containing
+neither optimization nor debug flags:
+$ cmake path/to/libwebm
+
+A makefile using release (optimized) flags is produced like this:
+$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=release
+
+A release build with debug info can be produced as well:
+$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=relwithdebinfo
+
+And your standard debug build will be produced using:
+$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=debug
+
+
+Tests
+
+To enable libwebm tests add -DENABLE_TESTS=ON CMake generation command line. For
+example:
+
+$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON
+
+Libwebm tests depend on googletest. By default googletest is expected to be a
+sibling directory of the Libwebm repository. To change that, update your CMake
+command to be similar to the following:
+
+$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON \
+  -DGTEST_SRC_DIR=/path/to/googletest
+
+The tests rely upon the LIBWEBM_TEST_DATA_PATH environment variable to locate
+test input. The following example demonstrates running the muxer tests from the
+build directory:
+
+$ LIBWEBM_TEST_DATA_PATH=path/to/libwebm/testing/testdata ./mkvmuxer_tests
+
+Note: Libwebm Googletest integration was built with googletest from
+      https://github.com/google/googletest.git at git revision
+      ddb8012eb48bc203aa93dcc2b22c1db516302b29.
+
+
+CMake Include-what-you-use integration
+
+Include-what-you-use is an analysis tool that helps ensure libwebm includes the
+C/C++ header files actually in use. To enable the integration support
+ENABLE_IWYU must be turned on at cmake run time:
+
+$ cmake path/to/libwebm -G "Unix Makefiles" -DENABLE_IWYU=ON
+
+This adds the iwyu target to the build. To run include-what-you-use:
+
+$ make iwyu
+
+The following requirements must be met for ENABLE_IWYU to enable the iwyu
+target:
+
+1. include-what-you-use and iwyu_tool.py must be in your PATH.
+2. A python interpreter must be on the system and available to CMake.
+
+The values of the following variables are used to determine if the requirements
+have been met. Values to the right of the equals sign are what a successful run
+might look like:
+  iwyu_path=/path/to/iwyu_tool.py
+  iwyu_tool_path=/path/to/include-what-you-use
+  PYTHONINTERP_FOUND=TRUE
+
+An empty PYTHONINTERP_FOUND, or iwyu_path/iwyu_tool_path suffixed with NOTFOUND
+are failures.
+
+For Include-what-you-use setup instructions, see:
+https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/InstructionsForUsers.md
+
+If, when building the iwyu target, compile errors reporting failures loading
+standard include files occur, one solution can be found here:
+https://github.com/include-what-you-use/include-what-you-use/issues/100
+
+
+CMake cross compile
+To cross compile libwebm for Windows using mingw-w64 run cmake with the
+following arguments:
+
+$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
+  path/to/libwebm
+
+Note1: As of this writing googletest will not build via mingw-w64 without
+disabling pthreads.
+googletest hash: d225acc90bc3a8c420a9bcd1f033033c1ccd7fe0
+
+To build with tests when using mingw-w64 use the following arguments when
+running CMake:
+
+$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
+  -DENABLE_TESTS=ON -Dgtest_disable_pthreads=ON path/to/libwebm
+
+Note2: i686-w64-mingw32 is the default compiler. This can be controlled using
+the MINGW_PREFIX variable:
+
+$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
+  -DMINGW_PREFIX=x86_64-w64-mingw32 path/to/libwebm
+
diff --git a/build/cxx_flags.cmake b/build/cxx_flags.cmake
new file mode 100644
index 0000000..9e96889
--- /dev/null
+++ b/build/cxx_flags.cmake
@@ -0,0 +1,73 @@
+##  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.
+cmake_minimum_required(VERSION 3.2)
+
+include(CheckCXXCompilerFlag)
+
+# String used to cache failed CXX flags.
+set(LIBWEBM_FAILED_CXX_FLAGS)
+
+# Checks C++ compiler for support of $cxx_flag. Adds $cxx_flag to
+# $CMAKE_CXX_FLAGS when the compile test passes. Caches $c_flag in
+# $LIBWEBM_FAILED_CXX_FLAGS when the test fails.
+function (add_cxx_flag_if_supported cxx_flag)
+  unset(CXX_FLAG_FOUND CACHE)
+  string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND)
+  unset(CXX_FLAG_FAILED CACHE)
+  string(FIND "${LIBWEBM_FAILED_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FAILED)
+
+  if (${CXX_FLAG_FOUND} EQUAL -1 AND ${CXX_FLAG_FAILED} EQUAL -1)
+    unset(CXX_FLAG_SUPPORTED CACHE)
+    message("Checking CXX compiler flag support for: " ${cxx_flag})
+    check_cxx_compiler_flag("${cxx_flag}" CXX_FLAG_SUPPORTED)
+    if (CXX_FLAG_SUPPORTED)
+      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${cxx_flag}" CACHE STRING ""
+          FORCE)
+    else ()
+      set(LIBWEBM_FAILED_CXX_FLAGS "${LIBWEBM_FAILED_CXX_FLAGS} ${cxx_flag}"
+          CACHE STRING "" FORCE)
+    endif ()
+  endif ()
+endfunction ()
+
+# Checks CXX compiler for support of $cxx_flag and terminates generation when
+# support is not present.
+function (require_cxx_flag cxx_flag)
+  unset(CXX_FLAG_FOUND CACHE)
+  string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND)
+
+  if (${CXX_FLAG_FOUND} EQUAL -1)
+    unset(LIBWEBM_HAVE_CXX_FLAG CACHE)
+    message("Checking CXX compiler flag support for: " ${cxx_flag})
+    check_cxx_compiler_flag("${cxx_flag}" LIBWEBM_HAVE_CXX_FLAG)
+    if (NOT LIBWEBM_HAVE_CXX_FLAG)
+      message(FATAL_ERROR
+              "${PROJECT_NAME} requires support for CXX flag: ${cxx_flag}.")
+    endif ()
+    set(CMAKE_CXX_FLAGS "${cxx_flag} ${CMAKE_CXX_FLAGS}" CACHE STRING "" FORCE)
+  endif ()
+endfunction ()
+
+# Checks only non-MSVC targets for support of $cxx_flag.
+function (require_cxx_flag_nomsvc cxx_flag)
+  if (NOT MSVC)
+    require_cxx_flag(${cxx_flag})
+  endif ()
+endfunction ()
+
+# Adds $preproc_def to CXX compiler command line (as -D$preproc_def) if not
+# already present.
+function (add_cxx_preproc_definition preproc_def)
+  unset(PREPROC_DEF_FOUND CACHE)
+  string(FIND "${CMAKE_CXX_FLAGS}" "${preproc_def}" PREPROC_DEF_FOUND)
+
+  if (${PREPROC_DEF_FOUND} EQUAL -1)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${preproc_def}" CACHE STRING ""
+        FORCE)
+  endif ()
+endfunction ()
diff --git a/build/msvc_runtime.cmake b/build/msvc_runtime.cmake
new file mode 100644
index 0000000..d7d7add
--- /dev/null
+++ b/build/msvc_runtime.cmake
@@ -0,0 +1,23 @@
+##  Copyright (c) 2015 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.
+cmake_minimum_required(VERSION 2.8)
+
+if (MSVC)
+  # CMake defaults to producing code linked to the DLL MSVC runtime. In libwebm
+  # static is typically desired. Force static code generation unless the user
+  # running CMake set MSVC_RUNTIME to dll.
+  if (NOT "${MSVC_RUNTIME}" STREQUAL "dll")
+    foreach (flag_var
+             CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+             CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+      if (${flag_var} MATCHES "/MD")
+        string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+      endif (${flag_var} MATCHES "/MD")
+    endforeach (flag_var)
+  endif ()
+endif ()
diff --git a/build/x86-mingw-gcc.cmake b/build/x86-mingw-gcc.cmake
new file mode 100644
index 0000000..8416eaf
--- /dev/null
+++ b/build/x86-mingw-gcc.cmake
@@ -0,0 +1,26 @@
+##  Copyright (c) 2017 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.
+if (NOT LIBWEBM_BUILD_X86_MINGW_GCC_CMAKE_)
+set(LIBWEBM_BUILD_X86_MINGW_GCC_CMAKE_ 1)
+
+set(CMAKE_SYSTEM_PROCESSOR "x86")
+set(CMAKE_SYSTEM_NAME "Windows")
+set(CMAKE_C_COMPILER_ARG1 "-m32")
+set(CMAKE_CXX_COMPILER_ARG1 "-m32")
+
+if ("${CROSS}" STREQUAL "")
+  set(CROSS i686-w64-mingw32-)
+endif ()
+
+set(CMAKE_C_COMPILER ${CROSS}gcc)
+set(CMAKE_CXX_COMPILER ${CROSS}g++)
+
+# Disable googletest CMake usage for mingw cross compiles.
+set(LIBWEBM_DISABLE_GTEST_CMAKE 1)
+
+endif ()  # LIBWEBM_BUILD_X86_MINGW_GCC_CMAKE_
diff --git a/build/x86_64-mingw-gcc.cmake b/build/x86_64-mingw-gcc.cmake
new file mode 100644
index 0000000..9db28b7
--- /dev/null
+++ b/build/x86_64-mingw-gcc.cmake
@@ -0,0 +1,24 @@
+##  Copyright (c) 2017 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.
+if (NOT LIBWEBM_BUILD_X86_64_MINGW_GCC_CMAKE_)
+set(LIBWEBM_BUILD_X86_64_MINGW_GCC_CMAKE_ 1)
+
+set(CMAKE_SYSTEM_PROCESSOR "x86_64")
+set(CMAKE_SYSTEM_NAME "Windows")
+
+if ("${CROSS}" STREQUAL "")
+  set(CROSS x86_64-w64-mingw32-)
+endif ()
+
+set(CMAKE_C_COMPILER ${CROSS}gcc)
+set(CMAKE_CXX_COMPILER ${CROSS}g++)
+
+# Disable googletest CMake usage for mingw cross compiles.
+set(LIBWEBM_DISABLE_GTEST_CMAKE 1)
+
+endif ()  # LIBWEBM_BUILD_X86_64_MINGW_GCC_CMAKE_
diff --git a/codereview.settings b/codereview.settings
new file mode 100644
index 0000000..ccba2ee
--- /dev/null
+++ b/codereview.settings
@@ -0,0 +1,4 @@
+# This file is used by git cl to get repository specific information.
+GERRIT_HOST: True
+CODE_REVIEW_SERVER: chromium-review.googlesource.com
+GERRIT_SQUASH_UPLOADS: False
diff --git a/common/common.sh b/common/common.sh
new file mode 100644
index 0000000..49fec79
--- /dev/null
+++ b/common/common.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+##
+##  Copyright (c) 2015 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.
+##
+set -e
+devnull='> /dev/null 2>&1'
+
+readonly ORIG_PWD="$(pwd)"
+
+elog() {
+  echo "${0##*/} failed because: $@" 1>&2
+}
+
+vlog() {
+  if [ "${VERBOSE}" = "yes" ]; then
+    echo "$@"
+  fi
+}
+
+# Terminates script when name of current directory does not match $1.
+check_dir() {
+  current_dir="$(pwd)"
+  required_dir="$1"
+  if [ "${current_dir##*/}" != "${required_dir}" ]; then
+    elog "This script must be run from the ${required_dir} directory."
+    exit 1
+  fi
+}
+
+# Terminates the script when $1 is not in $PATH. Any arguments required for
+# the tool being tested to return a successful exit code can be passed as
+# additional arguments.
+check_tool() {
+  tool="$1"
+  shift
+  tool_args="$@"
+  if ! eval "${tool}" ${tool_args} > /dev/null 2>&1; then
+    elog "${tool} must be in your path."
+    exit 1
+  fi
+}
+
+# Echoes git describe output for the directory specified by $1 to stdout.
+git_describe() {
+  git_dir="$1"
+  check_git
+  echo $(git -C "${git_dir}" describe)
+}
+
+# Echoes current git revision for the directory specifed by $1 to stdout.
+git_revision() {
+  git_dir="$1"
+  check_git
+  echo $(git -C "${git_dir}" rev-parse HEAD)
+}
+
diff --git a/common/file_util.cc b/common/file_util.cc
new file mode 100644
index 0000000..6eb6428
--- /dev/null
+++ b/common/file_util.cc
@@ -0,0 +1,93 @@
+// 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 <cstring>
+#include <fstream>
+#include <ios>
+#include <string>
+
+namespace libwebm {
+
+std::string GetTempFileName() {
+#if !defined _MSC_VER && !defined __MINGW32__
+  std::string temp_file_name_template_str =
+      std::string(std::getenv("TEST_TMPDIR") ? std::getenv("TEST_TMPDIR")
+                                             : ".") +
+      "/libwebm_temp.XXXXXX";
+  char* temp_file_name_template =
+      new char[temp_file_name_template_str.length() + 1];
+  memset(temp_file_name_template, 0, temp_file_name_template_str.length() + 1);
+  temp_file_name_template_str.copy(temp_file_name_template,
+                                   temp_file_name_template_str.length(), 0);
+  int fd = mkstemp(temp_file_name_template);
+  std::string temp_file_name =
+      (fd != -1) ? std::string(temp_file_name_template) : std::string();
+  delete[] temp_file_name_template;
+  if (fd != -1) {
+    close(fd);
+  }
+  return temp_file_name;
+#else
+  char tmp_file_name[_MAX_PATH];
+#if defined _MSC_VER || defined MINGW_HAS_SECURE_API
+  errno_t err = tmpnam_s(tmp_file_name);
+#else
+  char* fname_pointer = tmpnam(tmp_file_name);
+  int err = (fname_pointer == &tmp_file_name[0]) ? 0 : -1;
+#endif
+  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;
+}
+
+bool GetFileContents(const std::string& file_name, std::string* contents) {
+  std::ifstream file(file_name.c_str());
+  *contents = std::string(static_cast<size_t>(GetFileSize(file_name)), 0);
+  if (file.good() && contents->size()) {
+    file.read(&(*contents)[0], contents->size());
+  }
+  return !file.fail();
+}
+
+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/common/file_util.h b/common/file_util.h
new file mode 100644
index 0000000..a873734
--- /dev/null
+++ b/common/file_util.h
@@ -0,0 +1,44 @@
+// 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);
+
+// Gets the contents file_name as a string. Returns false on error.
+bool GetFileContents(const std::string& file_name, std::string* contents);
+
+// 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_
diff --git a/common/hdr_util.cc b/common/hdr_util.cc
new file mode 100644
index 0000000..916f717
--- /dev/null
+++ b/common/hdr_util.cc
@@ -0,0 +1,220 @@
+// 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 <climits>
+#include <cstddef>
+#include <new>
+
+#include "mkvparser/mkvparser.h"
+
+namespace libwebm {
+const int Vp9CodecFeatures::kValueNotPresent = INT_MAX;
+
+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->set_luminance_max(parser_mm.luminance_max);
+  if (MasteringMetadataValuePresent(parser_mm.luminance_min))
+    muxer_mm->set_luminance_min(parser_mm.luminance_min);
+
+  PrimaryChromaticityPtr r_ptr(nullptr);
+  PrimaryChromaticityPtr g_ptr(nullptr);
+  PrimaryChromaticityPtr b_ptr(nullptr);
+  PrimaryChromaticityPtr wp_ptr(nullptr);
+
+  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->set_matrix_coefficients(parser_colour.matrix_coefficients);
+  if (ColourValuePresent(parser_colour.bits_per_channel))
+    muxer_colour->set_bits_per_channel(parser_colour.bits_per_channel);
+  if (ColourValuePresent(parser_colour.chroma_subsampling_horz)) {
+    muxer_colour->set_chroma_subsampling_horz(
+        parser_colour.chroma_subsampling_horz);
+  }
+  if (ColourValuePresent(parser_colour.chroma_subsampling_vert)) {
+    muxer_colour->set_chroma_subsampling_vert(
+        parser_colour.chroma_subsampling_vert);
+  }
+  if (ColourValuePresent(parser_colour.cb_subsampling_horz))
+    muxer_colour->set_cb_subsampling_horz(parser_colour.cb_subsampling_horz);
+  if (ColourValuePresent(parser_colour.cb_subsampling_vert))
+    muxer_colour->set_cb_subsampling_vert(parser_colour.cb_subsampling_vert);
+  if (ColourValuePresent(parser_colour.chroma_siting_horz))
+    muxer_colour->set_chroma_siting_horz(parser_colour.chroma_siting_horz);
+  if (ColourValuePresent(parser_colour.chroma_siting_vert))
+    muxer_colour->set_chroma_siting_vert(parser_colour.chroma_siting_vert);
+  if (ColourValuePresent(parser_colour.range))
+    muxer_colour->set_range(parser_colour.range);
+  if (ColourValuePresent(parser_colour.transfer_characteristics)) {
+    muxer_colour->set_transfer_characteristics(
+        parser_colour.transfer_characteristics);
+  }
+  if (ColourValuePresent(parser_colour.primaries))
+    muxer_colour->set_primaries(parser_colour.primaries);
+  if (ColourValuePresent(parser_colour.max_cll))
+    muxer_colour->set_max_cll(parser_colour.max_cll);
+  if (ColourValuePresent(parser_colour.max_fall))
+    muxer_colour->set_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.
+//
+// See the following link for more information:
+// http://www.webmproject.org/vp9/profiles/
+bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length,
+                          Vp9CodecFeatures* features) {
+  const int kVpxCodecPrivateMinLength = 3;
+  if (!private_data || !features || length < kVpxCodecPrivateMinLength)
+    return false;
+
+  const uint8_t kVp9ProfileId = 1;
+  const uint8_t kVp9LevelId = 2;
+  const uint8_t kVp9BitDepthId = 3;
+  const uint8_t kVp9ChromaSubsamplingId = 4;
+  const int kVpxFeatureLength = 1;
+  int offset = 0;
+
+  // Set features to not set.
+  features->profile = Vp9CodecFeatures::kValueNotPresent;
+  features->level = Vp9CodecFeatures::kValueNotPresent;
+  features->bit_depth = Vp9CodecFeatures::kValueNotPresent;
+  features->chroma_subsampling = Vp9CodecFeatures::kValueNotPresent;
+  do {
+    const uint8_t id_byte = private_data[offset++];
+    const uint8_t length_byte = private_data[offset++];
+    if (length_byte != kVpxFeatureLength)
+      return false;
+    if (id_byte == kVp9ProfileId) {
+      const int priv_profile = static_cast<int>(private_data[offset++]);
+      if (priv_profile < 0 || priv_profile > 3)
+        return false;
+      if (features->profile != Vp9CodecFeatures::kValueNotPresent &&
+          features->profile != priv_profile) {
+        return false;
+      }
+      features->profile = priv_profile;
+    } else if (id_byte == kVp9LevelId) {
+      const int priv_level = static_cast<int>(private_data[offset++]);
+
+      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 (priv_level == levels[i]) {
+          if (features->level != Vp9CodecFeatures::kValueNotPresent &&
+              features->level != priv_level) {
+            return false;
+          }
+          features->level = priv_level;
+          break;
+        }
+      }
+      if (features->level == Vp9CodecFeatures::kValueNotPresent)
+        return false;
+    } else if (id_byte == kVp9BitDepthId) {
+      const int priv_profile = static_cast<int>(private_data[offset++]);
+      if (priv_profile != 8 && priv_profile != 10 && priv_profile != 12)
+        return false;
+      if (features->bit_depth != Vp9CodecFeatures::kValueNotPresent &&
+          features->bit_depth != priv_profile) {
+        return false;
+      }
+      features->bit_depth = priv_profile;
+    } else if (id_byte == kVp9ChromaSubsamplingId) {
+      const int priv_profile = static_cast<int>(private_data[offset++]);
+      if (priv_profile != 0 && priv_profile != 2 && priv_profile != 3)
+        return false;
+      if (features->chroma_subsampling != Vp9CodecFeatures::kValueNotPresent &&
+          features->chroma_subsampling != priv_profile) {
+        return false;
+      }
+      features->chroma_subsampling = priv_profile;
+    } else {
+      // Invalid ID.
+      return false;
+    }
+  } while (offset + kVpxCodecPrivateMinLength <= length);
+
+  return true;
+}
+}  // namespace libwebm
diff --git a/common/hdr_util.h b/common/hdr_util.h
new file mode 100644
index 0000000..78e2eeb
--- /dev/null
+++ b/common/hdr_util.h
@@ -0,0 +1,71 @@
+// 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.
+
+// Features of the VP9 codec that may be set in the CodecPrivate of a VP9 video
+// stream. A value of kValueNotPresent represents that the value was not set in
+// the CodecPrivate.
+struct Vp9CodecFeatures {
+  static const int kValueNotPresent;
+
+  Vp9CodecFeatures()
+      : profile(kValueNotPresent),
+        level(kValueNotPresent),
+        bit_depth(kValueNotPresent),
+        chroma_subsampling(kValueNotPresent) {}
+  ~Vp9CodecFeatures() {}
+
+  int profile;
+  int level;
+  int bit_depth;
+  int chroma_subsampling;
+};
+
+typedef std::unique_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 true if |features| is set to one or more valid values.
+bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length,
+                          Vp9CodecFeatures* features);
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_COMMON_HDR_UTIL_H_
diff --git a/common/indent.cc b/common/indent.cc
new file mode 100644
index 0000000..87d656c
--- /dev/null
+++ b/common/indent.cc
@@ -0,0 +1,29 @@
+/*
+ *  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 "common/indent.h"
+
+#include <string>
+
+namespace libwebm {
+
+Indent::Indent(int indent) : indent_(indent), indent_str_() { Update(); }
+
+void Indent::Adjust(int indent) {
+  indent_ += indent;
+  if (indent_ < 0)
+    indent_ = 0;
+
+  Update();
+}
+
+void Indent::Update() { indent_str_ = std::string(indent_, ' '); }
+
+}  // namespace libwebm
diff --git a/common/indent.h b/common/indent.h
new file mode 100644
index 0000000..22d3d26
--- /dev/null
+++ b/common/indent.h
@@ -0,0 +1,48 @@
+/*
+ *  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 LIBWEBM_COMMON_INDENT_H_
+#define LIBWEBM_COMMON_INDENT_H_
+
+#include <string>
+
+#include "mkvmuxer/mkvmuxertypes.h"
+
+namespace libwebm {
+
+const int kIncreaseIndent = 2;
+const int kDecreaseIndent = -2;
+
+// Used for formatting output so objects only have to keep track of spacing
+// within their scope.
+class Indent {
+ public:
+  explicit Indent(int indent);
+
+  // Changes the number of spaces output. The value adjusted is relative to
+  // |indent_|.
+  void Adjust(int indent);
+
+  std::string indent_str() const { return indent_str_; }
+
+ private:
+  // Called after |indent_| is changed. This will set |indent_str_| to the
+  // proper number of spaces.
+  void Update();
+
+  int indent_;
+  std::string indent_str_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Indent);
+};
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_COMMON_INDENT_H_
diff --git a/common/libwebm_util.cc b/common/libwebm_util.cc
new file mode 100644
index 0000000..d158250
--- /dev/null
+++ b/common/libwebm_util.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2015 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/libwebm_util.h"
+
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <limits>
+
+namespace libwebm {
+
+std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds) {
+  const double pts_seconds = nanoseconds / kNanosecondsPerSecond;
+  return static_cast<std::int64_t>(pts_seconds * 90000);
+}
+
+std::int64_t Khz90TicksToNanoseconds(std::int64_t ticks) {
+  const double seconds = ticks / 90000.0;
+  return static_cast<std::int64_t>(seconds * kNanosecondsPerSecond);
+}
+
+bool ParseVP9SuperFrameIndex(const std::uint8_t* frame,
+                             std::size_t frame_length, Ranges* frame_ranges,
+                             bool* error) {
+  if (frame == nullptr || frame_length == 0 || frame_ranges == nullptr ||
+      error == nullptr) {
+    return false;
+  }
+
+  bool parse_ok = false;
+  const std::uint8_t marker = frame[frame_length - 1];
+  const std::uint32_t kHasSuperFrameIndexMask = 0xe0;
+  const std::uint32_t kSuperFrameMarker = 0xc0;
+  const std::uint32_t kLengthFieldSizeMask = 0x3;
+
+  if ((marker & kHasSuperFrameIndexMask) == kSuperFrameMarker) {
+    const std::uint32_t kFrameCountMask = 0x7;
+    const int num_frames = (marker & kFrameCountMask) + 1;
+    const int length_field_size = ((marker >> 3) & kLengthFieldSizeMask) + 1;
+    const std::size_t index_length = 2 + length_field_size * num_frames;
+
+    if (frame_length < index_length) {
+      std::fprintf(stderr,
+                   "VP9ParseSuperFrameIndex: Invalid superframe index size.\n");
+      *error = true;
+      return false;
+    }
+
+    // Consume the super frame index. Note: it's at the end of the super frame.
+    const std::size_t length = frame_length - index_length;
+
+    if (length >= index_length &&
+        frame[frame_length - index_length] == marker) {
+      // Found a valid superframe index.
+      const std::uint8_t* byte = frame + length + 1;
+
+      std::size_t frame_offset = 0;
+
+      for (int i = 0; i < num_frames; ++i) {
+        std::uint32_t child_frame_length = 0;
+
+        for (int j = 0; j < length_field_size; ++j) {
+          child_frame_length |= (*byte++) << (j * 8);
+        }
+
+        if (length - frame_offset < child_frame_length) {
+          std::fprintf(stderr,
+                       "ParseVP9SuperFrameIndex: Invalid superframe, sub frame "
+                       "larger than entire frame.\n");
+          *error = true;
+          return false;
+        }
+
+        frame_ranges->push_back(Range(frame_offset, child_frame_length));
+        frame_offset += child_frame_length;
+      }
+
+      if (static_cast<int>(frame_ranges->size()) != num_frames) {
+        std::fprintf(stderr, "VP9Parse: superframe index parse failed.\n");
+        *error = true;
+        return false;
+      }
+
+      parse_ok = true;
+    } else {
+      std::fprintf(stderr, "VP9Parse: Invalid superframe index.\n");
+      *error = true;
+    }
+  }
+  return parse_ok;
+}
+
+bool WriteUint8(std::uint8_t val, std::FILE* fileptr) {
+  if (fileptr == nullptr)
+    return false;
+  return (std::fputc(val, fileptr) == val);
+}
+
+std::uint16_t ReadUint16(const std::uint8_t* buf) {
+  if (buf == nullptr)
+    return 0;
+  return ((buf[0] << 8) | buf[1]);
+}
+
+}  // namespace libwebm
diff --git a/common/libwebm_util.h b/common/libwebm_util.h
new file mode 100644
index 0000000..71d805f
--- /dev/null
+++ b/common/libwebm_util.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2015 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_LIBWEBM_UTIL_H_
+#define LIBWEBM_COMMON_LIBWEBM_UTIL_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <memory>
+#include <vector>
+
+namespace libwebm {
+
+const double kNanosecondsPerSecond = 1000000000.0;
+
+// fclose functor for wrapping FILE in std::unique_ptr.
+// TODO(tomfinegan): Move this to file_util once c++11 restrictions are
+//                   relaxed.
+struct FILEDeleter {
+  int operator()(std::FILE* f) {
+    if (f != nullptr)
+      return fclose(f);
+    return 0;
+  }
+};
+typedef std::unique_ptr<std::FILE, FILEDeleter> FilePtr;
+
+struct Range {
+  Range(std::size_t off, std::size_t len) : offset(off), length(len) {}
+  Range() = delete;
+  Range(const Range&) = default;
+  Range(Range&&) = default;
+  ~Range() = default;
+  const std::size_t offset;
+  const std::size_t length;
+};
+typedef std::vector<Range> Ranges;
+
+// Converts |nanoseconds| to 90000 Hz clock ticks and vice versa. Each return
+// the converted value.
+std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds);
+std::int64_t Khz90TicksToNanoseconds(std::int64_t khz90_ticks);
+
+// Returns true and stores frame offsets and lengths in |frame_ranges| when
+// |frame| has a valid VP9 super frame index. Sets |error| to true when parsing
+// fails for any reason.
+bool ParseVP9SuperFrameIndex(const std::uint8_t* frame,
+                             std::size_t frame_length, Ranges* frame_ranges,
+                             bool* error);
+
+// Writes |val| to |fileptr| and returns true upon success.
+bool WriteUint8(std::uint8_t val, std::FILE* fileptr);
+
+// Reads 2 bytes from |buf| and returns them as a uint16_t. Returns 0 when |buf|
+// is a nullptr.
+std::uint16_t ReadUint16(const std::uint8_t* buf);
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_COMMON_LIBWEBM_UTIL_H_
diff --git a/common/video_frame.cc b/common/video_frame.cc
new file mode 100644
index 0000000..00aa8da
--- /dev/null
+++ b/common/video_frame.cc
@@ -0,0 +1,45 @@
+// 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/video_frame.h"
+
+#include <cstdio>
+
+namespace libwebm {
+
+bool VideoFrame::Buffer::Init(std::size_t new_length) {
+  capacity = 0;
+  length = 0;
+  data.reset(new std::uint8_t[new_length]);
+
+  if (data.get() == nullptr) {
+    fprintf(stderr, "VideoFrame: Out of memory.");
+    return false;
+  }
+
+  capacity = new_length;
+  length = 0;
+  return true;
+}
+
+bool VideoFrame::Init(std::size_t length) { return buffer_.Init(length); }
+
+bool VideoFrame::Init(std::size_t length, std::int64_t nano_pts, Codec codec) {
+  nanosecond_pts_ = nano_pts;
+  codec_ = codec;
+  return Init(length);
+}
+
+bool VideoFrame::SetBufferLength(std::size_t length) {
+  if (length > buffer_.capacity || buffer_.data.get() == nullptr)
+    return false;
+
+  buffer_.length = length;
+  return true;
+}
+
+}  // namespace libwebm
diff --git a/common/video_frame.h b/common/video_frame.h
new file mode 100644
index 0000000..3031ef2
--- /dev/null
+++ b/common/video_frame.h
@@ -0,0 +1,68 @@
+// 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_VIDEO_FRAME_H_
+#define LIBWEBM_COMMON_VIDEO_FRAME_H_
+
+#include <cstdint>
+#include <memory>
+
+namespace libwebm {
+
+// VideoFrame is a storage class for compressed video frames.
+class VideoFrame {
+ public:
+  enum Codec { kVP8, kVP9 };
+  struct Buffer {
+    Buffer() = default;
+    ~Buffer() = default;
+
+    // Resets |data| to be of size |new_length| bytes, sets |capacity| to
+    // |new_length|, sets |length| to 0 (aka empty). Returns true for success.
+    bool Init(std::size_t new_length);
+
+    std::unique_ptr<std::uint8_t[]> data;
+    std::size_t length = 0;
+    std::size_t capacity = 0;
+  };
+
+  VideoFrame() = default;
+  ~VideoFrame() = default;
+  VideoFrame(std::int64_t pts_in_nanoseconds, Codec vpx_codec)
+      : nanosecond_pts_(pts_in_nanoseconds), codec_(vpx_codec) {}
+  VideoFrame(bool keyframe, std::int64_t pts_in_nanoseconds, Codec vpx_codec)
+      : keyframe_(keyframe),
+        nanosecond_pts_(pts_in_nanoseconds),
+        codec_(vpx_codec) {}
+  bool Init(std::size_t length);
+  bool Init(std::size_t length, std::int64_t nano_pts, Codec codec);
+
+  // Updates actual length of data stored in |buffer_.data| when it's been
+  // written via the raw pointer returned from buffer_.data.get().
+  // Returns false when buffer_.data.get() return nullptr and/or when
+  // |length| > |buffer_.length|. Returns true otherwise.
+  bool SetBufferLength(std::size_t length);
+
+  // Accessors.
+  const Buffer& buffer() const { return buffer_; }
+  bool keyframe() const { return keyframe_; }
+  std::int64_t nanosecond_pts() const { return nanosecond_pts_; }
+  Codec codec() const { return codec_; }
+
+  // Mutators.
+  void set_nanosecond_pts(std::int64_t nano_pts) { nanosecond_pts_ = nano_pts; }
+
+ private:
+  Buffer buffer_;
+  bool keyframe_ = false;
+  std::int64_t nanosecond_pts_ = 0;
+  Codec codec_ = kVP9;
+};
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_COMMON_VIDEO_FRAME_H_
\ No newline at end of file
diff --git a/common/vp9_header_parser.cc b/common/vp9_header_parser.cc
new file mode 100644
index 0000000..604a050
--- /dev/null
+++ b/common/vp9_header_parser.cc
@@ -0,0 +1,266 @@
+// 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/vp9_header_parser.h"
+
+#include <stdio.h>
+
+namespace vp9_parser {
+
+bool Vp9HeaderParser::SetFrame(const uint8_t* frame, size_t length) {
+  if (!frame || length == 0)
+    return false;
+
+  frame_ = frame;
+  frame_size_ = length;
+  bit_offset_ = 0;
+  profile_ = -1;
+  show_existing_frame_ = 0;
+  key_ = 0;
+  altref_ = 0;
+  error_resilient_mode_ = 0;
+  intra_only_ = 0;
+  reset_frame_context_ = 0;
+  color_space_ = 0;
+  color_range_ = 0;
+  subsampling_x_ = 0;
+  subsampling_y_ = 0;
+  refresh_frame_flags_ = 0;
+  return true;
+}
+
+bool Vp9HeaderParser::ParseUncompressedHeader(const uint8_t* frame,
+                                              size_t length) {
+  if (!SetFrame(frame, length))
+    return false;
+  const int frame_marker = VpxReadLiteral(2);
+  if (frame_marker != kVp9FrameMarker) {
+    fprintf(stderr, "Invalid VP9 frame_marker:%d\n", frame_marker);
+    return false;
+  }
+
+  profile_ = ReadBit();
+  profile_ |= ReadBit() << 1;
+  if (profile_ > 2)
+    profile_ += ReadBit();
+
+  // TODO(fgalligan): Decide how to handle show existing frames.
+  show_existing_frame_ = ReadBit();
+  if (show_existing_frame_)
+    return true;
+
+  key_ = !ReadBit();
+  altref_ = !ReadBit();
+  error_resilient_mode_ = ReadBit();
+  if (key_) {
+    if (!ValidateVp9SyncCode()) {
+      fprintf(stderr, "Invalid Sync code!\n");
+      return false;
+    }
+
+    ParseColorSpace();
+    ParseFrameResolution();
+    ParseFrameParallelMode();
+    ParseTileInfo();
+  } else {
+    intra_only_ = altref_ ? ReadBit() : 0;
+    reset_frame_context_ = error_resilient_mode_ ? 0 : VpxReadLiteral(2);
+    if (intra_only_) {
+      if (!ValidateVp9SyncCode()) {
+        fprintf(stderr, "Invalid Sync code!\n");
+        return false;
+      }
+
+      if (profile_ > 0) {
+        ParseColorSpace();
+      } else {
+        // NOTE: The intra-only frame header does not include the specification
+        // of either the color format or color sub-sampling in profile 0. VP9
+        // specifies that the default color format should be YUV 4:2:0 in this
+        // case (normative).
+        color_space_ = kVpxCsBt601;
+        color_range_ = kVpxCrStudioRange;
+        subsampling_y_ = subsampling_x_ = 1;
+        bit_depth_ = 8;
+      }
+
+      refresh_frame_flags_ = VpxReadLiteral(kRefFrames);
+      ParseFrameResolution();
+    } else {
+      refresh_frame_flags_ = VpxReadLiteral(kRefFrames);
+      for (int i = 0; i < kRefsPerFrame; ++i) {
+        VpxReadLiteral(kRefFrames_LOG2);  // Consume ref.
+        ReadBit();  // Consume ref sign bias.
+      }
+
+      bool found = false;
+      for (int i = 0; i < kRefsPerFrame; ++i) {
+        if (ReadBit()) {
+          // Found previous reference, width and height did not change since
+          // last frame.
+          found = true;
+          break;
+        }
+      }
+
+      if (!found)
+        ParseFrameResolution();
+    }
+  }
+  return true;
+}
+
+int Vp9HeaderParser::ReadBit() {
+  const size_t off = bit_offset_;
+  const size_t byte_offset = off >> 3;
+  const int bit_shift = 7 - static_cast<int>(off & 0x7);
+  if (byte_offset < frame_size_) {
+    const int bit = (frame_[byte_offset] >> bit_shift) & 1;
+    bit_offset_++;
+    return bit;
+  } else {
+    return 0;
+  }
+}
+
+int Vp9HeaderParser::VpxReadLiteral(int bits) {
+  int value = 0;
+  for (int bit = bits - 1; bit >= 0; --bit)
+    value |= ReadBit() << bit;
+  return value;
+}
+
+bool Vp9HeaderParser::ValidateVp9SyncCode() {
+  const int sync_code_0 = VpxReadLiteral(8);
+  const int sync_code_1 = VpxReadLiteral(8);
+  const int sync_code_2 = VpxReadLiteral(8);
+  return (sync_code_0 == 0x49 && sync_code_1 == 0x83 && sync_code_2 == 0x42);
+}
+
+void Vp9HeaderParser::ParseColorSpace() {
+  bit_depth_ = 0;
+  if (profile_ >= 2)
+    bit_depth_ = ReadBit() ? 12 : 10;
+  else
+    bit_depth_ = 8;
+  color_space_ = VpxReadLiteral(3);
+  if (color_space_ != kVpxCsSrgb) {
+    color_range_ = ReadBit();
+    if (profile_ == 1 || profile_ == 3) {
+      subsampling_x_ = ReadBit();
+      subsampling_y_ = ReadBit();
+      ReadBit();
+    } else {
+      subsampling_y_ = subsampling_x_ = 1;
+    }
+  } else {
+    color_range_ = kVpxCrFullRange;
+    if (profile_ == 1 || profile_ == 3) {
+      subsampling_y_ = subsampling_x_ = 0;
+      ReadBit();
+    }
+  }
+}
+
+void Vp9HeaderParser::ParseFrameResolution() {
+  width_ = VpxReadLiteral(16) + 1;
+  height_ = VpxReadLiteral(16) + 1;
+}
+
+void Vp9HeaderParser::ParseFrameParallelMode() {
+  if (ReadBit()) {
+    VpxReadLiteral(16);  // display width
+    VpxReadLiteral(16);  // display height
+  }
+  if (!error_resilient_mode_) {
+    ReadBit();  // Consume refresh frame context
+    frame_parallel_mode_ = ReadBit();
+  } else {
+    frame_parallel_mode_ = 1;
+  }
+}
+
+void Vp9HeaderParser::ParseTileInfo() {
+  VpxReadLiteral(2);  // Consume frame context index
+
+  // loopfilter
+  VpxReadLiteral(6);  // Consume filter level
+  VpxReadLiteral(3);  // Consume sharpness level
+
+  const bool mode_ref_delta_enabled = ReadBit();
+  if (mode_ref_delta_enabled) {
+    const bool mode_ref_delta_update = ReadBit();
+    if (mode_ref_delta_update) {
+      const int kMaxRefLFDeltas = 4;
+      for (int i = 0; i < kMaxRefLFDeltas; ++i) {
+        if (ReadBit())
+          VpxReadLiteral(7);  // Consume ref_deltas + sign
+      }
+
+      const int kMaxModeDeltas = 2;
+      for (int i = 0; i < kMaxModeDeltas; ++i) {
+        if (ReadBit())
+          VpxReadLiteral(7);  // Consume mode_delta + sign
+      }
+    }
+  }
+
+  // quantization
+  VpxReadLiteral(8);  // Consume base_q
+  SkipDeltaQ();  // y dc
+  SkipDeltaQ();  // uv ac
+  SkipDeltaQ();  // uv dc
+
+  // segmentation
+  const bool segmentation_enabled = ReadBit();
+  if (!segmentation_enabled) {
+    const int aligned_width = AlignPowerOfTwo(width_, kMiSizeLog2);
+    const int mi_cols = aligned_width >> kMiSizeLog2;
+    const int aligned_mi_cols = AlignPowerOfTwo(mi_cols, kMiSizeLog2);
+    const int sb_cols = aligned_mi_cols >> 3;  // to_sbs(mi_cols);
+    int min_log2_n_tiles, max_log2_n_tiles;
+
+    for (max_log2_n_tiles = 0;
+         (sb_cols >> max_log2_n_tiles) >= kMinTileWidthB64;
+         max_log2_n_tiles++) {
+    }
+    max_log2_n_tiles--;
+    if (max_log2_n_tiles < 0)
+      max_log2_n_tiles = 0;
+
+    for (min_log2_n_tiles = 0; (kMaxTileWidthB64 << min_log2_n_tiles) < sb_cols;
+         min_log2_n_tiles++) {
+    }
+
+    // columns
+    const int max_log2_tile_cols = max_log2_n_tiles;
+    const int min_log2_tile_cols = min_log2_n_tiles;
+    int max_ones = max_log2_tile_cols - min_log2_tile_cols;
+    int log2_tile_cols = min_log2_tile_cols;
+    while (max_ones-- && ReadBit())
+      log2_tile_cols++;
+
+    // rows
+    int log2_tile_rows = ReadBit();
+    if (log2_tile_rows)
+      log2_tile_rows += ReadBit();
+
+    row_tiles_ = 1 << log2_tile_rows;
+    column_tiles_ = 1 << log2_tile_cols;
+  }
+}
+
+void Vp9HeaderParser::SkipDeltaQ() {
+  if (ReadBit())
+    VpxReadLiteral(4);
+}
+
+int Vp9HeaderParser::AlignPowerOfTwo(int value, int n) {
+  return (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1));
+}
+
+}  // namespace vp9_parser
diff --git a/common/vp9_header_parser.h b/common/vp9_header_parser.h
new file mode 100644
index 0000000..06bd656
--- /dev/null
+++ b/common/vp9_header_parser.h
@@ -0,0 +1,125 @@
+// 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_VP9_HEADER_PARSER_H_
+#define LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace vp9_parser {
+
+const int kVp9FrameMarker = 2;
+const int kMinTileWidthB64 = 4;
+const int kMaxTileWidthB64 = 64;
+const int kRefFrames = 8;
+const int kRefsPerFrame = 3;
+const int kRefFrames_LOG2 = 3;
+const int kVpxCsBt601 = 1;
+const int kVpxCsSrgb = 7;
+const int kVpxCrStudioRange = 0;
+const int kVpxCrFullRange = 1;
+const int kMiSizeLog2 = 3;
+
+// Class to parse the header of a VP9 frame.
+class Vp9HeaderParser {
+ public:
+  Vp9HeaderParser()
+      : frame_(NULL),
+        frame_size_(0),
+        bit_offset_(0),
+        profile_(-1),
+        show_existing_frame_(0),
+        key_(0),
+        altref_(0),
+        error_resilient_mode_(0),
+        intra_only_(0),
+        reset_frame_context_(0),
+        bit_depth_(0),
+        color_space_(0),
+        color_range_(0),
+        subsampling_x_(0),
+        subsampling_y_(0),
+        refresh_frame_flags_(0),
+        width_(0),
+        height_(0),
+        row_tiles_(0),
+        column_tiles_(0),
+        frame_parallel_mode_(0) {}
+
+  // Parse the VP9 uncompressed header. The return values of the remaining
+  // functions are only valid on success.
+  bool ParseUncompressedHeader(const uint8_t* frame, size_t length);
+
+  size_t frame_size() const { return frame_size_; }
+  int profile() const { return profile_; }
+  int key() const { return key_; }
+  int altref() const { return altref_; }
+  int error_resilient_mode() const { return error_resilient_mode_; }
+  int bit_depth() const { return bit_depth_; }
+  int color_space() const { return color_space_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int refresh_frame_flags() const { return refresh_frame_flags_; }
+  int row_tiles() const { return row_tiles_; }
+  int column_tiles() const { return column_tiles_; }
+  int frame_parallel_mode() const { return frame_parallel_mode_; }
+
+ private:
+  // Set the compressed VP9 frame.
+  bool SetFrame(const uint8_t* frame, size_t length);
+
+  // Returns the next bit of the frame.
+  int ReadBit();
+
+  // Returns the next |bits| of the frame.
+  int VpxReadLiteral(int bits);
+
+  // Returns true if the vp9 sync code is valid.
+  bool ValidateVp9SyncCode();
+
+  // Parses bit_depth_, color_space_, subsampling_x_, subsampling_y_, and
+  // color_range_.
+  void ParseColorSpace();
+
+  // Parses width and height of the frame.
+  void ParseFrameResolution();
+
+  // Parses frame_parallel_mode_. This function skips over some features.
+  void ParseFrameParallelMode();
+
+  // Parses row and column tiles. This function skips over some features.
+  void ParseTileInfo();
+  void SkipDeltaQ();
+  int AlignPowerOfTwo(int value, int n);
+
+  const uint8_t* frame_;
+  size_t frame_size_;
+  size_t bit_offset_;
+  int profile_;
+  int show_existing_frame_;
+  int key_;
+  int altref_;
+  int error_resilient_mode_;
+  int intra_only_;
+  int reset_frame_context_;
+  int bit_depth_;
+  int color_space_;
+  int color_range_;
+  int subsampling_x_;
+  int subsampling_y_;
+  int refresh_frame_flags_;
+  int width_;
+  int height_;
+  int row_tiles_;
+  int column_tiles_;
+  int frame_parallel_mode_;
+};
+
+}  // namespace vp9_parser
+
+#endif  // LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
diff --git a/common/vp9_header_parser_tests.cc b/common/vp9_header_parser_tests.cc
new file mode 100644
index 0000000..d13bff2
--- /dev/null
+++ b/common/vp9_header_parser_tests.cc
@@ -0,0 +1,181 @@
+// 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/vp9_header_parser.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "common/hdr_util.h"
+#include "mkvparser/mkvparser.h"
+#include "mkvparser/mkvreader.h"
+#include "testing/test_util.h"
+
+namespace {
+
+class Vp9HeaderParserTests : public ::testing::Test {
+ public:
+  Vp9HeaderParserTests() : is_reader_open_(false), segment_(NULL) {}
+
+  ~Vp9HeaderParserTests() override {
+    CloseReader();
+    if (segment_ != NULL) {
+      delete segment_;
+      segment_ = NULL;
+    }
+  }
+
+  void CloseReader() {
+    if (is_reader_open_) {
+      reader_.Close();
+    }
+    is_reader_open_ = false;
+  }
+
+  void CreateAndLoadSegment(const std::string& filename,
+                            int expected_doc_type_ver) {
+    filename_ = test::GetTestFilePath(filename);
+    ASSERT_EQ(0, reader_.Open(filename_.c_str()));
+    is_reader_open_ = true;
+    pos_ = 0;
+    mkvparser::EBMLHeader ebml_header;
+    ebml_header.Parse(&reader_, pos_);
+    ASSERT_EQ(1, ebml_header.m_version);
+    ASSERT_EQ(1, ebml_header.m_readVersion);
+    ASSERT_STREQ("webm", ebml_header.m_docType);
+    ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
+    ASSERT_EQ(2, ebml_header.m_docTypeReadVersion);
+    ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
+    ASSERT_FALSE(HasFailure());
+    ASSERT_GE(0, segment_->Load());
+  }
+
+  void CreateAndLoadSegment(const std::string& filename) {
+    CreateAndLoadSegment(filename, 4);
+  }
+
+  // Load a corrupted segment with no expectation of correctness.
+  void CreateAndLoadInvalidSegment(const std::string& filename) {
+    filename_ = test::GetTestFilePath(filename);
+    ASSERT_EQ(0, reader_.Open(filename_.c_str()));
+    is_reader_open_ = true;
+    pos_ = 0;
+    mkvparser::EBMLHeader ebml_header;
+    ebml_header.Parse(&reader_, pos_);
+    ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
+    ASSERT_GE(0, segment_->Load());
+  }
+
+  void ProcessTheFrames(bool invalid_bitstream) {
+    unsigned char* data = NULL;
+    size_t data_len = 0;
+    const mkvparser::Tracks* const parser_tracks = segment_->GetTracks();
+    ASSERT_TRUE(parser_tracks != NULL);
+    const mkvparser::Cluster* cluster = segment_->GetFirst();
+    ASSERT_TRUE(cluster != NULL);
+
+    while ((cluster != NULL) && !cluster->EOS()) {
+      const mkvparser::BlockEntry* block_entry;
+      long status = cluster->GetFirst(block_entry);  // NOLINT
+      ASSERT_EQ(0, status);
+
+      while ((block_entry != NULL) && !block_entry->EOS()) {
+        const mkvparser::Block* const block = block_entry->GetBlock();
+        ASSERT_TRUE(block != NULL);
+        const long long trackNum = block->GetTrackNumber();  // NOLINT
+        const mkvparser::Track* const parser_track =
+            parser_tracks->GetTrackByNumber(
+                static_cast<unsigned long>(trackNum));  // NOLINT
+        ASSERT_TRUE(parser_track != NULL);
+        const long long track_type = parser_track->GetType();  // NOLINT
+
+        if (track_type == mkvparser::Track::kVideo) {
+          const int frame_count = block->GetFrameCount();
+
+          for (int i = 0; i < frame_count; ++i) {
+            const mkvparser::Block::Frame& frame = block->GetFrame(i);
+
+            if (static_cast<size_t>(frame.len) > data_len) {
+              delete[] data;
+              data = new unsigned char[frame.len];
+              ASSERT_TRUE(data != NULL);
+              data_len = static_cast<size_t>(frame.len);
+            }
+            ASSERT_FALSE(frame.Read(&reader_, data));
+            ASSERT_EQ(parser_.ParseUncompressedHeader(data, data_len),
+                      !invalid_bitstream);
+          }
+        }
+
+        status = cluster->GetNext(block_entry, block_entry);
+        ASSERT_EQ(0, status);
+      }
+
+      cluster = segment_->GetNext(cluster);
+    }
+    delete[] data;
+  }
+
+ protected:
+  mkvparser::MkvReader reader_;
+  bool is_reader_open_;
+  mkvparser::Segment* segment_;
+  std::string filename_;
+  long long pos_;  // NOLINT
+  vp9_parser::Vp9HeaderParser parser_;
+};
+
+TEST_F(Vp9HeaderParserTests, VideoOnlyFile) {
+  ASSERT_NO_FATAL_FAILURE(CreateAndLoadSegment("test_stereo_left_right.webm"));
+  ProcessTheFrames(false);
+  EXPECT_EQ(256, parser_.width());
+  EXPECT_EQ(144, parser_.height());
+  EXPECT_EQ(1, parser_.column_tiles());
+  EXPECT_EQ(0, parser_.frame_parallel_mode());
+}
+
+TEST_F(Vp9HeaderParserTests, Muxed) {
+  ASSERT_NO_FATAL_FAILURE(
+      CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4));
+  ProcessTheFrames(false);
+  EXPECT_EQ(854, parser_.width());
+  EXPECT_EQ(480, parser_.height());
+  EXPECT_EQ(2, parser_.column_tiles());
+  EXPECT_EQ(1, parser_.frame_parallel_mode());
+}
+
+TEST_F(Vp9HeaderParserTests, Invalid) {
+  const char* files[] = {
+      "invalid/invalid_vp9_bitstream-bug_1416.webm",
+      "invalid/invalid_vp9_bitstream-bug_1417.webm",
+  };
+
+  for (int i = 0; i < static_cast<int>(sizeof(files) / sizeof(files[0])); ++i) {
+    SCOPED_TRACE(files[i]);
+    ASSERT_NO_FATAL_FAILURE(CreateAndLoadInvalidSegment(files[i]));
+    ProcessTheFrames(true);
+    CloseReader();
+    delete segment_;
+    segment_ = NULL;
+  }
+}
+
+TEST_F(Vp9HeaderParserTests, API) {
+  vp9_parser::Vp9HeaderParser parser;
+  uint8_t data;
+  EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 0));
+  EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 10));
+  EXPECT_FALSE(parser.ParseUncompressedHeader(&data, 0));
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/common/vp9_level_stats.cc b/common/vp9_level_stats.cc
new file mode 100644
index 0000000..76891e6
--- /dev/null
+++ b/common/vp9_level_stats.cc
@@ -0,0 +1,269 @@
+// 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/vp9_level_stats.h"
+
+#include <inttypes.h>
+
+#include <limits>
+#include <utility>
+
+#include "common/webm_constants.h"
+
+namespace vp9_parser {
+
+const Vp9LevelRow Vp9LevelStats::Vp9LevelTable[kNumVp9Levels] = {
+    {LEVEL_1, 829440, 36864, 200, 400, 2, 1, 4, 8, 512},
+    {LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8, 768},
+    {LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8, 960},
+    {LEVEL_2_1, 9216000, 245760, 3600, 2800, 2, 2, 4, 8, 1344},
+    {LEVEL_3, 20736000, 552960, 7200, 6000, 2, 4, 4, 8, 2048},
+    {LEVEL_3_1, 36864000, 983040, 12000, 10000, 2, 4, 4, 8, 2752},
+    {LEVEL_4, 83558400, 2228224, 18000, 16000, 4, 4, 4, 8, 4160},
+    {LEVEL_4_1, 160432128, 2228224, 30000, 18000, 4, 4, 5, 6, 4160},
+    {LEVEL_5, 311951360, 8912896, 60000, 36000, 6, 8, 6, 4, 8384},
+    {LEVEL_5_1, 588251136, 8912896, 120000, 46000, 8, 8, 10, 4, 8384},
+    // CPB Size = 0 for levels 5_2 to 6_2
+    {LEVEL_5_2, 1176502272, 8912896, 180000, 0, 8, 8, 10, 4, 8384},
+    {LEVEL_6, 1176502272, 35651584, 180000, 0, 8, 16, 10, 4, 16832},
+    {LEVEL_6_1, 2353004544, 35651584, 240000, 0, 8, 16, 10, 4, 16832},
+    {LEVEL_6_2, 4706009088, 35651584, 480000, 0, 8, 16, 10, 4, 16832}};
+
+void Vp9LevelStats::AddFrame(const Vp9HeaderParser& parser, int64_t time_ns) {
+  ++frames;
+  if (start_ns_ == -1)
+    start_ns_ = time_ns;
+  end_ns_ = time_ns;
+
+  const int width = parser.width();
+  const int height = parser.height();
+  const int64_t luma_picture_size = width * height;
+  const int64_t luma_picture_breadth = (width > height) ? width : height;
+  if (luma_picture_size > max_luma_picture_size_)
+    max_luma_picture_size_ = luma_picture_size;
+  if (luma_picture_breadth > max_luma_picture_breadth_)
+    max_luma_picture_breadth_ = luma_picture_breadth;
+
+  total_compressed_size_ += parser.frame_size();
+
+  while (!luma_window_.empty() &&
+         luma_window_.front().first <
+             (time_ns - (libwebm::kNanosecondsPerSecondi - 1))) {
+    current_luma_size_ -= luma_window_.front().second;
+    luma_window_.pop();
+  }
+  current_luma_size_ += luma_picture_size;
+  luma_window_.push(std::make_pair(time_ns, luma_picture_size));
+  if (current_luma_size_ > max_luma_size_) {
+    max_luma_size_ = current_luma_size_;
+    max_luma_end_ns_ = luma_window_.back().first;
+  }
+
+  // Record CPB stats.
+  // Remove all frames that are less than window size.
+  while (cpb_window_.size() > 3) {
+    current_cpb_size_ -= cpb_window_.front().second;
+    cpb_window_.pop();
+  }
+  cpb_window_.push(std::make_pair(time_ns, parser.frame_size()));
+
+  current_cpb_size_ += parser.frame_size();
+  if (current_cpb_size_ > max_cpb_size_) {
+    max_cpb_size_ = current_cpb_size_;
+    max_cpb_start_ns_ = cpb_window_.front().first;
+    max_cpb_end_ns_ = cpb_window_.back().first;
+  }
+
+  if (max_cpb_window_size_ < static_cast<int64_t>(cpb_window_.size())) {
+    max_cpb_window_size_ = cpb_window_.size();
+    max_cpb_window_end_ns_ = time_ns;
+  }
+
+  // Record altref stats.
+  if (parser.altref()) {
+    const int delta_altref = frames_since_last_altref;
+    if (first_altref) {
+      first_altref = false;
+    } else if (delta_altref < minimum_altref_distance) {
+      minimum_altref_distance = delta_altref;
+      min_altref_end_ns = time_ns;
+    }
+    frames_since_last_altref = 0;
+  } else {
+    ++frames_since_last_altref;
+    ++displayed_frames;
+    // TODO(fgalligan): Add support for other color formats. Currently assuming
+    // 420.
+    total_uncompressed_bits_ +=
+        (luma_picture_size * parser.bit_depth() * 3) / 2;
+  }
+
+  // Count max reference frames.
+  if (parser.key() == 1) {
+    frames_refreshed_ = 0;
+  } else {
+    frames_refreshed_ |= parser.refresh_frame_flags();
+
+    int ref_frame_count = frames_refreshed_ & 1;
+    for (int i = 1; i < kMaxVp9RefFrames; ++i) {
+      ref_frame_count += (frames_refreshed_ >> i) & 1;
+    }
+
+    if (ref_frame_count > max_frames_refreshed_)
+      max_frames_refreshed_ = ref_frame_count;
+  }
+
+  // Count max tiles.
+  const int tiles = parser.column_tiles();
+  if (tiles > max_column_tiles_)
+    max_column_tiles_ = tiles;
+}
+
+Vp9Level Vp9LevelStats::GetLevel() const {
+  const int64_t max_luma_sample_rate = GetMaxLumaSampleRate();
+  const int64_t max_luma_picture_size = GetMaxLumaPictureSize();
+  const int64_t max_luma_picture_breadth = GetMaxLumaPictureBreadth();
+  const double average_bitrate = GetAverageBitRate();
+  const double max_cpb_size = GetMaxCpbSize();
+  const double compresion_ratio = GetCompressionRatio();
+  const int max_column_tiles = GetMaxColumnTiles();
+  const int min_altref_distance = GetMinimumAltrefDistance();
+  const int max_ref_frames = GetMaxReferenceFrames();
+
+  int level_index = 0;
+  Vp9Level max_level = LEVEL_UNKNOWN;
+  const double grace_multiplier =
+      max_luma_sample_rate_grace_percent_ / 100.0 + 1.0;
+  for (int i = 0; i < kNumVp9Levels; ++i) {
+    if (max_luma_sample_rate <=
+        Vp9LevelTable[i].max_luma_sample_rate * grace_multiplier) {
+      if (max_level < Vp9LevelTable[i].level) {
+        max_level = Vp9LevelTable[i].level;
+        level_index = i;
+      }
+      break;
+    }
+  }
+  for (int i = 0; i < kNumVp9Levels; ++i) {
+    if (max_luma_picture_size <= Vp9LevelTable[i].max_luma_picture_size) {
+      if (max_level < Vp9LevelTable[i].level) {
+        max_level = Vp9LevelTable[i].level;
+        level_index = i;
+      }
+      break;
+    }
+  }
+  for (int i = 0; i < kNumVp9Levels; ++i) {
+    if (max_luma_picture_breadth <= Vp9LevelTable[i].max_luma_picture_breadth) {
+      if (max_level < Vp9LevelTable[i].level) {
+        max_level = Vp9LevelTable[i].level;
+        level_index = i;
+      }
+      break;
+    }
+  }
+  for (int i = 0; i < kNumVp9Levels; ++i) {
+    if (average_bitrate <= Vp9LevelTable[i].average_bitrate) {
+      if (max_level < Vp9LevelTable[i].level) {
+        max_level = Vp9LevelTable[i].level;
+        level_index = i;
+      }
+      break;
+    }
+  }
+  for (int i = 0; i < kNumVp9Levels; ++i) {
+    // Only check CPB size for levels that are defined.
+    if (Vp9LevelTable[i].max_cpb_size > 0 &&
+        max_cpb_size <= Vp9LevelTable[i].max_cpb_size) {
+      if (max_level < Vp9LevelTable[i].level) {
+        max_level = Vp9LevelTable[i].level;
+        level_index = i;
+      }
+      break;
+    }
+  }
+  for (int i = 0; i < kNumVp9Levels; ++i) {
+    if (max_column_tiles <= Vp9LevelTable[i].max_tiles) {
+      if (max_level < Vp9LevelTable[i].level) {
+        max_level = Vp9LevelTable[i].level;
+        level_index = i;
+      }
+      break;
+    }
+  }
+
+  for (int i = 0; i < kNumVp9Levels; ++i) {
+    if (max_ref_frames <= Vp9LevelTable[i].max_ref_frames) {
+      if (max_level < Vp9LevelTable[i].level) {
+        max_level = Vp9LevelTable[i].level;
+        level_index = i;
+      }
+      break;
+    }
+  }
+
+  // Check if the current level meets the minimum altref distance requirement.
+  // If not, set to unknown level as we can't move up a level as the minimum
+  // altref distance get farther apart and we can't move down a level as we are
+  // already at the minimum level for all the other requirements.
+  if (min_altref_distance < Vp9LevelTable[level_index].min_altref_distance)
+    max_level = LEVEL_UNKNOWN;
+
+  // The minimum compression ratio has the same behavior as minimum altref
+  // distance.
+  if (compresion_ratio < Vp9LevelTable[level_index].compresion_ratio)
+    max_level = LEVEL_UNKNOWN;
+  return max_level;
+}
+
+int64_t Vp9LevelStats::GetMaxLumaSampleRate() const { return max_luma_size_; }
+
+int64_t Vp9LevelStats::GetMaxLumaPictureSize() const {
+  return max_luma_picture_size_;
+}
+
+int64_t Vp9LevelStats::GetMaxLumaPictureBreadth() const {
+  return max_luma_picture_breadth_;
+}
+
+double Vp9LevelStats::GetAverageBitRate() const {
+  const int64_t frame_duration_ns = end_ns_ - start_ns_;
+  double duration_seconds =
+      ((duration_ns_ == -1) ? frame_duration_ns : duration_ns_) /
+      libwebm::kNanosecondsPerSecond;
+  if (estimate_last_frame_duration_ &&
+      (duration_ns_ == -1 || duration_ns_ <= frame_duration_ns)) {
+    const double sec_per_frame = frame_duration_ns /
+                                 libwebm::kNanosecondsPerSecond /
+                                 (displayed_frames - 1);
+    duration_seconds += sec_per_frame;
+  }
+
+  return total_compressed_size_ / duration_seconds / 125.0;
+}
+
+double Vp9LevelStats::GetMaxCpbSize() const { return max_cpb_size_ / 125.0; }
+
+double Vp9LevelStats::GetCompressionRatio() const {
+  return total_uncompressed_bits_ /
+         static_cast<double>(total_compressed_size_ * 8);
+}
+
+int Vp9LevelStats::GetMaxColumnTiles() const { return max_column_tiles_; }
+
+int Vp9LevelStats::GetMinimumAltrefDistance() const {
+  if (minimum_altref_distance != std::numeric_limits<int>::max())
+    return minimum_altref_distance;
+  else
+    return -1;
+}
+
+int Vp9LevelStats::GetMaxReferenceFrames() const {
+  return max_frames_refreshed_;
+}
+
+}  // namespace vp9_parser
diff --git a/common/vp9_level_stats.h b/common/vp9_level_stats.h
new file mode 100644
index 0000000..45d6f5c
--- /dev/null
+++ b/common/vp9_level_stats.h
@@ -0,0 +1,215 @@
+// 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_VP9_LEVEL_STATS_H_
+#define LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
+
+#include <limits>
+#include <queue>
+#include <utility>
+
+#include "common/vp9_header_parser.h"
+
+namespace vp9_parser {
+
+const int kMaxVp9RefFrames = 8;
+
+// Defined VP9 levels. See http://www.webmproject.org/vp9/profiles/ for
+// detailed information on VP9 levels.
+const int kNumVp9Levels = 14;
+enum Vp9Level {
+  LEVEL_UNKNOWN = 0,
+  LEVEL_1 = 10,
+  LEVEL_1_1 = 11,
+  LEVEL_2 = 20,
+  LEVEL_2_1 = 21,
+  LEVEL_3 = 30,
+  LEVEL_3_1 = 31,
+  LEVEL_4 = 40,
+  LEVEL_4_1 = 41,
+  LEVEL_5 = 50,
+  LEVEL_5_1 = 51,
+  LEVEL_5_2 = 52,
+  LEVEL_6 = 60,
+  LEVEL_6_1 = 61,
+  LEVEL_6_2 = 62
+};
+
+struct Vp9LevelRow {
+  Vp9LevelRow() = default;
+  ~Vp9LevelRow() = default;
+  Vp9LevelRow(Vp9LevelRow&& other) = default;
+  Vp9LevelRow(const Vp9LevelRow& other) = default;
+  Vp9LevelRow& operator=(Vp9LevelRow&& other) = delete;
+  Vp9LevelRow& operator=(const Vp9LevelRow& other) = delete;
+
+  Vp9Level level;
+  int64_t max_luma_sample_rate;
+  int64_t max_luma_picture_size;
+  int64_t max_luma_picture_breadth;
+  double average_bitrate;
+  double max_cpb_size;
+  double compresion_ratio;
+  int max_tiles;
+  int min_altref_distance;
+  int max_ref_frames;
+};
+
+// Class to determine the VP9 level of a VP9 bitstream.
+class Vp9LevelStats {
+ public:
+  static const Vp9LevelRow Vp9LevelTable[kNumVp9Levels];
+
+  Vp9LevelStats()
+      : frames(0),
+        displayed_frames(0),
+        start_ns_(-1),
+        end_ns_(-1),
+        duration_ns_(-1),
+        max_luma_picture_size_(0),
+        max_luma_picture_breadth_(0),
+        current_luma_size_(0),
+        max_luma_size_(0),
+        max_luma_end_ns_(0),
+        max_luma_sample_rate_grace_percent_(1.5),
+        first_altref(true),
+        frames_since_last_altref(0),
+        minimum_altref_distance(std::numeric_limits<int>::max()),
+        min_altref_end_ns(0),
+        max_cpb_window_size_(0),
+        max_cpb_window_end_ns_(0),
+        current_cpb_size_(0),
+        max_cpb_size_(0),
+        max_cpb_start_ns_(0),
+        max_cpb_end_ns_(0),
+        total_compressed_size_(0),
+        total_uncompressed_bits_(0),
+        frames_refreshed_(0),
+        max_frames_refreshed_(0),
+        max_column_tiles_(0),
+        estimate_last_frame_duration_(true) {}
+
+  ~Vp9LevelStats() = default;
+  Vp9LevelStats(Vp9LevelStats&& other) = delete;
+  Vp9LevelStats(const Vp9LevelStats& other) = delete;
+  Vp9LevelStats& operator=(Vp9LevelStats&& other) = delete;
+  Vp9LevelStats& operator=(const Vp9LevelStats& other) = delete;
+
+  // Collects stats on a VP9 frame. The frame must already be parsed by
+  // |parser|. |time_ns| is the start time of the frame in nanoseconds.
+  void AddFrame(const Vp9HeaderParser& parser, int64_t time_ns);
+
+  // Returns the current VP9 level. All of the video frames should have been
+  // processed with AddFrame before calling this function.
+  Vp9Level GetLevel() const;
+
+  // Returns the maximum luma samples (pixels) per second. The Alt-Ref frames
+  // are taken into account, therefore this number may be larger than the
+  // display luma samples per second
+  int64_t GetMaxLumaSampleRate() const;
+
+  // The maximum frame size (width * height) in samples.
+  int64_t GetMaxLumaPictureSize() const;
+
+  // The maximum frame breadth (max of width and height) in samples.
+  int64_t GetMaxLumaPictureBreadth() const;
+
+  // The average bitrate of the video in kbps.
+  double GetAverageBitRate() const;
+
+  // The largest data size for any 4 consecutive frames in kilobits.
+  double GetMaxCpbSize() const;
+
+  // The ratio of total bytes decompressed over total bytes compressed.
+  double GetCompressionRatio() const;
+
+  // The maximum number of VP9 column tiles.
+  int GetMaxColumnTiles() const;
+
+  // The minimum distance in frames between two consecutive alternate reference
+  // frames.
+  int GetMinimumAltrefDistance() const;
+
+  // The maximum number of reference frames that had to be stored.
+  int GetMaxReferenceFrames() const;
+
+  // Sets the duration of the video stream in nanoseconds. If the duration is
+  // not explictly set by this function then this class will use end - start
+  // as the duration.
+  void set_duration(int64_t time_ns) { duration_ns_ = time_ns; }
+  double max_luma_sample_rate_grace_percent() const {
+    return max_luma_sample_rate_grace_percent_;
+  }
+  void set_max_luma_sample_rate_grace_percent(double percent) {
+    max_luma_sample_rate_grace_percent_ = percent;
+  }
+  bool estimate_last_frame_duration() const {
+    return estimate_last_frame_duration_;
+  }
+
+  // If true try to estimate the last frame's duration if the stream's duration
+  // is not set or the stream's duration equals the last frame's timestamp.
+  void set_estimate_last_frame_duration(bool flag) {
+    estimate_last_frame_duration_ = flag;
+  }
+
+ private:
+  int frames;
+  int displayed_frames;
+
+  int64_t start_ns_;
+  int64_t end_ns_;
+  int64_t duration_ns_;
+
+  int64_t max_luma_picture_size_;
+  int64_t max_luma_picture_breadth_;
+
+  // This is used to calculate the maximum number of luma samples per second.
+  // The first value is the luma picture size and the second value is the time
+  // in nanoseconds of one frame.
+  std::queue<std::pair<int64_t, int64_t>> luma_window_;
+  int64_t current_luma_size_;
+  int64_t max_luma_size_;
+  int64_t max_luma_end_ns_;
+
+  // MaxLumaSampleRate = (ExampleFrameRate + ExampleFrameRate /
+  // MinimumAltrefDistance) * MaxLumaPictureSize. For levels 1-4
+  // ExampleFrameRate / MinimumAltrefDistance is non-integer, so using a sliding
+  // window of one frame to calculate MaxLumaSampleRate may have frames >
+  // (ExampleFrameRate + ExampleFrameRate / MinimumAltrefDistance) in the
+  // window. In order to address this issue, a grace percent of 1.5 was added.
+  double max_luma_sample_rate_grace_percent_;
+
+  bool first_altref;
+  int frames_since_last_altref;
+  int minimum_altref_distance;
+  int64_t min_altref_end_ns;
+
+  // This is used to calculate the maximum number of compressed bytes for four
+  // consecutive frames. The first value is the compressed frame size and the
+  // second value is the time in nanoseconds of one frame.
+  std::queue<std::pair<int64_t, int64_t>> cpb_window_;
+  int64_t max_cpb_window_size_;
+  int64_t max_cpb_window_end_ns_;
+  int64_t current_cpb_size_;
+  int64_t max_cpb_size_;
+  int64_t max_cpb_start_ns_;
+  int64_t max_cpb_end_ns_;
+
+  int64_t total_compressed_size_;
+  int64_t total_uncompressed_bits_;
+  int frames_refreshed_;
+  int max_frames_refreshed_;
+
+  int max_column_tiles_;
+
+  bool estimate_last_frame_duration_;
+};
+
+}  // namespace vp9_parser
+
+#endif  // LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
diff --git a/common/vp9_level_stats_tests.cc b/common/vp9_level_stats_tests.cc
new file mode 100644
index 0000000..0dec071
--- /dev/null
+++ b/common/vp9_level_stats_tests.cc
@@ -0,0 +1,191 @@
+// 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/vp9_level_stats.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "common/hdr_util.h"
+#include "common/vp9_header_parser.h"
+#include "mkvparser/mkvparser.h"
+#include "mkvparser/mkvreader.h"
+#include "testing/test_util.h"
+
+namespace {
+
+// TODO(fgalligan): Refactor this test with other test files in this directory.
+class Vp9LevelStatsTests : public ::testing::Test {
+ public:
+  Vp9LevelStatsTests() : is_reader_open_(false) {}
+
+  ~Vp9LevelStatsTests() override { CloseReader(); }
+
+  void CloseReader() {
+    if (is_reader_open_) {
+      reader_.Close();
+    }
+    is_reader_open_ = false;
+  }
+
+  void CreateAndLoadSegment(const std::string& filename,
+                            int expected_doc_type_ver) {
+    ASSERT_NE(0u, filename.length());
+    filename_ = test::GetTestFilePath(filename);
+    ASSERT_EQ(0, reader_.Open(filename_.c_str()));
+    is_reader_open_ = true;
+    pos_ = 0;
+    mkvparser::EBMLHeader ebml_header;
+    ebml_header.Parse(&reader_, pos_);
+    ASSERT_EQ(1, ebml_header.m_version);
+    ASSERT_EQ(1, ebml_header.m_readVersion);
+    ASSERT_STREQ("webm", ebml_header.m_docType);
+    ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
+    ASSERT_EQ(2, ebml_header.m_docTypeReadVersion);
+    mkvparser::Segment* temp;
+    ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, temp));
+    segment_.reset(temp);
+    ASSERT_FALSE(HasFailure());
+    ASSERT_GE(0, segment_->Load());
+  }
+
+  void CreateAndLoadSegment(const std::string& filename) {
+    CreateAndLoadSegment(filename, 4);
+  }
+
+  void ProcessTheFrames() {
+    std::vector<uint8_t> data;
+    size_t data_len = 0;
+    const mkvparser::Tracks* const parser_tracks = segment_->GetTracks();
+    ASSERT_TRUE(parser_tracks != NULL);
+    const mkvparser::Cluster* cluster = segment_->GetFirst();
+    ASSERT_TRUE(cluster);
+
+    while ((cluster != NULL) && !cluster->EOS()) {
+      const mkvparser::BlockEntry* block_entry;
+      long status = cluster->GetFirst(block_entry);  // NOLINT
+      ASSERT_EQ(0, status);
+
+      while ((block_entry != NULL) && !block_entry->EOS()) {
+        const mkvparser::Block* const block = block_entry->GetBlock();
+        ASSERT_TRUE(block != NULL);
+        const long long trackNum = block->GetTrackNumber();  // NOLINT
+        const mkvparser::Track* const parser_track =
+            parser_tracks->GetTrackByNumber(
+                static_cast<unsigned long>(trackNum));  // NOLINT
+        ASSERT_TRUE(parser_track != NULL);
+        const long long track_type = parser_track->GetType();  // NOLINT
+
+        if (track_type == mkvparser::Track::kVideo) {
+          const int frame_count = block->GetFrameCount();
+          const long long time_ns = block->GetTime(cluster);  // NOLINT
+
+          for (int i = 0; i < frame_count; ++i) {
+            const mkvparser::Block::Frame& frame = block->GetFrame(i);
+            if (static_cast<size_t>(frame.len) > data.size()) {
+              data.resize(frame.len);
+              data_len = static_cast<size_t>(frame.len);
+            }
+            ASSERT_FALSE(frame.Read(&reader_, &data[0]));
+            parser_.ParseUncompressedHeader(&data[0], data_len);
+            stats_.AddFrame(parser_, time_ns);
+          }
+        }
+
+        status = cluster->GetNext(block_entry, block_entry);
+        ASSERT_EQ(0, status);
+      }
+
+      cluster = segment_->GetNext(cluster);
+    }
+  }
+
+ protected:
+  mkvparser::MkvReader reader_;
+  bool is_reader_open_;
+  std::unique_ptr<mkvparser::Segment> segment_;
+  std::string filename_;
+  long long pos_;  // NOLINT
+  vp9_parser::Vp9HeaderParser parser_;
+  vp9_parser::Vp9LevelStats stats_;
+};
+
+TEST_F(Vp9LevelStatsTests, VideoOnlyFile) {
+  CreateAndLoadSegment("test_stereo_left_right.webm");
+  ProcessTheFrames();
+  EXPECT_EQ(256, parser_.width());
+  EXPECT_EQ(144, parser_.height());
+  EXPECT_EQ(1, parser_.column_tiles());
+  EXPECT_EQ(0, parser_.frame_parallel_mode());
+
+  EXPECT_EQ(11, stats_.GetLevel());
+  EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate());
+  EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize());
+  EXPECT_DOUBLE_EQ(264.03233333333333, stats_.GetAverageBitRate());
+  EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize());
+  EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio());
+  EXPECT_EQ(1, stats_.GetMaxColumnTiles());
+  EXPECT_EQ(11, stats_.GetMinimumAltrefDistance());
+  EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
+
+  EXPECT_TRUE(stats_.estimate_last_frame_duration());
+  stats_.set_estimate_last_frame_duration(false);
+  EXPECT_DOUBLE_EQ(275.512, stats_.GetAverageBitRate());
+}
+
+TEST_F(Vp9LevelStatsTests, Muxed) {
+  CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4);
+  ProcessTheFrames();
+  EXPECT_EQ(854, parser_.width());
+  EXPECT_EQ(480, parser_.height());
+  EXPECT_EQ(2, parser_.column_tiles());
+  EXPECT_EQ(1, parser_.frame_parallel_mode());
+
+  EXPECT_EQ(30, stats_.GetLevel());
+  EXPECT_EQ(9838080, stats_.GetMaxLumaSampleRate());
+  EXPECT_EQ(409920, stats_.GetMaxLumaPictureSize());
+  EXPECT_DOUBLE_EQ(447.09394572025053, stats_.GetAverageBitRate());
+  EXPECT_DOUBLE_EQ(118.464, stats_.GetMaxCpbSize());
+  EXPECT_DOUBLE_EQ(241.17670131398313, stats_.GetCompressionRatio());
+  EXPECT_EQ(2, stats_.GetMaxColumnTiles());
+  EXPECT_EQ(9, stats_.GetMinimumAltrefDistance());
+  EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
+
+  stats_.set_estimate_last_frame_duration(false);
+  EXPECT_DOUBLE_EQ(468.38413361169108, stats_.GetAverageBitRate());
+}
+
+TEST_F(Vp9LevelStatsTests, SetDuration) {
+  CreateAndLoadSegment("test_stereo_left_right.webm");
+  ProcessTheFrames();
+  const int64_t kDurationNano = 2080000000;  // 2.08 seconds
+  stats_.set_duration(kDurationNano);
+  EXPECT_EQ(256, parser_.width());
+  EXPECT_EQ(144, parser_.height());
+  EXPECT_EQ(1, parser_.column_tiles());
+  EXPECT_EQ(0, parser_.frame_parallel_mode());
+
+  EXPECT_EQ(11, stats_.GetLevel());
+  EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate());
+  EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize());
+  EXPECT_DOUBLE_EQ(264.9153846153846, stats_.GetAverageBitRate());
+  EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize());
+  EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio());
+  EXPECT_EQ(1, stats_.GetMaxColumnTiles());
+  EXPECT_EQ(11, stats_.GetMinimumAltrefDistance());
+  EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/common/webm_constants.h b/common/webm_constants.h
new file mode 100644
index 0000000..a082ce8
--- /dev/null
+++ b/common/webm_constants.h
@@ -0,0 +1,20 @@
+// 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 LIBWEBM_COMMON_WEBM_CONSTANTS_H_
+#define LIBWEBM_COMMON_WEBM_CONSTANTS_H_
+
+namespace libwebm {
+
+const double kNanosecondsPerSecond = 1000000000.0;
+const int kNanosecondsPerSecondi = 1000000000;
+const int kNanosecondsPerMillisecond = 1000000;
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_COMMON_WEBM_CONSTANTS_H_
diff --git a/common/webm_endian.cc b/common/webm_endian.cc
new file mode 100644
index 0000000..b1ef7ca
--- /dev/null
+++ b/common/webm_endian.cc
@@ -0,0 +1,85 @@
+// 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 "common/webm_endian.h"
+
+#include <stdint.h>
+
+namespace {
+
+// Swaps unsigned 32 bit values to big endian if needed. Returns |value|
+// unmodified if architecture is big endian. Returns swapped bytes of |value|
+// if architecture is little endian. Returns 0 otherwise.
+uint32_t swap32_check_little_endian(uint32_t value) {
+  // Check endianness.
+  union {
+    uint32_t val32;
+    uint8_t c[4];
+  } check;
+  check.val32 = 0x01234567U;
+
+  // Check for big endian.
+  if (check.c[3] == 0x67)
+    return value;
+
+  // Check for not little endian.
+  if (check.c[0] != 0x67)
+    return 0;
+
+  return value << 24 | ((value << 8) & 0x0000FF00U) |
+         ((value >> 8) & 0x00FF0000U) | value >> 24;
+}
+
+// Swaps unsigned 64 bit values to big endian if needed. Returns |value|
+// unmodified if architecture is big endian. Returns swapped bytes of |value|
+// if architecture is little endian. Returns 0 otherwise.
+uint64_t swap64_check_little_endian(uint64_t value) {
+  // Check endianness.
+  union {
+    uint64_t val64;
+    uint8_t c[8];
+  } check;
+  check.val64 = 0x0123456789ABCDEFULL;
+
+  // Check for big endian.
+  if (check.c[7] == 0xEF)
+    return value;
+
+  // Check for not little endian.
+  if (check.c[0] != 0xEF)
+    return 0;
+
+  return value << 56 | ((value << 40) & 0x00FF000000000000ULL) |
+         ((value << 24) & 0x0000FF0000000000ULL) |
+         ((value << 8) & 0x000000FF00000000ULL) |
+         ((value >> 8) & 0x00000000FF000000ULL) |
+         ((value >> 24) & 0x0000000000FF0000ULL) |
+         ((value >> 40) & 0x000000000000FF00ULL) | value >> 56;
+}
+
+}  // namespace
+
+namespace libwebm {
+
+uint32_t host_to_bigendian(uint32_t value) {
+  return swap32_check_little_endian(value);
+}
+
+uint32_t bigendian_to_host(uint32_t value) {
+  return swap32_check_little_endian(value);
+}
+
+uint64_t host_to_bigendian(uint64_t value) {
+  return swap64_check_little_endian(value);
+}
+
+uint64_t bigendian_to_host(uint64_t value) {
+  return swap64_check_little_endian(value);
+}
+
+}  // namespace libwebm
diff --git a/common/webm_endian.h b/common/webm_endian.h
new file mode 100644
index 0000000..351778b
--- /dev/null
+++ b/common/webm_endian.h
@@ -0,0 +1,38 @@
+// 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 LIBWEBM_COMMON_WEBM_ENDIAN_H_
+#define LIBWEBM_COMMON_WEBM_ENDIAN_H_
+
+#include <stdint.h>
+
+namespace libwebm {
+
+// Swaps unsigned 32 bit values to big endian if needed. Returns |value| if
+// architecture is big endian. Returns big endian value if architecture is
+// little endian. Returns 0 otherwise.
+uint32_t host_to_bigendian(uint32_t value);
+
+// Swaps unsigned 32 bit values to little endian if needed. Returns |value| if
+// architecture is big endian. Returns little endian value if architecture is
+// little endian. Returns 0 otherwise.
+uint32_t bigendian_to_host(uint32_t value);
+
+// Swaps unsigned 64 bit values to big endian if needed. Returns |value| if
+// architecture is big endian. Returns big endian value if architecture is
+// little endian. Returns 0 otherwise.
+uint64_t host_to_bigendian(uint64_t value);
+
+// Swaps unsigned 64 bit values to little endian if needed. Returns |value| if
+// architecture is big endian. Returns little endian value if architecture is
+// little endian. Returns 0 otherwise.
+uint64_t bigendian_to_host(uint64_t value);
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_COMMON_WEBM_ENDIAN_H_
diff --git a/common/webmids.h b/common/webmids.h
new file mode 100644
index 0000000..fc0c208
--- /dev/null
+++ b/common/webmids.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS.  All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef COMMON_WEBMIDS_H_
+#define COMMON_WEBMIDS_H_
+
+namespace libwebm {
+
+enum MkvId {
+  kMkvEBML = 0x1A45DFA3,
+  kMkvEBMLVersion = 0x4286,
+  kMkvEBMLReadVersion = 0x42F7,
+  kMkvEBMLMaxIDLength = 0x42F2,
+  kMkvEBMLMaxSizeLength = 0x42F3,
+  kMkvDocType = 0x4282,
+  kMkvDocTypeVersion = 0x4287,
+  kMkvDocTypeReadVersion = 0x4285,
+  kMkvVoid = 0xEC,
+  kMkvSignatureSlot = 0x1B538667,
+  kMkvSignatureAlgo = 0x7E8A,
+  kMkvSignatureHash = 0x7E9A,
+  kMkvSignaturePublicKey = 0x7EA5,
+  kMkvSignature = 0x7EB5,
+  kMkvSignatureElements = 0x7E5B,
+  kMkvSignatureElementList = 0x7E7B,
+  kMkvSignedElement = 0x6532,
+  // segment
+  kMkvSegment = 0x18538067,
+  // Meta Seek Information
+  kMkvSeekHead = 0x114D9B74,
+  kMkvSeek = 0x4DBB,
+  kMkvSeekID = 0x53AB,
+  kMkvSeekPosition = 0x53AC,
+  // Segment Information
+  kMkvInfo = 0x1549A966,
+  kMkvTimecodeScale = 0x2AD7B1,
+  kMkvDuration = 0x4489,
+  kMkvDateUTC = 0x4461,
+  kMkvTitle = 0x7BA9,
+  kMkvMuxingApp = 0x4D80,
+  kMkvWritingApp = 0x5741,
+  // Cluster
+  kMkvCluster = 0x1F43B675,
+  kMkvTimecode = 0xE7,
+  kMkvPrevSize = 0xAB,
+  kMkvBlockGroup = 0xA0,
+  kMkvBlock = 0xA1,
+  kMkvBlockDuration = 0x9B,
+  kMkvReferenceBlock = 0xFB,
+  kMkvLaceNumber = 0xCC,
+  kMkvSimpleBlock = 0xA3,
+  kMkvBlockAdditions = 0x75A1,
+  kMkvBlockMore = 0xA6,
+  kMkvBlockAddID = 0xEE,
+  kMkvBlockAdditional = 0xA5,
+  kMkvDiscardPadding = 0x75A2,
+  // Track
+  kMkvTracks = 0x1654AE6B,
+  kMkvTrackEntry = 0xAE,
+  kMkvTrackNumber = 0xD7,
+  kMkvTrackUID = 0x73C5,
+  kMkvTrackType = 0x83,
+  kMkvFlagEnabled = 0xB9,
+  kMkvFlagDefault = 0x88,
+  kMkvFlagForced = 0x55AA,
+  kMkvFlagLacing = 0x9C,
+  kMkvDefaultDuration = 0x23E383,
+  kMkvMaxBlockAdditionID = 0x55EE,
+  kMkvName = 0x536E,
+  kMkvLanguage = 0x22B59C,
+  kMkvCodecID = 0x86,
+  kMkvCodecPrivate = 0x63A2,
+  kMkvCodecName = 0x258688,
+  kMkvCodecDelay = 0x56AA,
+  kMkvSeekPreRoll = 0x56BB,
+  // video
+  kMkvVideo = 0xE0,
+  kMkvFlagInterlaced = 0x9A,
+  kMkvStereoMode = 0x53B8,
+  kMkvAlphaMode = 0x53C0,
+  kMkvPixelWidth = 0xB0,
+  kMkvPixelHeight = 0xBA,
+  kMkvPixelCropBottom = 0x54AA,
+  kMkvPixelCropTop = 0x54BB,
+  kMkvPixelCropLeft = 0x54CC,
+  kMkvPixelCropRight = 0x54DD,
+  kMkvDisplayWidth = 0x54B0,
+  kMkvDisplayHeight = 0x54BA,
+  kMkvDisplayUnit = 0x54B2,
+  kMkvAspectRatioType = 0x54B3,
+  kMkvColourSpace = 0x2EB524,
+  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
+  // projection
+  kMkvProjection = 0x7670,
+  kMkvProjectionType = 0x7671,
+  kMkvProjectionPrivate = 0x7672,
+  kMkvProjectionPoseYaw = 0x7673,
+  kMkvProjectionPosePitch = 0x7674,
+  kMkvProjectionPoseRoll = 0x7675,
+  // end projection
+  // audio
+  kMkvAudio = 0xE1,
+  kMkvSamplingFrequency = 0xB5,
+  kMkvOutputSamplingFrequency = 0x78B5,
+  kMkvChannels = 0x9F,
+  kMkvBitDepth = 0x6264,
+  // end audio
+  // ContentEncodings
+  kMkvContentEncodings = 0x6D80,
+  kMkvContentEncoding = 0x6240,
+  kMkvContentEncodingOrder = 0x5031,
+  kMkvContentEncodingScope = 0x5032,
+  kMkvContentEncodingType = 0x5033,
+  kMkvContentCompression = 0x5034,
+  kMkvContentCompAlgo = 0x4254,
+  kMkvContentCompSettings = 0x4255,
+  kMkvContentEncryption = 0x5035,
+  kMkvContentEncAlgo = 0x47E1,
+  kMkvContentEncKeyID = 0x47E2,
+  kMkvContentSignature = 0x47E3,
+  kMkvContentSigKeyID = 0x47E4,
+  kMkvContentSigAlgo = 0x47E5,
+  kMkvContentSigHashAlgo = 0x47E6,
+  kMkvContentEncAESSettings = 0x47E7,
+  kMkvAESSettingsCipherMode = 0x47E8,
+  kMkvAESSettingsCipherInitData = 0x47E9,
+  // end ContentEncodings
+  // Cueing Data
+  kMkvCues = 0x1C53BB6B,
+  kMkvCuePoint = 0xBB,
+  kMkvCueTime = 0xB3,
+  kMkvCueTrackPositions = 0xB7,
+  kMkvCueTrack = 0xF7,
+  kMkvCueClusterPosition = 0xF1,
+  kMkvCueBlockNumber = 0x5378,
+  // Chapters
+  kMkvChapters = 0x1043A770,
+  kMkvEditionEntry = 0x45B9,
+  kMkvChapterAtom = 0xB6,
+  kMkvChapterUID = 0x73C4,
+  kMkvChapterStringUID = 0x5654,
+  kMkvChapterTimeStart = 0x91,
+  kMkvChapterTimeEnd = 0x92,
+  kMkvChapterDisplay = 0x80,
+  kMkvChapString = 0x85,
+  kMkvChapLanguage = 0x437C,
+  kMkvChapCountry = 0x437E,
+  // Tags
+  kMkvTags = 0x1254C367,
+  kMkvTag = 0x7373,
+  kMkvSimpleTag = 0x67C8,
+  kMkvTagName = 0x45A3,
+  kMkvTagString = 0x4487
+};
+
+}  // namespace libwebm
+
+#endif  // COMMON_WEBMIDS_H_
diff --git a/dumpvtt.cc b/dumpvtt.cc
new file mode 100644
index 0000000..472da52
--- /dev/null
+++ b/dumpvtt.cc
@@ -0,0 +1,91 @@
+// 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 <cstdio>
+#include <cstdlib>
+#include "webvtt/vttreader.h"
+#include "webvtt/webvttparser.h"
+
+int main(int argc, const char* argv[]) {
+  if (argc != 2) {
+    fprintf(stdout, "usage: dumpvtt <vtt file>\n");
+    return EXIT_SUCCESS;
+  }
+
+  libwebvtt::VttReader reader;
+  const char* const filename = argv[1];
+
+  if (int e = reader.Open(filename)) {
+    (void)e;
+    fprintf(stderr, "open failed\n");
+    return EXIT_FAILURE;
+  }
+
+  libwebvtt::Parser parser(&reader);
+
+  if (int e = parser.Init()) {
+    (void)e;
+    fprintf(stderr, "parser init failed\n");
+    return EXIT_FAILURE;
+  }
+
+  for (libwebvtt::Cue cue;;) {
+    const int e = parser.Parse(&cue);
+
+    if (e < 0) {  // error
+      fprintf(stderr, "error parsing cue\n");
+      return EXIT_FAILURE;
+    }
+
+    if (e > 0)  // EOF
+      return EXIT_SUCCESS;
+
+    fprintf(stdout, "cue identifier: \"%s\"\n", cue.identifier.c_str());
+
+    const libwebvtt::Time& st = cue.start_time;
+    fprintf(stdout, "cue start time: \"HH=%i MM=%i SS=%i SSS=%i\"\n", st.hours,
+            st.minutes, st.seconds, st.milliseconds);
+
+    const libwebvtt::Time& sp = cue.stop_time;
+    fprintf(stdout, "cue stop time: \"HH=%i MM=%i SS=%i SSS=%i\"\n", sp.hours,
+            sp.minutes, sp.seconds, sp.milliseconds);
+
+    {
+      typedef libwebvtt::Cue::settings_t::const_iterator iter_t;
+      iter_t i = cue.settings.begin();
+      const iter_t j = cue.settings.end();
+
+      if (i == j) {
+        fprintf(stdout, "cue setting: <no settings present>\n");
+      } else {
+        while (i != j) {
+          const libwebvtt::Setting& setting = *i++;
+          fprintf(stdout, "cue setting: name=%s value=%s\n",
+                  setting.name.c_str(), setting.value.c_str());
+        }
+      }
+    }
+
+    {
+      typedef libwebvtt::Cue::payload_t::const_iterator iter_t;
+      iter_t i = cue.payload.begin();
+      const iter_t j = cue.payload.end();
+
+      int idx = 1;
+      while (i != j) {
+        const std::string& payload = *i++;
+        const char* const payload_str = payload.c_str();
+        fprintf(stdout, "cue payload[%i]: \"%s\"\n", idx, payload_str);
+        ++idx;
+      }
+    }
+
+    fprintf(stdout, "\n");
+    fflush(stdout);
+  }
+}
diff --git a/hdr_util.hpp b/hdr_util.hpp
new file mode 100644
index 0000000..7abcb3a
--- /dev/null
+++ b/hdr_util.hpp
@@ -0,0 +1,15 @@
+// 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_HDR_UTIL_HPP_
+#define LIBWEBM_HDR_UTIL_HPP_
+
+// This file is a wrapper for the file included immediately after this comment.
+// New projects should not include this file: include the file included below.
+#include "common/hdr_util.h"
+
+#endif  // LIBWEBM_HDR_UTIL_HPP_
diff --git a/iosbuild.sh b/iosbuild.sh
new file mode 100755
index 0000000..a183af3
--- /dev/null
+++ b/iosbuild.sh
@@ -0,0 +1,207 @@
+#!/bin/sh
+##
+##  Copyright (c) 2015 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.
+##
+## This script generates 'WebM.framework'. An iOS app can mux/demux WebM
+## container files by including 'WebM.framework'.
+##
+## Run ./iosbuild.sh to generate 'WebM.framework'. By default the framework
+## bundle will be created in a directory called framework. Use --out-dir to
+## change the output directory.
+##
+## This script is based on iosbuild.sh from the libwebp project.
+. $(dirname $0)/common/common.sh
+
+# Trap function. Cleans up build output.
+cleanup() {
+  local readonly res=$?
+  cd "${ORIG_PWD}"
+
+  for dir in ${LIBDIRS}; do
+    if [ -d "${dir}" ]; then
+      rm -rf "${dir}"
+    fi
+  done
+
+  if [ $res -ne 0 ]; then
+    elog "build exited with error ($res)"
+  fi
+}
+
+trap cleanup EXIT
+
+check_dir libwebm
+
+iosbuild_usage() {
+cat << EOF
+  Usage: ${0##*/} [arguments]
+    --help: Display this message and exit.
+    --out-dir: Override output directory (default is ${OUTDIR}).
+    --show-build-output: Show output from each library build.
+    --verbose: Output information about the environment and each stage of the
+               build.
+EOF
+}
+
+# Extract the latest SDK version from the final field of the form: iphoneosX.Y
+readonly SDK=$(xcodebuild -showsdks \
+  | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
+)
+
+# Extract Xcode version.
+readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
+if [ -z "${XCODE}" ]; then
+  echo "Xcode not available"
+  exit 1
+fi
+
+# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
+# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
+readonly INCLUDES="common/file_util.h
+                   common/hdr_util.h
+                   common/webmids.h
+                   mkvmuxer/mkvmuxer.h
+                   mkvmuxer/mkvmuxertypes.h
+                   mkvmuxer/mkvmuxerutil.h
+                   mkvmuxer/mkvwriter.h
+                   mkvparser/mkvparser.h
+                   mkvparser/mkvreader.h"
+readonly PLATFORMS="iPhoneSimulator
+                    iPhoneSimulator64
+                    iPhoneOS-V7
+                    iPhoneOS-V7s
+                    iPhoneOS-V7-arm64"
+readonly TARGETDIR="WebM.framework"
+readonly DEVELOPER="$(xcode-select --print-path)"
+readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
+readonly LIPO="$(xcrun -sdk iphoneos${SDK} -find lipo)"
+LIBLIST=""
+OPT_FLAGS="-DNDEBUG -O3"
+readonly SDK_MAJOR_VERSION="$(echo ${SDK} | awk -F '.' '{ print $1 }')"
+
+if [ -z "${SDK_MAJOR_VERSION}" ]; then
+  elog "iOS SDK not available"
+  exit 1
+elif [ "${SDK_MAJOR_VERSION}" -lt "6" ]; then
+  elog "You need iOS SDK version 6 or above"
+  exit 1
+else
+  vlog "iOS SDK Version ${SDK}"
+fi
+
+
+# Parse the command line.
+while [ -n "$1" ]; do
+  case "$1" in
+    --help)
+      iosbuild_usage
+      exit
+      ;;
+    --out-dir)
+      OUTDIR="$2"
+      shift
+      ;;
+    --enable-debug)
+      OPT_FLAGS="-g"
+      ;;
+    --show-build-output)
+      devnull=
+      ;;
+    --verbose)
+      VERBOSE=yes
+      ;;
+    *)
+      iosbuild_usage
+      exit 1
+      ;;
+  esac
+  shift
+done
+
+readonly OPT_FLAGS="${OPT_FLAGS}"
+readonly OUTDIR="${OUTDIR:-framework}"
+
+if [ "${VERBOSE}" = "yes" ]; then
+cat << EOF
+  OUTDIR=${OUTDIR}
+  INCLUDES=${INCLUDES}
+  PLATFORMS=${PLATFORMS}
+  TARGETDIR=${TARGETDIR}
+  DEVELOPER=${DEVELOPER}
+  LIPO=${LIPO}
+  OPT_FLAGS=${OPT_FLAGS}
+  ORIG_PWD=${ORIG_PWD}
+EOF
+fi
+
+rm -rf "${OUTDIR}/${TARGETDIR}"
+mkdir -p "${OUTDIR}/${TARGETDIR}/Headers/"
+
+for PLATFORM in ${PLATFORMS}; do
+  ARCH2=""
+  if [ "${PLATFORM}" = "iPhoneOS-V7-arm64" ]; then
+    PLATFORM="iPhoneOS"
+    ARCH="aarch64"
+    ARCH2="arm64"
+  elif [ "${PLATFORM}" = "iPhoneOS-V7s" ]; then
+    PLATFORM="iPhoneOS"
+    ARCH="armv7s"
+  elif [ "${PLATFORM}" = "iPhoneOS-V7" ]; then
+    PLATFORM="iPhoneOS"
+    ARCH="armv7"
+  elif [ "${PLATFORM}" = "iPhoneOS-V6" ]; then
+    PLATFORM="iPhoneOS"
+    ARCH="armv6"
+  elif [ "${PLATFORM}" = "iPhoneSimulator64" ]; then
+    PLATFORM="iPhoneSimulator"
+    ARCH="x86_64"
+  else
+    ARCH="i386"
+  fi
+
+  LIBDIR="${OUTDIR}/${PLATFORM}-${SDK}-${ARCH}"
+  LIBDIRS="${LIBDIRS} ${LIBDIR}"
+  LIBFILE="${LIBDIR}/libwebm.a"
+  eval mkdir -p "${LIBDIR}" ${devnull}
+
+  DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
+  SDKROOT="${PLATFORMSROOT}/"
+  SDKROOT="${SDKROOT}${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/"
+  CXXFLAGS="-arch ${ARCH2:-${ARCH}} -isysroot ${SDKROOT} ${OPT_FLAGS}
+            -miphoneos-version-min=6.0"
+
+  # enable bitcode if available
+  if [ "${SDK_MAJOR_VERSION}" -gt 8 ]; then
+    CXXFLAGS="${CXXFLAGS} -fembed-bitcode"
+  fi
+
+  # Build using the legacy makefile (instead of generating via cmake).
+  eval make -f Makefile.unix libwebm.a CXXFLAGS=\"${CXXFLAGS}\" ${devnull}
+
+  # copy lib and add it to LIBLIST.
+  eval cp libwebm.a "${LIBFILE}" ${devnull}
+  LIBLIST="${LIBLIST} ${LIBFILE}"
+
+  # clean build so we can go again.
+  eval make -f Makefile.unix clean ${devnull}
+done
+
+# create include sub dirs in framework dir.
+readonly framework_header_dir="${OUTDIR}/${TARGETDIR}/Headers"
+readonly framework_header_sub_dirs="common mkvmuxer mkvparser"
+for dir in ${framework_header_sub_dirs}; do
+  mkdir "${framework_header_dir}/${dir}"
+done
+
+for header_file in ${INCLUDES}; do
+  eval cp -p ${header_file} "${framework_header_dir}/${header_file}" ${devnull}
+done
+
+eval ${LIPO} -create ${LIBLIST} -output "${OUTDIR}/${TARGETDIR}/WebM" ${devnull}
+echo "Succesfully built ${TARGETDIR} in ${OUTDIR}."
diff --git a/m2ts/tests/webm2pes_tests.cc b/m2ts/tests/webm2pes_tests.cc
new file mode 100644
index 0000000..664856f
--- /dev/null
+++ b/m2ts/tests/webm2pes_tests.cc
@@ -0,0 +1,158 @@
+// 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 "m2ts/webm2pes.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "common/file_util.h"
+#include "common/libwebm_util.h"
+#include "m2ts/vpxpes_parser.h"
+#include "testing/test_util.h"
+
+namespace {
+
+class Webm2PesTests : public ::testing::Test {
+ public:
+  // Constants for validating known values from input data.
+  const std::uint8_t kMinVideoStreamId = 0xE0;
+  const std::uint8_t kMaxVideoStreamId = 0xEF;
+  const int kPesHeaderSize = 6;
+  const int kPesOptionalHeaderStartOffset = kPesHeaderSize;
+  const int kPesOptionalHeaderSize = 9;
+  const int kPesOptionalHeaderMarkerValue = 0x2;
+  const int kWebm2PesOptHeaderRemainingSize = 6;
+  const int kBcmvHeaderSize = 10;
+
+  Webm2PesTests() = default;
+  ~Webm2PesTests() = default;
+
+  void CreateAndLoadTestInput() {
+    libwebm::Webm2Pes converter(input_file_name_, temp_file_name_.name());
+    ASSERT_TRUE(converter.ConvertToFile());
+    ASSERT_TRUE(parser_.Open(pes_file_name()));
+  }
+
+  bool VerifyPacketStartCode(const libwebm::VpxPesParser::PesHeader& header) {
+    // PES packets all start with the byte sequence 0x0 0x0 0x1.
+    if (header.start_code[0] != 0 || header.start_code[1] != 0 ||
+        header.start_code[2] != 1) {
+      return false;
+    }
+    return true;
+  }
+
+  const std::string& pes_file_name() const { return temp_file_name_.name(); }
+  libwebm::VpxPesParser* parser() { return &parser_; }
+
+ private:
+  const libwebm::TempFileDeleter temp_file_name_;
+  const std::string input_file_name_ =
+      test::GetTestFilePath("bbb_480p_vp9_opus_1second.webm");
+  libwebm::VpxPesParser parser_;
+};
+
+TEST_F(Webm2PesTests, CreatePesFile) { CreateAndLoadTestInput(); }
+
+TEST_F(Webm2PesTests, CanParseFirstPacket) {
+  CreateAndLoadTestInput();
+  libwebm::VpxPesParser::PesHeader header;
+  libwebm::VideoFrame frame;
+  ASSERT_TRUE(parser()->ParseNextPacket(&header, &frame));
+  EXPECT_TRUE(VerifyPacketStartCode(header));
+
+  //   9 bytes: PES optional header
+  //  10 bytes: BCMV Header
+  //  83 bytes: frame
+  // 102 bytes total in packet length field:
+  const std::size_t kPesPayloadLength = 102;
+  EXPECT_EQ(kPesPayloadLength, header.packet_length);
+
+  EXPECT_GE(header.stream_id, kMinVideoStreamId);
+  EXPECT_LE(header.stream_id, kMaxVideoStreamId);
+
+  // Test PesOptionalHeader values.
+  EXPECT_EQ(kPesOptionalHeaderMarkerValue, header.opt_header.marker);
+  EXPECT_EQ(kWebm2PesOptHeaderRemainingSize, header.opt_header.remaining_size);
+  EXPECT_EQ(0, header.opt_header.scrambling);
+  EXPECT_EQ(0, header.opt_header.priority);
+  EXPECT_EQ(0, header.opt_header.data_alignment);
+  EXPECT_EQ(0, header.opt_header.copyright);
+  EXPECT_EQ(0, header.opt_header.original);
+  EXPECT_EQ(1, header.opt_header.has_pts);
+  EXPECT_EQ(0, header.opt_header.has_dts);
+  EXPECT_EQ(0, header.opt_header.unused_fields);
+
+  // Test the BCMV header.
+  // Note: The length field of the BCMV header includes its own length.
+  const std::size_t kBcmvBaseLength = 10;
+  const std::size_t kFirstFrameLength = 83;
+  const libwebm::VpxPesParser::BcmvHeader kFirstBcmvHeader(kFirstFrameLength +
+                                                           kBcmvBaseLength);
+  EXPECT_TRUE(header.bcmv_header.Valid());
+  EXPECT_EQ(kFirstBcmvHeader, header.bcmv_header);
+
+  // Parse the next packet to confirm correct parse and consumption of payload.
+  EXPECT_TRUE(parser()->ParseNextPacket(&header, &frame));
+}
+
+TEST_F(Webm2PesTests, CanMuxLargeBuffers) {
+  const std::size_t kBufferSize = 100 * 1024;
+  const std::int64_t kFakeTimestamp = libwebm::kNanosecondsPerSecond;
+  libwebm::VideoFrame fake_frame(kFakeTimestamp, libwebm::VideoFrame::kVP9);
+  ASSERT_TRUE(fake_frame.Init(kBufferSize));
+  std::memset(fake_frame.buffer().data.get(), 0x80, kBufferSize);
+  ASSERT_TRUE(fake_frame.SetBufferLength(kBufferSize));
+  libwebm::PacketDataBuffer pes_packet_buffer;
+  ASSERT_TRUE(
+      libwebm::Webm2Pes::WritePesPacket(fake_frame, &pes_packet_buffer));
+
+  // TODO(tomfinegan): Change VpxPesParser so it can read from a buffer, and get
+  // rid of this extra step.
+  libwebm::FilePtr pes_file(std::fopen(pes_file_name().c_str(), "wb"),
+                            libwebm::FILEDeleter());
+  ASSERT_EQ(pes_packet_buffer.size(),
+            fwrite(&pes_packet_buffer[0], 1, pes_packet_buffer.size(),
+                   pes_file.get()));
+  fclose(pes_file.get());
+  pes_file.release();
+
+  libwebm::VpxPesParser parser;
+  ASSERT_TRUE(parser.Open(pes_file_name()));
+  libwebm::VpxPesParser::PesHeader header;
+  libwebm::VideoFrame parsed_frame;
+  ASSERT_TRUE(parser.ParseNextPacket(&header, &parsed_frame));
+  EXPECT_EQ(fake_frame.nanosecond_pts(), parsed_frame.nanosecond_pts());
+  EXPECT_EQ(fake_frame.buffer().length, parsed_frame.buffer().length);
+  EXPECT_EQ(0, std::memcmp(fake_frame.buffer().data.get(),
+                           parsed_frame.buffer().data.get(), kBufferSize));
+}
+
+TEST_F(Webm2PesTests, ParserConsumesAllInput) {
+  CreateAndLoadTestInput();
+  libwebm::VpxPesParser::PesHeader header;
+  libwebm::VideoFrame frame;
+  while (parser()->ParseNextPacket(&header, &frame) == true) {
+    EXPECT_TRUE(VerifyPacketStartCode(header));
+  }
+  EXPECT_EQ(0, parser()->BytesAvailable());
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/m2ts/vpxpes2ts.cc b/m2ts/vpxpes2ts.cc
new file mode 100644
index 0000000..7684b56
--- /dev/null
+++ b/m2ts/vpxpes2ts.cc
@@ -0,0 +1,217 @@
+// 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 "m2ts/vpxpes2ts.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdio>
+#include <vector>
+
+namespace libwebm {
+// TODO(tomfinegan): Dedupe this and PesHeaderField.
+// Stores a value and its size in bits for writing into a MPEG2 TS Header.
+// Maximum size is 64 bits. Users may call the Check() method to perform minimal
+// validation (size > 0 and <= 64).
+struct TsHeaderField {
+  TsHeaderField(std::uint64_t value, std::uint32_t size_in_bits,
+                std::uint8_t byte_index, std::uint8_t bits_to_shift)
+      : bits(value),
+        num_bits(size_in_bits),
+        index(byte_index),
+        shift(bits_to_shift) {}
+  TsHeaderField() = delete;
+  TsHeaderField(const TsHeaderField&) = default;
+  TsHeaderField(TsHeaderField&&) = default;
+  ~TsHeaderField() = default;
+  bool Check() const {
+    return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64;
+  }
+
+  // Value to be stored in the field.
+  std::uint64_t bits;
+
+  // Number of bits in the value.
+  const int num_bits;
+
+  // Index into the header for the byte in which |bits| will be written.
+  const std::uint8_t index;
+
+  // Number of bits to left shift value before or'ing. Ignored for whole bytes.
+  const int shift;
+};
+
+// Data storage for MPEG2 Transport Stream headers.
+// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet
+struct TsHeader {
+  TsHeader(bool payload_start, bool adaptation_flag, std::uint8_t counter)
+      : is_payload_start(payload_start),
+        has_adaptation(adaptation_flag),
+        counter_value(counter) {}
+  TsHeader() = delete;
+  TsHeader(const TsHeader&) = default;
+  TsHeader(TsHeader&&) = default;
+  ~TsHeader() = default;
+
+  void Write(PacketDataBuffer* buffer) const;
+
+  // Indicates the packet is the beginning of a new fragmented payload.
+  const bool is_payload_start;
+
+  // Indicates the packet contains an adaptation field.
+  const bool has_adaptation;
+
+  // The sync byte is the bit pattern of 0x47 (ASCII char 'G').
+  const std::uint8_t kTsHeaderSyncByte = 0x47;
+  const std::uint8_t sync_byte = kTsHeaderSyncByte;
+
+  // Value for |continuity_counter|. Used to detect gaps when demuxing.
+  const std::uint8_t counter_value;
+
+  // Set when FEC is impossible. Always 0.
+  const TsHeaderField transport_error_indicator = TsHeaderField(0, 1, 1, 7);
+
+  // This MPEG2 TS header is the start of a new payload (aka PES packet).
+  const TsHeaderField payload_unit_start_indicator =
+      TsHeaderField(is_payload_start ? 1 : 0, 1, 1, 6);
+
+  // Set when the current packet has a higher priority than other packets with
+  // the same PID. Always 0 for VPX.
+  const TsHeaderField transport_priority = TsHeaderField(0, 1, 1, 5);
+
+  // https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet_Identifier_.28PID.29
+  // 0x0020-0x1FFA May be assigned as needed to Program Map Tables, elementary
+  // streams and other data tables.
+  // Note: Though we hard code to 0x20, this value is actually 13 bits-- the
+  // buffer for the header is always set to 0, so it doesn't matter in practice.
+  const TsHeaderField pid = TsHeaderField(0x20, 8, 2, 0);
+
+  // Indicates scrambling key. Unused; always 0.
+  const TsHeaderField scrambling_control = TsHeaderField(0, 2, 3, 6);
+
+  // Adaptation field flag. Unused; always 0.
+  // TODO(tomfinegan): Not sure this is OK. Might need to add support for
+  // writing the Adaptation Field.
+  const TsHeaderField adaptation_field_flag =
+      TsHeaderField(has_adaptation ? 1 : 0, 1, 3, 5);
+
+  // Payload flag. All output packets created here have payloads. Always 1.
+  const TsHeaderField payload_flag = TsHeaderField(1, 1, 3, 4);
+
+  // Continuity counter. Two bit field that is incremented for every packet.
+  const TsHeaderField continuity_counter =
+      TsHeaderField(counter_value, 4, 3, 3);
+};
+
+void TsHeader::Write(PacketDataBuffer* buffer) const {
+  std::uint8_t* byte = &(*buffer)[0];
+  *byte = sync_byte;
+
+  *++byte = 0;
+  *byte |= transport_error_indicator.bits << transport_error_indicator.shift;
+  *byte |= payload_unit_start_indicator.bits
+           << payload_unit_start_indicator.shift;
+  *byte |= transport_priority.bits << transport_priority.shift;
+
+  *++byte = pid.bits & 0xff;
+
+  *++byte = 0;
+  *byte |= scrambling_control.bits << scrambling_control.shift;
+  *byte |= adaptation_field_flag.bits << adaptation_field_flag.shift;
+  *byte |= payload_flag.bits << payload_flag.shift;
+  *byte |= continuity_counter.bits;  // last 4 bits.
+}
+
+bool VpxPes2Ts::ConvertToFile() {
+  output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
+  if (output_file_ == nullptr) {
+    std::fprintf(stderr, "VpxPes2Ts: Cannot open %s for output.\n",
+                 output_file_name_.c_str());
+    return false;
+  }
+  pes_converter_.reset(new Webm2Pes(input_file_name_, this));
+  if (pes_converter_ == nullptr) {
+    std::fprintf(stderr, "VpxPes2Ts: Out of memory.\n");
+    return false;
+  }
+  return pes_converter_->ConvertToPacketReceiver();
+}
+
+bool VpxPes2Ts::ReceivePacket(const PacketDataBuffer& packet_data) {
+  const int kTsHeaderSize = 4;
+  const int kTsPayloadSize = 184;
+  const int kTsPacketSize = kTsHeaderSize + kTsPayloadSize;
+  int bytes_to_packetize = static_cast<int>(packet_data.size());
+  std::uint8_t continuity_counter = 0;
+  std::size_t read_pos = 0;
+
+  ts_buffer_.reserve(kTsPacketSize);
+
+  while (bytes_to_packetize > 0) {
+    if (continuity_counter > 0xf)
+      continuity_counter = 0;
+
+    // Calculate payload size (need to know if we'll have to pad with an empty
+    // adaptation field).
+    int payload_size = std::min(bytes_to_packetize, kTsPayloadSize);
+
+    // Write the TS header.
+    const TsHeader header(
+        bytes_to_packetize == static_cast<int>(packet_data.size()),
+        payload_size != kTsPayloadSize, continuity_counter);
+    header.Write(&ts_buffer_);
+    int write_pos = kTsHeaderSize;
+
+    // (pre)Pad payload with an empty adaptation field. All packets must be
+    // |kTsPacketSize| (188).
+    if (payload_size < kTsPayloadSize) {
+      // We need at least 2 bytes to write an empty adaptation field.
+      if (payload_size == (kTsPayloadSize - 1)) {
+        payload_size--;
+      }
+
+      // Padding adaptation field:
+      //   8 bits: number of adaptation field bytes following this byte.
+      //   8 bits: unused (in this program) flags.
+      // This is followed by a run of 0xff to reach |kTsPayloadSize| (184)
+      // bytes.
+      const int pad_size = kTsPayloadSize - payload_size - 1 - 1;
+      ts_buffer_[write_pos++] = pad_size + 1;
+      ts_buffer_[write_pos++] = 0;
+
+      const std::uint8_t kStuffingByte = 0xff;
+      for (int i = 0; i < pad_size; ++i) {
+        ts_buffer_[write_pos++] = kStuffingByte;
+      }
+    }
+
+    for (int i = 0; i < payload_size; ++i) {
+      ts_buffer_[write_pos++] = packet_data[read_pos++];
+    }
+
+    bytes_to_packetize -= payload_size;
+    continuity_counter++;
+
+    if (write_pos != kTsPacketSize) {
+      fprintf(stderr, "VpxPes2Ts: Invalid packet length.\n");
+      return false;
+    }
+
+    // Write contents of |ts_buffer_| to |output_file_|.
+    // TODO(tomfinegan): Writing 188 bytes at a time isn't exactly efficient...
+    // Fix me.
+    if (static_cast<int>(std::fwrite(&ts_buffer_[0], 1, kTsPacketSize,
+                                     output_file_.get())) != kTsPacketSize) {
+      std::fprintf(stderr, "VpxPes2Ts: TS packet write failed.\n");
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace libwebm
diff --git a/m2ts/vpxpes2ts.h b/m2ts/vpxpes2ts.h
new file mode 100644
index 0000000..2609a1f
--- /dev/null
+++ b/m2ts/vpxpes2ts.h
@@ -0,0 +1,45 @@
+// 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_M2TS_VPXPES2TS_H_
+#define LIBWEBM_M2TS_VPXPES2TS_H_
+
+#include <memory>
+#include <string>
+
+#include "common/libwebm_util.h"
+#include "m2ts/webm2pes.h"
+
+namespace libwebm {
+
+class VpxPes2Ts : public PacketReceiverInterface {
+ public:
+  VpxPes2Ts(const std::string& input_file_name,
+            const std::string& output_file_name)
+      : input_file_name_(input_file_name),
+        output_file_name_(output_file_name) {}
+  virtual ~VpxPes2Ts() = default;
+  VpxPes2Ts() = delete;
+  VpxPes2Ts(const VpxPes2Ts&) = delete;
+  VpxPes2Ts(VpxPes2Ts&&) = delete;
+
+  bool ConvertToFile();
+
+ private:
+  bool ReceivePacket(const PacketDataBuffer& packet_data) override;
+
+  const std::string input_file_name_;
+  const std::string output_file_name_;
+
+  FilePtr output_file_;
+  std::unique_ptr<Webm2Pes> pes_converter_;
+  PacketDataBuffer ts_buffer_;
+};
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_M2TS_VPXPES2TS_H_
diff --git a/m2ts/vpxpes2ts_main.cc b/m2ts/vpxpes2ts_main.cc
new file mode 100644
index 0000000..435d805
--- /dev/null
+++ b/m2ts/vpxpes2ts_main.cc
@@ -0,0 +1,33 @@
+// 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 "m2ts/vpxpes2ts.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+
+namespace {
+
+void Usage(const char* argv[]) {
+  printf("Usage: %s <WebM file> <output file>", argv[0]);
+}
+
+}  // namespace
+
+int main(int argc, const char* argv[]) {
+  if (argc < 3) {
+    Usage(argv);
+    return EXIT_FAILURE;
+  }
+
+  const std::string input_path = argv[1];
+  const std::string output_path = argv[2];
+
+  libwebm::VpxPes2Ts converter(input_path, output_path);
+  return converter.ConvertToFile() == true ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/m2ts/vpxpes_parser.cc b/m2ts/vpxpes_parser.cc
new file mode 100644
index 0000000..4f6fe5c
--- /dev/null
+++ b/m2ts/vpxpes_parser.cc
@@ -0,0 +1,409 @@
+// 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 "vpxpes_parser.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <limits>
+#include <vector>
+
+#include "common/file_util.h"
+
+namespace libwebm {
+
+VpxPesParser::BcmvHeader::BcmvHeader(std::uint32_t len) : length(len) {
+  id[0] = 'B';
+  id[1] = 'C';
+  id[2] = 'M';
+  id[3] = 'V';
+}
+
+bool VpxPesParser::BcmvHeader::operator==(const BcmvHeader& other) const {
+  return (other.length == length && other.id[0] == id[0] &&
+          other.id[1] == id[1] && other.id[2] == id[2] && other.id[3] == id[3]);
+}
+
+bool VpxPesParser::BcmvHeader::Valid() const {
+  return (length > 0 && id[0] == 'B' && id[1] == 'C' && id[2] == 'M' &&
+          id[3] == 'V');
+}
+
+// TODO(tomfinegan): Break Open() into separate functions. One that opens the
+// file, and one that reads one packet at a time. As things are files larger
+// than the maximum availble memory for the current process cannot be loaded.
+bool VpxPesParser::Open(const std::string& pes_file) {
+  pes_file_size_ = static_cast<size_t>(libwebm::GetFileSize(pes_file));
+  if (pes_file_size_ <= 0)
+    return false;
+  pes_file_data_.reserve(static_cast<size_t>(pes_file_size_));
+  libwebm::FilePtr file = libwebm::FilePtr(std::fopen(pes_file.c_str(), "rb"),
+                                           libwebm::FILEDeleter());
+  int byte;
+  while ((byte = fgetc(file.get())) != EOF) {
+    pes_file_data_.push_back(static_cast<std::uint8_t>(byte));
+  }
+
+  if (!feof(file.get()) || ferror(file.get()) ||
+      pes_file_size_ != pes_file_data_.size()) {
+    return false;
+  }
+
+  read_pos_ = 0;
+  parse_state_ = kFindStartCode;
+  return true;
+}
+
+bool VpxPesParser::VerifyPacketStartCode() const {
+  if (read_pos_ + 2 > pes_file_data_.size())
+    return false;
+
+  // PES packets all start with the byte sequence 0x0 0x0 0x1.
+  if (pes_file_data_[read_pos_] != 0 || pes_file_data_[read_pos_ + 1] != 0 ||
+      pes_file_data_[read_pos_ + 2] != 1) {
+    return false;
+  }
+
+  return true;
+}
+
+bool VpxPesParser::ReadStreamId(std::uint8_t* stream_id) const {
+  if (!stream_id || BytesAvailable() < 4)
+    return false;
+
+  *stream_id = pes_file_data_[read_pos_ + 3];
+  return true;
+}
+
+bool VpxPesParser::ReadPacketLength(std::uint16_t* packet_length) const {
+  if (!packet_length || BytesAvailable() < 6)
+    return false;
+
+  // Read and byte swap 16 bit big endian length.
+  *packet_length =
+      (pes_file_data_[read_pos_ + 4] << 8) | pes_file_data_[read_pos_ + 5];
+
+  return true;
+}
+
+bool VpxPesParser::ParsePesHeader(PesHeader* header) {
+  if (!header || parse_state_ != kParsePesHeader)
+    return false;
+
+  if (!VerifyPacketStartCode())
+    return false;
+
+  std::size_t pos = read_pos_;
+  for (auto& a : header->start_code) {
+    a = pes_file_data_[pos++];
+  }
+
+  // PES Video stream IDs start at E0.
+  if (!ReadStreamId(&header->stream_id))
+    return false;
+
+  if (header->stream_id < kMinVideoStreamId ||
+      header->stream_id > kMaxVideoStreamId)
+    return false;
+
+  if (!ReadPacketLength(&header->packet_length))
+    return false;
+
+  read_pos_ += kPesHeaderSize;
+  parse_state_ = kParsePesOptionalHeader;
+  return true;
+}
+
+// TODO(tomfinegan): Make these masks constants.
+bool VpxPesParser::ParsePesOptionalHeader(PesOptionalHeader* header) {
+  if (!header || parse_state_ != kParsePesOptionalHeader ||
+      read_pos_ >= pes_file_size_) {
+    return false;
+  }
+
+  std::size_t consumed = 0;
+  PacketData poh_buffer;
+  if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
+                                               kPesOptionalHeaderSize,
+                                               &poh_buffer, &consumed)) {
+    return false;
+  }
+
+  std::size_t offset = 0;
+  header->marker = (poh_buffer[offset] & 0x80) >> 6;
+  header->scrambling = (poh_buffer[offset] & 0x30) >> 4;
+  header->priority = (poh_buffer[offset] & 0x8) >> 3;
+  header->data_alignment = (poh_buffer[offset] & 0xc) >> 2;
+  header->copyright = (poh_buffer[offset] & 0x2) >> 1;
+  header->original = poh_buffer[offset] & 0x1;
+  offset++;
+
+  header->has_pts = (poh_buffer[offset] & 0x80) >> 7;
+  header->has_dts = (poh_buffer[offset] & 0x40) >> 6;
+  header->unused_fields = poh_buffer[offset] & 0x3f;
+  offset++;
+
+  header->remaining_size = poh_buffer[offset];
+  if (header->remaining_size !=
+      static_cast<int>(kWebm2PesOptHeaderRemainingSize))
+    return false;
+
+  size_t bytes_left = header->remaining_size;
+  offset++;
+
+  if (header->has_pts) {
+    // Read PTS markers. Format:
+    // PTS: 5 bytes
+    //   4 bits (flag: PTS present, but no DTS): 0x2 ('0010')
+    //   36 bits (90khz PTS):
+    //     top 3 bits
+    //     marker ('1')
+    //     middle 15 bits
+    //     marker ('1')
+    //     bottom 15 bits
+    //     marker ('1')
+    // TODO(tomfinegan): read/store the timestamp.
+    header->pts_dts_flag = (poh_buffer[offset] & 0x20) >> 4;
+    // Check the marker bits.
+    if ((poh_buffer[offset + 0] & 1) != 1 ||
+        (poh_buffer[offset + 2] & 1) != 1 ||
+        (poh_buffer[offset + 4] & 1) != 1) {
+      return false;
+    }
+
+    header->pts = (poh_buffer[offset] & 0xe) << 29 |
+                  ((ReadUint16(&poh_buffer[offset + 1]) & ~1) << 14) |
+                  (ReadUint16(&poh_buffer[offset + 3]) >> 1);
+    offset += 5;
+    bytes_left -= 5;
+  }
+
+  // Validate stuffing byte(s).
+  for (size_t i = 0; i < bytes_left; ++i) {
+    if (poh_buffer[offset + i] != 0xff)
+      return false;
+  }
+
+  read_pos_ += consumed;
+  parse_state_ = kParseBcmvHeader;
+
+  return true;
+}
+
+// Parses and validates a BCMV header.
+bool VpxPesParser::ParseBcmvHeader(BcmvHeader* header) {
+  if (!header || parse_state_ != kParseBcmvHeader)
+    return false;
+
+  PacketData bcmv_buffer;
+  std::size_t consumed = 0;
+  if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
+                                               kBcmvHeaderSize, &bcmv_buffer,
+                                               &consumed)) {
+    return false;
+  }
+
+  std::size_t offset = 0;
+  header->id[0] = bcmv_buffer[offset++];
+  header->id[1] = bcmv_buffer[offset++];
+  header->id[2] = bcmv_buffer[offset++];
+  header->id[3] = bcmv_buffer[offset++];
+
+  header->length = 0;
+  header->length |= bcmv_buffer[offset++] << 24;
+  header->length |= bcmv_buffer[offset++] << 16;
+  header->length |= bcmv_buffer[offset++] << 8;
+  header->length |= bcmv_buffer[offset++];
+
+  // Length stored in the BCMV header is followed by 2 bytes of 0 padding.
+  if (bcmv_buffer[offset++] != 0 || bcmv_buffer[offset++] != 0)
+    return false;
+
+  if (!header->Valid())
+    return false;
+
+  parse_state_ = kFindStartCode;
+  read_pos_ += consumed;
+
+  return true;
+}
+
+bool VpxPesParser::FindStartCode(std::size_t origin,
+                                 std::size_t* offset) const {
+  if (read_pos_ + 2 >= pes_file_size_)
+    return false;
+
+  const std::size_t length = pes_file_size_ - origin;
+  if (length < 3)
+    return false;
+
+  const uint8_t* const data = &pes_file_data_[origin];
+  for (std::size_t i = 0; i < length - 3; ++i) {
+    if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
+      *offset = origin + i;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool VpxPesParser::IsPayloadFragmented(const PesHeader& header) const {
+  return (header.packet_length != 0 &&
+          (header.packet_length - kPesOptionalHeaderSize) !=
+              header.bcmv_header.length);
+}
+
+bool VpxPesParser::AccumulateFragmentedPayload(std::size_t pes_packet_length,
+                                               std::size_t payload_length) {
+  const std::size_t first_fragment_length =
+      pes_packet_length - kPesOptionalHeaderSize - kBcmvHeaderSize;
+  for (std::size_t i = 0; i < first_fragment_length; ++i) {
+    payload_.push_back(pes_file_data_[read_pos_ + i]);
+  }
+  read_pos_ += first_fragment_length;
+  parse_state_ = kFindStartCode;
+
+  while (payload_.size() < payload_length) {
+    PesHeader header;
+    std::size_t packet_start_pos = read_pos_;
+    if (!FindStartCode(read_pos_, &packet_start_pos)) {
+      return false;
+    }
+    parse_state_ = kParsePesHeader;
+    read_pos_ = packet_start_pos;
+
+    if (!ParsePesHeader(&header)) {
+      return false;
+    }
+    if (!ParsePesOptionalHeader(&header.opt_header)) {
+      return false;
+    }
+
+    const std::size_t fragment_length =
+        header.packet_length - kPesOptionalHeaderSize;
+    std::size_t consumed = 0;
+    if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
+                                                 fragment_length, &payload_,
+                                                 &consumed)) {
+      return false;
+    }
+    read_pos_ += consumed;
+  }
+  return true;
+}
+
+bool VpxPesParser::RemoveStartCodeEmulationPreventionBytes(
+    const std::uint8_t* raw_data, std::size_t bytes_required,
+    PacketData* processed_data, std::size_t* bytes_consumed) const {
+  if (bytes_required == 0 || !processed_data)
+    return false;
+
+  std::size_t num_zeros = 0;
+  std::size_t bytes_copied = 0;
+  const std::uint8_t* const end_of_input =
+      &pes_file_data_[0] + pes_file_data_.size();
+  std::size_t i;
+  for (i = 0; bytes_copied < bytes_required; ++i) {
+    if (raw_data + i > end_of_input)
+      return false;
+
+    bool skip = false;
+
+    const std::uint8_t byte = raw_data[i];
+    if (byte == 0) {
+      ++num_zeros;
+    } else if (byte == 0x3 && num_zeros == 2) {
+      skip = true;
+      num_zeros = 0;
+    } else {
+      num_zeros = 0;
+    }
+
+    if (skip == false) {
+      processed_data->push_back(byte);
+      ++bytes_copied;
+    }
+  }
+  *bytes_consumed = i;
+  return true;
+}
+
+int VpxPesParser::BytesAvailable() const {
+  return static_cast<int>(pes_file_data_.size() - read_pos_);
+}
+
+bool VpxPesParser::ParseNextPacket(PesHeader* header, VideoFrame* frame) {
+  if (!header || !frame || parse_state_ != kFindStartCode ||
+      BytesAvailable() == 0) {
+    return false;
+  }
+
+  std::size_t packet_start_pos = read_pos_;
+  if (!FindStartCode(read_pos_, &packet_start_pos)) {
+    return false;
+  }
+  parse_state_ = kParsePesHeader;
+  read_pos_ = packet_start_pos;
+
+  if (!ParsePesHeader(header)) {
+    return false;
+  }
+  if (!ParsePesOptionalHeader(&header->opt_header)) {
+    return false;
+  }
+  if (!ParseBcmvHeader(&header->bcmv_header)) {
+    return false;
+  }
+
+  // BCMV header length includes the length of the BCMVHeader itself. Adjust:
+  const std::size_t payload_length =
+      header->bcmv_header.length - BcmvHeader::size();
+
+  // Make sure there's enough input data to read the entire frame.
+  if (read_pos_ + payload_length > pes_file_data_.size()) {
+    // Need more data.
+    printf("VpxPesParser: Not enough data. Required: %u Available: %u\n",
+           static_cast<unsigned int>(payload_length),
+           static_cast<unsigned int>(pes_file_data_.size() - read_pos_));
+    parse_state_ = kFindStartCode;
+    read_pos_ = packet_start_pos;
+    return false;
+  }
+
+  if (IsPayloadFragmented(*header)) {
+    if (!AccumulateFragmentedPayload(header->packet_length, payload_length)) {
+      fprintf(stderr, "VpxPesParser: Failed parsing fragmented payload!\n");
+      return false;
+    }
+  } else {
+    std::size_t consumed = 0;
+    if (!RemoveStartCodeEmulationPreventionBytes(
+            &pes_file_data_[read_pos_], payload_length, &payload_, &consumed)) {
+      return false;
+    }
+    read_pos_ += consumed;
+  }
+
+  if (frame->buffer().capacity < payload_.size()) {
+    if (frame->Init(payload_.size()) == false) {
+      fprintf(stderr, "VpxPesParser: Out of memory.\n");
+      return false;
+    }
+  }
+  frame->set_nanosecond_pts(Khz90TicksToNanoseconds(header->opt_header.pts));
+  std::memcpy(frame->buffer().data.get(), &payload_[0], payload_.size());
+  frame->SetBufferLength(payload_.size());
+
+  payload_.clear();
+  parse_state_ = kFindStartCode;
+
+  return true;
+}
+
+}  // namespace libwebm
diff --git a/m2ts/vpxpes_parser.h b/m2ts/vpxpes_parser.h
new file mode 100644
index 0000000..d18b4b7
--- /dev/null
+++ b/m2ts/vpxpes_parser.h
@@ -0,0 +1,177 @@
+// 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_M2TS_VPXPES_PARSER_H_
+#define LIBWEBM_M2TS_VPXPES_PARSER_H_
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "common/libwebm_util.h"
+#include "common/video_frame.h"
+
+namespace libwebm {
+
+// Parser for VPx PES. Requires that the _entire_ PES stream can be stored in
+// a std::vector<std::uint8_t> and read into memory when Open() is called.
+// TODO(tomfinegan): Support incremental parse.
+class VpxPesParser {
+ public:
+  typedef std::vector<std::uint8_t> PesFileData;
+  typedef std::vector<std::uint8_t> PacketData;
+
+  enum ParseState {
+    kFindStartCode,
+    kParsePesHeader,
+    kParsePesOptionalHeader,
+    kParseBcmvHeader,
+  };
+
+  struct PesOptionalHeader {
+    int marker = 0;
+    int scrambling = 0;
+    int priority = 0;
+    int data_alignment = 0;
+    int copyright = 0;
+    int original = 0;
+    int has_pts = 0;
+    int has_dts = 0;
+    int unused_fields = 0;
+    int remaining_size = 0;
+    int pts_dts_flag = 0;
+    std::uint64_t pts = 0;
+    int stuffing_byte = 0;
+  };
+
+  struct BcmvHeader {
+    BcmvHeader() = default;
+    ~BcmvHeader() = default;
+    BcmvHeader(const BcmvHeader&) = delete;
+    BcmvHeader(BcmvHeader&&) = delete;
+
+    // Convenience ctor for quick validation of expected values via operator==
+    // after parsing input.
+    explicit BcmvHeader(std::uint32_t len);
+
+    bool operator==(const BcmvHeader& other) const;
+
+    void Reset();
+    bool Valid() const;
+    static std::size_t size() { return 10; }
+
+    char id[4] = {0};
+    std::uint32_t length = 0;
+  };
+
+  struct PesHeader {
+    std::uint8_t start_code[4] = {0};
+    std::uint16_t packet_length = 0;
+    std::uint8_t stream_id = 0;
+    PesOptionalHeader opt_header;
+    BcmvHeader bcmv_header;
+  };
+
+  // Constants for validating known values from input data.
+  const std::uint8_t kMinVideoStreamId = 0xE0;
+  const std::uint8_t kMaxVideoStreamId = 0xEF;
+  const std::size_t kPesHeaderSize = 6;
+  const std::size_t kPesOptionalHeaderStartOffset = kPesHeaderSize;
+  const std::size_t kPesOptionalHeaderSize = 9;
+  const std::size_t kPesOptionalHeaderMarkerValue = 0x2;
+  const std::size_t kWebm2PesOptHeaderRemainingSize = 6;
+  const std::size_t kBcmvHeaderSize = 10;
+
+  VpxPesParser() = default;
+  ~VpxPesParser() = default;
+
+  // Opens file specified by |pes_file_path| and reads its contents. Returns
+  // true after successful read of input file.
+  bool Open(const std::string& pes_file_path);
+
+  // Parses the next packet in the PES. PES header information is stored in
+  // |header|, and the frame payload is stored in |frame|. Returns true when
+  // a full frame has been consumed from the PES.
+  bool ParseNextPacket(PesHeader* header, VideoFrame* frame);
+
+  // PES Header parsing utility functions.
+  // PES Header structure:
+  // Start code         Stream ID   Packet length (16 bits)
+  // /                  /      ____/
+  // |                  |     /
+  // Byte0 Byte1  Byte2 Byte3 Byte4 Byte5
+  //     0     0      1     X           Y
+  bool VerifyPacketStartCode() const;
+  bool ReadStreamId(std::uint8_t* stream_id) const;
+  bool ReadPacketLength(std::uint16_t* packet_length) const;
+
+  std::uint64_t pes_file_size() const { return pes_file_size_; }
+  const PesFileData& pes_file_data() const { return pes_file_data_; }
+
+  // Returns number of unparsed bytes remaining.
+  int BytesAvailable() const;
+
+ private:
+  // Parses and verifies the static 6 byte portion that begins every PES packet.
+  bool ParsePesHeader(PesHeader* header);
+
+  // Parses a PES optional header, the optional header following the static
+  // header that begins the VPX PES packet.
+  // https://en.wikipedia.org/wiki/Packetized_elementary_stream
+  bool ParsePesOptionalHeader(PesOptionalHeader* header);
+
+  // Parses and validates the BCMV header. This immediately follows the optional
+  // header.
+  bool ParseBcmvHeader(BcmvHeader* header);
+
+  // Returns true when a start code is found and sets |offset| to the position
+  // of the start code relative to |pes_file_data_[read_pos_]|.
+  // Does not set |offset| value if the end of |pes_file_data_| is reached
+  // without locating a start code.
+  // Note: A start code is the byte sequence 0x00 0x00 0x01.
+  bool FindStartCode(std::size_t origin, std::size_t* offset) const;
+
+  // Returns true when a PES packet containing a BCMV header contains only a
+  // portion of the frame payload length reported by the BCMV header.
+  bool IsPayloadFragmented(const PesHeader& header) const;
+
+  // Parses PES and PES Optional header while accumulating payload data in
+  // |payload_|.
+  // Returns true once all payload fragments have been stored in |payload_|.
+  // Returns false if unable to accumulate full payload.
+  bool AccumulateFragmentedPayload(std::size_t pes_packet_length,
+                                   std::size_t payload_length);
+
+  // The byte sequence 0x0 0x0 0x1 is a start code in PES. When PES muxers
+  // encounter 0x0 0x0 0x1 or 0x0 0x0 0x3, an additional 0x3 is inserted into
+  // the PES. The following change occurs:
+  //    0x0 0x0 0x1  =>  0x0 0x0 0x3 0x1
+  //    0x0 0x0 0x3  =>  0x0 0x0 0x3 0x3
+  // PES demuxers must reverse the change:
+  //    0x0 0x0 0x3 0x1  =>  0x0 0x0 0x1
+  //    0x0 0x0 0x3 0x3  =>  0x0 0x0 0x3
+  // PES optional header, BCMV header, and payload data must be preprocessed to
+  // avoid potentially invalid data due to the presence of inserted bytes.
+  //
+  // Removes start code emulation prevention bytes while copying data from
+  // |raw_data| to |processed_data|. Returns true when |bytes_required| bytes
+  // have been written to |processed_data|. Reports bytes consumed during the
+  // operation via |bytes_consumed|.
+  bool RemoveStartCodeEmulationPreventionBytes(
+      const std::uint8_t* raw_data, std::size_t bytes_required,
+      PacketData* processed_data, std::size_t* bytes_consumed) const;
+
+  std::size_t pes_file_size_ = 0;
+  PacketData payload_;
+  PesFileData pes_file_data_;
+  std::size_t read_pos_ = 0;
+  ParseState parse_state_ = kFindStartCode;
+};
+
+}  // namespace libwebm
+
+#endif  // LIBWEBM_M2TS_VPXPES_PARSER_H_
diff --git a/m2ts/webm2pes.cc b/m2ts/webm2pes.cc
new file mode 100644
index 0000000..fc4b314
--- /dev/null
+++ b/m2ts/webm2pes.cc
@@ -0,0 +1,551 @@
+// Copyright (c) 2015 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 "m2ts/webm2pes.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <new>
+#include <vector>
+
+#include "common/libwebm_util.h"
+
+namespace libwebm {
+
+const std::size_t Webm2Pes::kMaxPayloadSize = 32768;
+
+namespace {
+
+std::string ToString(const char* str) {
+  return std::string((str == nullptr) ? "" : str);
+}
+
+}  // namespace
+
+//
+// PesOptionalHeader methods.
+//
+
+void PesOptionalHeader::SetPtsBits(std::int64_t pts_90khz) {
+  std::uint64_t* pts_bits = &pts.bits;
+  *pts_bits = 0;
+
+  // PTS is broken up and stored in 40 bits as shown:
+  //
+  //  PES PTS Only flag
+  // /                  Marker              Marker              Marker
+  // |                 /                   /                   /
+  // |                 |                   |                   |
+  // 7654  321         0  765432107654321  0  765432107654321  0
+  // 0010  PTS 32-30   1  PTS 29-15        1  PTS 14-0         1
+  const std::uint32_t pts1 = (pts_90khz >> 30) & 0x7;
+  const std::uint32_t pts2 = (pts_90khz >> 15) & 0x7FFF;
+  const std::uint32_t pts3 = pts_90khz & 0x7FFF;
+
+  std::uint8_t buffer[5] = {0};
+  // PTS only flag.
+  buffer[0] |= 1 << 5;
+  // Top 3 bits of PTS and 1 bit marker.
+  buffer[0] |= pts1 << 1;
+  // Marker.
+  buffer[0] |= 1;
+
+  // Next 15 bits of pts and 1 bit marker.
+  // Top 8 bits of second PTS chunk.
+  buffer[1] |= (pts2 >> 7) & 0xff;
+  // bottom 7 bits of second PTS chunk.
+  buffer[2] |= (pts2 << 1);
+  // Marker.
+  buffer[2] |= 1;
+
+  // Last 15 bits of pts and 1 bit marker.
+  // Top 8 bits of second PTS chunk.
+  buffer[3] |= (pts3 >> 7) & 0xff;
+  // bottom 7 bits of second PTS chunk.
+  buffer[4] |= (pts3 << 1);
+  // Marker.
+  buffer[4] |= 1;
+
+  // Write bits into PesHeaderField.
+  std::memcpy(reinterpret_cast<std::uint8_t*>(pts_bits), buffer, 5);
+}
+
+// Writes fields to |buffer| and returns true. Returns false when write or
+// field value validation fails.
+bool PesOptionalHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
+  if (buffer == nullptr) {
+    std::fprintf(stderr, "Webm2Pes: nullptr in opt header writer.\n");
+    return false;
+  }
+
+  const int kHeaderSize = 9;
+  std::uint8_t header[kHeaderSize] = {0};
+  std::uint8_t* byte = header;
+
+  if (marker.Check() != true || scrambling.Check() != true ||
+      priority.Check() != true || data_alignment.Check() != true ||
+      copyright.Check() != true || original.Check() != true ||
+      has_pts.Check() != true || has_dts.Check() != true ||
+      pts.Check() != true || stuffing_byte.Check() != true) {
+    std::fprintf(stderr, "Webm2Pes: Invalid PES Optional Header field.\n");
+    return false;
+  }
+
+  // TODO(tomfinegan): As noted in above, the PesHeaderFields should be an
+  // array (or some data structure) that can be iterated over.
+
+  // First byte of header, fields: marker, scrambling, priority, alignment,
+  // copyright, original.
+  *byte = 0;
+  *byte |= marker.bits << marker.shift;
+  *byte |= scrambling.bits << scrambling.shift;
+  *byte |= priority.bits << priority.shift;
+  *byte |= data_alignment.bits << data_alignment.shift;
+  *byte |= copyright.bits << copyright.shift;
+  *byte |= original.bits << original.shift;
+
+  // Second byte of header, fields: has_pts, has_dts, unused fields.
+  *++byte = 0;
+  if (write_pts == true)
+    *byte |= has_pts.bits << has_pts.shift;
+
+  *byte |= has_dts.bits << has_dts.shift;
+
+  // Third byte of header, fields: remaining size of header.
+  *++byte = remaining_size.bits & 0xff;  // Field is 8 bits wide.
+
+  int num_stuffing_bytes =
+      (pts.num_bits + 7) / 8 + 1 /* always 1 stuffing byte */;
+  if (write_pts == true) {
+    // Write the PTS value as big endian and adjust stuffing byte count
+    // accordingly.
+    *++byte = pts.bits & 0xff;
+    *++byte = (pts.bits >> 8) & 0xff;
+    *++byte = (pts.bits >> 16) & 0xff;
+    *++byte = (pts.bits >> 24) & 0xff;
+    *++byte = (pts.bits >> 32) & 0xff;
+    num_stuffing_bytes = 1;
+  }
+
+  // Add the stuffing byte(s).
+  for (int i = 0; i < num_stuffing_bytes; ++i)
+    *++byte = stuffing_byte.bits & 0xff;
+
+  return CopyAndEscapeStartCodes(&header[0], kHeaderSize, buffer);
+}
+
+//
+// BCMVHeader methods.
+//
+
+bool BCMVHeader::Write(PacketDataBuffer* buffer) const {
+  if (buffer == nullptr) {
+    std::fprintf(stderr, "Webm2Pes: nullptr for buffer in BCMV Write.\n");
+    return false;
+  }
+  const int kBcmvSize = 4;
+  for (int i = 0; i < kBcmvSize; ++i)
+    buffer->push_back(bcmv[i]);
+
+  // Note: The 4 byte length field must include the size of the BCMV header.
+  const int kRemainingBytes = 6;
+  const uint32_t bcmv_total_length = length + static_cast<uint32_t>(size());
+  const uint8_t bcmv_buffer[kRemainingBytes] = {
+      static_cast<std::uint8_t>((bcmv_total_length >> 24) & 0xff),
+      static_cast<std::uint8_t>((bcmv_total_length >> 16) & 0xff),
+      static_cast<std::uint8_t>((bcmv_total_length >> 8) & 0xff),
+      static_cast<std::uint8_t>(bcmv_total_length & 0xff),
+      0,
+      0 /* 2 bytes 0 padding */};
+
+  return CopyAndEscapeStartCodes(bcmv_buffer, kRemainingBytes, buffer);
+}
+
+//
+// PesHeader methods.
+//
+
+// Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to write
+// |optional_header| contents. Returns true when successful, false otherwise.
+bool PesHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
+  if (buffer == nullptr) {
+    std::fprintf(stderr, "Webm2Pes: nullptr in header writer.\n");
+    return false;
+  }
+
+  // Write |start_code|.
+  const int kStartCodeLength = 4;
+  for (int i = 0; i < kStartCodeLength; ++i)
+    buffer->push_back(start_code[i]);
+
+  // The length field here reports number of bytes following the field. The
+  // length of the optional header must be added to the payload length set by
+  // the user.
+  const std::size_t header_length =
+      packet_length + optional_header.size_in_bytes();
+  if (header_length > UINT16_MAX)
+    return false;
+
+  // Write |header_length| as big endian.
+  std::uint8_t byte = (header_length >> 8) & 0xff;
+  buffer->push_back(byte);
+  byte = header_length & 0xff;
+  buffer->push_back(byte);
+
+  // Write the (not really) optional header.
+  if (optional_header.Write(write_pts, buffer) != true) {
+    std::fprintf(stderr, "Webm2Pes: PES optional header write failed.");
+    return false;
+  }
+  return true;
+}
+
+//
+// Webm2Pes methods.
+//
+
+bool Webm2Pes::ConvertToFile() {
+  if (input_file_name_.empty() || output_file_name_.empty()) {
+    std::fprintf(stderr, "Webm2Pes: input and/or output file name(s) empty.\n");
+    return false;
+  }
+
+  output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
+  if (output_file_ == nullptr) {
+    std::fprintf(stderr, "Webm2Pes: Cannot open %s for output.\n",
+                 output_file_name_.c_str());
+    return false;
+  }
+
+  if (InitWebmParser() != true) {
+    std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n");
+    return false;
+  }
+
+  // Walk clusters in segment.
+  const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
+  while (cluster != nullptr && cluster->EOS() == false) {
+    const mkvparser::BlockEntry* block_entry = nullptr;
+    std::int64_t block_status = cluster->GetFirst(block_entry);
+    if (block_status < 0) {
+      std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n",
+                   input_file_name_.c_str());
+      return false;
+    }
+
+    // Walk blocks in cluster.
+    while (block_entry != nullptr && block_entry->EOS() == false) {
+      const mkvparser::Block* block = block_entry->GetBlock();
+      if (block->GetTrackNumber() == video_track_num_) {
+        const int frame_count = block->GetFrameCount();
+
+        // Walk frames in block.
+        for (int frame_num = 0; frame_num < frame_count; ++frame_num) {
+          const mkvparser::Block::Frame& mkvparser_frame =
+              block->GetFrame(frame_num);
+
+          // Read the frame.
+          VideoFrame vpx_frame(block->GetTime(cluster), codec_);
+          if (ReadVideoFrame(mkvparser_frame, &vpx_frame) == false) {
+            fprintf(stderr, "Webm2Pes: frame read failed.\n");
+            return false;
+          }
+
+          // Write frame out as PES packet(s).
+          if (WritePesPacket(vpx_frame, &packet_data_) == false) {
+            std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
+            return false;
+          }
+
+          // Write contents of |packet_data_| to |output_file_|.
+          if (std::fwrite(&packet_data_[0], 1, packet_data_.size(),
+                          output_file_.get()) != packet_data_.size()) {
+            std::fprintf(stderr, "Webm2Pes: packet payload write failed.\n");
+            return false;
+          }
+          bytes_written_ += packet_data_.size();
+        }
+      }
+      block_status = cluster->GetNext(block_entry, block_entry);
+      if (block_status < 0) {
+        std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n",
+                     input_file_name_.c_str());
+        return false;
+      }
+    }
+
+    cluster = webm_parser_->GetNext(cluster);
+  }
+
+  std::fflush(output_file_.get());
+  return true;
+}
+
+bool Webm2Pes::ConvertToPacketReceiver() {
+  if (input_file_name_.empty() || packet_sink_ == nullptr) {
+    std::fprintf(stderr, "Webm2Pes: input file name empty or null sink.\n");
+    return false;
+  }
+
+  if (InitWebmParser() != true) {
+    std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n");
+    return false;
+  }
+
+  // Walk clusters in segment.
+  const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
+  while (cluster != nullptr && cluster->EOS() == false) {
+    const mkvparser::BlockEntry* block_entry = nullptr;
+    std::int64_t block_status = cluster->GetFirst(block_entry);
+    if (block_status < 0) {
+      std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n",
+                   input_file_name_.c_str());
+      return false;
+    }
+
+    // Walk blocks in cluster.
+    while (block_entry != nullptr && block_entry->EOS() == false) {
+      const mkvparser::Block* block = block_entry->GetBlock();
+      if (block->GetTrackNumber() == video_track_num_) {
+        const int frame_count = block->GetFrameCount();
+
+        // Walk frames in block.
+        for (int frame_num = 0; frame_num < frame_count; ++frame_num) {
+          const mkvparser::Block::Frame& mkvparser_frame =
+              block->GetFrame(frame_num);
+
+          // Read the frame.
+          VideoFrame frame(block->GetTime(cluster), codec_);
+          if (ReadVideoFrame(mkvparser_frame, &frame) == false) {
+            fprintf(stderr, "Webm2Pes: frame read failed.\n");
+            return false;
+          }
+
+          // Write frame out as PES packet(s).
+          if (WritePesPacket(frame, &packet_data_) == false) {
+            std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
+            return false;
+          }
+          if (packet_sink_->ReceivePacket(packet_data_) != true) {
+            std::fprintf(stderr, "Webm2Pes: ReceivePacket failed.\n");
+            return false;
+          }
+          bytes_written_ += packet_data_.size();
+        }
+      }
+      block_status = cluster->GetNext(block_entry, block_entry);
+      if (block_status < 0) {
+        std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n",
+                     input_file_name_.c_str());
+        return false;
+      }
+    }
+
+    cluster = webm_parser_->GetNext(cluster);
+  }
+
+  return true;
+}
+
+bool Webm2Pes::InitWebmParser() {
+  if (webm_reader_.Open(input_file_name_.c_str()) != 0) {
+    std::fprintf(stderr, "Webm2Pes: Cannot open %s as input.\n",
+                 input_file_name_.c_str());
+    return false;
+  }
+
+  using mkvparser::Segment;
+  Segment* webm_parser = nullptr;
+  if (Segment::CreateInstance(&webm_reader_, 0 /* pos */,
+                              webm_parser /* Segment*& */) != 0) {
+    std::fprintf(stderr, "Webm2Pes: Cannot create WebM parser.\n");
+    return false;
+  }
+  webm_parser_.reset(webm_parser);
+
+  if (webm_parser_->Load() != 0) {
+    std::fprintf(stderr, "Webm2Pes: Cannot parse %s.\n",
+                 input_file_name_.c_str());
+    return false;
+  }
+
+  // Make sure there's a video track.
+  const mkvparser::Tracks* tracks = webm_parser_->GetTracks();
+  if (tracks == nullptr) {
+    std::fprintf(stderr, "Webm2Pes: %s has no tracks.\n",
+                 input_file_name_.c_str());
+    return false;
+  }
+
+  timecode_scale_ = webm_parser_->GetInfo()->GetTimeCodeScale();
+
+  for (int track_index = 0;
+       track_index < static_cast<int>(tracks->GetTracksCount());
+       ++track_index) {
+    const mkvparser::Track* track = tracks->GetTrackByIndex(track_index);
+    if (track && track->GetType() == mkvparser::Track::kVideo) {
+      const std::string codec_id = ToString(track->GetCodecId());
+      if (codec_id == std::string("V_VP8")) {
+        codec_ = VideoFrame::kVP8;
+      } else if (codec_id == std::string("V_VP9")) {
+        codec_ = VideoFrame::kVP9;
+      } else {
+        fprintf(stderr, "Webm2Pes: Codec must be VP8 or VP9.\n");
+        return false;
+      }
+      video_track_num_ = track_index + 1;
+      break;
+    }
+  }
+  if (video_track_num_ < 1) {
+    std::fprintf(stderr, "Webm2Pes: No video track found in %s.\n",
+                 input_file_name_.c_str());
+    return false;
+  }
+  return true;
+}
+
+bool Webm2Pes::ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame,
+                              VideoFrame* frame) {
+  if (mkvparser_frame.len < 1 || frame == nullptr)
+    return false;
+
+  const std::size_t mkv_len = static_cast<std::size_t>(mkvparser_frame.len);
+  if (mkv_len > frame->buffer().capacity) {
+    const std::size_t new_size = 2 * mkv_len;
+    if (frame->Init(new_size) == false) {
+      std::fprintf(stderr, "Webm2Pes: Out of memory.\n");
+      return false;
+    }
+  }
+  if (mkvparser_frame.Read(&webm_reader_, frame->buffer().data.get()) != 0) {
+    std::fprintf(stderr, "Webm2Pes: Error reading VPx frame!\n");
+    return false;
+  }
+  return frame->SetBufferLength(mkv_len);
+}
+
+bool Webm2Pes::WritePesPacket(const VideoFrame& frame,
+                              PacketDataBuffer* packet_data) {
+  if (frame.buffer().data.get() == nullptr || frame.buffer().length < 1)
+    return false;
+
+  Ranges frame_ranges;
+  if (frame.codec() == VideoFrame::kVP9) {
+    bool error = false;
+    const bool has_superframe_index =
+        ParseVP9SuperFrameIndex(frame.buffer().data.get(),
+                                frame.buffer().length, &frame_ranges, &error);
+    if (error) {
+      std::fprintf(stderr, "Webm2Pes: Superframe index parse failed.\n");
+      return false;
+    }
+    if (has_superframe_index == false) {
+      frame_ranges.push_back(Range(0, frame.buffer().length));
+    }
+  } else {
+    frame_ranges.push_back(Range(0, frame.buffer().length));
+  }
+
+  const std::int64_t khz90_pts =
+      NanosecondsTo90KhzTicks(frame.nanosecond_pts());
+  PesHeader header;
+  header.optional_header.SetPtsBits(khz90_pts);
+
+  packet_data->clear();
+
+  for (const Range& packet_payload_range : frame_ranges) {
+    std::size_t extra_bytes = 0;
+    if (packet_payload_range.length > kMaxPayloadSize) {
+      extra_bytes = packet_payload_range.length - kMaxPayloadSize;
+    }
+    if (packet_payload_range.length + packet_payload_range.offset >
+        frame.buffer().length) {
+      std::fprintf(stderr, "Webm2Pes: Invalid frame length.\n");
+      return false;
+    }
+
+    // First packet of new frame. Always include PTS and BCMV header.
+    header.packet_length =
+        packet_payload_range.length - extra_bytes + BCMVHeader::size();
+    if (header.Write(true, packet_data) != true) {
+      std::fprintf(stderr, "Webm2Pes: packet header write failed.\n");
+      return false;
+    }
+
+    BCMVHeader bcmv_header(static_cast<uint32_t>(packet_payload_range.length));
+    if (bcmv_header.Write(packet_data) != true) {
+      std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n");
+      return false;
+    }
+
+    // Insert the payload at the end of |packet_data|.
+    const std::uint8_t* const payload_start =
+        frame.buffer().data.get() + packet_payload_range.offset;
+
+    const std::size_t bytes_to_copy = packet_payload_range.length - extra_bytes;
+    if (CopyAndEscapeStartCodes(payload_start, bytes_to_copy, packet_data) ==
+        false) {
+      fprintf(stderr, "Webm2Pes: Payload write failed.\n");
+      return false;
+    }
+
+    std::size_t bytes_copied = bytes_to_copy;
+    while (extra_bytes) {
+      // Write PES packets for the remaining data, but omit the PTS and BCMV
+      // header.
+      const std::size_t extra_bytes_to_copy =
+          std::min(kMaxPayloadSize, extra_bytes);
+      extra_bytes -= extra_bytes_to_copy;
+      header.packet_length = extra_bytes_to_copy;
+      if (header.Write(false, packet_data) != true) {
+        fprintf(stderr, "Webm2pes: fragment write failed.\n");
+        return false;
+      }
+
+      const std::uint8_t* fragment_start = payload_start + bytes_copied;
+      if (CopyAndEscapeStartCodes(fragment_start, extra_bytes_to_copy,
+                                  packet_data) == false) {
+        fprintf(stderr, "Webm2Pes: Payload write failed.\n");
+        return false;
+      }
+
+      bytes_copied += extra_bytes_to_copy;
+    }
+  }
+
+  return true;
+}
+
+bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
+                             std::size_t raw_input_length,
+                             PacketDataBuffer* packet_buffer) {
+  if (raw_input == nullptr || raw_input_length < 1 || packet_buffer == nullptr)
+    return false;
+
+  int num_zeros = 0;
+  for (std::size_t i = 0; i < raw_input_length; ++i) {
+    const uint8_t byte = raw_input[i];
+
+    if (byte == 0) {
+      ++num_zeros;
+    } else if (num_zeros >= 2 && (byte == 0x1 || byte == 0x3)) {
+      packet_buffer->push_back(0x3);
+      num_zeros = 0;
+    } else {
+      num_zeros = 0;
+    }
+
+    packet_buffer->push_back(byte);
+  }
+
+  return true;
+}
+
+}  // namespace libwebm
diff --git a/m2ts/webm2pes.h b/m2ts/webm2pes.h
new file mode 100644
index 0000000..6dcb0fd
--- /dev/null
+++ b/m2ts/webm2pes.h
@@ -0,0 +1,274 @@
+// Copyright (c) 2015 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_M2TS_WEBM2PES_H_
+#define LIBWEBM_M2TS_WEBM2PES_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "common/libwebm_util.h"
+#include "common/video_frame.h"
+#include "mkvparser/mkvparser.h"
+#include "mkvparser/mkvreader.h"
+
+// Webm2pes
+//
+// Webm2pes consumes a WebM file containing a VP8 or VP9 video stream and
+// outputs a PES stream suitable for inclusion in a MPEG2 Transport Stream.
+//
+// In the simplest case the PES stream output by Webm2pes consists of a sequence
+// of PES packets with the following structure:
+// | PES Header w/PTS | BCMV Header | Payload (VPx frame) |
+//
+// More typically the output will look like the following due to the PES
+// payload size limitations caused by the format of the PES header.
+// The PES header contains only 2 bytes of storage for expressing payload size.
+// VPx PES streams containing fragmented packets look like this:
+//
+// | PH PTS | BCMV | Payload fragment 1 | PH | Payload fragment 2 | ...
+//
+//   PH = PES Header
+//   PH PTS = PES Header with PTS
+//   BCMV = BCMV Header
+//
+// Note that start codes are properly escaped by Webm2pes, and start code
+// emulation prevention bytes must be stripped from the output stream before
+// it can be parsed.
+
+namespace libwebm {
+
+// Stores a value and its size in bits for writing into a PES Optional Header.
+// Maximum size is 64 bits. Users may call the Check() method to perform minimal
+// validation (size > 0 and <= 64).
+struct PesHeaderField {
+  PesHeaderField(std::uint64_t value, std::uint32_t size_in_bits,
+                 std::uint8_t byte_index, std::uint8_t bits_to_shift)
+      : bits(value),
+        num_bits(size_in_bits),
+        index(byte_index),
+        shift(bits_to_shift) {}
+  PesHeaderField() = delete;
+  PesHeaderField(const PesHeaderField&) = default;
+  PesHeaderField(PesHeaderField&&) = default;
+  ~PesHeaderField() = default;
+  bool Check() const {
+    return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64;
+  }
+
+  // Value to be stored in the field.
+  std::uint64_t bits;
+
+  // Number of bits in the value.
+  const int num_bits;
+
+  // Index into the header for the byte in which |bits| will be written.
+  const std::uint8_t index;
+
+  // Number of bits to shift value before or'ing.
+  const int shift;
+};
+
+// Data is stored in buffers before being written to output files.
+typedef std::vector<std::uint8_t> PacketDataBuffer;
+
+// Storage for PES Optional Header values. Fields written in order using sizes
+// specified.
+struct PesOptionalHeader {
+  // TODO(tomfinegan): The fields could be in an array, which would allow the
+  // code writing the optional header to iterate over the fields instead of
+  // having code for dealing with each one.
+
+  // 2 bits (marker): 2 ('10')
+  const PesHeaderField marker = PesHeaderField(2, 2, 0, 6);
+
+  // 2 bits (no scrambling): 0x0 ('00')
+  const PesHeaderField scrambling = PesHeaderField(0, 2, 0, 4);
+
+  // 1 bit (priority): 0x0 ('0')
+  const PesHeaderField priority = PesHeaderField(0, 1, 0, 3);
+
+  // TODO(tomfinegan): The BCMV header could be considered a sync word, and this
+  // field should be 1 when a sync word follows the packet. Clarify.
+  // 1 bit (data alignment): 0x0 ('0')
+  const PesHeaderField data_alignment = PesHeaderField(0, 1, 0, 2);
+
+  // 1 bit (copyright): 0x0 ('0')
+  const PesHeaderField copyright = PesHeaderField(0, 1, 0, 1);
+
+  // 1 bit (original/copy): 0x0 ('0')
+  const PesHeaderField original = PesHeaderField(0, 1, 0, 0);
+
+  // 1 bit (has_pts): 0x1 ('1')
+  const PesHeaderField has_pts = PesHeaderField(1, 1, 1, 7);
+
+  // 1 bit (has_dts): 0x0 ('0')
+  const PesHeaderField has_dts = PesHeaderField(0, 1, 1, 6);
+
+  // 6 bits (unused fields): 0x0 ('000000')
+  const PesHeaderField unused = PesHeaderField(0, 6, 1, 0);
+
+  // 8 bits (size of remaining data in the Header).
+  const PesHeaderField remaining_size = PesHeaderField(6, 8, 2, 0);
+
+  // PTS: 5 bytes
+  //   4 bits (flag: PTS present, but no DTS): 0x2 ('0010')
+  //   36 bits (90khz PTS):
+  //     top 3 bits
+  //     marker ('1')
+  //     middle 15 bits
+  //     marker ('1')
+  //     bottom 15 bits
+  //     marker ('1')
+  PesHeaderField pts = PesHeaderField(0, 40, 3, 0);
+
+  PesHeaderField stuffing_byte = PesHeaderField(0xFF, 8, 8, 0);
+
+  // PTS omitted in fragments. Size remains unchanged: More stuffing bytes.
+  bool fragment = false;
+
+  static std::size_t size_in_bytes() { return 9; }
+
+  // Writes |pts_90khz| to |pts| per format described at its declaration above.
+  void SetPtsBits(std::int64_t pts_90khz);
+
+  // Writes fields to |buffer| and returns true. Returns false when write or
+  // field value validation fails.
+  bool Write(bool write_pts, PacketDataBuffer* buffer) const;
+};
+
+// Describes custom 10 byte header that immediately follows the PES Optional
+// Header in each PES packet output by Webm2Pes:
+//   4 byte 'B' 'C' 'M' 'V'
+//   4 byte big-endian length of frame
+//   2 bytes 0 padding
+struct BCMVHeader {
+  explicit BCMVHeader(std::uint32_t frame_length) : length(frame_length) {}
+  BCMVHeader() = delete;
+  BCMVHeader(const BCMVHeader&) = delete;
+  BCMVHeader(BCMVHeader&&) = delete;
+  ~BCMVHeader() = default;
+  const std::uint8_t bcmv[4] = {'B', 'C', 'M', 'V'};
+  const std::uint32_t length;
+
+  static std::size_t size() { return 10; }
+
+  // Write the BCMV Header into |buffer|. Caller responsible for ensuring
+  // destination buffer is of size >= BCMVHeader::size().
+  bool Write(PacketDataBuffer* buffer) const;
+  bool Write(uint8_t* buffer);
+};
+
+struct PesHeader {
+  const std::uint8_t start_code[4] = {
+      0x00, 0x00,
+      0x01,  // 0x000001 is the PES packet start code prefix.
+      0xE0};  // 0xE0 is the minimum video stream ID.
+  std::uint16_t packet_length = 0;  // Number of bytes _after_ this field.
+  PesOptionalHeader optional_header;
+  std::size_t size() const {
+    return optional_header.size_in_bytes() +
+           6 /* start_code + packet_length */ + packet_length;
+  }
+
+  // Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to
+  // write |optional_header| contents. Returns true when successful, false
+  // otherwise.
+  bool Write(bool write_pts, PacketDataBuffer* buffer) const;
+};
+
+class PacketReceiverInterface {
+ public:
+  virtual ~PacketReceiverInterface() {}
+  virtual bool ReceivePacket(const PacketDataBuffer& packet) = 0;
+};
+
+// Converts the VP9 track of a WebM file to a Packetized Elementary Stream
+// suitable for use in a MPEG2TS.
+// https://en.wikipedia.org/wiki/Packetized_elementary_stream
+// https://en.wikipedia.org/wiki/MPEG_transport_stream
+class Webm2Pes {
+ public:
+  static const std::size_t kMaxPayloadSize;
+
+  Webm2Pes(const std::string& input_file, const std::string& output_file)
+      : input_file_name_(input_file), output_file_name_(output_file) {}
+  Webm2Pes(const std::string& input_file, PacketReceiverInterface* packet_sink)
+      : input_file_name_(input_file), packet_sink_(packet_sink) {}
+
+  Webm2Pes() = delete;
+  Webm2Pes(const Webm2Pes&) = delete;
+  Webm2Pes(Webm2Pes&&) = delete;
+  ~Webm2Pes() = default;
+
+  // Converts the VPx video stream to a PES file and returns true. Returns false
+  // to report failure.
+  bool ConvertToFile();
+
+  // Converts the VPx video stream to a sequence of PES packets, and calls the
+  // PacketReceiverInterface::ReceivePacket() once for each VPx frame. The
+  // packet sent to the receiver may contain multiple PES packets. Returns only
+  // after full conversion or error. Returns true for success, and false when
+  // an error occurs.
+  bool ConvertToPacketReceiver();
+
+  // Writes |vpx_frame| out as PES packet[s] and stores output in |packet_data|.
+  // Returns true for success, false for failure.
+  static bool WritePesPacket(const VideoFrame& frame,
+                             PacketDataBuffer* packet_data);
+
+  uint64_t bytes_written() const { return bytes_written_; }
+
+ private:
+  bool InitWebmParser();
+  bool ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame,
+                      VideoFrame* frame);
+
+  const std::string input_file_name_;
+  const std::string output_file_name_;
+  std::unique_ptr<mkvparser::Segment> webm_parser_;
+  mkvparser::MkvReader webm_reader_;
+  FilePtr output_file_;
+
+  // Video track num in the WebM file.
+  int video_track_num_ = 0;
+
+  // Video codec reported by CodecName from Video TrackEntry.
+  VideoFrame::Codec codec_;
+
+  // Input timecode scale.
+  std::int64_t timecode_scale_ = 1000000;
+
+  // Packet sink; when constructed with a PacketReceiverInterface*, packet and
+  // type of packet are sent to |packet_sink_| instead of written to an output
+  // file.
+  PacketReceiverInterface* packet_sink_ = nullptr;
+
+  PacketDataBuffer packet_data_;
+
+  std::uint64_t bytes_written_ = 0;
+};
+
+// Copies |raw_input_length| bytes from |raw_input| to |packet_buffer| while
+// escaping start codes. Returns true when bytes are successfully copied.
+// A start code is the 3 byte sequence 0x00 0x00 0x01. When
+// the sequence is encountered, the value 0x03 is inserted. To avoid
+// any ambiguity at reassembly time, the same is done for the sequence
+// 0x00 0x00 0x03. So, the following transformation occurs for when either
+// of the noted sequences is encountered:
+//
+//    0x00 0x00 0x01  =>  0x00 0x00 0x03 0x01
+//    0x00 0x00 0x03  =>  0x00 0x00 0x03 0x03
+bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
+                             std::size_t raw_input_length,
+                             PacketDataBuffer* packet_buffer);
+}  // namespace libwebm
+
+#endif  // LIBWEBM_M2TS_WEBM2PES_H_
diff --git a/m2ts/webm2pes_main.cc b/m2ts/webm2pes_main.cc
new file mode 100644
index 0000000..075e55c
--- /dev/null
+++ b/m2ts/webm2pes_main.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2015 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 "m2ts/webm2pes.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+
+namespace {
+
+void Usage(const char* argv[]) {
+  printf("Usage: %s <WebM file> <output file>", argv[0]);
+}
+
+}  // namespace
+
+int main(int argc, const char* argv[]) {
+  if (argc < 3) {
+    Usage(argv);
+    return EXIT_FAILURE;
+  }
+
+  const std::string input_path = argv[1];
+  const std::string output_path = argv[2];
+
+  libwebm::Webm2Pes converter(input_path, output_path);
+  return converter.ConvertToFile() == true ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/mkvmuxer.hpp b/mkvmuxer.hpp
new file mode 100644
index 0000000..092592b
--- /dev/null
+++ b/mkvmuxer.hpp
@@ -0,0 +1,15 @@
+// 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_MKVMUXER_HPP_
+#define LIBWEBM_MKVMUXER_HPP_
+
+// This file is a wrapper for the file included immediately after this comment.
+// New projects should not include this file: include the file included below.
+#include "mkvmuxer/mkvmuxer.h"
+
+#endif  // LIBWEBM_MKVMUXER_HPP_
diff --git a/mkvmuxer/mkvmuxer.cc b/mkvmuxer/mkvmuxer.cc
new file mode 100644
index 0000000..5120312
--- /dev/null
+++ b/mkvmuxer/mkvmuxer.cc
@@ -0,0 +1,4221 @@
+// 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 <stdint.h>
+
+#include <cfloat>
+#include <climits>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <memory>
+#include <new>
+#include <string>
+#include <vector>
+
+#include "common/webmids.h"
+#include "mkvmuxer/mkvmuxerutil.h"
+#include "mkvmuxer/mkvwriter.h"
+#include "mkvparser/mkvparser.h"
+
+namespace mkvmuxer {
+
+const float PrimaryChromaticity::kChromaticityMin = 0.0f;
+const float PrimaryChromaticity::kChromaticityMax = 1.0f;
+const float MasteringMetadata::kMinLuminance = 0.0f;
+const float MasteringMetadata::kMinLuminanceMax = 999.99f;
+const float MasteringMetadata::kMaxLuminanceMax = 9999.99f;
+const float MasteringMetadata::kValueNotPresent = FLT_MAX;
+const uint64_t Colour::kValueNotPresent = UINT64_MAX;
+
+namespace {
+
+const char kDocTypeWebm[] = "webm";
+const char kDocTypeMatroska[] = "matroska";
+
+// 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::unique_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,
+                     const char* const doc_type) {
+  // Level 0
+  uint64_t size =
+      EbmlElementSize(libwebm::kMkvEBMLVersion, static_cast<uint64>(1));
+  size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, static_cast<uint64>(1));
+  size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, static_cast<uint64>(4));
+  size +=
+      EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, static_cast<uint64>(8));
+  size += EbmlElementSize(libwebm::kMkvDocType, doc_type);
+  size += EbmlElementSize(libwebm::kMkvDocTypeVersion,
+                          static_cast<uint64>(doc_type_version));
+  size +=
+      EbmlElementSize(libwebm::kMkvDocTypeReadVersion, static_cast<uint64>(2));
+
+  if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion,
+                        static_cast<uint64>(1))) {
+    return false;
+  }
+  if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion,
+                        static_cast<uint64>(1))) {
+    return false;
+  }
+  if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength,
+                        static_cast<uint64>(4))) {
+    return false;
+  }
+  if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength,
+                        static_cast<uint64>(8))) {
+    return false;
+  }
+  if (!WriteEbmlElement(writer, libwebm::kMkvDocType, doc_type))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion,
+                        static_cast<uint64>(doc_type_version))) {
+    return false;
+  }
+  if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion,
+                        static_cast<uint64>(2))) {
+    return false;
+  }
+
+  return true;
+}
+
+bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) {
+  return WriteEbmlHeader(writer, doc_type_version, kDocTypeWebm);
+}
+
+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,
+                                  static_cast<uint64>(cluster_pos_));
+  size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast<uint64>(track_));
+  if (output_block_number_ && block_number_ > 1)
+    size += EbmlElementSize(libwebm::kMkvCueBlockNumber,
+                            static_cast<uint64>(block_number_));
+  const uint64_t track_pos_size =
+      EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
+  const uint64_t payload_size =
+      EbmlElementSize(libwebm::kMkvCueTime, static_cast<uint64>(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,
+                        static_cast<uint64>(time_))) {
+    return false;
+  }
+
+  if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack,
+                        static_cast<uint64>(track_))) {
+    return false;
+  }
+  if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition,
+                        static_cast<uint64>(cluster_pos_))) {
+    return false;
+  }
+  if (output_block_number_ && block_number_ > 1) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber,
+                          static_cast<uint64>(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,
+                                  static_cast<uint64>(cluster_pos_));
+  size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast<uint64>(track_));
+  if (output_block_number_ && block_number_ > 1)
+    size += EbmlElementSize(libwebm::kMkvCueBlockNumber,
+                            static_cast<uint64>(block_number_));
+  const uint64_t track_pos_size =
+      EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size;
+  const uint64_t payload_size =
+      EbmlElementSize(libwebm::kMkvCueTime, static_cast<uint64>(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,
+                        static_cast<uint64>(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,
+                                  static_cast<uint64>(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,
+                        static_cast<uint64>(encoding_order_)))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope,
+                        static_cast<uint64>(encoding_scope_)))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType,
+                        static_cast<uint64>(encoding_type_)))
+    return false;
+
+  if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption,
+                              encryption_size))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo,
+                        static_cast<uint64>(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,
+                                   static_cast<uint64>(encoding_type_));
+  encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingScope,
+                                   static_cast<uint64>(encoding_scope_));
+  encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingOrder,
+                                   static_cast<uint64>(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,
+                                     static_cast<uint64>(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, static_cast<uint64>(number_));
+  size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast<uint64>(uid_));
+  size += EbmlElementSize(libwebm::kMkvTrackType, static_cast<uint64>(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,
+                            static_cast<uint64>(max_block_additional_id_));
+  }
+  if (codec_delay_) {
+    size += EbmlElementSize(libwebm::kMkvCodecDelay,
+                            static_cast<uint64>(codec_delay_));
+  }
+  if (seek_pre_roll_) {
+    size += EbmlElementSize(libwebm::kMkvSeekPreRoll,
+                            static_cast<uint64>(seek_pre_roll_));
+  }
+  if (default_duration_) {
+    size += EbmlElementSize(libwebm::kMkvDefaultDuration,
+                            static_cast<uint64>(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;
+
+  // AV1 tracks require a CodecPrivate. See
+  // https://github.com/Matroska-Org/matroska-specification/blob/av1-mappin/codec/av1.md
+  // TODO(tomfinegan): Update the above link to the AV1 Matroska mappings to
+  // point to a stable version once it is finalized, or our own WebM mappings
+  // page on webmproject.org should we decide to release them.
+  if (!strcmp(codec_id_, Tracks::kAv1CodecId) && !codec_private_)
+    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, static_cast<uint64>(number_));
+  size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast<uint64>(uid_));
+  size += EbmlElementSize(libwebm::kMkvTrackType, static_cast<uint64>(type_));
+  if (codec_id_)
+    size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_);
+  if (codec_private_)
+    size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_,
+                            static_cast<uint64>(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,
+                            static_cast<uint64>(max_block_additional_id_));
+  if (codec_delay_)
+    size += EbmlElementSize(libwebm::kMkvCodecDelay,
+                            static_cast<uint64>(codec_delay_));
+  if (seek_pre_roll_)
+    size += EbmlElementSize(libwebm::kMkvSeekPreRoll,
+                            static_cast<uint64>(seek_pre_roll_));
+  if (default_duration_)
+    size += EbmlElementSize(libwebm::kMkvDefaultDuration,
+                            static_cast<uint64>(default_duration_));
+
+  const int64_t payload_position = writer->Position();
+  if (payload_position < 0)
+    return false;
+
+  if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber,
+                        static_cast<uint64>(number_)))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID,
+                        static_cast<uint64>(uid_)))
+    return false;
+  if (!WriteEbmlElement(writer, libwebm::kMkvTrackType,
+                        static_cast<uint64>(type_)))
+    return false;
+  if (max_block_additional_id_) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID,
+                          static_cast<uint64>(max_block_additional_id_))) {
+      return false;
+    }
+  }
+  if (codec_delay_) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay,
+                          static_cast<uint64>(codec_delay_)))
+      return false;
+  }
+  if (seek_pre_roll_) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll,
+                          static_cast<uint64>(seek_pre_roll_)))
+      return false;
+  }
+  if (default_duration_) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration,
+                          static_cast<uint64>(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_,
+                          static_cast<uint64>(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::PrimaryChromaticitySize(
+    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 {
+  if (!Valid()) {
+    return false;
+  }
+  return WriteEbmlElement(writer, x_id, x_) &&
+         WriteEbmlElement(writer, y_id, y_);
+}
+
+bool PrimaryChromaticity::Valid() const {
+  return (x_ >= kChromaticityMin && x_ <= kChromaticityMax &&
+          y_ >= kChromaticityMin && y_ <= kChromaticityMax);
+}
+
+uint64_t MasteringMetadata::MasteringMetadataSize() const {
+  uint64_t size = PayloadSize();
+
+  if (size > 0)
+    size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size);
+
+  return size;
+}
+
+bool MasteringMetadata::Valid() const {
+  if (luminance_min_ != kValueNotPresent) {
+    if (luminance_min_ < kMinLuminance || luminance_min_ > kMinLuminanceMax ||
+        luminance_min_ > luminance_max_) {
+      return false;
+    }
+  }
+  if (luminance_max_ != kValueNotPresent) {
+    if (luminance_max_ < kMinLuminance || luminance_max_ > kMaxLuminanceMax ||
+        luminance_max_ < luminance_min_) {
+      return false;
+    }
+  }
+  if (r_ && !r_->Valid())
+    return false;
+  if (g_ && !g_->Valid())
+    return false;
+  if (b_ && !b_->Valid())
+    return false;
+  if (white_point_ && !white_point_->Valid())
+    return false;
+
+  return true;
+}
+
+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(nullptr);
+  if (r) {
+    if (!CopyChromaticity(r, &r_ptr))
+      return false;
+  }
+  PrimaryChromaticityPtr g_ptr(nullptr);
+  if (g) {
+    if (!CopyChromaticity(g, &g_ptr))
+      return false;
+  }
+  PrimaryChromaticityPtr b_ptr(nullptr);
+  if (b) {
+    if (!CopyChromaticity(b, &b_ptr))
+      return false;
+  }
+  PrimaryChromaticityPtr wp_ptr(nullptr);
+  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_->PrimaryChromaticitySize(libwebm::kMkvPrimaryRChromaticityX,
+                                        libwebm::kMkvPrimaryRChromaticityY);
+  }
+  if (g_) {
+    size += g_->PrimaryChromaticitySize(libwebm::kMkvPrimaryGChromaticityX,
+                                        libwebm::kMkvPrimaryGChromaticityY);
+  }
+  if (b_) {
+    size += b_->PrimaryChromaticitySize(libwebm::kMkvPrimaryBChromaticityX,
+                                        libwebm::kMkvPrimaryBChromaticityY);
+  }
+  if (white_point_) {
+    size += white_point_->PrimaryChromaticitySize(
+        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::Valid() const {
+  if (mastering_metadata_ && !mastering_metadata_->Valid())
+    return false;
+  if (matrix_coefficients_ != kValueNotPresent &&
+      !IsMatrixCoefficientsValueValid(matrix_coefficients_)) {
+    return false;
+  }
+  if (chroma_siting_horz_ != kValueNotPresent &&
+      !IsChromaSitingHorzValueValid(chroma_siting_horz_)) {
+    return false;
+  }
+  if (chroma_siting_vert_ != kValueNotPresent &&
+      !IsChromaSitingVertValueValid(chroma_siting_vert_)) {
+    return false;
+  }
+  if (range_ != kValueNotPresent && !IsColourRangeValueValid(range_))
+    return false;
+  if (transfer_characteristics_ != kValueNotPresent &&
+      !IsTransferCharacteristicsValueValid(transfer_characteristics_)) {
+    return false;
+  }
+  if (primaries_ != kValueNotPresent && !IsPrimariesValueValid(primaries_))
+    return false;
+
+  return true;
+}
+
+bool Colour::Write(IMkvWriter* writer) const {
+  const uint64_t size = PayloadSize();
+
+  // Don't write an empty element.
+  if (size == 0)
+    return true;
+
+  // Don't write an invalid element.
+  if (!Valid())
+    return false;
+
+  if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size))
+    return false;
+
+  if (matrix_coefficients_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients,
+                        static_cast<uint64>(matrix_coefficients_))) {
+    return false;
+  }
+  if (bits_per_channel_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel,
+                        static_cast<uint64>(bits_per_channel_))) {
+    return false;
+  }
+  if (chroma_subsampling_horz_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz,
+                        static_cast<uint64>(chroma_subsampling_horz_))) {
+    return false;
+  }
+  if (chroma_subsampling_vert_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert,
+                        static_cast<uint64>(chroma_subsampling_vert_))) {
+    return false;
+  }
+
+  if (cb_subsampling_horz_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz,
+                        static_cast<uint64>(cb_subsampling_horz_))) {
+    return false;
+  }
+  if (cb_subsampling_vert_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert,
+                        static_cast<uint64>(cb_subsampling_vert_))) {
+    return false;
+  }
+  if (chroma_siting_horz_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz,
+                        static_cast<uint64>(chroma_siting_horz_))) {
+    return false;
+  }
+  if (chroma_siting_vert_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert,
+                        static_cast<uint64>(chroma_siting_vert_))) {
+    return false;
+  }
+  if (range_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvRange,
+                        static_cast<uint64>(range_))) {
+    return false;
+  }
+  if (transfer_characteristics_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics,
+                        static_cast<uint64>(transfer_characteristics_))) {
+    return false;
+  }
+  if (primaries_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvPrimaries,
+                        static_cast<uint64>(primaries_))) {
+    return false;
+  }
+  if (max_cll_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvMaxCLL,
+                        static_cast<uint64>(max_cll_))) {
+    return false;
+  }
+  if (max_fall_ != kValueNotPresent &&
+      !WriteEbmlElement(writer, libwebm::kMkvMaxFALL,
+                        static_cast<uint64>(max_fall_))) {
+    return false;
+  }
+
+  if (mastering_metadata_ && !mastering_metadata_->Write(writer))
+    return false;
+
+  return true;
+}
+
+bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) {
+  std::unique_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
+  if (!mm_ptr.get())
+    return false;
+
+  mm_ptr->set_luminance_max(mastering_metadata.luminance_max());
+  mm_ptr->set_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,
+                            static_cast<uint64>(matrix_coefficients_));
+  }
+  if (bits_per_channel_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvBitsPerChannel,
+                            static_cast<uint64>(bits_per_channel_));
+  }
+  if (chroma_subsampling_horz_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz,
+                            static_cast<uint64>(chroma_subsampling_horz_));
+  }
+  if (chroma_subsampling_vert_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert,
+                            static_cast<uint64>(chroma_subsampling_vert_));
+  }
+  if (cb_subsampling_horz_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvCbSubsamplingHorz,
+                            static_cast<uint64>(cb_subsampling_horz_));
+  }
+  if (cb_subsampling_vert_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvCbSubsamplingVert,
+                            static_cast<uint64>(cb_subsampling_vert_));
+  }
+  if (chroma_siting_horz_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvChromaSitingHorz,
+                            static_cast<uint64>(chroma_siting_horz_));
+  }
+  if (chroma_siting_vert_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvChromaSitingVert,
+                            static_cast<uint64>(chroma_siting_vert_));
+  }
+  if (range_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvRange, static_cast<uint64>(range_));
+  }
+  if (transfer_characteristics_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvTransferCharacteristics,
+                            static_cast<uint64>(transfer_characteristics_));
+  }
+  if (primaries_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvPrimaries,
+                            static_cast<uint64>(primaries_));
+  }
+  if (max_cll_ != kValueNotPresent) {
+    size += EbmlElementSize(libwebm::kMkvMaxCLL, static_cast<uint64>(max_cll_));
+  }
+  if (max_fall_ != kValueNotPresent) {
+    size +=
+        EbmlElementSize(libwebm::kMkvMaxFALL, static_cast<uint64>(max_fall_));
+  }
+
+  if (mastering_metadata_)
+    size += mastering_metadata_->MasteringMetadataSize();
+
+  return size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Projection element
+
+uint64_t Projection::ProjectionSize() const {
+  uint64_t size = PayloadSize();
+
+  if (size > 0)
+    size += EbmlMasterElementSize(libwebm::kMkvProjection, size);
+
+  return size;
+}
+
+bool Projection::Write(IMkvWriter* writer) const {
+  const uint64_t size = PayloadSize();
+
+  // Don't write an empty element.
+  if (size == 0)
+    return true;
+
+  if (!WriteEbmlMasterElement(writer, libwebm::kMkvProjection, size))
+    return false;
+
+  if (!WriteEbmlElement(writer, libwebm::kMkvProjectionType,
+                        static_cast<uint64>(type_))) {
+    return false;
+  }
+
+  if (private_data_length_ > 0 && private_data_ != NULL &&
+      !WriteEbmlElement(writer, libwebm::kMkvProjectionPrivate, private_data_,
+                        private_data_length_)) {
+    return false;
+  }
+
+  if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseYaw, pose_yaw_))
+    return false;
+
+  if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPosePitch,
+                        pose_pitch_)) {
+    return false;
+  }
+
+  if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseRoll, pose_roll_)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool Projection::SetProjectionPrivate(const uint8_t* data,
+                                      uint64_t data_length) {
+  if (data == NULL || data_length == 0) {
+    return false;
+  }
+
+  if (data_length != static_cast<size_t>(data_length)) {
+    return false;
+  }
+
+  uint8_t* new_private_data =
+      new (std::nothrow) uint8_t[static_cast<size_t>(data_length)];
+  if (new_private_data == NULL) {
+    return false;
+  }
+
+  delete[] private_data_;
+  private_data_ = new_private_data;
+  private_data_length_ = data_length;
+  memcpy(private_data_, data, static_cast<size_t>(data_length));
+
+  return true;
+}
+
+uint64_t Projection::PayloadSize() const {
+  uint64_t size =
+      EbmlElementSize(libwebm::kMkvProjection, static_cast<uint64>(type_));
+
+  if (private_data_length_ > 0 && private_data_ != NULL) {
+    size += EbmlElementSize(libwebm::kMkvProjectionPrivate, private_data_,
+                            private_data_length_);
+  }
+
+  size += EbmlElementSize(libwebm::kMkvProjectionPoseYaw, pose_yaw_);
+  size += EbmlElementSize(libwebm::kMkvProjectionPosePitch, pose_pitch_);
+  size += EbmlElementSize(libwebm::kMkvProjectionPoseRoll, pose_roll_);
+
+  return size;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// VideoTrack Class
+
+VideoTrack::VideoTrack(unsigned int* seed)
+    : Track(seed),
+      display_height_(0),
+      display_width_(0),
+      pixel_height_(0),
+      pixel_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_space_(NULL),
+      colour_(NULL),
+      projection_(NULL) {}
+
+VideoTrack::~VideoTrack() {
+  delete colour_;
+  delete projection_;
+}
+
+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,
+          static_cast<uint64>((pixel_width_ > 0) ? pixel_width_ : width_)))
+    return false;
+  if (!WriteEbmlElement(
+          writer, libwebm::kMkvPixelHeight,
+          static_cast<uint64>((pixel_height_ > 0) ? pixel_height_ : height_)))
+    return false;
+  if (display_width_ > 0) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth,
+                          static_cast<uint64>(display_width_)))
+      return false;
+  }
+  if (display_height_ > 0) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight,
+                          static_cast<uint64>(display_height_)))
+      return false;
+  }
+  if (crop_left_ > 0) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft,
+                          static_cast<uint64>(crop_left_)))
+      return false;
+  }
+  if (crop_right_ > 0) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight,
+                          static_cast<uint64>(crop_right_)))
+      return false;
+  }
+  if (crop_top_ > 0) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop,
+                          static_cast<uint64>(crop_top_)))
+      return false;
+  }
+  if (crop_bottom_ > 0) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom,
+                          static_cast<uint64>(crop_bottom_)))
+      return false;
+  }
+  if (stereo_mode_ > kMono) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode,
+                          static_cast<uint64>(stereo_mode_)))
+      return false;
+  }
+  if (alpha_mode_ > kNoAlpha) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode,
+                          static_cast<uint64>(alpha_mode_)))
+      return false;
+  }
+  if (colour_space_) {
+    if (!WriteEbmlElement(writer, libwebm::kMkvColourSpace, colour_space_))
+      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;
+  }
+  if (projection_) {
+    if (!projection_->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;
+}
+
+void VideoTrack::set_colour_space(const char* colour_space) {
+  if (colour_space) {
+    delete[] colour_space_;
+
+    const size_t length = strlen(colour_space) + 1;
+    colour_space_ = new (std::nothrow) char[length];  // NOLINT
+    if (colour_space_) {
+#ifdef _MSC_VER
+      strcpy_s(colour_space_, length, colour_space);
+#else
+      strcpy(colour_space_, colour_space);
+#endif
+    }
+  }
+}
+
+bool VideoTrack::SetColour(const Colour& colour) {
+  std::unique_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->set_matrix_coefficients(colour.matrix_coefficients());
+  colour_ptr->set_bits_per_channel(colour.bits_per_channel());
+  colour_ptr->set_chroma_subsampling_horz(colour.chroma_subsampling_horz());
+  colour_ptr->set_chroma_subsampling_vert(colour.chroma_subsampling_vert());
+  colour_ptr->set_cb_subsampling_horz(colour.cb_subsampling_horz());
+  colour_ptr->set_cb_subsampling_vert(colour.cb_subsampling_vert());
+  colour_ptr->set_chroma_siting_horz(colour.chroma_siting_horz());
+  colour_ptr->set_chroma_siting_vert(colour.chroma_siting_vert());
+  colour_ptr->set_range(colour.range());
+  colour_ptr->set_transfer_characteristics(colour.transfer_characteristics());
+  colour_ptr->set_primaries(colour.primaries());
+  colour_ptr->set_max_cll(colour.max_cll());
+  colour_ptr->set_max_fall(colour.max_fall());
+  delete colour_;
+  colour_ = colour_ptr.release();
+  return true;
+}
+
+bool VideoTrack::SetProjection(const Projection& projection) {
+  std::unique_ptr<Projection> projection_ptr(new Projection());
+  if (!projection_ptr.get())
+    return false;
+
+  if (projection.private_data()) {
+    if (!projection_ptr->SetProjectionPrivate(
+            projection.private_data(), projection.private_data_length())) {
+      return false;
+    }
+  }
+
+  projection_ptr->set_type(projection.type());
+  projection_ptr->set_pose_yaw(projection.pose_yaw());
+  projection_ptr->set_pose_pitch(projection.pose_pitch());
+  projection_ptr->set_pose_roll(projection.pose_roll());
+  delete projection_;
+  projection_ = projection_ptr.release();
+  return true;
+}
+
+uint64_t VideoTrack::VideoPayloadSize() const {
+  uint64_t size = EbmlElementSize(
+      libwebm::kMkvPixelWidth,
+      static_cast<uint64>((pixel_width_ > 0) ? pixel_width_ : width_));
+  size += EbmlElementSize(
+      libwebm::kMkvPixelHeight,
+      static_cast<uint64>((pixel_height_ > 0) ? pixel_height_ : height_));
+  if (display_width_ > 0)
+    size += EbmlElementSize(libwebm::kMkvDisplayWidth,
+                            static_cast<uint64>(display_width_));
+  if (display_height_ > 0)
+    size += EbmlElementSize(libwebm::kMkvDisplayHeight,
+                            static_cast<uint64>(display_height_));
+  if (crop_left_ > 0)
+    size += EbmlElementSize(libwebm::kMkvPixelCropLeft,
+                            static_cast<uint64>(crop_left_));
+  if (crop_right_ > 0)
+    size += EbmlElementSize(libwebm::kMkvPixelCropRight,
+                            static_cast<uint64>(crop_right_));
+  if (crop_top_ > 0)
+    size += EbmlElementSize(libwebm::kMkvPixelCropTop,
+                            static_cast<uint64>(crop_top_));
+  if (crop_bottom_ > 0)
+    size += EbmlElementSize(libwebm::kMkvPixelCropBottom,
+                            static_cast<uint64>(crop_bottom_));
+  if (stereo_mode_ > kMono)
+    size += EbmlElementSize(libwebm::kMkvStereoMode,
+                            static_cast<uint64>(stereo_mode_));
+  if (alpha_mode_ > kNoAlpha)
+    size += EbmlElementSize(libwebm::kMkvAlphaMode,
+                            static_cast<uint64>(alpha_mode_));
+  if (frame_rate_ > 0.0)
+    size += EbmlElementSize(libwebm::kMkvFrameRate,
+                            static_cast<float>(frame_rate_));
+  if (colour_space_)
+    size += EbmlElementSize(libwebm::kMkvColourSpace, colour_space_);
+  if (colour_)
+    size += colour_->ColourSize();
+  if (projection_)
+    size += projection_->ProjectionSize();
+
+  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, static_cast<uint64>(channels_));
+  if (bit_depth_ > 0)
+    size +=
+        EbmlElementSize(libwebm::kMkvBitDepth, static_cast<uint64>(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, static_cast<uint64>(channels_));
+  if (bit_depth_ > 0)
+    size +=
+        EbmlElementSize(libwebm::kMkvBitDepth, static_cast<uint64>(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,
+                        static_cast<uint64>(channels_)))
+    return false;
+  if (bit_depth_ > 0)
+    if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth,
+                          static_cast<uint64>(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::kAv1CodecId[] = "V_AV1";
+const char Tracks::kVp8CodecId[] = "V_VP8";
+const char Tracks::kVp9CodecId[] = "V_VP9";
+const char Tracks::kWebVttCaptionsId[] = "D_WEBVTT/CAPTIONS";
+const char Tracks::kWebVttDescriptionsId[] = "D_WEBVTT/DESCRIPTIONS";
+const char Tracks::kWebVttMetadataId[] = "D_WEBVTT/METADATA";
+const char Tracks::kWebVttSubtitlesId[] = "D_WEBVTT/SUBTITLES";
+
+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, static_cast<uint64>(uid_)) +
+      EbmlElementSize(libwebm::kMkvChapterTimeStart,
+                      static_cast<uint64>(start_timecode_)) +
+      EbmlElementSize(libwebm::kMkvChapterTimeEnd,
+                      static_cast<uint64>(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,
+                        static_cast<uint64>(uid_)))
+    return 0;
+
+  if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart,
+                        static_cast<uint64>(start_timecode_)))
+    return 0;
+
+  if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd,
+                        static_cast<uint64>(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() {
+  // Delete any stored frames that are left behind. This will happen if the
+  // Cluster was not Finalized for whatever reason.
+  while (!stored_frames_.empty()) {
+    while (!stored_frames_.begin()->second.empty()) {
+      delete stored_frames_.begin()->second.front();
+      stored_frames_.begin()->second.pop_front();
+    }
+    stored_frames_.erase(stored_frames_.begin()->first);
+  }
+}
+
+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>(seek_entry_id_[i]));
+        entry_size[i] += EbmlElementSize(
+            libwebm::kMkvSeekPosition, static_cast<uint64>(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>(seek_entry_id_[i])))
+          return false;
+
+        if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition,
+                              static_cast<uint64>(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,
+                      static_cast<uint64>(UINT64_C(0xffffffff))) +
+      EbmlElementSize(libwebm::kMkvSeekPosition,
+                      static_cast<uint64>(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,
+                                  static_cast<uint64>(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,
+                        static_cast<uint64>(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),
+      estimate_file_duration_(false),
+      payload_pos_(0),
+      size_position_(0),
+      doc_type_version_(kDefaultDocTypeVersion),
+      doc_type_version_written_(0),
+      duration_(0.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;
+  memset(&track_frames_written_, 0,
+         sizeof(track_frames_written_[0]) * kMaxTrackNumber);
+  memset(&last_track_timestamp_, 0,
+         sizeof(last_track_timestamp_[0]) * kMaxTrackNumber);
+  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;
+
+  // In kLive mode, call Cluster::Finalize only if |accurate_cluster_duration_|
+  // is set. In all other modes, always call Cluster::Finalize.
+  if ((mode_ == kLive ? accurate_cluster_duration_ : true) &&
+      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_++;
+    }
+
+    double duration =
+        (static_cast<double>(last_timestamp_) + last_block_duration_) /
+        segment_info_.timecode_scale();
+    if (duration_ > 0.0) {
+      duration = duration_;
+    } else {
+      if (last_block_duration_ == 0 && estimate_file_duration_) {
+        const int num_tracks = static_cast<int>(tracks_.track_entries_size());
+        for (int i = 0; i < num_tracks; ++i) {
+          if (track_frames_written_[i] < 2)
+            continue;
+
+          // Estimate the duration for the last block of a Track.
+          const double nano_per_frame =
+              static_cast<double>(last_track_timestamp_[i]) /
+              (track_frames_written_[i] - 1);
+          const double track_duration =
+              (last_track_timestamp_[i] + nano_per_frame) /
+              segment_info_.timecode_scale();
+          if (track_duration > duration)
+            duration = track_duration;
+        }
+      }
+    }
+    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;
+
+        const char* const doc_type =
+            DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska;
+        if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type))
+          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);
+
+  if (!tracks_.AddTrack(track, number)) {
+    delete track;
+    return 0;
+  }
+  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)) {
+    delete 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);
+
+  if (!tracks_.AddTrack(track, number)) {
+    delete track;
+    return 0;
+  }
+
+  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 (cluster_list_size_ > 0) {
+    const uint64_t timecode_scale = segment_info_.timecode_scale();
+    const uint64_t frame_timecode = frame->timestamp() / timecode_scale;
+
+    const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1];
+    const uint64_t last_cluster_timecode = last_cluster->timecode();
+
+    const uint64_t rel_timecode = frame_timecode - last_cluster_timecode;
+    if (rel_timecode > kMaxBlockTimecode) {
+      force_new_cluster_ = true;
+    }
+  }
+
+  // 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)) {
+      delete new_frame;
+      return false;
+    }
+    if (!QueueFrame(new_frame)) {
+      delete new_frame;
+      return false;
+    }
+    track_frames_written_[frame->track_number() - 1]++;
+    return true;
+  }
+
+  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 || !new_frame->CopyFrom(*frame)) {
+      delete new_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();
+  track_frames_written_[frame->track_number() - 1]++;
+
+  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();
+
+  const char* const doc_type =
+      DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska;
+  if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type))
+    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;
+}
+
+bool Segment::DocTypeIsWebm() const {
+  const int kNumCodecIds = 9;
+
+  // TODO(vigneshv): Tweak .clang-format.
+  const char* kWebmCodecIds[kNumCodecIds] = {
+      Tracks::kOpusCodecId,          Tracks::kVorbisCodecId,
+      Tracks::kAv1CodecId,           Tracks::kVp8CodecId,
+      Tracks::kVp9CodecId,           Tracks::kWebVttCaptionsId,
+      Tracks::kWebVttDescriptionsId, Tracks::kWebVttMetadataId,
+      Tracks::kWebVttSubtitlesId};
+
+  const int num_tracks = static_cast<int>(tracks_.track_entries_size());
+  for (int track_index = 0; track_index < num_tracks; ++track_index) {
+    const Track* const track = tracks_.GetTrackByIndex(track_index);
+    const std::string codec_id = track->codec_id();
+
+    bool id_is_webm = false;
+    for (int id_index = 0; id_index < kNumCodecIds; ++id_index) {
+      if (codec_id == kWebmCodecIds[id_index]) {
+        id_is_webm = true;
+        break;
+      }
+    }
+
+    if (!id_is_webm)
+      return false;
+  }
+
+  return true;
+}
+
+}  // namespace mkvmuxer
diff --git a/mkvmuxer/mkvmuxer.h b/mkvmuxer/mkvmuxer.h
new file mode 100644
index 0000000..f2db377
--- /dev/null
+++ b/mkvmuxer/mkvmuxer.h
@@ -0,0 +1,1924 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS.  All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef MKVMUXER_MKVMUXER_H_
+#define MKVMUXER_MKVMUXER_H_
+
+#include <stdint.h>
+
+#include <cstddef>
+#include <list>
+#include <map>
+
+#include "common/webmids.h"
+#include "mkvmuxer/mkvmuxertypes.h"
+
+// For a description of the WebM elements see
+// http://www.webmproject.org/code/specs/container/.
+
+namespace mkvparser {
+class IMkvReader;
+}  // namespace mkvparser
+
+namespace mkvmuxer {
+
+class MkvWriter;
+class Segment;
+
+const uint64_t kMaxTrackNumber = 126;
+
+///////////////////////////////////////////////////////////////
+// Interface used by the mkvmuxer to write out the Mkv data.
+class IMkvWriter {
+ public:
+  // Writes out |len| bytes of |buf|. Returns 0 on success.
+  virtual int32 Write(const void* buf, uint32 len) = 0;
+
+  // Returns the offset of the output position from the beginning of the
+  // output.
+  virtual int64 Position() const = 0;
+
+  // Set the current File position. Returns 0 on success.
+  virtual int32 Position(int64 position) = 0;
+
+  // Returns true if the writer is seekable.
+  virtual bool Seekable() const = 0;
+
+  // Element start notification. Called whenever an element identifier is about
+  // to be written to the stream. |element_id| is the element identifier, and
+  // |position| is the location in the WebM stream where the first octet of the
+  // element identifier will be written.
+  // Note: the |MkvId| enumeration in webmids.hpp defines element values.
+  virtual void ElementStartNotify(uint64 element_id, int64 position) = 0;
+
+ protected:
+  IMkvWriter();
+  virtual ~IMkvWriter();
+
+ private:
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(IMkvWriter);
+};
+
+// Writes out the EBML header for a WebM file, but allows caller to specify
+// DocType. This function must be called before any other libwebm writing
+// functions are called.
+bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version,
+                     const char* const doc_type);
+
+// Writes out the EBML header for a WebM file. This function must be called
+// before any other libwebm writing functions are called.
+bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version);
+
+// Deprecated. Writes out EBML header with doc_type_version as
+// kDefaultDocTypeVersion. Exists for backward compatibility.
+bool WriteEbmlHeader(IMkvWriter* writer);
+
+// Copies in Chunk from source to destination between the given byte positions
+bool ChunkedCopy(mkvparser::IMkvReader* source, IMkvWriter* dst, int64_t start,
+                 int64_t size);
+
+///////////////////////////////////////////////////////////////
+// Class to hold data the will be written to a block.
+class Frame {
+ public:
+  Frame();
+  ~Frame();
+
+  // Sets this frame's contents based on |frame|. Returns true on success. On
+  // failure, this frame's existing contents may be lost.
+  bool CopyFrom(const Frame& frame);
+
+  // Copies |frame| data into |frame_|. Returns true on success.
+  bool Init(const uint8_t* frame, uint64_t length);
+
+  // Copies |additional| data into |additional_|. Returns true on success.
+  bool AddAdditionalData(const uint8_t* additional, uint64_t length,
+                         uint64_t add_id);
+
+  // Returns true if the frame has valid parameters.
+  bool IsValid() const;
+
+  // Returns true if the frame can be written as a SimpleBlock based on current
+  // parameters.
+  bool CanBeSimpleBlock() const;
+
+  uint64_t add_id() const { return add_id_; }
+  const uint8_t* additional() const { return additional_; }
+  uint64_t additional_length() const { return additional_length_; }
+  void set_duration(uint64_t duration);
+  uint64_t duration() const { return duration_; }
+  bool duration_set() const { return duration_set_; }
+  const uint8_t* frame() const { return frame_; }
+  void set_is_key(bool key) { is_key_ = key; }
+  bool is_key() const { return is_key_; }
+  uint64_t length() const { return length_; }
+  void set_track_number(uint64_t track_number) { track_number_ = track_number; }
+  uint64_t track_number() const { return track_number_; }
+  void set_timestamp(uint64_t timestamp) { timestamp_ = timestamp; }
+  uint64_t timestamp() const { return timestamp_; }
+  void set_discard_padding(int64_t discard_padding) {
+    discard_padding_ = discard_padding;
+  }
+  int64_t discard_padding() const { return discard_padding_; }
+  void set_reference_block_timestamp(int64_t reference_block_timestamp);
+  int64_t reference_block_timestamp() const {
+    return reference_block_timestamp_;
+  }
+  bool reference_block_timestamp_set() const {
+    return reference_block_timestamp_set_;
+  }
+
+ private:
+  // Id of the Additional data.
+  uint64_t add_id_;
+
+  // Pointer to additional data. Owned by this class.
+  uint8_t* additional_;
+
+  // Length of the additional data.
+  uint64_t additional_length_;
+
+  // Duration of the frame in nanoseconds.
+  uint64_t duration_;
+
+  // Flag indicating that |duration_| has been set. Setting duration causes the
+  // frame to be written out as a Block with BlockDuration instead of as a
+  // SimpleBlock.
+  bool duration_set_;
+
+  // Pointer to the data. Owned by this class.
+  uint8_t* frame_;
+
+  // Flag telling if the data should set the key flag of a block.
+  bool is_key_;
+
+  // Length of the data.
+  uint64_t length_;
+
+  // Mkv track number the data is associated with.
+  uint64_t track_number_;
+
+  // Timestamp of the data in nanoseconds.
+  uint64_t timestamp_;
+
+  // Discard padding for the frame.
+  int64_t discard_padding_;
+
+  // Reference block timestamp.
+  int64_t reference_block_timestamp_;
+
+  // Flag indicating if |reference_block_timestamp_| has been set.
+  bool reference_block_timestamp_set_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Frame);
+};
+
+///////////////////////////////////////////////////////////////
+// Class to hold one cue point in a Cues element.
+class CuePoint {
+ public:
+  CuePoint();
+  ~CuePoint();
+
+  // Returns the size in bytes for the entire CuePoint element.
+  uint64_t Size() const;
+
+  // Output the CuePoint element to the writer. Returns true on success.
+  bool Write(IMkvWriter* writer) const;
+
+  void set_time(uint64_t time) { time_ = time; }
+  uint64_t time() const { return time_; }
+  void set_track(uint64_t track) { track_ = track; }
+  uint64_t track() const { return track_; }
+  void set_cluster_pos(uint64_t cluster_pos) { cluster_pos_ = cluster_pos; }
+  uint64_t cluster_pos() const { return cluster_pos_; }
+  void set_block_number(uint64_t block_number) { block_number_ = block_number; }
+  uint64_t block_number() const { return block_number_; }
+  void set_output_block_number(bool output_block_number) {
+    output_block_number_ = output_block_number;
+  }
+  bool output_block_number() const { return output_block_number_; }
+
+ private:
+  // Returns the size in bytes for the payload of the CuePoint element.
+  uint64_t PayloadSize() const;
+
+  // Absolute timecode according to the segment time base.
+  uint64_t time_;
+
+  // The Track element associated with the CuePoint.
+  uint64_t track_;
+
+  // The position of the Cluster containing the Block.
+  uint64_t cluster_pos_;
+
+  // Number of the Block within the Cluster, starting from 1.
+  uint64_t block_number_;
+
+  // If true the muxer will write out the block number for the cue if the
+  // block number is different than the default of 1. Default is set to true.
+  bool output_block_number_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(CuePoint);
+};
+
+///////////////////////////////////////////////////////////////
+// Cues element.
+class Cues {
+ public:
+  Cues();
+  ~Cues();
+
+  // Adds a cue point to the Cues element. Returns true on success.
+  bool AddCue(CuePoint* cue);
+
+  // Returns the cue point by index. Returns NULL if there is no cue point
+  // match.
+  CuePoint* GetCueByIndex(int32_t index) const;
+
+  // Returns the total size of the Cues element
+  uint64_t Size();
+
+  // Output the Cues element to the writer. Returns true on success.
+  bool Write(IMkvWriter* writer) const;
+
+  int32_t cue_entries_size() const { return cue_entries_size_; }
+  void set_output_block_number(bool output_block_number) {
+    output_block_number_ = output_block_number;
+  }
+  bool output_block_number() const { return output_block_number_; }
+
+ private:
+  // Number of allocated elements in |cue_entries_|.
+  int32_t cue_entries_capacity_;
+
+  // Number of CuePoints in |cue_entries_|.
+  int32_t cue_entries_size_;
+
+  // CuePoint list.
+  CuePoint** cue_entries_;
+
+  // If true the muxer will write out the block number for the cue if the
+  // block number is different than the default of 1. Default is set to true.
+  bool output_block_number_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cues);
+};
+
+///////////////////////////////////////////////////////////////
+// ContentEncAESSettings element
+class ContentEncAESSettings {
+ public:
+  enum { kCTR = 1 };
+
+  ContentEncAESSettings();
+  ~ContentEncAESSettings() {}
+
+  // Returns the size in bytes for the ContentEncAESSettings element.
+  uint64_t Size() const;
+
+  // Writes out the ContentEncAESSettings element to |writer|. Returns true on
+  // success.
+  bool Write(IMkvWriter* writer) const;
+
+  uint64_t cipher_mode() const { return cipher_mode_; }
+
+ private:
+  // Returns the size in bytes for the payload of the ContentEncAESSettings
+  // element.
+  uint64_t PayloadSize() const;
+
+  // Sub elements
+  uint64_t cipher_mode_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncAESSettings);
+};
+
+///////////////////////////////////////////////////////////////
+// ContentEncoding element
+// Elements used to describe if the track data has been encrypted or
+// compressed with zlib or header stripping.
+// Currently only whole frames can be encrypted with AES. This dictates that
+// ContentEncodingOrder will be 0, ContentEncodingScope will be 1,
+// ContentEncodingType will be 1, and ContentEncAlgo will be 5.
+class ContentEncoding {
+ public:
+  ContentEncoding();
+  ~ContentEncoding();
+
+  // Sets the content encryption id. Copies |length| bytes from |id| to
+  // |enc_key_id_|. Returns true on success.
+  bool SetEncryptionID(const uint8_t* id, uint64_t length);
+
+  // Returns the size in bytes for the ContentEncoding element.
+  uint64_t Size() const;
+
+  // Writes out the ContentEncoding element to |writer|. Returns true on
+  // success.
+  bool Write(IMkvWriter* writer) const;
+
+  uint64_t enc_algo() const { return enc_algo_; }
+  uint64_t encoding_order() const { return encoding_order_; }
+  uint64_t encoding_scope() const { return encoding_scope_; }
+  uint64_t encoding_type() const { return encoding_type_; }
+  ContentEncAESSettings* enc_aes_settings() { return &enc_aes_settings_; }
+
+ private:
+  // Returns the size in bytes for the encoding elements.
+  uint64_t EncodingSize(uint64_t compresion_size,
+                        uint64_t encryption_size) const;
+
+  // Returns the size in bytes for the encryption elements.
+  uint64_t EncryptionSize() const;
+
+  // Track element names
+  uint64_t enc_algo_;
+  uint8_t* enc_key_id_;
+  uint64_t encoding_order_;
+  uint64_t encoding_scope_;
+  uint64_t encoding_type_;
+
+  // ContentEncAESSettings element.
+  ContentEncAESSettings enc_aes_settings_;
+
+  // Size of the ContentEncKeyID data in bytes.
+  uint64_t enc_key_id_length_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
+};
+
+///////////////////////////////////////////////////////////////
+// Colour element.
+class PrimaryChromaticity {
+ public:
+  static const float kChromaticityMin;
+  static const float kChromaticityMax;
+
+  PrimaryChromaticity(float x_val, float y_val) : x_(x_val), y_(y_val) {}
+  PrimaryChromaticity() : x_(0), y_(0) {}
+  ~PrimaryChromaticity() {}
+
+  // Returns sum of |x_id| and |y_id| element id sizes and payload sizes.
+  uint64_t PrimaryChromaticitySize(libwebm::MkvId x_id,
+                                   libwebm::MkvId y_id) const;
+  bool Valid() const;
+  bool Write(IMkvWriter* writer, libwebm::MkvId x_id,
+             libwebm::MkvId y_id) const;
+
+  float x() const { return x_; }
+  void set_x(float new_x) { x_ = new_x; }
+  float y() const { return y_; }
+  void set_y(float new_y) { y_ = new_y; }
+
+ private:
+  float x_;
+  float y_;
+};
+
+class MasteringMetadata {
+ public:
+  static const float kValueNotPresent;
+  static const float kMinLuminance;
+  static const float kMinLuminanceMax;
+  static const float kMaxLuminanceMax;
+
+  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 Valid() 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() const { return luminance_max_; }
+  void set_luminance_max(float luminance_max) {
+    luminance_max_ = luminance_max;
+  }
+  float luminance_min() const { return luminance_min_; }
+  void set_luminance_min(float luminance_min) {
+    luminance_min_ = luminance_min;
+  }
+
+ private:
+  // Returns size of MasteringMetadata child elements.
+  uint64_t PayloadSize() const;
+
+  float luminance_max_;
+  float luminance_min_;
+  PrimaryChromaticity* r_;
+  PrimaryChromaticity* g_;
+  PrimaryChromaticity* b_;
+  PrimaryChromaticity* white_point_;
+};
+
+class Colour {
+ public:
+  enum MatrixCoefficients {
+    kGbr = 0,
+    kBt709 = 1,
+    kUnspecifiedMc = 2,
+    kReserved = 3,
+    kFcc = 4,
+    kBt470bg = 5,
+    kSmpte170MMc = 6,
+    kSmpte240MMc = 7,
+    kYcocg = 8,
+    kBt2020NonConstantLuminance = 9,
+    kBt2020ConstantLuminance = 10,
+  };
+  enum ChromaSitingHorz {
+    kUnspecifiedCsh = 0,
+    kLeftCollocated = 1,
+    kHalfCsh = 2,
+  };
+  enum ChromaSitingVert {
+    kUnspecifiedCsv = 0,
+    kTopCollocated = 1,
+    kHalfCsv = 2,
+  };
+  enum Range {
+    kUnspecifiedCr = 0,
+    kBroadcastRange = 1,
+    kFullRange = 2,
+    kMcTcDefined = 3,  // Defined by MatrixCoefficients/TransferCharacteristics.
+  };
+  enum TransferCharacteristics {
+    kIturBt709Tc = 1,
+    kUnspecifiedTc = 2,
+    kReservedTc = 3,
+    kGamma22Curve = 4,
+    kGamma28Curve = 5,
+    kSmpte170MTc = 6,
+    kSmpte240MTc = 7,
+    kLinear = 8,
+    kLog = 9,
+    kLogSqrt = 10,
+    kIec6196624 = 11,
+    kIturBt1361ExtendedColourGamut = 12,
+    kIec6196621 = 13,
+    kIturBt202010bit = 14,
+    kIturBt202012bit = 15,
+    kSmpteSt2084 = 16,
+    kSmpteSt4281Tc = 17,
+    kAribStdB67Hlg = 18,
+  };
+  enum Primaries {
+    kReservedP0 = 0,
+    kIturBt709P = 1,
+    kUnspecifiedP = 2,
+    kReservedP3 = 3,
+    kIturBt470M = 4,
+    kIturBt470Bg = 5,
+    kSmpte170MP = 6,
+    kSmpte240MP = 7,
+    kFilm = 8,
+    kIturBt2020 = 9,
+    kSmpteSt4281P = 10,
+    kJedecP22Phosphors = 22,
+  };
+  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 Valid() 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() const { return matrix_coefficients_; }
+  void set_matrix_coefficients(uint64_t matrix_coefficients) {
+    matrix_coefficients_ = matrix_coefficients;
+  }
+  uint64_t bits_per_channel() const { return bits_per_channel_; }
+  void set_bits_per_channel(uint64_t bits_per_channel) {
+    bits_per_channel_ = bits_per_channel;
+  }
+  uint64_t chroma_subsampling_horz() const { return chroma_subsampling_horz_; }
+  void set_chroma_subsampling_horz(uint64_t chroma_subsampling_horz) {
+    chroma_subsampling_horz_ = chroma_subsampling_horz;
+  }
+  uint64_t chroma_subsampling_vert() const { return chroma_subsampling_vert_; }
+  void set_chroma_subsampling_vert(uint64_t chroma_subsampling_vert) {
+    chroma_subsampling_vert_ = chroma_subsampling_vert;
+  }
+  uint64_t cb_subsampling_horz() const { return cb_subsampling_horz_; }
+  void set_cb_subsampling_horz(uint64_t cb_subsampling_horz) {
+    cb_subsampling_horz_ = cb_subsampling_horz;
+  }
+  uint64_t cb_subsampling_vert() const { return cb_subsampling_vert_; }
+  void set_cb_subsampling_vert(uint64_t cb_subsampling_vert) {
+    cb_subsampling_vert_ = cb_subsampling_vert;
+  }
+  uint64_t chroma_siting_horz() const { return chroma_siting_horz_; }
+  void set_chroma_siting_horz(uint64_t chroma_siting_horz) {
+    chroma_siting_horz_ = chroma_siting_horz;
+  }
+  uint64_t chroma_siting_vert() const { return chroma_siting_vert_; }
+  void set_chroma_siting_vert(uint64_t chroma_siting_vert) {
+    chroma_siting_vert_ = chroma_siting_vert;
+  }
+  uint64_t range() const { return range_; }
+  void set_range(uint64_t range) { range_ = range; }
+  uint64_t transfer_characteristics() const {
+    return transfer_characteristics_;
+  }
+  void set_transfer_characteristics(uint64_t transfer_characteristics) {
+    transfer_characteristics_ = transfer_characteristics;
+  }
+  uint64_t primaries() const { return primaries_; }
+  void set_primaries(uint64_t primaries) { primaries_ = primaries; }
+  uint64_t max_cll() const { return max_cll_; }
+  void set_max_cll(uint64_t max_cll) { max_cll_ = max_cll; }
+  uint64_t max_fall() const { return max_fall_; }
+  void set_max_fall(uint64_t max_fall) { max_fall_ = max_fall; }
+
+ private:
+  // Returns size of Colour child elements.
+  uint64_t PayloadSize() const;
+
+  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_;
+
+  MasteringMetadata* mastering_metadata_;
+};
+
+///////////////////////////////////////////////////////////////
+// Projection element.
+class Projection {
+ public:
+  enum ProjectionType {
+    kTypeNotPresent = -1,
+    kRectangular = 0,
+    kEquirectangular = 1,
+    kCubeMap = 2,
+    kMesh = 3,
+  };
+  static const uint64_t kValueNotPresent;
+  Projection()
+      : type_(kRectangular),
+        pose_yaw_(0.0),
+        pose_pitch_(0.0),
+        pose_roll_(0.0),
+        private_data_(NULL),
+        private_data_length_(0) {}
+  ~Projection() { delete[] private_data_; }
+
+  uint64_t ProjectionSize() const;
+  bool Write(IMkvWriter* writer) const;
+
+  bool SetProjectionPrivate(const uint8_t* private_data,
+                            uint64_t private_data_length);
+
+  ProjectionType type() const { return type_; }
+  void set_type(ProjectionType type) { type_ = type; }
+  float pose_yaw() const { return pose_yaw_; }
+  void set_pose_yaw(float pose_yaw) { pose_yaw_ = pose_yaw; }
+  float pose_pitch() const { return pose_pitch_; }
+  void set_pose_pitch(float pose_pitch) { pose_pitch_ = pose_pitch; }
+  float pose_roll() const { return pose_roll_; }
+  void set_pose_roll(float pose_roll) { pose_roll_ = pose_roll; }
+  uint8_t* private_data() const { return private_data_; }
+  uint64_t private_data_length() const { return private_data_length_; }
+
+ private:
+  // Returns size of VideoProjection child elements.
+  uint64_t PayloadSize() const;
+
+  ProjectionType type_;
+  float pose_yaw_;
+  float pose_pitch_;
+  float pose_roll_;
+  uint8_t* private_data_;
+  uint64_t private_data_length_;
+};
+
+///////////////////////////////////////////////////////////////
+// Track element.
+class Track {
+ public:
+  // The |seed| parameter is used to synthesize a UID for the track.
+  explicit Track(unsigned int* seed);
+  virtual ~Track();
+
+  // Adds a ContentEncoding element to the Track. Returns true on success.
+  virtual bool AddContentEncoding();
+
+  // Returns the ContentEncoding by index. Returns NULL if there is no
+  // ContentEncoding match.
+  ContentEncoding* GetContentEncodingByIndex(uint32_t index) const;
+
+  // Returns the size in bytes for the payload of the Track element.
+  virtual uint64_t PayloadSize() const;
+
+  // Returns the size in bytes of the Track element.
+  virtual uint64_t Size() const;
+
+  // Output the Track element to the writer. Returns true on success.
+  virtual bool Write(IMkvWriter* writer) const;
+
+  // Sets the CodecPrivate element of the Track element. Copies |length|
+  // bytes from |codec_private| to |codec_private_|. Returns true on success.
+  bool SetCodecPrivate(const uint8_t* codec_private, uint64_t length);
+
+  void set_codec_id(const char* codec_id);
+  const char* codec_id() const { return codec_id_; }
+  const uint8_t* codec_private() const { return codec_private_; }
+  void set_language(const char* language);
+  const char* language() const { return language_; }
+  void set_max_block_additional_id(uint64_t max_block_additional_id) {
+    max_block_additional_id_ = max_block_additional_id;
+  }
+  uint64_t max_block_additional_id() const { return max_block_additional_id_; }
+  void set_name(const char* name);
+  const char* name() const { return name_; }
+  void set_number(uint64_t number) { number_ = number; }
+  uint64_t number() const { return number_; }
+  void set_type(uint64_t type) { type_ = type; }
+  uint64_t type() const { return type_; }
+  void set_uid(uint64_t uid) { uid_ = uid; }
+  uint64_t uid() const { return uid_; }
+  void set_codec_delay(uint64_t codec_delay) { codec_delay_ = codec_delay; }
+  uint64_t codec_delay() const { return codec_delay_; }
+  void set_seek_pre_roll(uint64_t seek_pre_roll) {
+    seek_pre_roll_ = seek_pre_roll;
+  }
+  uint64_t seek_pre_roll() const { return seek_pre_roll_; }
+  void set_default_duration(uint64_t default_duration) {
+    default_duration_ = default_duration;
+  }
+  uint64_t default_duration() const { return default_duration_; }
+
+  uint64_t codec_private_length() const { return codec_private_length_; }
+  uint32_t content_encoding_entries_size() const {
+    return content_encoding_entries_size_;
+  }
+
+ private:
+  // Track element names.
+  char* codec_id_;
+  uint8_t* codec_private_;
+  char* language_;
+  uint64_t max_block_additional_id_;
+  char* name_;
+  uint64_t number_;
+  uint64_t type_;
+  uint64_t uid_;
+  uint64_t codec_delay_;
+  uint64_t seek_pre_roll_;
+  uint64_t default_duration_;
+
+  // Size of the CodecPrivate data in bytes.
+  uint64_t codec_private_length_;
+
+  // ContentEncoding element list.
+  ContentEncoding** content_encoding_entries_;
+
+  // Number of ContentEncoding elements added.
+  uint32_t content_encoding_entries_size_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Track);
+};
+
+///////////////////////////////////////////////////////////////
+// Track that has video specific elements.
+class VideoTrack : public Track {
+ public:
+  // Supported modes for stereo 3D.
+  enum StereoMode {
+    kMono = 0,
+    kSideBySideLeftIsFirst = 1,
+    kTopBottomRightIsFirst = 2,
+    kTopBottomLeftIsFirst = 3,
+    kSideBySideRightIsFirst = 11
+  };
+
+  enum AlphaMode { kNoAlpha = 0, kAlpha = 1 };
+
+  // The |seed| parameter is used to synthesize a UID for the track.
+  explicit VideoTrack(unsigned int* seed);
+  virtual ~VideoTrack();
+
+  // Returns the size in bytes for the payload of the Track element plus the
+  // video specific elements.
+  virtual uint64_t PayloadSize() const;
+
+  // Output the VideoTrack element to the writer. Returns true on success.
+  virtual bool Write(IMkvWriter* writer) const;
+
+  // Sets the video's stereo mode. Returns true on success.
+  bool SetStereoMode(uint64_t stereo_mode);
+
+  // Sets the video's alpha mode. Returns true on success.
+  bool SetAlphaMode(uint64_t alpha_mode);
+
+  void set_display_height(uint64_t height) { display_height_ = height; }
+  uint64_t display_height() const { return display_height_; }
+  void set_display_width(uint64_t width) { display_width_ = width; }
+  uint64_t display_width() const { return display_width_; }
+  void set_pixel_height(uint64_t height) { pixel_height_ = height; }
+  uint64_t pixel_height() const { return pixel_height_; }
+  void set_pixel_width(uint64_t width) { pixel_width_ = width; }
+  uint64_t pixel_width() const { return pixel_width_; }
+
+  void set_crop_left(uint64_t crop_left) { crop_left_ = crop_left; }
+  uint64_t crop_left() const { return crop_left_; }
+  void set_crop_right(uint64_t crop_right) { crop_right_ = crop_right; }
+  uint64_t crop_right() const { return crop_right_; }
+  void set_crop_top(uint64_t crop_top) { crop_top_ = crop_top; }
+  uint64_t crop_top() const { return crop_top_; }
+  void set_crop_bottom(uint64_t crop_bottom) { crop_bottom_ = crop_bottom; }
+  uint64_t crop_bottom() const { return crop_bottom_; }
+
+  void set_frame_rate(double frame_rate) { frame_rate_ = frame_rate; }
+  double frame_rate() const { return frame_rate_; }
+  void set_height(uint64_t height) { height_ = height; }
+  uint64_t height() const { return height_; }
+  uint64_t stereo_mode() { return stereo_mode_; }
+  uint64_t alpha_mode() { return alpha_mode_; }
+  void set_width(uint64_t width) { width_ = width; }
+  uint64_t width() const { return width_; }
+  void set_colour_space(const char* colour_space);
+  const char* colour_space() const { return colour_space_; }
+
+  Colour* colour() { return colour_; }
+
+  // Deep copies |colour|.
+  bool SetColour(const Colour& colour);
+
+  Projection* projection() { return projection_; }
+
+  // Deep copies |projection|.
+  bool SetProjection(const Projection& projection);
+
+ private:
+  // Returns the size in bytes of the Video element.
+  uint64_t VideoPayloadSize() const;
+
+  // Video track element names.
+  uint64_t display_height_;
+  uint64_t display_width_;
+  uint64_t pixel_height_;
+  uint64_t pixel_width_;
+  uint64_t crop_left_;
+  uint64_t crop_right_;
+  uint64_t crop_top_;
+  uint64_t crop_bottom_;
+  double frame_rate_;
+  uint64_t height_;
+  uint64_t stereo_mode_;
+  uint64_t alpha_mode_;
+  uint64_t width_;
+  char* colour_space_;
+
+  Colour* colour_;
+  Projection* projection_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack);
+};
+
+///////////////////////////////////////////////////////////////
+// Track that has audio specific elements.
+class AudioTrack : public Track {
+ public:
+  // The |seed| parameter is used to synthesize a UID for the track.
+  explicit AudioTrack(unsigned int* seed);
+  virtual ~AudioTrack();
+
+  // Returns the size in bytes for the payload of the Track element plus the
+  // audio specific elements.
+  virtual uint64_t PayloadSize() const;
+
+  // Output the AudioTrack element to the writer. Returns true on success.
+  virtual bool Write(IMkvWriter* writer) const;
+
+  void set_bit_depth(uint64_t bit_depth) { bit_depth_ = bit_depth; }
+  uint64_t bit_depth() const { return bit_depth_; }
+  void set_channels(uint64_t channels) { channels_ = channels; }
+  uint64_t channels() const { return channels_; }
+  void set_sample_rate(double sample_rate) { sample_rate_ = sample_rate; }
+  double sample_rate() const { return sample_rate_; }
+
+ private:
+  // Audio track element names.
+  uint64_t bit_depth_;
+  uint64_t channels_;
+  double sample_rate_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(AudioTrack);
+};
+
+///////////////////////////////////////////////////////////////
+// Tracks element
+class Tracks {
+ public:
+  // Audio and video type defined by the Matroska specs.
+  enum { kVideo = 0x1, kAudio = 0x2 };
+
+  static const char kOpusCodecId[];
+  static const char kVorbisCodecId[];
+  static const char kAv1CodecId[];
+  static const char kVp8CodecId[];
+  static const char kVp9CodecId[];
+  static const char kWebVttCaptionsId[];
+  static const char kWebVttDescriptionsId[];
+  static const char kWebVttMetadataId[];
+  static const char kWebVttSubtitlesId[];
+
+  Tracks();
+  ~Tracks();
+
+  // Adds a Track element to the Tracks object. |track| will be owned and
+  // deleted by the Tracks object. Returns true on success. |number| is the
+  // number to use for the track. |number| must be >= 0. If |number| == 0
+  // then the muxer will decide on the track number.
+  bool AddTrack(Track* track, int32_t number);
+
+  // Returns the track by index. Returns NULL if there is no track match.
+  const Track* GetTrackByIndex(uint32_t idx) const;
+
+  // Search the Tracks and return the track that matches |tn|. Returns NULL
+  // if there is no track match.
+  Track* GetTrackByNumber(uint64_t track_number) const;
+
+  // Returns true if the track number is an audio track.
+  bool TrackIsAudio(uint64_t track_number) const;
+
+  // Returns true if the track number is a video track.
+  bool TrackIsVideo(uint64_t track_number) const;
+
+  // Output the Tracks element to the writer. Returns true on success.
+  bool Write(IMkvWriter* writer) const;
+
+  uint32_t track_entries_size() const { return track_entries_size_; }
+
+ private:
+  // Track element list.
+  Track** track_entries_;
+
+  // Number of Track elements added.
+  uint32_t track_entries_size_;
+
+  // Whether or not Tracks element has already been written via IMkvWriter.
+  mutable bool wrote_tracks_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tracks);
+};
+
+///////////////////////////////////////////////////////////////
+// Chapter element
+//
+class Chapter {
+ public:
+  // Set the identifier for this chapter.  (This corresponds to the
+  // Cue Identifier line in WebVTT.)
+  // TODO(matthewjheaney): the actual serialization of this item in
+  // MKV is pending.
+  bool set_id(const char* id);
+
+  // Converts the nanosecond start and stop times of this chapter to
+  // their corresponding timecode values, and stores them that way.
+  void set_time(const Segment& segment, uint64_t start_time_ns,
+                uint64_t end_time_ns);
+
+  // Sets the uid for this chapter. Primarily used to enable
+  // deterministic output from the muxer.
+  void set_uid(const uint64_t uid) { uid_ = uid; }
+
+  // Add a title string to this chapter, per the semantics described
+  // here:
+  //  http://www.matroska.org/technical/specs/index.html
+  //
+  // The title ("chapter string") is a UTF-8 string.
+  //
+  // The language has ISO 639-2 representation, described here:
+  //  http://www.loc.gov/standards/iso639-2/englangn.html
+  //  http://www.loc.gov/standards/iso639-2/php/English_list.php
+  // If you specify NULL as the language value, this implies
+  // English ("eng").
+  //
+  // The country value corresponds to the codes listed here:
+  //  http://www.iana.org/domains/root/db/
+  //
+  // The function returns false if the string could not be allocated.
+  bool add_string(const char* title, const char* language, const char* country);
+
+ private:
+  friend class Chapters;
+
+  // For storage of chapter titles that differ by language.
+  class Display {
+   public:
+    // Establish representation invariant for new Display object.
+    void Init();
+
+    // Reclaim resources, in anticipation of destruction.
+    void Clear();
+
+    // Copies the title to the |title_| member.  Returns false on
+    // error.
+    bool set_title(const char* title);
+
+    // Copies the language to the |language_| member.  Returns false
+    // on error.
+    bool set_language(const char* language);
+
+    // Copies the country to the |country_| member.  Returns false on
+    // error.
+    bool set_country(const char* country);
+
+    // If |writer| is non-NULL, serialize the Display sub-element of
+    // the Atom into the stream.  Returns the Display element size on
+    // success, 0 if error.
+    uint64_t WriteDisplay(IMkvWriter* writer) const;
+
+   private:
+    char* title_;
+    char* language_;
+    char* country_;
+  };
+
+  Chapter();
+  ~Chapter();
+
+  // Establish the representation invariant for a newly-created
+  // Chapter object.  The |seed| parameter is used to create the UID
+  // for this chapter atom.
+  void Init(unsigned int* seed);
+
+  // Copies this Chapter object to a different one.  This is used when
+  // expanding a plain array of Chapter objects (see Chapters).
+  void ShallowCopy(Chapter* dst) const;
+
+  // Reclaim resources used by this Chapter object, pending its
+  // destruction.
+  void Clear();
+
+  // If there is no storage remaining on the |displays_| array for a
+  // new display object, creates a new, longer array and copies the
+  // existing Display objects to the new array.  Returns false if the
+  // array cannot be expanded.
+  bool ExpandDisplaysArray();
+
+  // If |writer| is non-NULL, serialize the Atom sub-element into the
+  // stream.  Returns the total size of the element on success, 0 if
+  // error.
+  uint64_t WriteAtom(IMkvWriter* writer) const;
+
+  // The string identifier for this chapter (corresponds to WebVTT cue
+  // identifier).
+  char* id_;
+
+  // Start timecode of the chapter.
+  uint64_t start_timecode_;
+
+  // Stop timecode of the chapter.
+  uint64_t end_timecode_;
+
+  // The binary identifier for this chapter.
+  uint64_t uid_;
+
+  // The Atom element can contain multiple Display sub-elements, as
+  // the same logical title can be rendered in different languages.
+  Display* displays_;
+
+  // The physical length (total size) of the |displays_| array.
+  int displays_size_;
+
+  // The logical length (number of active elements) on the |displays_|
+  // array.
+  int displays_count_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapter);
+};
+
+///////////////////////////////////////////////////////////////
+// Chapters element
+//
+class Chapters {
+ public:
+  Chapters();
+  ~Chapters();
+
+  Chapter* AddChapter(unsigned int* seed);
+
+  // Returns the number of chapters that have been added.
+  int Count() const;
+
+  // Output the Chapters element to the writer. Returns true on success.
+  bool Write(IMkvWriter* writer) const;
+
+ private:
+  // Expands the chapters_ array if there is not enough space to contain
+  // another chapter object.  Returns true on success.
+  bool ExpandChaptersArray();
+
+  // If |writer| is non-NULL, serialize the Edition sub-element of the
+  // Chapters element into the stream.  Returns the Edition element
+  // size on success, 0 if error.
+  uint64_t WriteEdition(IMkvWriter* writer) const;
+
+  // Total length of the chapters_ array.
+  int chapters_size_;
+
+  // Number of active chapters on the chapters_ array.
+  int chapters_count_;
+
+  // Array for storage of chapter objects.
+  Chapter* chapters_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapters);
+};
+
+///////////////////////////////////////////////////////////////
+// Tag element
+//
+class Tag {
+ public:
+  bool add_simple_tag(const char* tag_name, const char* tag_string);
+
+ private:
+  // Tags calls Clear and the destructor of Tag
+  friend class Tags;
+
+  // For storage of simple tags
+  class SimpleTag {
+   public:
+    // Establish representation invariant for new SimpleTag object.
+    void Init();
+
+    // Reclaim resources, in anticipation of destruction.
+    void Clear();
+
+    // Copies the title to the |tag_name_| member.  Returns false on
+    // error.
+    bool set_tag_name(const char* tag_name);
+
+    // Copies the language to the |tag_string_| member.  Returns false
+    // on error.
+    bool set_tag_string(const char* tag_string);
+
+    // If |writer| is non-NULL, serialize the SimpleTag sub-element of
+    // the Atom into the stream.  Returns the SimpleTag element size on
+    // success, 0 if error.
+    uint64_t Write(IMkvWriter* writer) const;
+
+   private:
+    char* tag_name_;
+    char* tag_string_;
+  };
+
+  Tag();
+  ~Tag();
+
+  // Copies this Tag object to a different one.  This is used when
+  // expanding a plain array of Tag objects (see Tags).
+  void ShallowCopy(Tag* dst) const;
+
+  // Reclaim resources used by this Tag object, pending its
+  // destruction.
+  void Clear();
+
+  // If there is no storage remaining on the |simple_tags_| array for a
+  // new display object, creates a new, longer array and copies the
+  // existing SimpleTag objects to the new array.  Returns false if the
+  // array cannot be expanded.
+  bool ExpandSimpleTagsArray();
+
+  // If |writer| is non-NULL, serialize the Tag sub-element into the
+  // stream.  Returns the total size of the element on success, 0 if
+  // error.
+  uint64_t Write(IMkvWriter* writer) const;
+
+  // The Atom element can contain multiple SimpleTag sub-elements
+  SimpleTag* simple_tags_;
+
+  // The physical length (total size) of the |simple_tags_| array.
+  int simple_tags_size_;
+
+  // The logical length (number of active elements) on the |simple_tags_|
+  // array.
+  int simple_tags_count_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tag);
+};
+
+///////////////////////////////////////////////////////////////
+// Tags element
+//
+class Tags {
+ public:
+  Tags();
+  ~Tags();
+
+  Tag* AddTag();
+
+  // Returns the number of tags that have been added.
+  int Count() const;
+
+  // Output the Tags element to the writer. Returns true on success.
+  bool Write(IMkvWriter* writer) const;
+
+ private:
+  // Expands the tags_ array if there is not enough space to contain
+  // another tag object.  Returns true on success.
+  bool ExpandTagsArray();
+
+  // Total length of the tags_ array.
+  int tags_size_;
+
+  // Number of active tags on the tags_ array.
+  int tags_count_;
+
+  // Array for storage of tag objects.
+  Tag* tags_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tags);
+};
+
+///////////////////////////////////////////////////////////////
+// Cluster element
+//
+// Notes:
+//  |Init| must be called before any other method in this class.
+class Cluster {
+ public:
+  // |timecode| is the absolute timecode of the cluster. |cues_pos| is the
+  // position for the cluster within the segment that should be written in
+  // the cues element. |timecode_scale| is the timecode scale of the segment.
+  Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale,
+          bool write_last_frame_with_duration = false,
+          bool fixed_size_timecode = false);
+  ~Cluster();
+
+  bool Init(IMkvWriter* ptr_writer);
+
+  // Adds a frame to be output in the file. The frame is written out through
+  // |writer_| if successful. Returns true on success.
+  bool AddFrame(const Frame* frame);
+
+  // Adds a frame to be output in the file. The frame is written out through
+  // |writer_| if successful. Returns true on success.
+  // Inputs:
+  //   data: Pointer to the data
+  //   length: Length of the data
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.  The range of allowed values is [1, 126].
+  //   timecode:     Absolute (not relative to cluster) timestamp of the
+  //                 frame, expressed in timecode units.
+  //   is_key:       Flag telling whether or not this frame is a key frame.
+  bool AddFrame(const uint8_t* data, uint64_t length, uint64_t track_number,
+                uint64_t timecode,  // timecode units (absolute)
+                bool is_key);
+
+  // Adds a frame to be output in the file. The frame is written out through
+  // |writer_| if successful. Returns true on success.
+  // Inputs:
+  //   data: Pointer to the data
+  //   length: Length of the data
+  //   additional: Pointer to the additional data
+  //   additional_length: Length of the additional data
+  //   add_id: Value of BlockAddID element
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.  The range of allowed values is [1, 126].
+  //   abs_timecode: Absolute (not relative to cluster) timestamp of the
+  //                 frame, expressed in timecode units.
+  //   is_key:       Flag telling whether or not this frame is a key frame.
+  bool AddFrameWithAdditional(const uint8_t* data, uint64_t length,
+                              const uint8_t* additional,
+                              uint64_t additional_length, uint64_t add_id,
+                              uint64_t track_number, uint64_t abs_timecode,
+                              bool is_key);
+
+  // Adds a frame to be output in the file. The frame is written out through
+  // |writer_| if successful. Returns true on success.
+  // Inputs:
+  //   data: Pointer to the data.
+  //   length: Length of the data.
+  //   discard_padding: DiscardPadding element value.
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.  The range of allowed values is [1, 126].
+  //   abs_timecode: Absolute (not relative to cluster) timestamp of the
+  //                 frame, expressed in timecode units.
+  //   is_key:       Flag telling whether or not this frame is a key frame.
+  bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
+                                  int64_t discard_padding,
+                                  uint64_t track_number, uint64_t abs_timecode,
+                                  bool is_key);
+
+  // Writes a frame of metadata to the output medium; returns true on
+  // success.
+  // Inputs:
+  //   data: Pointer to the data
+  //   length: Length of the data
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.  The range of allowed values is [1, 126].
+  //   timecode:     Absolute (not relative to cluster) timestamp of the
+  //                 metadata frame, expressed in timecode units.
+  //   duration:     Duration of metadata frame, in timecode units.
+  //
+  // The metadata frame is written as a block group, with a duration
+  // sub-element but no reference time sub-elements (indicating that
+  // it is considered a keyframe, per Matroska semantics).
+  bool AddMetadata(const uint8_t* data, uint64_t length, uint64_t track_number,
+                   uint64_t timecode, uint64_t duration);
+
+  // Increments the size of the cluster's data in bytes.
+  void AddPayloadSize(uint64_t size);
+
+  // Closes the cluster so no more data can be written to it. Will update the
+  // cluster's size if |writer_| is seekable. Returns true on success. This
+  // variant of Finalize() fails when |write_last_frame_with_duration_| is set
+  // to true.
+  bool Finalize();
+
+  // Closes the cluster so no more data can be written to it. Will update the
+  // cluster's size if |writer_| is seekable. Returns true on success.
+  // Inputs:
+  //   set_last_frame_duration: Boolean indicating whether or not the duration
+  //                            of the last frame should be set. If set to
+  //                            false, the |duration| value is ignored and
+  //                            |write_last_frame_with_duration_| will not be
+  //                            honored.
+  //   duration: Duration of the Cluster in timecode scale.
+  bool Finalize(bool set_last_frame_duration, uint64_t duration);
+
+  // Returns the size in bytes for the entire Cluster element.
+  uint64_t Size() const;
+
+  // Given |abs_timecode|, calculates timecode relative to most recent timecode.
+  // Returns -1 on failure, or a relative timecode.
+  int64_t GetRelativeTimecode(int64_t abs_timecode) const;
+
+  int64_t size_position() const { return size_position_; }
+  int32_t blocks_added() const { return blocks_added_; }
+  uint64_t payload_size() const { return payload_size_; }
+  int64_t position_for_cues() const { return position_for_cues_; }
+  uint64_t timecode() const { return timecode_; }
+  uint64_t timecode_scale() const { return timecode_scale_; }
+  void set_write_last_frame_with_duration(bool write_last_frame_with_duration) {
+    write_last_frame_with_duration_ = write_last_frame_with_duration;
+  }
+  bool write_last_frame_with_duration() const {
+    return write_last_frame_with_duration_;
+  }
+
+ private:
+  // Iterator type for the |stored_frames_| map.
+  typedef std::map<uint64_t, std::list<Frame*> >::iterator FrameMapIterator;
+
+  // Utility method that confirms that blocks can still be added, and that the
+  // cluster header has been written. Used by |DoWriteFrame*|. Returns true
+  // when successful.
+  bool PreWriteBlock();
+
+  // Utility method used by the |DoWriteFrame*| methods that handles the book
+  // keeping required after each block is written.
+  void PostWriteBlock(uint64_t element_size);
+
+  // Does some verification and calls WriteFrame.
+  bool DoWriteFrame(const Frame* const frame);
+
+  // Either holds back the given frame, or writes it out depending on whether or
+  // not |write_last_frame_with_duration_| is set.
+  bool QueueOrWriteFrame(const Frame* const frame);
+
+  // Outputs the Cluster header to |writer_|. Returns true on success.
+  bool WriteClusterHeader();
+
+  // Number of blocks added to the cluster.
+  int32_t blocks_added_;
+
+  // Flag telling if the cluster has been closed.
+  bool finalized_;
+
+  // Flag indicating whether the cluster's timecode will always be written out
+  // using 8 bytes.
+  bool fixed_size_timecode_;
+
+  // Flag telling if the cluster's header has been written.
+  bool header_written_;
+
+  // The size of the cluster elements in bytes.
+  uint64_t payload_size_;
+
+  // The file position used for cue points.
+  const int64_t position_for_cues_;
+
+  // The file position of the cluster's size element.
+  int64_t size_position_;
+
+  // The absolute timecode of the cluster.
+  const uint64_t timecode_;
+
+  // The timecode scale of the Segment containing the cluster.
+  const uint64_t timecode_scale_;
+
+  // Flag indicating whether the last frame of the cluster should be written as
+  // a Block with Duration. If set to true, then it will result in holding back
+  // of frames and the parameterized version of Finalize() must be called to
+  // finish writing the Cluster.
+  bool write_last_frame_with_duration_;
+
+  // Map used to hold back frames, if required. Track number is the key.
+  std::map<uint64_t, std::list<Frame*> > stored_frames_;
+
+  // Map from track number to the timestamp of the last block written for that
+  // track.
+  std::map<uint64_t, uint64_t> last_block_timestamp_;
+
+  // Pointer to the writer object. Not owned by this class.
+  IMkvWriter* writer_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cluster);
+};
+
+///////////////////////////////////////////////////////////////
+// SeekHead element
+class SeekHead {
+ public:
+  SeekHead();
+  ~SeekHead();
+
+  // TODO(fgalligan): Change this to reserve a certain size. Then check how
+  // big the seek entry to be added is as not every seek entry will be the
+  // maximum size it could be.
+  // Adds a seek entry to be written out when the element is finalized. |id|
+  // must be the coded mkv element id. |pos| is the file position of the
+  // element. Returns true on success.
+  bool AddSeekEntry(uint32_t id, uint64_t pos);
+
+  // Writes out SeekHead and SeekEntry elements. Returns true on success.
+  bool Finalize(IMkvWriter* writer) const;
+
+  // Returns the id of the Seek Entry at the given index. Returns -1 if index is
+  // out of range.
+  uint32_t GetId(int index) const;
+
+  // Returns the position of the Seek Entry at the given index. Returns -1 if
+  // index is out of range.
+  uint64_t GetPosition(int index) const;
+
+  // Sets the Seek Entry id and position at given index.
+  // Returns true on success.
+  bool SetSeekEntry(int index, uint32_t id, uint64_t position);
+
+  // Reserves space by writing out a Void element which will be updated with
+  // a SeekHead element later. Returns true on success.
+  bool Write(IMkvWriter* writer);
+
+  // We are going to put a cap on the number of Seek Entries.
+  const static int32_t kSeekEntryCount = 5;
+
+ private:
+  // Returns the maximum size in bytes of one seek entry.
+  uint64_t MaxEntrySize() const;
+
+  // Seek entry id element list.
+  uint32_t seek_entry_id_[kSeekEntryCount];
+
+  // Seek entry pos element list.
+  uint64_t seek_entry_pos_[kSeekEntryCount];
+
+  // The file position of SeekHead element.
+  int64_t start_pos_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SeekHead);
+};
+
+///////////////////////////////////////////////////////////////
+// Segment Information element
+class SegmentInfo {
+ public:
+  SegmentInfo();
+  ~SegmentInfo();
+
+  // Will update the duration if |duration_| is > 0.0. Returns true on success.
+  bool Finalize(IMkvWriter* writer) const;
+
+  // Sets |muxing_app_| and |writing_app_|.
+  bool Init();
+
+  // Output the Segment Information element to the writer. Returns true on
+  // success.
+  bool Write(IMkvWriter* writer);
+
+  void set_duration(double duration) { duration_ = duration; }
+  double duration() const { return duration_; }
+  void set_muxing_app(const char* app);
+  const char* muxing_app() const { return muxing_app_; }
+  void set_timecode_scale(uint64_t scale) { timecode_scale_ = scale; }
+  uint64_t timecode_scale() const { return timecode_scale_; }
+  void set_writing_app(const char* app);
+  const char* writing_app() const { return writing_app_; }
+  void set_date_utc(int64_t date_utc) { date_utc_ = date_utc; }
+  int64_t date_utc() const { return date_utc_; }
+
+ private:
+  // Segment Information element names.
+  // Initially set to -1 to signify that a duration has not been set and should
+  // not be written out.
+  double duration_;
+  // Set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
+  char* muxing_app_;
+  uint64_t timecode_scale_;
+  // Initially set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
+  char* writing_app_;
+  // LLONG_MIN when DateUTC is not set.
+  int64_t date_utc_;
+
+  // The file position of the duration element.
+  int64_t duration_pos_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SegmentInfo);
+};
+
+///////////////////////////////////////////////////////////////
+// This class represents the main segment in a WebM file. Currently only
+// supports one Segment element.
+//
+// Notes:
+//  |Init| must be called before any other method in this class.
+class Segment {
+ public:
+  enum Mode { kLive = 0x1, kFile = 0x2 };
+
+  enum CuesPosition {
+    kAfterClusters = 0x0,  // Position Cues after Clusters - Default
+    kBeforeClusters = 0x1  // Position Cues before Clusters
+  };
+
+  static const uint32_t kDefaultDocTypeVersion = 4;
+  static const uint64_t kDefaultMaxClusterDuration = 30000000000ULL;
+
+  Segment();
+  ~Segment();
+
+  // Initializes |SegmentInfo| and returns result. Always returns false when
+  // |ptr_writer| is NULL.
+  bool Init(IMkvWriter* ptr_writer);
+
+  // Adds a generic track to the segment.  Returns the newly-allocated
+  // track object (which is owned by the segment) on success, NULL on
+  // error. |number| is the number to use for the track.  |number|
+  // must be >= 0. If |number| == 0 then the muxer will decide on the
+  // track number.
+  Track* AddTrack(int32_t number);
+
+  // Adds a Vorbis audio track to the segment. Returns the number of the track
+  // on success, 0 on error. |number| is the number to use for the audio track.
+  // |number| must be >= 0. If |number| == 0 then the muxer will decide on
+  // the track number.
+  uint64_t AddAudioTrack(int32_t sample_rate, int32_t channels, int32_t number);
+
+  // Adds an empty chapter to the chapters of this segment.  Returns
+  // non-NULL on success.  After adding the chapter, the caller should
+  // populate its fields via the Chapter member functions.
+  Chapter* AddChapter();
+
+  // Adds an empty tag to the tags of this segment.  Returns
+  // non-NULL on success.  After adding the tag, the caller should
+  // populate its fields via the Tag member functions.
+  Tag* AddTag();
+
+  // Adds a cue point to the Cues element. |timestamp| is the time in
+  // nanoseconds of the cue's time. |track| is the Track of the Cue. This
+  // function must be called after AddFrame to calculate the correct
+  // BlockNumber for the CuePoint. Returns true on success.
+  bool AddCuePoint(uint64_t timestamp, uint64_t track);
+
+  // Adds a frame to be output in the file. Returns true on success.
+  // Inputs:
+  //   data: Pointer to the data
+  //   length: Length of the data
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.
+  //   timestamp:    Timestamp of the frame in nanoseconds from 0.
+  //   is_key:       Flag telling whether or not this frame is a key frame.
+  bool AddFrame(const uint8_t* data, uint64_t length, uint64_t track_number,
+                uint64_t timestamp_ns, bool is_key);
+
+  // Writes a frame of metadata to the output medium; returns true on
+  // success.
+  // Inputs:
+  //   data: Pointer to the data
+  //   length: Length of the data
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.
+  //   timecode:     Absolute timestamp of the metadata frame, expressed
+  //                 in nanosecond units.
+  //   duration:     Duration of metadata frame, in nanosecond units.
+  //
+  // The metadata frame is written as a block group, with a duration
+  // sub-element but no reference time sub-elements (indicating that
+  // it is considered a keyframe, per Matroska semantics).
+  bool AddMetadata(const uint8_t* data, uint64_t length, uint64_t track_number,
+                   uint64_t timestamp_ns, uint64_t duration_ns);
+
+  // Writes a frame with additional data to the output medium; returns true on
+  // success.
+  // Inputs:
+  //   data: Pointer to the data.
+  //   length: Length of the data.
+  //   additional: Pointer to additional data.
+  //   additional_length: Length of additional data.
+  //   add_id: Additional ID which identifies the type of additional data.
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.
+  //   timestamp:    Absolute timestamp of the frame, expressed in nanosecond
+  //                 units.
+  //   is_key:       Flag telling whether or not this frame is a key frame.
+  bool AddFrameWithAdditional(const uint8_t* data, uint64_t length,
+                              const uint8_t* additional,
+                              uint64_t additional_length, uint64_t add_id,
+                              uint64_t track_number, uint64_t timestamp,
+                              bool is_key);
+
+  // Writes a frame with DiscardPadding to the output medium; returns true on
+  // success.
+  // Inputs:
+  //   data: Pointer to the data.
+  //   length: Length of the data.
+  //   discard_padding: DiscardPadding element value.
+  //   track_number: Track to add the data to. Value returned by Add track
+  //                 functions.
+  //   timestamp:    Absolute timestamp of the frame, expressed in nanosecond
+  //                 units.
+  //   is_key:       Flag telling whether or not this frame is a key frame.
+  bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length,
+                                  int64_t discard_padding,
+                                  uint64_t track_number, uint64_t timestamp,
+                                  bool is_key);
+
+  // Writes a Frame to the output medium. Chooses the correct way of writing
+  // the frame (Block vs SimpleBlock) based on the parameters passed.
+  // Inputs:
+  //   frame: frame object
+  bool AddGenericFrame(const Frame* frame);
+
+  // Adds a VP8 video track to the segment. Returns the number of the track on
+  // success, 0 on error. |number| is the number to use for the video track.
+  // |number| must be >= 0. If |number| == 0 then the muxer will decide on
+  // the track number.
+  uint64_t AddVideoTrack(int32_t width, int32_t height, int32_t number);
+
+  // This function must be called after Finalize() if you need a copy of the
+  // output with Cues written before the Clusters. It will return false if the
+  // writer is not seekable of if chunking is set to true.
+  // Input parameters:
+  // reader - an IMkvReader object created with the same underlying file of the
+  //          current writer object. Make sure to close the existing writer
+  //          object before creating this so that all the data is properly
+  //          flushed and available for reading.
+  // writer - an IMkvWriter object pointing to a *different* file than the one
+  //          pointed by the current writer object. This file will contain the
+  //          Cues element before the Clusters.
+  bool CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
+                                     IMkvWriter* writer);
+
+  // Sets which track to use for the Cues element. Must have added the track
+  // before calling this function. Returns true on success. |track_number| is
+  // returned by the Add track functions.
+  bool CuesTrack(uint64_t track_number);
+
+  // This will force the muxer to create a new Cluster when the next frame is
+  // added.
+  void ForceNewClusterOnNextFrame();
+
+  // Writes out any frames that have not been written out. Finalizes the last
+  // cluster. May update the size and duration of the segment. May output the
+  // Cues element. May finalize the SeekHead element. Returns true on success.
+  bool Finalize();
+
+  // Returns the Cues object.
+  Cues* GetCues() { return &cues_; }
+
+  // Returns the Segment Information object.
+  const SegmentInfo* GetSegmentInfo() const { return &segment_info_; }
+  SegmentInfo* GetSegmentInfo() { return &segment_info_; }
+
+  // Search the Tracks and return the track that matches |track_number|.
+  // Returns NULL if there is no track match.
+  Track* GetTrackByNumber(uint64_t track_number) const;
+
+  // Toggles whether to output a cues element.
+  void OutputCues(bool output_cues);
+
+  // Toggles whether to write the last frame in each Cluster with Duration.
+  void AccurateClusterDuration(bool accurate_cluster_duration);
+
+  // Toggles whether to write the Cluster Timecode using exactly 8 bytes.
+  void UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode);
+
+  // Sets if the muxer will output files in chunks or not. |chunking| is a
+  // flag telling whether or not to turn on chunking. |filename| is the base
+  // filename for the chunk files. The header chunk file will be named
+  // |filename|.hdr and the data chunks will be named
+  // |filename|_XXXXXX.chk. Chunking implies that the muxer will be writing
+  // to files so the muxer will use the default MkvWriter class to control
+  // what data is written to what files. Returns true on success.
+  // TODO: Should we change the IMkvWriter Interface to add Open and Close?
+  // That will force the interface to be dependent on files.
+  bool SetChunking(bool chunking, const char* filename);
+
+  bool chunking() const { return chunking_; }
+  uint64_t cues_track() const { return cues_track_; }
+  void set_max_cluster_duration(uint64_t max_cluster_duration) {
+    max_cluster_duration_ = max_cluster_duration;
+  }
+  uint64_t max_cluster_duration() const { return max_cluster_duration_; }
+  void set_max_cluster_size(uint64_t max_cluster_size) {
+    max_cluster_size_ = max_cluster_size;
+  }
+  uint64_t max_cluster_size() const { return max_cluster_size_; }
+  void set_mode(Mode mode) { mode_ = mode; }
+  Mode mode() const { return mode_; }
+  CuesPosition cues_position() const { return cues_position_; }
+  bool output_cues() const { return output_cues_; }
+  void set_estimate_file_duration(bool estimate_duration) {
+    estimate_file_duration_ = estimate_duration;
+  }
+  bool estimate_file_duration() const { return estimate_file_duration_; }
+  const SegmentInfo* segment_info() const { return &segment_info_; }
+  void set_duration(double duration) { duration_ = duration; }
+  double duration() const { return duration_; }
+
+  // Returns true when codec IDs are valid for WebM.
+  bool DocTypeIsWebm() const;
+
+ private:
+  // Checks if header information has been output and initialized. If not it
+  // will output the Segment element and initialize the SeekHead elment and
+  // Cues elements.
+  bool CheckHeaderInfo();
+
+  // Sets |doc_type_version_| based on the current element requirements.
+  void UpdateDocTypeVersion();
+
+  // Sets |name| according to how many chunks have been written. |ext| is the
+  // file extension. |name| must be deleted by the calling app. Returns true
+  // on success.
+  bool UpdateChunkName(const char* ext, char** name) const;
+
+  // Returns the maximum offset within the segment's payload. When chunking
+  // this function is needed to determine offsets of elements within the
+  // chunked files. Returns -1 on error.
+  int64_t MaxOffset();
+
+  // Adds the frame to our frame array.
+  bool QueueFrame(Frame* frame);
+
+  // Output all frames that are queued. Returns -1 on error, otherwise
+  // it returns the number of frames written.
+  int WriteFramesAll();
+
+  // Output all frames that are queued that have an end time that is less
+  // then |timestamp|. Returns true on success and if there are no frames
+  // queued.
+  bool WriteFramesLessThan(uint64_t timestamp);
+
+  // Outputs the segment header, Segment Information element, SeekHead element,
+  // and Tracks element to |writer_|.
+  bool WriteSegmentHeader();
+
+  // Given a frame with the specified timestamp (nanosecond units) and
+  // keyframe status, determine whether a new cluster should be
+  // created, before writing enqueued frames and the frame itself. The
+  // function returns one of the following values:
+  //  -1 = error: an out-of-order frame was detected
+  //  0 = do not create a new cluster, and write frame to the existing cluster
+  //  1 = create a new cluster, and write frame to that new cluster
+  //  2 = create a new cluster, and re-run test
+  int TestFrame(uint64_t track_num, uint64_t timestamp_ns, bool key) const;
+
+  // Create a new cluster, using the earlier of the first enqueued
+  // frame, or the indicated time. Returns true on success.
+  bool MakeNewCluster(uint64_t timestamp_ns);
+
+  // Checks whether a new cluster needs to be created, and if so
+  // creates a new cluster. Returns false if creation of a new cluster
+  // was necessary but creation was not successful.
+  bool DoNewClusterProcessing(uint64_t track_num, uint64_t timestamp_ns,
+                              bool key);
+
+  // Adjusts Cue Point values (to place Cues before Clusters) so that they
+  // reflect the correct offsets.
+  void MoveCuesBeforeClusters();
+
+  // This function recursively computes the correct cluster offsets (this is
+  // done to move the Cues before Clusters). It recursively updates the change
+  // in size (which indicates a change in cluster offset) until no sizes change.
+  // Parameters:
+  // diff - indicates the difference in size of the Cues element that needs to
+  //        accounted for.
+  // index - index in the list of Cues which is currently being adjusted.
+  // cue_size - sum of size of all the CuePoint elements.
+  void MoveCuesBeforeClustersHelper(uint64_t diff, int index,
+                                    uint64_t* cue_size);
+
+  // Seeds the random number generator used to make UIDs.
+  unsigned int seed_;
+
+  // WebM elements
+  Cues cues_;
+  SeekHead seek_head_;
+  SegmentInfo segment_info_;
+  Tracks tracks_;
+  Chapters chapters_;
+  Tags tags_;
+
+  // Number of chunks written.
+  int chunk_count_;
+
+  // Current chunk filename.
+  char* chunk_name_;
+
+  // Default MkvWriter object created by this class used for writing clusters
+  // out in separate files.
+  MkvWriter* chunk_writer_cluster_;
+
+  // Default MkvWriter object created by this class used for writing Cues
+  // element out to a file.
+  MkvWriter* chunk_writer_cues_;
+
+  // Default MkvWriter object created by this class used for writing the
+  // Matroska header out to a file.
+  MkvWriter* chunk_writer_header_;
+
+  // Flag telling whether or not the muxer is chunking output to multiple
+  // files.
+  bool chunking_;
+
+  // Base filename for the chunked files.
+  char* chunking_base_name_;
+
+  // File position offset where the Clusters end.
+  int64_t cluster_end_offset_;
+
+  // List of clusters.
+  Cluster** cluster_list_;
+
+  // Number of cluster pointers allocated in the cluster list.
+  int32_t cluster_list_capacity_;
+
+  // Number of clusters in the cluster list.
+  int32_t cluster_list_size_;
+
+  // Indicates whether Cues should be written before or after Clusters
+  CuesPosition cues_position_;
+
+  // Track number that is associated with the cues element for this segment.
+  uint64_t cues_track_;
+
+  // Tells the muxer to force a new cluster on the next Block.
+  bool force_new_cluster_;
+
+  // List of stored audio frames. These variables are used to store frames so
+  // the muxer can follow the guideline "Audio blocks that contain the video
+  // key frame's timecode should be in the same cluster as the video key frame
+  // block."
+  Frame** frames_;
+
+  // Number of frame pointers allocated in the frame list.
+  int32_t frames_capacity_;
+
+  // Number of frames in the frame list.
+  int32_t frames_size_;
+
+  // Flag telling if a video track has been added to the segment.
+  bool has_video_;
+
+  // Flag telling if the segment's header has been written.
+  bool header_written_;
+
+  // Duration of the last block in nanoseconds.
+  uint64_t last_block_duration_;
+
+  // Last timestamp in nanoseconds added to a cluster.
+  uint64_t last_timestamp_;
+
+  // Last timestamp in nanoseconds by track number added to a cluster.
+  uint64_t last_track_timestamp_[kMaxTrackNumber];
+
+  // Number of frames written per track.
+  uint64_t track_frames_written_[kMaxTrackNumber];
+
+  // Maximum time in nanoseconds for a cluster duration. This variable is a
+  // guideline and some clusters may have a longer duration. Default is 30
+  // seconds.
+  uint64_t max_cluster_duration_;
+
+  // Maximum size in bytes for a cluster. This variable is a guideline and
+  // some clusters may have a larger size. Default is 0 which signifies that
+  // the muxer will decide the size.
+  uint64_t max_cluster_size_;
+
+  // The mode that segment is in. If set to |kLive| the writer must not
+  // seek backwards.
+  Mode mode_;
+
+  // Flag telling the muxer that a new cue point should be added.
+  bool new_cuepoint_;
+
+  // TODO(fgalligan): Should we add support for more than one Cues element?
+  // Flag whether or not the muxer should output a Cues element.
+  bool output_cues_;
+
+  // Flag whether or not the last frame in each Cluster will have a Duration
+  // element in it.
+  bool accurate_cluster_duration_;
+
+  // Flag whether or not to write the Cluster Timecode using exactly 8 bytes.
+  bool fixed_size_cluster_timecode_;
+
+  // Flag whether or not to estimate the file duration.
+  bool estimate_file_duration_;
+
+  // The size of the EBML header, used to validate the header if
+  // WriteEbmlHeader() is called more than once.
+  int32_t ebml_header_size_;
+
+  // The file position of the segment's payload.
+  int64_t payload_pos_;
+
+  // The file position of the element's size.
+  int64_t size_position_;
+
+  // Current DocTypeVersion (|doc_type_version_|) and that written in
+  // WriteSegmentHeader().
+  // WriteEbmlHeader() will be called from Finalize() if |doc_type_version_|
+  // differs from |doc_type_version_written_|.
+  uint32_t doc_type_version_;
+  uint32_t doc_type_version_written_;
+
+  // If |duration_| is > 0, then explicitly set the duration of the segment.
+  double duration_;
+
+  // Pointer to the writer objects. Not owned by this class.
+  IMkvWriter* writer_cluster_;
+  IMkvWriter* writer_cues_;
+  IMkvWriter* writer_header_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Segment);
+};
+
+}  // namespace mkvmuxer
+
+#endif  // MKVMUXER_MKVMUXER_H_
diff --git a/mkvmuxer/mkvmuxertypes.h b/mkvmuxer/mkvmuxertypes.h
new file mode 100644
index 0000000..e5db121
--- /dev/null
+++ b/mkvmuxer/mkvmuxertypes.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS.  All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef MKVMUXER_MKVMUXERTYPES_H_
+#define MKVMUXER_MKVMUXERTYPES_H_
+
+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
+// This should be used in the private: declarations for a class
+#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);                       \
+  void operator=(const TypeName&)
+
+#endif  // MKVMUXER_MKVMUXERTYPES_HPP_
diff --git a/mkvmuxer/mkvmuxerutil.cc b/mkvmuxer/mkvmuxerutil.cc
new file mode 100644
index 0000000..6436817
--- /dev/null
+++ b/mkvmuxer/mkvmuxerutil.cc
@@ -0,0 +1,743 @@
+// 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>
+#include <unistd.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 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(libwebm::kMkvBlockAdditional, frame->additional(),
+                        frame->additional_length());
+    block_addid_elem_size = EbmlElementSize(
+        libwebm::kMkvBlockAddID, static_cast<uint64>(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 discard_padding_elem_size = 0;
+  if (frame->discard_padding() != 0) {
+    discard_padding_elem_size =
+        EbmlElementSize(libwebm::kMkvDiscardPadding,
+                        static_cast<int64>(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(libwebm::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(libwebm::kMkvBlockDuration, duration);
+
+  const uint64 block_payload_size = 4 + frame->length();
+  const uint64 block_elem_size =
+      EbmlMasterElementSize(libwebm::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, 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>(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,
+                          static_cast<uint64>(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,
+                        static_cast<int64>(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 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame,
+                        int64 timecode) {
+  if (WriteID(writer, libwebm::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(libwebm::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) {
+  return EbmlElementSize(type, value, 0);
+}
+
+uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size) {
+  // Size of EBML ID
+  uint64 ebml_size = GetUIntSize(type);
+
+  // Datasize
+  ebml_size += (fixed_size > 0) ? fixed_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 += GetCodedUIntSize(strlen(value));
+
+  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) {
+  return WriteEbmlElement(writer, type, value, 0);
+}
+
+bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value,
+                      uint64 fixed_size) {
+  if (!writer)
+    return false;
+
+  if (WriteID(writer, type))
+    return false;
+
+  uint64 size = GetUIntSize(value);
+  if (fixed_size > 0) {
+    if (size > fixed_size)
+      return false;
+    size = fixed_size;
+  }
+  if (WriteUInt(writer, size))
+    return false;
+
+  if (SerializeInt(writer, value, static_cast<int32>(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<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(libwebm::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, libwebm::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;
+}
+
+uint64 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__
+    (void)seed;
+    int32 temp_num = 1;
+    int fd = open("/dev/urandom", O_RDONLY);
+    if (fd != -1) {
+      read(fd, &temp_num, sizeof(temp_num));
+      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;
+}
+
+bool IsMatrixCoefficientsValueValid(uint64_t value) {
+  switch (value) {
+    case mkvmuxer::Colour::kGbr:
+    case mkvmuxer::Colour::kBt709:
+    case mkvmuxer::Colour::kUnspecifiedMc:
+    case mkvmuxer::Colour::kReserved:
+    case mkvmuxer::Colour::kFcc:
+    case mkvmuxer::Colour::kBt470bg:
+    case mkvmuxer::Colour::kSmpte170MMc:
+    case mkvmuxer::Colour::kSmpte240MMc:
+    case mkvmuxer::Colour::kYcocg:
+    case mkvmuxer::Colour::kBt2020NonConstantLuminance:
+    case mkvmuxer::Colour::kBt2020ConstantLuminance:
+      return true;
+  }
+  return false;
+}
+
+bool IsChromaSitingHorzValueValid(uint64_t value) {
+  switch (value) {
+    case mkvmuxer::Colour::kUnspecifiedCsh:
+    case mkvmuxer::Colour::kLeftCollocated:
+    case mkvmuxer::Colour::kHalfCsh:
+      return true;
+  }
+  return false;
+}
+
+bool IsChromaSitingVertValueValid(uint64_t value) {
+  switch (value) {
+    case mkvmuxer::Colour::kUnspecifiedCsv:
+    case mkvmuxer::Colour::kTopCollocated:
+    case mkvmuxer::Colour::kHalfCsv:
+      return true;
+  }
+  return false;
+}
+
+bool IsColourRangeValueValid(uint64_t value) {
+  switch (value) {
+    case mkvmuxer::Colour::kUnspecifiedCr:
+    case mkvmuxer::Colour::kBroadcastRange:
+    case mkvmuxer::Colour::kFullRange:
+    case mkvmuxer::Colour::kMcTcDefined:
+      return true;
+  }
+  return false;
+}
+
+bool IsTransferCharacteristicsValueValid(uint64_t value) {
+  switch (value) {
+    case mkvmuxer::Colour::kIturBt709Tc:
+    case mkvmuxer::Colour::kUnspecifiedTc:
+    case mkvmuxer::Colour::kReservedTc:
+    case mkvmuxer::Colour::kGamma22Curve:
+    case mkvmuxer::Colour::kGamma28Curve:
+    case mkvmuxer::Colour::kSmpte170MTc:
+    case mkvmuxer::Colour::kSmpte240MTc:
+    case mkvmuxer::Colour::kLinear:
+    case mkvmuxer::Colour::kLog:
+    case mkvmuxer::Colour::kLogSqrt:
+    case mkvmuxer::Colour::kIec6196624:
+    case mkvmuxer::Colour::kIturBt1361ExtendedColourGamut:
+    case mkvmuxer::Colour::kIec6196621:
+    case mkvmuxer::Colour::kIturBt202010bit:
+    case mkvmuxer::Colour::kIturBt202012bit:
+    case mkvmuxer::Colour::kSmpteSt2084:
+    case mkvmuxer::Colour::kSmpteSt4281Tc:
+    case mkvmuxer::Colour::kAribStdB67Hlg:
+      return true;
+  }
+  return false;
+}
+
+bool IsPrimariesValueValid(uint64_t value) {
+  switch (value) {
+    case mkvmuxer::Colour::kReservedP0:
+    case mkvmuxer::Colour::kIturBt709P:
+    case mkvmuxer::Colour::kUnspecifiedP:
+    case mkvmuxer::Colour::kReservedP3:
+    case mkvmuxer::Colour::kIturBt470M:
+    case mkvmuxer::Colour::kIturBt470Bg:
+    case mkvmuxer::Colour::kSmpte170MP:
+    case mkvmuxer::Colour::kSmpte240MP:
+    case mkvmuxer::Colour::kFilm:
+    case mkvmuxer::Colour::kIturBt2020:
+    case mkvmuxer::Colour::kSmpteSt4281P:
+    case mkvmuxer::Colour::kJedecP22Phosphors:
+      return true;
+  }
+  return false;
+}
+
+}  // namespace mkvmuxer
diff --git a/mkvmuxer/mkvmuxerutil.h b/mkvmuxer/mkvmuxerutil.h
new file mode 100644
index 0000000..3355428
--- /dev/null
+++ b/mkvmuxer/mkvmuxerutil.h
@@ -0,0 +1,115 @@
+// 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 "mkvmuxertypes.h"
+
+#include "stdint.h"
+
+namespace mkvmuxer {
+class Cluster;
+class Frame;
+class IMkvWriter;
+
+// TODO(tomfinegan): mkvmuxer:: integer types continue to be used here because
+// changing them causes pain for downstream projects. It would be nice if a
+// solution that allows removal of the mkvmuxer:: integer types while avoiding
+// pain for downstream users of libwebm. Considering that mkvmuxerutil.{cc,h}
+// are really, for the great majority of cases, EBML size calculation and writer
+// functions, perhaps a more EBML focused utility would be the way to go as a
+// first step.
+
+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);
+
+// Writes out |f| in Big Endian order. Returns 0 on success.
+int32 SerializeFloat(IMkvWriter* writer, float f);
+
+// 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);
+
+// 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 EbmlElementSize(uint64 type, uint64 value, uint64 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 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 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 type, uint64 value,
+                      uint64 fixed_size);
+
+// 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);
+
+// Colour field validation helpers. All return true when |value| is valid.
+bool IsMatrixCoefficientsValueValid(uint64_t value);
+bool IsChromaSitingHorzValueValid(uint64_t value);
+bool IsChromaSitingVertValueValid(uint64_t value);
+bool IsColourRangeValueValid(uint64_t value);
+bool IsTransferCharacteristicsValueValid(uint64_t value);
+bool IsPrimariesValueValid(uint64_t value);
+
+}  // namespace mkvmuxer
+
+#endif  // MKVMUXER_MKVMUXERUTIL_H_
diff --git a/mkvmuxer/mkvwriter.cc b/mkvmuxer/mkvwriter.cc
new file mode 100644
index 0000000..d668384
--- /dev/null
+++ b/mkvmuxer/mkvwriter.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS.  All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#include "mkvmuxer/mkvwriter.h"
+
+#include <sys/types.h>
+
+#ifdef _MSC_VER
+#include <share.h>  // for _SH_DENYWR
+#endif
+
+namespace mkvmuxer {
+
+MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
+
+MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {}
+
+MkvWriter::~MkvWriter() { Close(); }
+
+int32 MkvWriter::Write(const void* buffer, uint32 length) {
+  if (!file_)
+    return -1;
+
+  if (length == 0)
+    return 0;
+
+  if (buffer == NULL)
+    return -1;
+
+  const size_t bytes_written = fwrite(buffer, 1, length, file_);
+
+  return (bytes_written == length) ? 0 : -1;
+}
+
+bool MkvWriter::Open(const char* filename) {
+  if (filename == NULL)
+    return false;
+
+  if (file_)
+    return false;
+
+#ifdef _MSC_VER
+  file_ = _fsopen(filename, "wb", _SH_DENYWR);
+#else
+  file_ = fopen(filename, "wb");
+#endif
+  if (file_ == NULL)
+    return false;
+  return true;
+}
+
+void MkvWriter::Close() {
+  if (file_ && writer_owns_file_) {
+    fclose(file_);
+  }
+  file_ = NULL;
+}
+
+int64 MkvWriter::Position() const {
+  if (!file_)
+    return 0;
+
+#ifdef _MSC_VER
+  return _ftelli64(file_);
+#else
+  return ftell(file_);
+#endif
+}
+
+int32 MkvWriter::Position(int64 position) {
+  if (!file_)
+    return -1;
+
+#ifdef _MSC_VER
+  return _fseeki64(file_, position, SEEK_SET);
+#elif defined(_WIN32)
+  return fseeko64(file_, static_cast<off_t>(position), SEEK_SET);
+#else
+  return fseeko(file_, static_cast<off_t>(position), SEEK_SET);
+#endif
+}
+
+bool MkvWriter::Seekable() const { return true; }
+
+void MkvWriter::ElementStartNotify(uint64, int64) {}
+
+}  // namespace mkvmuxer
diff --git a/mkvmuxer/mkvwriter.h b/mkvmuxer/mkvwriter.h
new file mode 100644
index 0000000..4227c63
--- /dev/null
+++ b/mkvmuxer/mkvwriter.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS.  All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef MKVMUXER_MKVWRITER_H_
+#define MKVMUXER_MKVWRITER_H_
+
+#include <stdio.h>
+
+#include "mkvmuxer/mkvmuxer.h"
+#include "mkvmuxer/mkvmuxertypes.h"
+
+namespace mkvmuxer {
+
+// Default implementation of the IMkvWriter interface on Windows.
+class MkvWriter : public IMkvWriter {
+ public:
+  MkvWriter();
+  explicit MkvWriter(FILE* fp);
+  virtual ~MkvWriter();
+
+  // IMkvWriter interface
+  virtual int64 Position() const;
+  virtual int32 Position(int64 position);
+  virtual bool Seekable() const;
+  virtual int32 Write(const void* buffer, uint32 length);
+  virtual void ElementStartNotify(uint64 element_id, int64 position);
+
+  // Creates and opens a file for writing. |filename| is the name of the file
+  // to open. This function will overwrite the contents of |filename|. Returns
+  // true on success.
+  bool Open(const char* filename);
+
+  // Closes an opened file.
+  void Close();
+
+ private:
+  // File handle to output file.
+  FILE* file_;
+  bool writer_owns_file_;
+
+  LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
+};
+
+}  // namespace mkvmuxer
+
+#endif  // MKVMUXER_MKVWRITER_H_
diff --git a/mkvmuxer_sample.cc b/mkvmuxer_sample.cc
new file mode 100644
index 0000000..9ef5569
--- /dev/null
+++ b/mkvmuxer_sample.cc
@@ -0,0 +1,802 @@
+// Copyright (c) 2011 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 <stdint.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <list>
+#include <memory>
+#include <string>
+
+// libwebm common includes.
+#include "common/file_util.h"
+#include "common/hdr_util.h"
+
+// libwebm mkvparser includes
+#include "mkvparser/mkvparser.h"
+#include "mkvparser/mkvreader.h"
+
+// libwebm mkvmuxer includes
+#include "mkvmuxer/mkvmuxer.h"
+#include "mkvmuxer/mkvmuxertypes.h"
+#include "mkvmuxer/mkvwriter.h"
+
+#include "sample_muxer_metadata.h"
+
+namespace {
+
+void Usage() {
+  printf("Usage: mkvmuxer_sample -i input -o output [options]\n");
+  printf("\n");
+  printf("Main options:\n");
+  printf("  -h | -?                     show help\n");
+  printf("  -video <int>                >0 outputs video\n");
+  printf("  -audio <int>                >0 outputs audio\n");
+  printf("  -live <int>                 >0 puts the muxer into live mode\n");
+  printf("                              0 puts the muxer into file mode\n");
+  printf("  -output_cues <int>          >0 outputs cues element\n");
+  printf("  -cues_on_video_track <int>  >0 outputs cues on video track\n");
+  printf("  -cues_on_audio_track <int>  >0 outputs cues on audio track\n");
+  printf("  -max_cluster_duration <double> in seconds\n");
+  printf("  -max_cluster_size <int>     in bytes\n");
+  printf("  -switch_tracks <int>        >0 switches tracks in output\n");
+  printf("  -audio_track_number <int>   >0 Changes the audio track number\n");
+  printf("  -video_track_number <int>   >0 Changes the video track number\n");
+  printf("  -chunking <string>          Chunk output\n");
+  printf("  -copy_tags <int>            >0 Copies the tags\n");
+  printf("  -accurate_cluster_duration <int> ");
+  printf(">0 Writes the last frame in each cluster with Duration\n");
+  printf("  -fixed_size_cluster_timecode <int> ");
+  printf(">0 Writes the cluster timecode using exactly 8 bytes\n");
+  printf("  -copy_input_duration        >0 Copies the input duration\n");
+  printf("\n");
+  printf("Video options:\n");
+  printf("  -display_width <int>           Display width in pixels\n");
+  printf("  -display_height <int>          Display height in pixels\n");
+  printf("  -pixel_width <int>             Override pixel width\n");
+  printf("  -pixel_height <int>            Override pixel height\n");
+  printf("  -projection_type <int>         Set/override projection type:\n");
+  printf("                                   0: Rectangular\n");
+  printf("                                   1: Equirectangular\n");
+  printf("                                   2: Cube map\n");
+  printf("                                   3: Mesh\n");
+  printf("  -projection_file <string>      Override projection private data");
+  printf("                                 with contents of this file\n");
+  printf("  -projection_pose_yaw <float>   Projection pose yaw\n");
+  printf("  -projection_pose_pitch <float> Projection pose pitch\n");
+  printf("  -projection_pose_roll <float>  Projection pose roll\n");
+  printf("  -stereo_mode <int>             3D video mode\n");
+  printf("\n");
+  printf("VP9 options:\n");
+  printf("  -profile <int>              VP9 profile\n");
+  printf("  -level <int>                VP9 level\n");
+  printf("\n");
+  printf("Cues options:\n");
+  printf("  -output_cues_block_number <int> >0 outputs cue block number\n");
+  printf("  -cues_before_clusters <int> >0 puts Cues before Clusters\n");
+  printf("\n");
+  printf("Metadata options:\n");
+  printf("  -webvtt-subtitles <vttfile>    ");
+  printf("add WebVTT subtitles as metadata track\n");
+  printf("  -webvtt-captions <vttfile>     ");
+  printf("add WebVTT captions as metadata track\n");
+  printf("  -webvtt-descriptions <vttfile> ");
+  printf("add WebVTT descriptions as metadata track\n");
+  printf("  -webvtt-metadata <vttfile>     ");
+  printf("add WebVTT subtitles as metadata track\n");
+  printf("  -webvtt-chapters <vttfile>     ");
+  printf("add WebVTT chapters as MKV chapters element\n");
+}
+
+struct MetadataFile {
+  const char* name;
+  SampleMuxerMetadata::Kind kind;
+};
+
+typedef std::list<MetadataFile> metadata_files_t;
+
+// Cache the WebVTT filenames specified as command-line args.
+bool LoadMetadataFiles(const metadata_files_t& files,
+                       SampleMuxerMetadata* metadata) {
+  typedef metadata_files_t::const_iterator iter_t;
+
+  iter_t i = files.begin();
+  const iter_t j = files.end();
+
+  while (i != j) {
+    const metadata_files_t::value_type& v = *i++;
+
+    if (!metadata->Load(v.name, v.kind))
+      return false;
+  }
+
+  return true;
+}
+
+int ParseArgWebVTT(char* argv[], int* argv_index, int argc_check,
+                   metadata_files_t* metadata_files) {
+  int& i = *argv_index;
+
+  enum { kCount = 5 };
+  struct Arg {
+    const char* name;
+    SampleMuxerMetadata::Kind kind;
+  };
+  const Arg args[kCount] = {
+      {"-webvtt-subtitles", SampleMuxerMetadata::kSubtitles},
+      {"-webvtt-captions", SampleMuxerMetadata::kCaptions},
+      {"-webvtt-descriptions", SampleMuxerMetadata::kDescriptions},
+      {"-webvtt-metadata", SampleMuxerMetadata::kMetadata},
+      {"-webvtt-chapters", SampleMuxerMetadata::kChapters}};
+
+  for (int idx = 0; idx < kCount; ++idx) {
+    const Arg& arg = args[idx];
+
+    if (strcmp(arg.name, argv[i]) != 0)  // no match
+      continue;
+
+    ++i;  // consume arg name here
+
+    if (i > argc_check) {
+      printf("missing value for %s\n", arg.name);
+      return -1;  // error
+    }
+
+    MetadataFile f;
+    f.name = argv[i];  // arg value is consumed via caller's loop idx
+    f.kind = arg.kind;
+
+    metadata_files->push_back(f);
+    return 1;  // successfully parsed WebVTT arg
+  }
+
+  return 0;  // not a WebVTT arg
+}
+
+bool CopyVideoProjection(const mkvparser::Projection& parser_projection,
+                         mkvmuxer::Projection* muxer_projection) {
+  typedef mkvmuxer::Projection::ProjectionType MuxerProjType;
+  const int kTypeNotPresent = mkvparser::Projection::kTypeNotPresent;
+  if (parser_projection.type != kTypeNotPresent) {
+    muxer_projection->set_type(
+        static_cast<MuxerProjType>(parser_projection.type));
+  }
+  if (parser_projection.private_data &&
+      parser_projection.private_data_length > 0) {
+    if (!muxer_projection->SetProjectionPrivate(
+            parser_projection.private_data,
+            parser_projection.private_data_length)) {
+      return false;
+    }
+  }
+
+  const float kValueNotPresent = mkvparser::Projection::kValueNotPresent;
+  if (parser_projection.pose_yaw != kValueNotPresent)
+    muxer_projection->set_pose_yaw(parser_projection.pose_yaw);
+  if (parser_projection.pose_pitch != kValueNotPresent)
+    muxer_projection->set_pose_pitch(parser_projection.pose_pitch);
+  if (parser_projection.pose_roll != kValueNotPresent)
+    muxer_projection->set_pose_roll(parser_projection.pose_roll);
+  return true;
+}
+}  // end namespace
+
+int main(int argc, char* argv[]) {
+  char* input = NULL;
+  char* output = NULL;
+
+  // Segment variables
+  bool output_video = true;
+  bool output_audio = true;
+  bool live_mode = false;
+  bool output_cues = true;
+  bool cues_before_clusters = false;
+  bool cues_on_video_track = true;
+  bool cues_on_audio_track = false;
+  uint64_t max_cluster_duration = 0;
+  uint64_t max_cluster_size = 0;
+  bool switch_tracks = false;
+  int audio_track_number = 0;  // 0 tells muxer to decide.
+  int video_track_number = 0;  // 0 tells muxer to decide.
+  bool chunking = false;
+  bool copy_tags = false;
+  const char* chunk_name = NULL;
+  bool accurate_cluster_duration = false;
+  bool fixed_size_cluster_timecode = false;
+  bool copy_input_duration = false;
+
+  bool output_cues_block_number = true;
+
+  uint64_t display_width = 0;
+  uint64_t display_height = 0;
+  uint64_t pixel_width = 0;
+  uint64_t pixel_height = 0;
+  uint64_t stereo_mode = 0;
+  const char* projection_file = 0;
+  int64_t projection_type = mkvparser::Projection::kTypeNotPresent;
+  float projection_pose_roll = mkvparser::Projection::kValueNotPresent;
+  float projection_pose_pitch = mkvparser::Projection::kValueNotPresent;
+  float projection_pose_yaw = mkvparser::Projection::kValueNotPresent;
+  int vp9_profile = -1;  // No profile set.
+  int vp9_level = -1;  // No level set.
+
+  metadata_files_t metadata_files;
+
+  const int argc_check = argc - 1;
+  for (int i = 1; i < argc; ++i) {
+    char* end;
+
+    if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) {
+      Usage();
+      return EXIT_SUCCESS;
+    } else if (!strcmp("-i", argv[i]) && i < argc_check) {
+      input = argv[++i];
+    } else if (!strcmp("-o", argv[i]) && i < argc_check) {
+      output = argv[++i];
+    } else if (!strcmp("-video", argv[i]) && i < argc_check) {
+      output_video = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-audio", argv[i]) && i < argc_check) {
+      output_audio = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-live", argv[i]) && i < argc_check) {
+      live_mode = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-output_cues", argv[i]) && i < argc_check) {
+      output_cues = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-cues_before_clusters", argv[i]) && i < argc_check) {
+      cues_before_clusters = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-cues_on_video_track", argv[i]) && i < argc_check) {
+      cues_on_video_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
+      if (cues_on_video_track)
+        cues_on_audio_track = false;
+    } else if (!strcmp("-cues_on_audio_track", argv[i]) && i < argc_check) {
+      cues_on_audio_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
+      if (cues_on_audio_track)
+        cues_on_video_track = false;
+    } else if (!strcmp("-max_cluster_duration", argv[i]) && i < argc_check) {
+      const double seconds = strtod(argv[++i], &end);
+      max_cluster_duration = static_cast<uint64_t>(seconds * 1000000000.0);
+    } else if (!strcmp("-max_cluster_size", argv[i]) && i < argc_check) {
+      max_cluster_size = strtol(argv[++i], &end, 10);
+    } else if (!strcmp("-switch_tracks", argv[i]) && i < argc_check) {
+      switch_tracks = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-audio_track_number", argv[i]) && i < argc_check) {
+      audio_track_number = static_cast<int>(strtol(argv[++i], &end, 10));
+    } else if (!strcmp("-video_track_number", argv[i]) && i < argc_check) {
+      video_track_number = static_cast<int>(strtol(argv[++i], &end, 10));
+    } else if (!strcmp("-chunking", argv[i]) && i < argc_check) {
+      chunking = true;
+      chunk_name = argv[++i];
+    } else if (!strcmp("-copy_tags", argv[i]) && i < argc_check) {
+      copy_tags = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-accurate_cluster_duration", argv[i]) &&
+               i < argc_check) {
+      accurate_cluster_duration =
+          strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-fixed_size_cluster_timecode", argv[i]) &&
+               i < argc_check) {
+      fixed_size_cluster_timecode =
+          strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-copy_input_duration", argv[i]) && i < argc_check) {
+      copy_input_duration = strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (!strcmp("-display_width", argv[i]) && i < argc_check) {
+      display_width = strtol(argv[++i], &end, 10);
+    } else if (!strcmp("-display_height", argv[i]) && i < argc_check) {
+      display_height = strtol(argv[++i], &end, 10);
+    } else if (!strcmp("-pixel_width", argv[i]) && i < argc_check) {
+      pixel_width = strtol(argv[++i], &end, 10);
+    } else if (!strcmp("-pixel_height", argv[i]) && i < argc_check) {
+      pixel_height = strtol(argv[++i], &end, 10);
+    } else if (!strcmp("-stereo_mode", argv[i]) && i < argc_check) {
+      stereo_mode = strtol(argv[++i], &end, 10);
+    } else if (!strcmp("-projection_type", argv[i]) && i < argc_check) {
+      projection_type = strtol(argv[++i], &end, 10);
+    } else if (!strcmp("-projection_file", argv[i]) && i < argc_check) {
+      projection_file = argv[++i];
+    } else if (!strcmp("-projection_pose_roll", argv[i]) && i < argc_check) {
+      projection_pose_roll = strtof(argv[++i], &end);
+    } else if (!strcmp("-projection_pose_pitch", argv[i]) && i < argc_check) {
+      projection_pose_pitch = strtof(argv[++i], &end);
+    } else if (!strcmp("-projection_pose_yaw", argv[i]) && i < argc_check) {
+      projection_pose_yaw = strtof(argv[++i], &end);
+    } else if (!strcmp("-profile", argv[i]) && i < argc_check) {
+      vp9_profile = static_cast<int>(strtol(argv[++i], &end, 10));
+    } else if (!strcmp("-level", argv[i]) && i < argc_check) {
+      vp9_level = static_cast<int>(strtol(argv[++i], &end, 10));
+    } else if (!strcmp("-output_cues_block_number", argv[i]) &&
+               i < argc_check) {
+      output_cues_block_number =
+          strtol(argv[++i], &end, 10) == 0 ? false : true;
+    } else if (int e = ParseArgWebVTT(argv, &i, argc_check, &metadata_files)) {
+      if (e < 0)
+        return EXIT_FAILURE;
+    }
+  }
+
+  if (input == NULL || output == NULL) {
+    Usage();
+    return EXIT_FAILURE;
+  }
+
+  // Get parser header info
+  mkvparser::MkvReader reader;
+
+  if (reader.Open(input)) {
+    printf("\n Filename is invalid or error while opening.\n");
+    return EXIT_FAILURE;
+  }
+
+  long long pos = 0;
+  mkvparser::EBMLHeader ebml_header;
+  long long ret = ebml_header.Parse(&reader, pos);
+  if (ret) {
+    printf("\n EBMLHeader::Parse() failed.");
+    return EXIT_FAILURE;
+  }
+
+  mkvparser::Segment* parser_segment_;
+  ret = mkvparser::Segment::CreateInstance(&reader, pos, parser_segment_);
+  if (ret) {
+    printf("\n Segment::CreateInstance() failed.");
+    return EXIT_FAILURE;
+  }
+
+  const std::unique_ptr<mkvparser::Segment> parser_segment(parser_segment_);
+  ret = parser_segment->Load();
+  if (ret < 0) {
+    printf("\n Segment::Load() failed.");
+    return EXIT_FAILURE;
+  }
+
+  const mkvparser::SegmentInfo* const segment_info = parser_segment->GetInfo();
+  if (segment_info == NULL) {
+    printf("\n Segment::GetInfo() failed.");
+    return EXIT_FAILURE;
+  }
+  const long long timeCodeScale = segment_info->GetTimeCodeScale();
+
+  // Set muxer header info
+  mkvmuxer::MkvWriter writer;
+
+  const std::string temp_file =
+      cues_before_clusters ? libwebm::GetTempFileName() : output;
+  if (!writer.Open(temp_file.c_str())) {
+    printf("\n Filename is invalid or error while opening.\n");
+    return EXIT_FAILURE;
+  }
+
+  // Set Segment element attributes
+  mkvmuxer::Segment muxer_segment;
+
+  if (!muxer_segment.Init(&writer)) {
+    printf("\n Could not initialize muxer segment!\n");
+    return EXIT_FAILURE;
+  }
+
+  muxer_segment.AccurateClusterDuration(accurate_cluster_duration);
+  muxer_segment.UseFixedSizeClusterTimecode(fixed_size_cluster_timecode);
+
+  if (live_mode)
+    muxer_segment.set_mode(mkvmuxer::Segment::kLive);
+  else
+    muxer_segment.set_mode(mkvmuxer::Segment::kFile);
+
+  if (chunking)
+    muxer_segment.SetChunking(true, chunk_name);
+
+  if (max_cluster_duration > 0)
+    muxer_segment.set_max_cluster_duration(max_cluster_duration);
+  if (max_cluster_size > 0)
+    muxer_segment.set_max_cluster_size(max_cluster_size);
+  muxer_segment.OutputCues(output_cues);
+
+  // Set SegmentInfo element attributes
+  mkvmuxer::SegmentInfo* const info = muxer_segment.GetSegmentInfo();
+  info->set_timecode_scale(timeCodeScale);
+  info->set_writing_app("mkvmuxer_sample");
+
+  const mkvparser::Tags* const tags = parser_segment->GetTags();
+  if (copy_tags && tags) {
+    for (int i = 0; i < tags->GetTagCount(); i++) {
+      const mkvparser::Tags::Tag* const tag = tags->GetTag(i);
+      mkvmuxer::Tag* muxer_tag = muxer_segment.AddTag();
+
+      for (int j = 0; j < tag->GetSimpleTagCount(); j++) {
+        const mkvparser::Tags::SimpleTag* const simple_tag =
+            tag->GetSimpleTag(j);
+        muxer_tag->add_simple_tag(simple_tag->GetTagName(),
+                                  simple_tag->GetTagString());
+      }
+    }
+  }
+
+  // Set Tracks element attributes
+  const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks();
+  unsigned long i = 0;
+  uint64_t vid_track = 0;  // no track added
+  uint64_t aud_track = 0;  // no track added
+
+  using mkvparser::Track;
+
+  while (i != parser_tracks->GetTracksCount()) {
+    unsigned long track_num = i++;
+    if (switch_tracks)
+      track_num = i % parser_tracks->GetTracksCount();
+
+    const Track* const parser_track = parser_tracks->GetTrackByIndex(track_num);
+
+    if (parser_track == NULL)
+      continue;
+
+    // TODO(fgalligan): Add support for language to parser.
+    const char* const track_name = parser_track->GetNameAsUTF8();
+
+    const long long track_type = parser_track->GetType();
+
+    if (track_type == Track::kVideo && output_video) {
+      // Get the video track from the parser
+      const mkvparser::VideoTrack* const pVideoTrack =
+          static_cast<const mkvparser::VideoTrack*>(parser_track);
+      const long long width = pVideoTrack->GetWidth();
+      const long long height = pVideoTrack->GetHeight();
+
+      // Add the video track to the muxer
+      vid_track = muxer_segment.AddVideoTrack(static_cast<int>(width),
+                                              static_cast<int>(height),
+                                              video_track_number);
+      if (!vid_track) {
+        printf("\n Could not add video track.\n");
+        return EXIT_FAILURE;
+      }
+
+      mkvmuxer::VideoTrack* const video = static_cast<mkvmuxer::VideoTrack*>(
+          muxer_segment.GetTrackByNumber(vid_track));
+      if (!video) {
+        printf("\n Could not get video track.\n");
+        return EXIT_FAILURE;
+      }
+
+      if (pVideoTrack->GetColour()) {
+        mkvmuxer::Colour muxer_colour;
+        if (!libwebm::CopyColour(*pVideoTrack->GetColour(), &muxer_colour))
+          return EXIT_FAILURE;
+        if (!video->SetColour(muxer_colour))
+          return EXIT_FAILURE;
+      }
+
+      if (pVideoTrack->GetProjection() ||
+          projection_type != mkvparser::Projection::kTypeNotPresent) {
+        mkvmuxer::Projection muxer_projection;
+        const mkvparser::Projection* const parser_projection =
+            pVideoTrack->GetProjection();
+        typedef mkvmuxer::Projection::ProjectionType MuxerProjType;
+        if (parser_projection &&
+            !CopyVideoProjection(*parser_projection, &muxer_projection)) {
+          printf("\n Unable to copy video projection.\n");
+          return EXIT_FAILURE;
+        }
+        // Override the values that came from parser if set on command line.
+        if (projection_type != mkvparser::Projection::kTypeNotPresent) {
+          muxer_projection.set_type(
+              static_cast<MuxerProjType>(projection_type));
+          if (projection_type == mkvparser::Projection::kRectangular &&
+              projection_file != NULL) {
+            printf("\n Rectangular projection must not have private data.\n");
+            return EXIT_FAILURE;
+          } else if ((projection_type == mkvparser::Projection::kCubeMap ||
+                      projection_type == mkvparser::Projection::kMesh) &&
+                     projection_file == NULL) {
+            printf("\n Mesh or CubeMap projection must have private data.\n");
+            return EXIT_FAILURE;
+          }
+          if (projection_file != NULL) {
+            std::string contents;
+            if (!libwebm::GetFileContents(projection_file, &contents) ||
+                contents.size() == 0) {
+              printf("\n Failed to read file \"%s\" or file is empty\n",
+                     projection_file);
+              return EXIT_FAILURE;
+            }
+            if (!muxer_projection.SetProjectionPrivate(
+                    reinterpret_cast<uint8_t*>(&contents[0]),
+                    contents.size())) {
+              printf("\n Failed to SetProjectionPrivate of length %zu.\n",
+                     contents.size());
+              return EXIT_FAILURE;
+            }
+          }
+        }
+        const float kValueNotPresent = mkvparser::Projection::kValueNotPresent;
+        if (projection_pose_yaw != kValueNotPresent)
+          muxer_projection.set_pose_yaw(projection_pose_yaw);
+        if (projection_pose_pitch != kValueNotPresent)
+          muxer_projection.set_pose_pitch(projection_pose_pitch);
+        if (projection_pose_roll != kValueNotPresent)
+          muxer_projection.set_pose_roll(projection_pose_roll);
+
+        if (!video->SetProjection(muxer_projection))
+          return EXIT_FAILURE;
+      }
+
+      if (track_name)
+        video->set_name(track_name);
+
+      video->set_codec_id(pVideoTrack->GetCodecId());
+
+      if (display_width > 0)
+        video->set_display_width(display_width);
+      if (display_height > 0)
+        video->set_display_height(display_height);
+      if (pixel_width > 0)
+        video->set_pixel_width(pixel_width);
+      if (pixel_height > 0)
+        video->set_pixel_height(pixel_height);
+      if (stereo_mode > 0)
+        video->SetStereoMode(stereo_mode);
+
+      const double rate = pVideoTrack->GetFrameRate();
+      if (rate > 0.0) {
+        video->set_frame_rate(rate);
+      }
+
+      size_t parser_private_size;
+      const unsigned char* const parser_private_data =
+          pVideoTrack->GetCodecPrivate(parser_private_size);
+
+      if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kAv1CodecId)) {
+        if (parser_private_data == NULL || parser_private_size == 0) {
+          printf("AV1 input track has no CodecPrivate. %s is invalid.", input);
+          return EXIT_FAILURE;
+        }
+      }
+
+      if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kVp9CodecId) &&
+          (vp9_profile >= 0 || vp9_level >= 0)) {
+        const int kMaxVp9PrivateSize = 6;
+        unsigned char vp9_private_data[kMaxVp9PrivateSize];
+        int vp9_private_size = 0;
+        if (vp9_profile >= 0) {
+          if (vp9_profile < 0 || vp9_profile > 3) {
+            printf("\n VP9 profile(%d) is not valid.\n", vp9_profile);
+            return EXIT_FAILURE;
+          }
+          const uint8_t kVp9ProfileId = 1;
+          const uint8_t kVp9ProfileIdLength = 1;
+          vp9_private_data[vp9_private_size++] = kVp9ProfileId;
+          vp9_private_data[vp9_private_size++] = kVp9ProfileIdLength;
+          vp9_private_data[vp9_private_size++] = vp9_profile;
+        }
+
+        if (vp9_level >= 0) {
+          const int kNumLevels = 14;
+          const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40,
+                                          41, 50, 51, 52, 60, 61, 62};
+          bool level_is_valid = false;
+          for (int i = 0; i < kNumLevels; ++i) {
+            if (vp9_level == levels[i]) {
+              level_is_valid = true;
+              break;
+            }
+          }
+          if (!level_is_valid) {
+            printf("\n VP9 level(%d) is not valid.\n", vp9_level);
+            return EXIT_FAILURE;
+          }
+          const uint8_t kVp9LevelId = 2;
+          const uint8_t kVp9LevelIdLength = 1;
+          vp9_private_data[vp9_private_size++] = kVp9LevelId;
+          vp9_private_data[vp9_private_size++] = kVp9LevelIdLength;
+          vp9_private_data[vp9_private_size++] = vp9_level;
+        }
+        if (!video->SetCodecPrivate(vp9_private_data, vp9_private_size)) {
+          printf("\n Could not add video private data.\n");
+          return EXIT_FAILURE;
+        }
+      } else if (parser_private_data && parser_private_size > 0) {
+        if (!video->SetCodecPrivate(parser_private_data, parser_private_size)) {
+          printf("\n Could not add video private data.\n");
+          return EXIT_FAILURE;
+        }
+      }
+    } else if (track_type == Track::kAudio && output_audio) {
+      // Get the audio track from the parser
+      const mkvparser::AudioTrack* const pAudioTrack =
+          static_cast<const mkvparser::AudioTrack*>(parser_track);
+      const long long channels = pAudioTrack->GetChannels();
+      const double sample_rate = pAudioTrack->GetSamplingRate();
+
+      // Add the audio track to the muxer
+      aud_track = muxer_segment.AddAudioTrack(static_cast<int>(sample_rate),
+                                              static_cast<int>(channels),
+                                              audio_track_number);
+      if (!aud_track) {
+        printf("\n Could not add audio track.\n");
+        return EXIT_FAILURE;
+      }
+
+      mkvmuxer::AudioTrack* const audio = static_cast<mkvmuxer::AudioTrack*>(
+          muxer_segment.GetTrackByNumber(aud_track));
+      if (!audio) {
+        printf("\n Could not get audio track.\n");
+        return EXIT_FAILURE;
+      }
+
+      if (track_name)
+        audio->set_name(track_name);
+
+      audio->set_codec_id(pAudioTrack->GetCodecId());
+
+      size_t private_size;
+      const unsigned char* const private_data =
+          pAudioTrack->GetCodecPrivate(private_size);
+      if (private_size > 0) {
+        if (!audio->SetCodecPrivate(private_data, private_size)) {
+          printf("\n Could not add audio private data.\n");
+          return EXIT_FAILURE;
+        }
+      }
+
+      const long long bit_depth = pAudioTrack->GetBitDepth();
+      if (bit_depth > 0)
+        audio->set_bit_depth(bit_depth);
+
+      if (pAudioTrack->GetCodecDelay())
+        audio->set_codec_delay(pAudioTrack->GetCodecDelay());
+      if (pAudioTrack->GetSeekPreRoll())
+        audio->set_seek_pre_roll(pAudioTrack->GetSeekPreRoll());
+    }
+  }
+
+  // We have created all the video and audio tracks.  If any WebVTT
+  // files were specified as command-line args, then parse them and
+  // add a track to the output file corresponding to each metadata
+  // input file.
+
+  SampleMuxerMetadata metadata;
+
+  if (!metadata.Init(&muxer_segment)) {
+    printf("\n Could not initialize metadata cache.\n");
+    return EXIT_FAILURE;
+  }
+
+  if (!LoadMetadataFiles(metadata_files, &metadata))
+    return EXIT_FAILURE;
+
+  if (!metadata.AddChapters())
+    return EXIT_FAILURE;
+
+  // Set Cues element attributes
+  mkvmuxer::Cues* const cues = muxer_segment.GetCues();
+  cues->set_output_block_number(output_cues_block_number);
+  if (cues_on_video_track && vid_track)
+    muxer_segment.CuesTrack(vid_track);
+  if (cues_on_audio_track && aud_track)
+    muxer_segment.CuesTrack(aud_track);
+
+  // Write clusters
+  unsigned char* data = NULL;
+  long data_len = 0;
+
+  const mkvparser::Cluster* cluster = parser_segment->GetFirst();
+
+  while (cluster != NULL && !cluster->EOS()) {
+    const mkvparser::BlockEntry* block_entry;
+
+    long status = cluster->GetFirst(block_entry);
+
+    if (status) {
+      printf("\n Could not get first block of cluster.\n");
+      return EXIT_FAILURE;
+    }
+
+    while (block_entry != NULL && !block_entry->EOS()) {
+      const mkvparser::Block* const block = block_entry->GetBlock();
+      const long long trackNum = block->GetTrackNumber();
+      const mkvparser::Track* const parser_track =
+          parser_tracks->GetTrackByNumber(static_cast<unsigned long>(trackNum));
+
+      // When |parser_track| is NULL, it means that the track number in the
+      // Block is invalid (i.e.) the was no TrackEntry corresponding to the
+      // track number. So we reject the file.
+      if (!parser_track) {
+        return EXIT_FAILURE;
+      }
+
+      const long long track_type = parser_track->GetType();
+      const long long time_ns = block->GetTime(cluster);
+
+      // Flush any metadata frames to the output file, before we write
+      // the current block.
+      if (!metadata.Write(time_ns))
+        return EXIT_FAILURE;
+
+      if ((track_type == Track::kAudio && output_audio) ||
+          (track_type == Track::kVideo && output_video)) {
+        const int frame_count = block->GetFrameCount();
+
+        for (int i = 0; i < frame_count; ++i) {
+          const mkvparser::Block::Frame& frame = block->GetFrame(i);
+
+          if (frame.len > data_len) {
+            delete[] data;
+            data = new unsigned char[frame.len];
+            if (!data)
+              return EXIT_FAILURE;
+            data_len = frame.len;
+          }
+
+          if (frame.Read(&reader, data))
+            return EXIT_FAILURE;
+
+          mkvmuxer::Frame muxer_frame;
+          if (!muxer_frame.Init(data, frame.len))
+            return EXIT_FAILURE;
+          muxer_frame.set_track_number(track_type == Track::kAudio ? aud_track
+                                                                   : vid_track);
+          if (block->GetDiscardPadding())
+            muxer_frame.set_discard_padding(block->GetDiscardPadding());
+          muxer_frame.set_timestamp(time_ns);
+          muxer_frame.set_is_key(block->IsKey());
+          if (!muxer_segment.AddGenericFrame(&muxer_frame)) {
+            printf("\n Could not add frame.\n");
+            return EXIT_FAILURE;
+          }
+        }
+      }
+
+      status = cluster->GetNext(block_entry, block_entry);
+
+      if (status) {
+        printf("\n Could not get next block of cluster.\n");
+        return EXIT_FAILURE;
+      }
+    }
+
+    cluster = parser_segment->GetNext(cluster);
+  }
+
+  // We have exhausted all video and audio frames in the input file.
+  // Flush any remaining metadata frames to the output file.
+  if (!metadata.Write(-1))
+    return EXIT_FAILURE;
+
+  if (copy_input_duration) {
+    const double input_duration =
+        static_cast<double>(segment_info->GetDuration()) / timeCodeScale;
+    muxer_segment.set_duration(input_duration);
+  }
+
+  if (!muxer_segment.Finalize()) {
+    printf("Finalization of segment failed.\n");
+    return EXIT_FAILURE;
+  }
+
+  reader.Close();
+  writer.Close();
+
+  if (cues_before_clusters) {
+    if (reader.Open(temp_file.c_str())) {
+      printf("\n Filename is invalid or error while opening.\n");
+      return EXIT_FAILURE;
+    }
+    if (!writer.Open(output)) {
+      printf("\n Filename is invalid or error while opening.\n");
+      return EXIT_FAILURE;
+    }
+    if (!muxer_segment.CopyAndMoveCuesBeforeClusters(&reader, &writer)) {
+      printf("\n Unable to copy and move cues before clusters.\n");
+      return EXIT_FAILURE;
+    }
+    reader.Close();
+    writer.Close();
+    remove(temp_file.c_str());
+  }
+
+  delete[] data;
+
+  return EXIT_SUCCESS;
+}
diff --git a/mkvmuxertypes.hpp b/mkvmuxertypes.hpp
new file mode 100644
index 0000000..78478f4
--- /dev/null
+++ b/mkvmuxertypes.hpp
@@ -0,0 +1,15 @@
+// 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_MKVMUXERTYPES_HPP_
+#define LIBWEBM_MKVMUXERTYPES_HPP_
+
+// This file is a wrapper for the file included immediately after this comment.
+// New projects should not include this file: include the file included below.
+#include "mkvmuxer/mkvmuxertypes.h"
+
+#endif  // LIBWEBM_MKVMUXERTYPES_HPP_
diff --git a/mkvmuxerutil.hpp b/mkvmuxerutil.hpp
new file mode 100644
index 0000000..a26ba18
--- /dev/null
+++ b/mkvmuxerutil.hpp
@@ -0,0 +1,18 @@
+// 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_MKVMUXERUTIL_HPP_
+#define LIBWEBM_MKVMUXERUTIL_HPP_
+
+// This file is a wrapper for the file included immediately after this comment.
+// New projects should not include this file: include the file included below.
+#include "mkvmuxer/mkvmuxerutil.h"
+
+using mkvmuxer::EbmlElementSize;
+using mkvmuxer::EbmlMasterElementSize;
+
+#endif  // LIBWEBM_MKVMUXERUTIL_HPP_
diff --git a/mkvparser.hpp b/mkvparser.hpp
new file mode 100644
index 0000000..3f86292
--- /dev/null
+++ b/mkvparser.hpp
@@ -0,0 +1,15 @@
+// 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_MKVPARSER_HPP_
+#define LIBWEBM_MKVPARSER_HPP_
+
+// This file is a wrapper for the file included immediately after this comment.
+// New projects should not include this file: include the file included below.
+#include "mkvparser/mkvparser.h"
+
+#endif  // LIBWEBM_MKVPARSER_HPP_
diff --git a/mkvparser/mkvparser.cc b/mkvparser/mkvparser.cc
new file mode 100644
index 0000000..412e6a5
--- /dev/null
+++ b/mkvparser/mkvparser.cc
@@ -0,0 +1,8076 @@
+// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS.  All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+#include "mkvparser/mkvparser.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#include <float.h>  // _isnan() / _finite()
+#define MSC_COMPAT
+#endif
+
+#include <cassert>
+#include <cfloat>
+#include <climits>
+#include <cmath>
+#include <cstring>
+#include <memory>
+#include <new>
+
+#include "common/webmids.h"
+
+namespace mkvparser {
+const long long kStringElementSizeLimit = 20 * 1000 * 1000;
+const float MasteringMetadata::kValueNotPresent = FLT_MAX;
+const long long Colour::kValueNotPresent = LLONG_MAX;
+const float Projection::kValueNotPresent = FLT_MAX;
+
+#ifdef MSC_COMPAT
+inline bool isnan(double val) { return !!_isnan(val); }
+inline bool isinf(double val) { return !_finite(val); }
+#else
+inline bool isnan(double val) { return std::isnan(val); }
+inline bool isinf(double val) { return std::isinf(val); }
+#endif  // MSC_COMPAT
+
+template <typename Type>
+Type* SafeArrayAlloc(unsigned long long num_elements,
+                     unsigned long long element_size) {
+  if (num_elements == 0 || element_size == 0)
+    return NULL;
+
+  const size_t kMaxAllocSize = 0x80000000;  // 2GiB
+  const unsigned long long num_bytes = num_elements * element_size;
+  if (element_size > (kMaxAllocSize / num_elements))
+    return NULL;
+  if (num_bytes != static_cast<size_t>(num_bytes))
+    return NULL;
+
+  return new (std::nothrow) Type[static_cast<size_t>(num_bytes)];
+}
+
+void GetVersion(int& major, int& minor, int& build, int& revision) {
+  major = 1;
+  minor = 0;
+  build = 0;
+  revision = 30;
+}
+
+long long ReadUInt(IMkvReader* pReader, long long pos, long& len) {
+  if (!pReader || pos < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  len = 1;
+  unsigned char b;
+  int status = pReader->Read(pos, 1, &b);
+
+  if (status < 0)  // error or underflow
+    return status;
+
+  if (status > 0)  // interpreted as "underflow"
+    return E_BUFFER_NOT_FULL;
+
+  if (b == 0)  // we can't handle u-int values larger than 8 bytes
+    return E_FILE_FORMAT_INVALID;
+
+  unsigned char m = 0x80;
+
+  while (!(b & m)) {
+    m >>= 1;
+    ++len;
+  }
+
+  long long result = b & (~m);
+  ++pos;
+
+  for (int i = 1; i < len; ++i) {
+    status = pReader->Read(pos, 1, &b);
+
+    if (status < 0) {
+      len = 1;
+      return status;
+    }
+
+    if (status > 0) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result <<= 8;
+    result |= b;
+
+    ++pos;
+  }
+
+  return result;
+}
+
+// Reads an EBML ID and returns it.
+// An ID must at least 1 byte long, cannot exceed 4, and its value must be
+// greater than 0.
+// See known EBML values and EBMLMaxIDLength:
+// http://www.matroska.org/technical/specs/index.html
+// Returns the ID, or a value less than 0 to report an error while reading the
+// ID.
+long long ReadID(IMkvReader* pReader, long long pos, long& len) {
+  if (pReader == NULL || pos < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  // Read the first byte. The length in bytes of the ID is determined by
+  // finding the first set bit in the first byte of the ID.
+  unsigned char temp_byte = 0;
+  int read_status = pReader->Read(pos, 1, &temp_byte);
+
+  if (read_status < 0)
+    return E_FILE_FORMAT_INVALID;
+  else if (read_status > 0)  // No data to read.
+    return E_BUFFER_NOT_FULL;
+
+  if (temp_byte == 0)  // ID length > 8 bytes; invalid file.
+    return E_FILE_FORMAT_INVALID;
+
+  int bit_pos = 0;
+  const int kMaxIdLengthInBytes = 4;
+  const int kCheckByte = 0x80;
+
+  // Find the first bit that's set.
+  bool found_bit = false;
+  for (; bit_pos < kMaxIdLengthInBytes; ++bit_pos) {
+    if ((kCheckByte >> bit_pos) & temp_byte) {
+      found_bit = true;
+      break;
+    }
+  }
+
+  if (!found_bit) {
+    // The value is too large to be a valid ID.
+    return E_FILE_FORMAT_INVALID;
+  }
+
+  // Read the remaining bytes of the ID (if any).
+  const int id_length = bit_pos + 1;
+  long long ebml_id = temp_byte;
+  for (int i = 1; i < id_length; ++i) {
+    ebml_id <<= 8;
+    read_status = pReader->Read(pos + i, 1, &temp_byte);
+
+    if (read_status < 0)
+      return E_FILE_FORMAT_INVALID;
+    else if (read_status > 0)
+      return E_BUFFER_NOT_FULL;
+
+    ebml_id |= temp_byte;
+  }
+
+  len = id_length;
+  return ebml_id;
+}
+
+long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) {
+  if (!pReader || pos < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  long long total, available;
+
+  int status = pReader->Length(&total, &available);
+  if (status < 0 || (total >= 0 && available > total))
+    return E_FILE_FORMAT_INVALID;
+
+  len = 1;
+
+  if (pos >= available)
+    return pos;  // too few bytes available
+
+  unsigned char b;
+
+  status = pReader->Read(pos, 1, &b);
+
+  if (status != 0)
+    return status;
+
+  if (b == 0)  // we can't handle u-int values larger than 8 bytes
+    return E_FILE_FORMAT_INVALID;
+
+  unsigned char m = 0x80;
+
+  while (!(b & m)) {
+    m >>= 1;
+    ++len;
+  }
+
+  return 0;  // success
+}
+
+// TODO(vigneshv): This function assumes that unsigned values never have their
+// high bit set.
+long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) {
+  if (!pReader || pos < 0 || (size <= 0) || (size > 8))
+    return E_FILE_FORMAT_INVALID;
+
+  long long result = 0;
+
+  for (long long i = 0; i < size; ++i) {
+    unsigned char b;
+
+    const long status = pReader->Read(pos, 1, &b);
+
+    if (status < 0)
+      return status;
+
+    result <<= 8;
+    result |= b;
+
+    ++pos;
+  }
+
+  return result;
+}
+
+long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
+                      double& result) {
+  if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8)))
+    return E_FILE_FORMAT_INVALID;
+
+  const long size = static_cast<long>(size_);
+
+  unsigned char buf[8];
+
+  const int status = pReader->Read(pos, size, buf);
+
+  if (status < 0)  // error
+    return status;
+
+  if (size == 4) {
+    union {
+      float f;
+      unsigned long ff;
+    };
+
+    ff = 0;
+
+    for (int i = 0;;) {
+      ff |= buf[i];
+
+      if (++i >= 4)
+        break;
+
+      ff <<= 8;
+    }
+
+    result = f;
+  } else {
+    union {
+      double d;
+      unsigned long long dd;
+    };
+
+    dd = 0;
+
+    for (int i = 0;;) {
+      dd |= buf[i];
+
+      if (++i >= 8)
+        break;
+
+      dd <<= 8;
+    }
+
+    result = d;
+  }
+
+  if (mkvparser::isinf(result) || mkvparser::isnan(result))
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+long UnserializeInt(IMkvReader* pReader, long long pos, long long size,
+                    long long& result_ref) {
+  if (!pReader || pos < 0 || size < 1 || size > 8)
+    return E_FILE_FORMAT_INVALID;
+
+  signed char first_byte = 0;
+  const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte);
+
+  if (status < 0)
+    return status;
+
+  unsigned long long result = first_byte;
+  ++pos;
+
+  for (long i = 1; i < size; ++i) {
+    unsigned char b;
+
+    const long status = pReader->Read(pos, 1, &b);
+
+    if (status < 0)
+      return status;
+
+    result <<= 8;
+    result |= b;
+
+    ++pos;
+  }
+
+  result_ref = static_cast<long long>(result);
+  return 0;
+}
+
+long UnserializeString(IMkvReader* pReader, long long pos, long long size,
+                       char*& str) {
+  delete[] str;
+  str = NULL;
+
+  if (size >= LONG_MAX || size < 0 || size > kStringElementSizeLimit)
+    return E_FILE_FORMAT_INVALID;
+
+  // +1 for '\0' terminator
+  const long required_size = static_cast<long>(size) + 1;
+
+  str = SafeArrayAlloc<char>(1, required_size);
+  if (str == NULL)
+    return E_FILE_FORMAT_INVALID;
+
+  unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
+
+  const long status = pReader->Read(pos, static_cast<long>(size), buf);
+
+  if (status) {
+    delete[] str;
+    str = NULL;
+
+    return status;
+  }
+
+  str[required_size - 1] = '\0';
+  return 0;
+}
+
+long ParseElementHeader(IMkvReader* pReader, long long& pos, long long stop,
+                        long long& id, long long& size) {
+  if (stop >= 0 && pos >= stop)
+    return E_FILE_FORMAT_INVALID;
+
+  long len;
+
+  id = ReadID(pReader, pos, len);
+
+  if (id < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  pos += len;  // consume id
+
+  if (stop >= 0 && pos >= stop)
+    return E_FILE_FORMAT_INVALID;
+
+  size = ReadUInt(pReader, pos, len);
+
+  if (size < 0 || len < 1 || len > 8) {
+    // Invalid: Negative payload size, negative or 0 length integer, or integer
+    // larger than 64 bits (libwebm cannot handle them).
+    return E_FILE_FORMAT_INVALID;
+  }
+
+  // Avoid rolling over pos when very close to LLONG_MAX.
+  const unsigned long long rollover_check =
+      static_cast<unsigned long long>(pos) + len;
+  if (rollover_check > LLONG_MAX)
+    return E_FILE_FORMAT_INVALID;
+
+  pos += len;  // consume length of size
+
+  // pos now designates payload
+
+  if (stop >= 0 && pos > stop)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;  // success
+}
+
+bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
+           long long& val) {
+  if (!pReader || pos < 0)
+    return false;
+
+  long long total = 0;
+  long long available = 0;
+
+  const long status = pReader->Length(&total, &available);
+  if (status < 0 || (total >= 0 && available > total))
+    return false;
+
+  long len = 0;
+
+  const long long id = ReadID(pReader, pos, len);
+  if (id < 0 || (available - pos) > len)
+    return false;
+
+  if (static_cast<unsigned long>(id) != expected_id)
+    return false;
+
+  pos += len;  // consume id
+
+  const long long size = ReadUInt(pReader, pos, len);
+  if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len)
+    return false;
+
+  pos += len;  // consume length of size of payload
+
+  val = UnserializeUInt(pReader, pos, size);
+  if (val < 0)
+    return false;
+
+  pos += size;  // consume size of payload
+
+  return true;
+}
+
+bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
+           unsigned char*& buf, size_t& buflen) {
+  if (!pReader || pos < 0)
+    return false;
+
+  long long total = 0;
+  long long available = 0;
+
+  long status = pReader->Length(&total, &available);
+  if (status < 0 || (total >= 0 && available > total))
+    return false;
+
+  long len = 0;
+  const long long id = ReadID(pReader, pos, len);
+  if (id < 0 || (available - pos) > len)
+    return false;
+
+  if (static_cast<unsigned long>(id) != expected_id)
+    return false;
+
+  pos += len;  // consume id
+
+  const long long size = ReadUInt(pReader, pos, len);
+  if (size < 0 || len <= 0 || len > 8 || (available - pos) > len)
+    return false;
+
+  unsigned long long rollover_check =
+      static_cast<unsigned long long>(pos) + len;
+  if (rollover_check > LLONG_MAX)
+    return false;
+
+  pos += len;  // consume length of size of payload
+
+  rollover_check = static_cast<unsigned long long>(pos) + size;
+  if (rollover_check > LLONG_MAX)
+    return false;
+
+  if ((pos + size) > available)
+    return false;
+
+  if (size >= LONG_MAX)
+    return false;
+
+  const long buflen_ = static_cast<long>(size);
+
+  buf = SafeArrayAlloc<unsigned char>(1, buflen_);
+  if (!buf)
+    return false;
+
+  status = pReader->Read(pos, buflen_, buf);
+  if (status != 0)
+    return false;
+
+  buflen = buflen_;
+
+  pos += size;  // consume size of payload
+  return true;
+}
+
+EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); }
+
+EBMLHeader::~EBMLHeader() { delete[] m_docType; }
+
+void EBMLHeader::Init() {
+  m_version = 1;
+  m_readVersion = 1;
+  m_maxIdLength = 4;
+  m_maxSizeLength = 8;
+
+  if (m_docType) {
+    delete[] m_docType;
+    m_docType = NULL;
+  }
+
+  m_docTypeVersion = 1;
+  m_docTypeReadVersion = 1;
+}
+
+long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) {
+  if (!pReader)
+    return E_FILE_FORMAT_INVALID;
+
+  long long total, available;
+
+  long status = pReader->Length(&total, &available);
+
+  if (status < 0)  // error
+    return status;
+
+  pos = 0;
+
+  // Scan until we find what looks like the first byte of the EBML header.
+  const long long kMaxScanBytes = (available >= 1024) ? 1024 : available;
+  const unsigned char kEbmlByte0 = 0x1A;
+  unsigned char scan_byte = 0;
+
+  while (pos < kMaxScanBytes) {
+    status = pReader->Read(pos, 1, &scan_byte);
+
+    if (status < 0)  // error
+      return status;
+    else if (status > 0)
+      return E_BUFFER_NOT_FULL;
+
+    if (scan_byte == kEbmlByte0)
+      break;
+
+    ++pos;
+  }
+
+  long len = 0;
+  const long long ebml_id = ReadID(pReader, pos, len);
+
+  if (ebml_id == E_BUFFER_NOT_FULL)
+    return E_BUFFER_NOT_FULL;
+
+  if (len != 4 || ebml_id != libwebm::kMkvEBML)
+    return E_FILE_FORMAT_INVALID;
+
+  // Move read pos forward to the EBML header size field.
+  pos += 4;
+
+  // Read length of size field.
+  long long result = GetUIntLength(pReader, pos, len);
+
+  if (result < 0)  // error
+    return E_FILE_FORMAT_INVALID;
+  else if (result > 0)  // need more data
+    return E_BUFFER_NOT_FULL;
+
+  if (len < 1 || len > 8)
+    return E_FILE_FORMAT_INVALID;
+
+  if ((total >= 0) && ((total - pos) < len))
+    return E_FILE_FORMAT_INVALID;
+
+  if ((available - pos) < len)
+    return pos + len;  // try again later
+
+  // Read the EBML header size.
+  result = ReadUInt(pReader, pos, len);
+
+  if (result < 0)  // error
+    return result;
+
+  pos += len;  // consume size field
+
+  // pos now designates start of payload
+
+  if ((total >= 0) && ((total - pos) < result))
+    return E_FILE_FORMAT_INVALID;
+
+  if ((available - pos) < result)
+    return pos + result;
+
+  const long long end = pos + result;
+
+  Init();
+
+  while (pos < end) {
+    long long id, size;
+
+    status = ParseElementHeader(pReader, pos, end, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (size == 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (id == libwebm::kMkvEBMLVersion) {
+      m_version = UnserializeUInt(pReader, pos, size);
+
+      if (m_version <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == libwebm::kMkvEBMLReadVersion) {
+      m_readVersion = UnserializeUInt(pReader, pos, size);
+
+      if (m_readVersion <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == libwebm::kMkvEBMLMaxIDLength) {
+      m_maxIdLength = UnserializeUInt(pReader, pos, size);
+
+      if (m_maxIdLength <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == libwebm::kMkvEBMLMaxSizeLength) {
+      m_maxSizeLength = UnserializeUInt(pReader, pos, size);
+
+      if (m_maxSizeLength <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == libwebm::kMkvDocType) {
+      if (m_docType)
+        return E_FILE_FORMAT_INVALID;
+
+      status = UnserializeString(pReader, pos, size, m_docType);
+
+      if (status)  // error
+        return status;
+    } else if (id == libwebm::kMkvDocTypeVersion) {
+      m_docTypeVersion = UnserializeUInt(pReader, pos, size);
+
+      if (m_docTypeVersion <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == libwebm::kMkvDocTypeReadVersion) {
+      m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
+
+      if (m_docTypeReadVersion <= 0)
+        return E_FILE_FORMAT_INVALID;
+    }
+
+    pos += size;
+  }
+
+  if (pos != end)
+    return E_FILE_FORMAT_INVALID;
+
+  // Make sure DocType, DocTypeReadVersion, and DocTypeVersion are valid.
+  if (m_docType == NULL || m_docTypeReadVersion <= 0 || m_docTypeVersion <= 0)
+    return E_FILE_FORMAT_INVALID;
+
+  // Make sure EBMLMaxIDLength and EBMLMaxSizeLength are valid.
+  if (m_maxIdLength <= 0 || m_maxIdLength > 4 || m_maxSizeLength <= 0 ||
+      m_maxSizeLength > 8)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+Segment::Segment(IMkvReader* pReader, long long elem_start,
+                 // long long elem_size,
+                 long long start, long long size)
+    : m_pReader(pReader),
+      m_element_start(elem_start),
+      // m_element_size(elem_size),
+      m_start(start),
+      m_size(size),
+      m_pos(start),
+      m_pUnknownSize(0),
+      m_pSeekHead(NULL),
+      m_pInfo(NULL),
+      m_pTracks(NULL),
+      m_pCues(NULL),
+      m_pChapters(NULL),
+      m_pTags(NULL),
+      m_clusters(NULL),
+      m_clusterCount(0),
+      m_clusterPreloadCount(0),
+      m_clusterSize(0) {}
+
+Segment::~Segment() {
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  Cluster** i = m_clusters;
+  Cluster** j = m_clusters + count;
+
+  while (i != j) {
+    Cluster* const p = *i++;
+    delete p;
+  }
+
+  delete[] m_clusters;
+
+  delete m_pTracks;
+  delete m_pInfo;
+  delete m_pCues;
+  delete m_pChapters;
+  delete m_pTags;
+  delete m_pSeekHead;
+}
+
+long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
+                                  Segment*& pSegment) {
+  if (pReader == NULL || pos < 0)
+    return E_PARSE_FAILED;
+
+  pSegment = NULL;
+
+  long long total, available;
+
+  const long status = pReader->Length(&total, &available);
+
+  if (status < 0)  // error
+    return status;
+
+  if (available < 0)
+    return -1;
+
+  if ((total >= 0) && (available > total))
+    return -1;
+
+  // I would assume that in practice this loop would execute
+  // exactly once, but we allow for other elements (e.g. Void)
+  // to immediately follow the EBML header.  This is fine for
+  // the source filter case (since the entire file is available),
+  // but in the splitter case over a network we should probably
+  // just give up early.  We could for example decide only to
+  // execute this loop a maximum of, say, 10 times.
+  // TODO:
+  // There is an implied "give up early" by only parsing up
+  // to the available limit.  We do do that, but only if the
+  // total file size is unknown.  We could decide to always
+  // use what's available as our limit (irrespective of whether
+  // we happen to know the total file length).  This would have
+  // as its sense "parse this much of the file before giving up",
+  // which a slightly different sense from "try to parse up to
+  // 10 EMBL elements before giving up".
+
+  for (;;) {
+    if ((total >= 0) && (pos >= total))
+      return E_FILE_FORMAT_INVALID;
+
+    // Read ID
+    long len;
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result)  // error, or too few available bytes
+      return result;
+
+    if ((total >= 0) && ((pos + len) > total))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    const long long idpos = pos;
+    const long long id = ReadID(pReader, pos, len);
+
+    if (id < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume ID
+
+    // Read Size
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result)  // error, or too few available bytes
+      return result;
+
+    if ((total >= 0) && ((pos + len) > total))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return size;
+
+    pos += len;  // consume length of size of element
+
+    // Pos now points to start of payload
+
+    // Handle "unknown size" for live streaming of webm files.
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (id == libwebm::kMkvSegment) {
+      if (size == unknown_size)
+        size = -1;
+
+      else if (total < 0)
+        size = -1;
+
+      else if ((pos + size) > total)
+        size = -1;
+
+      pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size);
+      if (pSegment == NULL)
+        return E_PARSE_FAILED;
+
+      return 0;  // success
+    }
+
+    if (size == unknown_size)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((total >= 0) && ((pos + size) > total))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + size) > available)
+      return pos + size;
+
+    pos += size;  // consume payload
+  }
+}
+
+long long Segment::ParseHeaders() {
+  // Outermost (level 0) segment object has been constructed,
+  // and pos designates start of payload.  We need to find the
+  // inner (level 1) elements.
+  long long total, available;
+
+  const int status = m_pReader->Length(&total, &available);
+
+  if (status < 0)  // error
+    return status;
+
+  if (total > 0 && available > total)
+    return E_FILE_FORMAT_INVALID;
+
+  const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+  if ((segment_stop >= 0 && total >= 0 && segment_stop > total) ||
+      (segment_stop >= 0 && m_pos > segment_stop)) {
+    return E_FILE_FORMAT_INVALID;
+  }
+
+  for (;;) {
+    if ((total >= 0) && (m_pos >= total))
+      break;
+
+    if ((segment_stop >= 0) && (m_pos >= segment_stop))
+      break;
+
+    long long pos = m_pos;
+    const long long element_start = pos;
+
+    // Avoid rolling over pos when very close to LLONG_MAX.
+    unsigned long long rollover_check = pos + 1ULL;
+    if (rollover_check > LLONG_MAX)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + 1) > available)
+      return (pos + 1);
+
+    long len;
+    long long result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return result;
+
+    if (result > 0) {
+      // MkvReader doesn't have enough data to satisfy this read attempt.
+      return (pos + 1);
+    }
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    const long long idpos = pos;
+    const long long id = ReadID(m_pReader, idpos, len);
+
+    if (id < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (id == libwebm::kMkvCluster)
+      break;
+
+    pos += len;  // consume ID
+
+    if ((pos + 1) > available)
+      return (pos + 1);
+
+    // Read Size
+    result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return result;
+
+    if (result > 0) {
+      // MkvReader doesn't have enough data to satisfy this read attempt.
+      return (pos + 1);
+    }
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+
+    if (size < 0 || len < 1 || len > 8) {
+      // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or
+      // len > 8 is true instead of checking this _everywhere_.
+      return size;
+    }
+
+    pos += len;  // consume length of size of element
+
+    // Avoid rolling over pos when very close to LLONG_MAX.
+    rollover_check = static_cast<unsigned long long>(pos) + size;
+    if (rollover_check > LLONG_MAX)
+      return E_FILE_FORMAT_INVALID;
+
+    const long long element_size = size + pos - element_start;
+
+    // Pos now points to start of payload
+
+    if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    // We read EBML elements either in total or nothing at all.
+
+    if ((pos + size) > available)
+      return pos + size;
+
+    if (id == libwebm::kMkvInfo) {
+      if (m_pInfo)
+        return E_FILE_FORMAT_INVALID;
+
+      m_pInfo = new (std::nothrow)
+          SegmentInfo(this, pos, size, element_start, element_size);
+
+      if (m_pInfo == NULL)
+        return -1;
+
+      const long status = m_pInfo->Parse();
+
+      if (status)
+        return status;
+    } else if (id == libwebm::kMkvTracks) {
+      if (m_pTracks)
+        return E_FILE_FORMAT_INVALID;
+
+      m_pTracks = new (std::nothrow)
+          Tracks(this, pos, size, element_start, element_size);
+
+      if (m_pTracks == NULL)
+        return -1;
+
+      const long status = m_pTracks->Parse();
+
+      if (status)
+        return status;
+    } else if (id == libwebm::kMkvCues) {
+      if (m_pCues == NULL) {
+        m_pCues = new (std::nothrow)
+            Cues(this, pos, size, element_start, element_size);
+
+        if (m_pCues == NULL)
+          return -1;
+      }
+    } else if (id == libwebm::kMkvSeekHead) {
+      if (m_pSeekHead == NULL) {
+        m_pSeekHead = new (std::nothrow)
+            SeekHead(this, pos, size, element_start, element_size);
+
+        if (m_pSeekHead == NULL)
+          return -1;
+
+        const long status = m_pSeekHead->Parse();
+
+        if (status)
+          return status;
+      }
+    } else if (id == libwebm::kMkvChapters) {
+      if (m_pChapters == NULL) {
+        m_pChapters = new (std::nothrow)
+            Chapters(this, pos, size, element_start, element_size);
+
+        if (m_pChapters == NULL)
+          return -1;
+
+        const long status = m_pChapters->Parse();
+
+        if (status)
+          return status;
+      }
+    } else if (id == libwebm::kMkvTags) {
+      if (m_pTags == NULL) {
+        m_pTags = new (std::nothrow)
+            Tags(this, pos, size, element_start, element_size);
+
+        if (m_pTags == NULL)
+          return -1;
+
+        const long status = m_pTags->Parse();
+
+        if (status)
+          return status;
+      }
+    }
+
+    m_pos = pos + size;  // consume payload
+  }
+
+  if (segment_stop >= 0 && m_pos > segment_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if (m_pInfo == NULL)  // TODO: liberalize this behavior
+    return E_FILE_FORMAT_INVALID;
+
+  if (m_pTracks == NULL)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;  // success
+}
+
+long Segment::LoadCluster(long long& pos, long& len) {
+  for (;;) {
+    const long result = DoLoadCluster(pos, len);
+
+    if (result <= 1)
+      return result;
+  }
+}
+
+long Segment::DoLoadCluster(long long& pos, long& len) {
+  if (m_pos < 0)
+    return DoLoadClusterUnknownSize(pos, len);
+
+  long long total, avail;
+
+  long status = m_pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  if (total >= 0 && avail > total)
+    return E_FILE_FORMAT_INVALID;
+
+  const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+  long long cluster_off = -1;  // offset relative to start of segment
+  long long cluster_size = -1;  // size of cluster payload
+
+  for (;;) {
+    if ((total >= 0) && (m_pos >= total))
+      return 1;  // no more clusters
+
+    if ((segment_stop >= 0) && (m_pos >= segment_stop))
+      return 1;  // no more clusters
+
+    pos = m_pos;
+
+    // Read ID
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long idpos = pos;
+    const long long id = ReadID(m_pReader, idpos, len);
+
+    if (id < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume ID
+
+    // Read Size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    pos += len;  // consume length of size of element
+
+    // pos now points to start of payload
+
+    if (size == 0) {
+      // Missing element payload: move on.
+      m_pos = pos;
+      continue;
+    }
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if ((segment_stop >= 0) && (size != unknown_size) &&
+        ((pos + size) > segment_stop)) {
+      return E_FILE_FORMAT_INVALID;
+    }
+
+    if (id == libwebm::kMkvCues) {
+      if (size == unknown_size) {
+        // Cues element of unknown size: Not supported.
+        return E_FILE_FORMAT_INVALID;
+      }
+
+      if (m_pCues == NULL) {
+        const long long element_size = (pos - idpos) + size;
+
+        m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
+        if (m_pCues == NULL)
+          return -1;
+      }
+
+      m_pos = pos + size;  // consume payload
+      continue;
+    }
+
+    if (id != libwebm::kMkvCluster) {
+      // Besides the Segment, Libwebm allows only cluster elements of unknown
+      // size. Fail the parse upon encountering a non-cluster element reporting
+      // unknown size.
+      if (size == unknown_size)
+        return E_FILE_FORMAT_INVALID;
+
+      m_pos = pos + size;  // consume payload
+      continue;
+    }
+
+    // We have a cluster.
+
+    cluster_off = idpos - m_start;  // relative pos
+
+    if (size != unknown_size)
+      cluster_size = size;
+
+    break;
+  }
+
+  if (cluster_off < 0) {
+    // No cluster, die.
+    return E_FILE_FORMAT_INVALID;
+  }
+
+  long long pos_;
+  long len_;
+
+  status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
+
+  if (status < 0) {  // error, or underflow
+    pos = pos_;
+    len = len_;
+
+    return status;
+  }
+
+  // status == 0 means "no block entries found"
+  // status > 0 means "found at least one block entry"
+
+  // TODO:
+  // The issue here is that the segment increments its own
+  // pos ptr past the most recent cluster parsed, and then
+  // starts from there to parse the next cluster.  If we
+  // don't know the size of the current cluster, then we
+  // must either parse its payload (as we do below), looking
+  // for the cluster (or cues) ID to terminate the parse.
+  // This isn't really what we want: rather, we really need
+  // a way to create the curr cluster object immediately.
+  // The pity is that cluster::parse can determine its own
+  // boundary, and we largely duplicate that same logic here.
+  //
+  // Maybe we need to get rid of our look-ahead preloading
+  // in source::parse???
+  //
+  // As we're parsing the blocks in the curr cluster
+  //(in cluster::parse), we should have some way to signal
+  // to the segment that we have determined the boundary,
+  // so it can adjust its own segment::m_pos member.
+  //
+  // The problem is that we're asserting in asyncreadinit,
+  // because we adjust the pos down to the curr seek pos,
+  // and the resulting adjusted len is > 2GB.  I'm suspicious
+  // that this is even correct, but even if it is, we can't
+  // be loading that much data in the cache anyway.
+
+  const long idx = m_clusterCount;
+
+  if (m_clusterPreloadCount > 0) {
+    if (idx >= m_clusterSize)
+      return E_FILE_FORMAT_INVALID;
+
+    Cluster* const pCluster = m_clusters[idx];
+    if (pCluster == NULL || pCluster->m_index >= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    const long long off = pCluster->GetPosition();
+    if (off < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (off == cluster_off) {  // preloaded already
+      if (status == 0)  // no entries found
+        return E_FILE_FORMAT_INVALID;
+
+      if (cluster_size >= 0)
+        pos += cluster_size;
+      else {
+        const long long element_size = pCluster->GetElementSize();
+
+        if (element_size <= 0)
+          return E_FILE_FORMAT_INVALID;  // TODO: handle this case
+
+        pos = pCluster->m_element_start + element_size;
+      }
+
+      pCluster->m_index = idx;  // move from preloaded to loaded
+      ++m_clusterCount;
+      --m_clusterPreloadCount;
+
+      m_pos = pos;  // consume payload
+      if (segment_stop >= 0 && m_pos > segment_stop)
+        return E_FILE_FORMAT_INVALID;
+
+      return 0;  // success
+    }
+  }
+
+  if (status == 0) {  // no entries found
+    if (cluster_size >= 0)
+      pos += cluster_size;
+
+    if ((total >= 0) && (pos >= total)) {
+      m_pos = total;
+      return 1;  // no more clusters
+    }
+
+    if ((segment_stop >= 0) && (pos >= segment_stop)) {
+      m_pos = segment_stop;
+      return 1;  // no more clusters
+    }
+
+    m_pos = pos;
+    return 2;  // try again
+  }
+
+  // status > 0 means we have an entry
+
+  Cluster* const pCluster = Cluster::Create(this, idx, cluster_off);
+  if (pCluster == NULL)
+    return -1;
+
+  if (!AppendCluster(pCluster)) {
+    delete pCluster;
+    return -1;
+  }
+
+  if (cluster_size >= 0) {
+    pos += cluster_size;
+
+    m_pos = pos;
+
+    if (segment_stop > 0 && m_pos > segment_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    return 0;
+  }
+
+  m_pUnknownSize = pCluster;
+  m_pos = -pos;
+
+  return 0;  // partial success, since we have a new cluster
+
+  // status == 0 means "no block entries found"
+  // pos designates start of payload
+  // m_pos has NOT been adjusted yet (in case we need to come back here)
+}
+
+long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
+  if (m_pos >= 0 || m_pUnknownSize == NULL)
+    return E_PARSE_FAILED;
+
+  const long status = m_pUnknownSize->Parse(pos, len);
+
+  if (status < 0)  // error or underflow
+    return status;
+
+  if (status == 0)  // parsed a block
+    return 2;  // continue parsing
+
+  const long long start = m_pUnknownSize->m_element_start;
+  const long long size = m_pUnknownSize->GetElementSize();
+
+  if (size < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  pos = start + size;
+  m_pos = pos;
+
+  m_pUnknownSize = 0;
+
+  return 2;  // continue parsing
+}
+
+bool Segment::AppendCluster(Cluster* pCluster) {
+  if (pCluster == NULL || pCluster->m_index < 0)
+    return false;
+
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  long& size = m_clusterSize;
+  const long idx = pCluster->m_index;
+
+  if (size < count || idx != m_clusterCount)
+    return false;
+
+  if (count >= size) {
+    const long n = (size <= 0) ? 2048 : 2 * size;
+
+    Cluster** const qq = new (std::nothrow) Cluster*[n];
+    if (qq == NULL)
+      return false;
+
+    Cluster** q = qq;
+    Cluster** p = m_clusters;
+    Cluster** const pp = p + count;
+
+    while (p != pp)
+      *q++ = *p++;
+
+    delete[] m_clusters;
+
+    m_clusters = qq;
+    size = n;
+  }
+
+  if (m_clusterPreloadCount > 0) {
+    Cluster** const p = m_clusters + m_clusterCount;
+    if (*p == NULL || (*p)->m_index >= 0)
+      return false;
+
+    Cluster** q = p + m_clusterPreloadCount;
+    if (q >= (m_clusters + size))
+      return false;
+
+    for (;;) {
+      Cluster** const qq = q - 1;
+      if ((*qq)->m_index >= 0)
+        return false;
+
+      *q = *qq;
+      q = qq;
+
+      if (q == p)
+        break;
+    }
+  }
+
+  m_clusters[idx] = pCluster;
+  ++m_clusterCount;
+  return true;
+}
+
+bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
+  if (pCluster == NULL || pCluster->m_index >= 0 || idx < m_clusterCount)
+    return false;
+
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  long& size = m_clusterSize;
+  if (size < count)
+    return false;
+
+  if (count >= size) {
+    const long n = (size <= 0) ? 2048 : 2 * size;
+
+    Cluster** const qq = new (std::nothrow) Cluster*[n];
+    if (qq == NULL)
+      return false;
+    Cluster** q = qq;
+
+    Cluster** p = m_clusters;
+    Cluster** const pp = p + count;
+
+    while (p != pp)
+      *q++ = *p++;
+
+    delete[] m_clusters;
+
+    m_clusters = qq;
+    size = n;
+  }
+
+  if (m_clusters == NULL)
+    return false;
+
+  Cluster** const p = m_clusters + idx;
+
+  Cluster** q = m_clusters + count;
+  if (q < p || q >= (m_clusters + size))
+    return false;
+
+  while (q > p) {
+    Cluster** const qq = q - 1;
+
+    if ((*qq)->m_index >= 0)
+      return false;
+
+    *q = *qq;
+    q = qq;
+  }
+
+  m_clusters[idx] = pCluster;
+  ++m_clusterPreloadCount;
+  return true;
+}
+
+long Segment::Load() {
+  if (m_clusters != NULL || m_clusterSize != 0 || m_clusterCount != 0)
+    return E_PARSE_FAILED;
+
+  // Outermost (level 0) segment object has been constructed,
+  // and pos designates start of payload.  We need to find the
+  // inner (level 1) elements.
+
+  const long long header_status = ParseHeaders();
+
+  if (header_status < 0)  // error
+    return static_cast<long>(header_status);
+
+  if (header_status > 0)  // underflow
+    return E_BUFFER_NOT_FULL;
+
+  if (m_pInfo == NULL || m_pTracks == NULL)
+    return E_FILE_FORMAT_INVALID;
+
+  for (;;) {
+    const long status = LoadCluster();
+
+    if (status < 0)  // error
+      return status;
+
+    if (status >= 1)  // no more clusters
+      return 0;
+  }
+}
+
+SeekHead::Entry::Entry() : id(0), pos(0), element_start(0), element_size(0) {}
+
+SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
+                   long long element_start, long long element_size)
+    : m_pSegment(pSegment),
+      m_start(start),
+      m_size(size_),
+      m_element_start(element_start),
+      m_element_size(element_size),
+      m_entries(0),
+      m_entry_count(0),
+      m_void_elements(0),
+      m_void_element_count(0) {}
+
+SeekHead::~SeekHead() {
+  delete[] m_entries;
+  delete[] m_void_elements;
+}
+
+long SeekHead::Parse() {
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long pos = m_start;
+  const long long stop = m_start + m_size;
+
+  // first count the seek head entries
+
+  int entry_count = 0;
+  int void_element_count = 0;
+
+  while (pos < stop) {
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (id == libwebm::kMkvSeek)
+      ++entry_count;
+    else if (id == libwebm::kMkvVoid)
+      ++void_element_count;
+
+    pos += size;  // consume payload
+
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if (entry_count > 0) {
+    m_entries = new (std::nothrow) Entry[entry_count];
+
+    if (m_entries == NULL)
+      return -1;
+  }
+
+  if (void_element_count > 0) {
+    m_void_elements = new (std::nothrow) VoidElement[void_element_count];
+
+    if (m_void_elements == NULL)
+      return -1;
+  }
+
+  // now parse the entries and void elements
+
+  Entry* pEntry = m_entries;
+  VoidElement* pVoidElement = m_void_elements;
+
+  pos = m_start;
+
+  while (pos < stop) {
+    const long long idpos = pos;
+
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error