(61d00a474) v0.9.7.1
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
# Generated with clang-format 7.0.1
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
IncludeCategories:
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
@@ -0,0 +1,6 @@
|
||||
*.sln eol=crlf
|
||||
*.vcproj eol=crlf
|
||||
*.vsprops eol=crlf
|
||||
*.vcxproj eol=crlf
|
||||
*.mkv -text -diff
|
||||
*.webm -text -diff
|
||||
@@ -0,0 +1,35 @@
|
||||
*.mkv
|
||||
*.MKV
|
||||
core
|
||||
*.a
|
||||
*.d
|
||||
*.so*
|
||||
*.o
|
||||
*~
|
||||
*.swp
|
||||
*.ncb
|
||||
*.user
|
||||
*.suo
|
||||
*.exe
|
||||
/*.webm
|
||||
Debug
|
||||
Release
|
||||
*.sdf
|
||||
*.opensdf
|
||||
ipch
|
||||
dumpvtt
|
||||
mkvmuxer_sample
|
||||
mkvparser_sample
|
||||
vttdemux
|
||||
mkvmuxer_tests
|
||||
mkvparser_tests
|
||||
webm_info
|
||||
webm2pes
|
||||
webm2pes_tests
|
||||
webm2ts
|
||||
vp9_header_parser_tests
|
||||
vp9_level_stats_tests
|
||||
Makefile
|
||||
CMakeFiles
|
||||
CMakeCache.txt
|
||||
*.cmake
|
||||
@@ -0,0 +1,4 @@
|
||||
# Names should be added to this file like so:
|
||||
# Name or Organization <email address>
|
||||
|
||||
Google Inc.
|
||||
@@ -0,0 +1,17 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libwebm
|
||||
LOCAL_CPPFLAGS:=-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
|
||||
LOCAL_CPPFLAGS+=-D__STDC_LIMIT_MACROS -std=c++11
|
||||
LOCAL_C_INCLUDES:= $(LOCAL_PATH)
|
||||
LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)
|
||||
|
||||
LOCAL_SRC_FILES:= common/file_util.cc \
|
||||
common/hdr_util.cc \
|
||||
mkvparser/mkvparser.cc \
|
||||
mkvparser/mkvreader.cc \
|
||||
mkvmuxer/mkvmuxer.cc \
|
||||
mkvmuxer/mkvmuxerutil.cc \
|
||||
mkvmuxer/mkvwriter.cc
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
@@ -0,0 +1,440 @@
|
||||
## 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)
|
||||
|
||||
if(WIN32)
|
||||
require_cxx_flag_nomsvc("-std=gnu++11")
|
||||
else()
|
||||
require_cxx_flag_nomsvc("-std=c++11")
|
||||
endif()
|
||||
|
||||
add_cxx_preproc_definition("__STDC_CONSTANT_MACROS")
|
||||
add_cxx_preproc_definition("__STDC_FORMAT_MACROS")
|
||||
add_cxx_preproc_definition("__STDC_LIMIT_MACROS")
|
||||
|
||||
# Set up compiler flags and build properties.
|
||||
include_directories("${LIBWEBM_SRC_DIR}")
|
||||
|
||||
if (MSVC)
|
||||
add_cxx_flag_if_supported("/W4")
|
||||
# Disable MSVC warnings that suggest making code non-portable.
|
||||
add_cxx_flag_if_supported("/wd4996")
|
||||
if (ENABLE_WERROR)
|
||||
add_cxx_flag_if_supported("/WX")
|
||||
endif ()
|
||||
else ()
|
||||
add_cxx_flag_if_supported("-Wall")
|
||||
add_cxx_flag_if_supported("-Wextra")
|
||||
add_cxx_flag_if_supported("-Wnarrowing")
|
||||
add_cxx_flag_if_supported("-Wno-deprecated")
|
||||
add_cxx_flag_if_supported("-Wshorten-64-to-32")
|
||||
if (ENABLE_WERROR)
|
||||
add_cxx_flag_if_supported("-Werror")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Source list variables.
|
||||
set(dumpvtt_sources "${LIBWEBM_SRC_DIR}/dumpvtt.cc")
|
||||
|
||||
set(libwebm_common_sources
|
||||
"${LIBWEBM_SRC_DIR}/common/file_util.cc"
|
||||
"${LIBWEBM_SRC_DIR}/common/file_util.h"
|
||||
"${LIBWEBM_SRC_DIR}/common/hdr_util.cc"
|
||||
"${LIBWEBM_SRC_DIR}/common/hdr_util.h"
|
||||
"${LIBWEBM_SRC_DIR}/common/webmids.h")
|
||||
|
||||
set(mkvmuxer_sources
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxer.cc"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxer.h"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxertypes.h"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxerutil.cc"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer/mkvmuxerutil.h"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer/mkvwriter.cc"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer/mkvwriter.h"
|
||||
"${LIBWEBM_SRC_DIR}/common/webmids.h")
|
||||
|
||||
set(mkvmuxer_sample_sources
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer_sample.cc"
|
||||
"${LIBWEBM_SRC_DIR}/sample_muxer_metadata.cc"
|
||||
"${LIBWEBM_SRC_DIR}/sample_muxer_metadata.h")
|
||||
|
||||
set(mkvmuxer_tests_sources
|
||||
"${LIBWEBM_SRC_DIR}/testing/mkvmuxer_tests.cc"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.cc"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.h")
|
||||
|
||||
set(mkvparser_sources
|
||||
"${LIBWEBM_SRC_DIR}/mkvparser/mkvparser.cc"
|
||||
"${LIBWEBM_SRC_DIR}/mkvparser/mkvparser.h"
|
||||
"${LIBWEBM_SRC_DIR}/mkvparser/mkvreader.cc"
|
||||
"${LIBWEBM_SRC_DIR}/mkvparser/mkvreader.h"
|
||||
"${LIBWEBM_SRC_DIR}/common/webmids.h")
|
||||
|
||||
set(mkvparser_sample_sources "${LIBWEBM_SRC_DIR}/mkvparser_sample.cc")
|
||||
set(mkvparser_tests_sources
|
||||
"${LIBWEBM_SRC_DIR}/testing/mkvparser_tests.cc"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.cc"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.h")
|
||||
|
||||
set(vp9_header_parser_tests_sources
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_header_parser_tests.cc"
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc"
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.cc"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.h")
|
||||
|
||||
set(vp9_level_stats_tests_sources
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc"
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h"
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_level_stats_tests.cc"
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_level_stats.cc"
|
||||
"${LIBWEBM_SRC_DIR}/common/vp9_level_stats.h"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.cc"
|
||||
"${LIBWEBM_SRC_DIR}/testing/test_util.h")
|
||||
|
||||
set(vttdemux_sources
|
||||
"${LIBWEBM_SRC_DIR}/vttdemux.cc"
|
||||
"${LIBWEBM_SRC_DIR}/webvtt/webvttparser.cc"
|
||||
"${LIBWEBM_SRC_DIR}/webvtt/webvttparser.h")
|
||||
|
||||
set(webm_parser_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 ()
|
||||
@@ -0,0 +1,30 @@
|
||||
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
CXX := g++
|
||||
DEFINES := -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
|
||||
DEFINES += -D__STDC_LIMIT_MACROS
|
||||
INCLUDES := -I.
|
||||
CXXFLAGS := -W -Wall -g -std=c++11
|
||||
ALL_CXXFLAGS := -MMD -MP $(DEFINES) $(INCLUDES) $(CXXFLAGS)
|
||||
LIBWEBMA := libwebm.a
|
||||
LIBWEBMSO := libwebm.so
|
||||
WEBMOBJS := mkvmuxer/mkvmuxer.o mkvmuxer/mkvmuxerutil.o mkvmuxer/mkvwriter.o
|
||||
WEBMOBJS += mkvparser/mkvparser.o mkvparser/mkvreader.o
|
||||
WEBMOBJS += common/file_util.o common/hdr_util.o
|
||||
OBJSA := $(WEBMOBJS:.o=_a.o)
|
||||
OBJSSO := $(WEBMOBJS:.o=_so.o)
|
||||
VTTOBJS := webvtt/vttreader.o webvtt/webvttparser.o sample_muxer_metadata.o
|
||||
EXEOBJS := mkvmuxer_sample.o mkvparser_sample.o dumpvtt.o vttdemux.o
|
||||
EXES := mkvparser_sample mkvmuxer_sample dumpvtt vttdemux
|
||||
DEPS := $(WEBMOBJS:.o=.d) $(OBJECTS1:.o=.d) $(OBJECTS2:.o=.d)
|
||||
DEPS += $(OBJECTS3:.o=.d) $(OBJECTS4:.o=.d) $(OBJSA:.o=.d) $(OBJSSO:.o=.d)
|
||||
DEPS += $(VTTOBJS:.o=.d) $(EXEOBJS:.o=.d)
|
||||
CLEAN := $(EXEOBJS) $(VTTOBJS) $(WEBMOBJS) $(OBJSA) $(OBJSSO) $(LIBWEBMA)
|
||||
CLEAN += $(LIBWEBMSO) $(EXES) $(DEPS) $(INFOOBJS)
|
||||
|
||||
all: $(EXES)
|
||||
|
||||
mkvparser_sample: mkvparser_sample.o $(LIBWEBMA)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
mkvmuxer_sample: mkvmuxer_sample.o $(VTTOBJS) $(LIBWEBMA)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
dumpvtt: dumpvtt.o $(VTTOBJS) $(WEBMOBJS)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
vttdemux: vttdemux.o $(VTTOBJS) $(LIBWEBMA)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
shared: $(LIBWEBMSO)
|
||||
|
||||
libwebm.a: $(OBJSA)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
libwebm.so: $(OBJSSO)
|
||||
$(CXX) $(ALL_CXXFLAGS) -shared $(OBJSSO) -o $(LIBWEBMSO)
|
||||
|
||||
%.o: %.cc
|
||||
$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
|
||||
%_a.o: %.cc
|
||||
$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
|
||||
%_so.o: %.cc
|
||||
$(CXX) -c $(ALL_CXXFLAGS) -fPIC $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) -f $(CLEAN) Makefile.bak
|
||||
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
@@ -0,0 +1,23 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
------------------------------------
|
||||
|
||||
"These implementations" means the copyrightable works that implement the WebM
|
||||
codecs distributed by Google as part of the WebM Project.
|
||||
|
||||
Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
|
||||
royalty-free, irrevocable (except as stated in this section) patent license to
|
||||
make, have made, use, offer to sell, sell, import, transfer, and otherwise
|
||||
run, modify and propagate the contents of these implementations of WebM, where
|
||||
such license applies only to those patent claims, both currently owned by
|
||||
Google and acquired in the future, licensable by Google that are necessarily
|
||||
infringed by these implementations of WebM. This grant does not include claims
|
||||
that would be infringed only as a consequence of further modification of these
|
||||
implementations. If you or your agent or exclusive licensee institute or order
|
||||
or agree to the institution of patent litigation or any other patent
|
||||
enforcement activity against any entity (including a cross-claim or
|
||||
counterclaim in a lawsuit) alleging that any of these implementations of WebM
|
||||
or any code incorporated within any of these implementations of WebM
|
||||
constitute direct or contributory patent infringement, or inducement of
|
||||
patent infringement, then any patent rights granted to you under this License
|
||||
for these implementations of WebM shall terminate as of the date such
|
||||
litigation is filed.
|
||||
@@ -0,0 +1,143 @@
|
||||
Building Libwebm
|
||||
|
||||
To build libwebm you must first create project files. To do this run cmake
|
||||
and pass it the path to your libwebm repo.
|
||||
|
||||
Makefile.unix can be used as a fallback on systems that cmake does not
|
||||
support.
|
||||
|
||||
|
||||
CMake Basics
|
||||
|
||||
To generate project/make files for the default toolchain on your system simply
|
||||
run cmake with the path to the libwebm repo:
|
||||
|
||||
$ cmake path/to/libwebm
|
||||
|
||||
On Windows the above command will produce Visual Studio project files for the
|
||||
newest Visual Studio detected on the system. On Mac OS X and Linux systems, the
|
||||
above command will produce a makefile.
|
||||
|
||||
To control what types of projects are generated the -G parameter is added to
|
||||
the cmake command line. This argument must be followed by the name of a
|
||||
generator. Running cmake with the --help argument will list the available
|
||||
generators for your system.
|
||||
|
||||
On Mac OS X you would run the following command to generate Xcode projects:
|
||||
|
||||
$ cmake path/to/libwebm -G Xcode
|
||||
|
||||
On a Windows box you would run the following command to generate Visual Studio
|
||||
2013 projects:
|
||||
|
||||
$ cmake path/to/libwebm -G "Visual Studio 12"
|
||||
|
||||
To generate 64-bit Windows Visual Studio 2013 projects:
|
||||
|
||||
$ cmake path/to/libwebm "Visual Studio 12 Win64"
|
||||
|
||||
|
||||
CMake Makefiles: Debugging and Optimization
|
||||
|
||||
Unlike Visual Studio and Xcode projects, the build configuration for make builds
|
||||
is controlled when you run cmake. The following examples demonstrate various
|
||||
build configurations.
|
||||
|
||||
Omitting the build type produces makefiles that use build flags containing
|
||||
neither optimization nor debug flags:
|
||||
$ cmake path/to/libwebm
|
||||
|
||||
A makefile using release (optimized) flags is produced like this:
|
||||
$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=release
|
||||
|
||||
A release build with debug info can be produced as well:
|
||||
$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=relwithdebinfo
|
||||
|
||||
And your standard debug build will be produced using:
|
||||
$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=debug
|
||||
|
||||
|
||||
Tests
|
||||
|
||||
To enable libwebm tests add -DENABLE_TESTS=ON CMake generation command line. For
|
||||
example:
|
||||
|
||||
$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON
|
||||
|
||||
Libwebm tests depend on googletest. By default googletest is expected to be a
|
||||
sibling directory of the Libwebm repository. To change that, update your CMake
|
||||
command to be similar to the following:
|
||||
|
||||
$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON \
|
||||
-DGTEST_SRC_DIR=/path/to/googletest
|
||||
|
||||
The tests rely upon the LIBWEBM_TEST_DATA_PATH environment variable to locate
|
||||
test input. The following example demonstrates running the muxer tests from the
|
||||
build directory:
|
||||
|
||||
$ LIBWEBM_TEST_DATA_PATH=path/to/libwebm/testing/testdata ./mkvmuxer_tests
|
||||
|
||||
Note: Libwebm Googletest integration was built with googletest from
|
||||
https://github.com/google/googletest.git at git revision
|
||||
ddb8012eb48bc203aa93dcc2b22c1db516302b29.
|
||||
|
||||
|
||||
CMake Include-what-you-use integration
|
||||
|
||||
Include-what-you-use is an analysis tool that helps ensure libwebm includes the
|
||||
C/C++ header files actually in use. To enable the integration support
|
||||
ENABLE_IWYU must be turned on at cmake run time:
|
||||
|
||||
$ cmake path/to/libwebm -G "Unix Makefiles" -DENABLE_IWYU=ON
|
||||
|
||||
This adds the iwyu target to the build. To run include-what-you-use:
|
||||
|
||||
$ make iwyu
|
||||
|
||||
The following requirements must be met for ENABLE_IWYU to enable the iwyu
|
||||
target:
|
||||
|
||||
1. include-what-you-use and iwyu_tool.py must be in your PATH.
|
||||
2. A python interpreter must be on the system and available to CMake.
|
||||
|
||||
The values of the following variables are used to determine if the requirements
|
||||
have been met. Values to the right of the equals sign are what a successful run
|
||||
might look like:
|
||||
iwyu_path=/path/to/iwyu_tool.py
|
||||
iwyu_tool_path=/path/to/include-what-you-use
|
||||
PYTHONINTERP_FOUND=TRUE
|
||||
|
||||
An empty PYTHONINTERP_FOUND, or iwyu_path/iwyu_tool_path suffixed with NOTFOUND
|
||||
are failures.
|
||||
|
||||
For Include-what-you-use setup instructions, see:
|
||||
https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/InstructionsForUsers.md
|
||||
|
||||
If, when building the iwyu target, compile errors reporting failures loading
|
||||
standard include files occur, one solution can be found here:
|
||||
https://github.com/include-what-you-use/include-what-you-use/issues/100
|
||||
|
||||
|
||||
CMake cross compile
|
||||
To cross compile libwebm for Windows using mingw-w64 run cmake with the
|
||||
following arguments:
|
||||
|
||||
$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
|
||||
path/to/libwebm
|
||||
|
||||
Note1: As of this writing googletest will not build via mingw-w64 without
|
||||
disabling pthreads.
|
||||
googletest hash: d225acc90bc3a8c420a9bcd1f033033c1ccd7fe0
|
||||
|
||||
To build with tests when using mingw-w64 use the following arguments when
|
||||
running CMake:
|
||||
|
||||
$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
|
||||
-DENABLE_TESTS=ON -Dgtest_disable_pthreads=ON path/to/libwebm
|
||||
|
||||
Note2: i686-w64-mingw32 is the default compiler. This can be controlled using
|
||||
the MINGW_PREFIX variable:
|
||||
|
||||
$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
|
||||
-DMINGW_PREFIX=x86_64-w64-mingw32 path/to/libwebm
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# This file is used by git cl to get repository specific information.
|
||||
GERRIT_HOST: True
|
||||
CODE_REVIEW_SERVER: chromium-review.googlesource.com
|
||||
GERRIT_SQUASH_UPLOADS: False
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/bin/sh
|
||||
##
|
||||
## Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
##
|
||||
set -e
|
||||
devnull='> /dev/null 2>&1'
|
||||
|
||||
readonly ORIG_PWD="$(pwd)"
|
||||
|
||||
elog() {
|
||||
echo "${0##*/} failed because: $@" 1>&2
|
||||
}
|
||||
|
||||
vlog() {
|
||||
if [ "${VERBOSE}" = "yes" ]; then
|
||||
echo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Terminates script when name of current directory does not match $1.
|
||||
check_dir() {
|
||||
current_dir="$(pwd)"
|
||||
required_dir="$1"
|
||||
if [ "${current_dir##*/}" != "${required_dir}" ]; then
|
||||
elog "This script must be run from the ${required_dir} directory."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Terminates the script when $1 is not in $PATH. Any arguments required for
|
||||
# the tool being tested to return a successful exit code can be passed as
|
||||
# additional arguments.
|
||||
check_tool() {
|
||||
tool="$1"
|
||||
shift
|
||||
tool_args="$@"
|
||||
if ! eval "${tool}" ${tool_args} > /dev/null 2>&1; then
|
||||
elog "${tool} must be in your path."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Echoes git describe output for the directory specified by $1 to stdout.
|
||||
git_describe() {
|
||||
git_dir="$1"
|
||||
check_git
|
||||
echo $(git -C "${git_dir}" describe)
|
||||
}
|
||||
|
||||
# Echoes current git revision for the directory specifed by $1 to stdout.
|
||||
git_revision() {
|
||||
git_dir="$1"
|
||||
check_git
|
||||
echo $(git -C "${git_dir}" rev-parse HEAD)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "common/file_util.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h> // close()
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <string>
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
std::string GetTempFileName() {
|
||||
#if !defined _MSC_VER && !defined __MINGW32__
|
||||
std::string temp_file_name_template_str =
|
||||
std::string(std::getenv("TEST_TMPDIR") ? std::getenv("TEST_TMPDIR")
|
||||
: ".") +
|
||||
"/libwebm_temp.XXXXXX";
|
||||
char* temp_file_name_template =
|
||||
new char[temp_file_name_template_str.length() + 1];
|
||||
memset(temp_file_name_template, 0, temp_file_name_template_str.length() + 1);
|
||||
temp_file_name_template_str.copy(temp_file_name_template,
|
||||
temp_file_name_template_str.length(), 0);
|
||||
int fd = mkstemp(temp_file_name_template);
|
||||
std::string temp_file_name =
|
||||
(fd != -1) ? std::string(temp_file_name_template) : std::string();
|
||||
delete[] temp_file_name_template;
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
return temp_file_name;
|
||||
#else
|
||||
char tmp_file_name[_MAX_PATH];
|
||||
#if defined _MSC_VER || defined MINGW_HAS_SECURE_API
|
||||
errno_t err = tmpnam_s(tmp_file_name);
|
||||
#else
|
||||
char* fname_pointer = tmpnam(tmp_file_name);
|
||||
int err = (fname_pointer == &tmp_file_name[0]) ? 0 : -1;
|
||||
#endif
|
||||
if (err == 0) {
|
||||
return std::string(tmp_file_name);
|
||||
}
|
||||
return std::string();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t GetFileSize(const std::string& file_name) {
|
||||
uint64_t file_size = 0;
|
||||
#ifndef _MSC_VER
|
||||
struct stat st;
|
||||
st.st_size = 0;
|
||||
if (stat(file_name.c_str(), &st) == 0) {
|
||||
#else
|
||||
struct _stat st;
|
||||
st.st_size = 0;
|
||||
if (_stat(file_name.c_str(), &st) == 0) {
|
||||
#endif
|
||||
file_size = st.st_size;
|
||||
}
|
||||
return file_size;
|
||||
}
|
||||
|
||||
bool GetFileContents(const std::string& file_name, std::string* contents) {
|
||||
std::ifstream file(file_name.c_str());
|
||||
*contents = std::string(static_cast<size_t>(GetFileSize(file_name)), 0);
|
||||
if (file.good() && contents->size()) {
|
||||
file.read(&(*contents)[0], contents->size());
|
||||
}
|
||||
return !file.fail();
|
||||
}
|
||||
|
||||
TempFileDeleter::TempFileDeleter() { file_name_ = GetTempFileName(); }
|
||||
|
||||
TempFileDeleter::~TempFileDeleter() {
|
||||
std::ifstream file(file_name_.c_str());
|
||||
if (file.good()) {
|
||||
file.close();
|
||||
std::remove(file_name_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_COMMON_FILE_UTIL_H_
|
||||
#define LIBWEBM_COMMON_FILE_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "mkvmuxer/mkvmuxertypes.h" // LIBWEBM_DISALLOW_COPY_AND_ASSIGN()
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
// Returns a temporary file name.
|
||||
std::string GetTempFileName();
|
||||
|
||||
// Returns size of file specified by |file_name|, or 0 upon failure.
|
||||
uint64_t GetFileSize(const std::string& file_name);
|
||||
|
||||
// Gets the contents file_name as a string. Returns false on error.
|
||||
bool GetFileContents(const std::string& file_name, std::string* contents);
|
||||
|
||||
// Manages life of temporary file specified at time of construction. Deletes
|
||||
// file upon destruction.
|
||||
class TempFileDeleter {
|
||||
public:
|
||||
TempFileDeleter();
|
||||
explicit TempFileDeleter(std::string file_name) : file_name_(file_name) {}
|
||||
~TempFileDeleter();
|
||||
const std::string& name() const { return file_name_; }
|
||||
|
||||
private:
|
||||
std::string file_name_;
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TempFileDeleter);
|
||||
};
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_COMMON_FILE_UTIL_H_
|
||||
@@ -0,0 +1,220 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "hdr_util.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
|
||||
#include "mkvparser/mkvparser.h"
|
||||
|
||||
namespace libwebm {
|
||||
const int Vp9CodecFeatures::kValueNotPresent = INT_MAX;
|
||||
|
||||
bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc,
|
||||
PrimaryChromaticityPtr* muxer_pc) {
|
||||
muxer_pc->reset(new (std::nothrow)
|
||||
mkvmuxer::PrimaryChromaticity(parser_pc.x, parser_pc.y));
|
||||
if (!muxer_pc->get())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MasteringMetadataValuePresent(double value) {
|
||||
return value != mkvparser::MasteringMetadata::kValueNotPresent;
|
||||
}
|
||||
|
||||
bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm,
|
||||
mkvmuxer::MasteringMetadata* muxer_mm) {
|
||||
if (MasteringMetadataValuePresent(parser_mm.luminance_max))
|
||||
muxer_mm->set_luminance_max(parser_mm.luminance_max);
|
||||
if (MasteringMetadataValuePresent(parser_mm.luminance_min))
|
||||
muxer_mm->set_luminance_min(parser_mm.luminance_min);
|
||||
|
||||
PrimaryChromaticityPtr r_ptr(nullptr);
|
||||
PrimaryChromaticityPtr g_ptr(nullptr);
|
||||
PrimaryChromaticityPtr b_ptr(nullptr);
|
||||
PrimaryChromaticityPtr wp_ptr(nullptr);
|
||||
|
||||
if (parser_mm.r) {
|
||||
if (!CopyPrimaryChromaticity(*parser_mm.r, &r_ptr))
|
||||
return false;
|
||||
}
|
||||
if (parser_mm.g) {
|
||||
if (!CopyPrimaryChromaticity(*parser_mm.g, &g_ptr))
|
||||
return false;
|
||||
}
|
||||
if (parser_mm.b) {
|
||||
if (!CopyPrimaryChromaticity(*parser_mm.b, &b_ptr))
|
||||
return false;
|
||||
}
|
||||
if (parser_mm.white_point) {
|
||||
if (!CopyPrimaryChromaticity(*parser_mm.white_point, &wp_ptr))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!muxer_mm->SetChromaticity(r_ptr.get(), g_ptr.get(), b_ptr.get(),
|
||||
wp_ptr.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ColourValuePresent(long long value) {
|
||||
return value != mkvparser::Colour::kValueNotPresent;
|
||||
}
|
||||
|
||||
bool CopyColour(const mkvparser::Colour& parser_colour,
|
||||
mkvmuxer::Colour* muxer_colour) {
|
||||
if (!muxer_colour)
|
||||
return false;
|
||||
|
||||
if (ColourValuePresent(parser_colour.matrix_coefficients))
|
||||
muxer_colour->set_matrix_coefficients(parser_colour.matrix_coefficients);
|
||||
if (ColourValuePresent(parser_colour.bits_per_channel))
|
||||
muxer_colour->set_bits_per_channel(parser_colour.bits_per_channel);
|
||||
if (ColourValuePresent(parser_colour.chroma_subsampling_horz)) {
|
||||
muxer_colour->set_chroma_subsampling_horz(
|
||||
parser_colour.chroma_subsampling_horz);
|
||||
}
|
||||
if (ColourValuePresent(parser_colour.chroma_subsampling_vert)) {
|
||||
muxer_colour->set_chroma_subsampling_vert(
|
||||
parser_colour.chroma_subsampling_vert);
|
||||
}
|
||||
if (ColourValuePresent(parser_colour.cb_subsampling_horz))
|
||||
muxer_colour->set_cb_subsampling_horz(parser_colour.cb_subsampling_horz);
|
||||
if (ColourValuePresent(parser_colour.cb_subsampling_vert))
|
||||
muxer_colour->set_cb_subsampling_vert(parser_colour.cb_subsampling_vert);
|
||||
if (ColourValuePresent(parser_colour.chroma_siting_horz))
|
||||
muxer_colour->set_chroma_siting_horz(parser_colour.chroma_siting_horz);
|
||||
if (ColourValuePresent(parser_colour.chroma_siting_vert))
|
||||
muxer_colour->set_chroma_siting_vert(parser_colour.chroma_siting_vert);
|
||||
if (ColourValuePresent(parser_colour.range))
|
||||
muxer_colour->set_range(parser_colour.range);
|
||||
if (ColourValuePresent(parser_colour.transfer_characteristics)) {
|
||||
muxer_colour->set_transfer_characteristics(
|
||||
parser_colour.transfer_characteristics);
|
||||
}
|
||||
if (ColourValuePresent(parser_colour.primaries))
|
||||
muxer_colour->set_primaries(parser_colour.primaries);
|
||||
if (ColourValuePresent(parser_colour.max_cll))
|
||||
muxer_colour->set_max_cll(parser_colour.max_cll);
|
||||
if (ColourValuePresent(parser_colour.max_fall))
|
||||
muxer_colour->set_max_fall(parser_colour.max_fall);
|
||||
|
||||
if (parser_colour.mastering_metadata) {
|
||||
mkvmuxer::MasteringMetadata muxer_mm;
|
||||
if (!CopyMasteringMetadata(*parser_colour.mastering_metadata, &muxer_mm))
|
||||
return false;
|
||||
if (!muxer_colour->SetMasteringMetadata(muxer_mm))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Format of VPx private data:
|
||||
//
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID Byte | Length | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
// | |
|
||||
// : Bytes 1..Length of Codec Feature :
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// ID Byte Format
|
||||
// ID byte is an unsigned byte.
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |X| ID |
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// The X bit is reserved.
|
||||
//
|
||||
// See the following link for more information:
|
||||
// http://www.webmproject.org/vp9/profiles/
|
||||
bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length,
|
||||
Vp9CodecFeatures* features) {
|
||||
const int kVpxCodecPrivateMinLength = 3;
|
||||
if (!private_data || !features || length < kVpxCodecPrivateMinLength)
|
||||
return false;
|
||||
|
||||
const uint8_t kVp9ProfileId = 1;
|
||||
const uint8_t kVp9LevelId = 2;
|
||||
const uint8_t kVp9BitDepthId = 3;
|
||||
const uint8_t kVp9ChromaSubsamplingId = 4;
|
||||
const int kVpxFeatureLength = 1;
|
||||
int offset = 0;
|
||||
|
||||
// Set features to not set.
|
||||
features->profile = Vp9CodecFeatures::kValueNotPresent;
|
||||
features->level = Vp9CodecFeatures::kValueNotPresent;
|
||||
features->bit_depth = Vp9CodecFeatures::kValueNotPresent;
|
||||
features->chroma_subsampling = Vp9CodecFeatures::kValueNotPresent;
|
||||
do {
|
||||
const uint8_t id_byte = private_data[offset++];
|
||||
const uint8_t length_byte = private_data[offset++];
|
||||
if (length_byte != kVpxFeatureLength)
|
||||
return false;
|
||||
if (id_byte == kVp9ProfileId) {
|
||||
const int priv_profile = static_cast<int>(private_data[offset++]);
|
||||
if (priv_profile < 0 || priv_profile > 3)
|
||||
return false;
|
||||
if (features->profile != Vp9CodecFeatures::kValueNotPresent &&
|
||||
features->profile != priv_profile) {
|
||||
return false;
|
||||
}
|
||||
features->profile = priv_profile;
|
||||
} else if (id_byte == kVp9LevelId) {
|
||||
const int priv_level = static_cast<int>(private_data[offset++]);
|
||||
|
||||
const int kNumLevels = 14;
|
||||
const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40,
|
||||
41, 50, 51, 52, 60, 61, 62};
|
||||
|
||||
for (int i = 0; i < kNumLevels; ++i) {
|
||||
if (priv_level == levels[i]) {
|
||||
if (features->level != Vp9CodecFeatures::kValueNotPresent &&
|
||||
features->level != priv_level) {
|
||||
return false;
|
||||
}
|
||||
features->level = priv_level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (features->level == Vp9CodecFeatures::kValueNotPresent)
|
||||
return false;
|
||||
} else if (id_byte == kVp9BitDepthId) {
|
||||
const int priv_profile = static_cast<int>(private_data[offset++]);
|
||||
if (priv_profile != 8 && priv_profile != 10 && priv_profile != 12)
|
||||
return false;
|
||||
if (features->bit_depth != Vp9CodecFeatures::kValueNotPresent &&
|
||||
features->bit_depth != priv_profile) {
|
||||
return false;
|
||||
}
|
||||
features->bit_depth = priv_profile;
|
||||
} else if (id_byte == kVp9ChromaSubsamplingId) {
|
||||
const int priv_profile = static_cast<int>(private_data[offset++]);
|
||||
if (priv_profile != 0 && priv_profile != 2 && priv_profile != 3)
|
||||
return false;
|
||||
if (features->chroma_subsampling != Vp9CodecFeatures::kValueNotPresent &&
|
||||
features->chroma_subsampling != priv_profile) {
|
||||
return false;
|
||||
}
|
||||
features->chroma_subsampling = priv_profile;
|
||||
} else {
|
||||
// Invalid ID.
|
||||
return false;
|
||||
}
|
||||
} while (offset + kVpxCodecPrivateMinLength <= length);
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_COMMON_HDR_UTIL_H_
|
||||
#define LIBWEBM_COMMON_HDR_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "mkvmuxer/mkvmuxer.h"
|
||||
|
||||
namespace mkvparser {
|
||||
struct Colour;
|
||||
struct MasteringMetadata;
|
||||
struct PrimaryChromaticity;
|
||||
} // namespace mkvparser
|
||||
|
||||
namespace libwebm {
|
||||
// Utility types and functions for working with the Colour element and its
|
||||
// children. Copiers return true upon success. Presence functions return true
|
||||
// when the specified element is present.
|
||||
|
||||
// TODO(tomfinegan): These should be moved to libwebm_utils once c++11 is
|
||||
// required by libwebm.
|
||||
|
||||
// Features of the VP9 codec that may be set in the CodecPrivate of a VP9 video
|
||||
// stream. A value of kValueNotPresent represents that the value was not set in
|
||||
// the CodecPrivate.
|
||||
struct Vp9CodecFeatures {
|
||||
static const int kValueNotPresent;
|
||||
|
||||
Vp9CodecFeatures()
|
||||
: profile(kValueNotPresent),
|
||||
level(kValueNotPresent),
|
||||
bit_depth(kValueNotPresent),
|
||||
chroma_subsampling(kValueNotPresent) {}
|
||||
~Vp9CodecFeatures() {}
|
||||
|
||||
int profile;
|
||||
int level;
|
||||
int bit_depth;
|
||||
int chroma_subsampling;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<mkvmuxer::PrimaryChromaticity> PrimaryChromaticityPtr;
|
||||
|
||||
bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc,
|
||||
PrimaryChromaticityPtr* muxer_pc);
|
||||
|
||||
bool MasteringMetadataValuePresent(double value);
|
||||
|
||||
bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm,
|
||||
mkvmuxer::MasteringMetadata* muxer_mm);
|
||||
|
||||
bool ColourValuePresent(long long value);
|
||||
|
||||
bool CopyColour(const mkvparser::Colour& parser_colour,
|
||||
mkvmuxer::Colour* muxer_colour);
|
||||
|
||||
// Returns true if |features| is set to one or more valid values.
|
||||
bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length,
|
||||
Vp9CodecFeatures* features);
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_COMMON_HDR_UTIL_H_
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "common/indent.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
Indent::Indent(int indent) : indent_(indent), indent_str_() { Update(); }
|
||||
|
||||
void Indent::Adjust(int indent) {
|
||||
indent_ += indent;
|
||||
if (indent_ < 0)
|
||||
indent_ = 0;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void Indent::Update() { indent_str_ = std::string(indent_, ' '); }
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef LIBWEBM_COMMON_INDENT_H_
|
||||
#define LIBWEBM_COMMON_INDENT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "mkvmuxer/mkvmuxertypes.h"
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
const int kIncreaseIndent = 2;
|
||||
const int kDecreaseIndent = -2;
|
||||
|
||||
// Used for formatting output so objects only have to keep track of spacing
|
||||
// within their scope.
|
||||
class Indent {
|
||||
public:
|
||||
explicit Indent(int indent);
|
||||
|
||||
// Changes the number of spaces output. The value adjusted is relative to
|
||||
// |indent_|.
|
||||
void Adjust(int indent);
|
||||
|
||||
std::string indent_str() const { return indent_str_; }
|
||||
|
||||
private:
|
||||
// Called after |indent_| is changed. This will set |indent_str_| to the
|
||||
// proper number of spaces.
|
||||
void Update();
|
||||
|
||||
int indent_;
|
||||
std::string indent_str_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Indent);
|
||||
};
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_COMMON_INDENT_H_
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "common/libwebm_util.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds) {
|
||||
const double pts_seconds = nanoseconds / kNanosecondsPerSecond;
|
||||
return static_cast<std::int64_t>(pts_seconds * 90000);
|
||||
}
|
||||
|
||||
std::int64_t Khz90TicksToNanoseconds(std::int64_t ticks) {
|
||||
const double seconds = ticks / 90000.0;
|
||||
return static_cast<std::int64_t>(seconds * kNanosecondsPerSecond);
|
||||
}
|
||||
|
||||
bool ParseVP9SuperFrameIndex(const std::uint8_t* frame,
|
||||
std::size_t frame_length, Ranges* frame_ranges,
|
||||
bool* error) {
|
||||
if (frame == nullptr || frame_length == 0 || frame_ranges == nullptr ||
|
||||
error == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse_ok = false;
|
||||
const std::uint8_t marker = frame[frame_length - 1];
|
||||
const std::uint32_t kHasSuperFrameIndexMask = 0xe0;
|
||||
const std::uint32_t kSuperFrameMarker = 0xc0;
|
||||
const std::uint32_t kLengthFieldSizeMask = 0x3;
|
||||
|
||||
if ((marker & kHasSuperFrameIndexMask) == kSuperFrameMarker) {
|
||||
const std::uint32_t kFrameCountMask = 0x7;
|
||||
const int num_frames = (marker & kFrameCountMask) + 1;
|
||||
const int length_field_size = ((marker >> 3) & kLengthFieldSizeMask) + 1;
|
||||
const std::size_t index_length = 2 + length_field_size * num_frames;
|
||||
|
||||
if (frame_length < index_length) {
|
||||
std::fprintf(stderr,
|
||||
"VP9ParseSuperFrameIndex: Invalid superframe index size.\n");
|
||||
*error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consume the super frame index. Note: it's at the end of the super frame.
|
||||
const std::size_t length = frame_length - index_length;
|
||||
|
||||
if (length >= index_length &&
|
||||
frame[frame_length - index_length] == marker) {
|
||||
// Found a valid superframe index.
|
||||
const std::uint8_t* byte = frame + length + 1;
|
||||
|
||||
std::size_t frame_offset = 0;
|
||||
|
||||
for (int i = 0; i < num_frames; ++i) {
|
||||
std::uint32_t child_frame_length = 0;
|
||||
|
||||
for (int j = 0; j < length_field_size; ++j) {
|
||||
child_frame_length |= (*byte++) << (j * 8);
|
||||
}
|
||||
|
||||
if (length - frame_offset < child_frame_length) {
|
||||
std::fprintf(stderr,
|
||||
"ParseVP9SuperFrameIndex: Invalid superframe, sub frame "
|
||||
"larger than entire frame.\n");
|
||||
*error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
frame_ranges->push_back(Range(frame_offset, child_frame_length));
|
||||
frame_offset += child_frame_length;
|
||||
}
|
||||
|
||||
if (static_cast<int>(frame_ranges->size()) != num_frames) {
|
||||
std::fprintf(stderr, "VP9Parse: superframe index parse failed.\n");
|
||||
*error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse_ok = true;
|
||||
} else {
|
||||
std::fprintf(stderr, "VP9Parse: Invalid superframe index.\n");
|
||||
*error = true;
|
||||
}
|
||||
}
|
||||
return parse_ok;
|
||||
}
|
||||
|
||||
bool WriteUint8(std::uint8_t val, std::FILE* fileptr) {
|
||||
if (fileptr == nullptr)
|
||||
return false;
|
||||
return (std::fputc(val, fileptr) == val);
|
||||
}
|
||||
|
||||
std::uint16_t ReadUint16(const std::uint8_t* buf) {
|
||||
if (buf == nullptr)
|
||||
return 0;
|
||||
return ((buf[0] << 8) | buf[1]);
|
||||
}
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_COMMON_LIBWEBM_UTIL_H_
|
||||
#define LIBWEBM_COMMON_LIBWEBM_UTIL_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
const double kNanosecondsPerSecond = 1000000000.0;
|
||||
|
||||
// fclose functor for wrapping FILE in std::unique_ptr.
|
||||
// TODO(tomfinegan): Move this to file_util once c++11 restrictions are
|
||||
// relaxed.
|
||||
struct FILEDeleter {
|
||||
int operator()(std::FILE* f) {
|
||||
if (f != nullptr)
|
||||
return fclose(f);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
typedef std::unique_ptr<std::FILE, FILEDeleter> FilePtr;
|
||||
|
||||
struct Range {
|
||||
Range(std::size_t off, std::size_t len) : offset(off), length(len) {}
|
||||
Range() = delete;
|
||||
Range(const Range&) = default;
|
||||
Range(Range&&) = default;
|
||||
~Range() = default;
|
||||
const std::size_t offset;
|
||||
const std::size_t length;
|
||||
};
|
||||
typedef std::vector<Range> Ranges;
|
||||
|
||||
// Converts |nanoseconds| to 90000 Hz clock ticks and vice versa. Each return
|
||||
// the converted value.
|
||||
std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds);
|
||||
std::int64_t Khz90TicksToNanoseconds(std::int64_t khz90_ticks);
|
||||
|
||||
// Returns true and stores frame offsets and lengths in |frame_ranges| when
|
||||
// |frame| has a valid VP9 super frame index. Sets |error| to true when parsing
|
||||
// fails for any reason.
|
||||
bool ParseVP9SuperFrameIndex(const std::uint8_t* frame,
|
||||
std::size_t frame_length, Ranges* frame_ranges,
|
||||
bool* error);
|
||||
|
||||
// Writes |val| to |fileptr| and returns true upon success.
|
||||
bool WriteUint8(std::uint8_t val, std::FILE* fileptr);
|
||||
|
||||
// Reads 2 bytes from |buf| and returns them as a uint16_t. Returns 0 when |buf|
|
||||
// is a nullptr.
|
||||
std::uint16_t ReadUint16(const std::uint8_t* buf);
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_COMMON_LIBWEBM_UTIL_H_
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "common/video_frame.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
bool VideoFrame::Buffer::Init(std::size_t new_length) {
|
||||
capacity = 0;
|
||||
length = 0;
|
||||
data.reset(new std::uint8_t[new_length]);
|
||||
|
||||
if (data.get() == nullptr) {
|
||||
fprintf(stderr, "VideoFrame: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
|
||||
capacity = new_length;
|
||||
length = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VideoFrame::Init(std::size_t length) { return buffer_.Init(length); }
|
||||
|
||||
bool VideoFrame::Init(std::size_t length, std::int64_t nano_pts, Codec codec) {
|
||||
nanosecond_pts_ = nano_pts;
|
||||
codec_ = codec;
|
||||
return Init(length);
|
||||
}
|
||||
|
||||
bool VideoFrame::SetBufferLength(std::size_t length) {
|
||||
if (length > buffer_.capacity || buffer_.data.get() == nullptr)
|
||||
return false;
|
||||
|
||||
buffer_.length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_COMMON_VIDEO_FRAME_H_
|
||||
#define LIBWEBM_COMMON_VIDEO_FRAME_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
// VideoFrame is a storage class for compressed video frames.
|
||||
class VideoFrame {
|
||||
public:
|
||||
enum Codec { kVP8, kVP9 };
|
||||
struct Buffer {
|
||||
Buffer() = default;
|
||||
~Buffer() = default;
|
||||
|
||||
// Resets |data| to be of size |new_length| bytes, sets |capacity| to
|
||||
// |new_length|, sets |length| to 0 (aka empty). Returns true for success.
|
||||
bool Init(std::size_t new_length);
|
||||
|
||||
std::unique_ptr<std::uint8_t[]> data;
|
||||
std::size_t length = 0;
|
||||
std::size_t capacity = 0;
|
||||
};
|
||||
|
||||
VideoFrame() = default;
|
||||
~VideoFrame() = default;
|
||||
VideoFrame(std::int64_t pts_in_nanoseconds, Codec vpx_codec)
|
||||
: nanosecond_pts_(pts_in_nanoseconds), codec_(vpx_codec) {}
|
||||
VideoFrame(bool keyframe, std::int64_t pts_in_nanoseconds, Codec vpx_codec)
|
||||
: keyframe_(keyframe),
|
||||
nanosecond_pts_(pts_in_nanoseconds),
|
||||
codec_(vpx_codec) {}
|
||||
bool Init(std::size_t length);
|
||||
bool Init(std::size_t length, std::int64_t nano_pts, Codec codec);
|
||||
|
||||
// Updates actual length of data stored in |buffer_.data| when it's been
|
||||
// written via the raw pointer returned from buffer_.data.get().
|
||||
// Returns false when buffer_.data.get() return nullptr and/or when
|
||||
// |length| > |buffer_.length|. Returns true otherwise.
|
||||
bool SetBufferLength(std::size_t length);
|
||||
|
||||
// Accessors.
|
||||
const Buffer& buffer() const { return buffer_; }
|
||||
bool keyframe() const { return keyframe_; }
|
||||
std::int64_t nanosecond_pts() const { return nanosecond_pts_; }
|
||||
Codec codec() const { return codec_; }
|
||||
|
||||
// Mutators.
|
||||
void set_nanosecond_pts(std::int64_t nano_pts) { nanosecond_pts_ = nano_pts; }
|
||||
|
||||
private:
|
||||
Buffer buffer_;
|
||||
bool keyframe_ = false;
|
||||
std::int64_t nanosecond_pts_ = 0;
|
||||
Codec codec_ = kVP9;
|
||||
};
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_COMMON_VIDEO_FRAME_H_
|
||||
@@ -0,0 +1,266 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "common/vp9_header_parser.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace vp9_parser {
|
||||
|
||||
bool Vp9HeaderParser::SetFrame(const uint8_t* frame, size_t length) {
|
||||
if (!frame || length == 0)
|
||||
return false;
|
||||
|
||||
frame_ = frame;
|
||||
frame_size_ = length;
|
||||
bit_offset_ = 0;
|
||||
profile_ = -1;
|
||||
show_existing_frame_ = 0;
|
||||
key_ = 0;
|
||||
altref_ = 0;
|
||||
error_resilient_mode_ = 0;
|
||||
intra_only_ = 0;
|
||||
reset_frame_context_ = 0;
|
||||
color_space_ = 0;
|
||||
color_range_ = 0;
|
||||
subsampling_x_ = 0;
|
||||
subsampling_y_ = 0;
|
||||
refresh_frame_flags_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Vp9HeaderParser::ParseUncompressedHeader(const uint8_t* frame,
|
||||
size_t length) {
|
||||
if (!SetFrame(frame, length))
|
||||
return false;
|
||||
const int frame_marker = VpxReadLiteral(2);
|
||||
if (frame_marker != kVp9FrameMarker) {
|
||||
fprintf(stderr, "Invalid VP9 frame_marker:%d\n", frame_marker);
|
||||
return false;
|
||||
}
|
||||
|
||||
profile_ = ReadBit();
|
||||
profile_ |= ReadBit() << 1;
|
||||
if (profile_ > 2)
|
||||
profile_ += ReadBit();
|
||||
|
||||
// TODO(fgalligan): Decide how to handle show existing frames.
|
||||
show_existing_frame_ = ReadBit();
|
||||
if (show_existing_frame_)
|
||||
return true;
|
||||
|
||||
key_ = !ReadBit();
|
||||
altref_ = !ReadBit();
|
||||
error_resilient_mode_ = ReadBit();
|
||||
if (key_) {
|
||||
if (!ValidateVp9SyncCode()) {
|
||||
fprintf(stderr, "Invalid Sync code!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseColorSpace();
|
||||
ParseFrameResolution();
|
||||
ParseFrameParallelMode();
|
||||
ParseTileInfo();
|
||||
} else {
|
||||
intra_only_ = altref_ ? ReadBit() : 0;
|
||||
reset_frame_context_ = error_resilient_mode_ ? 0 : VpxReadLiteral(2);
|
||||
if (intra_only_) {
|
||||
if (!ValidateVp9SyncCode()) {
|
||||
fprintf(stderr, "Invalid Sync code!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile_ > 0) {
|
||||
ParseColorSpace();
|
||||
} else {
|
||||
// NOTE: The intra-only frame header does not include the specification
|
||||
// of either the color format or color sub-sampling in profile 0. VP9
|
||||
// specifies that the default color format should be YUV 4:2:0 in this
|
||||
// case (normative).
|
||||
color_space_ = kVpxCsBt601;
|
||||
color_range_ = kVpxCrStudioRange;
|
||||
subsampling_y_ = subsampling_x_ = 1;
|
||||
bit_depth_ = 8;
|
||||
}
|
||||
|
||||
refresh_frame_flags_ = VpxReadLiteral(kRefFrames);
|
||||
ParseFrameResolution();
|
||||
} else {
|
||||
refresh_frame_flags_ = VpxReadLiteral(kRefFrames);
|
||||
for (int i = 0; i < kRefsPerFrame; ++i) {
|
||||
VpxReadLiteral(kRefFrames_LOG2); // Consume ref.
|
||||
ReadBit(); // Consume ref sign bias.
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < kRefsPerFrame; ++i) {
|
||||
if (ReadBit()) {
|
||||
// Found previous reference, width and height did not change since
|
||||
// last frame.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
ParseFrameResolution();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Vp9HeaderParser::ReadBit() {
|
||||
const size_t off = bit_offset_;
|
||||
const size_t byte_offset = off >> 3;
|
||||
const int bit_shift = 7 - static_cast<int>(off & 0x7);
|
||||
if (byte_offset < frame_size_) {
|
||||
const int bit = (frame_[byte_offset] >> bit_shift) & 1;
|
||||
bit_offset_++;
|
||||
return bit;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Vp9HeaderParser::VpxReadLiteral(int bits) {
|
||||
int value = 0;
|
||||
for (int bit = bits - 1; bit >= 0; --bit)
|
||||
value |= ReadBit() << bit;
|
||||
return value;
|
||||
}
|
||||
|
||||
bool Vp9HeaderParser::ValidateVp9SyncCode() {
|
||||
const int sync_code_0 = VpxReadLiteral(8);
|
||||
const int sync_code_1 = VpxReadLiteral(8);
|
||||
const int sync_code_2 = VpxReadLiteral(8);
|
||||
return (sync_code_0 == 0x49 && sync_code_1 == 0x83 && sync_code_2 == 0x42);
|
||||
}
|
||||
|
||||
void Vp9HeaderParser::ParseColorSpace() {
|
||||
bit_depth_ = 0;
|
||||
if (profile_ >= 2)
|
||||
bit_depth_ = ReadBit() ? 12 : 10;
|
||||
else
|
||||
bit_depth_ = 8;
|
||||
color_space_ = VpxReadLiteral(3);
|
||||
if (color_space_ != kVpxCsSrgb) {
|
||||
color_range_ = ReadBit();
|
||||
if (profile_ == 1 || profile_ == 3) {
|
||||
subsampling_x_ = ReadBit();
|
||||
subsampling_y_ = ReadBit();
|
||||
ReadBit();
|
||||
} else {
|
||||
subsampling_y_ = subsampling_x_ = 1;
|
||||
}
|
||||
} else {
|
||||
color_range_ = kVpxCrFullRange;
|
||||
if (profile_ == 1 || profile_ == 3) {
|
||||
subsampling_y_ = subsampling_x_ = 0;
|
||||
ReadBit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Vp9HeaderParser::ParseFrameResolution() {
|
||||
width_ = VpxReadLiteral(16) + 1;
|
||||
height_ = VpxReadLiteral(16) + 1;
|
||||
}
|
||||
|
||||
void Vp9HeaderParser::ParseFrameParallelMode() {
|
||||
if (ReadBit()) {
|
||||
VpxReadLiteral(16); // display width
|
||||
VpxReadLiteral(16); // display height
|
||||
}
|
||||
if (!error_resilient_mode_) {
|
||||
ReadBit(); // Consume refresh frame context
|
||||
frame_parallel_mode_ = ReadBit();
|
||||
} else {
|
||||
frame_parallel_mode_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Vp9HeaderParser::ParseTileInfo() {
|
||||
VpxReadLiteral(2); // Consume frame context index
|
||||
|
||||
// loopfilter
|
||||
VpxReadLiteral(6); // Consume filter level
|
||||
VpxReadLiteral(3); // Consume sharpness level
|
||||
|
||||
const bool mode_ref_delta_enabled = ReadBit();
|
||||
if (mode_ref_delta_enabled) {
|
||||
const bool mode_ref_delta_update = ReadBit();
|
||||
if (mode_ref_delta_update) {
|
||||
const int kMaxRefLFDeltas = 4;
|
||||
for (int i = 0; i < kMaxRefLFDeltas; ++i) {
|
||||
if (ReadBit())
|
||||
VpxReadLiteral(7); // Consume ref_deltas + sign
|
||||
}
|
||||
|
||||
const int kMaxModeDeltas = 2;
|
||||
for (int i = 0; i < kMaxModeDeltas; ++i) {
|
||||
if (ReadBit())
|
||||
VpxReadLiteral(7); // Consume mode_delta + sign
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// quantization
|
||||
VpxReadLiteral(8); // Consume base_q
|
||||
SkipDeltaQ(); // y dc
|
||||
SkipDeltaQ(); // uv ac
|
||||
SkipDeltaQ(); // uv dc
|
||||
|
||||
// segmentation
|
||||
const bool segmentation_enabled = ReadBit();
|
||||
if (!segmentation_enabled) {
|
||||
const int aligned_width = AlignPowerOfTwo(width_, kMiSizeLog2);
|
||||
const int mi_cols = aligned_width >> kMiSizeLog2;
|
||||
const int aligned_mi_cols = AlignPowerOfTwo(mi_cols, kMiSizeLog2);
|
||||
const int sb_cols = aligned_mi_cols >> 3; // to_sbs(mi_cols);
|
||||
int min_log2_n_tiles, max_log2_n_tiles;
|
||||
|
||||
for (max_log2_n_tiles = 0;
|
||||
(sb_cols >> max_log2_n_tiles) >= kMinTileWidthB64;
|
||||
max_log2_n_tiles++) {
|
||||
}
|
||||
max_log2_n_tiles--;
|
||||
if (max_log2_n_tiles < 0)
|
||||
max_log2_n_tiles = 0;
|
||||
|
||||
for (min_log2_n_tiles = 0; (kMaxTileWidthB64 << min_log2_n_tiles) < sb_cols;
|
||||
min_log2_n_tiles++) {
|
||||
}
|
||||
|
||||
// columns
|
||||
const int max_log2_tile_cols = max_log2_n_tiles;
|
||||
const int min_log2_tile_cols = min_log2_n_tiles;
|
||||
int max_ones = max_log2_tile_cols - min_log2_tile_cols;
|
||||
int log2_tile_cols = min_log2_tile_cols;
|
||||
while (max_ones-- && ReadBit())
|
||||
log2_tile_cols++;
|
||||
|
||||
// rows
|
||||
int log2_tile_rows = ReadBit();
|
||||
if (log2_tile_rows)
|
||||
log2_tile_rows += ReadBit();
|
||||
|
||||
row_tiles_ = 1 << log2_tile_rows;
|
||||
column_tiles_ = 1 << log2_tile_cols;
|
||||
}
|
||||
}
|
||||
|
||||
void Vp9HeaderParser::SkipDeltaQ() {
|
||||
if (ReadBit())
|
||||
VpxReadLiteral(4);
|
||||
}
|
||||
|
||||
int Vp9HeaderParser::AlignPowerOfTwo(int value, int n) {
|
||||
return (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1));
|
||||
}
|
||||
|
||||
} // namespace vp9_parser
|
||||
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
|
||||
#define LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace vp9_parser {
|
||||
|
||||
const int kVp9FrameMarker = 2;
|
||||
const int kMinTileWidthB64 = 4;
|
||||
const int kMaxTileWidthB64 = 64;
|
||||
const int kRefFrames = 8;
|
||||
const int kRefsPerFrame = 3;
|
||||
const int kRefFrames_LOG2 = 3;
|
||||
const int kVpxCsBt601 = 1;
|
||||
const int kVpxCsSrgb = 7;
|
||||
const int kVpxCrStudioRange = 0;
|
||||
const int kVpxCrFullRange = 1;
|
||||
const int kMiSizeLog2 = 3;
|
||||
|
||||
// Class to parse the header of a VP9 frame.
|
||||
class Vp9HeaderParser {
|
||||
public:
|
||||
Vp9HeaderParser()
|
||||
: frame_(NULL),
|
||||
frame_size_(0),
|
||||
bit_offset_(0),
|
||||
profile_(-1),
|
||||
show_existing_frame_(0),
|
||||
key_(0),
|
||||
altref_(0),
|
||||
error_resilient_mode_(0),
|
||||
intra_only_(0),
|
||||
reset_frame_context_(0),
|
||||
bit_depth_(0),
|
||||
color_space_(0),
|
||||
color_range_(0),
|
||||
subsampling_x_(0),
|
||||
subsampling_y_(0),
|
||||
refresh_frame_flags_(0),
|
||||
width_(0),
|
||||
height_(0),
|
||||
row_tiles_(0),
|
||||
column_tiles_(0),
|
||||
frame_parallel_mode_(0) {}
|
||||
|
||||
// Parse the VP9 uncompressed header. The return values of the remaining
|
||||
// functions are only valid on success.
|
||||
bool ParseUncompressedHeader(const uint8_t* frame, size_t length);
|
||||
|
||||
size_t frame_size() const { return frame_size_; }
|
||||
int profile() const { return profile_; }
|
||||
int key() const { return key_; }
|
||||
int altref() const { return altref_; }
|
||||
int error_resilient_mode() const { return error_resilient_mode_; }
|
||||
int bit_depth() const { return bit_depth_; }
|
||||
int color_space() const { return color_space_; }
|
||||
int width() const { return width_; }
|
||||
int height() const { return height_; }
|
||||
int refresh_frame_flags() const { return refresh_frame_flags_; }
|
||||
int row_tiles() const { return row_tiles_; }
|
||||
int column_tiles() const { return column_tiles_; }
|
||||
int frame_parallel_mode() const { return frame_parallel_mode_; }
|
||||
|
||||
private:
|
||||
// Set the compressed VP9 frame.
|
||||
bool SetFrame(const uint8_t* frame, size_t length);
|
||||
|
||||
// Returns the next bit of the frame.
|
||||
int ReadBit();
|
||||
|
||||
// Returns the next |bits| of the frame.
|
||||
int VpxReadLiteral(int bits);
|
||||
|
||||
// Returns true if the vp9 sync code is valid.
|
||||
bool ValidateVp9SyncCode();
|
||||
|
||||
// Parses bit_depth_, color_space_, subsampling_x_, subsampling_y_, and
|
||||
// color_range_.
|
||||
void ParseColorSpace();
|
||||
|
||||
// Parses width and height of the frame.
|
||||
void ParseFrameResolution();
|
||||
|
||||
// Parses frame_parallel_mode_. This function skips over some features.
|
||||
void ParseFrameParallelMode();
|
||||
|
||||
// Parses row and column tiles. This function skips over some features.
|
||||
void ParseTileInfo();
|
||||
void SkipDeltaQ();
|
||||
int AlignPowerOfTwo(int value, int n);
|
||||
|
||||
const uint8_t* frame_;
|
||||
size_t frame_size_;
|
||||
size_t bit_offset_;
|
||||
int profile_;
|
||||
int show_existing_frame_;
|
||||
int key_;
|
||||
int altref_;
|
||||
int error_resilient_mode_;
|
||||
int intra_only_;
|
||||
int reset_frame_context_;
|
||||
int bit_depth_;
|
||||
int color_space_;
|
||||
int color_range_;
|
||||
int subsampling_x_;
|
||||
int subsampling_y_;
|
||||
int refresh_frame_flags_;
|
||||
int width_;
|
||||
int height_;
|
||||
int row_tiles_;
|
||||
int column_tiles_;
|
||||
int frame_parallel_mode_;
|
||||
};
|
||||
|
||||
} // namespace vp9_parser
|
||||
|
||||
#endif // LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
|
||||
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "common/vp9_header_parser.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "common/hdr_util.h"
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
#include "testing/test_util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class Vp9HeaderParserTests : public ::testing::Test {
|
||||
public:
|
||||
Vp9HeaderParserTests() : is_reader_open_(false), segment_(NULL) {}
|
||||
|
||||
~Vp9HeaderParserTests() override {
|
||||
CloseReader();
|
||||
if (segment_ != NULL) {
|
||||
delete segment_;
|
||||
segment_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CloseReader() {
|
||||
if (is_reader_open_) {
|
||||
reader_.Close();
|
||||
}
|
||||
is_reader_open_ = false;
|
||||
}
|
||||
|
||||
void CreateAndLoadSegment(const std::string& filename,
|
||||
int expected_doc_type_ver) {
|
||||
filename_ = test::GetTestFilePath(filename);
|
||||
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
|
||||
is_reader_open_ = true;
|
||||
pos_ = 0;
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
ebml_header.Parse(&reader_, pos_);
|
||||
ASSERT_EQ(1, ebml_header.m_version);
|
||||
ASSERT_EQ(1, ebml_header.m_readVersion);
|
||||
ASSERT_STREQ("webm", ebml_header.m_docType);
|
||||
ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
|
||||
ASSERT_EQ(2, ebml_header.m_docTypeReadVersion);
|
||||
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
|
||||
ASSERT_FALSE(HasFailure());
|
||||
ASSERT_GE(0, segment_->Load());
|
||||
}
|
||||
|
||||
void CreateAndLoadSegment(const std::string& filename) {
|
||||
CreateAndLoadSegment(filename, 4);
|
||||
}
|
||||
|
||||
// Load a corrupted segment with no expectation of correctness.
|
||||
void CreateAndLoadInvalidSegment(const std::string& filename) {
|
||||
filename_ = test::GetTestFilePath(filename);
|
||||
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
|
||||
is_reader_open_ = true;
|
||||
pos_ = 0;
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
ebml_header.Parse(&reader_, pos_);
|
||||
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
|
||||
ASSERT_GE(0, segment_->Load());
|
||||
}
|
||||
|
||||
void ProcessTheFrames(bool invalid_bitstream) {
|
||||
unsigned char* data = NULL;
|
||||
size_t data_len = 0;
|
||||
const mkvparser::Tracks* const parser_tracks = segment_->GetTracks();
|
||||
ASSERT_TRUE(parser_tracks != NULL);
|
||||
const mkvparser::Cluster* cluster = segment_->GetFirst();
|
||||
ASSERT_TRUE(cluster != NULL);
|
||||
|
||||
while ((cluster != NULL) && !cluster->EOS()) {
|
||||
const mkvparser::BlockEntry* block_entry;
|
||||
long status = cluster->GetFirst(block_entry); // NOLINT
|
||||
ASSERT_EQ(0, status);
|
||||
|
||||
while ((block_entry != NULL) && !block_entry->EOS()) {
|
||||
const mkvparser::Block* const block = block_entry->GetBlock();
|
||||
ASSERT_TRUE(block != NULL);
|
||||
const long long trackNum = block->GetTrackNumber(); // NOLINT
|
||||
const mkvparser::Track* const parser_track =
|
||||
parser_tracks->GetTrackByNumber(
|
||||
static_cast<unsigned long>(trackNum)); // NOLINT
|
||||
ASSERT_TRUE(parser_track != NULL);
|
||||
const long long track_type = parser_track->GetType(); // NOLINT
|
||||
|
||||
if (track_type == mkvparser::Track::kVideo) {
|
||||
const int frame_count = block->GetFrameCount();
|
||||
|
||||
for (int i = 0; i < frame_count; ++i) {
|
||||
const mkvparser::Block::Frame& frame = block->GetFrame(i);
|
||||
|
||||
if (static_cast<size_t>(frame.len) > data_len) {
|
||||
delete[] data;
|
||||
data = new unsigned char[frame.len];
|
||||
ASSERT_TRUE(data != NULL);
|
||||
data_len = static_cast<size_t>(frame.len);
|
||||
}
|
||||
ASSERT_FALSE(frame.Read(&reader_, data));
|
||||
ASSERT_EQ(parser_.ParseUncompressedHeader(data, data_len),
|
||||
!invalid_bitstream);
|
||||
}
|
||||
}
|
||||
|
||||
status = cluster->GetNext(block_entry, block_entry);
|
||||
ASSERT_EQ(0, status);
|
||||
}
|
||||
|
||||
cluster = segment_->GetNext(cluster);
|
||||
}
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
protected:
|
||||
mkvparser::MkvReader reader_;
|
||||
bool is_reader_open_;
|
||||
mkvparser::Segment* segment_;
|
||||
std::string filename_;
|
||||
long long pos_; // NOLINT
|
||||
vp9_parser::Vp9HeaderParser parser_;
|
||||
};
|
||||
|
||||
TEST_F(Vp9HeaderParserTests, VideoOnlyFile) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateAndLoadSegment("test_stereo_left_right.webm"));
|
||||
ProcessTheFrames(false);
|
||||
EXPECT_EQ(256, parser_.width());
|
||||
EXPECT_EQ(144, parser_.height());
|
||||
EXPECT_EQ(1, parser_.column_tiles());
|
||||
EXPECT_EQ(0, parser_.frame_parallel_mode());
|
||||
}
|
||||
|
||||
TEST_F(Vp9HeaderParserTests, Muxed) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4));
|
||||
ProcessTheFrames(false);
|
||||
EXPECT_EQ(854, parser_.width());
|
||||
EXPECT_EQ(480, parser_.height());
|
||||
EXPECT_EQ(2, parser_.column_tiles());
|
||||
EXPECT_EQ(1, parser_.frame_parallel_mode());
|
||||
}
|
||||
|
||||
TEST_F(Vp9HeaderParserTests, Invalid) {
|
||||
const char* files[] = {
|
||||
"invalid/invalid_vp9_bitstream-bug_1416.webm",
|
||||
"invalid/invalid_vp9_bitstream-bug_1417.webm",
|
||||
};
|
||||
|
||||
for (int i = 0; i < static_cast<int>(sizeof(files) / sizeof(files[0])); ++i) {
|
||||
SCOPED_TRACE(files[i]);
|
||||
ASSERT_NO_FATAL_FAILURE(CreateAndLoadInvalidSegment(files[i]));
|
||||
ProcessTheFrames(true);
|
||||
CloseReader();
|
||||
delete segment_;
|
||||
segment_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Vp9HeaderParserTests, API) {
|
||||
vp9_parser::Vp9HeaderParser parser;
|
||||
uint8_t data;
|
||||
EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 0));
|
||||
EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 10));
|
||||
EXPECT_FALSE(parser.ParseUncompressedHeader(&data, 0));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "common/vp9_level_stats.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "common/webm_constants.h"
|
||||
|
||||
namespace vp9_parser {
|
||||
|
||||
const Vp9LevelRow Vp9LevelStats::Vp9LevelTable[kNumVp9Levels] = {
|
||||
{LEVEL_1, 829440, 36864, 200, 400, 2, 1, 4, 8, 512},
|
||||
{LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8, 768},
|
||||
{LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8, 960},
|
||||
{LEVEL_2_1, 9216000, 245760, 3600, 2800, 2, 2, 4, 8, 1344},
|
||||
{LEVEL_3, 20736000, 552960, 7200, 6000, 2, 4, 4, 8, 2048},
|
||||
{LEVEL_3_1, 36864000, 983040, 12000, 10000, 2, 4, 4, 8, 2752},
|
||||
{LEVEL_4, 83558400, 2228224, 18000, 16000, 4, 4, 4, 8, 4160},
|
||||
{LEVEL_4_1, 160432128, 2228224, 30000, 18000, 4, 4, 5, 6, 4160},
|
||||
{LEVEL_5, 311951360, 8912896, 60000, 36000, 6, 8, 6, 4, 8384},
|
||||
{LEVEL_5_1, 588251136, 8912896, 120000, 46000, 8, 8, 10, 4, 8384},
|
||||
// CPB Size = 0 for levels 5_2 to 6_2
|
||||
{LEVEL_5_2, 1176502272, 8912896, 180000, 0, 8, 8, 10, 4, 8384},
|
||||
{LEVEL_6, 1176502272, 35651584, 180000, 0, 8, 16, 10, 4, 16832},
|
||||
{LEVEL_6_1, 2353004544, 35651584, 240000, 0, 8, 16, 10, 4, 16832},
|
||||
{LEVEL_6_2, 4706009088, 35651584, 480000, 0, 8, 16, 10, 4, 16832}};
|
||||
|
||||
void Vp9LevelStats::AddFrame(const Vp9HeaderParser& parser, int64_t time_ns) {
|
||||
++frames;
|
||||
if (start_ns_ == -1)
|
||||
start_ns_ = time_ns;
|
||||
end_ns_ = time_ns;
|
||||
|
||||
const int width = parser.width();
|
||||
const int height = parser.height();
|
||||
const int64_t luma_picture_size = width * height;
|
||||
const int64_t luma_picture_breadth = (width > height) ? width : height;
|
||||
if (luma_picture_size > max_luma_picture_size_)
|
||||
max_luma_picture_size_ = luma_picture_size;
|
||||
if (luma_picture_breadth > max_luma_picture_breadth_)
|
||||
max_luma_picture_breadth_ = luma_picture_breadth;
|
||||
|
||||
total_compressed_size_ += parser.frame_size();
|
||||
|
||||
while (!luma_window_.empty() &&
|
||||
luma_window_.front().first <
|
||||
(time_ns - (libwebm::kNanosecondsPerSecondi - 1))) {
|
||||
current_luma_size_ -= luma_window_.front().second;
|
||||
luma_window_.pop();
|
||||
}
|
||||
current_luma_size_ += luma_picture_size;
|
||||
luma_window_.push(std::make_pair(time_ns, luma_picture_size));
|
||||
if (current_luma_size_ > max_luma_size_) {
|
||||
max_luma_size_ = current_luma_size_;
|
||||
max_luma_end_ns_ = luma_window_.back().first;
|
||||
}
|
||||
|
||||
// Record CPB stats.
|
||||
// Remove all frames that are less than window size.
|
||||
while (cpb_window_.size() > 3) {
|
||||
current_cpb_size_ -= cpb_window_.front().second;
|
||||
cpb_window_.pop();
|
||||
}
|
||||
cpb_window_.push(std::make_pair(time_ns, parser.frame_size()));
|
||||
|
||||
current_cpb_size_ += parser.frame_size();
|
||||
if (current_cpb_size_ > max_cpb_size_) {
|
||||
max_cpb_size_ = current_cpb_size_;
|
||||
max_cpb_start_ns_ = cpb_window_.front().first;
|
||||
max_cpb_end_ns_ = cpb_window_.back().first;
|
||||
}
|
||||
|
||||
if (max_cpb_window_size_ < static_cast<int64_t>(cpb_window_.size())) {
|
||||
max_cpb_window_size_ = cpb_window_.size();
|
||||
max_cpb_window_end_ns_ = time_ns;
|
||||
}
|
||||
|
||||
// Record altref stats.
|
||||
if (parser.altref()) {
|
||||
const int delta_altref = frames_since_last_altref;
|
||||
if (first_altref) {
|
||||
first_altref = false;
|
||||
} else if (delta_altref < minimum_altref_distance) {
|
||||
minimum_altref_distance = delta_altref;
|
||||
min_altref_end_ns = time_ns;
|
||||
}
|
||||
frames_since_last_altref = 0;
|
||||
} else {
|
||||
++frames_since_last_altref;
|
||||
++displayed_frames;
|
||||
// TODO(fgalligan): Add support for other color formats. Currently assuming
|
||||
// 420.
|
||||
total_uncompressed_bits_ +=
|
||||
(luma_picture_size * parser.bit_depth() * 3) / 2;
|
||||
}
|
||||
|
||||
// Count max reference frames.
|
||||
if (parser.key() == 1) {
|
||||
frames_refreshed_ = 0;
|
||||
} else {
|
||||
frames_refreshed_ |= parser.refresh_frame_flags();
|
||||
|
||||
int ref_frame_count = frames_refreshed_ & 1;
|
||||
for (int i = 1; i < kMaxVp9RefFrames; ++i) {
|
||||
ref_frame_count += (frames_refreshed_ >> i) & 1;
|
||||
}
|
||||
|
||||
if (ref_frame_count > max_frames_refreshed_)
|
||||
max_frames_refreshed_ = ref_frame_count;
|
||||
}
|
||||
|
||||
// Count max tiles.
|
||||
const int tiles = parser.column_tiles();
|
||||
if (tiles > max_column_tiles_)
|
||||
max_column_tiles_ = tiles;
|
||||
}
|
||||
|
||||
Vp9Level Vp9LevelStats::GetLevel() const {
|
||||
const int64_t max_luma_sample_rate = GetMaxLumaSampleRate();
|
||||
const int64_t max_luma_picture_size = GetMaxLumaPictureSize();
|
||||
const int64_t max_luma_picture_breadth = GetMaxLumaPictureBreadth();
|
||||
const double average_bitrate = GetAverageBitRate();
|
||||
const double max_cpb_size = GetMaxCpbSize();
|
||||
const double compresion_ratio = GetCompressionRatio();
|
||||
const int max_column_tiles = GetMaxColumnTiles();
|
||||
const int min_altref_distance = GetMinimumAltrefDistance();
|
||||
const int max_ref_frames = GetMaxReferenceFrames();
|
||||
|
||||
int level_index = 0;
|
||||
Vp9Level max_level = LEVEL_UNKNOWN;
|
||||
const double grace_multiplier =
|
||||
max_luma_sample_rate_grace_percent_ / 100.0 + 1.0;
|
||||
for (int i = 0; i < kNumVp9Levels; ++i) {
|
||||
if (max_luma_sample_rate <=
|
||||
Vp9LevelTable[i].max_luma_sample_rate * grace_multiplier) {
|
||||
if (max_level < Vp9LevelTable[i].level) {
|
||||
max_level = Vp9LevelTable[i].level;
|
||||
level_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kNumVp9Levels; ++i) {
|
||||
if (max_luma_picture_size <= Vp9LevelTable[i].max_luma_picture_size) {
|
||||
if (max_level < Vp9LevelTable[i].level) {
|
||||
max_level = Vp9LevelTable[i].level;
|
||||
level_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kNumVp9Levels; ++i) {
|
||||
if (max_luma_picture_breadth <= Vp9LevelTable[i].max_luma_picture_breadth) {
|
||||
if (max_level < Vp9LevelTable[i].level) {
|
||||
max_level = Vp9LevelTable[i].level;
|
||||
level_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kNumVp9Levels; ++i) {
|
||||
if (average_bitrate <= Vp9LevelTable[i].average_bitrate) {
|
||||
if (max_level < Vp9LevelTable[i].level) {
|
||||
max_level = Vp9LevelTable[i].level;
|
||||
level_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kNumVp9Levels; ++i) {
|
||||
// Only check CPB size for levels that are defined.
|
||||
if (Vp9LevelTable[i].max_cpb_size > 0 &&
|
||||
max_cpb_size <= Vp9LevelTable[i].max_cpb_size) {
|
||||
if (max_level < Vp9LevelTable[i].level) {
|
||||
max_level = Vp9LevelTable[i].level;
|
||||
level_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kNumVp9Levels; ++i) {
|
||||
if (max_column_tiles <= Vp9LevelTable[i].max_tiles) {
|
||||
if (max_level < Vp9LevelTable[i].level) {
|
||||
max_level = Vp9LevelTable[i].level;
|
||||
level_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < kNumVp9Levels; ++i) {
|
||||
if (max_ref_frames <= Vp9LevelTable[i].max_ref_frames) {
|
||||
if (max_level < Vp9LevelTable[i].level) {
|
||||
max_level = Vp9LevelTable[i].level;
|
||||
level_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the current level meets the minimum altref distance requirement.
|
||||
// If not, set to unknown level as we can't move up a level as the minimum
|
||||
// altref distance get farther apart and we can't move down a level as we are
|
||||
// already at the minimum level for all the other requirements.
|
||||
if (min_altref_distance < Vp9LevelTable[level_index].min_altref_distance)
|
||||
max_level = LEVEL_UNKNOWN;
|
||||
|
||||
// The minimum compression ratio has the same behavior as minimum altref
|
||||
// distance.
|
||||
if (compresion_ratio < Vp9LevelTable[level_index].compresion_ratio)
|
||||
max_level = LEVEL_UNKNOWN;
|
||||
return max_level;
|
||||
}
|
||||
|
||||
int64_t Vp9LevelStats::GetMaxLumaSampleRate() const { return max_luma_size_; }
|
||||
|
||||
int64_t Vp9LevelStats::GetMaxLumaPictureSize() const {
|
||||
return max_luma_picture_size_;
|
||||
}
|
||||
|
||||
int64_t Vp9LevelStats::GetMaxLumaPictureBreadth() const {
|
||||
return max_luma_picture_breadth_;
|
||||
}
|
||||
|
||||
double Vp9LevelStats::GetAverageBitRate() const {
|
||||
const int64_t frame_duration_ns = end_ns_ - start_ns_;
|
||||
double duration_seconds =
|
||||
((duration_ns_ == -1) ? frame_duration_ns : duration_ns_) /
|
||||
libwebm::kNanosecondsPerSecond;
|
||||
if (estimate_last_frame_duration_ &&
|
||||
(duration_ns_ == -1 || duration_ns_ <= frame_duration_ns)) {
|
||||
const double sec_per_frame = frame_duration_ns /
|
||||
libwebm::kNanosecondsPerSecond /
|
||||
(displayed_frames - 1);
|
||||
duration_seconds += sec_per_frame;
|
||||
}
|
||||
|
||||
return total_compressed_size_ / duration_seconds / 125.0;
|
||||
}
|
||||
|
||||
double Vp9LevelStats::GetMaxCpbSize() const { return max_cpb_size_ / 125.0; }
|
||||
|
||||
double Vp9LevelStats::GetCompressionRatio() const {
|
||||
return total_uncompressed_bits_ /
|
||||
static_cast<double>(total_compressed_size_ * 8);
|
||||
}
|
||||
|
||||
int Vp9LevelStats::GetMaxColumnTiles() const { return max_column_tiles_; }
|
||||
|
||||
int Vp9LevelStats::GetMinimumAltrefDistance() const {
|
||||
if (minimum_altref_distance != std::numeric_limits<int>::max())
|
||||
return minimum_altref_distance;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Vp9LevelStats::GetMaxReferenceFrames() const {
|
||||
return max_frames_refreshed_;
|
||||
}
|
||||
|
||||
} // namespace vp9_parser
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
|
||||
#define LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
|
||||
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
#include "common/vp9_header_parser.h"
|
||||
|
||||
namespace vp9_parser {
|
||||
|
||||
const int kMaxVp9RefFrames = 8;
|
||||
|
||||
// Defined VP9 levels. See http://www.webmproject.org/vp9/profiles/ for
|
||||
// detailed information on VP9 levels.
|
||||
const int kNumVp9Levels = 14;
|
||||
enum Vp9Level {
|
||||
LEVEL_UNKNOWN = 0,
|
||||
LEVEL_1 = 10,
|
||||
LEVEL_1_1 = 11,
|
||||
LEVEL_2 = 20,
|
||||
LEVEL_2_1 = 21,
|
||||
LEVEL_3 = 30,
|
||||
LEVEL_3_1 = 31,
|
||||
LEVEL_4 = 40,
|
||||
LEVEL_4_1 = 41,
|
||||
LEVEL_5 = 50,
|
||||
LEVEL_5_1 = 51,
|
||||
LEVEL_5_2 = 52,
|
||||
LEVEL_6 = 60,
|
||||
LEVEL_6_1 = 61,
|
||||
LEVEL_6_2 = 62
|
||||
};
|
||||
|
||||
struct Vp9LevelRow {
|
||||
Vp9LevelRow() = default;
|
||||
~Vp9LevelRow() = default;
|
||||
Vp9LevelRow(Vp9LevelRow&& other) = default;
|
||||
Vp9LevelRow(const Vp9LevelRow& other) = default;
|
||||
Vp9LevelRow& operator=(Vp9LevelRow&& other) = delete;
|
||||
Vp9LevelRow& operator=(const Vp9LevelRow& other) = delete;
|
||||
|
||||
Vp9Level level;
|
||||
int64_t max_luma_sample_rate;
|
||||
int64_t max_luma_picture_size;
|
||||
int64_t max_luma_picture_breadth;
|
||||
double average_bitrate;
|
||||
double max_cpb_size;
|
||||
double compresion_ratio;
|
||||
int max_tiles;
|
||||
int min_altref_distance;
|
||||
int max_ref_frames;
|
||||
};
|
||||
|
||||
// Class to determine the VP9 level of a VP9 bitstream.
|
||||
class Vp9LevelStats {
|
||||
public:
|
||||
static const Vp9LevelRow Vp9LevelTable[kNumVp9Levels];
|
||||
|
||||
Vp9LevelStats()
|
||||
: frames(0),
|
||||
displayed_frames(0),
|
||||
start_ns_(-1),
|
||||
end_ns_(-1),
|
||||
duration_ns_(-1),
|
||||
max_luma_picture_size_(0),
|
||||
max_luma_picture_breadth_(0),
|
||||
current_luma_size_(0),
|
||||
max_luma_size_(0),
|
||||
max_luma_end_ns_(0),
|
||||
max_luma_sample_rate_grace_percent_(1.5),
|
||||
first_altref(true),
|
||||
frames_since_last_altref(0),
|
||||
minimum_altref_distance(std::numeric_limits<int>::max()),
|
||||
min_altref_end_ns(0),
|
||||
max_cpb_window_size_(0),
|
||||
max_cpb_window_end_ns_(0),
|
||||
current_cpb_size_(0),
|
||||
max_cpb_size_(0),
|
||||
max_cpb_start_ns_(0),
|
||||
max_cpb_end_ns_(0),
|
||||
total_compressed_size_(0),
|
||||
total_uncompressed_bits_(0),
|
||||
frames_refreshed_(0),
|
||||
max_frames_refreshed_(0),
|
||||
max_column_tiles_(0),
|
||||
estimate_last_frame_duration_(true) {}
|
||||
|
||||
~Vp9LevelStats() = default;
|
||||
Vp9LevelStats(Vp9LevelStats&& other) = delete;
|
||||
Vp9LevelStats(const Vp9LevelStats& other) = delete;
|
||||
Vp9LevelStats& operator=(Vp9LevelStats&& other) = delete;
|
||||
Vp9LevelStats& operator=(const Vp9LevelStats& other) = delete;
|
||||
|
||||
// Collects stats on a VP9 frame. The frame must already be parsed by
|
||||
// |parser|. |time_ns| is the start time of the frame in nanoseconds.
|
||||
void AddFrame(const Vp9HeaderParser& parser, int64_t time_ns);
|
||||
|
||||
// Returns the current VP9 level. All of the video frames should have been
|
||||
// processed with AddFrame before calling this function.
|
||||
Vp9Level GetLevel() const;
|
||||
|
||||
// Returns the maximum luma samples (pixels) per second. The Alt-Ref frames
|
||||
// are taken into account, therefore this number may be larger than the
|
||||
// display luma samples per second
|
||||
int64_t GetMaxLumaSampleRate() const;
|
||||
|
||||
// The maximum frame size (width * height) in samples.
|
||||
int64_t GetMaxLumaPictureSize() const;
|
||||
|
||||
// The maximum frame breadth (max of width and height) in samples.
|
||||
int64_t GetMaxLumaPictureBreadth() const;
|
||||
|
||||
// The average bitrate of the video in kbps.
|
||||
double GetAverageBitRate() const;
|
||||
|
||||
// The largest data size for any 4 consecutive frames in kilobits.
|
||||
double GetMaxCpbSize() const;
|
||||
|
||||
// The ratio of total bytes decompressed over total bytes compressed.
|
||||
double GetCompressionRatio() const;
|
||||
|
||||
// The maximum number of VP9 column tiles.
|
||||
int GetMaxColumnTiles() const;
|
||||
|
||||
// The minimum distance in frames between two consecutive alternate reference
|
||||
// frames.
|
||||
int GetMinimumAltrefDistance() const;
|
||||
|
||||
// The maximum number of reference frames that had to be stored.
|
||||
int GetMaxReferenceFrames() const;
|
||||
|
||||
// Sets the duration of the video stream in nanoseconds. If the duration is
|
||||
// not explictly set by this function then this class will use end - start
|
||||
// as the duration.
|
||||
void set_duration(int64_t time_ns) { duration_ns_ = time_ns; }
|
||||
double max_luma_sample_rate_grace_percent() const {
|
||||
return max_luma_sample_rate_grace_percent_;
|
||||
}
|
||||
void set_max_luma_sample_rate_grace_percent(double percent) {
|
||||
max_luma_sample_rate_grace_percent_ = percent;
|
||||
}
|
||||
bool estimate_last_frame_duration() const {
|
||||
return estimate_last_frame_duration_;
|
||||
}
|
||||
|
||||
// If true try to estimate the last frame's duration if the stream's duration
|
||||
// is not set or the stream's duration equals the last frame's timestamp.
|
||||
void set_estimate_last_frame_duration(bool flag) {
|
||||
estimate_last_frame_duration_ = flag;
|
||||
}
|
||||
|
||||
private:
|
||||
int frames;
|
||||
int displayed_frames;
|
||||
|
||||
int64_t start_ns_;
|
||||
int64_t end_ns_;
|
||||
int64_t duration_ns_;
|
||||
|
||||
int64_t max_luma_picture_size_;
|
||||
int64_t max_luma_picture_breadth_;
|
||||
|
||||
// This is used to calculate the maximum number of luma samples per second.
|
||||
// The first value is the luma picture size and the second value is the time
|
||||
// in nanoseconds of one frame.
|
||||
std::queue<std::pair<int64_t, int64_t>> luma_window_;
|
||||
int64_t current_luma_size_;
|
||||
int64_t max_luma_size_;
|
||||
int64_t max_luma_end_ns_;
|
||||
|
||||
// MaxLumaSampleRate = (ExampleFrameRate + ExampleFrameRate /
|
||||
// MinimumAltrefDistance) * MaxLumaPictureSize. For levels 1-4
|
||||
// ExampleFrameRate / MinimumAltrefDistance is non-integer, so using a sliding
|
||||
// window of one frame to calculate MaxLumaSampleRate may have frames >
|
||||
// (ExampleFrameRate + ExampleFrameRate / MinimumAltrefDistance) in the
|
||||
// window. In order to address this issue, a grace percent of 1.5 was added.
|
||||
double max_luma_sample_rate_grace_percent_;
|
||||
|
||||
bool first_altref;
|
||||
int frames_since_last_altref;
|
||||
int minimum_altref_distance;
|
||||
int64_t min_altref_end_ns;
|
||||
|
||||
// This is used to calculate the maximum number of compressed bytes for four
|
||||
// consecutive frames. The first value is the compressed frame size and the
|
||||
// second value is the time in nanoseconds of one frame.
|
||||
std::queue<std::pair<int64_t, int64_t>> cpb_window_;
|
||||
int64_t max_cpb_window_size_;
|
||||
int64_t max_cpb_window_end_ns_;
|
||||
int64_t current_cpb_size_;
|
||||
int64_t max_cpb_size_;
|
||||
int64_t max_cpb_start_ns_;
|
||||
int64_t max_cpb_end_ns_;
|
||||
|
||||
int64_t total_compressed_size_;
|
||||
int64_t total_uncompressed_bits_;
|
||||
int frames_refreshed_;
|
||||
int max_frames_refreshed_;
|
||||
|
||||
int max_column_tiles_;
|
||||
|
||||
bool estimate_last_frame_duration_;
|
||||
};
|
||||
|
||||
} // namespace vp9_parser
|
||||
|
||||
#endif // LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "common/vp9_level_stats.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "common/hdr_util.h"
|
||||
#include "common/vp9_header_parser.h"
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
#include "testing/test_util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(fgalligan): Refactor this test with other test files in this directory.
|
||||
class Vp9LevelStatsTests : public ::testing::Test {
|
||||
public:
|
||||
Vp9LevelStatsTests() : is_reader_open_(false) {}
|
||||
|
||||
~Vp9LevelStatsTests() override { CloseReader(); }
|
||||
|
||||
void CloseReader() {
|
||||
if (is_reader_open_) {
|
||||
reader_.Close();
|
||||
}
|
||||
is_reader_open_ = false;
|
||||
}
|
||||
|
||||
void CreateAndLoadSegment(const std::string& filename,
|
||||
int expected_doc_type_ver) {
|
||||
ASSERT_NE(0u, filename.length());
|
||||
filename_ = test::GetTestFilePath(filename);
|
||||
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
|
||||
is_reader_open_ = true;
|
||||
pos_ = 0;
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
ebml_header.Parse(&reader_, pos_);
|
||||
ASSERT_EQ(1, ebml_header.m_version);
|
||||
ASSERT_EQ(1, ebml_header.m_readVersion);
|
||||
ASSERT_STREQ("webm", ebml_header.m_docType);
|
||||
ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
|
||||
ASSERT_EQ(2, ebml_header.m_docTypeReadVersion);
|
||||
mkvparser::Segment* temp;
|
||||
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, temp));
|
||||
segment_.reset(temp);
|
||||
ASSERT_FALSE(HasFailure());
|
||||
ASSERT_GE(0, segment_->Load());
|
||||
}
|
||||
|
||||
void CreateAndLoadSegment(const std::string& filename) {
|
||||
CreateAndLoadSegment(filename, 4);
|
||||
}
|
||||
|
||||
void ProcessTheFrames() {
|
||||
std::vector<uint8_t> data;
|
||||
size_t data_len = 0;
|
||||
const mkvparser::Tracks* const parser_tracks = segment_->GetTracks();
|
||||
ASSERT_TRUE(parser_tracks != NULL);
|
||||
const mkvparser::Cluster* cluster = segment_->GetFirst();
|
||||
ASSERT_TRUE(cluster);
|
||||
|
||||
while ((cluster != NULL) && !cluster->EOS()) {
|
||||
const mkvparser::BlockEntry* block_entry;
|
||||
long status = cluster->GetFirst(block_entry); // NOLINT
|
||||
ASSERT_EQ(0, status);
|
||||
|
||||
while ((block_entry != NULL) && !block_entry->EOS()) {
|
||||
const mkvparser::Block* const block = block_entry->GetBlock();
|
||||
ASSERT_TRUE(block != NULL);
|
||||
const long long trackNum = block->GetTrackNumber(); // NOLINT
|
||||
const mkvparser::Track* const parser_track =
|
||||
parser_tracks->GetTrackByNumber(
|
||||
static_cast<unsigned long>(trackNum)); // NOLINT
|
||||
ASSERT_TRUE(parser_track != NULL);
|
||||
const long long track_type = parser_track->GetType(); // NOLINT
|
||||
|
||||
if (track_type == mkvparser::Track::kVideo) {
|
||||
const int frame_count = block->GetFrameCount();
|
||||
const long long time_ns = block->GetTime(cluster); // NOLINT
|
||||
|
||||
for (int i = 0; i < frame_count; ++i) {
|
||||
const mkvparser::Block::Frame& frame = block->GetFrame(i);
|
||||
if (static_cast<size_t>(frame.len) > data.size()) {
|
||||
data.resize(frame.len);
|
||||
data_len = static_cast<size_t>(frame.len);
|
||||
}
|
||||
ASSERT_FALSE(frame.Read(&reader_, &data[0]));
|
||||
parser_.ParseUncompressedHeader(&data[0], data_len);
|
||||
stats_.AddFrame(parser_, time_ns);
|
||||
}
|
||||
}
|
||||
|
||||
status = cluster->GetNext(block_entry, block_entry);
|
||||
ASSERT_EQ(0, status);
|
||||
}
|
||||
|
||||
cluster = segment_->GetNext(cluster);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
mkvparser::MkvReader reader_;
|
||||
bool is_reader_open_;
|
||||
std::unique_ptr<mkvparser::Segment> segment_;
|
||||
std::string filename_;
|
||||
long long pos_; // NOLINT
|
||||
vp9_parser::Vp9HeaderParser parser_;
|
||||
vp9_parser::Vp9LevelStats stats_;
|
||||
};
|
||||
|
||||
TEST_F(Vp9LevelStatsTests, VideoOnlyFile) {
|
||||
CreateAndLoadSegment("test_stereo_left_right.webm");
|
||||
ProcessTheFrames();
|
||||
EXPECT_EQ(256, parser_.width());
|
||||
EXPECT_EQ(144, parser_.height());
|
||||
EXPECT_EQ(1, parser_.column_tiles());
|
||||
EXPECT_EQ(0, parser_.frame_parallel_mode());
|
||||
|
||||
EXPECT_EQ(11, stats_.GetLevel());
|
||||
EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate());
|
||||
EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize());
|
||||
EXPECT_DOUBLE_EQ(264.03233333333333, stats_.GetAverageBitRate());
|
||||
EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize());
|
||||
EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio());
|
||||
EXPECT_EQ(1, stats_.GetMaxColumnTiles());
|
||||
EXPECT_EQ(11, stats_.GetMinimumAltrefDistance());
|
||||
EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
|
||||
|
||||
EXPECT_TRUE(stats_.estimate_last_frame_duration());
|
||||
stats_.set_estimate_last_frame_duration(false);
|
||||
EXPECT_DOUBLE_EQ(275.512, stats_.GetAverageBitRate());
|
||||
}
|
||||
|
||||
TEST_F(Vp9LevelStatsTests, Muxed) {
|
||||
CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4);
|
||||
ProcessTheFrames();
|
||||
EXPECT_EQ(854, parser_.width());
|
||||
EXPECT_EQ(480, parser_.height());
|
||||
EXPECT_EQ(2, parser_.column_tiles());
|
||||
EXPECT_EQ(1, parser_.frame_parallel_mode());
|
||||
|
||||
EXPECT_EQ(30, stats_.GetLevel());
|
||||
EXPECT_EQ(9838080, stats_.GetMaxLumaSampleRate());
|
||||
EXPECT_EQ(409920, stats_.GetMaxLumaPictureSize());
|
||||
EXPECT_DOUBLE_EQ(447.09394572025053, stats_.GetAverageBitRate());
|
||||
EXPECT_DOUBLE_EQ(118.464, stats_.GetMaxCpbSize());
|
||||
EXPECT_DOUBLE_EQ(241.17670131398313, stats_.GetCompressionRatio());
|
||||
EXPECT_EQ(2, stats_.GetMaxColumnTiles());
|
||||
EXPECT_EQ(9, stats_.GetMinimumAltrefDistance());
|
||||
EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
|
||||
|
||||
stats_.set_estimate_last_frame_duration(false);
|
||||
EXPECT_DOUBLE_EQ(468.38413361169108, stats_.GetAverageBitRate());
|
||||
}
|
||||
|
||||
TEST_F(Vp9LevelStatsTests, SetDuration) {
|
||||
CreateAndLoadSegment("test_stereo_left_right.webm");
|
||||
ProcessTheFrames();
|
||||
const int64_t kDurationNano = 2080000000; // 2.08 seconds
|
||||
stats_.set_duration(kDurationNano);
|
||||
EXPECT_EQ(256, parser_.width());
|
||||
EXPECT_EQ(144, parser_.height());
|
||||
EXPECT_EQ(1, parser_.column_tiles());
|
||||
EXPECT_EQ(0, parser_.frame_parallel_mode());
|
||||
|
||||
EXPECT_EQ(11, stats_.GetLevel());
|
||||
EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate());
|
||||
EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize());
|
||||
EXPECT_DOUBLE_EQ(264.9153846153846, stats_.GetAverageBitRate());
|
||||
EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize());
|
||||
EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio());
|
||||
EXPECT_EQ(1, stats_.GetMaxColumnTiles());
|
||||
EXPECT_EQ(11, stats_.GetMinimumAltrefDistance());
|
||||
EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef LIBWEBM_COMMON_WEBM_CONSTANTS_H_
|
||||
#define LIBWEBM_COMMON_WEBM_CONSTANTS_H_
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
const double kNanosecondsPerSecond = 1000000000.0;
|
||||
const int kNanosecondsPerSecondi = 1000000000;
|
||||
const int kNanosecondsPerMillisecond = 1000000;
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_COMMON_WEBM_CONSTANTS_H_
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "common/webm_endian.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace {
|
||||
|
||||
// Swaps unsigned 32 bit values to big endian if needed. Returns |value|
|
||||
// unmodified if architecture is big endian. Returns swapped bytes of |value|
|
||||
// if architecture is little endian. Returns 0 otherwise.
|
||||
uint32_t swap32_check_little_endian(uint32_t value) {
|
||||
// Check endianness.
|
||||
union {
|
||||
uint32_t val32;
|
||||
uint8_t c[4];
|
||||
} check;
|
||||
check.val32 = 0x01234567U;
|
||||
|
||||
// Check for big endian.
|
||||
if (check.c[3] == 0x67)
|
||||
return value;
|
||||
|
||||
// Check for not little endian.
|
||||
if (check.c[0] != 0x67)
|
||||
return 0;
|
||||
|
||||
return value << 24 | ((value << 8) & 0x0000FF00U) |
|
||||
((value >> 8) & 0x00FF0000U) | value >> 24;
|
||||
}
|
||||
|
||||
// Swaps unsigned 64 bit values to big endian if needed. Returns |value|
|
||||
// unmodified if architecture is big endian. Returns swapped bytes of |value|
|
||||
// if architecture is little endian. Returns 0 otherwise.
|
||||
uint64_t swap64_check_little_endian(uint64_t value) {
|
||||
// Check endianness.
|
||||
union {
|
||||
uint64_t val64;
|
||||
uint8_t c[8];
|
||||
} check;
|
||||
check.val64 = 0x0123456789ABCDEFULL;
|
||||
|
||||
// Check for big endian.
|
||||
if (check.c[7] == 0xEF)
|
||||
return value;
|
||||
|
||||
// Check for not little endian.
|
||||
if (check.c[0] != 0xEF)
|
||||
return 0;
|
||||
|
||||
return value << 56 | ((value << 40) & 0x00FF000000000000ULL) |
|
||||
((value << 24) & 0x0000FF0000000000ULL) |
|
||||
((value << 8) & 0x000000FF00000000ULL) |
|
||||
((value >> 8) & 0x00000000FF000000ULL) |
|
||||
((value >> 24) & 0x0000000000FF0000ULL) |
|
||||
((value >> 40) & 0x000000000000FF00ULL) | value >> 56;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
uint32_t host_to_bigendian(uint32_t value) {
|
||||
return swap32_check_little_endian(value);
|
||||
}
|
||||
|
||||
uint32_t bigendian_to_host(uint32_t value) {
|
||||
return swap32_check_little_endian(value);
|
||||
}
|
||||
|
||||
uint64_t host_to_bigendian(uint64_t value) {
|
||||
return swap64_check_little_endian(value);
|
||||
}
|
||||
|
||||
uint64_t bigendian_to_host(uint64_t value) {
|
||||
return swap64_check_little_endian(value);
|
||||
}
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef LIBWEBM_COMMON_WEBM_ENDIAN_H_
|
||||
#define LIBWEBM_COMMON_WEBM_ENDIAN_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
// Swaps unsigned 32 bit values to big endian if needed. Returns |value| if
|
||||
// architecture is big endian. Returns big endian value if architecture is
|
||||
// little endian. Returns 0 otherwise.
|
||||
uint32_t host_to_bigendian(uint32_t value);
|
||||
|
||||
// Swaps unsigned 32 bit values to little endian if needed. Returns |value| if
|
||||
// architecture is big endian. Returns little endian value if architecture is
|
||||
// little endian. Returns 0 otherwise.
|
||||
uint32_t bigendian_to_host(uint32_t value);
|
||||
|
||||
// Swaps unsigned 64 bit values to big endian if needed. Returns |value| if
|
||||
// architecture is big endian. Returns big endian value if architecture is
|
||||
// little endian. Returns 0 otherwise.
|
||||
uint64_t host_to_bigendian(uint64_t value);
|
||||
|
||||
// Swaps unsigned 64 bit values to little endian if needed. Returns |value| if
|
||||
// architecture is big endian. Returns little endian value if architecture is
|
||||
// little endian. Returns 0 otherwise.
|
||||
uint64_t bigendian_to_host(uint64_t value);
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_COMMON_WEBM_ENDIAN_H_
|
||||
@@ -0,0 +1,193 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef COMMON_WEBMIDS_H_
|
||||
#define COMMON_WEBMIDS_H_
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
enum MkvId {
|
||||
kMkvEBML = 0x1A45DFA3,
|
||||
kMkvEBMLVersion = 0x4286,
|
||||
kMkvEBMLReadVersion = 0x42F7,
|
||||
kMkvEBMLMaxIDLength = 0x42F2,
|
||||
kMkvEBMLMaxSizeLength = 0x42F3,
|
||||
kMkvDocType = 0x4282,
|
||||
kMkvDocTypeVersion = 0x4287,
|
||||
kMkvDocTypeReadVersion = 0x4285,
|
||||
kMkvVoid = 0xEC,
|
||||
kMkvSignatureSlot = 0x1B538667,
|
||||
kMkvSignatureAlgo = 0x7E8A,
|
||||
kMkvSignatureHash = 0x7E9A,
|
||||
kMkvSignaturePublicKey = 0x7EA5,
|
||||
kMkvSignature = 0x7EB5,
|
||||
kMkvSignatureElements = 0x7E5B,
|
||||
kMkvSignatureElementList = 0x7E7B,
|
||||
kMkvSignedElement = 0x6532,
|
||||
// segment
|
||||
kMkvSegment = 0x18538067,
|
||||
// Meta Seek Information
|
||||
kMkvSeekHead = 0x114D9B74,
|
||||
kMkvSeek = 0x4DBB,
|
||||
kMkvSeekID = 0x53AB,
|
||||
kMkvSeekPosition = 0x53AC,
|
||||
// Segment Information
|
||||
kMkvInfo = 0x1549A966,
|
||||
kMkvTimecodeScale = 0x2AD7B1,
|
||||
kMkvDuration = 0x4489,
|
||||
kMkvDateUTC = 0x4461,
|
||||
kMkvTitle = 0x7BA9,
|
||||
kMkvMuxingApp = 0x4D80,
|
||||
kMkvWritingApp = 0x5741,
|
||||
// Cluster
|
||||
kMkvCluster = 0x1F43B675,
|
||||
kMkvTimecode = 0xE7,
|
||||
kMkvPrevSize = 0xAB,
|
||||
kMkvBlockGroup = 0xA0,
|
||||
kMkvBlock = 0xA1,
|
||||
kMkvBlockDuration = 0x9B,
|
||||
kMkvReferenceBlock = 0xFB,
|
||||
kMkvLaceNumber = 0xCC,
|
||||
kMkvSimpleBlock = 0xA3,
|
||||
kMkvBlockAdditions = 0x75A1,
|
||||
kMkvBlockMore = 0xA6,
|
||||
kMkvBlockAddID = 0xEE,
|
||||
kMkvBlockAdditional = 0xA5,
|
||||
kMkvDiscardPadding = 0x75A2,
|
||||
// Track
|
||||
kMkvTracks = 0x1654AE6B,
|
||||
kMkvTrackEntry = 0xAE,
|
||||
kMkvTrackNumber = 0xD7,
|
||||
kMkvTrackUID = 0x73C5,
|
||||
kMkvTrackType = 0x83,
|
||||
kMkvFlagEnabled = 0xB9,
|
||||
kMkvFlagDefault = 0x88,
|
||||
kMkvFlagForced = 0x55AA,
|
||||
kMkvFlagLacing = 0x9C,
|
||||
kMkvDefaultDuration = 0x23E383,
|
||||
kMkvMaxBlockAdditionID = 0x55EE,
|
||||
kMkvName = 0x536E,
|
||||
kMkvLanguage = 0x22B59C,
|
||||
kMkvCodecID = 0x86,
|
||||
kMkvCodecPrivate = 0x63A2,
|
||||
kMkvCodecName = 0x258688,
|
||||
kMkvCodecDelay = 0x56AA,
|
||||
kMkvSeekPreRoll = 0x56BB,
|
||||
// video
|
||||
kMkvVideo = 0xE0,
|
||||
kMkvFlagInterlaced = 0x9A,
|
||||
kMkvStereoMode = 0x53B8,
|
||||
kMkvAlphaMode = 0x53C0,
|
||||
kMkvPixelWidth = 0xB0,
|
||||
kMkvPixelHeight = 0xBA,
|
||||
kMkvPixelCropBottom = 0x54AA,
|
||||
kMkvPixelCropTop = 0x54BB,
|
||||
kMkvPixelCropLeft = 0x54CC,
|
||||
kMkvPixelCropRight = 0x54DD,
|
||||
kMkvDisplayWidth = 0x54B0,
|
||||
kMkvDisplayHeight = 0x54BA,
|
||||
kMkvDisplayUnit = 0x54B2,
|
||||
kMkvAspectRatioType = 0x54B3,
|
||||
kMkvColourSpace = 0x2EB524,
|
||||
kMkvFrameRate = 0x2383E3,
|
||||
// end video
|
||||
// colour
|
||||
kMkvColour = 0x55B0,
|
||||
kMkvMatrixCoefficients = 0x55B1,
|
||||
kMkvBitsPerChannel = 0x55B2,
|
||||
kMkvChromaSubsamplingHorz = 0x55B3,
|
||||
kMkvChromaSubsamplingVert = 0x55B4,
|
||||
kMkvCbSubsamplingHorz = 0x55B5,
|
||||
kMkvCbSubsamplingVert = 0x55B6,
|
||||
kMkvChromaSitingHorz = 0x55B7,
|
||||
kMkvChromaSitingVert = 0x55B8,
|
||||
kMkvRange = 0x55B9,
|
||||
kMkvTransferCharacteristics = 0x55BA,
|
||||
kMkvPrimaries = 0x55BB,
|
||||
kMkvMaxCLL = 0x55BC,
|
||||
kMkvMaxFALL = 0x55BD,
|
||||
// mastering metadata
|
||||
kMkvMasteringMetadata = 0x55D0,
|
||||
kMkvPrimaryRChromaticityX = 0x55D1,
|
||||
kMkvPrimaryRChromaticityY = 0x55D2,
|
||||
kMkvPrimaryGChromaticityX = 0x55D3,
|
||||
kMkvPrimaryGChromaticityY = 0x55D4,
|
||||
kMkvPrimaryBChromaticityX = 0x55D5,
|
||||
kMkvPrimaryBChromaticityY = 0x55D6,
|
||||
kMkvWhitePointChromaticityX = 0x55D7,
|
||||
kMkvWhitePointChromaticityY = 0x55D8,
|
||||
kMkvLuminanceMax = 0x55D9,
|
||||
kMkvLuminanceMin = 0x55DA,
|
||||
// end mastering metadata
|
||||
// end colour
|
||||
// projection
|
||||
kMkvProjection = 0x7670,
|
||||
kMkvProjectionType = 0x7671,
|
||||
kMkvProjectionPrivate = 0x7672,
|
||||
kMkvProjectionPoseYaw = 0x7673,
|
||||
kMkvProjectionPosePitch = 0x7674,
|
||||
kMkvProjectionPoseRoll = 0x7675,
|
||||
// end projection
|
||||
// audio
|
||||
kMkvAudio = 0xE1,
|
||||
kMkvSamplingFrequency = 0xB5,
|
||||
kMkvOutputSamplingFrequency = 0x78B5,
|
||||
kMkvChannels = 0x9F,
|
||||
kMkvBitDepth = 0x6264,
|
||||
// end audio
|
||||
// ContentEncodings
|
||||
kMkvContentEncodings = 0x6D80,
|
||||
kMkvContentEncoding = 0x6240,
|
||||
kMkvContentEncodingOrder = 0x5031,
|
||||
kMkvContentEncodingScope = 0x5032,
|
||||
kMkvContentEncodingType = 0x5033,
|
||||
kMkvContentCompression = 0x5034,
|
||||
kMkvContentCompAlgo = 0x4254,
|
||||
kMkvContentCompSettings = 0x4255,
|
||||
kMkvContentEncryption = 0x5035,
|
||||
kMkvContentEncAlgo = 0x47E1,
|
||||
kMkvContentEncKeyID = 0x47E2,
|
||||
kMkvContentSignature = 0x47E3,
|
||||
kMkvContentSigKeyID = 0x47E4,
|
||||
kMkvContentSigAlgo = 0x47E5,
|
||||
kMkvContentSigHashAlgo = 0x47E6,
|
||||
kMkvContentEncAESSettings = 0x47E7,
|
||||
kMkvAESSettingsCipherMode = 0x47E8,
|
||||
kMkvAESSettingsCipherInitData = 0x47E9,
|
||||
// end ContentEncodings
|
||||
// Cueing Data
|
||||
kMkvCues = 0x1C53BB6B,
|
||||
kMkvCuePoint = 0xBB,
|
||||
kMkvCueTime = 0xB3,
|
||||
kMkvCueTrackPositions = 0xB7,
|
||||
kMkvCueTrack = 0xF7,
|
||||
kMkvCueClusterPosition = 0xF1,
|
||||
kMkvCueBlockNumber = 0x5378,
|
||||
// Chapters
|
||||
kMkvChapters = 0x1043A770,
|
||||
kMkvEditionEntry = 0x45B9,
|
||||
kMkvChapterAtom = 0xB6,
|
||||
kMkvChapterUID = 0x73C4,
|
||||
kMkvChapterStringUID = 0x5654,
|
||||
kMkvChapterTimeStart = 0x91,
|
||||
kMkvChapterTimeEnd = 0x92,
|
||||
kMkvChapterDisplay = 0x80,
|
||||
kMkvChapString = 0x85,
|
||||
kMkvChapLanguage = 0x437C,
|
||||
kMkvChapCountry = 0x437E,
|
||||
// Tags
|
||||
kMkvTags = 0x1254C367,
|
||||
kMkvTag = 0x7373,
|
||||
kMkvSimpleTag = 0x67C8,
|
||||
kMkvTagName = 0x45A3,
|
||||
kMkvTagString = 0x4487
|
||||
};
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // COMMON_WEBMIDS_H_
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include "webvtt/vttreader.h"
|
||||
#include "webvtt/webvttparser.h"
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc != 2) {
|
||||
fprintf(stdout, "usage: dumpvtt <vtt file>\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
libwebvtt::VttReader reader;
|
||||
const char* const filename = argv[1];
|
||||
|
||||
if (int e = reader.Open(filename)) {
|
||||
(void)e;
|
||||
fprintf(stderr, "open failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
libwebvtt::Parser parser(&reader);
|
||||
|
||||
if (int e = parser.Init()) {
|
||||
(void)e;
|
||||
fprintf(stderr, "parser init failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (libwebvtt::Cue cue;;) {
|
||||
const int e = parser.Parse(&cue);
|
||||
|
||||
if (e < 0) { // error
|
||||
fprintf(stderr, "error parsing cue\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (e > 0) // EOF
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
fprintf(stdout, "cue identifier: \"%s\"\n", cue.identifier.c_str());
|
||||
|
||||
const libwebvtt::Time& st = cue.start_time;
|
||||
fprintf(stdout, "cue start time: \"HH=%i MM=%i SS=%i SSS=%i\"\n", st.hours,
|
||||
st.minutes, st.seconds, st.milliseconds);
|
||||
|
||||
const libwebvtt::Time& sp = cue.stop_time;
|
||||
fprintf(stdout, "cue stop time: \"HH=%i MM=%i SS=%i SSS=%i\"\n", sp.hours,
|
||||
sp.minutes, sp.seconds, sp.milliseconds);
|
||||
|
||||
{
|
||||
typedef libwebvtt::Cue::settings_t::const_iterator iter_t;
|
||||
iter_t i = cue.settings.begin();
|
||||
const iter_t j = cue.settings.end();
|
||||
|
||||
if (i == j) {
|
||||
fprintf(stdout, "cue setting: <no settings present>\n");
|
||||
} else {
|
||||
while (i != j) {
|
||||
const libwebvtt::Setting& setting = *i++;
|
||||
fprintf(stdout, "cue setting: name=%s value=%s\n",
|
||||
setting.name.c_str(), setting.value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
typedef libwebvtt::Cue::payload_t::const_iterator iter_t;
|
||||
iter_t i = cue.payload.begin();
|
||||
const iter_t j = cue.payload.end();
|
||||
|
||||
int idx = 1;
|
||||
while (i != j) {
|
||||
const std::string& payload = *i++;
|
||||
const char* const payload_str = payload.c_str();
|
||||
fprintf(stdout, "cue payload[%i]: \"%s\"\n", idx, payload_str);
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_HDR_UTIL_HPP_
|
||||
#define LIBWEBM_HDR_UTIL_HPP_
|
||||
|
||||
// This file is a wrapper for the file included immediately after this comment.
|
||||
// New projects should not include this file: include the file included below.
|
||||
#include "common/hdr_util.h"
|
||||
|
||||
#endif // LIBWEBM_HDR_UTIL_HPP_
|
||||
@@ -0,0 +1,207 @@
|
||||
#!/bin/sh
|
||||
##
|
||||
## Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
##
|
||||
## This script generates 'WebM.framework'. An iOS app can mux/demux WebM
|
||||
## container files by including 'WebM.framework'.
|
||||
##
|
||||
## Run ./iosbuild.sh to generate 'WebM.framework'. By default the framework
|
||||
## bundle will be created in a directory called framework. Use --out-dir to
|
||||
## change the output directory.
|
||||
##
|
||||
## This script is based on iosbuild.sh from the libwebp project.
|
||||
. $(dirname $0)/common/common.sh
|
||||
|
||||
# Trap function. Cleans up build output.
|
||||
cleanup() {
|
||||
local readonly res=$?
|
||||
cd "${ORIG_PWD}"
|
||||
|
||||
for dir in ${LIBDIRS}; do
|
||||
if [ -d "${dir}" ]; then
|
||||
rm -rf "${dir}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $res -ne 0 ]; then
|
||||
elog "build exited with error ($res)"
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
check_dir libwebm
|
||||
|
||||
iosbuild_usage() {
|
||||
cat << EOF
|
||||
Usage: ${0##*/} [arguments]
|
||||
--help: Display this message and exit.
|
||||
--out-dir: Override output directory (default is ${OUTDIR}).
|
||||
--show-build-output: Show output from each library build.
|
||||
--verbose: Output information about the environment and each stage of the
|
||||
build.
|
||||
EOF
|
||||
}
|
||||
|
||||
# Extract the latest SDK version from the final field of the form: iphoneosX.Y
|
||||
readonly SDK=$(xcodebuild -showsdks \
|
||||
| grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
|
||||
)
|
||||
|
||||
# Extract Xcode version.
|
||||
readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
|
||||
if [ -z "${XCODE}" ]; then
|
||||
echo "Xcode not available"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
|
||||
# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
|
||||
readonly INCLUDES="common/file_util.h
|
||||
common/hdr_util.h
|
||||
common/webmids.h
|
||||
mkvmuxer/mkvmuxer.h
|
||||
mkvmuxer/mkvmuxertypes.h
|
||||
mkvmuxer/mkvmuxerutil.h
|
||||
mkvmuxer/mkvwriter.h
|
||||
mkvparser/mkvparser.h
|
||||
mkvparser/mkvreader.h"
|
||||
readonly PLATFORMS="iPhoneSimulator
|
||||
iPhoneSimulator64
|
||||
iPhoneOS-V7
|
||||
iPhoneOS-V7s
|
||||
iPhoneOS-V7-arm64"
|
||||
readonly TARGETDIR="WebM.framework"
|
||||
readonly DEVELOPER="$(xcode-select --print-path)"
|
||||
readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
|
||||
readonly LIPO="$(xcrun -sdk iphoneos${SDK} -find lipo)"
|
||||
LIBLIST=""
|
||||
OPT_FLAGS="-DNDEBUG -O3"
|
||||
readonly SDK_MAJOR_VERSION="$(echo ${SDK} | awk -F '.' '{ print $1 }')"
|
||||
|
||||
if [ -z "${SDK_MAJOR_VERSION}" ]; then
|
||||
elog "iOS SDK not available"
|
||||
exit 1
|
||||
elif [ "${SDK_MAJOR_VERSION}" -lt "6" ]; then
|
||||
elog "You need iOS SDK version 6 or above"
|
||||
exit 1
|
||||
else
|
||||
vlog "iOS SDK Version ${SDK}"
|
||||
fi
|
||||
|
||||
|
||||
# Parse the command line.
|
||||
while [ -n "$1" ]; do
|
||||
case "$1" in
|
||||
--help)
|
||||
iosbuild_usage
|
||||
exit
|
||||
;;
|
||||
--out-dir)
|
||||
OUTDIR="$2"
|
||||
shift
|
||||
;;
|
||||
--enable-debug)
|
||||
OPT_FLAGS="-g"
|
||||
;;
|
||||
--show-build-output)
|
||||
devnull=
|
||||
;;
|
||||
--verbose)
|
||||
VERBOSE=yes
|
||||
;;
|
||||
*)
|
||||
iosbuild_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
readonly OPT_FLAGS="${OPT_FLAGS}"
|
||||
readonly OUTDIR="${OUTDIR:-framework}"
|
||||
|
||||
if [ "${VERBOSE}" = "yes" ]; then
|
||||
cat << EOF
|
||||
OUTDIR=${OUTDIR}
|
||||
INCLUDES=${INCLUDES}
|
||||
PLATFORMS=${PLATFORMS}
|
||||
TARGETDIR=${TARGETDIR}
|
||||
DEVELOPER=${DEVELOPER}
|
||||
LIPO=${LIPO}
|
||||
OPT_FLAGS=${OPT_FLAGS}
|
||||
ORIG_PWD=${ORIG_PWD}
|
||||
EOF
|
||||
fi
|
||||
|
||||
rm -rf "${OUTDIR}/${TARGETDIR}"
|
||||
mkdir -p "${OUTDIR}/${TARGETDIR}/Headers/"
|
||||
|
||||
for PLATFORM in ${PLATFORMS}; do
|
||||
ARCH2=""
|
||||
if [ "${PLATFORM}" = "iPhoneOS-V7-arm64" ]; then
|
||||
PLATFORM="iPhoneOS"
|
||||
ARCH="aarch64"
|
||||
ARCH2="arm64"
|
||||
elif [ "${PLATFORM}" = "iPhoneOS-V7s" ]; then
|
||||
PLATFORM="iPhoneOS"
|
||||
ARCH="armv7s"
|
||||
elif [ "${PLATFORM}" = "iPhoneOS-V7" ]; then
|
||||
PLATFORM="iPhoneOS"
|
||||
ARCH="armv7"
|
||||
elif [ "${PLATFORM}" = "iPhoneOS-V6" ]; then
|
||||
PLATFORM="iPhoneOS"
|
||||
ARCH="armv6"
|
||||
elif [ "${PLATFORM}" = "iPhoneSimulator64" ]; then
|
||||
PLATFORM="iPhoneSimulator"
|
||||
ARCH="x86_64"
|
||||
else
|
||||
ARCH="i386"
|
||||
fi
|
||||
|
||||
LIBDIR="${OUTDIR}/${PLATFORM}-${SDK}-${ARCH}"
|
||||
LIBDIRS="${LIBDIRS} ${LIBDIR}"
|
||||
LIBFILE="${LIBDIR}/libwebm.a"
|
||||
eval mkdir -p "${LIBDIR}" ${devnull}
|
||||
|
||||
DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
|
||||
SDKROOT="${PLATFORMSROOT}/"
|
||||
SDKROOT="${SDKROOT}${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/"
|
||||
CXXFLAGS="-arch ${ARCH2:-${ARCH}} -isysroot ${SDKROOT} ${OPT_FLAGS}
|
||||
-miphoneos-version-min=6.0"
|
||||
|
||||
# enable bitcode if available
|
||||
if [ "${SDK_MAJOR_VERSION}" -gt 8 ]; then
|
||||
CXXFLAGS="${CXXFLAGS} -fembed-bitcode"
|
||||
fi
|
||||
|
||||
# Build using the legacy makefile (instead of generating via cmake).
|
||||
eval make -f Makefile.unix libwebm.a CXXFLAGS=\"${CXXFLAGS}\" ${devnull}
|
||||
|
||||
# copy lib and add it to LIBLIST.
|
||||
eval cp libwebm.a "${LIBFILE}" ${devnull}
|
||||
LIBLIST="${LIBLIST} ${LIBFILE}"
|
||||
|
||||
# clean build so we can go again.
|
||||
eval make -f Makefile.unix clean ${devnull}
|
||||
done
|
||||
|
||||
# create include sub dirs in framework dir.
|
||||
readonly framework_header_dir="${OUTDIR}/${TARGETDIR}/Headers"
|
||||
readonly framework_header_sub_dirs="common mkvmuxer mkvparser"
|
||||
for dir in ${framework_header_sub_dirs}; do
|
||||
mkdir "${framework_header_dir}/${dir}"
|
||||
done
|
||||
|
||||
for header_file in ${INCLUDES}; do
|
||||
eval cp -p ${header_file} "${framework_header_dir}/${header_file}" ${devnull}
|
||||
done
|
||||
|
||||
eval ${LIPO} -create ${LIBLIST} -output "${OUTDIR}/${TARGETDIR}/WebM" ${devnull}
|
||||
echo "Succesfully built ${TARGETDIR} in ${OUTDIR}."
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "m2ts/webm2pes.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "common/libwebm_util.h"
|
||||
#include "m2ts/vpxpes_parser.h"
|
||||
#include "testing/test_util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class Webm2PesTests : public ::testing::Test {
|
||||
public:
|
||||
// Constants for validating known values from input data.
|
||||
const std::uint8_t kMinVideoStreamId = 0xE0;
|
||||
const std::uint8_t kMaxVideoStreamId = 0xEF;
|
||||
const int kPesHeaderSize = 6;
|
||||
const int kPesOptionalHeaderStartOffset = kPesHeaderSize;
|
||||
const int kPesOptionalHeaderSize = 9;
|
||||
const int kPesOptionalHeaderMarkerValue = 0x2;
|
||||
const int kWebm2PesOptHeaderRemainingSize = 6;
|
||||
const int kBcmvHeaderSize = 10;
|
||||
|
||||
Webm2PesTests() = default;
|
||||
~Webm2PesTests() = default;
|
||||
|
||||
void CreateAndLoadTestInput() {
|
||||
libwebm::Webm2Pes converter(input_file_name_, temp_file_name_.name());
|
||||
ASSERT_TRUE(converter.ConvertToFile());
|
||||
ASSERT_TRUE(parser_.Open(pes_file_name()));
|
||||
}
|
||||
|
||||
bool VerifyPacketStartCode(const libwebm::VpxPesParser::PesHeader& header) {
|
||||
// PES packets all start with the byte sequence 0x0 0x0 0x1.
|
||||
if (header.start_code[0] != 0 || header.start_code[1] != 0 ||
|
||||
header.start_code[2] != 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& pes_file_name() const { return temp_file_name_.name(); }
|
||||
libwebm::VpxPesParser* parser() { return &parser_; }
|
||||
|
||||
private:
|
||||
const libwebm::TempFileDeleter temp_file_name_;
|
||||
const std::string input_file_name_ =
|
||||
test::GetTestFilePath("bbb_480p_vp9_opus_1second.webm");
|
||||
libwebm::VpxPesParser parser_;
|
||||
};
|
||||
|
||||
TEST_F(Webm2PesTests, CreatePesFile) { CreateAndLoadTestInput(); }
|
||||
|
||||
TEST_F(Webm2PesTests, CanParseFirstPacket) {
|
||||
CreateAndLoadTestInput();
|
||||
libwebm::VpxPesParser::PesHeader header;
|
||||
libwebm::VideoFrame frame;
|
||||
ASSERT_TRUE(parser()->ParseNextPacket(&header, &frame));
|
||||
EXPECT_TRUE(VerifyPacketStartCode(header));
|
||||
|
||||
// 9 bytes: PES optional header
|
||||
// 10 bytes: BCMV Header
|
||||
// 83 bytes: frame
|
||||
// 102 bytes total in packet length field:
|
||||
const std::size_t kPesPayloadLength = 102;
|
||||
EXPECT_EQ(kPesPayloadLength, header.packet_length);
|
||||
|
||||
EXPECT_GE(header.stream_id, kMinVideoStreamId);
|
||||
EXPECT_LE(header.stream_id, kMaxVideoStreamId);
|
||||
|
||||
// Test PesOptionalHeader values.
|
||||
EXPECT_EQ(kPesOptionalHeaderMarkerValue, header.opt_header.marker);
|
||||
EXPECT_EQ(kWebm2PesOptHeaderRemainingSize, header.opt_header.remaining_size);
|
||||
EXPECT_EQ(0, header.opt_header.scrambling);
|
||||
EXPECT_EQ(0, header.opt_header.priority);
|
||||
EXPECT_EQ(0, header.opt_header.data_alignment);
|
||||
EXPECT_EQ(0, header.opt_header.copyright);
|
||||
EXPECT_EQ(0, header.opt_header.original);
|
||||
EXPECT_EQ(1, header.opt_header.has_pts);
|
||||
EXPECT_EQ(0, header.opt_header.has_dts);
|
||||
EXPECT_EQ(0, header.opt_header.unused_fields);
|
||||
|
||||
// Test the BCMV header.
|
||||
// Note: The length field of the BCMV header includes its own length.
|
||||
const std::size_t kBcmvBaseLength = 10;
|
||||
const std::size_t kFirstFrameLength = 83;
|
||||
const libwebm::VpxPesParser::BcmvHeader kFirstBcmvHeader(kFirstFrameLength +
|
||||
kBcmvBaseLength);
|
||||
EXPECT_TRUE(header.bcmv_header.Valid());
|
||||
EXPECT_EQ(kFirstBcmvHeader, header.bcmv_header);
|
||||
|
||||
// Parse the next packet to confirm correct parse and consumption of payload.
|
||||
EXPECT_TRUE(parser()->ParseNextPacket(&header, &frame));
|
||||
}
|
||||
|
||||
TEST_F(Webm2PesTests, CanMuxLargeBuffers) {
|
||||
const std::size_t kBufferSize = 100 * 1024;
|
||||
const std::int64_t kFakeTimestamp = libwebm::kNanosecondsPerSecond;
|
||||
libwebm::VideoFrame fake_frame(kFakeTimestamp, libwebm::VideoFrame::kVP9);
|
||||
ASSERT_TRUE(fake_frame.Init(kBufferSize));
|
||||
std::memset(fake_frame.buffer().data.get(), 0x80, kBufferSize);
|
||||
ASSERT_TRUE(fake_frame.SetBufferLength(kBufferSize));
|
||||
libwebm::PacketDataBuffer pes_packet_buffer;
|
||||
ASSERT_TRUE(
|
||||
libwebm::Webm2Pes::WritePesPacket(fake_frame, &pes_packet_buffer));
|
||||
|
||||
// TODO(tomfinegan): Change VpxPesParser so it can read from a buffer, and get
|
||||
// rid of this extra step.
|
||||
libwebm::FilePtr pes_file(std::fopen(pes_file_name().c_str(), "wb"),
|
||||
libwebm::FILEDeleter());
|
||||
ASSERT_EQ(pes_packet_buffer.size(),
|
||||
fwrite(&pes_packet_buffer[0], 1, pes_packet_buffer.size(),
|
||||
pes_file.get()));
|
||||
fclose(pes_file.get());
|
||||
pes_file.release();
|
||||
|
||||
libwebm::VpxPesParser parser;
|
||||
ASSERT_TRUE(parser.Open(pes_file_name()));
|
||||
libwebm::VpxPesParser::PesHeader header;
|
||||
libwebm::VideoFrame parsed_frame;
|
||||
ASSERT_TRUE(parser.ParseNextPacket(&header, &parsed_frame));
|
||||
EXPECT_EQ(fake_frame.nanosecond_pts(), parsed_frame.nanosecond_pts());
|
||||
EXPECT_EQ(fake_frame.buffer().length, parsed_frame.buffer().length);
|
||||
EXPECT_EQ(0, std::memcmp(fake_frame.buffer().data.get(),
|
||||
parsed_frame.buffer().data.get(), kBufferSize));
|
||||
}
|
||||
|
||||
TEST_F(Webm2PesTests, ParserConsumesAllInput) {
|
||||
CreateAndLoadTestInput();
|
||||
libwebm::VpxPesParser::PesHeader header;
|
||||
libwebm::VideoFrame frame;
|
||||
while (parser()->ParseNextPacket(&header, &frame) == true) {
|
||||
EXPECT_TRUE(VerifyPacketStartCode(header));
|
||||
}
|
||||
EXPECT_EQ(0, parser()->BytesAvailable());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "m2ts/vpxpes2ts.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
namespace libwebm {
|
||||
// TODO(tomfinegan): Dedupe this and PesHeaderField.
|
||||
// Stores a value and its size in bits for writing into a MPEG2 TS Header.
|
||||
// Maximum size is 64 bits. Users may call the Check() method to perform minimal
|
||||
// validation (size > 0 and <= 64).
|
||||
struct TsHeaderField {
|
||||
TsHeaderField(std::uint64_t value, std::uint32_t size_in_bits,
|
||||
std::uint8_t byte_index, std::uint8_t bits_to_shift)
|
||||
: bits(value),
|
||||
num_bits(size_in_bits),
|
||||
index(byte_index),
|
||||
shift(bits_to_shift) {}
|
||||
TsHeaderField() = delete;
|
||||
TsHeaderField(const TsHeaderField&) = default;
|
||||
TsHeaderField(TsHeaderField&&) = default;
|
||||
~TsHeaderField() = default;
|
||||
bool Check() const {
|
||||
return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64;
|
||||
}
|
||||
|
||||
// Value to be stored in the field.
|
||||
std::uint64_t bits;
|
||||
|
||||
// Number of bits in the value.
|
||||
const int num_bits;
|
||||
|
||||
// Index into the header for the byte in which |bits| will be written.
|
||||
const std::uint8_t index;
|
||||
|
||||
// Number of bits to left shift value before or'ing. Ignored for whole bytes.
|
||||
const int shift;
|
||||
};
|
||||
|
||||
// Data storage for MPEG2 Transport Stream headers.
|
||||
// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet
|
||||
struct TsHeader {
|
||||
TsHeader(bool payload_start, bool adaptation_flag, std::uint8_t counter)
|
||||
: is_payload_start(payload_start),
|
||||
has_adaptation(adaptation_flag),
|
||||
counter_value(counter) {}
|
||||
TsHeader() = delete;
|
||||
TsHeader(const TsHeader&) = default;
|
||||
TsHeader(TsHeader&&) = default;
|
||||
~TsHeader() = default;
|
||||
|
||||
void Write(PacketDataBuffer* buffer) const;
|
||||
|
||||
// Indicates the packet is the beginning of a new fragmented payload.
|
||||
const bool is_payload_start;
|
||||
|
||||
// Indicates the packet contains an adaptation field.
|
||||
const bool has_adaptation;
|
||||
|
||||
// The sync byte is the bit pattern of 0x47 (ASCII char 'G').
|
||||
const std::uint8_t kTsHeaderSyncByte = 0x47;
|
||||
const std::uint8_t sync_byte = kTsHeaderSyncByte;
|
||||
|
||||
// Value for |continuity_counter|. Used to detect gaps when demuxing.
|
||||
const std::uint8_t counter_value;
|
||||
|
||||
// Set when FEC is impossible. Always 0.
|
||||
const TsHeaderField transport_error_indicator = TsHeaderField(0, 1, 1, 7);
|
||||
|
||||
// This MPEG2 TS header is the start of a new payload (aka PES packet).
|
||||
const TsHeaderField payload_unit_start_indicator =
|
||||
TsHeaderField(is_payload_start ? 1 : 0, 1, 1, 6);
|
||||
|
||||
// Set when the current packet has a higher priority than other packets with
|
||||
// the same PID. Always 0 for VPX.
|
||||
const TsHeaderField transport_priority = TsHeaderField(0, 1, 1, 5);
|
||||
|
||||
// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet_Identifier_.28PID.29
|
||||
// 0x0020-0x1FFA May be assigned as needed to Program Map Tables, elementary
|
||||
// streams and other data tables.
|
||||
// Note: Though we hard code to 0x20, this value is actually 13 bits-- the
|
||||
// buffer for the header is always set to 0, so it doesn't matter in practice.
|
||||
const TsHeaderField pid = TsHeaderField(0x20, 8, 2, 0);
|
||||
|
||||
// Indicates scrambling key. Unused; always 0.
|
||||
const TsHeaderField scrambling_control = TsHeaderField(0, 2, 3, 6);
|
||||
|
||||
// Adaptation field flag. Unused; always 0.
|
||||
// TODO(tomfinegan): Not sure this is OK. Might need to add support for
|
||||
// writing the Adaptation Field.
|
||||
const TsHeaderField adaptation_field_flag =
|
||||
TsHeaderField(has_adaptation ? 1 : 0, 1, 3, 5);
|
||||
|
||||
// Payload flag. All output packets created here have payloads. Always 1.
|
||||
const TsHeaderField payload_flag = TsHeaderField(1, 1, 3, 4);
|
||||
|
||||
// Continuity counter. Two bit field that is incremented for every packet.
|
||||
const TsHeaderField continuity_counter =
|
||||
TsHeaderField(counter_value, 4, 3, 3);
|
||||
};
|
||||
|
||||
void TsHeader::Write(PacketDataBuffer* buffer) const {
|
||||
std::uint8_t* byte = &(*buffer)[0];
|
||||
*byte = sync_byte;
|
||||
|
||||
*++byte = 0;
|
||||
*byte |= transport_error_indicator.bits << transport_error_indicator.shift;
|
||||
*byte |= payload_unit_start_indicator.bits
|
||||
<< payload_unit_start_indicator.shift;
|
||||
*byte |= transport_priority.bits << transport_priority.shift;
|
||||
|
||||
*++byte = pid.bits & 0xff;
|
||||
|
||||
*++byte = 0;
|
||||
*byte |= scrambling_control.bits << scrambling_control.shift;
|
||||
*byte |= adaptation_field_flag.bits << adaptation_field_flag.shift;
|
||||
*byte |= payload_flag.bits << payload_flag.shift;
|
||||
*byte |= continuity_counter.bits; // last 4 bits.
|
||||
}
|
||||
|
||||
bool VpxPes2Ts::ConvertToFile() {
|
||||
output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
|
||||
if (output_file_ == nullptr) {
|
||||
std::fprintf(stderr, "VpxPes2Ts: Cannot open %s for output.\n",
|
||||
output_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
pes_converter_.reset(new Webm2Pes(input_file_name_, this));
|
||||
if (pes_converter_ == nullptr) {
|
||||
std::fprintf(stderr, "VpxPes2Ts: Out of memory.\n");
|
||||
return false;
|
||||
}
|
||||
return pes_converter_->ConvertToPacketReceiver();
|
||||
}
|
||||
|
||||
bool VpxPes2Ts::ReceivePacket(const PacketDataBuffer& packet_data) {
|
||||
const int kTsHeaderSize = 4;
|
||||
const int kTsPayloadSize = 184;
|
||||
const int kTsPacketSize = kTsHeaderSize + kTsPayloadSize;
|
||||
int bytes_to_packetize = static_cast<int>(packet_data.size());
|
||||
std::uint8_t continuity_counter = 0;
|
||||
std::size_t read_pos = 0;
|
||||
|
||||
ts_buffer_.reserve(kTsPacketSize);
|
||||
|
||||
while (bytes_to_packetize > 0) {
|
||||
if (continuity_counter > 0xf)
|
||||
continuity_counter = 0;
|
||||
|
||||
// Calculate payload size (need to know if we'll have to pad with an empty
|
||||
// adaptation field).
|
||||
int payload_size = std::min(bytes_to_packetize, kTsPayloadSize);
|
||||
|
||||
// Write the TS header.
|
||||
const TsHeader header(
|
||||
bytes_to_packetize == static_cast<int>(packet_data.size()),
|
||||
payload_size != kTsPayloadSize, continuity_counter);
|
||||
header.Write(&ts_buffer_);
|
||||
int write_pos = kTsHeaderSize;
|
||||
|
||||
// (pre)Pad payload with an empty adaptation field. All packets must be
|
||||
// |kTsPacketSize| (188).
|
||||
if (payload_size < kTsPayloadSize) {
|
||||
// We need at least 2 bytes to write an empty adaptation field.
|
||||
if (payload_size == (kTsPayloadSize - 1)) {
|
||||
payload_size--;
|
||||
}
|
||||
|
||||
// Padding adaptation field:
|
||||
// 8 bits: number of adaptation field bytes following this byte.
|
||||
// 8 bits: unused (in this program) flags.
|
||||
// This is followed by a run of 0xff to reach |kTsPayloadSize| (184)
|
||||
// bytes.
|
||||
const int pad_size = kTsPayloadSize - payload_size - 1 - 1;
|
||||
ts_buffer_[write_pos++] = pad_size + 1;
|
||||
ts_buffer_[write_pos++] = 0;
|
||||
|
||||
const std::uint8_t kStuffingByte = 0xff;
|
||||
for (int i = 0; i < pad_size; ++i) {
|
||||
ts_buffer_[write_pos++] = kStuffingByte;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < payload_size; ++i) {
|
||||
ts_buffer_[write_pos++] = packet_data[read_pos++];
|
||||
}
|
||||
|
||||
bytes_to_packetize -= payload_size;
|
||||
continuity_counter++;
|
||||
|
||||
if (write_pos != kTsPacketSize) {
|
||||
fprintf(stderr, "VpxPes2Ts: Invalid packet length.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write contents of |ts_buffer_| to |output_file_|.
|
||||
// TODO(tomfinegan): Writing 188 bytes at a time isn't exactly efficient...
|
||||
// Fix me.
|
||||
if (static_cast<int>(std::fwrite(&ts_buffer_[0], 1, kTsPacketSize,
|
||||
output_file_.get())) != kTsPacketSize) {
|
||||
std::fprintf(stderr, "VpxPes2Ts: TS packet write failed.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_M2TS_VPXPES2TS_H_
|
||||
#define LIBWEBM_M2TS_VPXPES2TS_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/libwebm_util.h"
|
||||
#include "m2ts/webm2pes.h"
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
class VpxPes2Ts : public PacketReceiverInterface {
|
||||
public:
|
||||
VpxPes2Ts(const std::string& input_file_name,
|
||||
const std::string& output_file_name)
|
||||
: input_file_name_(input_file_name),
|
||||
output_file_name_(output_file_name) {}
|
||||
virtual ~VpxPes2Ts() = default;
|
||||
VpxPes2Ts() = delete;
|
||||
VpxPes2Ts(const VpxPes2Ts&) = delete;
|
||||
VpxPes2Ts(VpxPes2Ts&&) = delete;
|
||||
|
||||
bool ConvertToFile();
|
||||
|
||||
private:
|
||||
bool ReceivePacket(const PacketDataBuffer& packet_data) override;
|
||||
|
||||
const std::string input_file_name_;
|
||||
const std::string output_file_name_;
|
||||
|
||||
FilePtr output_file_;
|
||||
std::unique_ptr<Webm2Pes> pes_converter_;
|
||||
PacketDataBuffer ts_buffer_;
|
||||
};
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_M2TS_VPXPES2TS_H_
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "m2ts/vpxpes2ts.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
void Usage(const char* argv[]) {
|
||||
printf("Usage: %s <WebM file> <output file>", argv[0]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc < 3) {
|
||||
Usage(argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const std::string input_path = argv[1];
|
||||
const std::string output_path = argv[2];
|
||||
|
||||
libwebm::VpxPes2Ts converter(input_path, output_path);
|
||||
return converter.ConvertToFile() == true ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "vpxpes_parser.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/file_util.h"
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
VpxPesParser::BcmvHeader::BcmvHeader(std::uint32_t len) : length(len) {
|
||||
id[0] = 'B';
|
||||
id[1] = 'C';
|
||||
id[2] = 'M';
|
||||
id[3] = 'V';
|
||||
}
|
||||
|
||||
bool VpxPesParser::BcmvHeader::operator==(const BcmvHeader& other) const {
|
||||
return (other.length == length && other.id[0] == id[0] &&
|
||||
other.id[1] == id[1] && other.id[2] == id[2] && other.id[3] == id[3]);
|
||||
}
|
||||
|
||||
bool VpxPesParser::BcmvHeader::Valid() const {
|
||||
return (length > 0 && id[0] == 'B' && id[1] == 'C' && id[2] == 'M' &&
|
||||
id[3] == 'V');
|
||||
}
|
||||
|
||||
// TODO(tomfinegan): Break Open() into separate functions. One that opens the
|
||||
// file, and one that reads one packet at a time. As things are files larger
|
||||
// than the maximum availble memory for the current process cannot be loaded.
|
||||
bool VpxPesParser::Open(const std::string& pes_file) {
|
||||
pes_file_size_ = static_cast<size_t>(libwebm::GetFileSize(pes_file));
|
||||
if (pes_file_size_ <= 0)
|
||||
return false;
|
||||
pes_file_data_.reserve(static_cast<size_t>(pes_file_size_));
|
||||
libwebm::FilePtr file = libwebm::FilePtr(std::fopen(pes_file.c_str(), "rb"),
|
||||
libwebm::FILEDeleter());
|
||||
int byte;
|
||||
while ((byte = fgetc(file.get())) != EOF) {
|
||||
pes_file_data_.push_back(static_cast<std::uint8_t>(byte));
|
||||
}
|
||||
|
||||
if (!feof(file.get()) || ferror(file.get()) ||
|
||||
pes_file_size_ != pes_file_data_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read_pos_ = 0;
|
||||
parse_state_ = kFindStartCode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VpxPesParser::VerifyPacketStartCode() const {
|
||||
if (read_pos_ + 2 > pes_file_data_.size())
|
||||
return false;
|
||||
|
||||
// PES packets all start with the byte sequence 0x0 0x0 0x1.
|
||||
if (pes_file_data_[read_pos_] != 0 || pes_file_data_[read_pos_ + 1] != 0 ||
|
||||
pes_file_data_[read_pos_ + 2] != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VpxPesParser::ReadStreamId(std::uint8_t* stream_id) const {
|
||||
if (!stream_id || BytesAvailable() < 4)
|
||||
return false;
|
||||
|
||||
*stream_id = pes_file_data_[read_pos_ + 3];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VpxPesParser::ReadPacketLength(std::uint16_t* packet_length) const {
|
||||
if (!packet_length || BytesAvailable() < 6)
|
||||
return false;
|
||||
|
||||
// Read and byte swap 16 bit big endian length.
|
||||
*packet_length =
|
||||
(pes_file_data_[read_pos_ + 4] << 8) | pes_file_data_[read_pos_ + 5];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VpxPesParser::ParsePesHeader(PesHeader* header) {
|
||||
if (!header || parse_state_ != kParsePesHeader)
|
||||
return false;
|
||||
|
||||
if (!VerifyPacketStartCode())
|
||||
return false;
|
||||
|
||||
std::size_t pos = read_pos_;
|
||||
for (auto& a : header->start_code) {
|
||||
a = pes_file_data_[pos++];
|
||||
}
|
||||
|
||||
// PES Video stream IDs start at E0.
|
||||
if (!ReadStreamId(&header->stream_id))
|
||||
return false;
|
||||
|
||||
if (header->stream_id < kMinVideoStreamId ||
|
||||
header->stream_id > kMaxVideoStreamId)
|
||||
return false;
|
||||
|
||||
if (!ReadPacketLength(&header->packet_length))
|
||||
return false;
|
||||
|
||||
read_pos_ += kPesHeaderSize;
|
||||
parse_state_ = kParsePesOptionalHeader;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(tomfinegan): Make these masks constants.
|
||||
bool VpxPesParser::ParsePesOptionalHeader(PesOptionalHeader* header) {
|
||||
if (!header || parse_state_ != kParsePesOptionalHeader ||
|
||||
read_pos_ >= pes_file_size_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t consumed = 0;
|
||||
PacketData poh_buffer;
|
||||
if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
|
||||
kPesOptionalHeaderSize,
|
||||
&poh_buffer, &consumed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t offset = 0;
|
||||
header->marker = (poh_buffer[offset] & 0x80) >> 6;
|
||||
header->scrambling = (poh_buffer[offset] & 0x30) >> 4;
|
||||
header->priority = (poh_buffer[offset] & 0x8) >> 3;
|
||||
header->data_alignment = (poh_buffer[offset] & 0xc) >> 2;
|
||||
header->copyright = (poh_buffer[offset] & 0x2) >> 1;
|
||||
header->original = poh_buffer[offset] & 0x1;
|
||||
offset++;
|
||||
|
||||
header->has_pts = (poh_buffer[offset] & 0x80) >> 7;
|
||||
header->has_dts = (poh_buffer[offset] & 0x40) >> 6;
|
||||
header->unused_fields = poh_buffer[offset] & 0x3f;
|
||||
offset++;
|
||||
|
||||
header->remaining_size = poh_buffer[offset];
|
||||
if (header->remaining_size !=
|
||||
static_cast<int>(kWebm2PesOptHeaderRemainingSize))
|
||||
return false;
|
||||
|
||||
size_t bytes_left = header->remaining_size;
|
||||
offset++;
|
||||
|
||||
if (header->has_pts) {
|
||||
// Read PTS markers. Format:
|
||||
// PTS: 5 bytes
|
||||
// 4 bits (flag: PTS present, but no DTS): 0x2 ('0010')
|
||||
// 36 bits (90khz PTS):
|
||||
// top 3 bits
|
||||
// marker ('1')
|
||||
// middle 15 bits
|
||||
// marker ('1')
|
||||
// bottom 15 bits
|
||||
// marker ('1')
|
||||
// TODO(tomfinegan): read/store the timestamp.
|
||||
header->pts_dts_flag = (poh_buffer[offset] & 0x20) >> 4;
|
||||
// Check the marker bits.
|
||||
if ((poh_buffer[offset + 0] & 1) != 1 ||
|
||||
(poh_buffer[offset + 2] & 1) != 1 ||
|
||||
(poh_buffer[offset + 4] & 1) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
header->pts = (poh_buffer[offset] & 0xe) << 29 |
|
||||
((ReadUint16(&poh_buffer[offset + 1]) & ~1) << 14) |
|
||||
(ReadUint16(&poh_buffer[offset + 3]) >> 1);
|
||||
offset += 5;
|
||||
bytes_left -= 5;
|
||||
}
|
||||
|
||||
// Validate stuffing byte(s).
|
||||
for (size_t i = 0; i < bytes_left; ++i) {
|
||||
if (poh_buffer[offset + i] != 0xff)
|
||||
return false;
|
||||
}
|
||||
|
||||
read_pos_ += consumed;
|
||||
parse_state_ = kParseBcmvHeader;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses and validates a BCMV header.
|
||||
bool VpxPesParser::ParseBcmvHeader(BcmvHeader* header) {
|
||||
if (!header || parse_state_ != kParseBcmvHeader)
|
||||
return false;
|
||||
|
||||
PacketData bcmv_buffer;
|
||||
std::size_t consumed = 0;
|
||||
if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
|
||||
kBcmvHeaderSize, &bcmv_buffer,
|
||||
&consumed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t offset = 0;
|
||||
header->id[0] = bcmv_buffer[offset++];
|
||||
header->id[1] = bcmv_buffer[offset++];
|
||||
header->id[2] = bcmv_buffer[offset++];
|
||||
header->id[3] = bcmv_buffer[offset++];
|
||||
|
||||
header->length = 0;
|
||||
header->length |= bcmv_buffer[offset++] << 24;
|
||||
header->length |= bcmv_buffer[offset++] << 16;
|
||||
header->length |= bcmv_buffer[offset++] << 8;
|
||||
header->length |= bcmv_buffer[offset++];
|
||||
|
||||
// Length stored in the BCMV header is followed by 2 bytes of 0 padding.
|
||||
if (bcmv_buffer[offset++] != 0 || bcmv_buffer[offset++] != 0)
|
||||
return false;
|
||||
|
||||
if (!header->Valid())
|
||||
return false;
|
||||
|
||||
parse_state_ = kFindStartCode;
|
||||
read_pos_ += consumed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VpxPesParser::FindStartCode(std::size_t origin,
|
||||
std::size_t* offset) const {
|
||||
if (read_pos_ + 2 >= pes_file_size_)
|
||||
return false;
|
||||
|
||||
const std::size_t length = pes_file_size_ - origin;
|
||||
if (length < 3)
|
||||
return false;
|
||||
|
||||
const uint8_t* const data = &pes_file_data_[origin];
|
||||
for (std::size_t i = 0; i < length - 3; ++i) {
|
||||
if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
|
||||
*offset = origin + i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VpxPesParser::IsPayloadFragmented(const PesHeader& header) const {
|
||||
return (header.packet_length != 0 &&
|
||||
(header.packet_length - kPesOptionalHeaderSize) !=
|
||||
header.bcmv_header.length);
|
||||
}
|
||||
|
||||
bool VpxPesParser::AccumulateFragmentedPayload(std::size_t pes_packet_length,
|
||||
std::size_t payload_length) {
|
||||
const std::size_t first_fragment_length =
|
||||
pes_packet_length - kPesOptionalHeaderSize - kBcmvHeaderSize;
|
||||
for (std::size_t i = 0; i < first_fragment_length; ++i) {
|
||||
payload_.push_back(pes_file_data_[read_pos_ + i]);
|
||||
}
|
||||
read_pos_ += first_fragment_length;
|
||||
parse_state_ = kFindStartCode;
|
||||
|
||||
while (payload_.size() < payload_length) {
|
||||
PesHeader header;
|
||||
std::size_t packet_start_pos = read_pos_;
|
||||
if (!FindStartCode(read_pos_, &packet_start_pos)) {
|
||||
return false;
|
||||
}
|
||||
parse_state_ = kParsePesHeader;
|
||||
read_pos_ = packet_start_pos;
|
||||
|
||||
if (!ParsePesHeader(&header)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParsePesOptionalHeader(&header.opt_header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t fragment_length =
|
||||
header.packet_length - kPesOptionalHeaderSize;
|
||||
std::size_t consumed = 0;
|
||||
if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
|
||||
fragment_length, &payload_,
|
||||
&consumed)) {
|
||||
return false;
|
||||
}
|
||||
read_pos_ += consumed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VpxPesParser::RemoveStartCodeEmulationPreventionBytes(
|
||||
const std::uint8_t* raw_data, std::size_t bytes_required,
|
||||
PacketData* processed_data, std::size_t* bytes_consumed) const {
|
||||
if (bytes_required == 0 || !processed_data)
|
||||
return false;
|
||||
|
||||
std::size_t num_zeros = 0;
|
||||
std::size_t bytes_copied = 0;
|
||||
const std::uint8_t* const end_of_input =
|
||||
&pes_file_data_[0] + pes_file_data_.size();
|
||||
std::size_t i;
|
||||
for (i = 0; bytes_copied < bytes_required; ++i) {
|
||||
if (raw_data + i > end_of_input)
|
||||
return false;
|
||||
|
||||
bool skip = false;
|
||||
|
||||
const std::uint8_t byte = raw_data[i];
|
||||
if (byte == 0) {
|
||||
++num_zeros;
|
||||
} else if (byte == 0x3 && num_zeros == 2) {
|
||||
skip = true;
|
||||
num_zeros = 0;
|
||||
} else {
|
||||
num_zeros = 0;
|
||||
}
|
||||
|
||||
if (skip == false) {
|
||||
processed_data->push_back(byte);
|
||||
++bytes_copied;
|
||||
}
|
||||
}
|
||||
*bytes_consumed = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
int VpxPesParser::BytesAvailable() const {
|
||||
return static_cast<int>(pes_file_data_.size() - read_pos_);
|
||||
}
|
||||
|
||||
bool VpxPesParser::ParseNextPacket(PesHeader* header, VideoFrame* frame) {
|
||||
if (!header || !frame || parse_state_ != kFindStartCode ||
|
||||
BytesAvailable() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t packet_start_pos = read_pos_;
|
||||
if (!FindStartCode(read_pos_, &packet_start_pos)) {
|
||||
return false;
|
||||
}
|
||||
parse_state_ = kParsePesHeader;
|
||||
read_pos_ = packet_start_pos;
|
||||
|
||||
if (!ParsePesHeader(header)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParsePesOptionalHeader(&header->opt_header)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseBcmvHeader(&header->bcmv_header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// BCMV header length includes the length of the BCMVHeader itself. Adjust:
|
||||
const std::size_t payload_length =
|
||||
header->bcmv_header.length - BcmvHeader::size();
|
||||
|
||||
// Make sure there's enough input data to read the entire frame.
|
||||
if (read_pos_ + payload_length > pes_file_data_.size()) {
|
||||
// Need more data.
|
||||
printf("VpxPesParser: Not enough data. Required: %u Available: %u\n",
|
||||
static_cast<unsigned int>(payload_length),
|
||||
static_cast<unsigned int>(pes_file_data_.size() - read_pos_));
|
||||
parse_state_ = kFindStartCode;
|
||||
read_pos_ = packet_start_pos;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsPayloadFragmented(*header)) {
|
||||
if (!AccumulateFragmentedPayload(header->packet_length, payload_length)) {
|
||||
fprintf(stderr, "VpxPesParser: Failed parsing fragmented payload!\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
std::size_t consumed = 0;
|
||||
if (!RemoveStartCodeEmulationPreventionBytes(
|
||||
&pes_file_data_[read_pos_], payload_length, &payload_, &consumed)) {
|
||||
return false;
|
||||
}
|
||||
read_pos_ += consumed;
|
||||
}
|
||||
|
||||
if (frame->buffer().capacity < payload_.size()) {
|
||||
if (frame->Init(payload_.size()) == false) {
|
||||
fprintf(stderr, "VpxPesParser: Out of memory.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
frame->set_nanosecond_pts(Khz90TicksToNanoseconds(header->opt_header.pts));
|
||||
std::memcpy(frame->buffer().data.get(), &payload_[0], payload_.size());
|
||||
frame->SetBufferLength(payload_.size());
|
||||
|
||||
payload_.clear();
|
||||
parse_state_ = kFindStartCode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_M2TS_VPXPES_PARSER_H_
|
||||
#define LIBWEBM_M2TS_VPXPES_PARSER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/libwebm_util.h"
|
||||
#include "common/video_frame.h"
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
// Parser for VPx PES. Requires that the _entire_ PES stream can be stored in
|
||||
// a std::vector<std::uint8_t> and read into memory when Open() is called.
|
||||
// TODO(tomfinegan): Support incremental parse.
|
||||
class VpxPesParser {
|
||||
public:
|
||||
typedef std::vector<std::uint8_t> PesFileData;
|
||||
typedef std::vector<std::uint8_t> PacketData;
|
||||
|
||||
enum ParseState {
|
||||
kFindStartCode,
|
||||
kParsePesHeader,
|
||||
kParsePesOptionalHeader,
|
||||
kParseBcmvHeader,
|
||||
};
|
||||
|
||||
struct PesOptionalHeader {
|
||||
int marker = 0;
|
||||
int scrambling = 0;
|
||||
int priority = 0;
|
||||
int data_alignment = 0;
|
||||
int copyright = 0;
|
||||
int original = 0;
|
||||
int has_pts = 0;
|
||||
int has_dts = 0;
|
||||
int unused_fields = 0;
|
||||
int remaining_size = 0;
|
||||
int pts_dts_flag = 0;
|
||||
std::uint64_t pts = 0;
|
||||
int stuffing_byte = 0;
|
||||
};
|
||||
|
||||
struct BcmvHeader {
|
||||
BcmvHeader() = default;
|
||||
~BcmvHeader() = default;
|
||||
BcmvHeader(const BcmvHeader&) = delete;
|
||||
BcmvHeader(BcmvHeader&&) = delete;
|
||||
|
||||
// Convenience ctor for quick validation of expected values via operator==
|
||||
// after parsing input.
|
||||
explicit BcmvHeader(std::uint32_t len);
|
||||
|
||||
bool operator==(const BcmvHeader& other) const;
|
||||
|
||||
void Reset();
|
||||
bool Valid() const;
|
||||
static std::size_t size() { return 10; }
|
||||
|
||||
char id[4] = {0};
|
||||
std::uint32_t length = 0;
|
||||
};
|
||||
|
||||
struct PesHeader {
|
||||
std::uint8_t start_code[4] = {0};
|
||||
std::uint16_t packet_length = 0;
|
||||
std::uint8_t stream_id = 0;
|
||||
PesOptionalHeader opt_header;
|
||||
BcmvHeader bcmv_header;
|
||||
};
|
||||
|
||||
// Constants for validating known values from input data.
|
||||
const std::uint8_t kMinVideoStreamId = 0xE0;
|
||||
const std::uint8_t kMaxVideoStreamId = 0xEF;
|
||||
const std::size_t kPesHeaderSize = 6;
|
||||
const std::size_t kPesOptionalHeaderStartOffset = kPesHeaderSize;
|
||||
const std::size_t kPesOptionalHeaderSize = 9;
|
||||
const std::size_t kPesOptionalHeaderMarkerValue = 0x2;
|
||||
const std::size_t kWebm2PesOptHeaderRemainingSize = 6;
|
||||
const std::size_t kBcmvHeaderSize = 10;
|
||||
|
||||
VpxPesParser() = default;
|
||||
~VpxPesParser() = default;
|
||||
|
||||
// Opens file specified by |pes_file_path| and reads its contents. Returns
|
||||
// true after successful read of input file.
|
||||
bool Open(const std::string& pes_file_path);
|
||||
|
||||
// Parses the next packet in the PES. PES header information is stored in
|
||||
// |header|, and the frame payload is stored in |frame|. Returns true when
|
||||
// a full frame has been consumed from the PES.
|
||||
bool ParseNextPacket(PesHeader* header, VideoFrame* frame);
|
||||
|
||||
// PES Header parsing utility functions.
|
||||
// PES Header structure:
|
||||
// Start code Stream ID Packet length (16 bits)
|
||||
// / / ____/
|
||||
// | | /
|
||||
// Byte0 Byte1 Byte2 Byte3 Byte4 Byte5
|
||||
// 0 0 1 X Y
|
||||
bool VerifyPacketStartCode() const;
|
||||
bool ReadStreamId(std::uint8_t* stream_id) const;
|
||||
bool ReadPacketLength(std::uint16_t* packet_length) const;
|
||||
|
||||
std::uint64_t pes_file_size() const { return pes_file_size_; }
|
||||
const PesFileData& pes_file_data() const { return pes_file_data_; }
|
||||
|
||||
// Returns number of unparsed bytes remaining.
|
||||
int BytesAvailable() const;
|
||||
|
||||
private:
|
||||
// Parses and verifies the static 6 byte portion that begins every PES packet.
|
||||
bool ParsePesHeader(PesHeader* header);
|
||||
|
||||
// Parses a PES optional header, the optional header following the static
|
||||
// header that begins the VPX PES packet.
|
||||
// https://en.wikipedia.org/wiki/Packetized_elementary_stream
|
||||
bool ParsePesOptionalHeader(PesOptionalHeader* header);
|
||||
|
||||
// Parses and validates the BCMV header. This immediately follows the optional
|
||||
// header.
|
||||
bool ParseBcmvHeader(BcmvHeader* header);
|
||||
|
||||
// Returns true when a start code is found and sets |offset| to the position
|
||||
// of the start code relative to |pes_file_data_[read_pos_]|.
|
||||
// Does not set |offset| value if the end of |pes_file_data_| is reached
|
||||
// without locating a start code.
|
||||
// Note: A start code is the byte sequence 0x00 0x00 0x01.
|
||||
bool FindStartCode(std::size_t origin, std::size_t* offset) const;
|
||||
|
||||
// Returns true when a PES packet containing a BCMV header contains only a
|
||||
// portion of the frame payload length reported by the BCMV header.
|
||||
bool IsPayloadFragmented(const PesHeader& header) const;
|
||||
|
||||
// Parses PES and PES Optional header while accumulating payload data in
|
||||
// |payload_|.
|
||||
// Returns true once all payload fragments have been stored in |payload_|.
|
||||
// Returns false if unable to accumulate full payload.
|
||||
bool AccumulateFragmentedPayload(std::size_t pes_packet_length,
|
||||
std::size_t payload_length);
|
||||
|
||||
// The byte sequence 0x0 0x0 0x1 is a start code in PES. When PES muxers
|
||||
// encounter 0x0 0x0 0x1 or 0x0 0x0 0x3, an additional 0x3 is inserted into
|
||||
// the PES. The following change occurs:
|
||||
// 0x0 0x0 0x1 => 0x0 0x0 0x3 0x1
|
||||
// 0x0 0x0 0x3 => 0x0 0x0 0x3 0x3
|
||||
// PES demuxers must reverse the change:
|
||||
// 0x0 0x0 0x3 0x1 => 0x0 0x0 0x1
|
||||
// 0x0 0x0 0x3 0x3 => 0x0 0x0 0x3
|
||||
// PES optional header, BCMV header, and payload data must be preprocessed to
|
||||
// avoid potentially invalid data due to the presence of inserted bytes.
|
||||
//
|
||||
// Removes start code emulation prevention bytes while copying data from
|
||||
// |raw_data| to |processed_data|. Returns true when |bytes_required| bytes
|
||||
// have been written to |processed_data|. Reports bytes consumed during the
|
||||
// operation via |bytes_consumed|.
|
||||
bool RemoveStartCodeEmulationPreventionBytes(
|
||||
const std::uint8_t* raw_data, std::size_t bytes_required,
|
||||
PacketData* processed_data, std::size_t* bytes_consumed) const;
|
||||
|
||||
std::size_t pes_file_size_ = 0;
|
||||
PacketData payload_;
|
||||
PesFileData pes_file_data_;
|
||||
std::size_t read_pos_ = 0;
|
||||
ParseState parse_state_ = kFindStartCode;
|
||||
};
|
||||
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_M2TS_VPXPES_PARSER_H_
|
||||
@@ -0,0 +1,551 @@
|
||||
// Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "m2ts/webm2pes.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <vector>
|
||||
|
||||
#include "common/libwebm_util.h"
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
const std::size_t Webm2Pes::kMaxPayloadSize = 32768;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string ToString(const char* str) {
|
||||
return std::string((str == nullptr) ? "" : str);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//
|
||||
// PesOptionalHeader methods.
|
||||
//
|
||||
|
||||
void PesOptionalHeader::SetPtsBits(std::int64_t pts_90khz) {
|
||||
std::uint64_t* pts_bits = &pts.bits;
|
||||
*pts_bits = 0;
|
||||
|
||||
// PTS is broken up and stored in 40 bits as shown:
|
||||
//
|
||||
// PES PTS Only flag
|
||||
// / Marker Marker Marker
|
||||
// | / / /
|
||||
// | | | |
|
||||
// 7654 321 0 765432107654321 0 765432107654321 0
|
||||
// 0010 PTS 32-30 1 PTS 29-15 1 PTS 14-0 1
|
||||
const std::uint32_t pts1 = (pts_90khz >> 30) & 0x7;
|
||||
const std::uint32_t pts2 = (pts_90khz >> 15) & 0x7FFF;
|
||||
const std::uint32_t pts3 = pts_90khz & 0x7FFF;
|
||||
|
||||
std::uint8_t buffer[5] = {0};
|
||||
// PTS only flag.
|
||||
buffer[0] |= 1 << 5;
|
||||
// Top 3 bits of PTS and 1 bit marker.
|
||||
buffer[0] |= pts1 << 1;
|
||||
// Marker.
|
||||
buffer[0] |= 1;
|
||||
|
||||
// Next 15 bits of pts and 1 bit marker.
|
||||
// Top 8 bits of second PTS chunk.
|
||||
buffer[1] |= (pts2 >> 7) & 0xff;
|
||||
// bottom 7 bits of second PTS chunk.
|
||||
buffer[2] |= (pts2 << 1);
|
||||
// Marker.
|
||||
buffer[2] |= 1;
|
||||
|
||||
// Last 15 bits of pts and 1 bit marker.
|
||||
// Top 8 bits of second PTS chunk.
|
||||
buffer[3] |= (pts3 >> 7) & 0xff;
|
||||
// bottom 7 bits of second PTS chunk.
|
||||
buffer[4] |= (pts3 << 1);
|
||||
// Marker.
|
||||
buffer[4] |= 1;
|
||||
|
||||
// Write bits into PesHeaderField.
|
||||
std::memcpy(reinterpret_cast<std::uint8_t*>(pts_bits), buffer, 5);
|
||||
}
|
||||
|
||||
// Writes fields to |buffer| and returns true. Returns false when write or
|
||||
// field value validation fails.
|
||||
bool PesOptionalHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
|
||||
if (buffer == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: nullptr in opt header writer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int kHeaderSize = 9;
|
||||
std::uint8_t header[kHeaderSize] = {0};
|
||||
std::uint8_t* byte = header;
|
||||
|
||||
if (marker.Check() != true || scrambling.Check() != true ||
|
||||
priority.Check() != true || data_alignment.Check() != true ||
|
||||
copyright.Check() != true || original.Check() != true ||
|
||||
has_pts.Check() != true || has_dts.Check() != true ||
|
||||
pts.Check() != true || stuffing_byte.Check() != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: Invalid PES Optional Header field.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(tomfinegan): As noted in above, the PesHeaderFields should be an
|
||||
// array (or some data structure) that can be iterated over.
|
||||
|
||||
// First byte of header, fields: marker, scrambling, priority, alignment,
|
||||
// copyright, original.
|
||||
*byte = 0;
|
||||
*byte |= marker.bits << marker.shift;
|
||||
*byte |= scrambling.bits << scrambling.shift;
|
||||
*byte |= priority.bits << priority.shift;
|
||||
*byte |= data_alignment.bits << data_alignment.shift;
|
||||
*byte |= copyright.bits << copyright.shift;
|
||||
*byte |= original.bits << original.shift;
|
||||
|
||||
// Second byte of header, fields: has_pts, has_dts, unused fields.
|
||||
*++byte = 0;
|
||||
if (write_pts == true)
|
||||
*byte |= has_pts.bits << has_pts.shift;
|
||||
|
||||
*byte |= has_dts.bits << has_dts.shift;
|
||||
|
||||
// Third byte of header, fields: remaining size of header.
|
||||
*++byte = remaining_size.bits & 0xff; // Field is 8 bits wide.
|
||||
|
||||
int num_stuffing_bytes =
|
||||
(pts.num_bits + 7) / 8 + 1 /* always 1 stuffing byte */;
|
||||
if (write_pts == true) {
|
||||
// Write the PTS value as big endian and adjust stuffing byte count
|
||||
// accordingly.
|
||||
*++byte = pts.bits & 0xff;
|
||||
*++byte = (pts.bits >> 8) & 0xff;
|
||||
*++byte = (pts.bits >> 16) & 0xff;
|
||||
*++byte = (pts.bits >> 24) & 0xff;
|
||||
*++byte = (pts.bits >> 32) & 0xff;
|
||||
num_stuffing_bytes = 1;
|
||||
}
|
||||
|
||||
// Add the stuffing byte(s).
|
||||
for (int i = 0; i < num_stuffing_bytes; ++i)
|
||||
*++byte = stuffing_byte.bits & 0xff;
|
||||
|
||||
return CopyAndEscapeStartCodes(&header[0], kHeaderSize, buffer);
|
||||
}
|
||||
|
||||
//
|
||||
// BCMVHeader methods.
|
||||
//
|
||||
|
||||
bool BCMVHeader::Write(PacketDataBuffer* buffer) const {
|
||||
if (buffer == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: nullptr for buffer in BCMV Write.\n");
|
||||
return false;
|
||||
}
|
||||
const int kBcmvSize = 4;
|
||||
for (int i = 0; i < kBcmvSize; ++i)
|
||||
buffer->push_back(bcmv[i]);
|
||||
|
||||
// Note: The 4 byte length field must include the size of the BCMV header.
|
||||
const int kRemainingBytes = 6;
|
||||
const uint32_t bcmv_total_length = length + static_cast<uint32_t>(size());
|
||||
const uint8_t bcmv_buffer[kRemainingBytes] = {
|
||||
static_cast<std::uint8_t>((bcmv_total_length >> 24) & 0xff),
|
||||
static_cast<std::uint8_t>((bcmv_total_length >> 16) & 0xff),
|
||||
static_cast<std::uint8_t>((bcmv_total_length >> 8) & 0xff),
|
||||
static_cast<std::uint8_t>(bcmv_total_length & 0xff),
|
||||
0,
|
||||
0 /* 2 bytes 0 padding */};
|
||||
|
||||
return CopyAndEscapeStartCodes(bcmv_buffer, kRemainingBytes, buffer);
|
||||
}
|
||||
|
||||
//
|
||||
// PesHeader methods.
|
||||
//
|
||||
|
||||
// Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to write
|
||||
// |optional_header| contents. Returns true when successful, false otherwise.
|
||||
bool PesHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
|
||||
if (buffer == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: nullptr in header writer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write |start_code|.
|
||||
const int kStartCodeLength = 4;
|
||||
for (int i = 0; i < kStartCodeLength; ++i)
|
||||
buffer->push_back(start_code[i]);
|
||||
|
||||
// The length field here reports number of bytes following the field. The
|
||||
// length of the optional header must be added to the payload length set by
|
||||
// the user.
|
||||
const std::size_t header_length =
|
||||
packet_length + optional_header.size_in_bytes();
|
||||
if (header_length > UINT16_MAX)
|
||||
return false;
|
||||
|
||||
// Write |header_length| as big endian.
|
||||
std::uint8_t byte = (header_length >> 8) & 0xff;
|
||||
buffer->push_back(byte);
|
||||
byte = header_length & 0xff;
|
||||
buffer->push_back(byte);
|
||||
|
||||
// Write the (not really) optional header.
|
||||
if (optional_header.Write(write_pts, buffer) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: PES optional header write failed.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Webm2Pes methods.
|
||||
//
|
||||
|
||||
bool Webm2Pes::ConvertToFile() {
|
||||
if (input_file_name_.empty() || output_file_name_.empty()) {
|
||||
std::fprintf(stderr, "Webm2Pes: input and/or output file name(s) empty.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
|
||||
if (output_file_ == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot open %s for output.\n",
|
||||
output_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (InitWebmParser() != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walk clusters in segment.
|
||||
const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
|
||||
while (cluster != nullptr && cluster->EOS() == false) {
|
||||
const mkvparser::BlockEntry* block_entry = nullptr;
|
||||
std::int64_t block_status = cluster->GetFirst(block_entry);
|
||||
if (block_status < 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walk blocks in cluster.
|
||||
while (block_entry != nullptr && block_entry->EOS() == false) {
|
||||
const mkvparser::Block* block = block_entry->GetBlock();
|
||||
if (block->GetTrackNumber() == video_track_num_) {
|
||||
const int frame_count = block->GetFrameCount();
|
||||
|
||||
// Walk frames in block.
|
||||
for (int frame_num = 0; frame_num < frame_count; ++frame_num) {
|
||||
const mkvparser::Block::Frame& mkvparser_frame =
|
||||
block->GetFrame(frame_num);
|
||||
|
||||
// Read the frame.
|
||||
VideoFrame vpx_frame(block->GetTime(cluster), codec_);
|
||||
if (ReadVideoFrame(mkvparser_frame, &vpx_frame) == false) {
|
||||
fprintf(stderr, "Webm2Pes: frame read failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write frame out as PES packet(s).
|
||||
if (WritePesPacket(vpx_frame, &packet_data_) == false) {
|
||||
std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write contents of |packet_data_| to |output_file_|.
|
||||
if (std::fwrite(&packet_data_[0], 1, packet_data_.size(),
|
||||
output_file_.get()) != packet_data_.size()) {
|
||||
std::fprintf(stderr, "Webm2Pes: packet payload write failed.\n");
|
||||
return false;
|
||||
}
|
||||
bytes_written_ += packet_data_.size();
|
||||
}
|
||||
}
|
||||
block_status = cluster->GetNext(block_entry, block_entry);
|
||||
if (block_status < 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = webm_parser_->GetNext(cluster);
|
||||
}
|
||||
|
||||
std::fflush(output_file_.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Webm2Pes::ConvertToPacketReceiver() {
|
||||
if (input_file_name_.empty() || packet_sink_ == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: input file name empty or null sink.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (InitWebmParser() != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walk clusters in segment.
|
||||
const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
|
||||
while (cluster != nullptr && cluster->EOS() == false) {
|
||||
const mkvparser::BlockEntry* block_entry = nullptr;
|
||||
std::int64_t block_status = cluster->GetFirst(block_entry);
|
||||
if (block_status < 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walk blocks in cluster.
|
||||
while (block_entry != nullptr && block_entry->EOS() == false) {
|
||||
const mkvparser::Block* block = block_entry->GetBlock();
|
||||
if (block->GetTrackNumber() == video_track_num_) {
|
||||
const int frame_count = block->GetFrameCount();
|
||||
|
||||
// Walk frames in block.
|
||||
for (int frame_num = 0; frame_num < frame_count; ++frame_num) {
|
||||
const mkvparser::Block::Frame& mkvparser_frame =
|
||||
block->GetFrame(frame_num);
|
||||
|
||||
// Read the frame.
|
||||
VideoFrame frame(block->GetTime(cluster), codec_);
|
||||
if (ReadVideoFrame(mkvparser_frame, &frame) == false) {
|
||||
fprintf(stderr, "Webm2Pes: frame read failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write frame out as PES packet(s).
|
||||
if (WritePesPacket(frame, &packet_data_) == false) {
|
||||
std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
|
||||
return false;
|
||||
}
|
||||
if (packet_sink_->ReceivePacket(packet_data_) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: ReceivePacket failed.\n");
|
||||
return false;
|
||||
}
|
||||
bytes_written_ += packet_data_.size();
|
||||
}
|
||||
}
|
||||
block_status = cluster->GetNext(block_entry, block_entry);
|
||||
if (block_status < 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = webm_parser_->GetNext(cluster);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Webm2Pes::InitWebmParser() {
|
||||
if (webm_reader_.Open(input_file_name_.c_str()) != 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot open %s as input.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
using mkvparser::Segment;
|
||||
Segment* webm_parser = nullptr;
|
||||
if (Segment::CreateInstance(&webm_reader_, 0 /* pos */,
|
||||
webm_parser /* Segment*& */) != 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot create WebM parser.\n");
|
||||
return false;
|
||||
}
|
||||
webm_parser_.reset(webm_parser);
|
||||
|
||||
if (webm_parser_->Load() != 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Cannot parse %s.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure there's a video track.
|
||||
const mkvparser::Tracks* tracks = webm_parser_->GetTracks();
|
||||
if (tracks == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: %s has no tracks.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
timecode_scale_ = webm_parser_->GetInfo()->GetTimeCodeScale();
|
||||
|
||||
for (int track_index = 0;
|
||||
track_index < static_cast<int>(tracks->GetTracksCount());
|
||||
++track_index) {
|
||||
const mkvparser::Track* track = tracks->GetTrackByIndex(track_index);
|
||||
if (track && track->GetType() == mkvparser::Track::kVideo) {
|
||||
const std::string codec_id = ToString(track->GetCodecId());
|
||||
if (codec_id == std::string("V_VP8")) {
|
||||
codec_ = VideoFrame::kVP8;
|
||||
} else if (codec_id == std::string("V_VP9")) {
|
||||
codec_ = VideoFrame::kVP9;
|
||||
} else {
|
||||
fprintf(stderr, "Webm2Pes: Codec must be VP8 or VP9.\n");
|
||||
return false;
|
||||
}
|
||||
video_track_num_ = track_index + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (video_track_num_ < 1) {
|
||||
std::fprintf(stderr, "Webm2Pes: No video track found in %s.\n",
|
||||
input_file_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Webm2Pes::ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame,
|
||||
VideoFrame* frame) {
|
||||
if (mkvparser_frame.len < 1 || frame == nullptr)
|
||||
return false;
|
||||
|
||||
const std::size_t mkv_len = static_cast<std::size_t>(mkvparser_frame.len);
|
||||
if (mkv_len > frame->buffer().capacity) {
|
||||
const std::size_t new_size = 2 * mkv_len;
|
||||
if (frame->Init(new_size) == false) {
|
||||
std::fprintf(stderr, "Webm2Pes: Out of memory.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mkvparser_frame.Read(&webm_reader_, frame->buffer().data.get()) != 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Error reading VPx frame!\n");
|
||||
return false;
|
||||
}
|
||||
return frame->SetBufferLength(mkv_len);
|
||||
}
|
||||
|
||||
bool Webm2Pes::WritePesPacket(const VideoFrame& frame,
|
||||
PacketDataBuffer* packet_data) {
|
||||
if (frame.buffer().data.get() == nullptr || frame.buffer().length < 1)
|
||||
return false;
|
||||
|
||||
Ranges frame_ranges;
|
||||
if (frame.codec() == VideoFrame::kVP9) {
|
||||
bool error = false;
|
||||
const bool has_superframe_index =
|
||||
ParseVP9SuperFrameIndex(frame.buffer().data.get(),
|
||||
frame.buffer().length, &frame_ranges, &error);
|
||||
if (error) {
|
||||
std::fprintf(stderr, "Webm2Pes: Superframe index parse failed.\n");
|
||||
return false;
|
||||
}
|
||||
if (has_superframe_index == false) {
|
||||
frame_ranges.push_back(Range(0, frame.buffer().length));
|
||||
}
|
||||
} else {
|
||||
frame_ranges.push_back(Range(0, frame.buffer().length));
|
||||
}
|
||||
|
||||
const std::int64_t khz90_pts =
|
||||
NanosecondsTo90KhzTicks(frame.nanosecond_pts());
|
||||
PesHeader header;
|
||||
header.optional_header.SetPtsBits(khz90_pts);
|
||||
|
||||
packet_data->clear();
|
||||
|
||||
for (const Range& packet_payload_range : frame_ranges) {
|
||||
std::size_t extra_bytes = 0;
|
||||
if (packet_payload_range.length > kMaxPayloadSize) {
|
||||
extra_bytes = packet_payload_range.length - kMaxPayloadSize;
|
||||
}
|
||||
if (packet_payload_range.length + packet_payload_range.offset >
|
||||
frame.buffer().length) {
|
||||
std::fprintf(stderr, "Webm2Pes: Invalid frame length.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First packet of new frame. Always include PTS and BCMV header.
|
||||
header.packet_length =
|
||||
packet_payload_range.length - extra_bytes + BCMVHeader::size();
|
||||
if (header.Write(true, packet_data) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: packet header write failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
BCMVHeader bcmv_header(static_cast<uint32_t>(packet_payload_range.length));
|
||||
if (bcmv_header.Write(packet_data) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert the payload at the end of |packet_data|.
|
||||
const std::uint8_t* const payload_start =
|
||||
frame.buffer().data.get() + packet_payload_range.offset;
|
||||
|
||||
const std::size_t bytes_to_copy = packet_payload_range.length - extra_bytes;
|
||||
if (CopyAndEscapeStartCodes(payload_start, bytes_to_copy, packet_data) ==
|
||||
false) {
|
||||
fprintf(stderr, "Webm2Pes: Payload write failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t bytes_copied = bytes_to_copy;
|
||||
while (extra_bytes) {
|
||||
// Write PES packets for the remaining data, but omit the PTS and BCMV
|
||||
// header.
|
||||
const std::size_t extra_bytes_to_copy =
|
||||
std::min(kMaxPayloadSize, extra_bytes);
|
||||
extra_bytes -= extra_bytes_to_copy;
|
||||
header.packet_length = extra_bytes_to_copy;
|
||||
if (header.Write(false, packet_data) != true) {
|
||||
fprintf(stderr, "Webm2pes: fragment write failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::uint8_t* fragment_start = payload_start + bytes_copied;
|
||||
if (CopyAndEscapeStartCodes(fragment_start, extra_bytes_to_copy,
|
||||
packet_data) == false) {
|
||||
fprintf(stderr, "Webm2Pes: Payload write failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_copied += extra_bytes_to_copy;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
|
||||
std::size_t raw_input_length,
|
||||
PacketDataBuffer* packet_buffer) {
|
||||
if (raw_input == nullptr || raw_input_length < 1 || packet_buffer == nullptr)
|
||||
return false;
|
||||
|
||||
int num_zeros = 0;
|
||||
for (std::size_t i = 0; i < raw_input_length; ++i) {
|
||||
const uint8_t byte = raw_input[i];
|
||||
|
||||
if (byte == 0) {
|
||||
++num_zeros;
|
||||
} else if (num_zeros >= 2 && (byte == 0x1 || byte == 0x3)) {
|
||||
packet_buffer->push_back(0x3);
|
||||
num_zeros = 0;
|
||||
} else {
|
||||
num_zeros = 0;
|
||||
}
|
||||
|
||||
packet_buffer->push_back(byte);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace libwebm
|
||||
@@ -0,0 +1,274 @@
|
||||
// Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_M2TS_WEBM2PES_H_
|
||||
#define LIBWEBM_M2TS_WEBM2PES_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/libwebm_util.h"
|
||||
#include "common/video_frame.h"
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
|
||||
// Webm2pes
|
||||
//
|
||||
// Webm2pes consumes a WebM file containing a VP8 or VP9 video stream and
|
||||
// outputs a PES stream suitable for inclusion in a MPEG2 Transport Stream.
|
||||
//
|
||||
// In the simplest case the PES stream output by Webm2pes consists of a sequence
|
||||
// of PES packets with the following structure:
|
||||
// | PES Header w/PTS | BCMV Header | Payload (VPx frame) |
|
||||
//
|
||||
// More typically the output will look like the following due to the PES
|
||||
// payload size limitations caused by the format of the PES header.
|
||||
// The PES header contains only 2 bytes of storage for expressing payload size.
|
||||
// VPx PES streams containing fragmented packets look like this:
|
||||
//
|
||||
// | PH PTS | BCMV | Payload fragment 1 | PH | Payload fragment 2 | ...
|
||||
//
|
||||
// PH = PES Header
|
||||
// PH PTS = PES Header with PTS
|
||||
// BCMV = BCMV Header
|
||||
//
|
||||
// Note that start codes are properly escaped by Webm2pes, and start code
|
||||
// emulation prevention bytes must be stripped from the output stream before
|
||||
// it can be parsed.
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
// Stores a value and its size in bits for writing into a PES Optional Header.
|
||||
// Maximum size is 64 bits. Users may call the Check() method to perform minimal
|
||||
// validation (size > 0 and <= 64).
|
||||
struct PesHeaderField {
|
||||
PesHeaderField(std::uint64_t value, std::uint32_t size_in_bits,
|
||||
std::uint8_t byte_index, std::uint8_t bits_to_shift)
|
||||
: bits(value),
|
||||
num_bits(size_in_bits),
|
||||
index(byte_index),
|
||||
shift(bits_to_shift) {}
|
||||
PesHeaderField() = delete;
|
||||
PesHeaderField(const PesHeaderField&) = default;
|
||||
PesHeaderField(PesHeaderField&&) = default;
|
||||
~PesHeaderField() = default;
|
||||
bool Check() const {
|
||||
return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64;
|
||||
}
|
||||
|
||||
// Value to be stored in the field.
|
||||
std::uint64_t bits;
|
||||
|
||||
// Number of bits in the value.
|
||||
const int num_bits;
|
||||
|
||||
// Index into the header for the byte in which |bits| will be written.
|
||||
const std::uint8_t index;
|
||||
|
||||
// Number of bits to shift value before or'ing.
|
||||
const int shift;
|
||||
};
|
||||
|
||||
// Data is stored in buffers before being written to output files.
|
||||
typedef std::vector<std::uint8_t> PacketDataBuffer;
|
||||
|
||||
// Storage for PES Optional Header values. Fields written in order using sizes
|
||||
// specified.
|
||||
struct PesOptionalHeader {
|
||||
// TODO(tomfinegan): The fields could be in an array, which would allow the
|
||||
// code writing the optional header to iterate over the fields instead of
|
||||
// having code for dealing with each one.
|
||||
|
||||
// 2 bits (marker): 2 ('10')
|
||||
const PesHeaderField marker = PesHeaderField(2, 2, 0, 6);
|
||||
|
||||
// 2 bits (no scrambling): 0x0 ('00')
|
||||
const PesHeaderField scrambling = PesHeaderField(0, 2, 0, 4);
|
||||
|
||||
// 1 bit (priority): 0x0 ('0')
|
||||
const PesHeaderField priority = PesHeaderField(0, 1, 0, 3);
|
||||
|
||||
// TODO(tomfinegan): The BCMV header could be considered a sync word, and this
|
||||
// field should be 1 when a sync word follows the packet. Clarify.
|
||||
// 1 bit (data alignment): 0x0 ('0')
|
||||
const PesHeaderField data_alignment = PesHeaderField(0, 1, 0, 2);
|
||||
|
||||
// 1 bit (copyright): 0x0 ('0')
|
||||
const PesHeaderField copyright = PesHeaderField(0, 1, 0, 1);
|
||||
|
||||
// 1 bit (original/copy): 0x0 ('0')
|
||||
const PesHeaderField original = PesHeaderField(0, 1, 0, 0);
|
||||
|
||||
// 1 bit (has_pts): 0x1 ('1')
|
||||
const PesHeaderField has_pts = PesHeaderField(1, 1, 1, 7);
|
||||
|
||||
// 1 bit (has_dts): 0x0 ('0')
|
||||
const PesHeaderField has_dts = PesHeaderField(0, 1, 1, 6);
|
||||
|
||||
// 6 bits (unused fields): 0x0 ('000000')
|
||||
const PesHeaderField unused = PesHeaderField(0, 6, 1, 0);
|
||||
|
||||
// 8 bits (size of remaining data in the Header).
|
||||
const PesHeaderField remaining_size = PesHeaderField(6, 8, 2, 0);
|
||||
|
||||
// PTS: 5 bytes
|
||||
// 4 bits (flag: PTS present, but no DTS): 0x2 ('0010')
|
||||
// 36 bits (90khz PTS):
|
||||
// top 3 bits
|
||||
// marker ('1')
|
||||
// middle 15 bits
|
||||
// marker ('1')
|
||||
// bottom 15 bits
|
||||
// marker ('1')
|
||||
PesHeaderField pts = PesHeaderField(0, 40, 3, 0);
|
||||
|
||||
PesHeaderField stuffing_byte = PesHeaderField(0xFF, 8, 8, 0);
|
||||
|
||||
// PTS omitted in fragments. Size remains unchanged: More stuffing bytes.
|
||||
bool fragment = false;
|
||||
|
||||
static std::size_t size_in_bytes() { return 9; }
|
||||
|
||||
// Writes |pts_90khz| to |pts| per format described at its declaration above.
|
||||
void SetPtsBits(std::int64_t pts_90khz);
|
||||
|
||||
// Writes fields to |buffer| and returns true. Returns false when write or
|
||||
// field value validation fails.
|
||||
bool Write(bool write_pts, PacketDataBuffer* buffer) const;
|
||||
};
|
||||
|
||||
// Describes custom 10 byte header that immediately follows the PES Optional
|
||||
// Header in each PES packet output by Webm2Pes:
|
||||
// 4 byte 'B' 'C' 'M' 'V'
|
||||
// 4 byte big-endian length of frame
|
||||
// 2 bytes 0 padding
|
||||
struct BCMVHeader {
|
||||
explicit BCMVHeader(std::uint32_t frame_length) : length(frame_length) {}
|
||||
BCMVHeader() = delete;
|
||||
BCMVHeader(const BCMVHeader&) = delete;
|
||||
BCMVHeader(BCMVHeader&&) = delete;
|
||||
~BCMVHeader() = default;
|
||||
const std::uint8_t bcmv[4] = {'B', 'C', 'M', 'V'};
|
||||
const std::uint32_t length;
|
||||
|
||||
static std::size_t size() { return 10; }
|
||||
|
||||
// Write the BCMV Header into |buffer|. Caller responsible for ensuring
|
||||
// destination buffer is of size >= BCMVHeader::size().
|
||||
bool Write(PacketDataBuffer* buffer) const;
|
||||
bool Write(uint8_t* buffer);
|
||||
};
|
||||
|
||||
struct PesHeader {
|
||||
const std::uint8_t start_code[4] = {
|
||||
0x00, 0x00,
|
||||
0x01, // 0x000001 is the PES packet start code prefix.
|
||||
0xE0}; // 0xE0 is the minimum video stream ID.
|
||||
std::uint16_t packet_length = 0; // Number of bytes _after_ this field.
|
||||
PesOptionalHeader optional_header;
|
||||
std::size_t size() const {
|
||||
return optional_header.size_in_bytes() +
|
||||
6 /* start_code + packet_length */ + packet_length;
|
||||
}
|
||||
|
||||
// Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to
|
||||
// write |optional_header| contents. Returns true when successful, false
|
||||
// otherwise.
|
||||
bool Write(bool write_pts, PacketDataBuffer* buffer) const;
|
||||
};
|
||||
|
||||
class PacketReceiverInterface {
|
||||
public:
|
||||
virtual ~PacketReceiverInterface() {}
|
||||
virtual bool ReceivePacket(const PacketDataBuffer& packet) = 0;
|
||||
};
|
||||
|
||||
// Converts the VP9 track of a WebM file to a Packetized Elementary Stream
|
||||
// suitable for use in a MPEG2TS.
|
||||
// https://en.wikipedia.org/wiki/Packetized_elementary_stream
|
||||
// https://en.wikipedia.org/wiki/MPEG_transport_stream
|
||||
class Webm2Pes {
|
||||
public:
|
||||
static const std::size_t kMaxPayloadSize;
|
||||
|
||||
Webm2Pes(const std::string& input_file, const std::string& output_file)
|
||||
: input_file_name_(input_file), output_file_name_(output_file) {}
|
||||
Webm2Pes(const std::string& input_file, PacketReceiverInterface* packet_sink)
|
||||
: input_file_name_(input_file), packet_sink_(packet_sink) {}
|
||||
|
||||
Webm2Pes() = delete;
|
||||
Webm2Pes(const Webm2Pes&) = delete;
|
||||
Webm2Pes(Webm2Pes&&) = delete;
|
||||
~Webm2Pes() = default;
|
||||
|
||||
// Converts the VPx video stream to a PES file and returns true. Returns false
|
||||
// to report failure.
|
||||
bool ConvertToFile();
|
||||
|
||||
// Converts the VPx video stream to a sequence of PES packets, and calls the
|
||||
// PacketReceiverInterface::ReceivePacket() once for each VPx frame. The
|
||||
// packet sent to the receiver may contain multiple PES packets. Returns only
|
||||
// after full conversion or error. Returns true for success, and false when
|
||||
// an error occurs.
|
||||
bool ConvertToPacketReceiver();
|
||||
|
||||
// Writes |vpx_frame| out as PES packet[s] and stores output in |packet_data|.
|
||||
// Returns true for success, false for failure.
|
||||
static bool WritePesPacket(const VideoFrame& frame,
|
||||
PacketDataBuffer* packet_data);
|
||||
|
||||
uint64_t bytes_written() const { return bytes_written_; }
|
||||
|
||||
private:
|
||||
bool InitWebmParser();
|
||||
bool ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame,
|
||||
VideoFrame* frame);
|
||||
|
||||
const std::string input_file_name_;
|
||||
const std::string output_file_name_;
|
||||
std::unique_ptr<mkvparser::Segment> webm_parser_;
|
||||
mkvparser::MkvReader webm_reader_;
|
||||
FilePtr output_file_;
|
||||
|
||||
// Video track num in the WebM file.
|
||||
int video_track_num_ = 0;
|
||||
|
||||
// Video codec reported by CodecName from Video TrackEntry.
|
||||
VideoFrame::Codec codec_;
|
||||
|
||||
// Input timecode scale.
|
||||
std::int64_t timecode_scale_ = 1000000;
|
||||
|
||||
// Packet sink; when constructed with a PacketReceiverInterface*, packet and
|
||||
// type of packet are sent to |packet_sink_| instead of written to an output
|
||||
// file.
|
||||
PacketReceiverInterface* packet_sink_ = nullptr;
|
||||
|
||||
PacketDataBuffer packet_data_;
|
||||
|
||||
std::uint64_t bytes_written_ = 0;
|
||||
};
|
||||
|
||||
// Copies |raw_input_length| bytes from |raw_input| to |packet_buffer| while
|
||||
// escaping start codes. Returns true when bytes are successfully copied.
|
||||
// A start code is the 3 byte sequence 0x00 0x00 0x01. When
|
||||
// the sequence is encountered, the value 0x03 is inserted. To avoid
|
||||
// any ambiguity at reassembly time, the same is done for the sequence
|
||||
// 0x00 0x00 0x03. So, the following transformation occurs for when either
|
||||
// of the noted sequences is encountered:
|
||||
//
|
||||
// 0x00 0x00 0x01 => 0x00 0x00 0x03 0x01
|
||||
// 0x00 0x00 0x03 => 0x00 0x00 0x03 0x03
|
||||
bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
|
||||
std::size_t raw_input_length,
|
||||
PacketDataBuffer* packet_buffer);
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_M2TS_WEBM2PES_H_
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include "m2ts/webm2pes.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
void Usage(const char* argv[]) {
|
||||
printf("Usage: %s <WebM file> <output file>", argv[0]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc < 3) {
|
||||
Usage(argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const std::string input_path = argv[1];
|
||||
const std::string output_path = argv[2];
|
||||
|
||||
libwebm::Webm2Pes converter(input_path, output_path);
|
||||
return converter.ConvertToFile() == true ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_MKVMUXER_HPP_
|
||||
#define LIBWEBM_MKVMUXER_HPP_
|
||||
|
||||
// This file is a wrapper for the file included immediately after this comment.
|
||||
// New projects should not include this file: include the file included below.
|
||||
#include "mkvmuxer/mkvmuxer.h"
|
||||
|
||||
#endif // LIBWEBM_MKVMUXER_HPP_
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVMUXER_MKVMUXERTYPES_H_
|
||||
#define MKVMUXER_MKVMUXERTYPES_H_
|
||||
|
||||
namespace mkvmuxer {
|
||||
typedef unsigned char uint8;
|
||||
typedef short int16;
|
||||
typedef int int32;
|
||||
typedef unsigned int uint32;
|
||||
typedef long long int64;
|
||||
typedef unsigned long long uint64;
|
||||
} // namespace mkvmuxer
|
||||
|
||||
// Copied from Chromium basictypes.h
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
#endif // MKVMUXER_MKVMUXERTYPES_HPP_
|
||||
@@ -0,0 +1,743 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvmuxer/mkvmuxerutil.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <new>
|
||||
|
||||
#include "common/webmids.h"
|
||||
#include "mkvmuxer/mkvmuxer.h"
|
||||
#include "mkvmuxer/mkvwriter.h"
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
namespace {
|
||||
|
||||
// Date elements are always 8 octets in size.
|
||||
const int kDateElementSize = 8;
|
||||
|
||||
uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode,
|
||||
uint64 timecode_scale) {
|
||||
uint64 block_additional_elem_size = 0;
|
||||
uint64 block_addid_elem_size = 0;
|
||||
uint64 block_more_payload_size = 0;
|
||||
uint64 block_more_elem_size = 0;
|
||||
uint64 block_additions_payload_size = 0;
|
||||
uint64 block_additions_elem_size = 0;
|
||||
if (frame->additional()) {
|
||||
block_additional_elem_size =
|
||||
EbmlElementSize(libwebm::kMkvBlockAdditional, frame->additional(),
|
||||
frame->additional_length());
|
||||
block_addid_elem_size = EbmlElementSize(
|
||||
libwebm::kMkvBlockAddID, static_cast<uint64>(frame->add_id()));
|
||||
|
||||
block_more_payload_size =
|
||||
block_addid_elem_size + block_additional_elem_size;
|
||||
block_more_elem_size =
|
||||
EbmlMasterElementSize(libwebm::kMkvBlockMore, block_more_payload_size) +
|
||||
block_more_payload_size;
|
||||
block_additions_payload_size = block_more_elem_size;
|
||||
block_additions_elem_size =
|
||||
EbmlMasterElementSize(libwebm::kMkvBlockAdditions,
|
||||
block_additions_payload_size) +
|
||||
block_additions_payload_size;
|
||||
}
|
||||
|
||||
uint64 discard_padding_elem_size = 0;
|
||||
if (frame->discard_padding() != 0) {
|
||||
discard_padding_elem_size =
|
||||
EbmlElementSize(libwebm::kMkvDiscardPadding,
|
||||
static_cast<int64>(frame->discard_padding()));
|
||||
}
|
||||
|
||||
const uint64 reference_block_timestamp =
|
||||
frame->reference_block_timestamp() / timecode_scale;
|
||||
uint64 reference_block_elem_size = 0;
|
||||
if (!frame->is_key()) {
|
||||
reference_block_elem_size =
|
||||
EbmlElementSize(libwebm::kMkvReferenceBlock, reference_block_timestamp);
|
||||
}
|
||||
|
||||
const uint64 duration = frame->duration() / timecode_scale;
|
||||
uint64 block_duration_elem_size = 0;
|
||||
if (duration > 0)
|
||||
block_duration_elem_size =
|
||||
EbmlElementSize(libwebm::kMkvBlockDuration, duration);
|
||||
|
||||
const uint64 block_payload_size = 4 + frame->length();
|
||||
const uint64 block_elem_size =
|
||||
EbmlMasterElementSize(libwebm::kMkvBlock, block_payload_size) +
|
||||
block_payload_size;
|
||||
|
||||
const uint64 block_group_payload_size =
|
||||
block_elem_size + block_additions_elem_size + block_duration_elem_size +
|
||||
discard_padding_elem_size + reference_block_elem_size;
|
||||
|
||||
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockGroup,
|
||||
block_group_payload_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlock, block_payload_size))
|
||||
return 0;
|
||||
|
||||
if (WriteUInt(writer, frame->track_number()))
|
||||
return 0;
|
||||
|
||||
if (SerializeInt(writer, timecode, 2))
|
||||
return 0;
|
||||
|
||||
// For a Block, flags is always 0.
|
||||
if (SerializeInt(writer, 0, 1))
|
||||
return 0;
|
||||
|
||||
if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
|
||||
return 0;
|
||||
|
||||
if (frame->additional()) {
|
||||
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockAdditions,
|
||||
block_additions_payload_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockMore,
|
||||
block_more_payload_size))
|
||||
return 0;
|
||||
|
||||
if (!WriteEbmlElement(writer, libwebm::kMkvBlockAddID,
|
||||
static_cast<uint64>(frame->add_id())))
|
||||
return 0;
|
||||
|
||||
if (!WriteEbmlElement(writer, libwebm::kMkvBlockAdditional,
|
||||
frame->additional(), frame->additional_length())) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->discard_padding() != 0 &&
|
||||
!WriteEbmlElement(writer, libwebm::kMkvDiscardPadding,
|
||||
static_cast<int64>(frame->discard_padding()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frame->is_key() && !WriteEbmlElement(writer, libwebm::kMkvReferenceBlock,
|
||||
reference_block_timestamp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (duration > 0 &&
|
||||
!WriteEbmlElement(writer, libwebm::kMkvBlockDuration, duration)) {
|
||||
return false;
|
||||
}
|
||||
return EbmlMasterElementSize(libwebm::kMkvBlockGroup,
|
||||
block_group_payload_size) +
|
||||
block_group_payload_size;
|
||||
}
|
||||
|
||||
uint64 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame,
|
||||
int64 timecode) {
|
||||
if (WriteID(writer, libwebm::kMkvSimpleBlock))
|
||||
return 0;
|
||||
|
||||
const int32 size = static_cast<int32>(frame->length()) + 4;
|
||||
if (WriteUInt(writer, size))
|
||||
return 0;
|
||||
|
||||
if (WriteUInt(writer, static_cast<uint64>(frame->track_number())))
|
||||
return 0;
|
||||
|
||||
if (SerializeInt(writer, timecode, 2))
|
||||
return 0;
|
||||
|
||||
uint64 flags = 0;
|
||||
if (frame->is_key())
|
||||
flags |= 0x80;
|
||||
|
||||
if (SerializeInt(writer, flags, 1))
|
||||
return 0;
|
||||
|
||||
if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
|
||||
return 0;
|
||||
|
||||
return GetUIntSize(libwebm::kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 +
|
||||
frame->length();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int32 GetCodedUIntSize(uint64 value) {
|
||||
if (value < 0x000000000000007FULL)
|
||||
return 1;
|
||||
else if (value < 0x0000000000003FFFULL)
|
||||
return 2;
|
||||
else if (value < 0x00000000001FFFFFULL)
|
||||
return 3;
|
||||
else if (value < 0x000000000FFFFFFFULL)
|
||||
return 4;
|
||||
else if (value < 0x00000007FFFFFFFFULL)
|
||||
return 5;
|
||||
else if (value < 0x000003FFFFFFFFFFULL)
|
||||
return 6;
|
||||
else if (value < 0x0001FFFFFFFFFFFFULL)
|
||||
return 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
int32 GetUIntSize(uint64 value) {
|
||||
if (value < 0x0000000000000100ULL)
|
||||
return 1;
|
||||
else if (value < 0x0000000000010000ULL)
|
||||
return 2;
|
||||
else if (value < 0x0000000001000000ULL)
|
||||
return 3;
|
||||
else if (value < 0x0000000100000000ULL)
|
||||
return 4;
|
||||
else if (value < 0x0000010000000000ULL)
|
||||
return 5;
|
||||
else if (value < 0x0001000000000000ULL)
|
||||
return 6;
|
||||
else if (value < 0x0100000000000000ULL)
|
||||
return 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
int32 GetIntSize(int64 value) {
|
||||
// Doubling the requested value ensures positive values with their high bit
|
||||
// set are written with 0-padding to avoid flipping the signedness.
|
||||
const uint64 v = (value < 0) ? value ^ -1LL : value;
|
||||
return GetUIntSize(2 * v);
|
||||
}
|
||||
|
||||
uint64 EbmlMasterElementSize(uint64 type, uint64 value) {
|
||||
// Size of EBML ID
|
||||
int32 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += GetCodedUIntSize(value);
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, int64 value) {
|
||||
// Size of EBML ID
|
||||
int32 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += GetIntSize(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, uint64 value) {
|
||||
return EbmlElementSize(type, value, 0);
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size) {
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += (fixed_size > 0) ? fixed_size : GetUIntSize(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, float /* value */) {
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += sizeof(float);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, const char* value) {
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += strlen(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size += GetCodedUIntSize(strlen(value));
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) {
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += size;
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size += GetCodedUIntSize(size);
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlDateElementSize(uint64 type) {
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += kDateElementSize;
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size) {
|
||||
if (!writer || size < 1 || size > 8)
|
||||
return -1;
|
||||
|
||||
for (int32 i = 1; i <= size; ++i) {
|
||||
const int32 byte_count = size - i;
|
||||
const int32 bit_count = byte_count * 8;
|
||||
|
||||
const int64 bb = value >> bit_count;
|
||||
const uint8 b = static_cast<uint8>(bb);
|
||||
|
||||
const int32 status = writer->Write(&b, 1);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 SerializeFloat(IMkvWriter* writer, float f) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
assert(sizeof(uint32) == sizeof(float));
|
||||
// This union is merely used to avoid a reinterpret_cast from float& to
|
||||
// uint32& which will result in violation of strict aliasing.
|
||||
union U32 {
|
||||
uint32 u32;
|
||||
float f;
|
||||
} value;
|
||||
value.f = f;
|
||||
|
||||
for (int32 i = 1; i <= 4; ++i) {
|
||||
const int32 byte_count = 4 - i;
|
||||
const int32 bit_count = byte_count * 8;
|
||||
|
||||
const uint8 byte = static_cast<uint8>(value.u32 >> bit_count);
|
||||
|
||||
const int32 status = writer->Write(&byte, 1);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 WriteUInt(IMkvWriter* writer, uint64 value) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
int32 size = GetCodedUIntSize(value);
|
||||
|
||||
return WriteUIntSize(writer, value, size);
|
||||
}
|
||||
|
||||
int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size) {
|
||||
if (!writer || size < 0 || size > 8)
|
||||
return -1;
|
||||
|
||||
if (size > 0) {
|
||||
const uint64 bit = 1LL << (size * 7);
|
||||
|
||||
if (value > (bit - 2))
|
||||
return -1;
|
||||
|
||||
value |= bit;
|
||||
} else {
|
||||
size = 1;
|
||||
int64 bit;
|
||||
|
||||
for (;;) {
|
||||
bit = 1LL << (size * 7);
|
||||
const uint64 max = bit - 2;
|
||||
|
||||
if (value <= max)
|
||||
break;
|
||||
|
||||
++size;
|
||||
}
|
||||
|
||||
if (size > 8)
|
||||
return false;
|
||||
|
||||
value |= bit;
|
||||
}
|
||||
|
||||
return SerializeInt(writer, value, size);
|
||||
}
|
||||
|
||||
int32 WriteID(IMkvWriter* writer, uint64 type) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
writer->ElementStartNotify(type, writer->Position());
|
||||
|
||||
const int32 size = GetUIntSize(type);
|
||||
|
||||
return SerializeInt(writer, type, size);
|
||||
}
|
||||
|
||||
bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 type, uint64 size) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) {
|
||||
return WriteEbmlElement(writer, type, value, 0);
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value,
|
||||
uint64 fixed_size) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
uint64 size = GetUIntSize(value);
|
||||
if (fixed_size > 0) {
|
||||
if (size > fixed_size)
|
||||
return false;
|
||||
size = fixed_size;
|
||||
}
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
if (SerializeInt(writer, value, static_cast<int32>(size)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return 0;
|
||||
|
||||
const uint64 size = GetIntSize(value);
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
if (SerializeInt(writer, value, static_cast<int32>(size)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, 4))
|
||||
return false;
|
||||
|
||||
if (SerializeFloat(writer, value))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value) {
|
||||
if (!writer || !value)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
const uint64 length = strlen(value);
|
||||
if (WriteUInt(writer, length))
|
||||
return false;
|
||||
|
||||
if (writer->Write(value, static_cast<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
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef MKVMUXER_MKVMUXERUTIL_H_
|
||||
#define MKVMUXER_MKVMUXERUTIL_H_
|
||||
|
||||
#include "mkvmuxertypes.h"
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
namespace mkvmuxer {
|
||||
class Cluster;
|
||||
class Frame;
|
||||
class IMkvWriter;
|
||||
|
||||
// TODO(tomfinegan): mkvmuxer:: integer types continue to be used here because
|
||||
// changing them causes pain for downstream projects. It would be nice if a
|
||||
// solution that allows removal of the mkvmuxer:: integer types while avoiding
|
||||
// pain for downstream users of libwebm. Considering that mkvmuxerutil.{cc,h}
|
||||
// are really, for the great majority of cases, EBML size calculation and writer
|
||||
// functions, perhaps a more EBML focused utility would be the way to go as a
|
||||
// first step.
|
||||
|
||||
const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL;
|
||||
const int64 kMaxBlockTimecode = 0x07FFFLL;
|
||||
|
||||
// Writes out |value| in Big Endian order. Returns 0 on success.
|
||||
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size);
|
||||
|
||||
// Writes out |f| in Big Endian order. Returns 0 on success.
|
||||
int32 SerializeFloat(IMkvWriter* writer, float f);
|
||||
|
||||
// Returns the size in bytes of the element.
|
||||
int32 GetUIntSize(uint64 value);
|
||||
int32 GetIntSize(int64 value);
|
||||
int32 GetCodedUIntSize(uint64 value);
|
||||
uint64 EbmlMasterElementSize(uint64 type, uint64 value);
|
||||
uint64 EbmlElementSize(uint64 type, int64 value);
|
||||
uint64 EbmlElementSize(uint64 type, uint64 value);
|
||||
uint64 EbmlElementSize(uint64 type, float value);
|
||||
uint64 EbmlElementSize(uint64 type, const char* value);
|
||||
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size);
|
||||
uint64 EbmlDateElementSize(uint64 type);
|
||||
|
||||
// Returns the size in bytes of the element assuming that the element was
|
||||
// written using |fixed_size| bytes. If |fixed_size| is set to zero, then it
|
||||
// computes the necessary number of bytes based on |value|.
|
||||
uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size);
|
||||
|
||||
// Creates an EBML coded number from |value| and writes it out. The size of
|
||||
// the coded number is determined by the value of |value|. |value| must not
|
||||
// be in a coded form. Returns 0 on success.
|
||||
int32 WriteUInt(IMkvWriter* writer, uint64 value);
|
||||
|
||||
// Creates an EBML coded number from |value| and writes it out. The size of
|
||||
// the coded number is determined by the value of |size|. |value| must not
|
||||
// be in a coded form. Returns 0 on success.
|
||||
int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size);
|
||||
|
||||
// Output an Mkv master element. Returns true if the element was written.
|
||||
bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 value, uint64 size);
|
||||
|
||||
// Outputs an Mkv ID, calls |IMkvWriter::ElementStartNotify|, and passes the
|
||||
// ID to |SerializeInt|. Returns 0 on success.
|
||||
int32 WriteID(IMkvWriter* writer, uint64 type);
|
||||
|
||||
// Output an Mkv non-master element. Returns true if the element was written.
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
|
||||
uint64 size);
|
||||
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value);
|
||||
|
||||
// Output an Mkv non-master element using fixed size. The element will be
|
||||
// written out using exactly |fixed_size| bytes. If |fixed_size| is set to zero
|
||||
// then it computes the necessary number of bytes based on |value|. Returns true
|
||||
// if the element was written.
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value,
|
||||
uint64 fixed_size);
|
||||
|
||||
// Output a Mkv Frame. It decides the correct element to write (Block vs
|
||||
// SimpleBlock) based on the parameters of the Frame.
|
||||
uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
|
||||
Cluster* cluster);
|
||||
|
||||
// Output a void element. |size| must be the entire size in bytes that will be
|
||||
// void. The function will calculate the size of the void header and subtract
|
||||
// it from |size|.
|
||||
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size);
|
||||
|
||||
// Returns the version number of the muxer in |major|, |minor|, |build|,
|
||||
// and |revision|.
|
||||
void GetVersion(int32* major, int32* minor, int32* build, int32* revision);
|
||||
|
||||
// Returns a random number to be used for UID, using |seed| to seed
|
||||
// the random-number generator (see POSIX rand_r() for semantics).
|
||||
uint64 MakeUID(unsigned int* seed);
|
||||
|
||||
// Colour field validation helpers. All return true when |value| is valid.
|
||||
bool IsMatrixCoefficientsValueValid(uint64_t value);
|
||||
bool IsChromaSitingHorzValueValid(uint64_t value);
|
||||
bool IsChromaSitingVertValueValid(uint64_t value);
|
||||
bool IsColourRangeValueValid(uint64_t value);
|
||||
bool IsTransferCharacteristicsValueValid(uint64_t value);
|
||||
bool IsPrimariesValueValid(uint64_t value);
|
||||
|
||||
} // namespace mkvmuxer
|
||||
|
||||
#endif // MKVMUXER_MKVMUXERUTIL_H_
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvmuxer/mkvwriter.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <share.h> // for _SH_DENYWR
|
||||
#endif
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
|
||||
|
||||
MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {}
|
||||
|
||||
MkvWriter::~MkvWriter() { Close(); }
|
||||
|
||||
int32 MkvWriter::Write(const void* buffer, uint32 length) {
|
||||
if (!file_)
|
||||
return -1;
|
||||
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
|
||||
const size_t bytes_written = fwrite(buffer, 1, length, file_);
|
||||
|
||||
return (bytes_written == length) ? 0 : -1;
|
||||
}
|
||||
|
||||
bool MkvWriter::Open(const char* filename) {
|
||||
if (filename == NULL)
|
||||
return false;
|
||||
|
||||
if (file_)
|
||||
return false;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
file_ = _fsopen(filename, "wb", _SH_DENYWR);
|
||||
#else
|
||||
file_ = fopen(filename, "wb");
|
||||
#endif
|
||||
if (file_ == NULL)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MkvWriter::Close() {
|
||||
if (file_ && writer_owns_file_) {
|
||||
fclose(file_);
|
||||
}
|
||||
file_ = NULL;
|
||||
}
|
||||
|
||||
int64 MkvWriter::Position() const {
|
||||
if (!file_)
|
||||
return 0;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
return _ftelli64(file_);
|
||||
#else
|
||||
return ftell(file_);
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 MkvWriter::Position(int64 position) {
|
||||
if (!file_)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
return _fseeki64(file_, position, SEEK_SET);
|
||||
#elif defined(_WIN32)
|
||||
return fseeko64(file_, static_cast<off_t>(position), SEEK_SET);
|
||||
#else
|
||||
return fseeko(file_, static_cast<off_t>(position), SEEK_SET);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MkvWriter::Seekable() const { return true; }
|
||||
|
||||
void MkvWriter::ElementStartNotify(uint64, int64) {}
|
||||
|
||||
} // namespace mkvmuxer
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVMUXER_MKVWRITER_H_
|
||||
#define MKVMUXER_MKVWRITER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mkvmuxer/mkvmuxer.h"
|
||||
#include "mkvmuxer/mkvmuxertypes.h"
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
// Default implementation of the IMkvWriter interface on Windows.
|
||||
class MkvWriter : public IMkvWriter {
|
||||
public:
|
||||
MkvWriter();
|
||||
explicit MkvWriter(FILE* fp);
|
||||
virtual ~MkvWriter();
|
||||
|
||||
// IMkvWriter interface
|
||||
virtual int64 Position() const;
|
||||
virtual int32 Position(int64 position);
|
||||
virtual bool Seekable() const;
|
||||
virtual int32 Write(const void* buffer, uint32 length);
|
||||
virtual void ElementStartNotify(uint64 element_id, int64 position);
|
||||
|
||||
// Creates and opens a file for writing. |filename| is the name of the file
|
||||
// to open. This function will overwrite the contents of |filename|. Returns
|
||||
// true on success.
|
||||
bool Open(const char* filename);
|
||||
|
||||
// Closes an opened file.
|
||||
void Close();
|
||||
|
||||
private:
|
||||
// File handle to output file.
|
||||
FILE* file_;
|
||||
bool writer_owns_file_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
|
||||
};
|
||||
|
||||
} // namespace mkvmuxer
|
||||
|
||||
#endif // MKVMUXER_MKVWRITER_H_
|
||||
@@ -0,0 +1,802 @@
|
||||
// Copyright (c) 2011 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// libwebm common includes.
|
||||
#include "common/file_util.h"
|
||||
#include "common/hdr_util.h"
|
||||
|
||||
// libwebm mkvparser includes
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
|
||||
// libwebm mkvmuxer includes
|
||||
#include "mkvmuxer/mkvmuxer.h"
|
||||
#include "mkvmuxer/mkvmuxertypes.h"
|
||||
#include "mkvmuxer/mkvwriter.h"
|
||||
|
||||
#include "sample_muxer_metadata.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void Usage() {
|
||||
printf("Usage: mkvmuxer_sample -i input -o output [options]\n");
|
||||
printf("\n");
|
||||
printf("Main options:\n");
|
||||
printf(" -h | -? show help\n");
|
||||
printf(" -video <int> >0 outputs video\n");
|
||||
printf(" -audio <int> >0 outputs audio\n");
|
||||
printf(" -live <int> >0 puts the muxer into live mode\n");
|
||||
printf(" 0 puts the muxer into file mode\n");
|
||||
printf(" -output_cues <int> >0 outputs cues element\n");
|
||||
printf(" -cues_on_video_track <int> >0 outputs cues on video track\n");
|
||||
printf(" -cues_on_audio_track <int> >0 outputs cues on audio track\n");
|
||||
printf(" -max_cluster_duration <double> in seconds\n");
|
||||
printf(" -max_cluster_size <int> in bytes\n");
|
||||
printf(" -switch_tracks <int> >0 switches tracks in output\n");
|
||||
printf(" -audio_track_number <int> >0 Changes the audio track number\n");
|
||||
printf(" -video_track_number <int> >0 Changes the video track number\n");
|
||||
printf(" -chunking <string> Chunk output\n");
|
||||
printf(" -copy_tags <int> >0 Copies the tags\n");
|
||||
printf(" -accurate_cluster_duration <int> ");
|
||||
printf(">0 Writes the last frame in each cluster with Duration\n");
|
||||
printf(" -fixed_size_cluster_timecode <int> ");
|
||||
printf(">0 Writes the cluster timecode using exactly 8 bytes\n");
|
||||
printf(" -copy_input_duration >0 Copies the input duration\n");
|
||||
printf("\n");
|
||||
printf("Video options:\n");
|
||||
printf(" -display_width <int> Display width in pixels\n");
|
||||
printf(" -display_height <int> Display height in pixels\n");
|
||||
printf(" -pixel_width <int> Override pixel width\n");
|
||||
printf(" -pixel_height <int> Override pixel height\n");
|
||||
printf(" -projection_type <int> Set/override projection type:\n");
|
||||
printf(" 0: Rectangular\n");
|
||||
printf(" 1: Equirectangular\n");
|
||||
printf(" 2: Cube map\n");
|
||||
printf(" 3: Mesh\n");
|
||||
printf(" -projection_file <string> Override projection private data");
|
||||
printf(" with contents of this file\n");
|
||||
printf(" -projection_pose_yaw <float> Projection pose yaw\n");
|
||||
printf(" -projection_pose_pitch <float> Projection pose pitch\n");
|
||||
printf(" -projection_pose_roll <float> Projection pose roll\n");
|
||||
printf(" -stereo_mode <int> 3D video mode\n");
|
||||
printf("\n");
|
||||
printf("VP9 options:\n");
|
||||
printf(" -profile <int> VP9 profile\n");
|
||||
printf(" -level <int> VP9 level\n");
|
||||
printf("\n");
|
||||
printf("Cues options:\n");
|
||||
printf(" -output_cues_block_number <int> >0 outputs cue block number\n");
|
||||
printf(" -cues_before_clusters <int> >0 puts Cues before Clusters\n");
|
||||
printf("\n");
|
||||
printf("Metadata options:\n");
|
||||
printf(" -webvtt-subtitles <vttfile> ");
|
||||
printf("add WebVTT subtitles as metadata track\n");
|
||||
printf(" -webvtt-captions <vttfile> ");
|
||||
printf("add WebVTT captions as metadata track\n");
|
||||
printf(" -webvtt-descriptions <vttfile> ");
|
||||
printf("add WebVTT descriptions as metadata track\n");
|
||||
printf(" -webvtt-metadata <vttfile> ");
|
||||
printf("add WebVTT subtitles as metadata track\n");
|
||||
printf(" -webvtt-chapters <vttfile> ");
|
||||
printf("add WebVTT chapters as MKV chapters element\n");
|
||||
}
|
||||
|
||||
struct MetadataFile {
|
||||
const char* name;
|
||||
SampleMuxerMetadata::Kind kind;
|
||||
};
|
||||
|
||||
typedef std::list<MetadataFile> metadata_files_t;
|
||||
|
||||
// Cache the WebVTT filenames specified as command-line args.
|
||||
bool LoadMetadataFiles(const metadata_files_t& files,
|
||||
SampleMuxerMetadata* metadata) {
|
||||
typedef metadata_files_t::const_iterator iter_t;
|
||||
|
||||
iter_t i = files.begin();
|
||||
const iter_t j = files.end();
|
||||
|
||||
while (i != j) {
|
||||
const metadata_files_t::value_type& v = *i++;
|
||||
|
||||
if (!metadata->Load(v.name, v.kind))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ParseArgWebVTT(char* argv[], int* argv_index, int argc_check,
|
||||
metadata_files_t* metadata_files) {
|
||||
int& i = *argv_index;
|
||||
|
||||
enum { kCount = 5 };
|
||||
struct Arg {
|
||||
const char* name;
|
||||
SampleMuxerMetadata::Kind kind;
|
||||
};
|
||||
const Arg args[kCount] = {
|
||||
{"-webvtt-subtitles", SampleMuxerMetadata::kSubtitles},
|
||||
{"-webvtt-captions", SampleMuxerMetadata::kCaptions},
|
||||
{"-webvtt-descriptions", SampleMuxerMetadata::kDescriptions},
|
||||
{"-webvtt-metadata", SampleMuxerMetadata::kMetadata},
|
||||
{"-webvtt-chapters", SampleMuxerMetadata::kChapters}};
|
||||
|
||||
for (int idx = 0; idx < kCount; ++idx) {
|
||||
const Arg& arg = args[idx];
|
||||
|
||||
if (strcmp(arg.name, argv[i]) != 0) // no match
|
||||
continue;
|
||||
|
||||
++i; // consume arg name here
|
||||
|
||||
if (i > argc_check) {
|
||||
printf("missing value for %s\n", arg.name);
|
||||
return -1; // error
|
||||
}
|
||||
|
||||
MetadataFile f;
|
||||
f.name = argv[i]; // arg value is consumed via caller's loop idx
|
||||
f.kind = arg.kind;
|
||||
|
||||
metadata_files->push_back(f);
|
||||
return 1; // successfully parsed WebVTT arg
|
||||
}
|
||||
|
||||
return 0; // not a WebVTT arg
|
||||
}
|
||||
|
||||
bool CopyVideoProjection(const mkvparser::Projection& parser_projection,
|
||||
mkvmuxer::Projection* muxer_projection) {
|
||||
typedef mkvmuxer::Projection::ProjectionType MuxerProjType;
|
||||
const int kTypeNotPresent = mkvparser::Projection::kTypeNotPresent;
|
||||
if (parser_projection.type != kTypeNotPresent) {
|
||||
muxer_projection->set_type(
|
||||
static_cast<MuxerProjType>(parser_projection.type));
|
||||
}
|
||||
if (parser_projection.private_data &&
|
||||
parser_projection.private_data_length > 0) {
|
||||
if (!muxer_projection->SetProjectionPrivate(
|
||||
parser_projection.private_data,
|
||||
parser_projection.private_data_length)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const float kValueNotPresent = mkvparser::Projection::kValueNotPresent;
|
||||
if (parser_projection.pose_yaw != kValueNotPresent)
|
||||
muxer_projection->set_pose_yaw(parser_projection.pose_yaw);
|
||||
if (parser_projection.pose_pitch != kValueNotPresent)
|
||||
muxer_projection->set_pose_pitch(parser_projection.pose_pitch);
|
||||
if (parser_projection.pose_roll != kValueNotPresent)
|
||||
muxer_projection->set_pose_roll(parser_projection.pose_roll);
|
||||
return true;
|
||||
}
|
||||
} // end namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
char* input = NULL;
|
||||
char* output = NULL;
|
||||
|
||||
// Segment variables
|
||||
bool output_video = true;
|
||||
bool output_audio = true;
|
||||
bool live_mode = false;
|
||||
bool output_cues = true;
|
||||
bool cues_before_clusters = false;
|
||||
bool cues_on_video_track = true;
|
||||
bool cues_on_audio_track = false;
|
||||
uint64_t max_cluster_duration = 0;
|
||||
uint64_t max_cluster_size = 0;
|
||||
bool switch_tracks = false;
|
||||
int audio_track_number = 0; // 0 tells muxer to decide.
|
||||
int video_track_number = 0; // 0 tells muxer to decide.
|
||||
bool chunking = false;
|
||||
bool copy_tags = false;
|
||||
const char* chunk_name = NULL;
|
||||
bool accurate_cluster_duration = false;
|
||||
bool fixed_size_cluster_timecode = false;
|
||||
bool copy_input_duration = false;
|
||||
|
||||
bool output_cues_block_number = true;
|
||||
|
||||
uint64_t display_width = 0;
|
||||
uint64_t display_height = 0;
|
||||
uint64_t pixel_width = 0;
|
||||
uint64_t pixel_height = 0;
|
||||
uint64_t stereo_mode = 0;
|
||||
const char* projection_file = 0;
|
||||
int64_t projection_type = mkvparser::Projection::kTypeNotPresent;
|
||||
float projection_pose_roll = mkvparser::Projection::kValueNotPresent;
|
||||
float projection_pose_pitch = mkvparser::Projection::kValueNotPresent;
|
||||
float projection_pose_yaw = mkvparser::Projection::kValueNotPresent;
|
||||
int vp9_profile = -1; // No profile set.
|
||||
int vp9_level = -1; // No level set.
|
||||
|
||||
metadata_files_t metadata_files;
|
||||
|
||||
const int argc_check = argc - 1;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
char* end;
|
||||
|
||||
if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) {
|
||||
Usage();
|
||||
return EXIT_SUCCESS;
|
||||
} else if (!strcmp("-i", argv[i]) && i < argc_check) {
|
||||
input = argv[++i];
|
||||
} else if (!strcmp("-o", argv[i]) && i < argc_check) {
|
||||
output = argv[++i];
|
||||
} else if (!strcmp("-video", argv[i]) && i < argc_check) {
|
||||
output_video = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-audio", argv[i]) && i < argc_check) {
|
||||
output_audio = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-live", argv[i]) && i < argc_check) {
|
||||
live_mode = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-output_cues", argv[i]) && i < argc_check) {
|
||||
output_cues = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-cues_before_clusters", argv[i]) && i < argc_check) {
|
||||
cues_before_clusters = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-cues_on_video_track", argv[i]) && i < argc_check) {
|
||||
cues_on_video_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
if (cues_on_video_track)
|
||||
cues_on_audio_track = false;
|
||||
} else if (!strcmp("-cues_on_audio_track", argv[i]) && i < argc_check) {
|
||||
cues_on_audio_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
if (cues_on_audio_track)
|
||||
cues_on_video_track = false;
|
||||
} else if (!strcmp("-max_cluster_duration", argv[i]) && i < argc_check) {
|
||||
const double seconds = strtod(argv[++i], &end);
|
||||
max_cluster_duration = static_cast<uint64_t>(seconds * 1000000000.0);
|
||||
} else if (!strcmp("-max_cluster_size", argv[i]) && i < argc_check) {
|
||||
max_cluster_size = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-switch_tracks", argv[i]) && i < argc_check) {
|
||||
switch_tracks = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-audio_track_number", argv[i]) && i < argc_check) {
|
||||
audio_track_number = static_cast<int>(strtol(argv[++i], &end, 10));
|
||||
} else if (!strcmp("-video_track_number", argv[i]) && i < argc_check) {
|
||||
video_track_number = static_cast<int>(strtol(argv[++i], &end, 10));
|
||||
} else if (!strcmp("-chunking", argv[i]) && i < argc_check) {
|
||||
chunking = true;
|
||||
chunk_name = argv[++i];
|
||||
} else if (!strcmp("-copy_tags", argv[i]) && i < argc_check) {
|
||||
copy_tags = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-accurate_cluster_duration", argv[i]) &&
|
||||
i < argc_check) {
|
||||
accurate_cluster_duration =
|
||||
strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-fixed_size_cluster_timecode", argv[i]) &&
|
||||
i < argc_check) {
|
||||
fixed_size_cluster_timecode =
|
||||
strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-copy_input_duration", argv[i]) && i < argc_check) {
|
||||
copy_input_duration = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-display_width", argv[i]) && i < argc_check) {
|
||||
display_width = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-display_height", argv[i]) && i < argc_check) {
|
||||
display_height = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-pixel_width", argv[i]) && i < argc_check) {
|
||||
pixel_width = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-pixel_height", argv[i]) && i < argc_check) {
|
||||
pixel_height = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-stereo_mode", argv[i]) && i < argc_check) {
|
||||
stereo_mode = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-projection_type", argv[i]) && i < argc_check) {
|
||||
projection_type = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-projection_file", argv[i]) && i < argc_check) {
|
||||
projection_file = argv[++i];
|
||||
} else if (!strcmp("-projection_pose_roll", argv[i]) && i < argc_check) {
|
||||
projection_pose_roll = strtof(argv[++i], &end);
|
||||
} else if (!strcmp("-projection_pose_pitch", argv[i]) && i < argc_check) {
|
||||
projection_pose_pitch = strtof(argv[++i], &end);
|
||||
} else if (!strcmp("-projection_pose_yaw", argv[i]) && i < argc_check) {
|
||||
projection_pose_yaw = strtof(argv[++i], &end);
|
||||
} else if (!strcmp("-profile", argv[i]) && i < argc_check) {
|
||||
vp9_profile = static_cast<int>(strtol(argv[++i], &end, 10));
|
||||
} else if (!strcmp("-level", argv[i]) && i < argc_check) {
|
||||
vp9_level = static_cast<int>(strtol(argv[++i], &end, 10));
|
||||
} else if (!strcmp("-output_cues_block_number", argv[i]) &&
|
||||
i < argc_check) {
|
||||
output_cues_block_number =
|
||||
strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (int e = ParseArgWebVTT(argv, &i, argc_check, &metadata_files)) {
|
||||
if (e < 0)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (input == NULL || output == NULL) {
|
||||
Usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Get parser header info
|
||||
mkvparser::MkvReader reader;
|
||||
|
||||
if (reader.Open(input)) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
long long pos = 0;
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
long long ret = ebml_header.Parse(&reader, pos);
|
||||
if (ret) {
|
||||
printf("\n EBMLHeader::Parse() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mkvparser::Segment* parser_segment_;
|
||||
ret = mkvparser::Segment::CreateInstance(&reader, pos, parser_segment_);
|
||||
if (ret) {
|
||||
printf("\n Segment::CreateInstance() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const std::unique_ptr<mkvparser::Segment> parser_segment(parser_segment_);
|
||||
ret = parser_segment->Load();
|
||||
if (ret < 0) {
|
||||
printf("\n Segment::Load() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const mkvparser::SegmentInfo* const segment_info = parser_segment->GetInfo();
|
||||
if (segment_info == NULL) {
|
||||
printf("\n Segment::GetInfo() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const long long timeCodeScale = segment_info->GetTimeCodeScale();
|
||||
|
||||
// Set muxer header info
|
||||
mkvmuxer::MkvWriter writer;
|
||||
|
||||
const std::string temp_file =
|
||||
cues_before_clusters ? libwebm::GetTempFileName() : output;
|
||||
if (!writer.Open(temp_file.c_str())) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Set Segment element attributes
|
||||
mkvmuxer::Segment muxer_segment;
|
||||
|
||||
if (!muxer_segment.Init(&writer)) {
|
||||
printf("\n Could not initialize muxer segment!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
muxer_segment.AccurateClusterDuration(accurate_cluster_duration);
|
||||
muxer_segment.UseFixedSizeClusterTimecode(fixed_size_cluster_timecode);
|
||||
|
||||
if (live_mode)
|
||||
muxer_segment.set_mode(mkvmuxer::Segment::kLive);
|
||||
else
|
||||
muxer_segment.set_mode(mkvmuxer::Segment::kFile);
|
||||
|
||||
if (chunking)
|
||||
muxer_segment.SetChunking(true, chunk_name);
|
||||
|
||||
if (max_cluster_duration > 0)
|
||||
muxer_segment.set_max_cluster_duration(max_cluster_duration);
|
||||
if (max_cluster_size > 0)
|
||||
muxer_segment.set_max_cluster_size(max_cluster_size);
|
||||
muxer_segment.OutputCues(output_cues);
|
||||
|
||||
// Set SegmentInfo element attributes
|
||||
mkvmuxer::SegmentInfo* const info = muxer_segment.GetSegmentInfo();
|
||||
info->set_timecode_scale(timeCodeScale);
|
||||
info->set_writing_app("mkvmuxer_sample");
|
||||
|
||||
const mkvparser::Tags* const tags = parser_segment->GetTags();
|
||||
if (copy_tags && tags) {
|
||||
for (int i = 0; i < tags->GetTagCount(); i++) {
|
||||
const mkvparser::Tags::Tag* const tag = tags->GetTag(i);
|
||||
mkvmuxer::Tag* muxer_tag = muxer_segment.AddTag();
|
||||
|
||||
for (int j = 0; j < tag->GetSimpleTagCount(); j++) {
|
||||
const mkvparser::Tags::SimpleTag* const simple_tag =
|
||||
tag->GetSimpleTag(j);
|
||||
muxer_tag->add_simple_tag(simple_tag->GetTagName(),
|
||||
simple_tag->GetTagString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Tracks element attributes
|
||||
const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks();
|
||||
unsigned long i = 0;
|
||||
uint64_t vid_track = 0; // no track added
|
||||
uint64_t aud_track = 0; // no track added
|
||||
|
||||
using mkvparser::Track;
|
||||
|
||||
while (i != parser_tracks->GetTracksCount()) {
|
||||
unsigned long track_num = i++;
|
||||
if (switch_tracks)
|
||||
track_num = i % parser_tracks->GetTracksCount();
|
||||
|
||||
const Track* const parser_track = parser_tracks->GetTrackByIndex(track_num);
|
||||
|
||||
if (parser_track == NULL)
|
||||
continue;
|
||||
|
||||
// TODO(fgalligan): Add support for language to parser.
|
||||
const char* const track_name = parser_track->GetNameAsUTF8();
|
||||
|
||||
const long long track_type = parser_track->GetType();
|
||||
|
||||
if (track_type == Track::kVideo && output_video) {
|
||||
// Get the video track from the parser
|
||||
const mkvparser::VideoTrack* const pVideoTrack =
|
||||
static_cast<const mkvparser::VideoTrack*>(parser_track);
|
||||
const long long width = pVideoTrack->GetWidth();
|
||||
const long long height = pVideoTrack->GetHeight();
|
||||
|
||||
// Add the video track to the muxer
|
||||
vid_track = muxer_segment.AddVideoTrack(static_cast<int>(width),
|
||||
static_cast<int>(height),
|
||||
video_track_number);
|
||||
if (!vid_track) {
|
||||
printf("\n Could not add video track.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mkvmuxer::VideoTrack* const video = static_cast<mkvmuxer::VideoTrack*>(
|
||||
muxer_segment.GetTrackByNumber(vid_track));
|
||||
if (!video) {
|
||||
printf("\n Could not get video track.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (pVideoTrack->GetColour()) {
|
||||
mkvmuxer::Colour muxer_colour;
|
||||
if (!libwebm::CopyColour(*pVideoTrack->GetColour(), &muxer_colour))
|
||||
return EXIT_FAILURE;
|
||||
if (!video->SetColour(muxer_colour))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (pVideoTrack->GetProjection() ||
|
||||
projection_type != mkvparser::Projection::kTypeNotPresent) {
|
||||
mkvmuxer::Projection muxer_projection;
|
||||
const mkvparser::Projection* const parser_projection =
|
||||
pVideoTrack->GetProjection();
|
||||
typedef mkvmuxer::Projection::ProjectionType MuxerProjType;
|
||||
if (parser_projection &&
|
||||
!CopyVideoProjection(*parser_projection, &muxer_projection)) {
|
||||
printf("\n Unable to copy video projection.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Override the values that came from parser if set on command line.
|
||||
if (projection_type != mkvparser::Projection::kTypeNotPresent) {
|
||||
muxer_projection.set_type(
|
||||
static_cast<MuxerProjType>(projection_type));
|
||||
if (projection_type == mkvparser::Projection::kRectangular &&
|
||||
projection_file != NULL) {
|
||||
printf("\n Rectangular projection must not have private data.\n");
|
||||
return EXIT_FAILURE;
|
||||
} else if ((projection_type == mkvparser::Projection::kCubeMap ||
|
||||
projection_type == mkvparser::Projection::kMesh) &&
|
||||
projection_file == NULL) {
|
||||
printf("\n Mesh or CubeMap projection must have private data.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (projection_file != NULL) {
|
||||
std::string contents;
|
||||
if (!libwebm::GetFileContents(projection_file, &contents) ||
|
||||
contents.size() == 0) {
|
||||
printf("\n Failed to read file \"%s\" or file is empty\n",
|
||||
projection_file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!muxer_projection.SetProjectionPrivate(
|
||||
reinterpret_cast<uint8_t*>(&contents[0]),
|
||||
contents.size())) {
|
||||
printf("\n Failed to SetProjectionPrivate of length %zu.\n",
|
||||
contents.size());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
const float kValueNotPresent = mkvparser::Projection::kValueNotPresent;
|
||||
if (projection_pose_yaw != kValueNotPresent)
|
||||
muxer_projection.set_pose_yaw(projection_pose_yaw);
|
||||
if (projection_pose_pitch != kValueNotPresent)
|
||||
muxer_projection.set_pose_pitch(projection_pose_pitch);
|
||||
if (projection_pose_roll != kValueNotPresent)
|
||||
muxer_projection.set_pose_roll(projection_pose_roll);
|
||||
|
||||
if (!video->SetProjection(muxer_projection))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (track_name)
|
||||
video->set_name(track_name);
|
||||
|
||||
video->set_codec_id(pVideoTrack->GetCodecId());
|
||||
|
||||
if (display_width > 0)
|
||||
video->set_display_width(display_width);
|
||||
if (display_height > 0)
|
||||
video->set_display_height(display_height);
|
||||
if (pixel_width > 0)
|
||||
video->set_pixel_width(pixel_width);
|
||||
if (pixel_height > 0)
|
||||
video->set_pixel_height(pixel_height);
|
||||
if (stereo_mode > 0)
|
||||
video->SetStereoMode(stereo_mode);
|
||||
|
||||
const double rate = pVideoTrack->GetFrameRate();
|
||||
if (rate > 0.0) {
|
||||
video->set_frame_rate(rate);
|
||||
}
|
||||
|
||||
size_t parser_private_size;
|
||||
const unsigned char* const parser_private_data =
|
||||
pVideoTrack->GetCodecPrivate(parser_private_size);
|
||||
|
||||
if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kAv1CodecId)) {
|
||||
if (parser_private_data == NULL || parser_private_size == 0) {
|
||||
printf("AV1 input track has no CodecPrivate. %s is invalid.", input);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kVp9CodecId) &&
|
||||
(vp9_profile >= 0 || vp9_level >= 0)) {
|
||||
const int kMaxVp9PrivateSize = 6;
|
||||
unsigned char vp9_private_data[kMaxVp9PrivateSize];
|
||||
int vp9_private_size = 0;
|
||||
if (vp9_profile >= 0) {
|
||||
if (vp9_profile < 0 || vp9_profile > 3) {
|
||||
printf("\n VP9 profile(%d) is not valid.\n", vp9_profile);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const uint8_t kVp9ProfileId = 1;
|
||||
const uint8_t kVp9ProfileIdLength = 1;
|
||||
vp9_private_data[vp9_private_size++] = kVp9ProfileId;
|
||||
vp9_private_data[vp9_private_size++] = kVp9ProfileIdLength;
|
||||
vp9_private_data[vp9_private_size++] = vp9_profile;
|
||||
}
|
||||
|
||||
if (vp9_level >= 0) {
|
||||
const int kNumLevels = 14;
|
||||
const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40,
|
||||
41, 50, 51, 52, 60, 61, 62};
|
||||
bool level_is_valid = false;
|
||||
for (int i = 0; i < kNumLevels; ++i) {
|
||||
if (vp9_level == levels[i]) {
|
||||
level_is_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!level_is_valid) {
|
||||
printf("\n VP9 level(%d) is not valid.\n", vp9_level);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const uint8_t kVp9LevelId = 2;
|
||||
const uint8_t kVp9LevelIdLength = 1;
|
||||
vp9_private_data[vp9_private_size++] = kVp9LevelId;
|
||||
vp9_private_data[vp9_private_size++] = kVp9LevelIdLength;
|
||||
vp9_private_data[vp9_private_size++] = vp9_level;
|
||||
}
|
||||
if (!video->SetCodecPrivate(vp9_private_data, vp9_private_size)) {
|
||||
printf("\n Could not add video private data.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else if (parser_private_data && parser_private_size > 0) {
|
||||
if (!video->SetCodecPrivate(parser_private_data, parser_private_size)) {
|
||||
printf("\n Could not add video private data.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
} else if (track_type == Track::kAudio && output_audio) {
|
||||
// Get the audio track from the parser
|
||||
const mkvparser::AudioTrack* const pAudioTrack =
|
||||
static_cast<const mkvparser::AudioTrack*>(parser_track);
|
||||
const long long channels = pAudioTrack->GetChannels();
|
||||
const double sample_rate = pAudioTrack->GetSamplingRate();
|
||||
|
||||
// Add the audio track to the muxer
|
||||
aud_track = muxer_segment.AddAudioTrack(static_cast<int>(sample_rate),
|
||||
static_cast<int>(channels),
|
||||
audio_track_number);
|
||||
if (!aud_track) {
|
||||
printf("\n Could not add audio track.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mkvmuxer::AudioTrack* const audio = static_cast<mkvmuxer::AudioTrack*>(
|
||||
muxer_segment.GetTrackByNumber(aud_track));
|
||||
if (!audio) {
|
||||
printf("\n Could not get audio track.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (track_name)
|
||||
audio->set_name(track_name);
|
||||
|
||||
audio->set_codec_id(pAudioTrack->GetCodecId());
|
||||
|
||||
size_t private_size;
|
||||
const unsigned char* const private_data =
|
||||
pAudioTrack->GetCodecPrivate(private_size);
|
||||
if (private_size > 0) {
|
||||
if (!audio->SetCodecPrivate(private_data, private_size)) {
|
||||
printf("\n Could not add audio private data.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
const long long bit_depth = pAudioTrack->GetBitDepth();
|
||||
if (bit_depth > 0)
|
||||
audio->set_bit_depth(bit_depth);
|
||||
|
||||
if (pAudioTrack->GetCodecDelay())
|
||||
audio->set_codec_delay(pAudioTrack->GetCodecDelay());
|
||||
if (pAudioTrack->GetSeekPreRoll())
|
||||
audio->set_seek_pre_roll(pAudioTrack->GetSeekPreRoll());
|
||||
}
|
||||
}
|
||||
|
||||
// We have created all the video and audio tracks. If any WebVTT
|
||||
// files were specified as command-line args, then parse them and
|
||||
// add a track to the output file corresponding to each metadata
|
||||
// input file.
|
||||
|
||||
SampleMuxerMetadata metadata;
|
||||
|
||||
if (!metadata.Init(&muxer_segment)) {
|
||||
printf("\n Could not initialize metadata cache.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!LoadMetadataFiles(metadata_files, &metadata))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (!metadata.AddChapters())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Set Cues element attributes
|
||||
mkvmuxer::Cues* const cues = muxer_segment.GetCues();
|
||||
cues->set_output_block_number(output_cues_block_number);
|
||||
if (cues_on_video_track && vid_track)
|
||||
muxer_segment.CuesTrack(vid_track);
|
||||
if (cues_on_audio_track && aud_track)
|
||||
muxer_segment.CuesTrack(aud_track);
|
||||
|
||||
// Write clusters
|
||||
unsigned char* data = NULL;
|
||||
long data_len = 0;
|
||||
|
||||
const mkvparser::Cluster* cluster = parser_segment->GetFirst();
|
||||
|
||||
while (cluster != NULL && !cluster->EOS()) {
|
||||
const mkvparser::BlockEntry* block_entry;
|
||||
|
||||
long status = cluster->GetFirst(block_entry);
|
||||
|
||||
if (status) {
|
||||
printf("\n Could not get first block of cluster.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (block_entry != NULL && !block_entry->EOS()) {
|
||||
const mkvparser::Block* const block = block_entry->GetBlock();
|
||||
const long long trackNum = block->GetTrackNumber();
|
||||
const mkvparser::Track* const parser_track =
|
||||
parser_tracks->GetTrackByNumber(static_cast<unsigned long>(trackNum));
|
||||
|
||||
// When |parser_track| is NULL, it means that the track number in the
|
||||
// Block is invalid (i.e.) the was no TrackEntry corresponding to the
|
||||
// track number. So we reject the file.
|
||||
if (!parser_track) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const long long track_type = parser_track->GetType();
|
||||
const long long time_ns = block->GetTime(cluster);
|
||||
|
||||
// Flush any metadata frames to the output file, before we write
|
||||
// the current block.
|
||||
if (!metadata.Write(time_ns))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if ((track_type == Track::kAudio && output_audio) ||
|
||||
(track_type == Track::kVideo && output_video)) {
|
||||
const int frame_count = block->GetFrameCount();
|
||||
|
||||
for (int i = 0; i < frame_count; ++i) {
|
||||
const mkvparser::Block::Frame& frame = block->GetFrame(i);
|
||||
|
||||
if (frame.len > data_len) {
|
||||
delete[] data;
|
||||
data = new unsigned char[frame.len];
|
||||
if (!data)
|
||||
return EXIT_FAILURE;
|
||||
data_len = frame.len;
|
||||
}
|
||||
|
||||
if (frame.Read(&reader, data))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
mkvmuxer::Frame muxer_frame;
|
||||
if (!muxer_frame.Init(data, frame.len))
|
||||
return EXIT_FAILURE;
|
||||
muxer_frame.set_track_number(track_type == Track::kAudio ? aud_track
|
||||
: vid_track);
|
||||
if (block->GetDiscardPadding())
|
||||
muxer_frame.set_discard_padding(block->GetDiscardPadding());
|
||||
muxer_frame.set_timestamp(time_ns);
|
||||
muxer_frame.set_is_key(block->IsKey());
|
||||
if (!muxer_segment.AddGenericFrame(&muxer_frame)) {
|
||||
printf("\n Could not add frame.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status = cluster->GetNext(block_entry, block_entry);
|
||||
|
||||
if (status) {
|
||||
printf("\n Could not get next block of cluster.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = parser_segment->GetNext(cluster);
|
||||
}
|
||||
|
||||
// We have exhausted all video and audio frames in the input file.
|
||||
// Flush any remaining metadata frames to the output file.
|
||||
if (!metadata.Write(-1))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (copy_input_duration) {
|
||||
const double input_duration =
|
||||
static_cast<double>(segment_info->GetDuration()) / timeCodeScale;
|
||||
muxer_segment.set_duration(input_duration);
|
||||
}
|
||||
|
||||
if (!muxer_segment.Finalize()) {
|
||||
printf("Finalization of segment failed.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
reader.Close();
|
||||
writer.Close();
|
||||
|
||||
if (cues_before_clusters) {
|
||||
if (reader.Open(temp_file.c_str())) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!writer.Open(output)) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!muxer_segment.CopyAndMoveCuesBeforeClusters(&reader, &writer)) {
|
||||
printf("\n Unable to copy and move cues before clusters.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
reader.Close();
|
||||
writer.Close();
|
||||
remove(temp_file.c_str());
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_MKVMUXERTYPES_HPP_
|
||||
#define LIBWEBM_MKVMUXERTYPES_HPP_
|
||||
|
||||
// This file is a wrapper for the file included immediately after this comment.
|
||||
// New projects should not include this file: include the file included below.
|
||||
#include "mkvmuxer/mkvmuxertypes.h"
|
||||
|
||||
#endif // LIBWEBM_MKVMUXERTYPES_HPP_
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_MKVMUXERUTIL_HPP_
|
||||
#define LIBWEBM_MKVMUXERUTIL_HPP_
|
||||
|
||||
// This file is a wrapper for the file included immediately after this comment.
|
||||
// New projects should not include this file: include the file included below.
|
||||
#include "mkvmuxer/mkvmuxerutil.h"
|
||||
|
||||
using mkvmuxer::EbmlElementSize;
|
||||
using mkvmuxer::EbmlMasterElementSize;
|
||||
|
||||
#endif // LIBWEBM_MKVMUXERUTIL_HPP_
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_MKVPARSER_HPP_
|
||||
#define LIBWEBM_MKVPARSER_HPP_
|
||||
|
||||
// This file is a wrapper for the file included immediately after this comment.
|
||||
// New projects should not include this file: include the file included below.
|
||||
#include "mkvparser/mkvparser.h"
|
||||
|
||||
#endif // LIBWEBM_MKVPARSER_HPP_
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,135 @@
|
||||
// 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
|
||||
#elif defined(_WIN32)
|
||||
fseeko64(m_file, static_cast<off_t>(offset), SEEK_SET);
|
||||
#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
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef MKVPARSER_MKVREADER_H_
|
||||
#define MKVPARSER_MKVREADER_H_
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "mkvparser/mkvparser.h"
|
||||
|
||||
namespace mkvparser {
|
||||
|
||||
class MkvReader : public IMkvReader {
|
||||
public:
|
||||
MkvReader();
|
||||
explicit MkvReader(FILE* fp);
|
||||
virtual ~MkvReader();
|
||||
|
||||
int Open(const char*);
|
||||
void Close();
|
||||
|
||||
virtual int Read(long long position, long length, unsigned char* buffer);
|
||||
virtual int Length(long long* total, long long* available);
|
||||
|
||||
private:
|
||||
MkvReader(const MkvReader&);
|
||||
MkvReader& operator=(const MkvReader&);
|
||||
|
||||
// Determines the size of the file. This is called either by the constructor
|
||||
// or by the Open function depending on file ownership. Returns true on
|
||||
// success.
|
||||
bool GetFileSize();
|
||||
|
||||
long long m_length;
|
||||
FILE* m_file;
|
||||
bool reader_owns_file_;
|
||||
};
|
||||
|
||||
} // namespace mkvparser
|
||||
|
||||
#endif // MKVPARSER_MKVREADER_H_
|
||||
@@ -0,0 +1,459 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_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_
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#ifndef LIBWEBM_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_
|
||||
@@ -0,0 +1,391 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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
@@ -0,0 +1,823 @@
|
||||
// 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();
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
#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
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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 mkvparser
|
||||
|
||||
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
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
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.
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+1
@@ -0,0 +1 @@
|
||||
EߣŸB0�0B0�0B0�0B0�0B‚„webmB0�0B0�0S€g0000000»‹00000000000»Œ000000000000ì¼000000000000000000000000000000000000000000000000000000000000€“0000000000000000000W0“0000000000000000000T®k@ž®@›×�0s0�0ƒ�†…00000à@‡°‚00º�0U°ýU¸�0U¹�0Uº�0U»�0U¼�0U½�0UÐÆUÙ„0000UÚ„0000UÑ„0000UÒ000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
Vendored
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user