Compare commits
3 Commits
main
...
sandbox/ma
Author | SHA1 | Date | |
---|---|---|---|
|
379839bedc | ||
|
484c71d875 | ||
|
a45b72d731 |
@ -1,97 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
# Generated with clang-format 4.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
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: false
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -1,6 +0,0 @@
|
||||
*.sln eol=crlf
|
||||
*.vcproj eol=crlf
|
||||
*.vsprops eol=crlf
|
||||
*.vcxproj eol=crlf
|
||||
*.mkv -text -diff
|
||||
*.webm -text -diff
|
24
.gitignore
vendored
24
.gitignore
vendored
@ -2,7 +2,6 @@
|
||||
*.MKV
|
||||
core
|
||||
*.a
|
||||
*.d
|
||||
*.so*
|
||||
*.o
|
||||
*~
|
||||
@ -11,25 +10,6 @@ core
|
||||
*.user
|
||||
*.suo
|
||||
*.exe
|
||||
/*.webm
|
||||
*.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
|
||||
Release
|
22
Android.mk
22
Android.mk
@ -1,17 +1,13 @@
|
||||
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
|
||||
LOCAL_MODULE:= libmkvparser
|
||||
LOCAL_SRC_FILES:= mkvparser.cpp \
|
||||
mkvreader.cpp
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= mkvparser
|
||||
LOCAL_SRC_FILES:= sample.cpp
|
||||
LOCAL_STATIC_LIBRARIES:= libmkvparser
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
436
CMakeLists.txt
436
CMakeLists.txt
@ -1,436 +0,0 @@
|
||||
## 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("${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)
|
||||
|
||||
require_cxx_flag_nomsvc("-std=c++11")
|
||||
|
||||
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_sources
|
||||
"${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"
|
||||
"${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>)
|
||||
|
||||
add_executable(webm_parser_demo ${webm_parser_demo_sources})
|
||||
target_link_libraries(webm_parser_demo LINK_PUBLIC 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 ()
|
25
Makefile
Normal file
25
Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
CXX := g++
|
||||
CXXFLAGS := -W -Wall -g
|
||||
LIBWEBM := libwebm.a
|
||||
WEBMOBJS := mkvparser.o mkvreader.o mkvmuxer.o mkvmuxerutil.o mkvwriter.o
|
||||
OBJECTS1 := $(PARSEOBJ) sample.o
|
||||
OBJECTS2 := $(PARSEOBJ) $(MUXEROBJ) sample_muxer/sample_muxer.o
|
||||
INCLUDES := -I.
|
||||
EXES := samplemuxer sample
|
||||
|
||||
all: $(EXES)
|
||||
|
||||
sample: sample.o $(LIBWEBM)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
samplemuxer: sample_muxer/sample_muxer.o $(LIBWEBM)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
libwebm.a: $(WEBMOBJS)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) -f $(OBJECTS1) $(OBJECTS2) $(WEBMOBJS) $(LIBWEBM) $(EXES) Makefile.bak
|
@ -1,57 +0,0 @@
|
||||
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
|
39
PATENTS.TXT
39
PATENTS.TXT
@ -1,23 +1,22 @@
|
||||
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.
|
||||
"This implementation" means the copyrightable works 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.
|
||||
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 this
|
||||
implementation of VP8, 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 this
|
||||
implementation of VP8. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of VP8 or any code incorporated within this
|
||||
implementation of VP8 constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of VP8
|
||||
shall terminate as of the date such litigation is filed.
|
||||
|
143
README.libwebm
143
README.libwebm
@ -1,143 +0,0 @@
|
||||
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
|
||||
|
34
RELEASE.TXT
Normal file
34
RELEASE.TXT
Normal file
@ -0,0 +1,34 @@
|
||||
1.0.0.5
|
||||
* Handled case when no duration
|
||||
* Handled empty clusters
|
||||
* Handled empty clusters when seeking
|
||||
* Implemented check lacing bits
|
||||
|
||||
1.0.0.4
|
||||
* Made Cues member variables mutables
|
||||
* Defined against badly-formatted cue points
|
||||
* Segment::GetCluster returns CuePoint too
|
||||
* Separated cue-based searches
|
||||
|
||||
1.0.0.3
|
||||
* Added Block::GetOffset() to get a frame's offset in a block
|
||||
* Changed cluster count type from size_t to long
|
||||
* Parsed SeekHead to find cues
|
||||
* Allowed seeking beyond end of cluster cache
|
||||
* Added not to attempt to reparse cues element
|
||||
* Restructured Segment::LoadCluster
|
||||
* Marked position of cues without parsing cues element
|
||||
* Allowed cue points to be loaded incrementally
|
||||
* Implemented to load lazily cue points as they're searched
|
||||
* Merged Cues::LoadCuePoint into Cues::Find
|
||||
* Lazy init cues
|
||||
* Loaded cue point during find
|
||||
|
||||
1.0.0.2
|
||||
* added support for Cues element
|
||||
* seeking was improved
|
||||
|
||||
1.0.0.1
|
||||
* fixed item 141
|
||||
* added item 142
|
||||
* added this file, RELEASE.TXT, to repository
|
@ -1,73 +0,0 @@
|
||||
## 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 ()
|
@ -1,23 +0,0 @@
|
||||
## 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 ()
|
@ -1,26 +0,0 @@
|
||||
## 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_
|
@ -1,24 +0,0 @@
|
||||
## 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_
|
@ -1,62 +0,0 @@
|
||||
#!/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)
|
||||
}
|
||||
|
@ -1,93 +0,0 @@
|
||||
// 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);
|
||||
errno_t 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
|
@ -1,44 +0,0 @@
|
||||
// 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_
|
@ -1,220 +0,0 @@
|
||||
// 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
|
@ -1,71 +0,0 @@
|
||||
// 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_
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "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
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef 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_
|
@ -1,110 +0,0 @@
|
||||
// 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
|
@ -1,65 +0,0 @@
|
||||
// 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_
|
@ -1,43 +0,0 @@
|
||||
// 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"
|
||||
|
||||
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
|
@ -1,68 +0,0 @@
|
||||
// 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_
|
@ -1,266 +0,0 @@
|
||||
// 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
|
@ -1,125 +0,0 @@
|
||||
// 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_
|
@ -1,181 +0,0 @@
|
||||
// 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();
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
// 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
|
@ -1,215 +0,0 @@
|
||||
// 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_
|
@ -1,191 +0,0 @@
|
||||
// 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();
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef 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_
|
@ -1,85 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "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
|
@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef 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_
|
192
common/webmids.h
192
common/webmids.h
@ -1,192 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef COMMON_WEBMIDS_H_
|
||||
#define COMMON_WEBMIDS_H_
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
enum MkvId {
|
||||
kMkvEBML = 0x1A45DFA3,
|
||||
kMkvEBMLVersion = 0x4286,
|
||||
kMkvEBMLReadVersion = 0x42F7,
|
||||
kMkvEBMLMaxIDLength = 0x42F2,
|
||||
kMkvEBMLMaxSizeLength = 0x42F3,
|
||||
kMkvDocType = 0x4282,
|
||||
kMkvDocTypeVersion = 0x4287,
|
||||
kMkvDocTypeReadVersion = 0x4285,
|
||||
kMkvVoid = 0xEC,
|
||||
kMkvSignatureSlot = 0x1B538667,
|
||||
kMkvSignatureAlgo = 0x7E8A,
|
||||
kMkvSignatureHash = 0x7E9A,
|
||||
kMkvSignaturePublicKey = 0x7EA5,
|
||||
kMkvSignature = 0x7EB5,
|
||||
kMkvSignatureElements = 0x7E5B,
|
||||
kMkvSignatureElementList = 0x7E7B,
|
||||
kMkvSignedElement = 0x6532,
|
||||
// segment
|
||||
kMkvSegment = 0x18538067,
|
||||
// Meta Seek Information
|
||||
kMkvSeekHead = 0x114D9B74,
|
||||
kMkvSeek = 0x4DBB,
|
||||
kMkvSeekID = 0x53AB,
|
||||
kMkvSeekPosition = 0x53AC,
|
||||
// Segment Information
|
||||
kMkvInfo = 0x1549A966,
|
||||
kMkvTimecodeScale = 0x2AD7B1,
|
||||
kMkvDuration = 0x4489,
|
||||
kMkvDateUTC = 0x4461,
|
||||
kMkvTitle = 0x7BA9,
|
||||
kMkvMuxingApp = 0x4D80,
|
||||
kMkvWritingApp = 0x5741,
|
||||
// Cluster
|
||||
kMkvCluster = 0x1F43B675,
|
||||
kMkvTimecode = 0xE7,
|
||||
kMkvPrevSize = 0xAB,
|
||||
kMkvBlockGroup = 0xA0,
|
||||
kMkvBlock = 0xA1,
|
||||
kMkvBlockDuration = 0x9B,
|
||||
kMkvReferenceBlock = 0xFB,
|
||||
kMkvLaceNumber = 0xCC,
|
||||
kMkvSimpleBlock = 0xA3,
|
||||
kMkvBlockAdditions = 0x75A1,
|
||||
kMkvBlockMore = 0xA6,
|
||||
kMkvBlockAddID = 0xEE,
|
||||
kMkvBlockAdditional = 0xA5,
|
||||
kMkvDiscardPadding = 0x75A2,
|
||||
// Track
|
||||
kMkvTracks = 0x1654AE6B,
|
||||
kMkvTrackEntry = 0xAE,
|
||||
kMkvTrackNumber = 0xD7,
|
||||
kMkvTrackUID = 0x73C5,
|
||||
kMkvTrackType = 0x83,
|
||||
kMkvFlagEnabled = 0xB9,
|
||||
kMkvFlagDefault = 0x88,
|
||||
kMkvFlagForced = 0x55AA,
|
||||
kMkvFlagLacing = 0x9C,
|
||||
kMkvDefaultDuration = 0x23E383,
|
||||
kMkvMaxBlockAdditionID = 0x55EE,
|
||||
kMkvName = 0x536E,
|
||||
kMkvLanguage = 0x22B59C,
|
||||
kMkvCodecID = 0x86,
|
||||
kMkvCodecPrivate = 0x63A2,
|
||||
kMkvCodecName = 0x258688,
|
||||
kMkvCodecDelay = 0x56AA,
|
||||
kMkvSeekPreRoll = 0x56BB,
|
||||
// video
|
||||
kMkvVideo = 0xE0,
|
||||
kMkvFlagInterlaced = 0x9A,
|
||||
kMkvStereoMode = 0x53B8,
|
||||
kMkvAlphaMode = 0x53C0,
|
||||
kMkvPixelWidth = 0xB0,
|
||||
kMkvPixelHeight = 0xBA,
|
||||
kMkvPixelCropBottom = 0x54AA,
|
||||
kMkvPixelCropTop = 0x54BB,
|
||||
kMkvPixelCropLeft = 0x54CC,
|
||||
kMkvPixelCropRight = 0x54DD,
|
||||
kMkvDisplayWidth = 0x54B0,
|
||||
kMkvDisplayHeight = 0x54BA,
|
||||
kMkvDisplayUnit = 0x54B2,
|
||||
kMkvAspectRatioType = 0x54B3,
|
||||
kMkvFrameRate = 0x2383E3,
|
||||
// end video
|
||||
// colour
|
||||
kMkvColour = 0x55B0,
|
||||
kMkvMatrixCoefficients = 0x55B1,
|
||||
kMkvBitsPerChannel = 0x55B2,
|
||||
kMkvChromaSubsamplingHorz = 0x55B3,
|
||||
kMkvChromaSubsamplingVert = 0x55B4,
|
||||
kMkvCbSubsamplingHorz = 0x55B5,
|
||||
kMkvCbSubsamplingVert = 0x55B6,
|
||||
kMkvChromaSitingHorz = 0x55B7,
|
||||
kMkvChromaSitingVert = 0x55B8,
|
||||
kMkvRange = 0x55B9,
|
||||
kMkvTransferCharacteristics = 0x55BA,
|
||||
kMkvPrimaries = 0x55BB,
|
||||
kMkvMaxCLL = 0x55BC,
|
||||
kMkvMaxFALL = 0x55BD,
|
||||
// mastering metadata
|
||||
kMkvMasteringMetadata = 0x55D0,
|
||||
kMkvPrimaryRChromaticityX = 0x55D1,
|
||||
kMkvPrimaryRChromaticityY = 0x55D2,
|
||||
kMkvPrimaryGChromaticityX = 0x55D3,
|
||||
kMkvPrimaryGChromaticityY = 0x55D4,
|
||||
kMkvPrimaryBChromaticityX = 0x55D5,
|
||||
kMkvPrimaryBChromaticityY = 0x55D6,
|
||||
kMkvWhitePointChromaticityX = 0x55D7,
|
||||
kMkvWhitePointChromaticityY = 0x55D8,
|
||||
kMkvLuminanceMax = 0x55D9,
|
||||
kMkvLuminanceMin = 0x55DA,
|
||||
// end mastering metadata
|
||||
// end colour
|
||||
// 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_
|
91
dumpvtt.cc
91
dumpvtt.cc
@ -1,91 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include <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);
|
||||
}
|
||||
}
|
15
hdr_util.hpp
15
hdr_util.hpp
@ -1,15 +0,0 @@
|
||||
// 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_
|
207
iosbuild.sh
207
iosbuild.sh
@ -1,207 +0,0 @@
|
||||
#!/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}."
|
37
libwebm_2005.sln
Normal file
37
libwebm_2005.sln
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwebm", "libwebm_2005.vcproj", "{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
ProjectSection(WebsiteProperties) = preProject
|
||||
Debug.AspNetCompiler.Debug = "True"
|
||||
Release.AspNetCompiler.Debug = "False"
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample", "sample_2005.vcproj", "{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4} = {F9128EC6-C008-41AD-B38F-0E70D549D9F4}
|
||||
EndProjectSection
|
||||
ProjectSection(WebsiteProperties) = preProject
|
||||
Debug.AspNetCompiler.Debug = "True"
|
||||
Release.AspNetCompiler.Debug = "False"
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.Build.0 = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
204
libwebm_2005.vcproj
Normal file
204
libwebm_2005.vcproj
Normal file
@ -0,0 +1,204 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="libwebm"
|
||||
ProjectGUID="{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
RootNamespace="mkvparser"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxer.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxertypes.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\webmids.hpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
38
libwebm_2008.sln
Normal file
38
libwebm_2008.sln
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample", "sample_2008.vcproj", "{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA} = {7B1F12CA-0724-430B-B61A-1D357C912CBA}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwebm", "libwebm_2008.vcproj", "{7B1F12CA-0724-430B-B61A-1D357C912CBA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample_muxer", "sample_muxer\sample_muxer.vcproj", "{B407561F-1F5E-4798-B9C2-81AB09CFBC16}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA} = {7B1F12CA-0724-430B-B61A-1D357C912CBA}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.Build.0 = Release|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Release|Win32.Build.0 = Release|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
209
libwebm_2008.vcproj
Normal file
209
libwebm_2008.vcproj
Normal file
@ -0,0 +1,209 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="libwebm"
|
||||
ProjectGUID="{7B1F12CA-0724-430B-B61A-1D357C912CBA}"
|
||||
RootNamespace="libwebm"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="196613"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
EnableIntrinsicFunctions="true"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxer.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxer.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxertypes.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\webmids.hpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
@ -1,158 +0,0 @@
|
||||
// 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();
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
// 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
|
@ -1,45 +0,0 @@
|
||||
// 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_
|
@ -1,33 +0,0 @@
|
||||
// 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;
|
||||
}
|
@ -1,410 +0,0 @@
|
||||
// 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) {
|
||||
PesHeader fragment_header;
|
||||
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
|
@ -1,177 +0,0 @@
|
||||
// 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_
|
542
m2ts/webm2pes.cc
542
m2ts/webm2pes.cc
@ -1,542 +0,0 @@
|
||||
// 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;
|
||||
|
||||
//
|
||||
// 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) {
|
||||
if (std::string(track->GetCodecId()) == std::string("V_VP8")) {
|
||||
codec_ = VideoFrame::kVP8;
|
||||
} else if (std::string(track->GetCodecId()) == 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
|
274
m2ts/webm2pes.h
274
m2ts/webm2pes.h
@ -1,274 +0,0 @@
|
||||
// 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_
|
@ -1,33 +0,0 @@
|
||||
// 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;
|
||||
}
|
2081
mkvmuxer.cpp
Normal file
2081
mkvmuxer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
840
mkvmuxer.hpp
840
mkvmuxer.hpp
@ -1,15 +1,841 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
// 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.
|
||||
#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"
|
||||
#ifndef MKVMUXER_HPP
|
||||
#define MKVMUXER_HPP
|
||||
|
||||
#endif // LIBWEBM_MKVMUXER_HPP_
|
||||
#include "mkvmuxertypes.hpp"
|
||||
|
||||
// For a description of the WebM elements see
|
||||
// http://www.webmproject.org/code/specs/container/.
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
class MkvWriter;
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Interface used by the mkvmuxer to write out the Mkv data.
|
||||
class IMkvWriter {
|
||||
public:
|
||||
// Writes out |len| bytes of |buf|. Returns 0 on success.
|
||||
virtual int32 Write(const void* buf, uint32 len) = 0;
|
||||
|
||||
// Returns the offset of the output position from the beginning of the
|
||||
// output.
|
||||
virtual int64 Position() const = 0;
|
||||
|
||||
// Set the current File position. Returns 0 on success.
|
||||
virtual int32 Position(int64 position) = 0;
|
||||
|
||||
// Returns true if the writer is seekable.
|
||||
virtual bool Seekable() const = 0;
|
||||
|
||||
// Element start notification. Called whenever an element identifier is about
|
||||
// to be written to the stream. |element_id| is the element identifier, and
|
||||
// |position| is the location in the WebM stream where the first octet of the
|
||||
// element identifier will be written.
|
||||
// Note: the |MkvId| enumeration in webmids.hpp defines element values.
|
||||
virtual void ElementStartNotify(uint64 element_id, int64 position) = 0;
|
||||
|
||||
protected:
|
||||
IMkvWriter();
|
||||
virtual ~IMkvWriter();
|
||||
|
||||
private:
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(IMkvWriter);
|
||||
};
|
||||
|
||||
// Writes out the EBML header for a WebM file. This function must be called
|
||||
// before any other libwebm writing functions are called.
|
||||
bool WriteEbmlHeader(IMkvWriter* writer);
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Class to hold data the will be written to a block.
|
||||
class Frame {
|
||||
public:
|
||||
Frame();
|
||||
~Frame();
|
||||
|
||||
// Copies |frame| data into |frame_|. Returns true on success.
|
||||
bool Init(const uint8* frame, uint64 length);
|
||||
|
||||
const uint8* frame() const { return frame_; }
|
||||
uint64 length() const { return length_; }
|
||||
void set_track_number(uint64 track_number) { track_number_ = track_number; }
|
||||
uint64 track_number() const { return track_number_; }
|
||||
void set_timestamp(uint64 timestamp) { timestamp_ = timestamp; }
|
||||
uint64 timestamp() const { return timestamp_; }
|
||||
void set_is_key(bool key) { is_key_ = key; }
|
||||
bool is_key() const { return is_key_; }
|
||||
|
||||
private:
|
||||
// Pointer to the data. Owned by this class.
|
||||
uint8* frame_;
|
||||
|
||||
// Length of the data.
|
||||
uint64 length_;
|
||||
|
||||
// Mkv track number the data is associated with.
|
||||
uint64 track_number_;
|
||||
|
||||
// Timestamp of the data in nanoseconds.
|
||||
uint64 timestamp_;
|
||||
|
||||
// Flag telling if the data should set the key flag of a block.
|
||||
bool is_key_;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Class to hold one cue point in a Cues element.
|
||||
class CuePoint {
|
||||
public:
|
||||
CuePoint();
|
||||
~CuePoint();
|
||||
|
||||
// Returns the size in bytes for the entire CuePoint element.
|
||||
uint64 Size() const;
|
||||
|
||||
// Output the CuePoint element to the writer. Returns true on success.
|
||||
bool Write(IMkvWriter* writer) const;
|
||||
|
||||
void set_time(uint64 time) { time_ = time; }
|
||||
uint64 time() const { return time_; }
|
||||
void set_track(uint64 track) { track_ = track; }
|
||||
uint64 track() const { return track_; }
|
||||
void set_cluster_pos(uint64 cluster_pos) { cluster_pos_ = cluster_pos; }
|
||||
uint64 cluster_pos() const { return cluster_pos_; }
|
||||
void set_block_number(uint64 block_number) { block_number_ = block_number; }
|
||||
uint64 block_number() const { return block_number_; }
|
||||
void set_output_block_number(bool output_block_number) {
|
||||
output_block_number_ = output_block_number;
|
||||
}
|
||||
bool output_block_number() const { return output_block_number_; }
|
||||
|
||||
private:
|
||||
// Returns the size in bytes for the payload of the CuePoint element.
|
||||
uint64 PayloadSize() const;
|
||||
|
||||
// Absolute timecode according to the segment time base.
|
||||
uint64 time_;
|
||||
|
||||
// The Track element associated with the CuePoint.
|
||||
uint64 track_;
|
||||
|
||||
// The position of the Cluster containing the Block.
|
||||
uint64 cluster_pos_;
|
||||
|
||||
// Number of the Block within the Cluster, starting from 1.
|
||||
uint64 block_number_;
|
||||
|
||||
// If true the muxer will write out the block number for the cue if the
|
||||
// block number is different than the default of 1. Default is set to true.
|
||||
bool output_block_number_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(CuePoint);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Cues element.
|
||||
class Cues {
|
||||
public:
|
||||
Cues();
|
||||
~Cues();
|
||||
|
||||
// Adds a cue point to the Cues element. Returns true on success.
|
||||
bool AddCue(CuePoint* cue);
|
||||
|
||||
// Returns the cue point by index. Returns NULL if there is no cue point
|
||||
// match.
|
||||
const CuePoint* GetCueByIndex(int32 index) const;
|
||||
|
||||
// Output the Cues element to the writer. Returns true on success.
|
||||
bool Write(IMkvWriter* writer) const;
|
||||
|
||||
int32 cue_entries_size() const { return cue_entries_size_; }
|
||||
void set_output_block_number(bool output_block_number) {
|
||||
output_block_number_ = output_block_number;
|
||||
}
|
||||
bool output_block_number() const { return output_block_number_; }
|
||||
|
||||
private:
|
||||
// Number of allocated elements in |cue_entries_|.
|
||||
int32 cue_entries_capacity_;
|
||||
|
||||
// Number of CuePoints in |cue_entries_|.
|
||||
int32 cue_entries_size_;
|
||||
|
||||
// CuePoint list.
|
||||
CuePoint** cue_entries_;
|
||||
|
||||
// If true the muxer will write out the block number for the cue if the
|
||||
// block number is different than the default of 1. Default is set to true.
|
||||
bool output_block_number_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cues);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// 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 only be encrypted once 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();
|
||||
|
||||
uint64 enc_algo() const { return enc_algo_; }
|
||||
uint64 encoding_order() const { return encoding_order_; }
|
||||
uint64 encoding_scope() const { return encoding_scope_; }
|
||||
uint64 encoding_type() const { return encoding_type_; }
|
||||
|
||||
// Sets the content encryption id. Copies |length| bytes from |id| to
|
||||
// |enc_key_id_|. Returns true on success.
|
||||
bool SetEncryptionID(const uint8* id, uint64 length);
|
||||
|
||||
// Returns the size in bytes for the ContentEncoding element.
|
||||
uint64 Size() const;
|
||||
|
||||
// Writes out the ContentEncoding element to |writer|. Returns true on
|
||||
// success.
|
||||
bool Write(IMkvWriter* writer) const;
|
||||
|
||||
private:
|
||||
// Returns the size in bytes for the encoding elements.
|
||||
uint64 EncodingSize(uint64 compresion_size, uint64 encryption_size) const;
|
||||
|
||||
// Returns the size in bytes for the encryption elements.
|
||||
uint64 EncryptionSize() const;
|
||||
|
||||
// Track element names
|
||||
uint64 enc_algo_;
|
||||
uint8* enc_key_id_;
|
||||
uint64 encoding_order_;
|
||||
uint64 encoding_scope_;
|
||||
uint64 encoding_type_;
|
||||
|
||||
// Size of the ContentEncKeyID data in bytes.
|
||||
uint64 enc_key_id_length_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Track element.
|
||||
class Track {
|
||||
public:
|
||||
Track();
|
||||
virtual ~Track();
|
||||
|
||||
// Adds a ContentEncoding element to the Track. Returns true on success.
|
||||
virtual bool AddContentEncoding();
|
||||
|
||||
// Returns the ContentEncoding by index. Returns NULL if there is no
|
||||
// ContentEncoding match.
|
||||
ContentEncoding* GetContentEncodingByIndex(uint32 index) const;
|
||||
|
||||
// Returns the size in bytes for the payload of the Track element.
|
||||
virtual uint64 PayloadSize() const;
|
||||
|
||||
// Returns the size in bytes of the Track element.
|
||||
virtual uint64 Size() const;
|
||||
|
||||
// Output the Track element to the writer. Returns true on success.
|
||||
virtual bool Write(IMkvWriter* writer) const;
|
||||
|
||||
// Sets the CodecPrivate element of the Track element. Copies |length|
|
||||
// bytes from |codec_private| to |codec_private_|. Returns true on success.
|
||||
bool SetCodecPrivate(const uint8* codec_private, uint64 length);
|
||||
|
||||
void set_codec_id(const char* codec_id);
|
||||
const char* codec_id() const { return codec_id_; }
|
||||
const uint8* codec_private() const { return codec_private_; }
|
||||
void set_language(const char* language);
|
||||
const char* language() const { return language_; }
|
||||
void set_name(const char* name);
|
||||
const char* name() const { return name_; }
|
||||
void set_number(uint64 number) { number_ = number; }
|
||||
uint64 number() const { return number_; }
|
||||
void set_type(uint64 type) { type_ = type; }
|
||||
uint64 type() const { return type_; }
|
||||
uint64 uid() const { return uid_; }
|
||||
|
||||
uint64 codec_private_length() const { return codec_private_length_; }
|
||||
uint32 content_encoding_entries_size() const {
|
||||
return content_encoding_entries_size_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns a random number to be used for the Track UID.
|
||||
static uint64 MakeUID();
|
||||
|
||||
// Track element names
|
||||
char* codec_id_;
|
||||
uint8* codec_private_;
|
||||
char* language_;
|
||||
char* name_;
|
||||
uint64 number_;
|
||||
uint64 type_;
|
||||
const uint64 uid_;
|
||||
|
||||
// Size of the CodecPrivate data in bytes.
|
||||
uint64 codec_private_length_;
|
||||
|
||||
// ContentEncoding element list.
|
||||
ContentEncoding** content_encoding_entries_;
|
||||
|
||||
// Number of ContentEncoding elements added.
|
||||
uint32 content_encoding_entries_size_;
|
||||
|
||||
// Flag telling if the rand call was seeded.
|
||||
static bool is_seeded_;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
VideoTrack();
|
||||
virtual ~VideoTrack();
|
||||
|
||||
// Returns the size in bytes for the payload of the Track element plus the
|
||||
// video specific elements.
|
||||
virtual uint64 PayloadSize() const;
|
||||
|
||||
// Output the VideoTrack element to the writer. Returns true on success.
|
||||
virtual bool Write(IMkvWriter* writer) const;
|
||||
|
||||
// Sets the video's stereo mode. Returns true on success.
|
||||
bool SetStereoMode(uint64 stereo_mode);
|
||||
|
||||
void set_display_height(uint64 height) { display_height_ = height; }
|
||||
uint64 display_height() const { return display_height_; }
|
||||
void set_display_width(uint64 width) { display_width_ = width; }
|
||||
uint64 display_width() const { return display_width_; }
|
||||
void set_frame_rate(double frame_rate) { frame_rate_ = frame_rate; }
|
||||
double frame_rate() const { return frame_rate_; }
|
||||
void set_height(uint64 height) { height_ = height; }
|
||||
uint64 height() const { return height_; }
|
||||
uint64 stereo_mode() { return stereo_mode_; }
|
||||
void set_width(uint64 width) { width_ = width; }
|
||||
uint64 width() const { return width_; }
|
||||
|
||||
private:
|
||||
// Returns the size in bytes of the Video element.
|
||||
uint64 VideoPayloadSize() const;
|
||||
|
||||
// Video track element names.
|
||||
uint64 display_height_;
|
||||
uint64 display_width_;
|
||||
double frame_rate_;
|
||||
uint64 height_;
|
||||
uint64 stereo_mode_;
|
||||
uint64 width_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Track that has audio specific elements.
|
||||
class AudioTrack : public Track {
|
||||
public:
|
||||
AudioTrack();
|
||||
virtual ~AudioTrack();
|
||||
|
||||
// Returns the size in bytes for the payload of the Track element plus the
|
||||
// audio specific elements.
|
||||
virtual uint64 PayloadSize() const;
|
||||
|
||||
// Output the AudioTrack element to the writer. Returns true on success.
|
||||
virtual bool Write(IMkvWriter* writer) const;
|
||||
|
||||
void set_bit_depth(uint64 bit_depth) { bit_depth_ = bit_depth; }
|
||||
uint64 bit_depth() const { return bit_depth_; }
|
||||
void set_channels(uint64 channels) { channels_ = channels; }
|
||||
uint64 channels() const { return channels_; }
|
||||
void set_sample_rate(double sample_rate) { sample_rate_ = sample_rate; }
|
||||
double sample_rate() const { return sample_rate_; }
|
||||
|
||||
private:
|
||||
// Audio track element names.
|
||||
uint64 bit_depth_;
|
||||
uint64 channels_;
|
||||
double sample_rate_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(AudioTrack);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Tracks element
|
||||
class Tracks {
|
||||
public:
|
||||
// Audio and video type defined by the Matroska specs.
|
||||
enum {
|
||||
kVideo = 0x1,
|
||||
kAudio = 0x2
|
||||
};
|
||||
// Vorbis and VP8 coded id defined by the Matroska specs.
|
||||
static const char* const kVorbisCodecId;
|
||||
static const char* const kVp8CodecId;
|
||||
|
||||
Tracks();
|
||||
~Tracks();
|
||||
|
||||
// Adds a Track element to the Tracks object. |track| will be owned and
|
||||
// deleted by the Tracks object. Returns true on success. |number| is the
|
||||
// number to use for the track. |number| must be >= 0. If |number| == 0
|
||||
// then the muxer will decide on the track number.
|
||||
bool AddTrack(Track* track, int32 number);
|
||||
|
||||
// Returns the track by index. Returns NULL if there is no track match.
|
||||
const Track* GetTrackByIndex(uint32 idx) const;
|
||||
|
||||
// Search the Tracks and return the track that matches |tn|. Returns NULL
|
||||
// if there is no track match.
|
||||
Track* GetTrackByNumber(uint64 track_number) const;
|
||||
|
||||
// Returns true if the track number is an audio track.
|
||||
bool TrackIsAudio(uint64 track_number) const;
|
||||
|
||||
// Returns true if the track number is a video track.
|
||||
bool TrackIsVideo(uint64 track_number) const;
|
||||
|
||||
// Output the Tracks element to the writer. Returns true on success.
|
||||
bool Write(IMkvWriter* writer) const;
|
||||
|
||||
uint32 track_entries_size() const { return track_entries_size_; }
|
||||
|
||||
private:
|
||||
// Track element list.
|
||||
Track** track_entries_;
|
||||
|
||||
// Number of Track elements added.
|
||||
uint32 track_entries_size_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tracks);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Cluster element
|
||||
//
|
||||
// Notes:
|
||||
// |Init| must be called before any other method in this class.
|
||||
class Cluster {
|
||||
public:
|
||||
Cluster(uint64 timecode, int64 cues_pos);
|
||||
~Cluster();
|
||||
|
||||
// |timecode| is the absolute timecode of the cluster. |cues_pos| is the
|
||||
// position for the cluster within the segment that should be written in
|
||||
// the cues element.
|
||||
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.
|
||||
// Inputs:
|
||||
// frame: Pointer to the data
|
||||
// length: Length of the data
|
||||
// track_number: Track to add the data to. Value returned by Add track
|
||||
// functions.
|
||||
// timestamp: Timecode of the frame relative to the cluster timecode.
|
||||
// is_key: Flag telling whether or not this frame is a key frame.
|
||||
bool AddFrame(const uint8* frame,
|
||||
uint64 length,
|
||||
uint64 track_number,
|
||||
short timecode,
|
||||
bool is_key);
|
||||
|
||||
// Increments the size of the cluster's data in bytes.
|
||||
void AddPayloadSize(uint64 size);
|
||||
|
||||
// Closes the cluster so no more data can be written to it. Will update the
|
||||
// cluster's size if |writer_| is seekable. Returns true on success.
|
||||
bool Finalize();
|
||||
|
||||
// Returns the size in bytes for the entire Cluster element.
|
||||
uint64 Size() const;
|
||||
|
||||
int32 blocks_added() const { return blocks_added_; }
|
||||
uint64 payload_size() const { return payload_size_; }
|
||||
int64 position_for_cues() const { return position_for_cues_; }
|
||||
uint64 timecode() const { return timecode_; }
|
||||
|
||||
private:
|
||||
// Outputs the Cluster header to |writer_|. Returns true on success.
|
||||
bool WriteClusterHeader();
|
||||
|
||||
// Number of blocks added to the cluster.
|
||||
int32 blocks_added_;
|
||||
|
||||
// Flag telling if the cluster has been closed.
|
||||
bool finalized_;
|
||||
|
||||
// Flag telling if the cluster's header has been written.
|
||||
bool header_written_;
|
||||
|
||||
// The size of the cluster elements in bytes.
|
||||
uint64 payload_size_;
|
||||
|
||||
// The file position used for cue points.
|
||||
const int64 position_for_cues_;
|
||||
|
||||
// The file position of the cluster's size element.
|
||||
int64 size_position_;
|
||||
|
||||
// The absolute timecode of the cluster.
|
||||
const uint64 timecode_;
|
||||
|
||||
// Pointer to the writer object. Not owned by this class.
|
||||
IMkvWriter* writer_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cluster);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// SeekHead element
|
||||
class SeekHead {
|
||||
public:
|
||||
SeekHead();
|
||||
~SeekHead();
|
||||
|
||||
// TODO(fgalligan): Change this to reserve a certain size. Then check how
|
||||
// big the seek entry to be added is as not every seek entry will be the
|
||||
// maximum size it could be.
|
||||
// Adds a seek entry to be written out when the element is finalized. |id|
|
||||
// must be the coded mkv element id. |pos| is the file position of the
|
||||
// element. Returns true on success.
|
||||
bool AddSeekEntry(uint32 id, uint64 pos);
|
||||
|
||||
// Writes out SeekHead and SeekEntry elements. Returns true on success.
|
||||
bool Finalize(IMkvWriter* writer) const;
|
||||
|
||||
// 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);
|
||||
|
||||
private:
|
||||
// We are going to put a cap on the number of Seek Entries.
|
||||
const static int32 kSeekEntryCount = 4;
|
||||
|
||||
// Returns the maximum size in bytes of one seek entry.
|
||||
uint64 MaxEntrySize() const;
|
||||
|
||||
// Seek entry id element list.
|
||||
uint32 seek_entry_id_[kSeekEntryCount];
|
||||
|
||||
// Seek entry pos element list.
|
||||
uint64 seek_entry_pos_[kSeekEntryCount];
|
||||
|
||||
// The file position of SeekHead element.
|
||||
int64 start_pos_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SeekHead);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Segment Information element
|
||||
class SegmentInfo {
|
||||
public:
|
||||
SegmentInfo();
|
||||
~SegmentInfo();
|
||||
|
||||
// Will update the duration if |duration_| is > 0.0. Returns true on success.
|
||||
bool Finalize(IMkvWriter* writer) const;
|
||||
|
||||
// Sets |muxing_app_| and |writing_app_|.
|
||||
bool Init();
|
||||
|
||||
// Output the Segment Information element to the writer. Returns true on
|
||||
// success.
|
||||
bool Write(IMkvWriter* writer);
|
||||
|
||||
void set_duration(double duration) { duration_ = duration; }
|
||||
double duration() const { return duration_; }
|
||||
const char* muxing_app() const { return muxing_app_; }
|
||||
void set_timecode_scale(uint64 scale) { timecode_scale_ = scale; }
|
||||
uint64 timecode_scale() const { return timecode_scale_; }
|
||||
void set_writing_app(const char* app);
|
||||
const char* writing_app() const { return writing_app_; }
|
||||
|
||||
private:
|
||||
// Segment Information element names.
|
||||
// Initially set to -1 to signify that a duration has not been set and should
|
||||
// not be written out.
|
||||
double duration_;
|
||||
// Set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
|
||||
char* muxing_app_;
|
||||
uint64 timecode_scale_;
|
||||
// Initially set to libwebm-%d.%d.%d.%d, major, minor, build, revision.
|
||||
char* writing_app_;
|
||||
|
||||
// The file position of the duration element.
|
||||
int64 duration_pos_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SegmentInfo);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// This class represents the main segment in a WebM file. Currently only
|
||||
// supports one Segment element.
|
||||
//
|
||||
// Notes:
|
||||
// |Init| must be called before any other method in this class.
|
||||
class Segment {
|
||||
public:
|
||||
enum Mode {
|
||||
kLive = 0x1,
|
||||
kFile = 0x2
|
||||
};
|
||||
|
||||
const static uint64 kDefaultMaxClusterDuration = 30000000000ULL;
|
||||
|
||||
Segment();
|
||||
~Segment();
|
||||
|
||||
// Initializes |SegmentInfo| and returns result. Always returns false when
|
||||
// |ptr_writer| is NULL.
|
||||
bool Init(IMkvWriter* ptr_writer);
|
||||
|
||||
// Adds an audio track to the segment. Returns the number of the track on
|
||||
// success, 0 on error.
|
||||
uint64 AddAudioTrack(int32 sample_rate, int32 channels);
|
||||
|
||||
// Adds an audio track to the segment. Returns the number of the track on
|
||||
// success, 0 on error. |number| is the number to use for the audio track.
|
||||
// |number| must be >= 0. If |number| == 0 then the muxer will decide on
|
||||
// the track number.
|
||||
uint64 AddAudioTrack(int32 sample_rate, int32 channels, int32 number);
|
||||
|
||||
// Adds a frame to be output in the file. Returns true on success.
|
||||
// Inputs:
|
||||
// frame: Pointer to the data
|
||||
// length: Length of the data
|
||||
// track_number: Track to add the data to. Value returned by Add track
|
||||
// functions.
|
||||
// timestamp: Timestamp of the frame in nanoseconds from 0.
|
||||
// is_key: Flag telling whether or not this frame is a key frame.
|
||||
bool AddFrame(const uint8* frame,
|
||||
uint64 length,
|
||||
uint64 track_number,
|
||||
uint64 timestamp,
|
||||
bool is_key);
|
||||
|
||||
// Adds a video track to the segment. Returns the number of the track on
|
||||
// success, 0 on error.
|
||||
uint64 AddVideoTrack(int32 width, int32 height);
|
||||
|
||||
// Adds a video track to the segment. Returns the number of the track on
|
||||
// success, 0 on error. |number| is the number to use for the video track.
|
||||
// |number| must be >= 0. If |number| == 0 then the muxer will decide on
|
||||
// the track number.
|
||||
uint64 AddVideoTrack(int32 width, int32 height, int32 number);
|
||||
|
||||
// Sets which track to use for the Cues element. Must have added the track
|
||||
// before calling this function. Returns true on success. |track_number| is
|
||||
// returned by the Add track functions.
|
||||
bool CuesTrack(uint64 track_number);
|
||||
|
||||
// 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.
|
||||
SegmentInfo* GetSegmentInfo() { return &segment_info_; }
|
||||
|
||||
// Search the Tracks and return the track that matches |track_number|.
|
||||
// Returns NULL if there is no track match.
|
||||
Track* GetTrackByNumber(uint64 track_number) const;
|
||||
|
||||
// Toggles whether to output a cues element.
|
||||
void OutputCues(bool output_cues);
|
||||
|
||||
// Sets if the muxer will output files in chunks or not. |chunking| is a
|
||||
// flag telling whether or not to turn on chunking. |filename| is the base
|
||||
// filename for the chunk files. The header chunk file will be named
|
||||
// |filename|.hdr and the data chunks will be named
|
||||
// |filename|_XXXXXX.chk. Chunking implies that the muxer will be writing
|
||||
// to files so the muxer will use the default MkvWriter class to control
|
||||
// what data is written to what files. Returns true on success.
|
||||
// TODO: Should we change the IMkvWriter Interface to add Open and Close?
|
||||
// That will force the interface to be dependent on files.
|
||||
bool SetChunking(bool chunking, const char* filename);
|
||||
|
||||
bool chunking() const { return chunking_; }
|
||||
uint64 cues_track() const { return cues_track_; }
|
||||
void set_max_cluster_duration(uint64 max_cluster_duration) {
|
||||
max_cluster_duration_ = max_cluster_duration;
|
||||
}
|
||||
uint64 max_cluster_duration() const { return max_cluster_duration_; }
|
||||
void set_max_cluster_size(uint64 max_cluster_size) {
|
||||
max_cluster_size_ = max_cluster_size;
|
||||
}
|
||||
uint64 max_cluster_size() const { return max_cluster_size_; }
|
||||
void set_mode(Mode mode) { mode_ = mode; }
|
||||
Mode mode() const { return mode_; }
|
||||
bool output_cues() const { return output_cues_; }
|
||||
const SegmentInfo* segment_info() const { return &segment_info_; }
|
||||
|
||||
private:
|
||||
// Adds a cue point to the Cues element. |timestamp| is the time in
|
||||
// nanoseconds of the cue's time. Returns true on success.
|
||||
bool AddCuePoint(uint64 timestamp);
|
||||
|
||||
// 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 |name| according to how many chunks have been written. |ext| is the
|
||||
// file extension. |name| must be deleted by the calling app. Returns true
|
||||
// on success.
|
||||
bool UpdateChunkName(const char* ext, char** name) const;
|
||||
|
||||
// Returns the maximum offset within the segment's payload. When chunking
|
||||
// this function is needed to determine offsets of elements within the
|
||||
// chunked files. Returns -1 on error.
|
||||
int64 MaxOffset();
|
||||
|
||||
// Adds the frame to our frame array.
|
||||
bool QueueFrame(Frame* frame);
|
||||
|
||||
// Output all frames that are queued. Returns true on success and if there
|
||||
// are no frames queued.
|
||||
bool WriteFramesAll();
|
||||
|
||||
// Output all frames that are queued that have an end time that is less
|
||||
// then |timestamp|. Returns true on success and if there are no frames
|
||||
// queued.
|
||||
bool WriteFramesLessThan(uint64 timestamp);
|
||||
|
||||
// Outputs the segment header, Segment Information element, SeekHead element,
|
||||
// and Tracks element to |writer_|.
|
||||
bool WriteSegmentHeader();
|
||||
|
||||
// WebM elements
|
||||
Cues cues_;
|
||||
SeekHead seek_head_;
|
||||
SegmentInfo segment_info_;
|
||||
Tracks tracks_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// List of clusters.
|
||||
Cluster** cluster_list_;
|
||||
|
||||
// Number of cluster pointers allocated in the cluster list.
|
||||
int32 cluster_list_capacity_;
|
||||
|
||||
// Number of clusters in the cluster list.
|
||||
int32 cluster_list_size_;
|
||||
|
||||
// Track number that is associated with the cues element for this segment.
|
||||
uint64 cues_track_;
|
||||
|
||||
// List of stored audio frames. These variables are used to store frames so
|
||||
// the muxer can follow the guideline "Audio blocks that contain the video
|
||||
// key frame's timecode should be in the same cluster as the video key frame
|
||||
// block."
|
||||
Frame** frames_;
|
||||
|
||||
// Number of frame pointers allocated in the frame list.
|
||||
int32 frames_capacity_;
|
||||
|
||||
// Number of frames in the frame list.
|
||||
int32 frames_size_;
|
||||
|
||||
// Flag telling if a video track has been added to the segment.
|
||||
bool has_video_;
|
||||
|
||||
// Flag telling if the segment's header has been written.
|
||||
bool header_written_;
|
||||
|
||||
// Last timestamp in nanoseconds added to a cluster.
|
||||
uint64 last_timestamp_;
|
||||
|
||||
// Maximum time in nanoseconds for a cluster duration. This variable is a
|
||||
// guideline and some clusters may have a longer duration. Default is 30
|
||||
// seconds.
|
||||
uint64 max_cluster_duration_;
|
||||
|
||||
// Maximum size in bytes for a cluster. This variable is a guideline and
|
||||
// some clusters may have a larger size. Default is 0 which signifies that
|
||||
// the muxer will decide the size.
|
||||
uint64 max_cluster_size_;
|
||||
|
||||
// The mode that segment is in. If set to |kLive| the writer must not
|
||||
// seek backwards.
|
||||
Mode mode_;
|
||||
|
||||
// Flag telling the muxer that a new cluster should be started with the next
|
||||
// frame.
|
||||
bool new_cluster_;
|
||||
|
||||
// Flag telling the muxer that a new cue point should be added.
|
||||
bool new_cuepoint_;
|
||||
|
||||
// TODO(fgalligan): Should we add support for more than one Cues element?
|
||||
// Flag whether or not the muxer should output a Cues element.
|
||||
bool output_cues_;
|
||||
|
||||
// The file position of the segment's payload.
|
||||
int64 payload_pos_;
|
||||
|
||||
// The file position of the element's size.
|
||||
int64 size_position_;
|
||||
|
||||
// Pointer to the writer objects. Not owned by this class.
|
||||
IMkvWriter* writer_cluster_;
|
||||
IMkvWriter* writer_cues_;
|
||||
IMkvWriter* writer_header_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Segment);
|
||||
};
|
||||
|
||||
} //end namespace mkvmuxer
|
||||
|
||||
#endif //MKVMUXER_HPP
|
||||
|
4193
mkvmuxer/mkvmuxer.cc
4193
mkvmuxer/mkvmuxer.cc
File diff suppressed because it is too large
Load Diff
1921
mkvmuxer/mkvmuxer.h
1921
mkvmuxer/mkvmuxer.h
File diff suppressed because it is too large
Load Diff
@ -1,28 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef 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_
|
@ -1,744 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvmuxer/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<const uint32>(length)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
|
||||
uint64 size) {
|
||||
if (!writer || !value || size < 1)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
if (writer->Write(value, static_cast<uint32>(size)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, kDateElementSize))
|
||||
return false;
|
||||
|
||||
if (SerializeInt(writer, value, kDateElementSize))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
|
||||
Cluster* cluster) {
|
||||
if (!writer || !frame || !frame->IsValid() || !cluster ||
|
||||
!cluster->timecode_scale())
|
||||
return 0;
|
||||
|
||||
// Technically the timecode for a block can be less than the
|
||||
// timecode for the cluster itself (remember that block timecode
|
||||
// is a signed, 16-bit integer). However, as a simplification we
|
||||
// only permit non-negative cluster-relative timecodes for blocks.
|
||||
const int64 relative_timecode = cluster->GetRelativeTimecode(
|
||||
frame->timestamp() / cluster->timecode_scale());
|
||||
if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode)
|
||||
return 0;
|
||||
|
||||
return frame->CanBeSimpleBlock() ?
|
||||
WriteSimpleBlock(writer, frame, relative_timecode) :
|
||||
WriteBlock(writer, frame, relative_timecode,
|
||||
cluster->timecode_scale());
|
||||
}
|
||||
|
||||
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
// Subtract one for the void ID and the coded size.
|
||||
uint64 void_entry_size = size - 1 - GetCodedUIntSize(size - 1);
|
||||
uint64 void_size = EbmlMasterElementSize(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
|
@ -1,112 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef 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);
|
||||
|
||||
// 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_
|
@ -1,51 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef 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_
|
@ -1,785 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (vp9_profile >= 0 || vp9_level >= 0) {
|
||||
const int kMaxVp9PrivateSize = 6;
|
||||
unsigned char private_data[kMaxVp9PrivateSize];
|
||||
int 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;
|
||||
private_data[private_size++] = kVp9ProfileId;
|
||||
private_data[private_size++] = kVp9ProfileIdLength;
|
||||
private_data[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;
|
||||
private_data[private_size++] = kVp9LevelId;
|
||||
private_data[private_size++] = kVp9LevelIdLength;
|
||||
private_data[private_size++] = vp9_level;
|
||||
}
|
||||
if (!video->SetCodecPrivate(private_data, 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;
|
||||
}
|
@ -1,15 +1,29 @@
|
||||
// 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_
|
||||
// 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.
|
||||
|
||||
#ifndef MKVMUXERTYPES_HPP
|
||||
#define MKVMUXERTYPES_HPP
|
||||
|
||||
// Copied from Chromium basictypes.h
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef int int32;
|
||||
typedef unsigned int uint32;
|
||||
typedef long long int64;
|
||||
typedef unsigned long long uint64;
|
||||
|
||||
} //end namespace mkvmuxer
|
||||
|
||||
#endif // MKVMUXERTYPES_HPP
|
||||
|
390
mkvmuxerutil.cpp
Normal file
390
mkvmuxerutil.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
// 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 "mkvmuxerutil.hpp"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "mkvwriter.hpp"
|
||||
#include "webmids.hpp"
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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, uint64 value) {
|
||||
// Size of EBML ID
|
||||
int32 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += GetUIntSize(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, float value) {
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += sizeof(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, const char* value) {
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += strlen(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) {
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += size;
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size += GetCodedUIntSize(size);
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const uint32& val = reinterpret_cast<const uint32&>(f);
|
||||
|
||||
for (int32 i = 1; i <= 4; ++i) {
|
||||
const int32 byte_count = 4 - i;
|
||||
const int32 bit_count = byte_count * 8;
|
||||
|
||||
const uint32 bb = val >> bit_count;
|
||||
const uint8 b = static_cast<uint8>(bb);
|
||||
|
||||
const int32 status = writer->Write(&b, 1);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 WriteUInt(IMkvWriter* writer, uint64 value) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
int32 size = GetCodedUIntSize(value);
|
||||
|
||||
return WriteUIntSize(writer, value, size);
|
||||
}
|
||||
|
||||
int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size) {
|
||||
if (!writer || size < 0 || size > 8)
|
||||
return -1;
|
||||
|
||||
if (size > 0) {
|
||||
const uint64 bit = 1LL << (size * 7);
|
||||
|
||||
if (value > (bit - 2))
|
||||
return -1;
|
||||
|
||||
value |= bit;
|
||||
} else {
|
||||
size = 1;
|
||||
int64 bit;
|
||||
|
||||
for (;;) {
|
||||
bit = 1LL << (size * 7);
|
||||
const uint64 max = bit - 2;
|
||||
|
||||
if (value <= max)
|
||||
break;
|
||||
|
||||
++size;
|
||||
}
|
||||
|
||||
if (size > 8)
|
||||
return false;
|
||||
|
||||
value |= bit;
|
||||
}
|
||||
|
||||
return SerializeInt(writer, value, size);
|
||||
}
|
||||
|
||||
int32 WriteID(IMkvWriter* writer, uint64 type) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
writer->ElementStartNotify(type, writer->Position());
|
||||
|
||||
const int32 size = GetUIntSize(type);
|
||||
|
||||
return SerializeInt(writer, type, size);
|
||||
}
|
||||
|
||||
bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 type, uint64 size) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
const uint64 size = GetUIntSize(value);
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
if (SerializeInt(writer, value, static_cast<int32>(size)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, 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 int32 length = strlen(value);
|
||||
if (WriteUInt(writer, length))
|
||||
return false;
|
||||
|
||||
if (writer->Write(value, 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;
|
||||
}
|
||||
|
||||
uint64 WriteSimpleBlock(IMkvWriter* writer,
|
||||
const uint8* data,
|
||||
uint64 length,
|
||||
char track_number,
|
||||
short timecode,
|
||||
bool is_key) {
|
||||
|
||||
if (!writer || !data || length < 1 || track_number < 1 || timecode < 0)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, kMkvSimpleBlock))
|
||||
return 0;
|
||||
|
||||
const int32 size = static_cast<int32>(length) + 4;
|
||||
if (WriteUInt(writer, size))
|
||||
return 0;
|
||||
|
||||
if (WriteUInt(writer, static_cast<uint64>(track_number)))
|
||||
return 0;
|
||||
|
||||
if (SerializeInt(writer, static_cast<uint64>(timecode), 2))
|
||||
return 0;
|
||||
|
||||
uint64 flags = 0;
|
||||
if(is_key)
|
||||
flags |= 0x80;
|
||||
|
||||
if (SerializeInt(writer, flags, 1))
|
||||
return 0;
|
||||
|
||||
if (writer->Write(data, static_cast<uint32>(length)))
|
||||
return 0;
|
||||
|
||||
const uint64 element_size =
|
||||
GetUIntSize(kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + length;
|
||||
|
||||
return element_size;
|
||||
}
|
||||
|
||||
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
// Subtract one for the void ID and the coded size.
|
||||
uint64 void_entry_size = size - 1 - GetCodedUIntSize(size-1);
|
||||
uint64 void_size = EbmlMasterElementSize(kMkvVoid, void_entry_size) +
|
||||
void_entry_size;
|
||||
|
||||
if (void_size != size)
|
||||
return 0;
|
||||
|
||||
const int64 payload_position = writer->Position();
|
||||
if (payload_position < 0)
|
||||
return 0;
|
||||
|
||||
if (WriteID(writer, kMkvVoid))
|
||||
return 0;
|
||||
|
||||
if (WriteUInt(writer, void_entry_size))
|
||||
return 0;
|
||||
|
||||
const uint8 value = 0;
|
||||
for (int32 i = 0; i < static_cast<int32>(void_entry_size); ++i) {
|
||||
if (writer->Write(&value, 1))
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int64 stop_position = writer->Position();
|
||||
if (stop_position < 0 ||
|
||||
stop_position - payload_position != static_cast<int64>(void_size))
|
||||
return 0;
|
||||
|
||||
return void_size;
|
||||
}
|
||||
|
||||
void GetVersion(int32& major, int32& minor, int32& build, int32& revision) {
|
||||
major = 0;
|
||||
minor = 0;
|
||||
build = 0;
|
||||
revision = 1;
|
||||
}
|
||||
|
||||
} // namespace mkvmuxer
|
@ -1,18 +1,82 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
// 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_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"
|
||||
#ifndef MKVMUXERUTIL_HPP
|
||||
#define MKVMUXERUTIL_HPP
|
||||
|
||||
using mkvmuxer::EbmlElementSize;
|
||||
using mkvmuxer::EbmlMasterElementSize;
|
||||
#include "mkvmuxertypes.hpp"
|
||||
|
||||
#endif // LIBWEBM_MKVMUXERUTIL_HPP_
|
||||
namespace mkvmuxer {
|
||||
|
||||
class IMkvWriter;
|
||||
|
||||
const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL;
|
||||
|
||||
// Writes out |value| in Big Endian order. Returns 0 on success.
|
||||
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size);
|
||||
|
||||
// Returns the size in bytes of the element.
|
||||
uint64 EbmlMasterElementSize(uint64 type, uint64 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);
|
||||
|
||||
// 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, float value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer,
|
||||
uint64 type,
|
||||
const uint8* value,
|
||||
uint64 size);
|
||||
|
||||
// Output an Mkv Simple Block.
|
||||
// Inputs:
|
||||
// data: Pointer to the data.
|
||||
// length: Length of the data.
|
||||
// track_number: Track to add the data to. Value returned by Add track
|
||||
// functions.
|
||||
// timecode: Relative timecode of the Block.
|
||||
// is_key: Flag telling whether or not this frame is a key frame.
|
||||
uint64 WriteSimpleBlock(IMkvWriter* writer,
|
||||
const uint8* data,
|
||||
uint64 length,
|
||||
char track_number,
|
||||
short timecode,
|
||||
bool is_key);
|
||||
|
||||
// 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);
|
||||
|
||||
} //end namespace mkvmuxer
|
||||
|
||||
#endif // MKVMUXERUTIL_HPP
|
||||
|
9429
mkvparser.cpp
Normal file
9429
mkvparser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
911
mkvparser.hpp
911
mkvparser.hpp
@ -1,15 +1,896 @@
|
||||
// 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_
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVPARSER_HPP
|
||||
#define MKVPARSER_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
|
||||
namespace mkvparser
|
||||
{
|
||||
|
||||
const int E_FILE_FORMAT_INVALID = -2;
|
||||
const int E_BUFFER_NOT_FULL = -3;
|
||||
|
||||
class IMkvReader
|
||||
{
|
||||
public:
|
||||
virtual int Read(long long pos, long len, unsigned char* buf) = 0;
|
||||
virtual int Length(long long* total, long long* available) = 0;
|
||||
protected:
|
||||
virtual ~IMkvReader();
|
||||
};
|
||||
|
||||
long long GetUIntLength(IMkvReader*, long long, long&);
|
||||
long long ReadUInt(IMkvReader*, long long, long&);
|
||||
long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&);
|
||||
long long UnserializeUInt(IMkvReader*, long long pos, long long size);
|
||||
float Unserialize4Float(IMkvReader*, long long);
|
||||
double Unserialize8Double(IMkvReader*, long long);
|
||||
|
||||
#if 0
|
||||
short Unserialize2SInt(IMkvReader*, long long);
|
||||
signed char Unserialize1SInt(IMkvReader*, long long);
|
||||
#else
|
||||
long UnserializeInt(IMkvReader*, long long pos, long len, long long& result);
|
||||
#endif
|
||||
|
||||
long UnserializeString(IMkvReader*, long long pos, long long size, char*&);
|
||||
|
||||
long ParseElementHeader(
|
||||
IMkvReader* pReader,
|
||||
long long& pos, //consume id and size fields
|
||||
long long stop, //if you know size of element's parent
|
||||
long long& id,
|
||||
long long& size);
|
||||
|
||||
bool Match(IMkvReader*, long long&, unsigned long, long long&);
|
||||
bool Match(IMkvReader*, long long&, unsigned long, char*&);
|
||||
bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&);
|
||||
bool Match(IMkvReader*, long long&, unsigned long, double&);
|
||||
bool Match(IMkvReader*, long long&, unsigned long, short&);
|
||||
|
||||
void GetVersion(int& major, int& minor, int& build, int& revision);
|
||||
|
||||
struct EBMLHeader
|
||||
{
|
||||
EBMLHeader();
|
||||
~EBMLHeader();
|
||||
long long m_version;
|
||||
long long m_readVersion;
|
||||
long long m_maxIdLength;
|
||||
long long m_maxSizeLength;
|
||||
char* m_docType;
|
||||
long long m_docTypeVersion;
|
||||
long long m_docTypeReadVersion;
|
||||
|
||||
long long Parse(IMkvReader*, long long&);
|
||||
void Init();
|
||||
};
|
||||
|
||||
|
||||
class Segment;
|
||||
class Track;
|
||||
class Cluster;
|
||||
|
||||
class Block
|
||||
{
|
||||
Block(const Block&);
|
||||
Block& operator=(const Block&);
|
||||
|
||||
public:
|
||||
const long long m_start;
|
||||
const long long m_size;
|
||||
|
||||
Block(long long start, long long size);
|
||||
~Block();
|
||||
|
||||
long Parse(IMkvReader*);
|
||||
|
||||
long long GetTrackNumber() const;
|
||||
long long GetTimeCode(const Cluster*) const; //absolute, but not scaled
|
||||
long long GetTime(const Cluster*) const; //absolute, and scaled (ns)
|
||||
bool IsKey() const;
|
||||
void SetKey(bool);
|
||||
bool IsInvisible() const;
|
||||
|
||||
enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml };
|
||||
Lacing GetLacing() const;
|
||||
|
||||
int GetFrameCount() const; //to index frames: [0, count)
|
||||
|
||||
struct Frame
|
||||
{
|
||||
long long pos; //absolute offset
|
||||
long len;
|
||||
|
||||
long Read(IMkvReader*, unsigned char*) const;
|
||||
};
|
||||
|
||||
const Frame& GetFrame(int frame_index) const;
|
||||
|
||||
private:
|
||||
long long m_track; //Track::Number()
|
||||
short m_timecode; //relative to cluster
|
||||
unsigned char m_flags;
|
||||
|
||||
Frame* m_frames;
|
||||
int m_frame_count;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class BlockEntry
|
||||
{
|
||||
BlockEntry(const BlockEntry&);
|
||||
BlockEntry& operator=(const BlockEntry&);
|
||||
|
||||
protected:
|
||||
BlockEntry(Cluster*, long index);
|
||||
|
||||
public:
|
||||
virtual ~BlockEntry();
|
||||
|
||||
bool EOS() const;
|
||||
const Cluster* GetCluster() const;
|
||||
long GetIndex() const;
|
||||
virtual const Block* GetBlock() const = 0;
|
||||
|
||||
enum Kind { kBlockEOS, kBlockSimple, kBlockGroup };
|
||||
virtual Kind GetKind() const = 0;
|
||||
|
||||
protected:
|
||||
Cluster* const m_pCluster;
|
||||
const long m_index;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class SimpleBlock : public BlockEntry
|
||||
{
|
||||
SimpleBlock(const SimpleBlock&);
|
||||
SimpleBlock& operator=(const SimpleBlock&);
|
||||
|
||||
public:
|
||||
SimpleBlock(Cluster*, long index, long long start, long long size);
|
||||
long Parse();
|
||||
|
||||
Kind GetKind() const;
|
||||
const Block* GetBlock() const;
|
||||
|
||||
protected:
|
||||
Block m_block;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class BlockGroup : public BlockEntry
|
||||
{
|
||||
BlockGroup(const BlockGroup&);
|
||||
BlockGroup& operator=(const BlockGroup&);
|
||||
|
||||
public:
|
||||
BlockGroup(
|
||||
Cluster*,
|
||||
long index,
|
||||
long long block_start, //absolute pos of block's payload
|
||||
long long block_size, //size of block's payload
|
||||
long long prev,
|
||||
long long next,
|
||||
long long duration);
|
||||
|
||||
long Parse();
|
||||
|
||||
Kind GetKind() const;
|
||||
const Block* GetBlock() const;
|
||||
|
||||
long long GetPrevTimeCode() const; //relative to block's time
|
||||
long long GetNextTimeCode() const; //as above
|
||||
long long GetDuration() const;
|
||||
|
||||
private:
|
||||
Block m_block;
|
||||
const long long m_prev;
|
||||
const long long m_next;
|
||||
const long long m_duration;
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// ContentEncoding element
|
||||
// Elements used to describe if the track data has been encrypted or
|
||||
// compressed with zlib or header stripping.
|
||||
class ContentEncoding {
|
||||
public:
|
||||
ContentEncoding();
|
||||
~ContentEncoding();
|
||||
|
||||
// ContentCompression element names
|
||||
struct ContentCompression {
|
||||
ContentCompression();
|
||||
~ContentCompression();
|
||||
|
||||
unsigned long long algo;
|
||||
unsigned char* settings;
|
||||
};
|
||||
|
||||
// ContentEncryption element names
|
||||
struct ContentEncryption {
|
||||
ContentEncryption();
|
||||
~ContentEncryption();
|
||||
|
||||
unsigned long long algo;
|
||||
unsigned char* key_id;
|
||||
long long key_id_len;
|
||||
unsigned char* signature;
|
||||
long long signature_len;
|
||||
unsigned char* sig_key_id;
|
||||
long long sig_key_id_len;
|
||||
unsigned long long sig_algo;
|
||||
unsigned long long sig_hash_algo;
|
||||
};
|
||||
|
||||
// Returns ContentCompression represented by |idx|. Returns NULL if |idx|
|
||||
// is out of bounds.
|
||||
const ContentCompression* GetCompressionByIndex(unsigned long idx) const;
|
||||
|
||||
// Returns number of ContentCompression elements in this ContentEncoding
|
||||
// element.
|
||||
unsigned long GetCompressionCount() const;
|
||||
|
||||
// Returns ContentEncryption represented by |idx|. Returns NULL if |idx|
|
||||
// is out of bounds.
|
||||
const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const;
|
||||
|
||||
// Returns number of ContentEncryption elements in this ContentEncoding
|
||||
// element.
|
||||
unsigned long GetEncryptionCount() const;
|
||||
|
||||
// Parses the ContentEncoding element from |pReader|. |start| is the
|
||||
// starting offset of the ContentEncoding payload. |size| is the size in
|
||||
// bytes of the ContentEncoding payload. Returns true on success.
|
||||
bool ParseContentEncodingEntry(long long start,
|
||||
long long size,
|
||||
IMkvReader* const pReader);
|
||||
|
||||
// Parses the ContentEncryption element from |pReader|. |start| is the
|
||||
// starting offset of the ContentEncryption payload. |size| is the size in
|
||||
// bytes of the ContentEncryption payload. |encryption| is where the parsed
|
||||
// values will be stored.
|
||||
void ParseEncryptionEntry(long long start,
|
||||
long long size,
|
||||
IMkvReader* const pReader,
|
||||
ContentEncryption* const encryption);
|
||||
|
||||
unsigned long long encoding_order() const { return encoding_order_; }
|
||||
unsigned long long encoding_scope() const { return encoding_scope_; }
|
||||
unsigned long long encoding_type() const { return encoding_type_; }
|
||||
|
||||
private:
|
||||
// Member variables for list of ContentCompression elements.
|
||||
ContentCompression** compression_entries_;
|
||||
ContentCompression** compression_entries_end_;
|
||||
|
||||
// Member variables for list of ContentEncryption elements.
|
||||
ContentEncryption** encryption_entries_;
|
||||
ContentEncryption** encryption_entries_end_;
|
||||
|
||||
// ContentEncoding element names
|
||||
unsigned long long encoding_order_;
|
||||
unsigned long long encoding_scope_;
|
||||
unsigned long long encoding_type_;
|
||||
|
||||
// LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
|
||||
ContentEncoding(const ContentEncoding&);
|
||||
ContentEncoding& operator=(const ContentEncoding&);
|
||||
};
|
||||
|
||||
class Track
|
||||
{
|
||||
Track(const Track&);
|
||||
Track& operator=(const Track&);
|
||||
|
||||
public:
|
||||
enum Type { kVideo = 1, kAudio = 2 };
|
||||
|
||||
Segment* const m_pSegment;
|
||||
const long long m_element_start;
|
||||
const long long m_element_size;
|
||||
virtual ~Track();
|
||||
|
||||
long long GetType() const;
|
||||
long long GetNumber() const;
|
||||
unsigned long long GetUid() const;
|
||||
const char* GetNameAsUTF8() const;
|
||||
const char* GetCodecNameAsUTF8() const;
|
||||
const char* GetCodecId() const;
|
||||
const unsigned char* GetCodecPrivate(size_t&) const;
|
||||
bool GetLacing() const;
|
||||
|
||||
const BlockEntry* GetEOS() const;
|
||||
|
||||
struct Settings
|
||||
{
|
||||
long long start;
|
||||
long long size;
|
||||
};
|
||||
|
||||
struct Info
|
||||
{
|
||||
long long type;
|
||||
long long number;
|
||||
unsigned long long uid;
|
||||
char* nameAsUTF8;
|
||||
char* codecId;
|
||||
unsigned char* codecPrivate;
|
||||
size_t codecPrivateSize;
|
||||
char* codecNameAsUTF8;
|
||||
bool lacing;
|
||||
Settings settings;
|
||||
|
||||
Info();
|
||||
void Clear();
|
||||
};
|
||||
|
||||
long GetFirst(const BlockEntry*&) const;
|
||||
long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
|
||||
virtual bool VetEntry(const BlockEntry*) const = 0;
|
||||
virtual long Seek(long long time_ns, const BlockEntry*&) const = 0;
|
||||
|
||||
const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const;
|
||||
unsigned long GetContentEncodingCount() const;
|
||||
|
||||
void ParseContentEncodingsEntry(long long start, long long size);
|
||||
|
||||
protected:
|
||||
Track(
|
||||
Segment*,
|
||||
const Info&,
|
||||
long long element_start,
|
||||
long long element_size);
|
||||
|
||||
const Info m_info;
|
||||
|
||||
class EOSBlock : public BlockEntry
|
||||
{
|
||||
public:
|
||||
EOSBlock();
|
||||
|
||||
Kind GetKind() const;
|
||||
const Block* GetBlock() const;
|
||||
};
|
||||
|
||||
EOSBlock m_eos;
|
||||
|
||||
private:
|
||||
ContentEncoding** content_encoding_entries_;
|
||||
ContentEncoding** content_encoding_entries_end_;
|
||||
};
|
||||
|
||||
|
||||
class VideoTrack : public Track
|
||||
{
|
||||
VideoTrack(const VideoTrack&);
|
||||
VideoTrack& operator=(const VideoTrack&);
|
||||
|
||||
VideoTrack(
|
||||
Segment*,
|
||||
const Info&,
|
||||
long long element_start,
|
||||
long long element_size);
|
||||
|
||||
public:
|
||||
static long Parse(
|
||||
Segment*,
|
||||
const Info&,
|
||||
long long element_start,
|
||||
long long element_size,
|
||||
VideoTrack*&);
|
||||
|
||||
long long GetWidth() const;
|
||||
long long GetHeight() const;
|
||||
double GetFrameRate() const;
|
||||
|
||||
bool VetEntry(const BlockEntry*) const;
|
||||
long Seek(long long time_ns, const BlockEntry*&) const;
|
||||
|
||||
private:
|
||||
long long m_width;
|
||||
long long m_height;
|
||||
double m_rate;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class AudioTrack : public Track
|
||||
{
|
||||
AudioTrack(const AudioTrack&);
|
||||
AudioTrack& operator=(const AudioTrack&);
|
||||
|
||||
AudioTrack(
|
||||
Segment*,
|
||||
const Info&,
|
||||
long long element_start,
|
||||
long long element_size);
|
||||
public:
|
||||
static long Parse(
|
||||
Segment*,
|
||||
const Info&,
|
||||
long long element_start,
|
||||
long long element_size,
|
||||
AudioTrack*&);
|
||||
|
||||
double GetSamplingRate() const;
|
||||
long long GetChannels() const;
|
||||
long long GetBitDepth() const;
|
||||
bool VetEntry(const BlockEntry*) const;
|
||||
long Seek(long long time_ns, const BlockEntry*&) const;
|
||||
|
||||
private:
|
||||
double m_rate;
|
||||
long long m_channels;
|
||||
long long m_bitDepth;
|
||||
};
|
||||
|
||||
|
||||
class Tracks
|
||||
{
|
||||
Tracks(const Tracks&);
|
||||
Tracks& operator=(const Tracks&);
|
||||
|
||||
public:
|
||||
Segment* const m_pSegment;
|
||||
const long long m_start;
|
||||
const long long m_size;
|
||||
const long long m_element_start;
|
||||
const long long m_element_size;
|
||||
|
||||
Tracks(
|
||||
Segment*,
|
||||
long long start,
|
||||
long long size,
|
||||
long long element_start,
|
||||
long long element_size);
|
||||
|
||||
~Tracks();
|
||||
|
||||
long Parse();
|
||||
|
||||
unsigned long GetTracksCount() const;
|
||||
|
||||
const Track* GetTrackByNumber(unsigned long tn) const;
|
||||
const Track* GetTrackByIndex(unsigned long idx) const;
|
||||
|
||||
private:
|
||||
Track** m_trackEntries;
|
||||
Track** m_trackEntriesEnd;
|
||||
|
||||
long ParseTrackEntry(
|
||||
long long payload_start,
|
||||
long long payload_size,
|
||||
long long element_start,
|
||||
long long element_size,
|
||||
Track*&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class SegmentInfo
|
||||
{
|
||||
SegmentInfo(const SegmentInfo&);
|
||||
SegmentInfo& operator=(const SegmentInfo&);
|
||||
|
||||
public:
|
||||
Segment* const m_pSegment;
|
||||
const long long m_start;
|
||||
const long long m_size;
|
||||
const long long m_element_start;
|
||||
const long long m_element_size;
|
||||
|
||||
SegmentInfo(
|
||||
Segment*,
|
||||
long long start,
|
||||
long long size,
|
||||
long long element_start,
|
||||
long long element_size);
|
||||
|
||||
~SegmentInfo();
|
||||
|
||||
long Parse();
|
||||
|
||||
long long GetTimeCodeScale() const;
|
||||
long long GetDuration() const; //scaled
|
||||
const char* GetMuxingAppAsUTF8() const;
|
||||
const char* GetWritingAppAsUTF8() const;
|
||||
const char* GetTitleAsUTF8() const;
|
||||
|
||||
private:
|
||||
long long m_timecodeScale;
|
||||
double m_duration;
|
||||
char* m_pMuxingAppAsUTF8;
|
||||
char* m_pWritingAppAsUTF8;
|
||||
char* m_pTitleAsUTF8;
|
||||
};
|
||||
|
||||
|
||||
class SeekHead
|
||||
{
|
||||
SeekHead(const SeekHead&);
|
||||
SeekHead& operator=(const SeekHead&);
|
||||
|
||||
public:
|
||||
Segment* const m_pSegment;
|
||||
const long long m_start;
|
||||
const long long m_size;
|
||||
const long long m_element_start;
|
||||
const long long m_element_size;
|
||||
|
||||
SeekHead(
|
||||
Segment*,
|
||||
long long start,
|
||||
long long size,
|
||||
long long element_start,
|
||||
long long element_size);
|
||||
|
||||
~SeekHead();
|
||||
|
||||
long Parse();
|
||||
|
||||
struct Entry
|
||||
{
|
||||
//the SeekHead entry payload
|
||||
long long id;
|
||||
long long pos;
|
||||
|
||||
//absolute pos of SeekEntry ID
|
||||
long long element_start;
|
||||
|
||||
//SeekEntry ID size + size size + payload
|
||||
long long element_size;
|
||||
};
|
||||
|
||||
int GetCount() const;
|
||||
const Entry* GetEntry(int idx) const;
|
||||
|
||||
struct VoidElement
|
||||
{
|
||||
//absolute pos of Void ID
|
||||
long long element_start;
|
||||
|
||||
//ID size + size size + payload size
|
||||
long long element_size;
|
||||
};
|
||||
|
||||
int GetVoidElementCount() const;
|
||||
const VoidElement* GetVoidElement(int idx) const;
|
||||
|
||||
private:
|
||||
Entry* m_entries;
|
||||
int m_entry_count;
|
||||
|
||||
VoidElement* m_void_elements;
|
||||
int m_void_element_count;
|
||||
|
||||
static bool ParseEntry(
|
||||
IMkvReader*,
|
||||
long long pos, //payload
|
||||
long long size,
|
||||
Entry*);
|
||||
|
||||
};
|
||||
|
||||
class Cues;
|
||||
class CuePoint
|
||||
{
|
||||
friend class Cues;
|
||||
|
||||
CuePoint(long, long long);
|
||||
~CuePoint();
|
||||
|
||||
CuePoint(const CuePoint&);
|
||||
CuePoint& operator=(const CuePoint&);
|
||||
|
||||
public:
|
||||
long long m_element_start;
|
||||
long long m_element_size;
|
||||
|
||||
void Load(IMkvReader*);
|
||||
|
||||
long long GetTimeCode() const; //absolute but unscaled
|
||||
long long GetTime(const Segment*) const; //absolute and scaled (ns units)
|
||||
|
||||
struct TrackPosition
|
||||
{
|
||||
long long m_track;
|
||||
long long m_pos; //of cluster
|
||||
long long m_block;
|
||||
//codec_state //defaults to 0
|
||||
//reference = clusters containing req'd referenced blocks
|
||||
// reftime = timecode of the referenced block
|
||||
|
||||
void Parse(IMkvReader*, long long, long long);
|
||||
};
|
||||
|
||||
const TrackPosition* Find(const Track*) const;
|
||||
|
||||
private:
|
||||
const long m_index;
|
||||
long long m_timecode;
|
||||
TrackPosition* m_track_positions;
|
||||
size_t m_track_positions_count;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Cues
|
||||
{
|
||||
friend class Segment;
|
||||
|
||||
Cues(
|
||||
Segment*,
|
||||
long long start,
|
||||
long long size,
|
||||
long long element_start,
|
||||
long long element_size);
|
||||
~Cues();
|
||||
|
||||
Cues(const Cues&);
|
||||
Cues& operator=(const Cues&);
|
||||
|
||||
public:
|
||||
Segment* const m_pSegment;
|
||||
const long long m_start;
|
||||
const long long m_size;
|
||||
const long long m_element_start;
|
||||
const long long m_element_size;
|
||||
|
||||
bool Find( //lower bound of time_ns
|
||||
long long time_ns,
|
||||
const Track*,
|
||||
const CuePoint*&,
|
||||
const CuePoint::TrackPosition*&) const;
|
||||
|
||||
#if 0
|
||||
bool FindNext( //upper_bound of time_ns
|
||||
long long time_ns,
|
||||
const Track*,
|
||||
const CuePoint*&,
|
||||
const CuePoint::TrackPosition*&) const;
|
||||
#endif
|
||||
|
||||
const CuePoint* GetFirst() const;
|
||||
const CuePoint* GetLast() const;
|
||||
const CuePoint* GetNext(const CuePoint*) const;
|
||||
|
||||
const BlockEntry* GetBlock(
|
||||
const CuePoint*,
|
||||
const CuePoint::TrackPosition*) const;
|
||||
|
||||
bool LoadCuePoint() const;
|
||||
long GetCount() const; //loaded only
|
||||
//long GetTotal() const; //loaded + preloaded
|
||||
bool DoneParsing() const;
|
||||
|
||||
private:
|
||||
void Init() const;
|
||||
void PreloadCuePoint(long&, long long) const;
|
||||
|
||||
mutable CuePoint** m_cue_points;
|
||||
mutable long m_count;
|
||||
mutable long m_preload_count;
|
||||
mutable long long m_pos;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Cluster
|
||||
{
|
||||
friend class Segment;
|
||||
|
||||
Cluster(const Cluster&);
|
||||
Cluster& operator=(const Cluster&);
|
||||
|
||||
public:
|
||||
Segment* const m_pSegment;
|
||||
|
||||
public:
|
||||
static Cluster* Create(
|
||||
Segment*,
|
||||
long index, //index in segment
|
||||
long long off); //offset relative to segment
|
||||
//long long element_size);
|
||||
|
||||
Cluster(); //EndOfStream
|
||||
~Cluster();
|
||||
|
||||
bool EOS() const;
|
||||
|
||||
long long GetTimeCode() const; //absolute, but not scaled
|
||||
long long GetTime() const; //absolute, and scaled (nanosecond units)
|
||||
long long GetFirstTime() const; //time (ns) of first (earliest) block
|
||||
long long GetLastTime() const; //time (ns) of last (latest) block
|
||||
|
||||
long GetFirst(const BlockEntry*&) const;
|
||||
long GetLast(const BlockEntry*&) const;
|
||||
long GetNext(const BlockEntry* curr, const BlockEntry*& next) const;
|
||||
|
||||
const BlockEntry* GetEntry(const Track*, long long ns = -1) const;
|
||||
const BlockEntry* GetEntry(
|
||||
const CuePoint&,
|
||||
const CuePoint::TrackPosition&) const;
|
||||
//const BlockEntry* GetMaxKey(const VideoTrack*) const;
|
||||
|
||||
// static bool HasBlockEntries(const Segment*, long long);
|
||||
|
||||
static long HasBlockEntries(
|
||||
const Segment*,
|
||||
long long idoff,
|
||||
long long& pos,
|
||||
long& size);
|
||||
|
||||
long GetEntryCount() const;
|
||||
|
||||
long Load(long long& pos, long& size) const;
|
||||
|
||||
long Parse(long long& pos, long& size) const;
|
||||
long GetEntry(long index, const mkvparser::BlockEntry*&) const;
|
||||
|
||||
protected:
|
||||
Cluster(
|
||||
Segment*,
|
||||
long index,
|
||||
long long element_start);
|
||||
//long long element_size);
|
||||
|
||||
public:
|
||||
const long long m_element_start;
|
||||
long long GetPosition() const; //offset relative to segment
|
||||
|
||||
long GetIndex() const;
|
||||
long long GetElementSize() const;
|
||||
//long long GetPayloadSize() const;
|
||||
|
||||
//long long Unparsed() const;
|
||||
|
||||
private:
|
||||
long m_index;
|
||||
mutable long long m_pos;
|
||||
//mutable long long m_size;
|
||||
mutable long long m_element_size;
|
||||
mutable long long m_timecode;
|
||||
mutable BlockEntry** m_entries;
|
||||
mutable long m_entries_size;
|
||||
mutable long m_entries_count;
|
||||
|
||||
long ParseSimpleBlock(long long, long long&, long&);
|
||||
long ParseBlockGroup(long long, long long&, long&);
|
||||
|
||||
long CreateBlock(long long id, long long pos, long long size);
|
||||
long CreateBlockGroup(long long, long long);
|
||||
long CreateSimpleBlock(long long, long long);
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Segment
|
||||
{
|
||||
friend class Cues;
|
||||
friend class VideoTrack;
|
||||
friend class AudioTrack;
|
||||
|
||||
Segment(const Segment&);
|
||||
Segment& operator=(const Segment&);
|
||||
|
||||
private:
|
||||
Segment(
|
||||
IMkvReader*,
|
||||
long long elem_start,
|
||||
//long long elem_size,
|
||||
long long pos,
|
||||
long long size);
|
||||
|
||||
public:
|
||||
IMkvReader* const m_pReader;
|
||||
const long long m_element_start;
|
||||
//const long long m_element_size;
|
||||
const long long m_start; //posn of segment payload
|
||||
const long long m_size; //size of segment payload
|
||||
Cluster m_eos; //TODO: make private?
|
||||
|
||||
static long long CreateInstance(IMkvReader*, long long, Segment*&);
|
||||
~Segment();
|
||||
|
||||
long Load(); //loads headers and all clusters
|
||||
|
||||
//for incremental loading
|
||||
//long long Unparsed() const;
|
||||
bool DoneParsing() const;
|
||||
long long ParseHeaders(); //stops when first cluster is found
|
||||
//long FindNextCluster(long long& pos, long& size) const;
|
||||
long LoadCluster(long long& pos, long& size); //load one cluster
|
||||
long LoadCluster();
|
||||
|
||||
long ParseNext(
|
||||
const Cluster* pCurr,
|
||||
const Cluster*& pNext,
|
||||
long long& pos,
|
||||
long& size);
|
||||
|
||||
#if 0
|
||||
//This pair parses one cluster, but only changes the state of the
|
||||
//segment object when the cluster is actually added to the index.
|
||||
long ParseCluster(long long& cluster_pos, long long& new_pos) const;
|
||||
bool AddCluster(long long cluster_pos, long long new_pos);
|
||||
#endif
|
||||
|
||||
const SeekHead* GetSeekHead() const;
|
||||
const Tracks* GetTracks() const;
|
||||
const SegmentInfo* GetInfo() const;
|
||||
const Cues* GetCues() const;
|
||||
|
||||
long long GetDuration() const;
|
||||
|
||||
unsigned long GetCount() const;
|
||||
const Cluster* GetFirst() const;
|
||||
const Cluster* GetLast() const;
|
||||
const Cluster* GetNext(const Cluster*);
|
||||
|
||||
const Cluster* FindCluster(long long time_nanoseconds) const;
|
||||
//const BlockEntry* Seek(long long time_nanoseconds, const Track*) const;
|
||||
|
||||
const Cluster* FindOrPreloadCluster(long long pos);
|
||||
|
||||
long ParseCues(
|
||||
long long cues_off, //offset relative to start of segment
|
||||
long long& parse_pos,
|
||||
long& parse_len);
|
||||
|
||||
private:
|
||||
|
||||
long long m_pos; //absolute file posn; what has been consumed so far
|
||||
Cluster* m_pUnknownSize;
|
||||
|
||||
SeekHead* m_pSeekHead;
|
||||
SegmentInfo* m_pInfo;
|
||||
Tracks* m_pTracks;
|
||||
Cues* m_pCues;
|
||||
Cluster** m_clusters;
|
||||
long m_clusterCount; //number of entries for which m_index >= 0
|
||||
long m_clusterPreloadCount; //number of entries for which m_index < 0
|
||||
long m_clusterSize; //array size
|
||||
|
||||
long DoLoadCluster(long long&, long&);
|
||||
long DoLoadClusterUnknownSize(long long&, long&);
|
||||
long DoParseNext(const Cluster*&, long long&, long&);
|
||||
|
||||
void AppendCluster(Cluster*);
|
||||
void PreloadCluster(Cluster*, ptrdiff_t);
|
||||
|
||||
//void ParseSeekHead(long long pos, long long size);
|
||||
//void ParseSeekEntry(long long pos, long long size);
|
||||
//void ParseCues(long long);
|
||||
|
||||
const BlockEntry* GetBlock(
|
||||
const CuePoint&,
|
||||
const CuePoint::TrackPosition&);
|
||||
|
||||
};
|
||||
|
||||
} //end namespace mkvparser
|
||||
|
||||
inline long mkvparser::Segment::LoadCluster()
|
||||
{
|
||||
long long pos;
|
||||
long size;
|
||||
|
||||
return LoadCluster(pos, size);
|
||||
}
|
||||
|
||||
#endif //MKVPARSER_HPP
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,133 +0,0 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "mkvparser/mkvreader.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace mkvparser {
|
||||
|
||||
MkvReader::MkvReader() : m_file(NULL), reader_owns_file_(true) {}
|
||||
|
||||
MkvReader::MkvReader(FILE* fp) : m_file(fp), reader_owns_file_(false) {
|
||||
GetFileSize();
|
||||
}
|
||||
|
||||
MkvReader::~MkvReader() {
|
||||
if (reader_owns_file_)
|
||||
Close();
|
||||
m_file = NULL;
|
||||
}
|
||||
|
||||
int MkvReader::Open(const char* fileName) {
|
||||
if (fileName == NULL)
|
||||
return -1;
|
||||
|
||||
if (m_file)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
const errno_t e = fopen_s(&m_file, fileName, "rb");
|
||||
|
||||
if (e)
|
||||
return -1; // error
|
||||
#else
|
||||
m_file = fopen(fileName, "rb");
|
||||
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
#endif
|
||||
return !GetFileSize();
|
||||
}
|
||||
|
||||
bool MkvReader::GetFileSize() {
|
||||
if (m_file == NULL)
|
||||
return false;
|
||||
#ifdef _MSC_VER
|
||||
int status = _fseeki64(m_file, 0L, SEEK_END);
|
||||
|
||||
if (status)
|
||||
return false; // error
|
||||
|
||||
m_length = _ftelli64(m_file);
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_END);
|
||||
m_length = ftell(m_file);
|
||||
#endif
|
||||
assert(m_length >= 0);
|
||||
|
||||
if (m_length < 0)
|
||||
return false;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
status = _fseeki64(m_file, 0L, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return false; // error
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_SET);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MkvReader::Close() {
|
||||
if (m_file != NULL) {
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int MkvReader::Length(long long* total, long long* available) {
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (total)
|
||||
*total = m_length;
|
||||
|
||||
if (available)
|
||||
*available = m_length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MkvReader::Read(long long offset, long len, unsigned char* buffer) {
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (offset >= m_length)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
const int status = _fseeki64(m_file, offset, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return -1; // error
|
||||
#else
|
||||
fseeko(m_file, static_cast<off_t>(offset), SEEK_SET);
|
||||
#endif
|
||||
|
||||
const size_t size = fread(buffer, 1, len, m_file);
|
||||
|
||||
if (size < size_t(len))
|
||||
return -1; // error
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
} // namespace mkvparser
|
@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef MKVPARSER_MKVREADER_H_
|
||||
#define MKVPARSER_MKVREADER_H_
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "mkvparser/mkvparser.h"
|
||||
|
||||
namespace mkvparser {
|
||||
|
||||
class MkvReader : public IMkvReader {
|
||||
public:
|
||||
MkvReader();
|
||||
explicit MkvReader(FILE* fp);
|
||||
virtual ~MkvReader();
|
||||
|
||||
int Open(const char*);
|
||||
void Close();
|
||||
|
||||
virtual int Read(long long position, long length, unsigned char* buffer);
|
||||
virtual int Length(long long* total, long long* available);
|
||||
|
||||
private:
|
||||
MkvReader(const MkvReader&);
|
||||
MkvReader& operator=(const MkvReader&);
|
||||
|
||||
// Determines the size of the file. This is called either by the constructor
|
||||
// or by the Open function depending on file ownership. Returns true on
|
||||
// success.
|
||||
bool GetFileSize();
|
||||
|
||||
long long m_length;
|
||||
FILE* m_file;
|
||||
bool reader_owns_file_;
|
||||
};
|
||||
|
||||
} // namespace mkvparser
|
||||
|
||||
#endif // MKVPARSER_MKVREADER_H_
|
@ -1,459 +0,0 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
//
|
||||
// This sample application demonstrates how to use the Matroska parser
|
||||
// library, which allows clients to handle a Matroska format file.
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
|
||||
namespace {
|
||||
const wchar_t* utf8towcs(const char* str) {
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
// TODO: this probably requires that the locale be
|
||||
// configured somehow:
|
||||
|
||||
const size_t size = mbstowcs(NULL, str, 0);
|
||||
|
||||
if (size == 0 || size == static_cast<size_t>(-1))
|
||||
return NULL;
|
||||
|
||||
wchar_t* const val = new (std::nothrow) wchar_t[size + 1];
|
||||
if (val == NULL)
|
||||
return NULL;
|
||||
|
||||
mbstowcs(val, str, size);
|
||||
val[size] = L'\0';
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
bool InputHasCues(const mkvparser::Segment* const segment) {
|
||||
const mkvparser::Cues* const cues = segment->GetCues();
|
||||
if (cues == NULL)
|
||||
return false;
|
||||
|
||||
while (!cues->DoneParsing())
|
||||
cues->LoadCuePoint();
|
||||
|
||||
const mkvparser::CuePoint* const cue_point = cues->GetFirst();
|
||||
if (cue_point == NULL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MasteringMetadataValuePresent(double value) {
|
||||
return value != mkvparser::MasteringMetadata::kValueNotPresent;
|
||||
}
|
||||
|
||||
bool ColourValuePresent(long long value) {
|
||||
return value != mkvparser::Colour::kValueNotPresent;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc == 1) {
|
||||
printf("Mkv Parser Sample Application\n");
|
||||
printf(" Usage: %s <input file> \n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mkvparser::MkvReader reader;
|
||||
|
||||
if (reader.Open(argv[1])) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int maj, min, build, rev;
|
||||
|
||||
mkvparser::GetVersion(maj, min, build, rev);
|
||||
printf("\t\t libwebm version: %d.%d.%d.%d\n", maj, min, build, rev);
|
||||
|
||||
long long pos = 0;
|
||||
|
||||
mkvparser::EBMLHeader ebmlHeader;
|
||||
|
||||
long long ret = ebmlHeader.Parse(&reader, pos);
|
||||
if (ret < 0) {
|
||||
printf("\n EBMLHeader::Parse() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("\t\t\t EBML Header\n");
|
||||
printf("\t\tEBML Version\t\t: %lld\n", ebmlHeader.m_version);
|
||||
printf("\t\tEBML MaxIDLength\t: %lld\n", ebmlHeader.m_maxIdLength);
|
||||
printf("\t\tEBML MaxSizeLength\t: %lld\n", ebmlHeader.m_maxSizeLength);
|
||||
printf("\t\tDoc Type\t\t: %s\n", ebmlHeader.m_docType);
|
||||
printf("\t\tPos\t\t\t: %lld\n", pos);
|
||||
|
||||
typedef mkvparser::Segment seg_t;
|
||||
seg_t* pSegment_;
|
||||
|
||||
ret = seg_t::CreateInstance(&reader, pos, pSegment_);
|
||||
if (ret) {
|
||||
printf("\n Segment::CreateInstance() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const std::unique_ptr<seg_t> pSegment(pSegment_);
|
||||
|
||||
ret = pSegment->Load();
|
||||
if (ret < 0) {
|
||||
printf("\n Segment::Load() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const mkvparser::SegmentInfo* const pSegmentInfo = pSegment->GetInfo();
|
||||
if (pSegmentInfo == NULL) {
|
||||
printf("\n Segment::GetInfo() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const long long timeCodeScale = pSegmentInfo->GetTimeCodeScale();
|
||||
const long long duration_ns = pSegmentInfo->GetDuration();
|
||||
|
||||
const char* const pTitle_ = pSegmentInfo->GetTitleAsUTF8();
|
||||
const wchar_t* const pTitle = utf8towcs(pTitle_);
|
||||
|
||||
const char* const pMuxingApp_ = pSegmentInfo->GetMuxingAppAsUTF8();
|
||||
const wchar_t* const pMuxingApp = utf8towcs(pMuxingApp_);
|
||||
|
||||
const char* const pWritingApp_ = pSegmentInfo->GetWritingAppAsUTF8();
|
||||
const wchar_t* const pWritingApp = utf8towcs(pWritingApp_);
|
||||
|
||||
printf("\n");
|
||||
printf("\t\t\t Segment Info\n");
|
||||
printf("\t\tTimeCodeScale\t\t: %lld \n", timeCodeScale);
|
||||
printf("\t\tDuration\t\t: %lld\n", duration_ns);
|
||||
|
||||
const double duration_sec = double(duration_ns) / 1000000000;
|
||||
printf("\t\tDuration(secs)\t\t: %7.3lf\n", duration_sec);
|
||||
|
||||
if (pTitle == NULL)
|
||||
printf("\t\tTrack Name\t\t: NULL\n");
|
||||
else {
|
||||
printf("\t\tTrack Name\t\t: %ls\n", pTitle);
|
||||
delete[] pTitle;
|
||||
}
|
||||
|
||||
if (pMuxingApp == NULL)
|
||||
printf("\t\tMuxing App\t\t: NULL\n");
|
||||
else {
|
||||
printf("\t\tMuxing App\t\t: %ls\n", pMuxingApp);
|
||||
delete[] pMuxingApp;
|
||||
}
|
||||
|
||||
if (pWritingApp == NULL)
|
||||
printf("\t\tWriting App\t\t: NULL\n");
|
||||
else {
|
||||
printf("\t\tWriting App\t\t: %ls\n", pWritingApp);
|
||||
delete[] pWritingApp;
|
||||
}
|
||||
|
||||
// pos of segment payload
|
||||
printf("\t\tPosition(Segment)\t: %lld\n", pSegment->m_start);
|
||||
|
||||
// size of segment payload
|
||||
printf("\t\tSize(Segment)\t\t: %lld\n", pSegment->m_size);
|
||||
|
||||
const mkvparser::Tracks* pTracks = pSegment->GetTracks();
|
||||
|
||||
unsigned long track_num = 0;
|
||||
const unsigned long num_tracks = pTracks->GetTracksCount();
|
||||
|
||||
printf("\n\t\t\t Track Info\n");
|
||||
|
||||
while (track_num != num_tracks) {
|
||||
const mkvparser::Track* const pTrack =
|
||||
pTracks->GetTrackByIndex(track_num++);
|
||||
|
||||
if (pTrack == NULL)
|
||||
continue;
|
||||
|
||||
const long trackType = pTrack->GetType();
|
||||
const long trackNumber = pTrack->GetNumber();
|
||||
const unsigned long long trackUid = pTrack->GetUid();
|
||||
const wchar_t* const pTrackName = utf8towcs(pTrack->GetNameAsUTF8());
|
||||
|
||||
printf("\t\tTrack Type\t\t: %ld\n", trackType);
|
||||
printf("\t\tTrack Number\t\t: %ld\n", trackNumber);
|
||||
printf("\t\tTrack Uid\t\t: %lld\n", trackUid);
|
||||
|
||||
if (pTrackName == NULL)
|
||||
printf("\t\tTrack Name\t\t: NULL\n");
|
||||
else {
|
||||
printf("\t\tTrack Name\t\t: %ls \n", pTrackName);
|
||||
delete[] pTrackName;
|
||||
}
|
||||
|
||||
const char* const pCodecId = pTrack->GetCodecId();
|
||||
|
||||
if (pCodecId == NULL)
|
||||
printf("\t\tCodec Id\t\t: NULL\n");
|
||||
else
|
||||
printf("\t\tCodec Id\t\t: %s\n", pCodecId);
|
||||
|
||||
size_t codec_private_size = 0;
|
||||
if (pTrack->GetCodecPrivate(codec_private_size)) {
|
||||
printf("\t\tCodec private length: %u bytes\n",
|
||||
static_cast<unsigned int>(codec_private_size));
|
||||
}
|
||||
|
||||
const char* const pCodecName_ = pTrack->GetCodecNameAsUTF8();
|
||||
const wchar_t* const pCodecName = utf8towcs(pCodecName_);
|
||||
|
||||
if (pCodecName == NULL)
|
||||
printf("\t\tCodec Name\t\t: NULL\n");
|
||||
else {
|
||||
printf("\t\tCodec Name\t\t: %ls\n", pCodecName);
|
||||
delete[] pCodecName;
|
||||
}
|
||||
|
||||
if (trackType == mkvparser::Track::kVideo) {
|
||||
const mkvparser::VideoTrack* const pVideoTrack =
|
||||
static_cast<const mkvparser::VideoTrack*>(pTrack);
|
||||
|
||||
const long long width = pVideoTrack->GetWidth();
|
||||
printf("\t\tVideo Width\t\t: %lld\n", width);
|
||||
|
||||
const long long height = pVideoTrack->GetHeight();
|
||||
printf("\t\tVideo Height\t\t: %lld\n", height);
|
||||
|
||||
const double rate = pVideoTrack->GetFrameRate();
|
||||
printf("\t\tVideo Rate\t\t: %f\n", rate);
|
||||
|
||||
const mkvparser::Colour* const colour = pVideoTrack->GetColour();
|
||||
if (colour) {
|
||||
printf("\t\tVideo Colour:\n");
|
||||
if (ColourValuePresent(colour->matrix_coefficients))
|
||||
printf("\t\t\tMatrixCoefficients: %lld\n",
|
||||
colour->matrix_coefficients);
|
||||
if (ColourValuePresent(colour->bits_per_channel))
|
||||
printf("\t\t\tBitsPerChannel: %lld\n", colour->bits_per_channel);
|
||||
if (ColourValuePresent(colour->chroma_subsampling_horz))
|
||||
printf("\t\t\tChromaSubsamplingHorz: %lld\n",
|
||||
colour->chroma_subsampling_horz);
|
||||
if (ColourValuePresent(colour->chroma_subsampling_vert))
|
||||
printf("\t\t\tChromaSubsamplingVert: %lld\n",
|
||||
colour->chroma_subsampling_vert);
|
||||
if (ColourValuePresent(colour->cb_subsampling_horz))
|
||||
printf("\t\t\tCbSubsamplingHorz: %lld\n",
|
||||
colour->cb_subsampling_horz);
|
||||
if (ColourValuePresent(colour->cb_subsampling_vert))
|
||||
printf("\t\t\tCbSubsamplingVert: %lld\n",
|
||||
colour->cb_subsampling_vert);
|
||||
if (ColourValuePresent(colour->chroma_siting_horz))
|
||||
printf("\t\t\tChromaSitingHorz: %lld\n", colour->chroma_siting_horz);
|
||||
if (ColourValuePresent(colour->chroma_siting_vert))
|
||||
printf("\t\t\tChromaSitingVert: %lld\n", colour->chroma_siting_vert);
|
||||
if (ColourValuePresent(colour->range))
|
||||
printf("\t\t\tRange: %lld\n", colour->range);
|
||||
if (ColourValuePresent(colour->transfer_characteristics))
|
||||
printf("\t\t\tTransferCharacteristics: %lld\n",
|
||||
colour->transfer_characteristics);
|
||||
if (ColourValuePresent(colour->primaries))
|
||||
printf("\t\t\tPrimaries: %lld\n", colour->primaries);
|
||||
if (ColourValuePresent(colour->max_cll))
|
||||
printf("\t\t\tMaxCLL: %lld\n", colour->max_cll);
|
||||
if (ColourValuePresent(colour->max_fall))
|
||||
printf("\t\t\tMaxFALL: %lld\n", colour->max_fall);
|
||||
if (colour->mastering_metadata) {
|
||||
const mkvparser::MasteringMetadata* const mm =
|
||||
colour->mastering_metadata;
|
||||
printf("\t\t\tMastering Metadata:\n");
|
||||
if (MasteringMetadataValuePresent(mm->luminance_max))
|
||||
printf("\t\t\t\tLuminanceMax: %f\n", mm->luminance_max);
|
||||
if (MasteringMetadataValuePresent(mm->luminance_min))
|
||||
printf("\t\t\t\tLuminanceMin: %f\n", mm->luminance_min);
|
||||
if (mm->r) {
|
||||
printf("\t\t\t\t\tPrimaryRChromaticityX: %f\n", mm->r->x);
|
||||
printf("\t\t\t\t\tPrimaryRChromaticityY: %f\n", mm->r->y);
|
||||
}
|
||||
if (mm->g) {
|
||||
printf("\t\t\t\t\tPrimaryGChromaticityX: %f\n", mm->g->x);
|
||||
printf("\t\t\t\t\tPrimaryGChromaticityY: %f\n", mm->g->y);
|
||||
}
|
||||
if (mm->b) {
|
||||
printf("\t\t\t\t\tPrimaryBChromaticityX: %f\n", mm->b->x);
|
||||
printf("\t\t\t\t\tPrimaryBChromaticityY: %f\n", mm->b->y);
|
||||
}
|
||||
if (mm->white_point) {
|
||||
printf("\t\t\t\t\tWhitePointChromaticityX: %f\n",
|
||||
mm->white_point->x);
|
||||
printf("\t\t\t\t\tWhitePointChromaticityY: %f\n",
|
||||
mm->white_point->y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mkvparser::Projection* const projection =
|
||||
pVideoTrack->GetProjection();
|
||||
if (projection) {
|
||||
printf("\t\tVideo Projection:\n");
|
||||
if (projection->type != mkvparser::Projection::kTypeNotPresent)
|
||||
printf("\t\t\tProjectionType: %d\n",
|
||||
static_cast<int>(projection->type));
|
||||
if (projection->private_data) {
|
||||
printf("\t\t\tProjectionPrivate: %u bytes\n",
|
||||
static_cast<unsigned int>(projection->private_data_length));
|
||||
}
|
||||
if (projection->pose_yaw != mkvparser::Projection::kValueNotPresent)
|
||||
printf("\t\t\tProjectionPoseYaw: %f\n", projection->pose_yaw);
|
||||
if (projection->pose_pitch != mkvparser::Projection::kValueNotPresent)
|
||||
printf("\t\t\tProjectionPosePitch: %f\n", projection->pose_pitch);
|
||||
if (projection->pose_roll != mkvparser::Projection::kValueNotPresent)
|
||||
printf("\t\t\tProjectionPosePitch: %f\n", projection->pose_roll);
|
||||
}
|
||||
}
|
||||
|
||||
if (trackType == mkvparser::Track::kAudio) {
|
||||
const mkvparser::AudioTrack* const pAudioTrack =
|
||||
static_cast<const mkvparser::AudioTrack*>(pTrack);
|
||||
|
||||
const long long channels = pAudioTrack->GetChannels();
|
||||
printf("\t\tAudio Channels\t\t: %lld\n", channels);
|
||||
|
||||
const long long bitDepth = pAudioTrack->GetBitDepth();
|
||||
printf("\t\tAudio BitDepth\t\t: %lld\n", bitDepth);
|
||||
|
||||
const double sampleRate = pAudioTrack->GetSamplingRate();
|
||||
printf("\t\tAddio Sample Rate\t: %.3f\n", sampleRate);
|
||||
|
||||
const long long codecDelay = pAudioTrack->GetCodecDelay();
|
||||
printf("\t\tAudio Codec Delay\t\t: %lld\n", codecDelay);
|
||||
|
||||
const long long seekPreRoll = pAudioTrack->GetSeekPreRoll();
|
||||
printf("\t\tAudio Seek Pre Roll\t\t: %lld\n", seekPreRoll);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\n\t\t\t Cluster Info\n");
|
||||
const unsigned long clusterCount = pSegment->GetCount();
|
||||
|
||||
printf("\t\tCluster Count\t: %ld\n\n", clusterCount);
|
||||
|
||||
if (clusterCount == 0) {
|
||||
printf("\t\tSegment has no clusters.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const mkvparser::Cluster* pCluster = pSegment->GetFirst();
|
||||
|
||||
while (pCluster != NULL && !pCluster->EOS()) {
|
||||
const long long timeCode = pCluster->GetTimeCode();
|
||||
printf("\t\tCluster Time Code\t: %lld\n", timeCode);
|
||||
|
||||
const long long time_ns = pCluster->GetTime();
|
||||
printf("\t\tCluster Time (ns)\t: %lld\n", time_ns);
|
||||
|
||||
const mkvparser::BlockEntry* pBlockEntry;
|
||||
|
||||
long status = pCluster->GetFirst(pBlockEntry);
|
||||
|
||||
if (status < 0) // error
|
||||
{
|
||||
printf("\t\tError parsing first block of cluster\n");
|
||||
fflush(stdout);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (pBlockEntry != NULL && !pBlockEntry->EOS()) {
|
||||
const mkvparser::Block* const pBlock = pBlockEntry->GetBlock();
|
||||
const long long trackNum = pBlock->GetTrackNumber();
|
||||
const unsigned long tn = static_cast<unsigned long>(trackNum);
|
||||
const mkvparser::Track* const pTrack = pTracks->GetTrackByNumber(tn);
|
||||
|
||||
if (pTrack == NULL)
|
||||
printf("\t\t\tBlock\t\t:UNKNOWN TRACK TYPE\n");
|
||||
else {
|
||||
const long long trackType = pTrack->GetType();
|
||||
const int frameCount = pBlock->GetFrameCount();
|
||||
const long long time_ns = pBlock->GetTime(pCluster);
|
||||
const long long discard_padding = pBlock->GetDiscardPadding();
|
||||
|
||||
printf("\t\t\tBlock\t\t:%s,%s,%15lld,%lld\n",
|
||||
(trackType == mkvparser::Track::kVideo) ? "V" : "A",
|
||||
pBlock->IsKey() ? "I" : "P", time_ns, discard_padding);
|
||||
|
||||
for (int i = 0; i < frameCount; ++i) {
|
||||
const mkvparser::Block::Frame& theFrame = pBlock->GetFrame(i);
|
||||
const long size = theFrame.len;
|
||||
const long long offset = theFrame.pos;
|
||||
printf("\t\t\t %15ld,%15llx\n", size, offset);
|
||||
}
|
||||
}
|
||||
|
||||
status = pCluster->GetNext(pBlockEntry, pBlockEntry);
|
||||
|
||||
if (status < 0) {
|
||||
printf("\t\t\tError parsing next block of cluster\n");
|
||||
fflush(stdout);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
pCluster = pSegment->GetNext(pCluster);
|
||||
}
|
||||
|
||||
if (InputHasCues(pSegment.get())) {
|
||||
// Walk them.
|
||||
const mkvparser::Cues* const cues = pSegment->GetCues();
|
||||
const mkvparser::CuePoint* cue = cues->GetFirst();
|
||||
int cue_point_num = 1;
|
||||
|
||||
printf("\t\tCues\n");
|
||||
do {
|
||||
for (track_num = 0; track_num < num_tracks; ++track_num) {
|
||||
const mkvparser::Track* const track =
|
||||
pTracks->GetTrackByIndex(track_num);
|
||||
const mkvparser::CuePoint::TrackPosition* const track_pos =
|
||||
cue->Find(track);
|
||||
|
||||
if (track_pos != NULL) {
|
||||
const char track_type =
|
||||
(track->GetType() == mkvparser::Track::kVideo) ? 'V' : 'A';
|
||||
printf(
|
||||
"\t\t\tCue Point %4d Track %3lu(%c) Time %14lld "
|
||||
"Block %4lld Pos %8llx\n",
|
||||
cue_point_num, track->GetNumber(), track_type,
|
||||
cue->GetTime(pSegment.get()), track_pos->m_block,
|
||||
track_pos->m_pos);
|
||||
}
|
||||
}
|
||||
|
||||
cue = cues->GetNext(cue);
|
||||
++cue_point_num;
|
||||
} while (cue != NULL);
|
||||
}
|
||||
|
||||
const mkvparser::Tags* const tags = pSegment->GetTags();
|
||||
if (tags && tags->GetTagCount() > 0) {
|
||||
printf("\t\tTags\n");
|
||||
for (int i = 0; i < tags->GetTagCount(); ++i) {
|
||||
const mkvparser::Tags::Tag* const tag = tags->GetTag(i);
|
||||
printf("\t\t\tTag\n");
|
||||
for (int j = 0; j < tag->GetSimpleTagCount(); j++) {
|
||||
const mkvparser::Tags::SimpleTag* const simple_tag =
|
||||
tag->GetSimpleTag(j);
|
||||
printf("\t\t\t\tSimple Tag \"%s\" Value \"%s\"\n",
|
||||
simple_tag->GetTagName(), simple_tag->GetTagString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
128
mkvreader.cpp
Normal file
128
mkvreader.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvreader.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace mkvparser
|
||||
{
|
||||
|
||||
MkvReader::MkvReader() :
|
||||
m_file(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
MkvReader::~MkvReader()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
int MkvReader::Open(const char* fileName)
|
||||
{
|
||||
if (fileName == NULL)
|
||||
return -1;
|
||||
|
||||
if (m_file)
|
||||
return -1;
|
||||
|
||||
#ifdef WIN32
|
||||
const errno_t e = fopen_s(&m_file, fileName, "rb");
|
||||
|
||||
if (e)
|
||||
return -1; //error
|
||||
#else
|
||||
m_file = fopen(fileName, "rb");
|
||||
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
int status = _fseeki64(m_file, 0L, SEEK_END);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
|
||||
m_length = _ftelli64(m_file);
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_END);
|
||||
m_length = ftell(m_file);
|
||||
#endif
|
||||
assert(m_length >= 0);
|
||||
|
||||
#ifdef WIN32
|
||||
status = _fseeki64(m_file, 0L, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_SET);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MkvReader::Close()
|
||||
{
|
||||
if (m_file != NULL)
|
||||
{
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int MkvReader::Length(long long* total, long long* available)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (total)
|
||||
*total = m_length;
|
||||
|
||||
if (available)
|
||||
*available = m_length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MkvReader::Read(long long offset, long len, unsigned char* buffer)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (offset >= m_length)
|
||||
return -1;
|
||||
|
||||
#ifdef WIN32
|
||||
const int status = _fseeki64(m_file, offset, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
#else
|
||||
fseek(m_file, offset, SEEK_SET);
|
||||
#endif
|
||||
|
||||
const size_t size = fread(buffer, 1, len, m_file);
|
||||
|
||||
if (size < size_t(len))
|
||||
return -1; //error
|
||||
|
||||
return 0; //success
|
||||
}
|
||||
|
||||
} //end namespace mkvparser
|
@ -1,15 +1,39 @@
|
||||
// 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_MKVREADER_HPP_
|
||||
#define LIBWEBM_MKVREADER_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/mkvreader.h"
|
||||
|
||||
#endif // LIBWEBM_MKVREADER_HPP_
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVREADER_HPP
|
||||
#define MKVREADER_HPP
|
||||
|
||||
#include "mkvparser.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
namespace mkvparser
|
||||
{
|
||||
|
||||
class MkvReader : public IMkvReader
|
||||
{
|
||||
MkvReader(const MkvReader&);
|
||||
MkvReader& operator=(const MkvReader&);
|
||||
public:
|
||||
MkvReader();
|
||||
virtual ~MkvReader();
|
||||
|
||||
int Open(const char*);
|
||||
void Close();
|
||||
bool IsOpen() const;
|
||||
|
||||
virtual int Read(long long position, long length, unsigned char* buffer);
|
||||
virtual int Length(long long* total, long long* available);
|
||||
private:
|
||||
long long m_length;
|
||||
FILE* m_file;
|
||||
};
|
||||
|
||||
} //end namespace mkvparser
|
||||
|
||||
#endif //MKVREADER_HPP
|
||||
|
@ -1,90 +1,94 @@
|
||||
// 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);
|
||||
#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
|
||||
// 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 "mkvwriter.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <share.h> // for _SH_DENYWR
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
MkvWriter::MkvWriter() : file_(NULL) {
|
||||
}
|
||||
|
||||
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_) {
|
||||
fclose(file_);
|
||||
file_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int64 MkvWriter::Position() const {
|
||||
if (!file_)
|
||||
return 0;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
return _ftelli64(file_);
|
||||
#else
|
||||
return ftell(file_);
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 MkvWriter::Position(int64 position) {
|
||||
if (!file_)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
return _fseeki64(file_, position, SEEK_SET);
|
||||
#else
|
||||
return fseek(file_, position, SEEK_SET);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MkvWriter::Seekable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MkvWriter::ElementStartNotify(uint64, int64) {
|
||||
}
|
||||
|
||||
} // namespace mkvmuxer
|
@ -1,15 +1,49 @@
|
||||
// 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_MKVWRITER_HPP_
|
||||
#define LIBWEBM_MKVWRITER_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/mkvwriter.h"
|
||||
|
||||
#endif // LIBWEBM_MKVWRITER_HPP_
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVWRITER_HPP
|
||||
#define MKVWRITER_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "mkvmuxertypes.hpp"
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
// Default implementation of the IMkvWriter interface on Windows.
|
||||
class MkvWriter : public IMkvWriter {
|
||||
public:
|
||||
MkvWriter();
|
||||
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_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
|
||||
};
|
||||
|
||||
} //end namespace mkvmuxer
|
||||
|
||||
#endif // MKVWRITER_HPP
|
||||
|
317
sample.cpp
Normal file
317
sample.cpp
Normal file
@ -0,0 +1,317 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
//
|
||||
// This sample application demonstrates how to use the matroska parser
|
||||
// library, which allows clients to handle a matroska format file.
|
||||
|
||||
#include "mkvreader.hpp"
|
||||
#include "mkvparser.hpp"
|
||||
#include <memory>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silences these warnings:
|
||||
// warning C4996: 'mbstowcs': This function or variable may be unsafe. Consider
|
||||
// using mbstowcs_s instead. To disable deprecation, use
|
||||
// _CRT_SECURE_NO_WARNINGS. See online help for details.
|
||||
// Fixing this warning requires use of a function available only on Windows,
|
||||
// and this sample code must support non-windows platforms.
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
static const wchar_t* utf8towcs(const char* str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
//TODO: this probably requires that the locale be
|
||||
//configured somehow:
|
||||
|
||||
const size_t size = mbstowcs(NULL, str, 0);
|
||||
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
|
||||
wchar_t* const val = new wchar_t[size+1];
|
||||
|
||||
mbstowcs(val, str, size);
|
||||
val[size] = L'\0';
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
printf("\t\t\tMkv Parser Sample Application\n");
|
||||
printf("\t\t\tUsage: \n");
|
||||
printf("\t\t\t ./sample [input file] \n");
|
||||
printf("\t\t\t ./sample sample.mkv \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
using namespace mkvparser;
|
||||
|
||||
MkvReader reader;
|
||||
|
||||
if (reader.Open(argv[1]))
|
||||
{
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int maj, min, build, rev;
|
||||
|
||||
GetVersion(maj, min, build, rev);
|
||||
printf("\t\t libmkv verison: %d.%d.%d.%d\n", maj, min, build, rev);
|
||||
|
||||
long long pos = 0;
|
||||
|
||||
EBMLHeader ebmlHeader;
|
||||
|
||||
ebmlHeader.Parse(&reader, pos);
|
||||
|
||||
printf("\t\t\t EBML Header\n");
|
||||
printf("\t\tEBML Version\t\t: %lld\n", ebmlHeader.m_version);
|
||||
printf("\t\tEBML MaxIDLength\t: %lld\n", ebmlHeader.m_maxIdLength);
|
||||
printf("\t\tEBML MaxSizeLength\t: %lld\n", ebmlHeader.m_maxSizeLength);
|
||||
printf("\t\tDoc Type\t\t: %s\n", ebmlHeader.m_docType);
|
||||
printf("\t\tPos\t\t\t: %lld\n", pos);
|
||||
|
||||
typedef mkvparser::Segment seg_t;
|
||||
seg_t* pSegment_;
|
||||
|
||||
long long ret = seg_t::CreateInstance(&reader, pos, pSegment_);
|
||||
if (ret)
|
||||
{
|
||||
printf("\n Segment::CreateInstance() failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const std::auto_ptr<seg_t> pSegment(pSegment_);
|
||||
|
||||
ret = pSegment->Load();
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("\n Segment::Load() failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const SegmentInfo* const pSegmentInfo = pSegment->GetInfo();
|
||||
|
||||
const long long timeCodeScale = pSegmentInfo->GetTimeCodeScale();
|
||||
const long long duration_ns = pSegmentInfo->GetDuration();
|
||||
|
||||
const char* const pTitle_ = pSegmentInfo->GetTitleAsUTF8();
|
||||
const wchar_t* const pTitle = utf8towcs(pTitle_);
|
||||
|
||||
const char* const pMuxingApp_ = pSegmentInfo->GetMuxingAppAsUTF8();
|
||||
const wchar_t* const pMuxingApp = utf8towcs(pMuxingApp_);
|
||||
|
||||
const char* const pWritingApp_ = pSegmentInfo->GetWritingAppAsUTF8();
|
||||
const wchar_t* const pWritingApp = utf8towcs(pWritingApp_);
|
||||
|
||||
printf("\n");
|
||||
printf("\t\t\t Segment Info\n");
|
||||
printf("\t\tTimeCodeScale\t\t: %lld \n", timeCodeScale);
|
||||
printf("\t\tDuration\t\t: %lld\n", duration_ns);
|
||||
|
||||
const double duration_sec = double(duration_ns) / 1000000000;
|
||||
printf("\t\tDuration(secs)\t\t: %7.3lf\n", duration_sec);
|
||||
|
||||
if (pTitle == NULL)
|
||||
printf("\t\tTrack Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tTrack Name\t\t: %ls\n", pTitle);
|
||||
delete [] pTitle;
|
||||
}
|
||||
|
||||
|
||||
if (pMuxingApp == NULL)
|
||||
printf("\t\tMuxing App\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tMuxing App\t\t: %ls\n", pMuxingApp);
|
||||
delete [] pMuxingApp;
|
||||
}
|
||||
|
||||
if (pWritingApp == NULL)
|
||||
printf("\t\tWriting App\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tWriting App\t\t: %ls\n", pWritingApp);
|
||||
delete [] pWritingApp;
|
||||
}
|
||||
|
||||
// pos of segment payload
|
||||
printf("\t\tPosition(Segment)\t: %lld\n", pSegment->m_start);
|
||||
|
||||
// size of segment payload
|
||||
printf("\t\tSize(Segment)\t\t: %lld\n", pSegment->m_size);
|
||||
|
||||
const mkvparser::Tracks* pTracks = pSegment->GetTracks();
|
||||
|
||||
unsigned long i = 0;
|
||||
const unsigned long j = pTracks->GetTracksCount();
|
||||
|
||||
enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
|
||||
|
||||
printf("\n\t\t\t Track Info\n");
|
||||
|
||||
while (i != j)
|
||||
{
|
||||
const Track* const pTrack = pTracks->GetTrackByIndex(i++);
|
||||
|
||||
if (pTrack == NULL)
|
||||
continue;
|
||||
|
||||
const long long trackType = pTrack->GetType();
|
||||
const long long trackNumber = pTrack->GetNumber();
|
||||
const unsigned long long trackUid = pTrack->GetUid();
|
||||
const wchar_t* const pTrackName = utf8towcs(pTrack->GetNameAsUTF8());
|
||||
|
||||
printf("\t\tTrack Type\t\t: %lld\n", trackType);
|
||||
printf("\t\tTrack Number\t\t: %lld\n", trackNumber);
|
||||
printf("\t\tTrack Uid\t\t: %lld\n", trackUid);
|
||||
|
||||
if (pTrackName == NULL)
|
||||
printf("\t\tTrack Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tTrack Name\t\t: %ls \n", pTrackName);
|
||||
delete [] pTrackName;
|
||||
}
|
||||
|
||||
const char* const pCodecId = pTrack->GetCodecId();
|
||||
|
||||
if (pCodecId == NULL)
|
||||
printf("\t\tCodec Id\t\t: NULL\n");
|
||||
else
|
||||
printf("\t\tCodec Id\t\t: %s\n", pCodecId);
|
||||
|
||||
const char* const pCodecName_ = pTrack->GetCodecNameAsUTF8();
|
||||
const wchar_t* const pCodecName = utf8towcs(pCodecName_);
|
||||
|
||||
if (pCodecName == NULL)
|
||||
printf("\t\tCodec Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tCodec Name\t\t: %ls\n", pCodecName);
|
||||
delete [] pCodecName;
|
||||
}
|
||||
|
||||
if (trackType == VIDEO_TRACK)
|
||||
{
|
||||
const VideoTrack* const pVideoTrack =
|
||||
static_cast<const VideoTrack*>(pTrack);
|
||||
|
||||
const long long width = pVideoTrack->GetWidth();
|
||||
printf("\t\tVideo Width\t\t: %lld\n", width);
|
||||
|
||||
const long long height = pVideoTrack->GetHeight();
|
||||
printf("\t\tVideo Height\t\t: %lld\n", height);
|
||||
|
||||
const double rate = pVideoTrack->GetFrameRate();
|
||||
printf("\t\tVideo Rate\t\t: %f\n",rate);
|
||||
}
|
||||
|
||||
if (trackType == AUDIO_TRACK)
|
||||
{
|
||||
const AudioTrack* const pAudioTrack =
|
||||
static_cast<const AudioTrack*>(pTrack);
|
||||
|
||||
const long long channels = pAudioTrack->GetChannels();
|
||||
printf("\t\tAudio Channels\t\t: %lld\n", channels);
|
||||
|
||||
const long long bitDepth = pAudioTrack->GetBitDepth();
|
||||
printf("\t\tAudio BitDepth\t\t: %lld\n", bitDepth);
|
||||
|
||||
const double sampleRate = pAudioTrack->GetSamplingRate();
|
||||
printf("\t\tAddio Sample Rate\t: %.3f\n", sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\n\t\t\t Cluster Info\n");
|
||||
const unsigned long clusterCount = pSegment->GetCount();
|
||||
|
||||
printf("\t\tCluster Count\t: %ld\n\n", clusterCount);
|
||||
|
||||
if (clusterCount == 0)
|
||||
{
|
||||
printf("\t\tSegment has no clusters.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const mkvparser::Cluster* pCluster = pSegment->GetFirst();
|
||||
|
||||
while ((pCluster != NULL) && !pCluster->EOS())
|
||||
{
|
||||
const long long timeCode = pCluster->GetTimeCode();
|
||||
printf("\t\tCluster Time Code\t: %lld\n", timeCode);
|
||||
|
||||
const long long time_ns = pCluster->GetTime();
|
||||
printf("\t\tCluster Time (ns)\t: %lld\n", time_ns);
|
||||
|
||||
const BlockEntry* pBlockEntry;
|
||||
|
||||
long status = pCluster->GetFirst(pBlockEntry);
|
||||
|
||||
if (status < 0) //error
|
||||
{
|
||||
printf("\t\tError parsing first block of cluster\n");
|
||||
fflush(stdout);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((pBlockEntry != NULL) && !pBlockEntry->EOS())
|
||||
{
|
||||
const Block* const pBlock = pBlockEntry->GetBlock();
|
||||
const long long trackNum = pBlock->GetTrackNumber();
|
||||
const unsigned long tn = static_cast<unsigned long>(trackNum);
|
||||
const Track* const pTrack = pTracks->GetTrackByNumber(tn);
|
||||
|
||||
if (pTrack == NULL)
|
||||
printf("\t\t\tBlock\t\t:UNKNOWN TRACK TYPE\n");
|
||||
else
|
||||
{
|
||||
const long long trackType = pTrack->GetType();
|
||||
const int frameCount = pBlock->GetFrameCount();
|
||||
const long long time_ns = pBlock->GetTime(pCluster);
|
||||
|
||||
printf("\t\t\tBlock\t\t:%s,%s,%15lld\n",
|
||||
(trackType == VIDEO_TRACK) ? "V" : "A",
|
||||
pBlock->IsKey() ? "I" : "P",
|
||||
time_ns);
|
||||
|
||||
for (int i = 0; i < frameCount; ++i)
|
||||
{
|
||||
const Block::Frame& theFrame = pBlock->GetFrame(i);
|
||||
const long size = theFrame.len;
|
||||
const long long offset = theFrame.pos;
|
||||
printf("\t\t\t %15ld,%15llx\n", size, offset);
|
||||
}
|
||||
}
|
||||
|
||||
status = pCluster->GetNext(pBlockEntry, pBlockEntry);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
printf("\t\t\tError parsing next block of cluster\n");
|
||||
fflush(stdout);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
pCluster = pSegment->GetNext(pCluster);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
194
sample_2005.vcproj
Normal file
194
sample_2005.vcproj
Normal file
@ -0,0 +1,194 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="sample"
|
||||
ProjectGUID="{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
RootNamespace="sample"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Debug\libwebm.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Release\libwebm.lib"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
193
sample_2008.vcproj
Normal file
193
sample_2008.vcproj
Normal file
@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="sample"
|
||||
ProjectGUID="{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
RootNamespace="sample"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="131072"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Debug\libwebm.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Release\libwebm.lib"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
397
sample_muxer/sample_muxer.cpp
Normal file
397
sample_muxer/sample_muxer.cpp
Normal file
@ -0,0 +1,397 @@
|
||||
// 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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
// libwebm parser includes
|
||||
#include "mkvreader.hpp"
|
||||
#include "mkvparser.hpp"
|
||||
|
||||
// libwebm muxer includes
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "mkvwriter.hpp"
|
||||
#include "mkvmuxerutil.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
void Usage() {
|
||||
printf("Usage: sample_muxer -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(" 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("\n");
|
||||
printf("Video options:\n");
|
||||
printf(" -display_width <int> Display width in pixels\n");
|
||||
printf(" -display_height <int> Display height in pixels\n");
|
||||
printf(" -stereo_mode <int> 3D video mode\n");
|
||||
printf("\n");
|
||||
printf("Cues options:\n");
|
||||
printf(" -output_cues_block_number <int> >0 outputs cue block number\n");
|
||||
}
|
||||
|
||||
} //end namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
using mkvmuxer::uint64;
|
||||
|
||||
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_on_video_track = true;
|
||||
bool cues_on_audio_track = true;
|
||||
uint64 max_cluster_duration = 0;
|
||||
uint64 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;
|
||||
const char* chunk_name = NULL;
|
||||
|
||||
bool output_cues_block_number = true;
|
||||
|
||||
uint64 display_width = 0;
|
||||
uint64 display_height = 0;
|
||||
uint64 stereo_mode = 0;
|
||||
|
||||
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])) {
|
||||
input = argv[++i];
|
||||
} else if (!strcmp("-o", argv[i])) {
|
||||
output = argv[++i];
|
||||
} else if (!strcmp("-video", argv[i])) {
|
||||
output_video = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-audio", argv[i])) {
|
||||
output_audio = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-live", argv[i])) {
|
||||
live_mode = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-output_cues", argv[i])) {
|
||||
output_cues = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-cues_on_video_track", argv[i])) {
|
||||
cues_on_video_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-cues_on_audio_track", argv[i])) {
|
||||
cues_on_audio_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-max_cluster_duration", argv[i])) {
|
||||
const double seconds = strtod(argv[++i], &end);
|
||||
max_cluster_duration =
|
||||
static_cast<uint64>(seconds * 1000000000.0);
|
||||
} else if (!strcmp("-max_cluster_size", argv[i])) {
|
||||
max_cluster_size = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-switch_tracks", argv[i])) {
|
||||
switch_tracks = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-audio_track_number", argv[i])) {
|
||||
audio_track_number = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-video_track_number", argv[i])) {
|
||||
video_track_number = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-chunking", argv[i])) {
|
||||
chunking = true;
|
||||
chunk_name = argv[++i];
|
||||
} else if (!strcmp("-display_width", argv[i])) {
|
||||
display_width = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-display_height", argv[i])) {
|
||||
display_height = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-stereo_mode", argv[i])) {
|
||||
stereo_mode = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-output_cues_block_number", argv[i])) {
|
||||
output_cues_block_number =
|
||||
strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
ebml_header.Parse(&reader, pos);
|
||||
|
||||
mkvparser::Segment* parser_segment;
|
||||
long long ret = mkvparser::Segment::CreateInstance(&reader,
|
||||
pos,
|
||||
parser_segment);
|
||||
if (ret) {
|
||||
printf("\n Segment::CreateInstance() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = parser_segment->Load();
|
||||
if (ret < 0) {
|
||||
printf("\n Segment::Load() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const mkvparser::SegmentInfo* const segment_info = parser_segment->GetInfo();
|
||||
const long long timeCodeScale = segment_info->GetTimeCodeScale();
|
||||
|
||||
// Set muxer header info
|
||||
mkvmuxer::MkvWriter writer;
|
||||
|
||||
if (!writer.Open(output)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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("sample_muxer");
|
||||
|
||||
// Set Tracks element attributes
|
||||
enum { kVideoTrack = 1, kAudioTrack = 2 };
|
||||
const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks();
|
||||
unsigned long i = 0;
|
||||
uint64 vid_track = 0; // no track added
|
||||
uint64 aud_track = 0; // no track added
|
||||
|
||||
while (i != parser_tracks->GetTracksCount()) {
|
||||
int track_num = i++;
|
||||
if (switch_tracks)
|
||||
track_num = i % parser_tracks->GetTracksCount();
|
||||
|
||||
const mkvparser::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 == kVideoTrack && 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 (track_name)
|
||||
video->set_name(track_name);
|
||||
|
||||
if (display_width > 0)
|
||||
video->set_display_width(display_width);
|
||||
if (display_height > 0)
|
||||
video->set_display_height(display_height);
|
||||
if (stereo_mode > 0)
|
||||
video->SetStereoMode(stereo_mode);
|
||||
|
||||
const double rate = pVideoTrack->GetFrameRate();
|
||||
if (rate > 0.0) {
|
||||
video->set_frame_rate(rate);
|
||||
}
|
||||
} else if (track_type == kAudioTrack && 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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
int 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));
|
||||
const long long track_type = parser_track->GetType();
|
||||
|
||||
if ((track_type == kAudioTrack && output_audio) ||
|
||||
(track_type == kVideoTrack && output_video)) {
|
||||
const int frame_count = block->GetFrameCount();
|
||||
const long long time_ns = block->GetTime(cluster);
|
||||
const bool is_key = block->IsKey();
|
||||
|
||||
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;
|
||||
|
||||
uint64 track_num = vid_track;
|
||||
if (track_type == kAudioTrack)
|
||||
track_num = aud_track;
|
||||
|
||||
if (!muxer_segment.AddFrame(data,
|
||||
frame.len,
|
||||
track_num,
|
||||
time_ns,
|
||||
is_key)) {
|
||||
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);
|
||||
}
|
||||
|
||||
muxer_segment.Finalize();
|
||||
|
||||
delete [] data;
|
||||
delete parser_segment;
|
||||
|
||||
writer.Close();
|
||||
reader.Close();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
197
sample_muxer/sample_muxer.vcproj
Normal file
197
sample_muxer/sample_muxer.vcproj
Normal file
@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="sample_muxer"
|
||||
ProjectGUID="{B407561F-1F5E-4798-B9C2-81AB09CFBC16}"
|
||||
RootNamespace="sample_muxer"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="196613"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="../"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="..\Debug\libwebm.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
EnableIntrinsicFunctions="true"
|
||||
AdditionalIncludeDirectories="../"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="..\Release\libwebm.lib"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample_muxer.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
@ -1,391 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
//
|
||||
// This sample application demonstrates how to use the matroska parser
|
||||
// library, which allows clients to handle a matroska format file.
|
||||
|
||||
#include "sample_muxer_metadata.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "mkvmuxer/mkvmuxer.h"
|
||||
#include "webvtt/vttreader.h"
|
||||
|
||||
SampleMuxerMetadata::SampleMuxerMetadata() : segment_(NULL) {}
|
||||
|
||||
bool SampleMuxerMetadata::Init(mkvmuxer::Segment* segment) {
|
||||
if (segment == NULL || segment_ != NULL)
|
||||
return false;
|
||||
|
||||
segment_ = segment;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::Load(const char* file, Kind kind) {
|
||||
if (kind == kChapters)
|
||||
return LoadChapters(file);
|
||||
|
||||
uint64_t track_num;
|
||||
|
||||
if (!AddTrack(kind, &track_num)) {
|
||||
printf("Unable to add track for WebVTT file \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
return Parse(file, kind, track_num);
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::AddChapters() {
|
||||
typedef cue_list_t::const_iterator iter_t;
|
||||
iter_t i = chapter_cues_.begin();
|
||||
const iter_t j = chapter_cues_.end();
|
||||
|
||||
while (i != j) {
|
||||
const cue_t& chapter = *i++;
|
||||
|
||||
if (!AddChapter(chapter))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::Write(int64_t time_ns) {
|
||||
typedef cues_set_t::iterator iter_t;
|
||||
|
||||
iter_t i = cues_set_.begin();
|
||||
const iter_t j = cues_set_.end();
|
||||
|
||||
while (i != j) {
|
||||
const cues_set_t::value_type& v = *i;
|
||||
|
||||
if (time_ns >= 0 && v > time_ns)
|
||||
return true; // nothing else to do just yet
|
||||
|
||||
if (!v.Write(segment_)) {
|
||||
printf("\nCould not add metadata.\n");
|
||||
return false; // error
|
||||
}
|
||||
|
||||
cues_set_.erase(i++);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::LoadChapters(const char* file) {
|
||||
if (!chapter_cues_.empty()) {
|
||||
printf("Support for more than one chapters file is not yet implemented\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
cue_list_t cues;
|
||||
|
||||
if (!ParseChapters(file, &cues))
|
||||
return false;
|
||||
|
||||
// TODO(matthewjheaney): support more than one chapters file
|
||||
chapter_cues_.swap(cues);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::ParseChapters(const char* file,
|
||||
cue_list_t* cues_ptr) {
|
||||
cue_list_t& cues = *cues_ptr;
|
||||
cues.clear();
|
||||
|
||||
libwebvtt::VttReader r;
|
||||
int e = r.Open(file);
|
||||
|
||||
if (e) {
|
||||
printf("Unable to open WebVTT file: \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
libwebvtt::Parser p(&r);
|
||||
e = p.Init();
|
||||
|
||||
if (e < 0) { // error
|
||||
printf("Error parsing WebVTT file: \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
libwebvtt::Time t;
|
||||
t.hours = -1;
|
||||
|
||||
for (;;) {
|
||||
cue_t c;
|
||||
e = p.Parse(&c);
|
||||
|
||||
if (e < 0) { // error
|
||||
printf("Error parsing WebVTT file: \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e > 0) // EOF
|
||||
return true;
|
||||
|
||||
if (c.start_time < t) {
|
||||
printf("bad WebVTT cue timestamp (out-of-order)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c.stop_time < c.start_time) {
|
||||
printf("bad WebVTT cue timestamp (stop < start)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
t = c.start_time;
|
||||
cues.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::AddChapter(const cue_t& cue) {
|
||||
// TODO(matthewjheaney): support language and country
|
||||
|
||||
mkvmuxer::Chapter* const chapter = segment_->AddChapter();
|
||||
|
||||
if (chapter == NULL) {
|
||||
printf("Unable to add chapter\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cue.identifier.empty()) {
|
||||
chapter->set_id(NULL);
|
||||
} else {
|
||||
const char* const id = cue.identifier.c_str();
|
||||
if (!chapter->set_id(id)) {
|
||||
printf("Unable to set chapter id\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
typedef libwebvtt::presentation_t time_ms_t;
|
||||
const time_ms_t start_time_ms = cue.start_time.presentation();
|
||||
const time_ms_t stop_time_ms = cue.stop_time.presentation();
|
||||
|
||||
enum { kNsPerMs = 1000000 };
|
||||
const uint64_t start_time_ns = start_time_ms * kNsPerMs;
|
||||
const uint64_t stop_time_ns = stop_time_ms * kNsPerMs;
|
||||
|
||||
chapter->set_time(*segment_, start_time_ns, stop_time_ns);
|
||||
|
||||
typedef libwebvtt::Cue::payload_t::const_iterator iter_t;
|
||||
iter_t i = cue.payload.begin();
|
||||
const iter_t j = cue.payload.end();
|
||||
|
||||
std::string title;
|
||||
|
||||
for (;;) {
|
||||
title += *i++;
|
||||
|
||||
if (i == j)
|
||||
break;
|
||||
|
||||
enum { kLF = '\x0A' };
|
||||
title += kLF;
|
||||
}
|
||||
|
||||
if (!chapter->add_string(title.c_str(), NULL, NULL)) {
|
||||
printf("Unable to set chapter title\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::AddTrack(Kind kind, uint64_t* track_num) {
|
||||
*track_num = 0;
|
||||
|
||||
// Track number value 0 means "let muxer choose track number"
|
||||
mkvmuxer::Track* const track = segment_->AddTrack(0);
|
||||
|
||||
if (track == NULL) // error
|
||||
return false;
|
||||
|
||||
// Return the track number value chosen by the muxer
|
||||
*track_num = track->number();
|
||||
|
||||
int type;
|
||||
const char* codec_id;
|
||||
|
||||
switch (kind) {
|
||||
case kSubtitles:
|
||||
type = 0x11;
|
||||
codec_id = "D_WEBVTT/SUBTITLES";
|
||||
break;
|
||||
|
||||
case kCaptions:
|
||||
type = 0x11;
|
||||
codec_id = "D_WEBVTT/CAPTIONS";
|
||||
break;
|
||||
|
||||
case kDescriptions:
|
||||
type = 0x21;
|
||||
codec_id = "D_WEBVTT/DESCRIPTIONS";
|
||||
break;
|
||||
|
||||
case kMetadata:
|
||||
type = 0x21;
|
||||
codec_id = "D_WEBVTT/METADATA";
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
track->set_type(type);
|
||||
track->set_codec_id(codec_id);
|
||||
|
||||
// TODO(matthewjheaney): set name and language
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::Parse(const char* file, Kind /* kind */,
|
||||
uint64_t track_num) {
|
||||
libwebvtt::VttReader r;
|
||||
int e = r.Open(file);
|
||||
|
||||
if (e) {
|
||||
printf("Unable to open WebVTT file: \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
libwebvtt::Parser p(&r);
|
||||
|
||||
e = p.Init();
|
||||
|
||||
if (e < 0) { // error
|
||||
printf("Error parsing WebVTT file: \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
SortableCue cue;
|
||||
cue.track_num = track_num;
|
||||
|
||||
libwebvtt::Time t;
|
||||
t.hours = -1;
|
||||
|
||||
for (;;) {
|
||||
cue_t& c = cue.cue;
|
||||
e = p.Parse(&c);
|
||||
|
||||
if (e < 0) { // error
|
||||
printf("Error parsing WebVTT file: \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e > 0) // EOF
|
||||
return true;
|
||||
|
||||
if (c.start_time >= t) {
|
||||
t = c.start_time;
|
||||
} else {
|
||||
printf("bad WebVTT cue timestamp (out-of-order)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c.stop_time < c.start_time) {
|
||||
printf("bad WebVTT cue timestamp (stop < start)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
cues_set_.insert(cue);
|
||||
}
|
||||
}
|
||||
|
||||
void SampleMuxerMetadata::MakeFrame(const cue_t& c, std::string* pf) {
|
||||
pf->clear();
|
||||
WriteCueIdentifier(c.identifier, pf);
|
||||
WriteCueSettings(c.settings, pf);
|
||||
WriteCuePayload(c.payload, pf);
|
||||
}
|
||||
|
||||
void SampleMuxerMetadata::WriteCueIdentifier(const std::string& identifier,
|
||||
std::string* pf) {
|
||||
pf->append(identifier);
|
||||
pf->push_back('\x0A'); // LF
|
||||
}
|
||||
|
||||
void SampleMuxerMetadata::WriteCueSettings(const cue_t::settings_t& settings,
|
||||
std::string* pf) {
|
||||
if (settings.empty()) {
|
||||
pf->push_back('\x0A'); // LF
|
||||
return;
|
||||
}
|
||||
|
||||
typedef cue_t::settings_t::const_iterator iter_t;
|
||||
|
||||
iter_t i = settings.begin();
|
||||
const iter_t j = settings.end();
|
||||
|
||||
for (;;) {
|
||||
const libwebvtt::Setting& setting = *i++;
|
||||
|
||||
pf->append(setting.name);
|
||||
pf->push_back(':');
|
||||
pf->append(setting.value);
|
||||
|
||||
if (i == j)
|
||||
break;
|
||||
|
||||
pf->push_back(' '); // separate settings with whitespace
|
||||
}
|
||||
|
||||
pf->push_back('\x0A'); // LF
|
||||
}
|
||||
|
||||
void SampleMuxerMetadata::WriteCuePayload(const cue_t::payload_t& payload,
|
||||
std::string* pf) {
|
||||
typedef cue_t::payload_t::const_iterator iter_t;
|
||||
|
||||
iter_t i = payload.begin();
|
||||
const iter_t j = payload.end();
|
||||
|
||||
while (i != j) {
|
||||
const std::string& line = *i++;
|
||||
pf->append(line);
|
||||
pf->push_back('\x0A'); // LF
|
||||
}
|
||||
}
|
||||
|
||||
bool SampleMuxerMetadata::SortableCue::Write(mkvmuxer::Segment* segment) const {
|
||||
// Cue start time expressed in milliseconds
|
||||
const int64_t start_ms = cue.start_time.presentation();
|
||||
|
||||
// Cue start time expressed in nanoseconds (MKV time)
|
||||
const int64_t start_ns = start_ms * 1000000;
|
||||
|
||||
// Cue stop time expressed in milliseconds
|
||||
const int64_t stop_ms = cue.stop_time.presentation();
|
||||
|
||||
// Cue stop time expressed in nanonseconds
|
||||
const int64_t stop_ns = stop_ms * 1000000;
|
||||
|
||||
// Metadata blocks always specify the block duration.
|
||||
const int64_t duration_ns = stop_ns - start_ns;
|
||||
|
||||
std::string frame;
|
||||
MakeFrame(cue, &frame);
|
||||
|
||||
typedef const uint8_t* data_t;
|
||||
const data_t buf = reinterpret_cast<data_t>(frame.data());
|
||||
const uint64_t len = frame.length();
|
||||
|
||||
mkvmuxer::Frame muxer_frame;
|
||||
if (!muxer_frame.Init(buf, len))
|
||||
return 0;
|
||||
muxer_frame.set_track_number(track_num);
|
||||
muxer_frame.set_timestamp(start_ns);
|
||||
muxer_frame.set_duration(duration_ns);
|
||||
muxer_frame.set_is_key(true); // All metadata frames are keyframes.
|
||||
return segment->AddGenericFrame(&muxer_frame);
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef SAMPLE_MUXER_METADATA_H_ // NOLINT
|
||||
#define SAMPLE_MUXER_METADATA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "webvtt/webvttparser.h"
|
||||
|
||||
namespace mkvmuxer {
|
||||
class Chapter;
|
||||
class Frame;
|
||||
class Segment;
|
||||
class Track;
|
||||
} // namespace mkvmuxer
|
||||
|
||||
class SampleMuxerMetadata {
|
||||
public:
|
||||
enum Kind { kSubtitles, kCaptions, kDescriptions, kMetadata, kChapters };
|
||||
|
||||
SampleMuxerMetadata();
|
||||
|
||||
// Bind this metadata object to the muxer instance. Returns false
|
||||
// if segment equals NULL, or Init has already been called.
|
||||
bool Init(mkvmuxer::Segment* segment);
|
||||
|
||||
// Parse the WebVTT file |filename| having the indicated |kind|, and
|
||||
// create a corresponding track (or chapters element) in the
|
||||
// segment. Returns false on error.
|
||||
bool Load(const char* filename, Kind kind);
|
||||
|
||||
bool AddChapters();
|
||||
|
||||
// Write any WebVTT cues whose time is less or equal to |time_ns| as
|
||||
// a metadata block in its corresponding track. If |time_ns| is
|
||||
// negative, write all remaining cues. Returns false on error.
|
||||
bool Write(int64_t time_ns);
|
||||
|
||||
private:
|
||||
typedef libwebvtt::Cue cue_t;
|
||||
|
||||
// Used to sort cues as they are loaded.
|
||||
struct SortableCue {
|
||||
bool operator>(int64_t time_ns) const {
|
||||
// Cue start time expressed in milliseconds
|
||||
const int64_t start_ms = cue.start_time.presentation();
|
||||
|
||||
// Cue start time expressed in nanoseconds (MKV time)
|
||||
const int64_t start_ns = start_ms * 1000000;
|
||||
|
||||
return (start_ns > time_ns);
|
||||
}
|
||||
|
||||
bool operator<(const SortableCue& rhs) const {
|
||||
if (cue.start_time < rhs.cue.start_time)
|
||||
return true;
|
||||
|
||||
if (cue.start_time > rhs.cue.start_time)
|
||||
return false;
|
||||
|
||||
return (track_num < rhs.track_num);
|
||||
}
|
||||
|
||||
// Write this cue as a metablock to |segment|. Returns false on
|
||||
// error.
|
||||
bool Write(mkvmuxer::Segment* segment) const;
|
||||
|
||||
uint64_t track_num;
|
||||
cue_t cue;
|
||||
};
|
||||
|
||||
typedef std::multiset<SortableCue> cues_set_t;
|
||||
typedef std::list<cue_t> cue_list_t;
|
||||
|
||||
// Parse the WebVTT cues in the named |file|, returning false on
|
||||
// error. We handle chapters as a special case, because they are
|
||||
// stored in their own, dedicated level-1 element.
|
||||
bool LoadChapters(const char* file);
|
||||
|
||||
// Parse the WebVTT chapters in |file| to populate |cues|. Returns
|
||||
// false on error.
|
||||
static bool ParseChapters(const char* file, cue_list_t* cues);
|
||||
|
||||
// Adds WebVTT cue |chapter| to the chapters element of the output
|
||||
// file's segment element. Returns false on error.
|
||||
bool AddChapter(const cue_t& chapter);
|
||||
|
||||
// Add a metadata track to the segment having the indicated |kind|,
|
||||
// returning the |track_num| that has been chosen for this track.
|
||||
// Returns false on error.
|
||||
bool AddTrack(Kind kind, uint64_t* track_num);
|
||||
|
||||
// Parse the WebVTT |file| having the indicated |kind| and
|
||||
// |track_num|, adding each parsed cue to cues set. Returns false
|
||||
// on error.
|
||||
bool Parse(const char* file, Kind kind, uint64_t track_num);
|
||||
|
||||
// Converts a WebVTT cue to a Matroska metadata block.
|
||||
static void MakeFrame(const cue_t& cue, std::string* frame);
|
||||
|
||||
// Populate the cue identifier part of the metadata block.
|
||||
static void WriteCueIdentifier(const std::string& identifier,
|
||||
std::string* frame);
|
||||
|
||||
// Populate the cue settings part of the metadata block.
|
||||
static void WriteCueSettings(const cue_t::settings_t& settings,
|
||||
std::string* frame);
|
||||
|
||||
// Populate the payload part of the metadata block.
|
||||
static void WriteCuePayload(const cue_t::payload_t& payload,
|
||||
std::string* frame);
|
||||
|
||||
mkvmuxer::Segment* segment_;
|
||||
|
||||
// Set of cues ordered by time and then by track number.
|
||||
cues_set_t cues_set_;
|
||||
|
||||
// The cues that will be used to populate the Chapters level-1
|
||||
// element of the output file.
|
||||
cue_list_t chapter_cues_;
|
||||
|
||||
// Disable copy ctor and copy assign.
|
||||
SampleMuxerMetadata(const SampleMuxerMetadata&);
|
||||
SampleMuxerMetadata& operator=(const SampleMuxerMetadata&);
|
||||
};
|
||||
|
||||
#endif // SAMPLE_MUXER_METADATA_H_ // NOLINT
|
File diff suppressed because it is too large
Load Diff
@ -1,823 +0,0 @@
|
||||
// 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 "gtest/gtest.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
#include "common/hdr_util.h"
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
#include "testing/test_util.h"
|
||||
|
||||
using mkvparser::AudioTrack;
|
||||
using mkvparser::Block;
|
||||
using mkvparser::BlockEntry;
|
||||
using mkvparser::BlockGroup;
|
||||
using mkvparser::Cluster;
|
||||
using mkvparser::CuePoint;
|
||||
using mkvparser::Cues;
|
||||
using mkvparser::MkvReader;
|
||||
using mkvparser::Segment;
|
||||
using mkvparser::SegmentInfo;
|
||||
using mkvparser::Track;
|
||||
using mkvparser::Tracks;
|
||||
using mkvparser::VideoTrack;
|
||||
|
||||
namespace test {
|
||||
|
||||
// Base class containing boiler plate stuff.
|
||||
class ParserTest : public testing::Test {
|
||||
public:
|
||||
ParserTest() : is_reader_open_(false), segment_(NULL) {
|
||||
memset(dummy_data_, -1, kFrameLength);
|
||||
memset(gold_frame_, 0, kFrameLength);
|
||||
}
|
||||
|
||||
virtual ~ParserTest() {
|
||||
CloseReader();
|
||||
if (segment_ != NULL) {
|
||||
delete segment_;
|
||||
segment_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CloseReader() {
|
||||
if (is_reader_open_) {
|
||||
reader_.Close();
|
||||
}
|
||||
is_reader_open_ = false;
|
||||
}
|
||||
|
||||
bool CreateAndLoadSegment(const std::string& filename,
|
||||
int expected_doc_type_ver) {
|
||||
filename_ = GetTestFilePath(filename);
|
||||
if (reader_.Open(filename_.c_str())) {
|
||||
return false;
|
||||
}
|
||||
is_reader_open_ = true;
|
||||
pos_ = 0;
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
ebml_header.Parse(&reader_, pos_);
|
||||
EXPECT_EQ(1, ebml_header.m_version);
|
||||
EXPECT_EQ(1, ebml_header.m_readVersion);
|
||||
EXPECT_STREQ("webm", ebml_header.m_docType);
|
||||
EXPECT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
|
||||
EXPECT_EQ(2, ebml_header.m_docTypeReadVersion);
|
||||
|
||||
if (mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)) {
|
||||
return false;
|
||||
}
|
||||
return !HasFailure() && segment_->Load() >= 0;
|
||||
}
|
||||
|
||||
bool CreateAndLoadSegment(const std::string& filename) {
|
||||
return CreateAndLoadSegment(filename, 4);
|
||||
}
|
||||
|
||||
void CreateSegmentNoHeaderChecks(const std::string& filename) {
|
||||
filename_ = GetTestFilePath(filename);
|
||||
ASSERT_NE(0u, filename_.length());
|
||||
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
ASSERT_EQ(0, ebml_header.Parse(&reader_, pos_));
|
||||
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
|
||||
}
|
||||
|
||||
void CompareBlockContents(const Cluster* const cluster,
|
||||
const Block* const block, std::uint64_t timestamp,
|
||||
int track_number, bool is_key, int frame_count) {
|
||||
ASSERT_TRUE(block != NULL);
|
||||
EXPECT_EQ(track_number, block->GetTrackNumber());
|
||||
EXPECT_EQ(static_cast<long long>(timestamp), block->GetTime(cluster));
|
||||
EXPECT_EQ(is_key, block->IsKey());
|
||||
EXPECT_EQ(frame_count, block->GetFrameCount());
|
||||
const Block::Frame& frame = block->GetFrame(0);
|
||||
EXPECT_EQ(kFrameLength, frame.len);
|
||||
std::memset(dummy_data_, -1, kFrameLength);
|
||||
frame.Read(&reader_, dummy_data_);
|
||||
EXPECT_EQ(0, std::memcmp(gold_frame_, dummy_data_, kFrameLength));
|
||||
}
|
||||
|
||||
void CompareCuePointContents(const Track* const track,
|
||||
const CuePoint* const cue_point,
|
||||
std::uint64_t timestamp, int track_number,
|
||||
std::uint64_t pos) {
|
||||
ASSERT_TRUE(cue_point != NULL);
|
||||
EXPECT_EQ(static_cast<long long>(timestamp), cue_point->GetTime(segment_));
|
||||
const CuePoint::TrackPosition* const track_position =
|
||||
cue_point->Find(track);
|
||||
EXPECT_EQ(track_number, track_position->m_track);
|
||||
EXPECT_EQ(static_cast<long long>(pos), track_position->m_pos);
|
||||
}
|
||||
|
||||
protected:
|
||||
MkvReader reader_;
|
||||
bool is_reader_open_;
|
||||
Segment* segment_;
|
||||
std::string filename_;
|
||||
long long pos_;
|
||||
std::uint8_t dummy_data_[kFrameLength];
|
||||
std::uint8_t gold_frame_[kFrameLength];
|
||||
};
|
||||
|
||||
TEST_F(ParserTest, SegmentInfo) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("segment_info.webm"));
|
||||
const SegmentInfo* const info = segment_->GetInfo();
|
||||
EXPECT_EQ(kTimeCodeScale, info->GetTimeCodeScale());
|
||||
EXPECT_STREQ(kAppString, info->GetMuxingAppAsUTF8());
|
||||
EXPECT_STREQ(kAppString, info->GetWritingAppAsUTF8());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, TrackEntries) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("tracks.webm"));
|
||||
const Tracks* const tracks = segment_->GetTracks();
|
||||
const unsigned int kTracksCount = 2;
|
||||
EXPECT_EQ(kTracksCount, tracks->GetTracksCount());
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const Track* const track = tracks->GetTrackByIndex(i);
|
||||
ASSERT_TRUE(track != NULL);
|
||||
EXPECT_STREQ(kTrackName, track->GetNameAsUTF8());
|
||||
if (track->GetType() == Track::kVideo) {
|
||||
const VideoTrack* const video_track =
|
||||
dynamic_cast<const VideoTrack*>(track);
|
||||
EXPECT_EQ(kWidth, static_cast<int>(video_track->GetWidth()));
|
||||
EXPECT_EQ(kHeight, static_cast<int>(video_track->GetHeight()));
|
||||
EXPECT_STREQ(kVP8CodecId, video_track->GetCodecId());
|
||||
EXPECT_DOUBLE_EQ(kVideoFrameRate, video_track->GetFrameRate());
|
||||
const unsigned int kTrackUid = 1;
|
||||
EXPECT_EQ(kTrackUid, video_track->GetUid());
|
||||
} else if (track->GetType() == Track::kAudio) {
|
||||
const AudioTrack* const audio_track =
|
||||
dynamic_cast<const AudioTrack*>(track);
|
||||
EXPECT_EQ(kSampleRate, audio_track->GetSamplingRate());
|
||||
EXPECT_EQ(kChannels, audio_track->GetChannels());
|
||||
EXPECT_EQ(kBitDepth, audio_track->GetBitDepth());
|
||||
EXPECT_STREQ(kVorbisCodecId, audio_track->GetCodecId());
|
||||
const unsigned int kTrackUid = 2;
|
||||
EXPECT_EQ(kTrackUid, audio_track->GetUid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, SimpleBlock) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("simple_block.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
// Get the cluster
|
||||
const Cluster* cluster = segment_->GetFirst();
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_FALSE(cluster->EOS());
|
||||
|
||||
// Get the first block
|
||||
const BlockEntry* block_entry;
|
||||
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
CompareBlockContents(cluster, block_entry->GetBlock(), 0, kVideoTrackNumber,
|
||||
false, 1);
|
||||
|
||||
// Get the second block
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
CompareBlockContents(cluster, block_entry->GetBlock(), 2000000,
|
||||
kVideoTrackNumber, false, 1);
|
||||
|
||||
// End of Stream
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
ASSERT_EQ(NULL, block_entry);
|
||||
cluster = segment_->GetNext(cluster);
|
||||
EXPECT_TRUE(cluster->EOS());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, MultipleClusters) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("force_new_cluster.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
// Get the first cluster
|
||||
const Cluster* cluster = segment_->GetFirst();
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_FALSE(cluster->EOS());
|
||||
|
||||
// Get the first block
|
||||
const BlockEntry* block_entry;
|
||||
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
CompareBlockContents(cluster, block_entry->GetBlock(), 0, kVideoTrackNumber,
|
||||
false, 1);
|
||||
|
||||
// Get the second cluster
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
EXPECT_EQ(NULL, block_entry);
|
||||
cluster = segment_->GetNext(cluster);
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_FALSE(cluster->EOS());
|
||||
|
||||
// Get the second block
|
||||
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
CompareBlockContents(cluster, block_entry->GetBlock(), 2000000,
|
||||
kVideoTrackNumber, false, 1);
|
||||
|
||||
// Get the third block
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
CompareBlockContents(cluster, block_entry->GetBlock(), 4000000,
|
||||
kVideoTrackNumber, false, 1);
|
||||
|
||||
// Get the third cluster
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
EXPECT_EQ(NULL, block_entry);
|
||||
cluster = segment_->GetNext(cluster);
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_FALSE(cluster->EOS());
|
||||
|
||||
// Get the fourth block
|
||||
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
CompareBlockContents(cluster, block_entry->GetBlock(), 6000000,
|
||||
kVideoTrackNumber, false, 1);
|
||||
|
||||
// End of Stream
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
EXPECT_EQ(NULL, block_entry);
|
||||
cluster = segment_->GetNext(cluster);
|
||||
EXPECT_TRUE(cluster->EOS());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, BlockGroup) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("metadata_block.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
// Get the cluster
|
||||
const Cluster* cluster = segment_->GetFirst();
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_FALSE(cluster->EOS());
|
||||
|
||||
// Get the first block
|
||||
const BlockEntry* block_entry;
|
||||
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
EXPECT_EQ(BlockEntry::Kind::kBlockGroup, block_entry->GetKind());
|
||||
const BlockGroup* block_group = static_cast<const BlockGroup*>(block_entry);
|
||||
EXPECT_EQ(2, block_group->GetDurationTimeCode());
|
||||
CompareBlockContents(cluster, block_group->GetBlock(), 0,
|
||||
kMetadataTrackNumber, true, 1);
|
||||
|
||||
// Get the second block
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
EXPECT_EQ(BlockEntry::Kind::kBlockGroup, block_entry->GetKind());
|
||||
block_group = static_cast<const BlockGroup*>(block_entry);
|
||||
EXPECT_EQ(6, block_group->GetDurationTimeCode());
|
||||
CompareBlockContents(cluster, block_group->GetBlock(), 2000000,
|
||||
kMetadataTrackNumber, true, 1);
|
||||
|
||||
// End of Stream
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
EXPECT_EQ(NULL, block_entry);
|
||||
cluster = segment_->GetNext(cluster);
|
||||
EXPECT_TRUE(cluster->EOS());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Cues) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("output_cues.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
const Track* const track = segment_->GetTracks()->GetTrackByIndex(0);
|
||||
const Cues* const cues = segment_->GetCues();
|
||||
ASSERT_TRUE(cues != NULL);
|
||||
while (!cues->DoneParsing()) {
|
||||
cues->LoadCuePoint();
|
||||
}
|
||||
EXPECT_EQ(3, cues->GetCount());
|
||||
|
||||
// Get first Cue Point
|
||||
const CuePoint* cue_point = cues->GetFirst();
|
||||
CompareCuePointContents(track, cue_point, 0, kVideoTrackNumber, 206);
|
||||
|
||||
// Get second Cue Point
|
||||
cue_point = cues->GetNext(cue_point);
|
||||
CompareCuePointContents(track, cue_point, 6000000, kVideoTrackNumber, 269);
|
||||
|
||||
// Get third (also last) Cue Point
|
||||
cue_point = cues->GetNext(cue_point);
|
||||
const CuePoint* last_cue_point = cues->GetLast();
|
||||
EXPECT_TRUE(cue_point == last_cue_point);
|
||||
CompareCuePointContents(track, cue_point, 4000000, kVideoTrackNumber, 269);
|
||||
|
||||
EXPECT_TRUE(ValidateCues(segment_, &reader_));
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, CuesBeforeClusters) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("cues_before_clusters.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
const Track* const track = segment_->GetTracks()->GetTrackByIndex(0);
|
||||
const Cues* const cues = segment_->GetCues();
|
||||
ASSERT_TRUE(cues != NULL);
|
||||
while (!cues->DoneParsing()) {
|
||||
cues->LoadCuePoint();
|
||||
}
|
||||
EXPECT_EQ(2, cues->GetCount());
|
||||
|
||||
// Get first Cue Point
|
||||
const CuePoint* cue_point = cues->GetFirst();
|
||||
CompareCuePointContents(track, cue_point, 0, kVideoTrackNumber, 238);
|
||||
|
||||
// Get second (also last) Cue Point
|
||||
cue_point = cues->GetNext(cue_point);
|
||||
const CuePoint* last_cue_point = cues->GetLast();
|
||||
EXPECT_TRUE(cue_point == last_cue_point);
|
||||
CompareCuePointContents(track, cue_point, 6000000, kVideoTrackNumber, 301);
|
||||
|
||||
EXPECT_TRUE(ValidateCues(segment_, &reader_));
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, CuesTrackNumber) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("set_cues_track_number.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
const Track* const track = segment_->GetTracks()->GetTrackByIndex(0);
|
||||
const Cues* const cues = segment_->GetCues();
|
||||
ASSERT_TRUE(cues != NULL);
|
||||
while (!cues->DoneParsing()) {
|
||||
cues->LoadCuePoint();
|
||||
}
|
||||
EXPECT_EQ(2, cues->GetCount());
|
||||
|
||||
// Get first Cue Point
|
||||
const CuePoint* cue_point = cues->GetFirst();
|
||||
CompareCuePointContents(track, cue_point, 0, 10, 206);
|
||||
|
||||
// Get second (also last) Cue Point
|
||||
cue_point = cues->GetNext(cue_point);
|
||||
const CuePoint* last_cue_point = cues->GetLast();
|
||||
EXPECT_TRUE(cue_point == last_cue_point);
|
||||
CompareCuePointContents(track, cue_point, 6000000, 10, 269);
|
||||
|
||||
EXPECT_TRUE(ValidateCues(segment_, &reader_));
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Opus) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4));
|
||||
const unsigned int kTracksCount = 2;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Track Header validation.
|
||||
const Tracks* const tracks = segment_->GetTracks();
|
||||
EXPECT_EQ(kTracksCount, tracks->GetTracksCount());
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const Track* const track = tracks->GetTrackByIndex(i);
|
||||
ASSERT_TRUE(track != NULL);
|
||||
|
||||
EXPECT_EQ(NULL, track->GetNameAsUTF8());
|
||||
EXPECT_STREQ("und", track->GetLanguage());
|
||||
EXPECT_EQ(i + 1, track->GetNumber());
|
||||
EXPECT_FALSE(track->GetLacing());
|
||||
|
||||
if (track->GetType() == Track::kVideo) {
|
||||
const VideoTrack* const video_track =
|
||||
dynamic_cast<const VideoTrack*>(track);
|
||||
EXPECT_EQ(854, static_cast<int>(video_track->GetWidth()));
|
||||
EXPECT_EQ(480, static_cast<int>(video_track->GetHeight()));
|
||||
EXPECT_STREQ(kVP9CodecId, video_track->GetCodecId());
|
||||
EXPECT_DOUBLE_EQ(0., video_track->GetFrameRate());
|
||||
EXPECT_EQ(41666666,
|
||||
static_cast<int>(video_track->GetDefaultDuration())); // 24.000
|
||||
const unsigned int kVideoUid = kVideoTrackNumber;
|
||||
EXPECT_EQ(kVideoUid, video_track->GetUid());
|
||||
const unsigned int kCodecDelay = 0;
|
||||
EXPECT_EQ(kCodecDelay, video_track->GetCodecDelay());
|
||||
const unsigned int kSeekPreRoll = 0;
|
||||
EXPECT_EQ(kSeekPreRoll, video_track->GetSeekPreRoll());
|
||||
|
||||
size_t video_codec_private_size;
|
||||
EXPECT_EQ(NULL, video_track->GetCodecPrivate(video_codec_private_size));
|
||||
const unsigned int kPrivateSize = 0;
|
||||
EXPECT_EQ(kPrivateSize, video_codec_private_size);
|
||||
} else if (track->GetType() == Track::kAudio) {
|
||||
const AudioTrack* const audio_track =
|
||||
dynamic_cast<const AudioTrack*>(track);
|
||||
EXPECT_EQ(48000, audio_track->GetSamplingRate());
|
||||
EXPECT_EQ(6, audio_track->GetChannels());
|
||||
EXPECT_EQ(32, audio_track->GetBitDepth());
|
||||
EXPECT_STREQ(kOpusCodecId, audio_track->GetCodecId());
|
||||
EXPECT_EQ(kAudioTrackNumber, static_cast<int>(audio_track->GetUid()));
|
||||
const unsigned int kDefaultDuration = 0;
|
||||
EXPECT_EQ(kDefaultDuration, audio_track->GetDefaultDuration());
|
||||
EXPECT_EQ(kOpusCodecDelay, audio_track->GetCodecDelay());
|
||||
EXPECT_EQ(kOpusSeekPreroll, audio_track->GetSeekPreRoll());
|
||||
|
||||
size_t audio_codec_private_size;
|
||||
EXPECT_TRUE(audio_track->GetCodecPrivate(audio_codec_private_size) !=
|
||||
NULL);
|
||||
EXPECT_GE(audio_codec_private_size, kOpusPrivateDataSizeMinimum);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Parse the file to do block-level validation.
|
||||
const Cluster* cluster = segment_->GetFirst();
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_FALSE(cluster->EOS());
|
||||
|
||||
for (; cluster != NULL && !cluster->EOS();
|
||||
cluster = segment_->GetNext(cluster)) {
|
||||
// Get the first block
|
||||
const BlockEntry* block_entry;
|
||||
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
|
||||
while (block_entry != NULL && !block_entry->EOS()) {
|
||||
const Block* const block = block_entry->GetBlock();
|
||||
ASSERT_TRUE(block != NULL);
|
||||
EXPECT_FALSE(block->IsInvisible());
|
||||
EXPECT_EQ(Block::kLacingNone, block->GetLacing());
|
||||
|
||||
const std::uint32_t track_number =
|
||||
static_cast<std::uint32_t>(block->GetTrackNumber());
|
||||
const Track* const track = tracks->GetTrackByNumber(track_number);
|
||||
ASSERT_TRUE(track != NULL);
|
||||
EXPECT_EQ(track->GetNumber(), block->GetTrackNumber());
|
||||
const unsigned int kContentEncodingCount = 0;
|
||||
EXPECT_EQ(kContentEncodingCount,
|
||||
track->GetContentEncodingCount()); // no encryption
|
||||
|
||||
const std::int64_t track_type = track->GetType();
|
||||
EXPECT_TRUE(track_type == Track::kVideo || track_type == Track::kAudio);
|
||||
if (track_type == Track::kVideo) {
|
||||
EXPECT_EQ(BlockEntry::kBlockSimple, block_entry->GetKind());
|
||||
EXPECT_EQ(0, block->GetDiscardPadding());
|
||||
} else {
|
||||
EXPECT_TRUE(block->IsKey());
|
||||
const std::int64_t kLastAudioTimecode = 1001;
|
||||
const std::int64_t timecode = block->GetTimeCode(cluster);
|
||||
// Only the final Opus block should have discard padding.
|
||||
if (timecode == kLastAudioTimecode) {
|
||||
EXPECT_EQ(BlockEntry::kBlockGroup, block_entry->GetKind());
|
||||
EXPECT_EQ(13500000, block->GetDiscardPadding());
|
||||
} else {
|
||||
EXPECT_EQ(BlockEntry::kBlockSimple, block_entry->GetKind());
|
||||
EXPECT_EQ(0, block->GetDiscardPadding());
|
||||
}
|
||||
}
|
||||
|
||||
const int frame_count = block->GetFrameCount();
|
||||
const Block::Frame& frame = block->GetFrame(0);
|
||||
EXPECT_EQ(1, frame_count);
|
||||
EXPECT_GT(frame.len, 0);
|
||||
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_TRUE(cluster->EOS());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, DiscardPadding) {
|
||||
// Test an artificial file with some extreme DiscardPadding values.
|
||||
const std::string file = "discard_padding.webm";
|
||||
ASSERT_TRUE(CreateAndLoadSegment(file, 4));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Track Header validation.
|
||||
const Tracks* const tracks = segment_->GetTracks();
|
||||
EXPECT_EQ(kTracksCount, tracks->GetTracksCount());
|
||||
const Track* const track = tracks->GetTrackByIndex(0);
|
||||
ASSERT_TRUE(track != NULL);
|
||||
|
||||
EXPECT_STREQ(NULL, track->GetNameAsUTF8());
|
||||
EXPECT_EQ(NULL, track->GetLanguage());
|
||||
EXPECT_EQ(kAudioTrackNumber, track->GetNumber());
|
||||
EXPECT_TRUE(track->GetLacing());
|
||||
|
||||
EXPECT_EQ(Track::kAudio, track->GetType());
|
||||
const AudioTrack* const audio_track = dynamic_cast<const AudioTrack*>(track);
|
||||
EXPECT_EQ(30, audio_track->GetSamplingRate());
|
||||
EXPECT_EQ(2, audio_track->GetChannels());
|
||||
EXPECT_STREQ(kOpusCodecId, audio_track->GetCodecId());
|
||||
EXPECT_EQ(kAudioTrackNumber, static_cast<int>(audio_track->GetUid()));
|
||||
const unsigned int kDefaultDuration = 0;
|
||||
EXPECT_EQ(kDefaultDuration, audio_track->GetDefaultDuration());
|
||||
const unsigned int kCodecDelay = 0;
|
||||
EXPECT_EQ(kCodecDelay, audio_track->GetCodecDelay());
|
||||
const unsigned int kSeekPreRoll = 0;
|
||||
EXPECT_EQ(kSeekPreRoll, audio_track->GetSeekPreRoll());
|
||||
|
||||
size_t audio_codec_private_size;
|
||||
EXPECT_EQ(NULL, audio_track->GetCodecPrivate(audio_codec_private_size));
|
||||
const unsigned int kPrivateSize = 0;
|
||||
EXPECT_EQ(kPrivateSize, audio_codec_private_size);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Parse the file to do block-level validation.
|
||||
const Cluster* cluster = segment_->GetFirst();
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_FALSE(cluster->EOS());
|
||||
const unsigned int kSegmentCount = 1;
|
||||
EXPECT_EQ(kSegmentCount, segment_->GetCount());
|
||||
|
||||
// Get the first block
|
||||
const BlockEntry* block_entry;
|
||||
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
||||
ASSERT_TRUE(block_entry != NULL);
|
||||
EXPECT_FALSE(block_entry->EOS());
|
||||
|
||||
const std::array<int, 3> discard_padding = {{12810000, 127, -128}};
|
||||
int index = 0;
|
||||
while (block_entry != NULL && !block_entry->EOS()) {
|
||||
const Block* const block = block_entry->GetBlock();
|
||||
ASSERT_TRUE(block != NULL);
|
||||
EXPECT_FALSE(block->IsInvisible());
|
||||
EXPECT_EQ(Block::kLacingNone, block->GetLacing());
|
||||
|
||||
const std::uint32_t track_number =
|
||||
static_cast<std::uint32_t>(block->GetTrackNumber());
|
||||
const Track* const track = tracks->GetTrackByNumber(track_number);
|
||||
ASSERT_TRUE(track != NULL);
|
||||
EXPECT_EQ(track->GetNumber(), block->GetTrackNumber());
|
||||
const unsigned int kContentEncodingCount = 0;
|
||||
EXPECT_EQ(kContentEncodingCount,
|
||||
track->GetContentEncodingCount()); // no encryption
|
||||
|
||||
const std::int64_t track_type = track->GetType();
|
||||
EXPECT_EQ(Track::kAudio, track_type);
|
||||
EXPECT_TRUE(block->IsKey());
|
||||
|
||||
// All blocks have DiscardPadding.
|
||||
EXPECT_EQ(BlockEntry::kBlockGroup, block_entry->GetKind());
|
||||
ASSERT_LT(index, static_cast<int>(discard_padding.size()));
|
||||
EXPECT_EQ(discard_padding[index], block->GetDiscardPadding());
|
||||
++index;
|
||||
|
||||
const int frame_count = block->GetFrameCount();
|
||||
const Block::Frame& frame = block->GetFrame(0);
|
||||
EXPECT_EQ(1, frame_count);
|
||||
EXPECT_GT(frame.len, 0);
|
||||
|
||||
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
||||
}
|
||||
|
||||
cluster = segment_->GetNext(cluster);
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
EXPECT_TRUE(cluster->EOS());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, StereoModeParsedCorrectly) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("test_stereo_left_right.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
|
||||
const VideoTrack* const video_track = dynamic_cast<const VideoTrack*>(
|
||||
segment_->GetTracks()->GetTrackByIndex(0));
|
||||
|
||||
EXPECT_EQ(1, video_track->GetStereoMode());
|
||||
EXPECT_EQ(256, video_track->GetWidth());
|
||||
EXPECT_EQ(144, video_track->GetHeight());
|
||||
EXPECT_EQ(128, video_track->GetDisplayWidth());
|
||||
EXPECT_EQ(144, video_track->GetDisplayHeight());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, CanParseColour) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("colour.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
const VideoTrack* const video_track = dynamic_cast<const VideoTrack*>(
|
||||
segment_->GetTracks()->GetTrackByIndex(0));
|
||||
|
||||
const mkvparser::Colour* const colour = video_track->GetColour();
|
||||
ASSERT_TRUE(colour != nullptr);
|
||||
EXPECT_EQ(0u, colour->matrix_coefficients);
|
||||
EXPECT_EQ(1u, colour->bits_per_channel);
|
||||
EXPECT_EQ(2u, colour->chroma_subsampling_horz);
|
||||
EXPECT_EQ(3u, colour->chroma_subsampling_vert);
|
||||
EXPECT_EQ(4u, colour->cb_subsampling_horz);
|
||||
EXPECT_EQ(5u, colour->cb_subsampling_vert);
|
||||
EXPECT_EQ(1u, colour->chroma_siting_horz);
|
||||
EXPECT_EQ(1u, colour->chroma_siting_vert);
|
||||
EXPECT_EQ(2u, colour->range);
|
||||
EXPECT_EQ(9u, colour->transfer_characteristics);
|
||||
EXPECT_EQ(10u, colour->primaries);
|
||||
EXPECT_EQ(11u, colour->max_cll);
|
||||
EXPECT_EQ(12u, colour->max_fall);
|
||||
|
||||
const mkvparser::MasteringMetadata* const mm =
|
||||
video_track->GetColour()->mastering_metadata;
|
||||
ASSERT_TRUE(mm != nullptr);
|
||||
ASSERT_TRUE(mm->r != nullptr);
|
||||
ASSERT_TRUE(mm->g != nullptr);
|
||||
ASSERT_TRUE(mm->b != nullptr);
|
||||
ASSERT_TRUE(mm->white_point != nullptr);
|
||||
EXPECT_FLOAT_EQ(.1, mm->r->x);
|
||||
EXPECT_FLOAT_EQ(.2, mm->r->y);
|
||||
EXPECT_FLOAT_EQ(.1, mm->g->x);
|
||||
EXPECT_FLOAT_EQ(.2, mm->g->y);
|
||||
EXPECT_FLOAT_EQ(.1, mm->b->x);
|
||||
EXPECT_FLOAT_EQ(.2, mm->b->y);
|
||||
EXPECT_FLOAT_EQ(.1, mm->white_point->x);
|
||||
EXPECT_FLOAT_EQ(.2, mm->white_point->y);
|
||||
EXPECT_FLOAT_EQ(30.0, mm->luminance_min);
|
||||
EXPECT_FLOAT_EQ(40.0, mm->luminance_max);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, CanParseProjection) {
|
||||
ASSERT_TRUE(CreateAndLoadSegment("projection.webm"));
|
||||
const unsigned int kTracksCount = 1;
|
||||
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
||||
const VideoTrack* const video_track =
|
||||
static_cast<const VideoTrack*>(segment_->GetTracks()->GetTrackByIndex(0));
|
||||
|
||||
const mkvparser::Projection* const projection = video_track->GetProjection();
|
||||
ASSERT_TRUE(projection != nullptr);
|
||||
EXPECT_EQ(mkvparser::Projection::kRectangular, projection->type);
|
||||
EXPECT_FLOAT_EQ(1, projection->pose_yaw);
|
||||
EXPECT_FLOAT_EQ(2, projection->pose_pitch);
|
||||
EXPECT_FLOAT_EQ(3, projection->pose_roll);
|
||||
EXPECT_EQ(1u, projection->private_data_length);
|
||||
ASSERT_TRUE(projection->private_data != nullptr);
|
||||
EXPECT_EQ(4u, projection->private_data[0]);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Vp9CodecLevelTest) {
|
||||
const int kCodecPrivateLength = 3;
|
||||
const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11};
|
||||
libwebm::Vp9CodecFeatures features;
|
||||
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0],
|
||||
kCodecPrivateLength, &features));
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile);
|
||||
EXPECT_EQ(11, features.level);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent,
|
||||
features.chroma_subsampling);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Vp9CodecProfileTest) {
|
||||
const int kCodecPrivateLength = 3;
|
||||
const uint8_t good_codec_private_profile[kCodecPrivateLength] = {1, 1, 1};
|
||||
libwebm::Vp9CodecFeatures features;
|
||||
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0],
|
||||
kCodecPrivateLength, &features));
|
||||
EXPECT_EQ(1, features.profile);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent,
|
||||
features.chroma_subsampling);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Vp9CodecBitDepthTest) {
|
||||
const int kCodecPrivateLength = 3;
|
||||
const uint8_t good_codec_private_profile[kCodecPrivateLength] = {3, 1, 8};
|
||||
libwebm::Vp9CodecFeatures features;
|
||||
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0],
|
||||
kCodecPrivateLength, &features));
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level);
|
||||
EXPECT_EQ(8, features.bit_depth);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent,
|
||||
features.chroma_subsampling);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Vp9CodecChromaSubsamplingTest) {
|
||||
const int kCodecPrivateLength = 3;
|
||||
const uint8_t good_codec_private_profile[kCodecPrivateLength] = {4, 1, 0};
|
||||
libwebm::Vp9CodecFeatures features;
|
||||
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0],
|
||||
kCodecPrivateLength, &features));
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level);
|
||||
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth);
|
||||
EXPECT_EQ(0, features.chroma_subsampling);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Vp9CodecProfileLevelTest) {
|
||||
const int kCodecPrivateLength = 6;
|
||||
const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11};
|
||||
libwebm::Vp9CodecFeatures features;
|
||||
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0],
|
||||
kCodecPrivateLength, &features));
|
||||
EXPECT_EQ(1, features.profile);
|
||||
EXPECT_EQ(11, features.level);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Vp9CodecAllTest) {
|
||||
const int kCodecPrivateLength = 12;
|
||||
const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11,
|
||||
3, 1, 8, 4, 1, 0};
|
||||
libwebm::Vp9CodecFeatures features;
|
||||
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0],
|
||||
kCodecPrivateLength, &features));
|
||||
EXPECT_EQ(1, features.profile);
|
||||
EXPECT_EQ(11, features.level);
|
||||
EXPECT_EQ(8, features.bit_depth);
|
||||
EXPECT_EQ(0, features.chroma_subsampling);
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, Vp9CodecPrivateBadTest) {
|
||||
const int kCodecPrivateLength = 3;
|
||||
libwebm::Vp9CodecFeatures features;
|
||||
// Test invalid codec private data; all of these should return false.
|
||||
const uint8_t bad_codec_private[kCodecPrivateLength] = {0, 0, 0};
|
||||
EXPECT_FALSE(
|
||||
libwebm::ParseVpxCodecPrivate(NULL, kCodecPrivateLength, &features));
|
||||
EXPECT_FALSE(
|
||||
libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features));
|
||||
EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&bad_codec_private[0],
|
||||
kCodecPrivateLength, &features));
|
||||
const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11};
|
||||
|
||||
// Test parse of codec private chunks, but lie about length.
|
||||
EXPECT_FALSE(
|
||||
libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features));
|
||||
EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], 0,
|
||||
&features));
|
||||
EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0],
|
||||
kCodecPrivateLength, NULL));
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, InvalidTruncatedChapterString) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
||||
"invalid/chapters_truncated_chapter_string.mkv"));
|
||||
EXPECT_EQ(mkvparser::E_PARSE_FAILED, segment_->Load());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, InvalidTruncatedChapterString2) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
||||
"invalid/chapters_truncated_chapter_string_2.mkv"));
|
||||
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, InvalidFixedLacingSize) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
CreateSegmentNoHeaderChecks("invalid/fixed_lacing_bad_lace_size.mkv"));
|
||||
ASSERT_EQ(0, segment_->Load());
|
||||
const mkvparser::BlockEntry* block_entry = NULL;
|
||||
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID,
|
||||
segment_->GetFirst()->GetFirst(block_entry));
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, InvalidBlockEndsBeyondCluster) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
CreateSegmentNoHeaderChecks("invalid/block_ends_beyond_cluster.mkv"));
|
||||
ASSERT_EQ(0, segment_->Load());
|
||||
const mkvparser::BlockEntry* block_entry = NULL;
|
||||
EXPECT_EQ(0, segment_->GetFirst()->GetFirst(block_entry));
|
||||
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID,
|
||||
segment_->GetFirst()->GetNext(block_entry, block_entry));
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, InvalidBlockGroupBlockEndsBlockGroup) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
||||
"invalid/blockgroup_block_ends_beyond_blockgroup.mkv"));
|
||||
ASSERT_EQ(0, segment_->Load());
|
||||
const mkvparser::BlockEntry* block_entry = NULL;
|
||||
EXPECT_EQ(0, segment_->GetFirst()->GetFirst(block_entry));
|
||||
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID,
|
||||
segment_->GetFirst()->GetNext(block_entry, block_entry));
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, InvalidProjectionFloatOverflow) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
CreateSegmentNoHeaderChecks("invalid/projection_float_overflow.webm"));
|
||||
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load());
|
||||
}
|
||||
|
||||
TEST_F(ParserTest, InvalidPrimaryChromaticityParseFail) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
||||
"invalid/primarychromaticity_fieldtoolarge.webm"));
|
||||
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load());
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
// 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 "testing/test_util.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ios>
|
||||
#include <string>
|
||||
|
||||
#include "common/libwebm_util.h"
|
||||
#include "common/webmids.h"
|
||||
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
std::string GetTestDataDir() {
|
||||
const char* test_data_path = std::getenv("LIBWEBM_TEST_DATA_PATH");
|
||||
return test_data_path ? std::string(test_data_path) : std::string();
|
||||
}
|
||||
|
||||
std::string GetTestFilePath(const std::string& name) {
|
||||
const std::string libwebm_testdata_dir = GetTestDataDir();
|
||||
return libwebm_testdata_dir + "/" + name;
|
||||
}
|
||||
|
||||
bool CompareFiles(const std::string& file1, const std::string& file2) {
|
||||
const std::size_t kBlockSize = 4096;
|
||||
std::uint8_t buf1[kBlockSize] = {0};
|
||||
std::uint8_t buf2[kBlockSize] = {0};
|
||||
|
||||
libwebm::FilePtr f1 =
|
||||
libwebm::FilePtr(std::fopen(file1.c_str(), "rb"), libwebm::FILEDeleter());
|
||||
libwebm::FilePtr f2 =
|
||||
libwebm::FilePtr(std::fopen(file2.c_str(), "rb"), libwebm::FILEDeleter());
|
||||
|
||||
if (!f1.get() || !f2.get()) {
|
||||
// Files cannot match if one or both couldn't be opened.
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
const std::size_t r1 = std::fread(buf1, 1, kBlockSize, f1.get());
|
||||
const std::size_t r2 = std::fread(buf2, 1, kBlockSize, f2.get());
|
||||
|
||||
// TODO(fgalligan): Add output of which byte differs.
|
||||
if (r1 != r2 || std::memcmp(buf1, buf2, r1)) {
|
||||
return 0; // Files are not equal
|
||||
}
|
||||
} while (!std::feof(f1.get()) && !std::feof(f2.get()));
|
||||
|
||||
return std::feof(f1.get()) && std::feof(f2.get());
|
||||
}
|
||||
|
||||
bool HasCuePoints(const mkvparser::Segment* segment,
|
||||
std::int64_t* cues_offset) {
|
||||
if (!segment || !cues_offset) {
|
||||
return false;
|
||||
}
|
||||
using mkvparser::SeekHead;
|
||||
const SeekHead* const seek_head = segment->GetSeekHead();
|
||||
if (!seek_head) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::int64_t offset = 0;
|
||||
for (int i = 0; i < seek_head->GetCount(); ++i) {
|
||||
const SeekHead::Entry* const entry = seek_head->GetEntry(i);
|
||||
if (entry->id == libwebm::kMkvCues) {
|
||||
offset = entry->pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset <= 0) {
|
||||
// No Cues found.
|
||||
return false;
|
||||
}
|
||||
|
||||
*cues_offset = offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateCues(mkvparser::Segment* segment, mkvparser::IMkvReader* reader) {
|
||||
if (!segment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::int64_t cues_offset = 0;
|
||||
if (!HasCuePoints(segment, &cues_offset)) {
|
||||
// No cues to validate, everything is OK.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse Cues.
|
||||
long long cues_pos = 0; // NOLINT
|
||||
long cues_len = 0; // NOLINT
|
||||
if (segment->ParseCues(cues_offset, cues_pos, cues_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a pointer to the video track if it exists. Otherwise, we assume
|
||||
// that Cues are based on the first track (which is true for all our test
|
||||
// files).
|
||||
const mkvparser::Tracks* const tracks = segment->GetTracks();
|
||||
const mkvparser::Track* cues_track = tracks->GetTrackByIndex(0);
|
||||
for (int i = 1; i < static_cast<int>(tracks->GetTracksCount()); ++i) {
|
||||
const mkvparser::Track* const track = tracks->GetTrackByIndex(i);
|
||||
if (track->GetType() == mkvparser::Track::kVideo) {
|
||||
cues_track = track;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through Cues and verify if they are pointing to the correct
|
||||
// Cluster position.
|
||||
const mkvparser::Cues* const cues = segment->GetCues();
|
||||
const mkvparser::CuePoint* cue_point = NULL;
|
||||
while (cues->LoadCuePoint()) {
|
||||
if (!cue_point) {
|
||||
cue_point = cues->GetFirst();
|
||||
} else {
|
||||
cue_point = cues->GetNext(cue_point);
|
||||
}
|
||||
const mkvparser::CuePoint::TrackPosition* const track_position =
|
||||
cue_point->Find(cues_track);
|
||||
const long long cluster_pos = track_position->m_pos + // NOLINT
|
||||
segment->m_start;
|
||||
|
||||
// If a cluster does not begin at |cluster_pos|, then the file is
|
||||
// incorrect.
|
||||
long length; // NOLINT
|
||||
const std::int64_t id = mkvparser::ReadID(reader, cluster_pos, length);
|
||||
if (id != libwebm::kMkvCluster) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MkvParser::~MkvParser() {
|
||||
delete segment;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
bool ParseMkvFileReleaseParser(const std::string& webm_file,
|
||||
MkvParser* parser_out) {
|
||||
parser_out->reader = new (std::nothrow) mkvparser::MkvReader;
|
||||
mkvparser::MkvReader& reader = *parser_out->reader;
|
||||
if (!parser_out->reader || reader.Open(webm_file.c_str()) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long long pos = 0; // NOLINT
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
if (ebml_header.Parse(&reader, pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using mkvparser::Segment;
|
||||
Segment* segment_ptr = nullptr;
|
||||
if (Segment::CreateInstance(&reader, pos, segment_ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Segment> segment(segment_ptr);
|
||||
long result;
|
||||
if ((result = segment->Load()) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mkvparser::Cluster* cluster = segment->GetFirst();
|
||||
if (!cluster || cluster->EOS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (cluster && cluster->EOS() == false) {
|
||||
if (cluster->GetTimeCode() < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mkvparser::BlockEntry* block = nullptr;
|
||||
if (cluster->GetFirst(block) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (block != NULL && block->EOS() == false) {
|
||||
if (cluster->GetNext(block, block) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = segment->GetNext(cluster);
|
||||
}
|
||||
|
||||
parser_out->segment = segment.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseMkvFile(const std::string& webm_file) {
|
||||
MkvParser parser;
|
||||
const bool result = ParseMkvFileReleaseParser(webm_file, &parser);
|
||||
delete parser.segment;
|
||||
delete parser.reader;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace test
|
@ -1,88 +0,0 @@
|
||||
// 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_TESTING_TEST_UTIL_H_
|
||||
#define LIBWEBM_TESTING_TEST_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace mkvparser {
|
||||
class IMkvReader;
|
||||
class MkvReader;
|
||||
class Segment;
|
||||
}
|
||||
|
||||
namespace test {
|
||||
|
||||
// constants for muxer and parser tests
|
||||
const char kAppString[] = "mkvmuxer_unit_tests";
|
||||
const char kOpusCodecId[] = "A_OPUS";
|
||||
const char kVorbisCodecId[] = "A_VORBIS";
|
||||
const int kAudioTrackNumber = 2;
|
||||
const int kBitDepth = 2;
|
||||
const int kChannels = 2;
|
||||
const double kDuration = 2.345;
|
||||
const int kFrameLength = 10;
|
||||
const int kHeight = 180;
|
||||
const int kInvalidTrackNumber = 100;
|
||||
const std::uint64_t kOpusCodecDelay = 6500000;
|
||||
const std::size_t kOpusPrivateDataSizeMinimum = 19;
|
||||
const std::uint64_t kOpusSeekPreroll = 80000000;
|
||||
const char kMetadataCodecId[] = "D_WEBVTT/METADATA";
|
||||
const int kMetadataTrackNumber = 3;
|
||||
const int kMetadataTrackType = 0x21;
|
||||
const int kSampleRate = 30;
|
||||
const int kTimeCodeScale = 1000;
|
||||
const char kTrackName[] = "unit_test";
|
||||
const char kVP8CodecId[] = "V_VP8";
|
||||
const char kVP9CodecId[] = "V_VP9";
|
||||
const double kVideoFrameRate = 0.5;
|
||||
const int kVideoTrackNumber = 1;
|
||||
const int kWidth = 320;
|
||||
|
||||
// Returns the path to the test data directory by reading and returning the
|
||||
// contents the LIBWEBM_TESTDATA_DIR environment variable.
|
||||
std::string GetTestDataDir();
|
||||
|
||||
// Returns the absolute path to the file of |name| in LIBWEBM_TESTDATA_DIR.
|
||||
std::string GetTestFilePath(const std::string& name);
|
||||
|
||||
// Byte-wise comparison of two files |file1| and |file2|. Returns true if the
|
||||
// files match exactly, false otherwise.
|
||||
bool CompareFiles(const std::string& file1, const std::string& file2);
|
||||
|
||||
// Returns true and sets |cues_offset| to the cues location within the MKV file
|
||||
// parsed by |segment| when the MKV file has cue points.
|
||||
bool HasCuePoints(const mkvparser::Segment* segment, std::int64_t* cues_offset);
|
||||
|
||||
// Validates cue points. Assumes caller has already called Load() on |segment|.
|
||||
// Returns true when:
|
||||
// All cue points point at clusters, OR
|
||||
// Data parsed by |segment| has no cue points.
|
||||
bool ValidateCues(mkvparser::Segment* segment, mkvparser::IMkvReader* reader);
|
||||
|
||||
// Parses |webm_file| using mkvparser and returns true when file parses
|
||||
// successfully (all clusters and blocks can be successfully walked). Second
|
||||
// variant allows further interaction with the parsed file via transferring
|
||||
// ownership of the mkvparser Segment and MkvReader to the caller via
|
||||
// |parser_out|.
|
||||
struct MkvParser {
|
||||
MkvParser() = default;
|
||||
~MkvParser();
|
||||
mkvparser::Segment* segment = nullptr;
|
||||
mkvparser::MkvReader* reader = nullptr;
|
||||
};
|
||||
bool ParseMkvFile(const std::string& webm_file);
|
||||
bool ParseMkvFileReleaseParser(const std::string& webm_file,
|
||||
MkvParser* parser_out);
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif // LIBWEBM_TESTING_TEST_UTIL_H_
|
BIN
testing/testdata/accurate_cluster_duration.webm
vendored
BIN
testing/testdata/accurate_cluster_duration.webm
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
testing/testdata/bbb_480p_vp9_opus_1second.webm
vendored
BIN
testing/testdata/bbb_480p_vp9_opus_1second.webm
vendored
Binary file not shown.
BIN
testing/testdata/block_with_additional.webm
vendored
BIN
testing/testdata/block_with_additional.webm
vendored
Binary file not shown.
BIN
testing/testdata/chapters.webm
vendored
BIN
testing/testdata/chapters.webm
vendored
Binary file not shown.
BIN
testing/testdata/colour.webm
vendored
BIN
testing/testdata/colour.webm
vendored
Binary file not shown.
BIN
testing/testdata/cues_before_clusters.webm
vendored
BIN
testing/testdata/cues_before_clusters.webm
vendored
Binary file not shown.
BIN
testing/testdata/discard_padding.webm
vendored
BIN
testing/testdata/discard_padding.webm
vendored
Binary file not shown.
BIN
testing/testdata/estimate_duration.webm
vendored
BIN
testing/testdata/estimate_duration.webm
vendored
Binary file not shown.
BIN
testing/testdata/fixed_size_cluster_timecode.webm
vendored
BIN
testing/testdata/fixed_size_cluster_timecode.webm
vendored
Binary file not shown.
BIN
testing/testdata/force_new_cluster.webm
vendored
BIN
testing/testdata/force_new_cluster.webm
vendored
Binary file not shown.
24
testing/testdata/invalid/README.libwebm
vendored
24
testing/testdata/invalid/README.libwebm
vendored
@ -1,24 +0,0 @@
|
||||
Why the files in this directory are considered invalid:
|
||||
|
||||
block_ends_beyond_cluster.mkv -
|
||||
File containing a single cluster with two simple blocks. One valid, and the
|
||||
second reporting a size that would cause the block to end far beyond the end
|
||||
of its parent cluster.
|
||||
|
||||
blockgroup_block_ends_beyond_blockgroup.mkv -
|
||||
File containing a single cluster and two blockgroups. The first blockgroup is
|
||||
valid. The second blockgroup contains a block reporting a size that spans well
|
||||
past the block and the end of the file.
|
||||
|
||||
chapters_truncated_chapter_string.mkv -
|
||||
File with a Chapters element that ends with a ChapterAtom whose ChapterDisplay
|
||||
element contains a truncated ChapterString.
|
||||
|
||||
chapters_truncated_chapter_string_2.mkv -
|
||||
Nearly identical to chapters_truncated_chapter_string.mkv, but with a void
|
||||
element and a partial cluster. Causes mkvparser to fail in a slightly
|
||||
different manner.
|
||||
|
||||
fixed_lacing_bad_lace_size.mkv -
|
||||
File containing a BlockGroup with fixed lacing, but reports a total laced size
|
||||
that is not evenly divisible by the number of laced frames.
|
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user