Compare commits
378 Commits
libwebm-1.
...
libwebm-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82ac5fcdc8 | ||
|
|
852e1733a8 | ||
|
|
faf85c227d | ||
|
|
7c19266548 | ||
|
|
01fdee435c | ||
|
|
1ad314e297 | ||
|
|
476366249e | ||
|
|
267f71c76e | ||
|
|
f1a99d5f25 | ||
|
|
bff1aa512d | ||
|
|
a58c32339e | ||
|
|
714f3c4e4f | ||
|
|
cec98d4110 | ||
|
|
eb36ae4840 | ||
|
|
229f49347d | ||
|
|
287faf95f6 | ||
|
|
1a87b59032 | ||
|
|
d26ec6909d | ||
|
|
f2029be5f4 | ||
|
|
19f5694277 | ||
|
|
27a07c1fd1 | ||
|
|
d0313dd7ce | ||
|
|
b108695b9b | ||
|
|
4630f80f79 | ||
|
|
841a9b5fd9 | ||
|
|
8c4ca2ea04 | ||
|
|
49ae6f0dd9 | ||
|
|
0735bb5bdc | ||
|
|
93b24c4195 | ||
|
|
a57d6602b4 | ||
|
|
1c5bd949d0 | ||
|
|
7f77201dca | ||
|
|
795fd568b6 | ||
|
|
23bb18b76d | ||
|
|
7b57e37fde | ||
|
|
83a1f68944 | ||
|
|
507471457e | ||
|
|
b18110541a | ||
|
|
06b4337ed8 | ||
|
|
b366a98053 | ||
|
|
2857b2350c | ||
|
|
f1b2cfa03e | ||
|
|
ca8062959a | ||
|
|
6b4b297220 | ||
|
|
c0d2c9852b | ||
|
|
1a6dc4f210 | ||
|
|
275ac22cae | ||
|
|
064f2eed62 | ||
|
|
3778408b2a | ||
|
|
e86d046c07 | ||
|
|
f9885b5882 | ||
|
|
21ee398281 | ||
|
|
08fb6546e8 | ||
|
|
c8960955db | ||
|
|
680b4bfd3d | ||
|
|
5889e6c18b | ||
|
|
5135c4cd74 | ||
|
|
b0e4f32011 | ||
|
|
13ccc7f089 | ||
|
|
db3f9bbd79 | ||
|
|
8de3654fdb | ||
|
|
fa2aa7da2d | ||
|
|
d9bdadeff8 | ||
|
|
07a9cf7127 | ||
|
|
c56ee29254 | ||
|
|
d9013243ff | ||
|
|
7f7d898a27 | ||
|
|
42fe2cd755 | ||
|
|
8bccd9c306 | ||
|
|
7a2fa0daf3 | ||
|
|
44f5ce64b1 | ||
|
|
b521e3030e | ||
|
|
7680e2a76b | ||
|
|
39a315f8c1 | ||
|
|
f250aceeaa | ||
|
|
cd96a76985 | ||
|
|
8e8b3dbc6a | ||
|
|
82b7e5f487 | ||
|
|
04d7809375 | ||
|
|
986b64b8c0 | ||
|
|
2dec09426a | ||
|
|
b6de61a5c0 | ||
|
|
75a6d2da8b | ||
|
|
cec1f8521f | ||
|
|
8a61b4033e | ||
|
|
7affc5c3f8 | ||
|
|
d6d04acdcc | ||
|
|
4928b0bd5f | ||
|
|
c2e4a46244 | ||
|
|
e97f296855 | ||
|
|
d66ba4470a | ||
|
|
deb41c2ea1 | ||
|
|
42e5660e73 | ||
|
|
fe1e9bba6e | ||
|
|
2cb6a28b09 | ||
|
|
d04580fda5 | ||
|
|
c3550fdacf | ||
|
|
5dd0e40dbd | ||
|
|
8e96863b56 | ||
|
|
a9e4819e9f | ||
|
|
5a3be734f3 | ||
|
|
f99f3b20fb | ||
|
|
ff572b5399 | ||
|
|
b6311dc16f | ||
|
|
256cd02327 | ||
|
|
16c8e78265 | ||
|
|
c5e511c00a | ||
|
|
4baaa2c9a3 | ||
|
|
3d06eb1e2c | ||
|
|
d3849c2f7b | ||
|
|
f439e523b8 | ||
|
|
d3a44cd549 | ||
|
|
c6255af02a | ||
|
|
b5229c7bc8 | ||
|
|
e3616a6614 | ||
|
|
a4b68f825e | ||
|
|
bab0a002c5 | ||
|
|
feeb9b13ff | ||
|
|
b9a549b467 | ||
|
|
b386aa5c6b | ||
|
|
b0f8a81df9 | ||
|
|
f06e152abd | ||
|
|
27bb7476dd | ||
|
|
623d182613 | ||
|
|
1156da8d29 | ||
|
|
0d4cb404ea | ||
|
|
e12fff0ebb | ||
|
|
a321704b4c | ||
|
|
574045edd4 | ||
|
|
8be63972fd | ||
|
|
8f2d1b3cde | ||
|
|
1c36c24694 | ||
|
|
568504e64e | ||
|
|
acf788bedd | ||
|
|
418188b03e | ||
|
|
07688c92d7 | ||
|
|
2a63e473d8 | ||
|
|
d13c017744 | ||
|
|
249629d46c | ||
|
|
7f3cda494a | ||
|
|
5c0617852f | ||
|
|
4df111e60a | ||
|
|
7b2450131c | ||
|
|
c6767b94fd | ||
|
|
9097a0691b | ||
|
|
eddf9744cb | ||
|
|
def325c010 | ||
|
|
41f869cb0c | ||
|
|
fd0be37ba4 | ||
|
|
207d8a193a | ||
|
|
02429eb11b | ||
|
|
0cf7b1b7d3 | ||
|
|
2e80fedf8c | ||
|
|
3402e12d1a | ||
|
|
1a685db45b | ||
|
|
6634c7f87a | ||
|
|
7566004462 | ||
|
|
9915b8408e | ||
|
|
743725477c | ||
|
|
0d5a98cee5 | ||
|
|
e3485c9b9f | ||
|
|
46cc823994 | ||
|
|
5218bd291c | ||
|
|
1a0130d069 | ||
|
|
867f1894da | ||
|
|
4c7bec5743 | ||
|
|
9ead078aaf | ||
|
|
fb6b6e6444 | ||
|
|
ce775929a6 | ||
|
|
0a24fe44ae | ||
|
|
11d5b66eb4 | ||
|
|
a1a3b14538 | ||
|
|
0fcec38045 | ||
|
|
a7118d8ec5 | ||
|
|
abe9c2d0d1 | ||
|
|
630a0e3c33 | ||
|
|
e369bed319 | ||
|
|
a9d94ef2c5 | ||
|
|
3b66306126 | ||
|
|
acb7a2c8bc | ||
|
|
99f40649a7 | ||
|
|
c6634bca44 | ||
|
|
17cf7cc519 | ||
|
|
7f79df14a8 | ||
|
|
23cdb09fd2 | ||
|
|
8ae2137637 | ||
|
|
7952ce8f78 | ||
|
|
d58f55542f | ||
|
|
a0c85b1e46 | ||
|
|
4600f5b4a2 | ||
|
|
a142b15ada | ||
|
|
4a3f5c9f99 | ||
|
|
84f2156ca1 | ||
|
|
5440f20071 | ||
|
|
5c14a3f035 | ||
|
|
71a097fb12 | ||
|
|
cd6f7bff4b | ||
|
|
327e8ab617 | ||
|
|
796c90798a | ||
|
|
0f7815b036 | ||
|
|
ccb7fa9c73 | ||
|
|
4ec1e5cae5 | ||
|
|
849093f84f | ||
|
|
32e1556e68 | ||
|
|
5efd6e3c1d | ||
|
|
a6c71c1407 | ||
|
|
81c1d8415c | ||
|
|
d2f7478148 | ||
|
|
fd0a65af98 | ||
|
|
872cc57de4 | ||
|
|
ddfea3431f | ||
|
|
4134f6e04e | ||
|
|
25025a5471 | ||
|
|
f72dc7b052 | ||
|
|
c5892de23b | ||
|
|
15a708e416 | ||
|
|
8206558561 | ||
|
|
d782edd68e | ||
|
|
4ac7b755f4 | ||
|
|
09dd90fcc7 | ||
|
|
54ca052b1c | ||
|
|
74e5f0fa1d | ||
|
|
9b42d039ad | ||
|
|
3f7681d11e | ||
|
|
7a64466954 | ||
|
|
05912e8a96 | ||
|
|
a9f43fe8e7 | ||
|
|
5af56bb9ee | ||
|
|
64cee42b85 | ||
|
|
d5cb6c7fa4 | ||
|
|
252bd24950 | ||
|
|
9cb9ee4efa | ||
|
|
3af8d02ca1 | ||
|
|
0d5b3fc5ae | ||
|
|
3980cf4159 | ||
|
|
1274be1184 | ||
|
|
43178b4c9a | ||
|
|
8376a8e9d7 | ||
|
|
2b09f9b53a | ||
|
|
28222b4927 | ||
|
|
0fcf5e5a40 | ||
|
|
c26db03d5a | ||
|
|
baefebcf1c | ||
|
|
386928d8b8 | ||
|
|
50ee255b8c | ||
|
|
ad54bfb572 | ||
|
|
c99838a5fe | ||
|
|
2c5836837e | ||
|
|
50afbea946 | ||
|
|
25ee621061 | ||
|
|
ac238c0c5c | ||
|
|
4a5141344e | ||
|
|
2fc496a0d6 | ||
|
|
cb8899a920 | ||
|
|
49078292b4 | ||
|
|
4f494f6dd4 | ||
|
|
7ef225de9f | ||
|
|
8f0c3333d1 | ||
|
|
bf664baf05 | ||
|
|
232bae0d50 | ||
|
|
69c9348f07 | ||
|
|
711af0c505 | ||
|
|
38173f9d49 | ||
|
|
21a2bd14c7 | ||
|
|
9ec562f72d | ||
|
|
e6e2b6b387 | ||
|
|
a2ca300513 | ||
|
|
6cf00207c0 | ||
|
|
4df02ce68b | ||
|
|
4ffb162798 | ||
|
|
76d9cf9cf4 | ||
|
|
20f64ff979 | ||
|
|
537da82f37 | ||
|
|
db20aaa2b1 | ||
|
|
c9e284b9e7 | ||
|
|
07ac1947f0 | ||
|
|
0edf087bbb | ||
|
|
c425e965aa | ||
|
|
ed6282b2d6 | ||
|
|
282a67599c | ||
|
|
adebb53754 | ||
|
|
a320f5be63 | ||
|
|
c4d2b27b7a | ||
|
|
13a90600ea | ||
|
|
478b524df3 | ||
|
|
010a457bc9 | ||
|
|
47a09523b2 | ||
|
|
84bf4a41dc | ||
|
|
177df33d25 | ||
|
|
4fff53441d | ||
|
|
ca13a5bae0 | ||
|
|
cb69e608b4 | ||
|
|
f6b0408aba | ||
|
|
9a561ab4dd | ||
|
|
0568dd63a6 | ||
|
|
56488f73ef | ||
|
|
f270ddaeb8 | ||
|
|
041a5c5811 | ||
|
|
6fa7c7611c | ||
|
|
71d296e44c | ||
|
|
72052ed8b1 | ||
|
|
a88d62b682 | ||
|
|
28d54555a7 | ||
|
|
359b3654ad | ||
|
|
000944c4e4 | ||
|
|
8da8206fa0 | ||
|
|
7a9a72f984 | ||
|
|
d2327e2b65 | ||
|
|
1e37a264f1 | ||
|
|
01d5924817 | ||
|
|
a20f4255c7 | ||
|
|
69df730519 | ||
|
|
f7aa8ab33d | ||
|
|
73ad7bd83c | ||
|
|
4c682199b0 | ||
|
|
9303667611 | ||
|
|
4affedd0a7 | ||
|
|
23808a7ba4 | ||
|
|
32227e70c1 | ||
|
|
2d3461b4b3 | ||
|
|
a09f15f00e | ||
|
|
6d99850e7c | ||
|
|
1ae4335c1c | ||
|
|
52f0a92192 | ||
|
|
6ebe4a39df | ||
|
|
a3dd40877d | ||
|
|
1623fb983b | ||
|
|
18ac83d501 | ||
|
|
5942099a85 | ||
|
|
9ecedef185 | ||
|
|
6f68021678 | ||
|
|
3395c36a8e | ||
|
|
ffe5a8e548 | ||
|
|
a9c65fbbc0 | ||
|
|
31a9d5470e | ||
|
|
2b84a12da8 | ||
|
|
bd833a82e4 | ||
|
|
7b07758854 | ||
|
|
00ed87aad6 | ||
|
|
06f08663be | ||
|
|
35ded77a23 | ||
|
|
1f33611caa | ||
|
|
70f9644a8d | ||
|
|
f2bd78ef6b | ||
|
|
2083c72300 | ||
|
|
f5ec272e54 | ||
|
|
b324e52139 | ||
|
|
4137f9c999 | ||
|
|
ba4096f120 | ||
|
|
365a39b5ec | ||
|
|
601f7903a3 | ||
|
|
16b2cdaf57 | ||
|
|
ca1e6b7323 | ||
|
|
c226b79e61 | ||
|
|
dc8bdb3389 | ||
|
|
31b2d8689c | ||
|
|
227d62aadf | ||
|
|
85beb00fe3 | ||
|
|
eb4bd69098 | ||
|
|
cf36dc2848 | ||
|
|
b0465e167e | ||
|
|
a1aa16692a | ||
|
|
f971a94349 | ||
|
|
5ac9764a95 | ||
|
|
d82f86a40a | ||
|
|
a1736157be | ||
|
|
2c835bcc28 | ||
|
|
d931a6ecc6 | ||
|
|
7a8b013f16 | ||
|
|
a977a2b536 | ||
|
|
5e72a2dfc2 | ||
|
|
056b0d96a8 | ||
|
|
dbc58d0510 | ||
|
|
a131a01446 | ||
|
|
b8cb358204 | ||
|
|
5b06b22b31 | ||
|
|
598de03ef3 | ||
|
|
94f2d589fc |
53
.clang-format
Normal file
53
.clang-format
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BinPackParameters: true
|
||||
ColumnLimit: 80
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
DerivePointerBinding: true
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: true
|
||||
MaxEmptyLinesToKeep: 1
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: false
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerBindsToType: true
|
||||
SpacesBeforeTrailingComments: 2
|
||||
Cpp11BracedListStyle: true
|
||||
Standard: Auto
|
||||
IndentWidth: 2
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Attach
|
||||
IndentFunctionDeclarationAfterType: true
|
||||
SpacesInParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
ContinuationIndentWidth: 4
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
SpaceBeforeParens: ControlStatements
|
||||
...
|
||||
|
||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.sln eol=crlf
|
||||
*.vcproj eol=crlf
|
||||
*.vsprops eol=crlf
|
||||
*.vcxproj eol=crlf
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
*.MKV
|
||||
core
|
||||
*.a
|
||||
*.d
|
||||
*.so*
|
||||
*.o
|
||||
*~
|
||||
@@ -12,4 +13,15 @@ core
|
||||
*.exe
|
||||
*.webm
|
||||
Debug
|
||||
Release
|
||||
Release
|
||||
*.sdf
|
||||
*.opensdf
|
||||
ipch
|
||||
dumpvtt
|
||||
sample
|
||||
sample_muxer
|
||||
vttdemux
|
||||
Makefile
|
||||
CMakeFiles
|
||||
CMakeCache.txt
|
||||
*.cmake
|
||||
|
||||
13
Android.mk
13
Android.mk
@@ -1,13 +1,10 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libmkvparser
|
||||
LOCAL_MODULE:= libwebm
|
||||
LOCAL_SRC_FILES:= mkvparser.cpp \
|
||||
mkvreader.cpp
|
||||
mkvreader.cpp \
|
||||
mkvmuxer.cpp \
|
||||
mkvmuxerutil.cpp \
|
||||
mkvwriter.cpp
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= mkvparser
|
||||
LOCAL_SRC_FILES:= sample.cpp
|
||||
LOCAL_STATIC_LIBRARIES:= libmkvparser
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
64
CMakeLists.txt
Normal file
64
CMakeLists.txt
Normal file
@@ -0,0 +1,64 @@
|
||||
## Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
project(LIBWEBM)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/build/msvc_runtime.cmake")
|
||||
|
||||
set(LIBWEBM_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
# Libwebm section.
|
||||
add_library(webm STATIC
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer.cpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxer.hpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxertypes.hpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxerutil.cpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvmuxerutil.hpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvparser.cpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvparser.hpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvreader.cpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvreader.hpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvwriter.cpp"
|
||||
"${LIBWEBM_SRC_DIR}/mkvwriter.hpp"
|
||||
"${LIBWEBM_SRC_DIR}/webmids.hpp")
|
||||
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(WIN32)
|
||||
|
||||
include_directories("${LIBWEBM_SRC_DIR}")
|
||||
|
||||
# Sample section.
|
||||
add_executable(sample
|
||||
"${LIBWEBM_SRC_DIR}/sample.cpp")
|
||||
target_link_libraries(sample LINK_PUBLIC webm)
|
||||
|
||||
# Sample muxer section.
|
||||
add_executable(sample_muxer
|
||||
"${LIBWEBM_SRC_DIR}/sample_muxer.cpp"
|
||||
"${LIBWEBM_SRC_DIR}/sample_muxer_metadata.cc"
|
||||
"${LIBWEBM_SRC_DIR}/sample_muxer_metadata.h"
|
||||
"${LIBWEBM_SRC_DIR}/vttreader.cc"
|
||||
"${LIBWEBM_SRC_DIR}/vttreader.h"
|
||||
"${LIBWEBM_SRC_DIR}/webvttparser.cc"
|
||||
"${LIBWEBM_SRC_DIR}/webvttparser.h")
|
||||
target_link_libraries(sample_muxer LINK_PUBLIC webm)
|
||||
|
||||
# Vttdemux section.
|
||||
add_executable(vttdemux
|
||||
"${LIBWEBM_SRC_DIR}/vttdemux.cc"
|
||||
"${LIBWEBM_SRC_DIR}/webvttparser.cc"
|
||||
"${LIBWEBM_SRC_DIR}/webvttparser.h")
|
||||
target_link_libraries(vttdemux LINK_PUBLIC webm)
|
||||
|
||||
# webm2pes section.
|
||||
add_executable(webm2pes
|
||||
"${LIBWEBM_SRC_DIR}/webm2pes.cc"
|
||||
"${LIBWEBM_SRC_DIR}/webm2pes.h")
|
||||
target_link_libraries(webm2pes LINK_PUBLIC webm)
|
||||
20
Makefile
20
Makefile
@@ -1,20 +0,0 @@
|
||||
LIB = libmkvparser.a
|
||||
OBJECTS = mkvparser.o mkvreader.o sample.o
|
||||
EXE = sample
|
||||
CFLAGS = -W -Wall -g
|
||||
|
||||
$(EXE): $(OBJECTS)
|
||||
$(AR) rcs $(LIB) mkvparser.o
|
||||
$(CXX) $(OBJECTS) -L./ -lmkvparser -o $(EXE)
|
||||
|
||||
mkvparser.o: mkvparser.cpp
|
||||
$(CXX) -c $(CFLAGS) mkvparser.cpp -o mkvparser.o
|
||||
|
||||
mkvreader.o: mkvreader.cpp
|
||||
$(CXX) -c $(CFLAGS) mkvreader.cpp -o mkvreader.o
|
||||
|
||||
sample.o: sample.cpp
|
||||
$(CXX) -c $(CFLAGS) sample.cpp -o sample.o
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJECTS) $(LIB) $(EXE) Makefile.bak
|
||||
53
Makefile.unix
Normal file
53
Makefile.unix
Normal file
@@ -0,0 +1,53 @@
|
||||
CXX := g++
|
||||
CXXFLAGS := -W -Wall -g -MMD -MP
|
||||
LIBWEBMA := libwebm.a
|
||||
LIBWEBMSO := libwebm.so
|
||||
WEBMOBJS := mkvparser.o mkvreader.o mkvmuxer.o mkvmuxerutil.o mkvwriter.o
|
||||
OBJSA := $(WEBMOBJS:.o=_a.o)
|
||||
OBJSSO := $(WEBMOBJS:.o=_so.o)
|
||||
OBJECTS1 := sample.o
|
||||
OBJECTS2 := sample_muxer.o vttreader.o webvttparser.o sample_muxer_metadata.o
|
||||
OBJECTS3 := dumpvtt.o vttreader.o webvttparser.o
|
||||
OBJECTS4 := vttdemux.o webvttparser.o
|
||||
INCLUDES := -I.
|
||||
DEPS := $(WEBMOBJS:.o=.d) $(OBJECTS1:.o=.d) $(OBJECTS2:.o=.d)
|
||||
DEPS += $(OBJECTS3:.o=.d) $(OBJECTS4:.o=.d)
|
||||
EXES := sample_muxer sample dumpvtt vttdemux
|
||||
|
||||
all: $(EXES)
|
||||
|
||||
sample: sample.o $(LIBWEBMA)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
sample_muxer: $(OBJECTS2) $(LIBWEBMA)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
dumpvtt: $(OBJECTS3)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
shared: $(LIBWEBMSO)
|
||||
|
||||
vttdemux: $(OBJECTS4) $(LIBWEBMA)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
libwebm.a: $(OBJSA)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
libwebm.so: $(OBJSSO)
|
||||
$(CXX) $(CXXFLAGS) -shared $(OBJSSO) -o $(LIBWEBMSO)
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
%_a.o: %.cpp
|
||||
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
%_so.o: %.cpp
|
||||
$(CXX) -c $(CXXFLAGS) -fPIC $(INCLUDES) $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) -f $(OBJECTS1) $(OBJECTS2) $(OBJECTS3) $(OBJECTS4) $(OBJSA) $(OBJSSO) $(LIBWEBMA) $(LIBWEBMSO) $(EXES) $(DEPS) Makefile.bak
|
||||
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
39
PATENTS.TXT
39
PATENTS.TXT
@@ -1,22 +1,23 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
------------------------------------
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the WebM Project.
|
||||
"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 this
|
||||
implementation of VP8, where such license applies only to those patent
|
||||
claims, both currently owned by Google and acquired in the future,
|
||||
licensable by Google that are necessarily infringed by this
|
||||
implementation of VP8. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of VP8 or any code incorporated within this
|
||||
implementation of VP8 constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of VP8
|
||||
shall terminate as of the date such litigation is filed.
|
||||
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.
|
||||
|
||||
57
README.libwebm
Normal file
57
README.libwebm
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
34
RELEASE.TXT
34
RELEASE.TXT
@@ -1,34 +0,0 @@
|
||||
1.0.0.5
|
||||
* Handled case when no duration
|
||||
* Handled empty clusters
|
||||
* Handled empty clusters when seeking
|
||||
* Implemented check lacing bits
|
||||
|
||||
1.0.0.4
|
||||
* Made Cues member variables mutables
|
||||
* Defined against badly-formatted cue points
|
||||
* Segment::GetCluster returns CuePoint too
|
||||
* Separated cue-based searches
|
||||
|
||||
1.0.0.3
|
||||
* Added Block::GetOffset() to get a frame's offset in a block
|
||||
* Changed cluster count type from size_t to long
|
||||
* Parsed SeekHead to find cues
|
||||
* Allowed seeking beyond end of cluster cache
|
||||
* Added not to attempt to reparse cues element
|
||||
* Restructured Segment::LoadCluster
|
||||
* Marked position of cues without parsing cues element
|
||||
* Allowed cue points to be loaded incrementally
|
||||
* Implemented to load lazily cue points as they're searched
|
||||
* Merged Cues::LoadCuePoint into Cues::Find
|
||||
* Lazy init cues
|
||||
* Loaded cue point during find
|
||||
|
||||
1.0.0.2
|
||||
* added support for Cues element
|
||||
* seeking was improved
|
||||
|
||||
1.0.0.1
|
||||
* fixed item 141
|
||||
* added item 142
|
||||
* added this file, RELEASE.TXT, to repository
|
||||
24
build/msvc_runtime.cmake
Normal file
24
build/msvc_runtime.cmake
Normal file
@@ -0,0 +1,24 @@
|
||||
## Copyright (c) 2015 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
if(WIN32)
|
||||
# CMake defaults to producing code linked to the DLL MSVC runtime. In libwebm
|
||||
# static is typically desired. Force static code generation unless the user
|
||||
# running CMake set MSVC_RUNTIME to dll.
|
||||
if(NOT "${MSVC_RUNTIME}" STREQUAL "dll")
|
||||
foreach(flag_var
|
||||
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
if(${flag_var} MATCHES "/MD")
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endif(${flag_var} MATCHES "/MD")
|
||||
endforeach(flag_var)
|
||||
endif(NOT "${MSVC_RUNTIME}" STREQUAL "dll")
|
||||
endif(WIN32)
|
||||
|
||||
62
common/common.sh
Normal file
62
common/common.sh
Normal file
@@ -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)
|
||||
}
|
||||
|
||||
91
dumpvtt.cc
Normal file
91
dumpvtt.cc
Normal file
@@ -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 "./vttreader.h"
|
||||
#include "./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);
|
||||
}
|
||||
}
|
||||
193
iosbuild.sh
Executable file
193
iosbuild.sh
Executable file
@@ -0,0 +1,193 @@
|
||||
#!/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="mkvmuxer.hpp
|
||||
mkvmuxertypes.hpp
|
||||
mkvmuxerutil.hpp
|
||||
mkvparser.hpp
|
||||
mkvreader.hpp
|
||||
mkvwriter.hpp
|
||||
webmids.hpp"
|
||||
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"
|
||||
|
||||
# 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
|
||||
|
||||
for include_file in ${INCLUDES}; do
|
||||
eval cp -p ${include_file} "${OUTDIR}/${TARGETDIR}/Headers/" ${devnull}
|
||||
done
|
||||
|
||||
eval ${LIPO} -create ${LIBLIST} -output "${OUTDIR}/${TARGETDIR}/WebM" ${devnull}
|
||||
echo "Succesfully built ${TARGETDIR} in ${OUTDIR}."
|
||||
3281
mkvmuxer.cpp
Normal file
3281
mkvmuxer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1495
mkvmuxer.hpp
Normal file
1495
mkvmuxer.hpp
Normal file
File diff suppressed because it is too large
Load Diff
30
mkvmuxertypes.hpp
Normal file
30
mkvmuxertypes.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVMUXERTYPES_HPP
|
||||
#define MKVMUXERTYPES_HPP
|
||||
|
||||
// Copied from Chromium basictypes.h
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef short int16;
|
||||
typedef int int32;
|
||||
typedef unsigned int uint32;
|
||||
typedef long long int64;
|
||||
typedef unsigned long long uint64;
|
||||
|
||||
} // end namespace mkvmuxer
|
||||
|
||||
#endif // MKVMUXERTYPES_HPP
|
||||
629
mkvmuxerutil.cpp
Normal file
629
mkvmuxerutil.cpp
Normal file
@@ -0,0 +1,629 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvmuxerutil.hpp"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <new>
|
||||
|
||||
#include "mkvwriter.hpp"
|
||||
#include "webmids.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable MSVC warnings that suggest making code non-portable.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
namespace {
|
||||
|
||||
// Date elements are always 8 octets in size.
|
||||
const int kDateElementSize = 8;
|
||||
|
||||
uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode,
|
||||
uint64 timecode_scale) {
|
||||
uint64 block_additional_elem_size = 0;
|
||||
uint64 block_addid_elem_size = 0;
|
||||
uint64 block_more_payload_size = 0;
|
||||
uint64 block_more_elem_size = 0;
|
||||
uint64 block_additions_payload_size = 0;
|
||||
uint64 block_additions_elem_size = 0;
|
||||
if (frame->additional()) {
|
||||
block_additional_elem_size = EbmlElementSize(
|
||||
kMkvBlockAdditional, frame->additional(), frame->additional_length());
|
||||
block_addid_elem_size = EbmlElementSize(kMkvBlockAddID, frame->add_id());
|
||||
|
||||
block_more_payload_size =
|
||||
block_addid_elem_size + block_additional_elem_size;
|
||||
block_more_elem_size =
|
||||
EbmlMasterElementSize(kMkvBlockMore, block_more_payload_size) +
|
||||
block_more_payload_size;
|
||||
block_additions_payload_size = block_more_elem_size;
|
||||
block_additions_elem_size =
|
||||
EbmlMasterElementSize(kMkvBlockAdditions,
|
||||
block_additions_payload_size) +
|
||||
block_additions_payload_size;
|
||||
}
|
||||
|
||||
uint64 discard_padding_elem_size = 0;
|
||||
if (frame->discard_padding() != 0) {
|
||||
discard_padding_elem_size =
|
||||
EbmlElementSize(kMkvDiscardPadding, frame->discard_padding());
|
||||
}
|
||||
|
||||
const uint64 reference_block_timestamp =
|
||||
frame->reference_block_timestamp() / timecode_scale;
|
||||
uint64 reference_block_elem_size = 0;
|
||||
if (!frame->is_key()) {
|
||||
reference_block_elem_size =
|
||||
EbmlElementSize(kMkvReferenceBlock, reference_block_timestamp);
|
||||
}
|
||||
|
||||
const uint64 duration = frame->duration() / timecode_scale;
|
||||
uint64 block_duration_elem_size = 0;
|
||||
if (duration > 0)
|
||||
block_duration_elem_size = EbmlElementSize(kMkvBlockDuration, duration);
|
||||
|
||||
const uint64 block_payload_size = 4 + frame->length();
|
||||
const uint64 block_elem_size =
|
||||
EbmlMasterElementSize(kMkvBlock, block_payload_size) + block_payload_size;
|
||||
|
||||
const uint64 block_group_payload_size =
|
||||
block_elem_size + block_additions_elem_size + block_duration_elem_size +
|
||||
discard_padding_elem_size + reference_block_elem_size;
|
||||
|
||||
if (!WriteEbmlMasterElement(writer, kMkvBlockGroup,
|
||||
block_group_payload_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!WriteEbmlMasterElement(writer, kMkvBlock, block_payload_size))
|
||||
return 0;
|
||||
|
||||
if (WriteUInt(writer, frame->track_number()))
|
||||
return 0;
|
||||
|
||||
if (SerializeInt(writer, timecode, 2))
|
||||
return 0;
|
||||
|
||||
// For a Block, flags is always 0.
|
||||
if (SerializeInt(writer, 0, 1))
|
||||
return 0;
|
||||
|
||||
if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
|
||||
return 0;
|
||||
|
||||
if (frame->additional()) {
|
||||
if (!WriteEbmlMasterElement(writer, kMkvBlockAdditions,
|
||||
block_additions_payload_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!WriteEbmlMasterElement(writer, kMkvBlockMore, block_more_payload_size))
|
||||
return 0;
|
||||
|
||||
if (!WriteEbmlElement(writer, kMkvBlockAddID, frame->add_id()))
|
||||
return 0;
|
||||
|
||||
if (!WriteEbmlElement(writer, kMkvBlockAdditional, frame->additional(),
|
||||
frame->additional_length())) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->discard_padding() != 0 &&
|
||||
!WriteEbmlElement(writer, kMkvDiscardPadding, frame->discard_padding())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frame->is_key() &&
|
||||
!WriteEbmlElement(writer, kMkvReferenceBlock,
|
||||
reference_block_timestamp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (duration > 0 && !WriteEbmlElement(writer, kMkvBlockDuration, duration)) {
|
||||
return false;
|
||||
}
|
||||
return EbmlMasterElementSize(kMkvBlockGroup, block_group_payload_size) +
|
||||
block_group_payload_size;
|
||||
}
|
||||
|
||||
uint64 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame,
|
||||
int64 timecode) {
|
||||
if (WriteID(writer, kMkvSimpleBlock))
|
||||
return 0;
|
||||
|
||||
const int32 size = static_cast<int32>(frame->length()) + 4;
|
||||
if (WriteUInt(writer, size))
|
||||
return 0;
|
||||
|
||||
if (WriteUInt(writer, static_cast<uint64>(frame->track_number())))
|
||||
return 0;
|
||||
|
||||
if (SerializeInt(writer, timecode, 2))
|
||||
return 0;
|
||||
|
||||
uint64 flags = 0;
|
||||
if (frame->is_key())
|
||||
flags |= 0x80;
|
||||
|
||||
if (SerializeInt(writer, flags, 1))
|
||||
return 0;
|
||||
|
||||
if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
|
||||
return 0;
|
||||
|
||||
return GetUIntSize(kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 +
|
||||
frame->length();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int32 GetCodedUIntSize(uint64 value) {
|
||||
if (value < 0x000000000000007FULL)
|
||||
return 1;
|
||||
else if (value < 0x0000000000003FFFULL)
|
||||
return 2;
|
||||
else if (value < 0x00000000001FFFFFULL)
|
||||
return 3;
|
||||
else if (value < 0x000000000FFFFFFFULL)
|
||||
return 4;
|
||||
else if (value < 0x00000007FFFFFFFFULL)
|
||||
return 5;
|
||||
else if (value < 0x000003FFFFFFFFFFULL)
|
||||
return 6;
|
||||
else if (value < 0x0001FFFFFFFFFFFFULL)
|
||||
return 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
int32 GetUIntSize(uint64 value) {
|
||||
if (value < 0x0000000000000100ULL)
|
||||
return 1;
|
||||
else if (value < 0x0000000000010000ULL)
|
||||
return 2;
|
||||
else if (value < 0x0000000001000000ULL)
|
||||
return 3;
|
||||
else if (value < 0x0000000100000000ULL)
|
||||
return 4;
|
||||
else if (value < 0x0000010000000000ULL)
|
||||
return 5;
|
||||
else if (value < 0x0001000000000000ULL)
|
||||
return 6;
|
||||
else if (value < 0x0100000000000000ULL)
|
||||
return 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
int32 GetIntSize(int64 value) {
|
||||
// Doubling the requested value ensures positive values with their high bit
|
||||
// set are written with 0-padding to avoid flipping the signedness.
|
||||
const uint64 v = (value < 0) ? value ^ -1LL : value;
|
||||
return GetUIntSize(2 * v);
|
||||
}
|
||||
|
||||
uint64 EbmlMasterElementSize(uint64 type, uint64 value) {
|
||||
// Size of EBML ID
|
||||
int32 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += GetCodedUIntSize(value);
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, int64 value) {
|
||||
// Size of EBML ID
|
||||
int32 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += GetIntSize(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, uint64 value) {
|
||||
// Size of EBML ID
|
||||
int32 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += GetUIntSize(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, float /* value */) {
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += sizeof(float);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, const char* value) {
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += strlen(value);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) {
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += size;
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size += GetCodedUIntSize(size);
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlDateElementSize(uint64 type) {
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += kDateElementSize;
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size) {
|
||||
if (!writer || size < 1 || size > 8)
|
||||
return -1;
|
||||
|
||||
for (int32 i = 1; i <= size; ++i) {
|
||||
const int32 byte_count = size - i;
|
||||
const int32 bit_count = byte_count * 8;
|
||||
|
||||
const int64 bb = value >> bit_count;
|
||||
const uint8 b = static_cast<uint8>(bb);
|
||||
|
||||
const int32 status = writer->Write(&b, 1);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 SerializeFloat(IMkvWriter* writer, float f) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
assert(sizeof(uint32) == sizeof(float));
|
||||
// This union is merely used to avoid a reinterpret_cast from float& to
|
||||
// uint32& which will result in violation of strict aliasing.
|
||||
union U32 {
|
||||
uint32 u32;
|
||||
float f;
|
||||
} value;
|
||||
value.f = f;
|
||||
|
||||
for (int32 i = 1; i <= 4; ++i) {
|
||||
const int32 byte_count = 4 - i;
|
||||
const int32 bit_count = byte_count * 8;
|
||||
|
||||
const uint8 byte = static_cast<uint8>(value.u32 >> bit_count);
|
||||
|
||||
const int32 status = writer->Write(&byte, 1);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 WriteUInt(IMkvWriter* writer, uint64 value) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
int32 size = GetCodedUIntSize(value);
|
||||
|
||||
return WriteUIntSize(writer, value, size);
|
||||
}
|
||||
|
||||
int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size) {
|
||||
if (!writer || size < 0 || size > 8)
|
||||
return -1;
|
||||
|
||||
if (size > 0) {
|
||||
const uint64 bit = 1LL << (size * 7);
|
||||
|
||||
if (value > (bit - 2))
|
||||
return -1;
|
||||
|
||||
value |= bit;
|
||||
} else {
|
||||
size = 1;
|
||||
int64 bit;
|
||||
|
||||
for (;;) {
|
||||
bit = 1LL << (size * 7);
|
||||
const uint64 max = bit - 2;
|
||||
|
||||
if (value <= max)
|
||||
break;
|
||||
|
||||
++size;
|
||||
}
|
||||
|
||||
if (size > 8)
|
||||
return false;
|
||||
|
||||
value |= bit;
|
||||
}
|
||||
|
||||
return SerializeInt(writer, value, size);
|
||||
}
|
||||
|
||||
int32 WriteID(IMkvWriter* writer, uint64 type) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
writer->ElementStartNotify(type, writer->Position());
|
||||
|
||||
const int32 size = GetUIntSize(type);
|
||||
|
||||
return SerializeInt(writer, type, size);
|
||||
}
|
||||
|
||||
bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 type, uint64 size) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
const uint64 size = GetUIntSize(value);
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
if (SerializeInt(writer, value, static_cast<int32>(size)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return 0;
|
||||
|
||||
const uint64 size = GetIntSize(value);
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
if (SerializeInt(writer, value, static_cast<int32>(size)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, 4))
|
||||
return false;
|
||||
|
||||
if (SerializeFloat(writer, value))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value) {
|
||||
if (!writer || !value)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
const uint64 length = strlen(value);
|
||||
if (WriteUInt(writer, length))
|
||||
return false;
|
||||
|
||||
if (writer->Write(value, static_cast<const uint32>(length)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
|
||||
uint64 size) {
|
||||
if (!writer || !value || size < 1)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, size))
|
||||
return false;
|
||||
|
||||
if (writer->Write(value, static_cast<uint32>(size)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
if (WriteUInt(writer, kDateElementSize))
|
||||
return false;
|
||||
|
||||
if (SerializeInt(writer, value, kDateElementSize))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
|
||||
Cluster* cluster) {
|
||||
if (!writer || !frame || !frame->IsValid() || !cluster ||
|
||||
!cluster->timecode_scale())
|
||||
return 0;
|
||||
|
||||
// Technically the timecode for a block can be less than the
|
||||
// timecode for the cluster itself (remember that block timecode
|
||||
// is a signed, 16-bit integer). However, as a simplification we
|
||||
// only permit non-negative cluster-relative timecodes for blocks.
|
||||
const int64 relative_timecode = cluster->GetRelativeTimecode(
|
||||
frame->timestamp() / cluster->timecode_scale());
|
||||
if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode)
|
||||
return 0;
|
||||
|
||||
return frame->CanBeSimpleBlock() ?
|
||||
WriteSimpleBlock(writer, frame, relative_timecode) :
|
||||
WriteBlock(writer, frame, relative_timecode,
|
||||
cluster->timecode_scale());
|
||||
}
|
||||
|
||||
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
// Subtract one for the void ID and the coded size.
|
||||
uint64 void_entry_size = size - 1 - GetCodedUIntSize(size - 1);
|
||||
uint64 void_size =
|
||||
EbmlMasterElementSize(kMkvVoid, void_entry_size) + void_entry_size;
|
||||
|
||||
if (void_size != size)
|
||||
return 0;
|
||||
|
||||
const int64 payload_position = writer->Position();
|
||||
if (payload_position < 0)
|
||||
return 0;
|
||||
|
||||
if (WriteID(writer, kMkvVoid))
|
||||
return 0;
|
||||
|
||||
if (WriteUInt(writer, void_entry_size))
|
||||
return 0;
|
||||
|
||||
const uint8 value = 0;
|
||||
for (int32 i = 0; i < static_cast<int32>(void_entry_size); ++i) {
|
||||
if (writer->Write(&value, 1))
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int64 stop_position = writer->Position();
|
||||
if (stop_position < 0 ||
|
||||
stop_position - payload_position != static_cast<int64>(void_size))
|
||||
return 0;
|
||||
|
||||
return void_size;
|
||||
}
|
||||
|
||||
void GetVersion(int32* major, int32* minor, int32* build, int32* revision) {
|
||||
*major = 0;
|
||||
*minor = 2;
|
||||
*build = 1;
|
||||
*revision = 0;
|
||||
}
|
||||
|
||||
} // namespace mkvmuxer
|
||||
|
||||
mkvmuxer::uint64 mkvmuxer::MakeUID(unsigned int* seed) {
|
||||
uint64 uid = 0;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
srand(*seed);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values
|
||||
uid <<= 8;
|
||||
|
||||
// TODO(fgalligan): Move random number generation to platform specific code.
|
||||
#ifdef _MSC_VER
|
||||
(void)seed;
|
||||
const int32 nn = rand();
|
||||
#elif __ANDROID__
|
||||
int32 temp_num = 1;
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd != -1) {
|
||||
read(fd, &temp_num, sizeof(int32));
|
||||
close(fd);
|
||||
}
|
||||
const int32 nn = temp_num;
|
||||
#elif defined __MINGW32__
|
||||
const int32 nn = rand();
|
||||
#else
|
||||
const int32 nn = rand_r(seed);
|
||||
#endif
|
||||
const int32 n = 0xFF & (nn >> 4); // throw away low-order bits
|
||||
|
||||
uid |= n;
|
||||
}
|
||||
|
||||
return uid;
|
||||
}
|
||||
83
mkvmuxerutil.hpp
Normal file
83
mkvmuxerutil.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVMUXERUTIL_HPP
|
||||
#define MKVMUXERUTIL_HPP
|
||||
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "mkvmuxertypes.hpp"
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
class IMkvWriter;
|
||||
|
||||
const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL;
|
||||
const int64 kMaxBlockTimecode = 0x07FFFLL;
|
||||
|
||||
// Writes out |value| in Big Endian order. Returns 0 on success.
|
||||
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size);
|
||||
|
||||
// Returns the size in bytes of the element.
|
||||
int32 GetUIntSize(uint64 value);
|
||||
int32 GetIntSize(int64 value);
|
||||
int32 GetCodedUIntSize(uint64 value);
|
||||
uint64 EbmlMasterElementSize(uint64 type, uint64 value);
|
||||
uint64 EbmlElementSize(uint64 type, int64 value);
|
||||
uint64 EbmlElementSize(uint64 type, uint64 value);
|
||||
uint64 EbmlElementSize(uint64 type, float value);
|
||||
uint64 EbmlElementSize(uint64 type, const char* value);
|
||||
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size);
|
||||
uint64 EbmlDateElementSize(uint64 type);
|
||||
|
||||
// Creates an EBML coded number from |value| and writes it out. The size of
|
||||
// the coded number is determined by the value of |value|. |value| must not
|
||||
// be in a coded form. Returns 0 on success.
|
||||
int32 WriteUInt(IMkvWriter* writer, uint64 value);
|
||||
|
||||
// Creates an EBML coded number from |value| and writes it out. The size of
|
||||
// the coded number is determined by the value of |size|. |value| must not
|
||||
// be in a coded form. Returns 0 on success.
|
||||
int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size);
|
||||
|
||||
// Output an Mkv master element. Returns true if the element was written.
|
||||
bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 value, uint64 size);
|
||||
|
||||
// Outputs an Mkv ID, calls |IMkvWriter::ElementStartNotify|, and passes the
|
||||
// ID to |SerializeInt|. Returns 0 on success.
|
||||
int32 WriteID(IMkvWriter* writer, uint64 type);
|
||||
|
||||
// Output an Mkv non-master element. Returns true if the element was written.
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value);
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
|
||||
uint64 size);
|
||||
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value);
|
||||
|
||||
// Output a Mkv Frame. It decides the correct element to write (Block vs
|
||||
// SimpleBlock) based on the parameters of the Frame.
|
||||
uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
|
||||
Cluster* cluster);
|
||||
|
||||
// Output a void element. |size| must be the entire size in bytes that will be
|
||||
// void. The function will calculate the size of the void header and subtract
|
||||
// it from |size|.
|
||||
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size);
|
||||
|
||||
// Returns the version number of the muxer in |major|, |minor|, |build|,
|
||||
// and |revision|.
|
||||
void GetVersion(int32* major, int32* minor, int32* build, int32* revision);
|
||||
|
||||
// Returns a random number to be used for UID, using |seed| to seed
|
||||
// the random-number generator (see POSIX rand_r() for semantics).
|
||||
uint64 MakeUID(unsigned int* seed);
|
||||
|
||||
} // end namespace mkvmuxer
|
||||
|
||||
#endif // MKVMUXERUTIL_HPP
|
||||
13975
mkvparser.cpp
13975
mkvparser.cpp
File diff suppressed because it is too large
Load Diff
1727
mkvparser.hpp
1727
mkvparser.hpp
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mkvparser_2005", "mkvparser_2005.vcproj", "{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
ProjectSection(WebsiteProperties) = preProject
|
||||
Debug.AspNetCompiler.Debug = "True"
|
||||
Release.AspNetCompiler.Debug = "False"
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample_2005", "sample_2005.vcproj", "{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
ProjectSection(WebsiteProperties) = preProject
|
||||
Debug.AspNetCompiler.Debug = "True"
|
||||
Release.AspNetCompiler.Debug = "False"
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.Build.0 = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,176 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="mkvparser"
|
||||
ProjectGUID="{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
RootNamespace="mkvparser"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvparser.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvparser.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.hpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mkvparser", "mkvparser_2008.vcproj", "{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample", "sample_2008.vcproj", "{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4} = {F9128EC6-C008-41AD-B38F-0E70D549D9F4}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.Build.0 = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,178 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="mkvparser"
|
||||
ProjectGUID="{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
RootNamespace="mkvparser"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="131072"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvparser.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvparser.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.hpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
255
mkvreader.cpp
255
mkvreader.cpp
@@ -1,123 +1,132 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvreader.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
MkvReader::MkvReader() :
|
||||
m_file(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
MkvReader::~MkvReader()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
int MkvReader::Open(const char* fileName)
|
||||
{
|
||||
if (fileName == NULL)
|
||||
return -1;
|
||||
|
||||
if (m_file)
|
||||
return -1;
|
||||
|
||||
#ifdef WIN32
|
||||
const errno_t e = fopen_s(&m_file, fileName, "rb");
|
||||
|
||||
if (e)
|
||||
return -1; //error
|
||||
#else
|
||||
m_file = fopen(fileName, "rb");
|
||||
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
int status = _fseeki64(m_file, 0L, SEEK_END);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
|
||||
m_length = _ftelli64(m_file);
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_END);
|
||||
m_length = ftell(m_file);
|
||||
#endif
|
||||
assert(m_length >= 0);
|
||||
|
||||
#ifdef WIN32
|
||||
status = _fseeki64(m_file, 0L, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_SET);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MkvReader::Close()
|
||||
{
|
||||
if (m_file != NULL)
|
||||
{
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int MkvReader::Length(long long* total, long long* available)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (total)
|
||||
*total = m_length;
|
||||
|
||||
if (available)
|
||||
*available = m_length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MkvReader::Read(long long offset, long len, unsigned char* buffer)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (offset >= m_length)
|
||||
return -1;
|
||||
|
||||
#ifdef WIN32
|
||||
const int status = _fseeki64(m_file, offset, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
#else
|
||||
fseek(m_file, offset, SEEK_SET);
|
||||
#endif
|
||||
|
||||
const size_t size = fread(buffer, 1, len, m_file);
|
||||
|
||||
if (size < size_t(len))
|
||||
return -1; //error
|
||||
|
||||
return 0; //success
|
||||
}
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvreader.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace mkvparser {
|
||||
|
||||
MkvReader::MkvReader() : m_file(NULL), reader_owns_file_(true) {}
|
||||
|
||||
MkvReader::MkvReader(FILE* fp) : m_file(fp), reader_owns_file_(false) {
|
||||
GetFileSize();
|
||||
}
|
||||
|
||||
MkvReader::~MkvReader() {
|
||||
if (reader_owns_file_)
|
||||
Close();
|
||||
m_file = NULL;
|
||||
}
|
||||
|
||||
int MkvReader::Open(const char* fileName) {
|
||||
if (fileName == NULL)
|
||||
return -1;
|
||||
|
||||
if (m_file)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
const errno_t e = fopen_s(&m_file, fileName, "rb");
|
||||
|
||||
if (e)
|
||||
return -1; // error
|
||||
#else
|
||||
m_file = fopen(fileName, "rb");
|
||||
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
#endif
|
||||
return !GetFileSize();
|
||||
}
|
||||
|
||||
bool MkvReader::GetFileSize() {
|
||||
if (m_file == NULL)
|
||||
return false;
|
||||
#ifdef _MSC_VER
|
||||
int status = _fseeki64(m_file, 0L, SEEK_END);
|
||||
|
||||
if (status)
|
||||
return false; // error
|
||||
|
||||
m_length = _ftelli64(m_file);
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_END);
|
||||
m_length = ftell(m_file);
|
||||
#endif
|
||||
assert(m_length >= 0);
|
||||
|
||||
if (m_length < 0)
|
||||
return false;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
status = _fseeki64(m_file, 0L, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return false; // error
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_SET);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MkvReader::Close() {
|
||||
if (m_file != NULL) {
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int MkvReader::Length(long long* total, long long* available) {
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (total)
|
||||
*total = m_length;
|
||||
|
||||
if (available)
|
||||
*available = m_length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MkvReader::Read(long long offset, long len, unsigned char* buffer) {
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (offset >= m_length)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
const int status = _fseeki64(m_file, offset, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return -1; // error
|
||||
#else
|
||||
fseek(m_file, offset, SEEK_SET);
|
||||
#endif
|
||||
|
||||
const size_t size = fread(buffer, 1, len, m_file);
|
||||
|
||||
if (size < size_t(len))
|
||||
return -1; // error
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
} // end namespace mkvparser
|
||||
|
||||
@@ -1,34 +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 MKVREADER_HPP
|
||||
#define MKVREADER_HPP
|
||||
|
||||
#include "mkvparser.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
class MkvReader : public mkvparser::IMkvReader
|
||||
{
|
||||
MkvReader(const MkvReader&);
|
||||
MkvReader& operator=(const MkvReader&);
|
||||
public:
|
||||
MkvReader();
|
||||
virtual ~MkvReader();
|
||||
|
||||
int Open(const char*);
|
||||
void Close();
|
||||
bool IsOpen() const;
|
||||
|
||||
virtual int Read(long long position, long length, unsigned char* buffer);
|
||||
virtual int Length(long long* total, long long* available);
|
||||
private:
|
||||
long long m_length;
|
||||
FILE* m_file;
|
||||
};
|
||||
|
||||
#endif //MKVREADER_HPP
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef MKVREADER_HPP
|
||||
#define MKVREADER_HPP
|
||||
|
||||
#include "mkvparser.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
namespace mkvparser {
|
||||
|
||||
class MkvReader : public IMkvReader {
|
||||
public:
|
||||
MkvReader();
|
||||
explicit MkvReader(FILE* fp);
|
||||
virtual ~MkvReader();
|
||||
|
||||
int Open(const char*);
|
||||
void Close();
|
||||
|
||||
virtual int Read(long long position, long length, unsigned char* buffer);
|
||||
virtual int Length(long long* total, long long* available);
|
||||
|
||||
private:
|
||||
MkvReader(const MkvReader&);
|
||||
MkvReader& operator=(const MkvReader&);
|
||||
|
||||
// Determines the size of the file. This is called either by the constructor
|
||||
// or by the Open function depending on file ownership. Returns true on
|
||||
// success.
|
||||
bool GetFileSize();
|
||||
|
||||
long long m_length;
|
||||
FILE* m_file;
|
||||
bool reader_owns_file_;
|
||||
};
|
||||
|
||||
} // end namespace mkvparser
|
||||
|
||||
#endif // MKVREADER_HPP
|
||||
|
||||
90
mkvwriter.cpp
Normal file
90
mkvwriter.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include "mkvwriter.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <share.h> // for _SH_DENYWR
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
|
||||
|
||||
MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {}
|
||||
|
||||
MkvWriter::~MkvWriter() { Close(); }
|
||||
|
||||
int32 MkvWriter::Write(const void* buffer, uint32 length) {
|
||||
if (!file_)
|
||||
return -1;
|
||||
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
|
||||
const size_t bytes_written = fwrite(buffer, 1, length, file_);
|
||||
|
||||
return (bytes_written == length) ? 0 : -1;
|
||||
}
|
||||
|
||||
bool MkvWriter::Open(const char* filename) {
|
||||
if (filename == NULL)
|
||||
return false;
|
||||
|
||||
if (file_)
|
||||
return false;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
file_ = _fsopen(filename, "wb", _SH_DENYWR);
|
||||
#else
|
||||
file_ = fopen(filename, "wb");
|
||||
#endif
|
||||
if (file_ == NULL)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MkvWriter::Close() {
|
||||
if (file_ && writer_owns_file_) {
|
||||
fclose(file_);
|
||||
}
|
||||
file_ = NULL;
|
||||
}
|
||||
|
||||
int64 MkvWriter::Position() const {
|
||||
if (!file_)
|
||||
return 0;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
return _ftelli64(file_);
|
||||
#else
|
||||
return ftell(file_);
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 MkvWriter::Position(int64 position) {
|
||||
if (!file_)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
return _fseeki64(file_, position, SEEK_SET);
|
||||
#else
|
||||
return fseek(file_, position, SEEK_SET);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MkvWriter::Seekable() const { return true; }
|
||||
|
||||
void MkvWriter::ElementStartNotify(uint64, int64) {}
|
||||
|
||||
} // namespace mkvmuxer
|
||||
51
mkvwriter.hpp
Normal file
51
mkvwriter.hpp
Normal file
@@ -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 MKVWRITER_HPP
|
||||
#define MKVWRITER_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "mkvmuxertypes.hpp"
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
// Default implementation of the IMkvWriter interface on Windows.
|
||||
class MkvWriter : public IMkvWriter {
|
||||
public:
|
||||
MkvWriter();
|
||||
explicit MkvWriter(FILE* fp);
|
||||
virtual ~MkvWriter();
|
||||
|
||||
// IMkvWriter interface
|
||||
virtual int64 Position() const;
|
||||
virtual int32 Position(int64 position);
|
||||
virtual bool Seekable() const;
|
||||
virtual int32 Write(const void* buffer, uint32 length);
|
||||
virtual void ElementStartNotify(uint64 element_id, int64 position);
|
||||
|
||||
// Creates and opens a file for writing. |filename| is the name of the file
|
||||
// to open. This function will overwrite the contents of |filename|. Returns
|
||||
// true on success.
|
||||
bool Open(const char* filename);
|
||||
|
||||
// Closes an opened file.
|
||||
void Close();
|
||||
|
||||
private:
|
||||
// File handle to output file.
|
||||
FILE* file_;
|
||||
bool writer_owns_file_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
|
||||
};
|
||||
|
||||
} // end namespace mkvmuxer
|
||||
|
||||
#endif // MKVWRITER_HPP
|
||||
649
sample.cpp
649
sample.cpp
@@ -1,283 +1,366 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
//
|
||||
// This sample application demonstrates how to use the matroska parser
|
||||
// library, which allows clients to handle a matroska format file.
|
||||
|
||||
#include "mkvreader.hpp"
|
||||
#include "mkvparser.hpp"
|
||||
|
||||
static const wchar_t* utf8towcs(const char* str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
//TODO: this probably requires that the locale be
|
||||
//configured somehow:
|
||||
|
||||
const size_t size = mbstowcs(NULL, str, 0);
|
||||
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
|
||||
wchar_t* const val = new wchar_t[size+1];
|
||||
|
||||
mbstowcs(val, str, size);
|
||||
val[size] = L'\0';
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
printf("\t\t\tMkv Parser Sample Application\n");
|
||||
printf("\t\t\tUsage: \n");
|
||||
printf("\t\t\t ./sample [input file] \n");
|
||||
printf("\t\t\t ./sample sample.mkv \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
using namespace mkvparser;
|
||||
|
||||
MkvReader reader;
|
||||
|
||||
if (reader.Open(argv[1]))
|
||||
{
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int maj, min, build, rev;
|
||||
|
||||
GetVersion(maj, min, build, rev);
|
||||
printf("\t\t libmkv verison: %d.%d.%d.%d\n", maj, min, build, rev);
|
||||
|
||||
long long pos = 0;
|
||||
|
||||
EBMLHeader ebmlHeader;
|
||||
|
||||
ebmlHeader.Parse(&reader, pos);
|
||||
|
||||
printf("\t\t\t EBML Header\n");
|
||||
printf("\t\tEBML Version\t\t: %lld\n", ebmlHeader.m_version);
|
||||
printf("\t\tEBML MaxIDLength\t: %lld\n", ebmlHeader.m_maxIdLength);
|
||||
printf("\t\tEBML MaxSizeLength\t: %lld\n", ebmlHeader.m_maxSizeLength);
|
||||
printf("\t\tDoc Type\t\t: %s\n", ebmlHeader.m_docType);
|
||||
printf("\t\tPos\t\t\t: %lld\n", pos);
|
||||
|
||||
mkvparser::Segment* pSegment;
|
||||
|
||||
long long ret = mkvparser::Segment::CreateInstance(&reader, pos, pSegment);
|
||||
if (ret)
|
||||
{
|
||||
printf("\n Segment::CreateInstance() failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = pSegment->Load();
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("\n Segment::Load() failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const SegmentInfo* const pSegmentInfo = pSegment->GetInfo();
|
||||
|
||||
const long long timeCodeScale = pSegmentInfo->GetTimeCodeScale();
|
||||
const long long duration_ns = pSegmentInfo->GetDuration();
|
||||
|
||||
const char* const pTitle_ = pSegmentInfo->GetTitleAsUTF8();
|
||||
const wchar_t* const pTitle = utf8towcs(pTitle_);
|
||||
|
||||
const char* const pMuxingApp_ = pSegmentInfo->GetMuxingAppAsUTF8();
|
||||
const wchar_t* const pMuxingApp = utf8towcs(pMuxingApp_);
|
||||
|
||||
const char* const pWritingApp_ = pSegmentInfo->GetWritingAppAsUTF8();
|
||||
const wchar_t* const pWritingApp = utf8towcs(pWritingApp_);
|
||||
|
||||
printf("\n");
|
||||
printf("\t\t\t Segment Info\n");
|
||||
printf("\t\tTimeCodeScale\t\t: %lld \n", timeCodeScale);
|
||||
printf("\t\tDuration\t\t: %lld\n", duration_ns);
|
||||
|
||||
const double duration_sec = double(duration_ns) / 1000000000;
|
||||
printf("\t\tDuration(secs)\t\t: %7.3lf\n", duration_sec);
|
||||
|
||||
if (pTitle == NULL)
|
||||
printf("\t\tTrack Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tTrack Name\t\t: %ls\n", pTitle);
|
||||
delete [] pTitle;
|
||||
}
|
||||
|
||||
|
||||
if (pMuxingApp == NULL)
|
||||
printf("\t\tMuxing App\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tMuxing App\t\t: %ls\n", pMuxingApp);
|
||||
delete [] pMuxingApp;
|
||||
}
|
||||
|
||||
if (pWritingApp == NULL)
|
||||
printf("\t\tWriting App\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tWriting App\t\t: %ls\n", pWritingApp);
|
||||
delete [] pWritingApp;
|
||||
}
|
||||
|
||||
// pos of segment payload
|
||||
printf("\t\tPosition(Segment)\t: %lld\n", pSegment->m_start);
|
||||
|
||||
// size of segment payload
|
||||
printf("\t\tSize(Segment)\t\t: %lld\n", pSegment->m_size);
|
||||
|
||||
mkvparser::Tracks* const pTracks = pSegment->GetTracks();
|
||||
|
||||
unsigned long i = 0;
|
||||
const unsigned long j = pTracks->GetTracksCount();
|
||||
|
||||
enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
|
||||
|
||||
printf("\n\t\t\t Track Info\n");
|
||||
|
||||
while (i != j)
|
||||
{
|
||||
const Track* const pTrack = pTracks->GetTrackByIndex(i++);
|
||||
|
||||
if (pTrack == NULL)
|
||||
continue;
|
||||
|
||||
const long long trackType = pTrack->GetType();
|
||||
const long long trackNumber = pTrack->GetNumber();
|
||||
const unsigned long long trackUid = pTrack->GetUid();
|
||||
const wchar_t* const pTrackName = utf8towcs(pTrack->GetNameAsUTF8());
|
||||
|
||||
printf("\t\tTrack Type\t\t: %lld\n", trackType);
|
||||
printf("\t\tTrack Number\t\t: %lld\n", trackNumber);
|
||||
printf("\t\tTrack Uid\t\t: %lld\n", trackUid);
|
||||
|
||||
if (pTrackName == NULL)
|
||||
printf("\t\tTrack Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tTrack Name\t\t: %ls \n", pTrackName);
|
||||
delete [] pTrackName;
|
||||
}
|
||||
|
||||
const char* const pCodecId = pTrack->GetCodecId();
|
||||
|
||||
if (pCodecId == NULL)
|
||||
printf("\t\tCodec Id\t\t: NULL\n");
|
||||
else
|
||||
printf("\t\tCodec Id\t\t: %s\n", pCodecId);
|
||||
|
||||
const char* const pCodecName_ = pTrack->GetCodecNameAsUTF8();
|
||||
const wchar_t* const pCodecName = utf8towcs(pCodecName_);
|
||||
|
||||
if (pCodecName == NULL)
|
||||
printf("\t\tCodec Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
printf("\t\tCodec Name\t\t: %ls\n", pCodecName);
|
||||
delete [] pCodecName;
|
||||
}
|
||||
|
||||
if (trackType == VIDEO_TRACK)
|
||||
{
|
||||
const VideoTrack* const pVideoTrack =
|
||||
static_cast<const VideoTrack*>(pTrack);
|
||||
|
||||
const long long width = pVideoTrack->GetWidth();
|
||||
printf("\t\tVideo Width\t\t: %lld\n", width);
|
||||
|
||||
const long long height = pVideoTrack->GetHeight();
|
||||
printf("\t\tVideo Height\t\t: %lld\n", height);
|
||||
|
||||
const double rate = pVideoTrack->GetFrameRate();
|
||||
printf("\t\tVideo Rate\t\t: %f\n",rate);
|
||||
}
|
||||
|
||||
if (trackType == AUDIO_TRACK)
|
||||
{
|
||||
const AudioTrack* const pAudioTrack =
|
||||
static_cast<const AudioTrack*>(pTrack);
|
||||
|
||||
const long long channels = pAudioTrack->GetChannels();
|
||||
printf("\t\tAudio Channels\t\t: %lld\n", channels);
|
||||
|
||||
const long long bitDepth = pAudioTrack->GetBitDepth();
|
||||
printf("\t\tAudio BitDepth\t\t: %lld\n", bitDepth);
|
||||
|
||||
const double sampleRate = pAudioTrack->GetSamplingRate();
|
||||
printf("\t\tAddio Sample Rate\t: %.3f\n", sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\n\t\t\t Cluster Info\n");
|
||||
const unsigned long clusterCount = pSegment->GetCount();
|
||||
|
||||
printf("\t\tCluster Count\t: %ld\n\n", clusterCount);
|
||||
|
||||
if (clusterCount == 0)
|
||||
{
|
||||
printf("\t\tSegment has no clusters.\n");
|
||||
delete pSegment;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const mkvparser::Cluster* pCluster = pSegment->GetFirst();
|
||||
|
||||
while ((pCluster != NULL) && !pCluster->EOS())
|
||||
{
|
||||
const long long timeCode = pCluster->GetTimeCode();
|
||||
printf("\t\tCluster Time Code\t: %lld\n", timeCode);
|
||||
|
||||
const long long time_ns = pCluster->GetTime();
|
||||
printf("\t\tCluster Time (ns)\t: %lld\n", time_ns);
|
||||
|
||||
const BlockEntry* pBlockEntry = pCluster->GetFirst();
|
||||
|
||||
while ((pBlockEntry != NULL) && !pBlockEntry->EOS())
|
||||
{
|
||||
const Block* const pBlock = pBlockEntry->GetBlock();
|
||||
const long long trackNum = pBlock->GetTrackNumber();
|
||||
const unsigned long tn = static_cast<unsigned long>(trackNum);
|
||||
const Track* const pTrack = pTracks->GetTrackByNumber(tn);
|
||||
const long long trackType = pTrack->GetType();
|
||||
const int frameCount = pBlock->GetFrameCount();
|
||||
const long long time_ns = pBlock->GetTime(pCluster);
|
||||
|
||||
printf("\t\t\tBlock\t\t:%s,%s,%15lld\n",
|
||||
(trackType == VIDEO_TRACK) ? "V" : "A",
|
||||
pBlock->IsKey() ? "I" : "P",
|
||||
time_ns);
|
||||
|
||||
for (int i = 0; i < frameCount; ++i)
|
||||
{
|
||||
const Block::Frame& theFrame = pBlock->GetFrame(i);
|
||||
const long size = theFrame.len;
|
||||
const long long offset = theFrame.pos;
|
||||
printf("\t\t\t %15ld,%15llx\n", size, offset);
|
||||
}
|
||||
|
||||
pBlockEntry = pCluster->GetNext(pBlockEntry);
|
||||
}
|
||||
|
||||
pCluster = pSegment->GetNext(pCluster);
|
||||
}
|
||||
|
||||
delete pSegment;
|
||||
return 0;
|
||||
|
||||
}
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
//
|
||||
// This sample application demonstrates how to use the Matroska parser
|
||||
// library, which allows clients to handle a Matroska format file.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "mkvreader.hpp"
|
||||
#include "mkvparser.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable MSVC warnings that suggest making code non-portable.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
static const wchar_t* utf8towcs(const char* str) {
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
// TODO: this probably requires that the locale be
|
||||
// configured somehow:
|
||||
|
||||
const size_t size = mbstowcs(NULL, str, 0);
|
||||
|
||||
if (size == 0 || 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;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc == 1) {
|
||||
printf("\t\t\tMkv Parser Sample Application\n");
|
||||
printf("\t\t\tUsage: \n");
|
||||
printf("\t\t\t ./sample [input file] \n");
|
||||
printf("\t\t\t ./sample sample.mkv \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
using namespace mkvparser;
|
||||
|
||||
MkvReader reader;
|
||||
|
||||
if (reader.Open(argv[1])) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int maj, min, build, rev;
|
||||
|
||||
GetVersion(maj, min, build, rev);
|
||||
printf("\t\t libmkv verison: %d.%d.%d.%d\n", maj, min, build, rev);
|
||||
|
||||
long long pos = 0;
|
||||
|
||||
EBMLHeader ebmlHeader;
|
||||
|
||||
long long ret = ebmlHeader.Parse(&reader, pos);
|
||||
if (ret < 0) {
|
||||
printf("\n EBMLHeader::Parse() failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 -1;
|
||||
}
|
||||
|
||||
const std::auto_ptr<seg_t> pSegment(pSegment_);
|
||||
|
||||
ret = pSegment->Load();
|
||||
if (ret < 0) {
|
||||
printf("\n Segment::Load() failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const SegmentInfo* const pSegmentInfo = pSegment->GetInfo();
|
||||
if (pSegmentInfo == NULL) {
|
||||
printf("\n Segment::GetInfo() failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
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 VideoTrack* const pVideoTrack =
|
||||
static_cast<const VideoTrack*>(pTrack);
|
||||
|
||||
const long long width = pVideoTrack->GetWidth();
|
||||
printf("\t\tVideo Width\t\t: %lld\n", width);
|
||||
|
||||
const long long height = pVideoTrack->GetHeight();
|
||||
printf("\t\tVideo Height\t\t: %lld\n", height);
|
||||
|
||||
const double rate = pVideoTrack->GetFrameRate();
|
||||
printf("\t\tVideo Rate\t\t: %f\n", rate);
|
||||
}
|
||||
|
||||
if (trackType == mkvparser::Track::kAudio) {
|
||||
const AudioTrack* const pAudioTrack =
|
||||
static_cast<const AudioTrack*>(pTrack);
|
||||
|
||||
const long long channels = pAudioTrack->GetChannels();
|
||||
printf("\t\tAudio Channels\t\t: %lld\n", channels);
|
||||
|
||||
const long long bitDepth = pAudioTrack->GetBitDepth();
|
||||
printf("\t\tAudio BitDepth\t\t: %lld\n", bitDepth);
|
||||
|
||||
const double sampleRate = pAudioTrack->GetSamplingRate();
|
||||
printf("\t\tAddio Sample Rate\t: %.3f\n", sampleRate);
|
||||
|
||||
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 -1;
|
||||
}
|
||||
|
||||
const mkvparser::Cluster* pCluster = pSegment->GetFirst();
|
||||
|
||||
while ((pCluster != NULL) && !pCluster->EOS()) {
|
||||
const long long timeCode = pCluster->GetTimeCode();
|
||||
printf("\t\tCluster Time Code\t: %lld\n", timeCode);
|
||||
|
||||
const long long time_ns = pCluster->GetTime();
|
||||
printf("\t\tCluster Time (ns)\t: %lld\n", time_ns);
|
||||
|
||||
const BlockEntry* pBlockEntry;
|
||||
|
||||
long status = pCluster->GetFirst(pBlockEntry);
|
||||
|
||||
if (status < 0) // error
|
||||
{
|
||||
printf("\t\tError parsing first block of cluster\n");
|
||||
fflush(stdout);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((pBlockEntry != NULL) && !pBlockEntry->EOS()) {
|
||||
const Block* const pBlock = pBlockEntry->GetBlock();
|
||||
const long long trackNum = pBlock->GetTrackNumber();
|
||||
const unsigned long tn = static_cast<unsigned long>(trackNum);
|
||||
const Track* const pTrack = pTracks->GetTrackByNumber(tn);
|
||||
|
||||
if (pTrack == NULL)
|
||||
printf("\t\t\tBlock\t\t:UNKNOWN TRACK TYPE\n");
|
||||
else {
|
||||
const long long trackType = pTrack->GetType();
|
||||
const int frameCount = pBlock->GetFrameCount();
|
||||
const long long time_ns = pBlock->GetTime(pCluster);
|
||||
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 Block::Frame& theFrame = pBlock->GetFrame(i);
|
||||
const long size = theFrame.len;
|
||||
const long long offset = theFrame.pos;
|
||||
printf("\t\t\t %15ld,%15llx\n", size, offset);
|
||||
}
|
||||
}
|
||||
|
||||
status = pCluster->GetNext(pBlockEntry, pBlockEntry);
|
||||
|
||||
if (status < 0) {
|
||||
printf("\t\t\tError parsing next block of cluster\n");
|
||||
fflush(stdout);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
pCluster = pSegment->GetNext(pCluster);
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="sample"
|
||||
ProjectGUID="{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
RootNamespace="sample"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Debug\mkvparser.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -1,193 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="sample"
|
||||
ProjectGUID="{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
RootNamespace="sample"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="131072"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Debug\mkvparser.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Release\mkvparser.lib"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
583
sample_muxer.cpp
Normal file
583
sample_muxer.cpp
Normal file
@@ -0,0 +1,583 @@
|
||||
// Copyright (c) 2011 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
// libwebm parser includes
|
||||
#include "mkvreader.hpp"
|
||||
#include "mkvparser.hpp"
|
||||
|
||||
// libwebm muxer includes
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "mkvwriter.hpp"
|
||||
#include "mkvmuxerutil.hpp"
|
||||
|
||||
#include "sample_muxer_metadata.h"
|
||||
|
||||
using mkvmuxer::int64;
|
||||
using mkvmuxer::uint64;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable MSVC warnings that suggest making code non-portable.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void Usage() {
|
||||
printf("Usage: sample_muxer -i input -o output [options]\n");
|
||||
printf("\n");
|
||||
printf("Main options:\n");
|
||||
printf(" -h | -? show help\n");
|
||||
printf(" -video <int> >0 outputs video\n");
|
||||
printf(" -audio <int> >0 outputs audio\n");
|
||||
printf(" -live <int> >0 puts the muxer into live mode\n");
|
||||
printf(" 0 puts the muxer into file mode\n");
|
||||
printf(" -output_cues <int> >0 outputs cues element\n");
|
||||
printf(" -cues_on_video_track <int> >0 outputs cues on video track\n");
|
||||
printf(" -cues_on_audio_track <int> >0 outputs cues on audio track\n");
|
||||
printf(" -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("\n");
|
||||
printf("Video options:\n");
|
||||
printf(" -display_width <int> Display width in pixels\n");
|
||||
printf(" -display_height <int> Display height in pixels\n");
|
||||
printf(" -stereo_mode <int> 3D video mode\n");
|
||||
printf("\n");
|
||||
printf("Cues options:\n");
|
||||
printf(" -output_cues_block_number <int> >0 outputs cue block number\n");
|
||||
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
|
||||
}
|
||||
|
||||
} // 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 max_cluster_duration = 0;
|
||||
uint64 max_cluster_size = 0;
|
||||
bool switch_tracks = false;
|
||||
int audio_track_number = 0; // 0 tells muxer to decide.
|
||||
int video_track_number = 0; // 0 tells muxer to decide.
|
||||
bool chunking = false;
|
||||
bool copy_tags = false;
|
||||
const char* chunk_name = NULL;
|
||||
|
||||
bool output_cues_block_number = true;
|
||||
|
||||
uint64 display_width = 0;
|
||||
uint64 display_height = 0;
|
||||
uint64 stereo_mode = 0;
|
||||
|
||||
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>(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 = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-video_track_number", argv[i]) && i < argc_check) {
|
||||
video_track_number = 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("-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("-stereo_mode", argv[i]) && i < argc_check) {
|
||||
stereo_mode = 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::auto_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;
|
||||
|
||||
char* temp_file = tmpnam(NULL);
|
||||
if (!writer.Open(cues_before_clusters ? temp_file : output)) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Set Segment element attributes
|
||||
mkvmuxer::Segment muxer_segment;
|
||||
|
||||
if (!muxer_segment.Init(&writer)) {
|
||||
printf("\n Could not initialize muxer segment!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (live_mode)
|
||||
muxer_segment.set_mode(mkvmuxer::Segment::kLive);
|
||||
else
|
||||
muxer_segment.set_mode(mkvmuxer::Segment::kFile);
|
||||
|
||||
if (chunking)
|
||||
muxer_segment.SetChunking(true, chunk_name);
|
||||
|
||||
if (max_cluster_duration > 0)
|
||||
muxer_segment.set_max_cluster_duration(max_cluster_duration);
|
||||
if (max_cluster_size > 0)
|
||||
muxer_segment.set_max_cluster_size(max_cluster_size);
|
||||
muxer_segment.OutputCues(output_cues);
|
||||
|
||||
// Set SegmentInfo element attributes
|
||||
mkvmuxer::SegmentInfo* const info = muxer_segment.GetSegmentInfo();
|
||||
info->set_timecode_scale(timeCodeScale);
|
||||
info->set_writing_app("sample_muxer");
|
||||
|
||||
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 vid_track = 0; // no track added
|
||||
uint64 aud_track = 0; // no track added
|
||||
|
||||
using mkvparser::Track;
|
||||
|
||||
while (i != parser_tracks->GetTracksCount()) {
|
||||
int track_num = i++;
|
||||
if (switch_tracks)
|
||||
track_num = i % parser_tracks->GetTracksCount();
|
||||
|
||||
const mkvparser::Track* const parser_track =
|
||||
parser_tracks->GetTrackByIndex(track_num);
|
||||
|
||||
if (parser_track == NULL)
|
||||
continue;
|
||||
|
||||
// TODO(fgalligan): Add support for language to parser.
|
||||
const char* const track_name = parser_track->GetNameAsUTF8();
|
||||
|
||||
const long long track_type = parser_track->GetType();
|
||||
|
||||
if (track_type == 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 (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 (stereo_mode > 0)
|
||||
video->SetStereoMode(stereo_mode);
|
||||
|
||||
const double rate = pVideoTrack->GetFrameRate();
|
||||
if (rate > 0.0) {
|
||||
video->set_frame_rate(rate);
|
||||
}
|
||||
} else if (track_type == 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;
|
||||
int data_len = 0;
|
||||
|
||||
const mkvparser::Cluster* cluster = parser_segment->GetFirst();
|
||||
|
||||
while ((cluster != NULL) && !cluster->EOS()) {
|
||||
const mkvparser::BlockEntry* block_entry;
|
||||
|
||||
long status = cluster->GetFirst(block_entry);
|
||||
|
||||
if (status) {
|
||||
printf("\n Could not get first block of cluster.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((block_entry != NULL) && !block_entry->EOS()) {
|
||||
const mkvparser::Block* const block = block_entry->GetBlock();
|
||||
const long long trackNum = block->GetTrackNumber();
|
||||
const mkvparser::Track* const parser_track =
|
||||
parser_tracks->GetTrackByNumber(static_cast<unsigned long>(trackNum));
|
||||
|
||||
// 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 (!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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
387
sample_muxer_metadata.cc
Normal file
387
sample_muxer_metadata.cc
Normal file
@@ -0,0 +1,387 @@
|
||||
// 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 <string>
|
||||
#include "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);
|
||||
|
||||
mkvmuxer::uint64 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(mkvmuxer::int64 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 mkvmuxer::uint64 start_time_ns = start_time_ms * kNsPerMs;
|
||||
const mkvmuxer::uint64 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, mkvmuxer::uint64* 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 */,
|
||||
mkvmuxer::uint64 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 mkvmuxer::int64 start_ms = cue.start_time.presentation();
|
||||
|
||||
// Cue start time expressed in nanoseconds (MKV time)
|
||||
const mkvmuxer::int64 start_ns = start_ms * 1000000;
|
||||
|
||||
// Cue stop time expressed in milliseconds
|
||||
const mkvmuxer::int64 stop_ms = cue.stop_time.presentation();
|
||||
|
||||
// Cue stop time expressed in nanonseconds
|
||||
const mkvmuxer::int64 stop_ns = stop_ms * 1000000;
|
||||
|
||||
// Metadata blocks always specify the block duration.
|
||||
const mkvmuxer::int64 duration_ns = stop_ns - start_ns;
|
||||
|
||||
std::string frame;
|
||||
MakeFrame(cue, &frame);
|
||||
|
||||
typedef const mkvmuxer::uint8* data_t;
|
||||
const data_t buf = reinterpret_cast<data_t>(frame.data());
|
||||
const mkvmuxer::uint64 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);
|
||||
}
|
||||
128
sample_muxer_metadata.h
Normal file
128
sample_muxer_metadata.h
Normal file
@@ -0,0 +1,128 @@
|
||||
// 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 <list>
|
||||
#include <set>
|
||||
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "webvttparser.h"
|
||||
|
||||
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(mkvmuxer::int64 time_ns);
|
||||
|
||||
private:
|
||||
typedef libwebvtt::Cue cue_t;
|
||||
|
||||
// Used to sort cues as they are loaded.
|
||||
struct SortableCue {
|
||||
bool operator>(mkvmuxer::int64 time_ns) const {
|
||||
// Cue start time expressed in milliseconds
|
||||
const mkvmuxer::int64 start_ms = cue.start_time.presentation();
|
||||
|
||||
// Cue start time expressed in nanoseconds (MKV time)
|
||||
const mkvmuxer::int64 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;
|
||||
|
||||
mkvmuxer::uint64 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, mkvmuxer::uint64* 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, mkvmuxer::uint64 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
|
||||
999
vttdemux.cc
Normal file
999
vttdemux.cc
Normal file
@@ -0,0 +1,999 @@
|
||||
// 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 <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "./mkvparser.hpp"
|
||||
#include "./mkvreader.hpp"
|
||||
#include "./webvttparser.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable MSVC warnings that suggest making code non-portable.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace vttdemux {
|
||||
|
||||
typedef long long mkvtime_t; // NOLINT
|
||||
typedef long long mkvpos_t; // NOLINT
|
||||
typedef std::auto_ptr<mkvparser::Segment> segment_ptr_t;
|
||||
|
||||
// WebVTT metadata tracks have a type (encoded in the CodecID for the track).
|
||||
// We use |type| to synthesize a filename for the out-of-band WebVTT |file|.
|
||||
struct MetadataInfo {
|
||||
enum Type { kSubtitles, kCaptions, kDescriptions, kMetadata, kChapters } type;
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
// We use a map, indexed by track number, to collect information about
|
||||
// each track in the input file.
|
||||
typedef std::map<long, MetadataInfo> metadata_map_t; // NOLINT
|
||||
|
||||
// The distinguished key value we use to store the chapters
|
||||
// information in the metadata map.
|
||||
enum { kChaptersKey = 0 };
|
||||
|
||||
// The data from the original WebVTT Cue is stored as a WebM block.
|
||||
// The FrameParser is used to parse the lines of text out from the
|
||||
// block, in order to reconstruct the original WebVTT Cue.
|
||||
class FrameParser : public libwebvtt::LineReader {
|
||||
public:
|
||||
// Bind the FrameParser instance to a WebM block.
|
||||
explicit FrameParser(const mkvparser::BlockGroup* block_group);
|
||||
virtual ~FrameParser();
|
||||
|
||||
// The Webm block (group) to which this instance is bound. We
|
||||
// treat the payload of the block as a stream of characters.
|
||||
const mkvparser::BlockGroup* const block_group_;
|
||||
|
||||
protected:
|
||||
// Read the next character from the character stream (the payload
|
||||
// of the WebM block). We increment the stream pointer |pos_| as
|
||||
// each character from the stream is consumed.
|
||||
virtual int GetChar(char* c);
|
||||
|
||||
// End-of-line handling requires that we put a character back into
|
||||
// the stream. Here we need only decrement the stream pointer |pos_|
|
||||
// to unconsume the character.
|
||||
virtual void UngetChar(char c);
|
||||
|
||||
// The current position in the character stream (the payload of the block).
|
||||
mkvpos_t pos_;
|
||||
|
||||
// The position of the end of the character stream. When the current
|
||||
// position |pos_| equals the end position |pos_end_|, the entire
|
||||
// stream (block payload) has been consumed and end-of-stream is indicated.
|
||||
mkvpos_t pos_end_;
|
||||
|
||||
private:
|
||||
// Disable copy ctor and copy assign
|
||||
FrameParser(const FrameParser&);
|
||||
FrameParser& operator=(const FrameParser&);
|
||||
};
|
||||
|
||||
// The data from the original WebVTT Cue is stored as an MKV Chapters
|
||||
// Atom element (the cue payload is stored as a Display sub-element).
|
||||
// The ChapterAtomParser is used to parse the lines of text out from
|
||||
// the String sub-element of the Display element (though it would be
|
||||
// admittedly odd if there were more than one line).
|
||||
class ChapterAtomParser : public libwebvtt::LineReader {
|
||||
public:
|
||||
explicit ChapterAtomParser(const mkvparser::Chapters::Display* display);
|
||||
virtual ~ChapterAtomParser();
|
||||
|
||||
const mkvparser::Chapters::Display* const display_;
|
||||
|
||||
protected:
|
||||
// Read the next character from the character stream (the title
|
||||
// member of the atom's display). We increment the stream pointer
|
||||
// |str_| as each character from the stream is consumed.
|
||||
virtual int GetChar(char* c);
|
||||
|
||||
// End-of-line handling requires that we put a character back into
|
||||
// the stream. Here we need only decrement the stream pointer |str_|
|
||||
// to unconsume the character.
|
||||
virtual void UngetChar(char c);
|
||||
|
||||
// The current position in the character stream (the title of the
|
||||
// atom's display).
|
||||
const char* str_;
|
||||
|
||||
// The position of the end of the character stream. When the current
|
||||
// position |str_| equals the end position |str_end_|, the entire
|
||||
// stream (title of the display) has been consumed and end-of-stream
|
||||
// is indicated.
|
||||
const char* str_end_;
|
||||
|
||||
private:
|
||||
ChapterAtomParser(const ChapterAtomParser&);
|
||||
ChapterAtomParser& operator=(const ChapterAtomParser&);
|
||||
};
|
||||
|
||||
// Parse the EBML header of the WebM input file, to determine whether we
|
||||
// actually have a WebM file. Returns false if this is not a WebM file.
|
||||
bool ParseHeader(mkvparser::IMkvReader* reader, mkvpos_t* pos);
|
||||
|
||||
// Parse the Segment of the input file and load all of its clusters.
|
||||
// Returns false if there was an error parsing the file.
|
||||
bool ParseSegment(mkvparser::IMkvReader* reader, mkvpos_t pos,
|
||||
segment_ptr_t* segment);
|
||||
|
||||
// If |segment| has a Chapters element (in which case, there will be a
|
||||
// corresponding entry in |metadata_map|), convert the MKV chapters to
|
||||
// WebVTT chapter cues and write them to the output file. Returns
|
||||
// false on error.
|
||||
bool WriteChaptersFile(const metadata_map_t& metadata_map,
|
||||
const mkvparser::Segment* segment);
|
||||
|
||||
// Convert an MKV Chapters Atom to a WebVTT cue and write it to the
|
||||
// output |file|. Returns false on error.
|
||||
bool WriteChaptersCue(FILE* file, const mkvparser::Chapters* chapters,
|
||||
const mkvparser::Chapters::Atom* atom,
|
||||
const mkvparser::Chapters::Display* display);
|
||||
|
||||
// Write the Cue Identifier line of the WebVTT cue, if it's present.
|
||||
// Returns false on error.
|
||||
bool WriteChaptersCueIdentifier(FILE* file,
|
||||
const mkvparser::Chapters::Atom* atom);
|
||||
|
||||
// Use the timecodes from the chapters |atom| to write just the
|
||||
// timings line of the WebVTT cue. Returns false on error.
|
||||
bool WriteChaptersCueTimings(FILE* file, const mkvparser::Chapters* chapters,
|
||||
const mkvparser::Chapters::Atom* atom);
|
||||
|
||||
// Parse the String sub-element of the |display| and write the payload
|
||||
// of the WebVTT cue. Returns false on error.
|
||||
bool WriteChaptersCuePayload(FILE* file,
|
||||
const mkvparser::Chapters::Display* display);
|
||||
|
||||
// Iterate over the tracks of the input file (and any chapters
|
||||
// element) and cache information about each metadata track.
|
||||
void BuildMap(const mkvparser::Segment* segment, metadata_map_t* metadata_map);
|
||||
|
||||
// For each track listed in the cache, synthesize its output filename
|
||||
// and open a file handle that designates the out-of-band file.
|
||||
// Returns false if we were unable to open an output file for a track.
|
||||
bool OpenFiles(metadata_map_t* metadata_map, const char* filename);
|
||||
|
||||
// Close the file handle for each track in the cache.
|
||||
void CloseFiles(metadata_map_t* metadata_map);
|
||||
|
||||
// Iterate over the clusters of the input file, and write a WebVTT cue
|
||||
// for each metadata block. Returns false if processing of a cluster
|
||||
// failed.
|
||||
bool WriteFiles(const metadata_map_t& m, mkvparser::Segment* s);
|
||||
|
||||
// Write the WebVTT header for each track in the cache. We do this
|
||||
// immediately before writing the actual WebVTT cues. Returns false
|
||||
// if the write failed.
|
||||
bool InitializeFiles(const metadata_map_t& metadata_map);
|
||||
|
||||
// Iterate over the blocks of the |cluster|, writing a WebVTT cue to
|
||||
// its associated output file for each block of metadata. Returns
|
||||
// false if processing a block failed, or there was a parse error.
|
||||
bool ProcessCluster(const metadata_map_t& metadata_map,
|
||||
const mkvparser::Cluster* cluster);
|
||||
|
||||
// Look up this track number in the cache, and if found (meaning this
|
||||
// is a metadata track), write a WebVTT cue to the associated output
|
||||
// file. Returns false if writing the WebVTT cue failed.
|
||||
bool ProcessBlockEntry(const metadata_map_t& metadata_map,
|
||||
const mkvparser::BlockEntry* block_entry);
|
||||
|
||||
// Parse the lines of text from the |block_group| to reconstruct the
|
||||
// original WebVTT cue, and write it to the associated output |file|.
|
||||
// Returns false if there was an error writing to the output file.
|
||||
bool WriteCue(FILE* file, const mkvparser::BlockGroup* block_group);
|
||||
|
||||
// Consume a line of text from the character stream, and if the line
|
||||
// is not empty write the cue identifier to the associated output
|
||||
// file. Returns false if there was an error writing to the file.
|
||||
bool WriteCueIdentifier(FILE* f, FrameParser* parser);
|
||||
|
||||
// Consume a line of text from the character stream (which holds any
|
||||
// cue settings) and write the cue timings line for this cue to the
|
||||
// associated output file. Returns false if there was an error
|
||||
// writing to the file.
|
||||
bool WriteCueTimings(FILE* f, FrameParser* parser);
|
||||
|
||||
// Write the timestamp (representating either the start time or stop
|
||||
// time of the cue) to the output file. Returns false if there was an
|
||||
// error writing to the file.
|
||||
bool WriteCueTime(FILE* f, mkvtime_t time_ns);
|
||||
|
||||
// Consume the remaining lines of text from the character stream
|
||||
// (these lines are the actual payload of the WebVTT cue), and write
|
||||
// them to the associated output file. Returns false if there was an
|
||||
// error writing to the file.
|
||||
bool WriteCuePayload(FILE* f, FrameParser* parser);
|
||||
} // namespace vttdemux
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("usage: vttdemux <webmfile>\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
const char* const filename = argv[1];
|
||||
mkvparser::MkvReader reader;
|
||||
|
||||
int e = reader.Open(filename);
|
||||
|
||||
if (e) { // error
|
||||
printf("unable to open file\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
vttdemux::mkvpos_t pos;
|
||||
|
||||
if (!vttdemux::ParseHeader(&reader, &pos))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
vttdemux::segment_ptr_t segment_ptr;
|
||||
|
||||
if (!vttdemux::ParseSegment(&reader, pos, &segment_ptr))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
vttdemux::metadata_map_t metadata_map;
|
||||
|
||||
BuildMap(segment_ptr.get(), &metadata_map);
|
||||
|
||||
if (metadata_map.empty()) {
|
||||
printf("no WebVTT metadata found\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!OpenFiles(&metadata_map, filename)) {
|
||||
CloseFiles(&metadata_map); // nothing to flush, so not strictly necessary
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!WriteFiles(metadata_map, segment_ptr.get())) {
|
||||
CloseFiles(&metadata_map); // might as well flush what we do have
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
CloseFiles(&metadata_map);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
namespace vttdemux {
|
||||
|
||||
FrameParser::FrameParser(const mkvparser::BlockGroup* block_group)
|
||||
: block_group_(block_group) {
|
||||
const mkvparser::Block* const block = block_group->GetBlock();
|
||||
const mkvparser::Block::Frame& f = block->GetFrame(0);
|
||||
|
||||
// The beginning and end of the character stream corresponds to the
|
||||
// position of this block's frame within the WebM input file.
|
||||
|
||||
pos_ = f.pos;
|
||||
pos_end_ = f.pos + f.len;
|
||||
}
|
||||
|
||||
FrameParser::~FrameParser() {}
|
||||
|
||||
int FrameParser::GetChar(char* c) {
|
||||
if (pos_ >= pos_end_) // end-of-stream
|
||||
return 1; // per the semantics of libwebvtt::Reader::GetChar
|
||||
|
||||
const mkvparser::Cluster* const cluster = block_group_->GetCluster();
|
||||
const mkvparser::Segment* const segment = cluster->m_pSegment;
|
||||
mkvparser::IMkvReader* const reader = segment->m_pReader;
|
||||
|
||||
unsigned char* const buf = reinterpret_cast<unsigned char*>(c);
|
||||
const int result = reader->Read(pos_, 1, buf);
|
||||
|
||||
if (result < 0) // error
|
||||
return -1;
|
||||
|
||||
++pos_; // consume this character in the stream
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FrameParser::UngetChar(char /* c */) {
|
||||
// All we need to do here is decrement the position in the stream.
|
||||
// The next time GetChar is called the same character will be
|
||||
// re-read from the input file.
|
||||
--pos_;
|
||||
}
|
||||
|
||||
ChapterAtomParser::ChapterAtomParser(
|
||||
const mkvparser::Chapters::Display* display)
|
||||
: display_(display) {
|
||||
str_ = display->GetString();
|
||||
const size_t len = strlen(str_);
|
||||
str_end_ = str_ + len;
|
||||
}
|
||||
|
||||
ChapterAtomParser::~ChapterAtomParser() {}
|
||||
|
||||
int ChapterAtomParser::GetChar(char* c) {
|
||||
if (str_ >= str_end_) // end-of-stream
|
||||
return 1; // per the semantics of libwebvtt::Reader::GetChar
|
||||
|
||||
*c = *str_++; // consume this character in the stream
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ChapterAtomParser::UngetChar(char /* c */) {
|
||||
// All we need to do here is decrement the position in the stream.
|
||||
// The next time GetChar is called the same character will be
|
||||
// re-read from the input file.
|
||||
--str_;
|
||||
}
|
||||
|
||||
} // namespace vttdemux
|
||||
|
||||
bool vttdemux::ParseHeader(mkvparser::IMkvReader* reader, mkvpos_t* pos) {
|
||||
mkvparser::EBMLHeader h;
|
||||
const mkvpos_t status = h.Parse(reader, *pos);
|
||||
|
||||
if (status) {
|
||||
printf("error parsing EBML header\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (h.m_docType == NULL || strcmp(h.m_docType, "webm") != 0) {
|
||||
printf("bad doctype\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // success
|
||||
}
|
||||
|
||||
bool vttdemux::ParseSegment(mkvparser::IMkvReader* reader, mkvpos_t pos,
|
||||
segment_ptr_t* segment_ptr) {
|
||||
// We first create the segment object.
|
||||
|
||||
mkvparser::Segment* p;
|
||||
const mkvpos_t create = mkvparser::Segment::CreateInstance(reader, pos, p);
|
||||
|
||||
if (create) {
|
||||
printf("error parsing segment element\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
segment_ptr->reset(p);
|
||||
|
||||
// Now parse all of the segment's sub-elements, in toto.
|
||||
|
||||
const long status = p->Load(); // NOLINT
|
||||
|
||||
if (status < 0) {
|
||||
printf("error loading segment\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vttdemux::BuildMap(const mkvparser::Segment* segment,
|
||||
metadata_map_t* map_ptr) {
|
||||
metadata_map_t& m = *map_ptr;
|
||||
m.clear();
|
||||
|
||||
if (segment->GetChapters()) {
|
||||
MetadataInfo info;
|
||||
info.file = NULL;
|
||||
info.type = MetadataInfo::kChapters;
|
||||
|
||||
m[kChaptersKey] = info;
|
||||
}
|
||||
|
||||
const mkvparser::Tracks* const tt = segment->GetTracks();
|
||||
if (tt == NULL)
|
||||
return;
|
||||
|
||||
const long tc = tt->GetTracksCount(); // NOLINT
|
||||
if (tc <= 0)
|
||||
return;
|
||||
|
||||
// Iterate over the tracks in the intput file. We determine whether
|
||||
// a track holds metadata by inspecting its CodecID.
|
||||
|
||||
for (long idx = 0; idx < tc; ++idx) { // NOLINT
|
||||
const mkvparser::Track* const t = tt->GetTrackByIndex(idx);
|
||||
|
||||
if (t == NULL) // weird
|
||||
continue;
|
||||
|
||||
const long tn = t->GetNumber(); // NOLINT
|
||||
|
||||
if (tn <= 0) // weird
|
||||
continue;
|
||||
|
||||
const char* const codec_id = t->GetCodecId();
|
||||
|
||||
if (codec_id == NULL) // weird
|
||||
continue;
|
||||
|
||||
MetadataInfo info;
|
||||
info.file = NULL;
|
||||
|
||||
if (strcmp(codec_id, "D_WEBVTT/SUBTITLES") == 0) {
|
||||
info.type = MetadataInfo::kSubtitles;
|
||||
} else if (strcmp(codec_id, "D_WEBVTT/CAPTIONS") == 0) {
|
||||
info.type = MetadataInfo::kCaptions;
|
||||
} else if (strcmp(codec_id, "D_WEBVTT/DESCRIPTIONS") == 0) {
|
||||
info.type = MetadataInfo::kDescriptions;
|
||||
} else if (strcmp(codec_id, "D_WEBVTT/METADATA") == 0) {
|
||||
info.type = MetadataInfo::kMetadata;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
m[tn] = info; // create an entry in the cache for this track
|
||||
}
|
||||
}
|
||||
|
||||
bool vttdemux::OpenFiles(metadata_map_t* metadata_map, const char* filename) {
|
||||
if (metadata_map == NULL || metadata_map->empty())
|
||||
return false;
|
||||
|
||||
if (filename == NULL)
|
||||
return false;
|
||||
|
||||
// Find the position of the filename extension. We synthesize the
|
||||
// output filename from the directory path and basename of the input
|
||||
// filename.
|
||||
|
||||
const char* const ext = strrchr(filename, '.');
|
||||
|
||||
if (ext == NULL) // TODO(matthewjheaney): liberalize?
|
||||
return false;
|
||||
|
||||
// Remember whether a track of this type has already been seen (the
|
||||
// map key) by keeping a count (the map item). We quality the
|
||||
// output filename with the track number if there is more than one
|
||||
// track having a given type.
|
||||
|
||||
std::map<MetadataInfo::Type, int> exists;
|
||||
|
||||
typedef metadata_map_t::iterator iter_t;
|
||||
|
||||
metadata_map_t& m = *metadata_map;
|
||||
const iter_t ii = m.begin();
|
||||
const iter_t j = m.end();
|
||||
|
||||
// Make a first pass over the cache to determine whether there is
|
||||
// more than one track corresponding to a given metadata type.
|
||||
|
||||
iter_t i = ii;
|
||||
while (i != j) {
|
||||
const metadata_map_t::value_type& v = *i++;
|
||||
const MetadataInfo& info = v.second;
|
||||
const MetadataInfo::Type type = info.type;
|
||||
++exists[type];
|
||||
}
|
||||
|
||||
// Make a second pass over the cache, synthesizing the filename of
|
||||
// each output file (from the input file basename, the input track
|
||||
// metadata type, and its track number if necessary), and then
|
||||
// opening a WebVTT output file having that filename.
|
||||
|
||||
i = ii;
|
||||
while (i != j) {
|
||||
metadata_map_t::value_type& v = *i++;
|
||||
MetadataInfo& info = v.second;
|
||||
const MetadataInfo::Type type = info.type;
|
||||
|
||||
// Start with the basename of the input file.
|
||||
|
||||
string name(filename, ext);
|
||||
|
||||
// Next append the metadata kind.
|
||||
|
||||
switch (type) {
|
||||
case MetadataInfo::kSubtitles:
|
||||
name += "_SUBTITLES";
|
||||
break;
|
||||
|
||||
case MetadataInfo::kCaptions:
|
||||
name += "_CAPTIONS";
|
||||
break;
|
||||
|
||||
case MetadataInfo::kDescriptions:
|
||||
name += "_DESCRIPTIONS";
|
||||
break;
|
||||
|
||||
case MetadataInfo::kMetadata:
|
||||
name += "_METADATA";
|
||||
break;
|
||||
|
||||
case MetadataInfo::kChapters:
|
||||
name += "_CHAPTERS";
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is more than one metadata track having a given type
|
||||
// (the WebVTT-in-WebM spec doesn't preclude this), then qualify
|
||||
// the output filename with the input track number.
|
||||
|
||||
if (exists[type] > 1) {
|
||||
enum { kLen = 33 };
|
||||
char str[kLen]; // max 126 tracks, so only 4 chars really needed
|
||||
#ifndef _MSC_VER
|
||||
snprintf(str, kLen, "%ld", v.first); // track number
|
||||
#else
|
||||
_snprintf_s(str, sizeof(str), kLen, "%ld", v.first); // track number
|
||||
#endif
|
||||
name += str;
|
||||
}
|
||||
|
||||
// Finally append the output filename extension.
|
||||
|
||||
name += ".vtt";
|
||||
|
||||
// We have synthesized the full output filename, so attempt to
|
||||
// open the WebVTT output file.
|
||||
|
||||
info.file = fopen(name.c_str(), "wb");
|
||||
const bool success = (info.file != NULL);
|
||||
|
||||
if (!success) {
|
||||
printf("unable to open output file %s\n", name.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vttdemux::CloseFiles(metadata_map_t* metadata_map) {
|
||||
if (metadata_map == NULL)
|
||||
return;
|
||||
|
||||
metadata_map_t& m = *metadata_map;
|
||||
|
||||
typedef metadata_map_t::iterator iter_t;
|
||||
|
||||
iter_t i = m.begin();
|
||||
const iter_t j = m.end();
|
||||
|
||||
// Gracefully close each output file, to ensure all output gets
|
||||
// propertly flushed.
|
||||
|
||||
while (i != j) {
|
||||
metadata_map_t::value_type& v = *i++;
|
||||
MetadataInfo& info = v.second;
|
||||
|
||||
fclose(info.file);
|
||||
info.file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool vttdemux::WriteFiles(const metadata_map_t& m, mkvparser::Segment* s) {
|
||||
// First write the WebVTT header.
|
||||
|
||||
InitializeFiles(m);
|
||||
|
||||
if (!WriteChaptersFile(m, s))
|
||||
return false;
|
||||
|
||||
// Now iterate over the clusters, writing the WebVTT cue as we parse
|
||||
// each metadata block.
|
||||
|
||||
const mkvparser::Cluster* cluster = s->GetFirst();
|
||||
|
||||
while (cluster != NULL && !cluster->EOS()) {
|
||||
if (!ProcessCluster(m, cluster))
|
||||
return false;
|
||||
|
||||
cluster = s->GetNext(cluster);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::InitializeFiles(const metadata_map_t& m) {
|
||||
// Write the WebVTT header for each output file in the cache.
|
||||
|
||||
typedef metadata_map_t::const_iterator iter_t;
|
||||
iter_t i = m.begin();
|
||||
const iter_t j = m.end();
|
||||
|
||||
while (i != j) {
|
||||
const metadata_map_t::value_type& v = *i++;
|
||||
const MetadataInfo& info = v.second;
|
||||
FILE* const f = info.file;
|
||||
|
||||
if (fputs("WEBVTT\n", f) < 0) {
|
||||
printf("unable to initialize output file\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteChaptersFile(const metadata_map_t& m,
|
||||
const mkvparser::Segment* s) {
|
||||
const metadata_map_t::const_iterator info_iter = m.find(kChaptersKey);
|
||||
if (info_iter == m.end()) // no chapters, so nothing to do
|
||||
return true;
|
||||
|
||||
const mkvparser::Chapters* const chapters = s->GetChapters();
|
||||
if (chapters == NULL) // weird
|
||||
return true;
|
||||
|
||||
const MetadataInfo& info = info_iter->second;
|
||||
FILE* const file = info.file;
|
||||
|
||||
const int edition_count = chapters->GetEditionCount();
|
||||
|
||||
if (edition_count <= 0) // weird
|
||||
return true; // nothing to do
|
||||
|
||||
if (edition_count > 1) {
|
||||
// TODO(matthewjheaney): figure what to do here
|
||||
printf("more than one chapter edition detected\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const mkvparser::Chapters::Edition* const edition = chapters->GetEdition(0);
|
||||
|
||||
const int atom_count = edition->GetAtomCount();
|
||||
|
||||
for (int idx = 0; idx < atom_count; ++idx) {
|
||||
const mkvparser::Chapters::Atom* const atom = edition->GetAtom(idx);
|
||||
const int display_count = atom->GetDisplayCount();
|
||||
|
||||
if (display_count <= 0)
|
||||
continue;
|
||||
|
||||
if (display_count > 1) {
|
||||
// TODO(matthewjheaney): handle case of multiple languages
|
||||
printf("more than 1 display in atom detected\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const mkvparser::Chapters::Display* const display = atom->GetDisplay(0);
|
||||
|
||||
if (const char* language = display->GetLanguage()) {
|
||||
if (strcmp(language, "eng") != 0) {
|
||||
// TODO(matthewjheaney): handle case of multiple languages.
|
||||
|
||||
// We must create a separate webvtt file for each language.
|
||||
// This isn't a simple problem (which is why we defer it for
|
||||
// now), because there's nothing in the header that tells us
|
||||
// what languages we have as cues. We must parse the displays
|
||||
// of each atom to determine that.
|
||||
|
||||
// One solution is to make two passes over the input data.
|
||||
// First parse the displays, creating an in-memory cache of
|
||||
// all the chapter cues, sorted according to their language.
|
||||
// After we have read all of the chapter atoms from the input
|
||||
// file, we can then write separate output files for each
|
||||
// language.
|
||||
|
||||
printf("only English-language chapter cues are supported\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WriteChaptersCue(file, chapters, atom, display))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteChaptersCue(FILE* f, const mkvparser::Chapters* chapters,
|
||||
const mkvparser::Chapters::Atom* atom,
|
||||
const mkvparser::Chapters::Display* display) {
|
||||
// We start a new cue by writing a cue separator (an empty line)
|
||||
// into the stream.
|
||||
|
||||
if (fputc('\n', f) < 0)
|
||||
return false;
|
||||
|
||||
// A WebVTT Cue comprises 3 things: a cue identifier, followed by
|
||||
// the cue timings, followed by the payload of the cue. We write
|
||||
// each part of the cue in sequence.
|
||||
|
||||
if (!WriteChaptersCueIdentifier(f, atom))
|
||||
return false;
|
||||
|
||||
if (!WriteChaptersCueTimings(f, chapters, atom))
|
||||
return false;
|
||||
|
||||
if (!WriteChaptersCuePayload(f, display))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteChaptersCueIdentifier(
|
||||
FILE* f, const mkvparser::Chapters::Atom* atom) {
|
||||
const char* const identifier = atom->GetStringUID();
|
||||
|
||||
if (identifier == NULL)
|
||||
return true; // nothing else to do
|
||||
|
||||
if (fprintf(f, "%s\n", identifier) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteChaptersCueTimings(FILE* f,
|
||||
const mkvparser::Chapters* chapters,
|
||||
const mkvparser::Chapters::Atom* atom) {
|
||||
const mkvtime_t start_ns = atom->GetStartTime(chapters);
|
||||
|
||||
if (start_ns < 0)
|
||||
return false;
|
||||
|
||||
const mkvtime_t stop_ns = atom->GetStopTime(chapters);
|
||||
|
||||
if (stop_ns < 0)
|
||||
return false;
|
||||
|
||||
if (!WriteCueTime(f, start_ns))
|
||||
return false;
|
||||
|
||||
if (fputs(" --> ", f) < 0)
|
||||
return false;
|
||||
|
||||
if (!WriteCueTime(f, stop_ns))
|
||||
return false;
|
||||
|
||||
if (fputc('\n', f) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteChaptersCuePayload(
|
||||
FILE* f, const mkvparser::Chapters::Display* display) {
|
||||
// Bind a Chapter parser object to the display, which allows us to
|
||||
// extract each line of text from the title-part of the display.
|
||||
ChapterAtomParser parser(display);
|
||||
|
||||
int count = 0; // count of lines of payload text written to output file
|
||||
for (string line;;) {
|
||||
const int e = parser.GetLine(&line);
|
||||
|
||||
if (e < 0) // error (only -- we allow EOS here)
|
||||
return false;
|
||||
|
||||
if (line.empty()) // TODO(matthewjheaney): retain this check?
|
||||
break;
|
||||
|
||||
if (fprintf(f, "%s\n", line.c_str()) < 0)
|
||||
return false;
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count <= 0) // WebVTT cue requires non-empty payload
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::ProcessCluster(const metadata_map_t& m,
|
||||
const mkvparser::Cluster* c) {
|
||||
// Visit the blocks in this cluster, writing a WebVTT cue for each
|
||||
// metadata block.
|
||||
|
||||
const mkvparser::BlockEntry* block_entry;
|
||||
|
||||
long result = c->GetFirst(block_entry); // NOLINT
|
||||
if (result < 0) {
|
||||
printf("bad cluster (unable to get first block)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
while (block_entry != NULL && !block_entry->EOS()) {
|
||||
if (!ProcessBlockEntry(m, block_entry))
|
||||
return false;
|
||||
|
||||
result = c->GetNext(block_entry, block_entry);
|
||||
if (result < 0) { // error
|
||||
printf("bad cluster (unable to get next block)\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::ProcessBlockEntry(const metadata_map_t& m,
|
||||
const mkvparser::BlockEntry* block_entry) {
|
||||
// If the track number for this block is in the cache, then we have
|
||||
// a metadata block, so write the WebVTT cue to the output file.
|
||||
|
||||
const mkvparser::Block* const block = block_entry->GetBlock();
|
||||
const long long tn = block->GetTrackNumber(); // NOLINT
|
||||
|
||||
typedef metadata_map_t::const_iterator iter_t;
|
||||
const iter_t i = m.find(static_cast<metadata_map_t::key_type>(tn));
|
||||
|
||||
if (i == m.end()) // not a metadata track
|
||||
return true; // nothing else to do
|
||||
|
||||
if (block_entry->GetKind() != mkvparser::BlockEntry::kBlockGroup)
|
||||
return false; // weird
|
||||
|
||||
typedef mkvparser::BlockGroup BG;
|
||||
const BG* const block_group = static_cast<const BG*>(block_entry);
|
||||
|
||||
const MetadataInfo& info = i->second;
|
||||
FILE* const f = info.file;
|
||||
|
||||
return WriteCue(f, block_group);
|
||||
}
|
||||
|
||||
bool vttdemux::WriteCue(FILE* f, const mkvparser::BlockGroup* block_group) {
|
||||
// Bind a FrameParser object to the block, which allows us to
|
||||
// extract each line of text from the payload of the block.
|
||||
FrameParser parser(block_group);
|
||||
|
||||
// We start a new cue by writing a cue separator (an empty line)
|
||||
// into the stream.
|
||||
|
||||
if (fputc('\n', f) < 0)
|
||||
return false;
|
||||
|
||||
// A WebVTT Cue comprises 3 things: a cue identifier, followed by
|
||||
// the cue timings, followed by the payload of the cue. We write
|
||||
// each part of the cue in sequence.
|
||||
|
||||
if (!WriteCueIdentifier(f, &parser))
|
||||
return false;
|
||||
|
||||
if (!WriteCueTimings(f, &parser))
|
||||
return false;
|
||||
|
||||
if (!WriteCuePayload(f, &parser))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteCueIdentifier(FILE* f, FrameParser* parser) {
|
||||
string line;
|
||||
int e = parser->GetLine(&line);
|
||||
|
||||
if (e) // error or EOS
|
||||
return false;
|
||||
|
||||
// If the cue identifier line is empty, this means that the original
|
||||
// WebVTT cue did not have a cue identifier, so we don't bother
|
||||
// writing an extra line terminator to the output file (though doing
|
||||
// so would be harmless).
|
||||
|
||||
if (!line.empty()) {
|
||||
if (fputs(line.c_str(), f) < 0)
|
||||
return false;
|
||||
|
||||
if (fputc('\n', f) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteCueTimings(FILE* f, FrameParser* parser) {
|
||||
const mkvparser::BlockGroup* const block_group = parser->block_group_;
|
||||
const mkvparser::Cluster* const cluster = block_group->GetCluster();
|
||||
const mkvparser::Block* const block = block_group->GetBlock();
|
||||
|
||||
// A WebVTT Cue "timings" line comprises two parts: the start and
|
||||
// stop time for this cue, followed by the (optional) cue settings,
|
||||
// such as orientation of the rendered text or its size. Only the
|
||||
// settings part of the cue timings line is stored in the WebM
|
||||
// block. We reconstruct the start and stop times of the WebVTT cue
|
||||
// from the timestamp and duration of the WebM block.
|
||||
|
||||
const mkvtime_t start_ns = block->GetTime(cluster);
|
||||
|
||||
if (!WriteCueTime(f, start_ns))
|
||||
return false;
|
||||
|
||||
if (fputs(" --> ", f) < 0)
|
||||
return false;
|
||||
|
||||
const mkvtime_t duration_timecode = block_group->GetDurationTimeCode();
|
||||
|
||||
if (duration_timecode < 0)
|
||||
return false;
|
||||
|
||||
const mkvparser::Segment* const segment = cluster->m_pSegment;
|
||||
const mkvparser::SegmentInfo* const info = segment->GetInfo();
|
||||
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
const mkvtime_t timecode_scale = info->GetTimeCodeScale();
|
||||
|
||||
if (timecode_scale <= 0)
|
||||
return false;
|
||||
|
||||
const mkvtime_t duration_ns = duration_timecode * timecode_scale;
|
||||
const mkvtime_t stop_ns = start_ns + duration_ns;
|
||||
|
||||
if (!WriteCueTime(f, stop_ns))
|
||||
return false;
|
||||
|
||||
string line;
|
||||
int e = parser->GetLine(&line);
|
||||
|
||||
if (e) // error or EOS
|
||||
return false;
|
||||
|
||||
if (!line.empty()) {
|
||||
if (fputc(' ', f) < 0)
|
||||
return false;
|
||||
|
||||
if (fputs(line.c_str(), f) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fputc('\n', f) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteCueTime(FILE* f, mkvtime_t time_ns) {
|
||||
mkvtime_t ms = time_ns / 1000000; // WebVTT time has millisecond resolution
|
||||
|
||||
mkvtime_t sec = ms / 1000;
|
||||
ms -= sec * 1000;
|
||||
|
||||
mkvtime_t min = sec / 60;
|
||||
sec -= 60 * min;
|
||||
|
||||
mkvtime_t hr = min / 60;
|
||||
min -= 60 * hr;
|
||||
|
||||
if (hr > 0) {
|
||||
if (fprintf(f, "%02lld:", hr) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fprintf(f, "%02lld:%02lld.%03lld", min, sec, ms) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vttdemux::WriteCuePayload(FILE* f, FrameParser* parser) {
|
||||
int count = 0; // count of lines of payload text written to output file
|
||||
for (string line;;) {
|
||||
const int e = parser->GetLine(&line);
|
||||
|
||||
if (e < 0) // error (only -- we allow EOS here)
|
||||
return false;
|
||||
|
||||
if (line.empty()) // TODO(matthewjheaney): retain this check?
|
||||
break;
|
||||
|
||||
if (fprintf(f, "%s\n", line.c_str()) < 0)
|
||||
return false;
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count <= 0) // WebVTT cue requires non-empty payload
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
59
vttreader.cc
Normal file
59
vttreader.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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 "./vttreader.h" // NOLINT
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable MSVC warnings that suggest making code non-portable.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
namespace libwebvtt {
|
||||
|
||||
VttReader::VttReader() : file_(NULL) {}
|
||||
|
||||
VttReader::~VttReader() { Close(); }
|
||||
|
||||
int VttReader::Open(const char* filename) {
|
||||
if (filename == NULL || file_ != NULL)
|
||||
return -1;
|
||||
|
||||
file_ = fopen(filename, "rb");
|
||||
if (file_ == NULL)
|
||||
return -1;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
void VttReader::Close() {
|
||||
if (file_) {
|
||||
fclose(file_);
|
||||
file_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int VttReader::GetChar(char* c) {
|
||||
if (c == NULL || file_ == NULL)
|
||||
return -1;
|
||||
|
||||
const int result = fgetc(file_);
|
||||
if (result != EOF) {
|
||||
*c = static_cast<char>(result);
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
if (ferror(file_))
|
||||
return -1; // error
|
||||
|
||||
if (feof(file_))
|
||||
return 1; // EOF
|
||||
|
||||
return -1; // weird
|
||||
}
|
||||
|
||||
} // namespace libwebvtt
|
||||
44
vttreader.h
Normal file
44
vttreader.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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 VTTREADER_H_ // NOLINT
|
||||
#define VTTREADER_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include "./webvttparser.h"
|
||||
|
||||
namespace libwebvtt {
|
||||
|
||||
class VttReader : public libwebvtt::Reader {
|
||||
public:
|
||||
VttReader();
|
||||
virtual ~VttReader();
|
||||
|
||||
// Open the file identified by |filename| in read-only mode, as a
|
||||
// binary stream of bytes. Returns 0 on success, negative if error.
|
||||
int Open(const char* filename);
|
||||
|
||||
// Closes the file stream. Note that the stream is automatically
|
||||
// closed when the VttReader object is destroyed.
|
||||
void Close();
|
||||
|
||||
// Reads the next character in the file stream, as per the semantics
|
||||
// of Reader::GetChar. Returns negative if error, 0 on success, and
|
||||
// positive if end-of-stream has been reached.
|
||||
virtual int GetChar(char* c);
|
||||
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
VttReader(const VttReader&);
|
||||
VttReader& operator=(const VttReader&);
|
||||
};
|
||||
|
||||
} // namespace libwebvtt
|
||||
|
||||
#endif // VTTREADER_H_ // NOLINT
|
||||
517
webm2pes.cc
Normal file
517
webm2pes.cc
Normal file
@@ -0,0 +1,517 @@
|
||||
// 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 "webm2pes.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
void Usage(const char* argv[]) {
|
||||
printf("Usage: %s <WebM file> <output file>", argv[0]);
|
||||
}
|
||||
|
||||
bool WriteUint8(std::uint8_t val, std::FILE* fileptr) {
|
||||
if (fileptr == nullptr)
|
||||
return false;
|
||||
return (std::fputc(val, fileptr) == val);
|
||||
}
|
||||
|
||||
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> FrameRanges;
|
||||
|
||||
// Returns true and stores frame offsets and lengths in |frame_ranges| when
|
||||
// |frame| has a valid VP9 super frame index.
|
||||
bool ParseVP9SuperFrameIndex(const std::uint8_t* frame,
|
||||
std::size_t length,
|
||||
FrameRanges* frame_ranges) {
|
||||
if (frame == nullptr || length == 0 || frame_ranges == nullptr)
|
||||
return false;
|
||||
|
||||
bool parse_ok = false;
|
||||
const std::uint8_t marker = frame[length - 1];
|
||||
const std::uint32_t kHasSuperFrameIndexMask = 0xe0;
|
||||
const std::uint32_t kSuperFrameMarker = 0xc0;
|
||||
|
||||
if ((marker & kHasSuperFrameIndexMask) == kSuperFrameMarker) {
|
||||
const std::uint32_t kFrameCountMask = 0x7;
|
||||
const int num_frames = (marker & kFrameCountMask) + 1;
|
||||
const int length_field_size = ((marker >> 3) & 0x3) + 1;
|
||||
const std::size_t index_length = 2 + length_field_size * num_frames;
|
||||
|
||||
// Consume the super frame index.
|
||||
std::size_t frame_offset = index_length;
|
||||
|
||||
if (length >= index_length && frame[length - index_length] == marker) {
|
||||
// Found a valid superframe index.
|
||||
const std::uint8_t* byte = frame + length - index_length + 1;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
frame_ranges->push_back(Range(frame_offset, child_frame_length));
|
||||
frame_offset += child_frame_length;
|
||||
}
|
||||
|
||||
if (frame_ranges->size() != num_frames) {
|
||||
std::fprintf(stderr, "Webm2Pes: superframe index parse failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
parse_ok = true;
|
||||
} else {
|
||||
std::fprintf(stderr, "Webm2Pes: Invalid superframe index.\n");
|
||||
}
|
||||
}
|
||||
return parse_ok;
|
||||
}
|
||||
|
||||
std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds) {
|
||||
const double kNanosecondsPerSecond = 1000000000.0;
|
||||
const double pts_seconds = nanoseconds / kNanosecondsPerSecond;
|
||||
return pts_seconds * 90000;
|
||||
}
|
||||
|
||||
bool GetPacketPayloadRanges(const libwebm::PesHeader& header,
|
||||
const FrameRanges& frame_ranges,
|
||||
FrameRanges* packet_payload_ranges) {
|
||||
// TODO(tomfinegan): The length field in PES is actually number of bytes that
|
||||
// follow the length field, and does not include the 6 byte fixed portion of
|
||||
// the header (4 byte start code + 2 bytes for the length). We can fit in 6
|
||||
// more bytes if we really want to, and avoid packetization when size is very
|
||||
// close to UINT16_MAX.
|
||||
if (packet_payload_ranges == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: nullptr getting payload ranges.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t kMaxPacketPayloadSize = UINT16_MAX - header.size();
|
||||
|
||||
for (const Range& frame_range : frame_ranges) {
|
||||
if (frame_range.length + header.size() > kMaxPacketPayloadSize) {
|
||||
// make packet ranges until range.length is exhausted
|
||||
const std::size_t kBytesToPacketize = frame_range.length;
|
||||
std::size_t packet_payload_length = 0;
|
||||
for (std::size_t pos = 0; pos < kBytesToPacketize;
|
||||
pos += packet_payload_length) {
|
||||
packet_payload_length =
|
||||
(frame_range.length - pos < kMaxPacketPayloadSize)
|
||||
? frame_range.length - pos
|
||||
: kMaxPacketPayloadSize;
|
||||
packet_payload_ranges->push_back(
|
||||
Range(frame_range.offset + pos, packet_payload_length));
|
||||
}
|
||||
} else {
|
||||
// copy range into |packet_ranges|
|
||||
packet_payload_ranges->push_back(
|
||||
Range(frame_range.offset, frame_range.length));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace libwebm {
|
||||
|
||||
//
|
||||
// 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 |file| and returns true. Returns false when write or
|
||||
// field value validation fails.
|
||||
bool PesOptionalHeader::Write(std::FILE* file, bool write_pts) const {
|
||||
if (file == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: nullptr in opt header writer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::uint8_t header[9] = {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; // Field is 8 bits wide.
|
||||
|
||||
int num_stuffing_bytes =
|
||||
(pts.num_bits + 7) / 8 + 1 /* always 1 stuffing byte */;
|
||||
if (write_pts == true) {
|
||||
// Set the PTS value and adjust stuffing byte count accordingly.
|
||||
*++byte = (pts.bits >> 32) & 0xff;
|
||||
*++byte = (pts.bits >> 24) & 0xff;
|
||||
*++byte = (pts.bits >> 16) & 0xff;
|
||||
*++byte = (pts.bits >> 8) & 0xff;
|
||||
*++byte = pts.bits & 0xff;
|
||||
num_stuffing_bytes = 1;
|
||||
}
|
||||
|
||||
// Add the stuffing byte(s).
|
||||
for (int i = 0; i < num_stuffing_bytes; ++i)
|
||||
*++byte = stuffing_byte.bits;
|
||||
|
||||
if (std::fwrite(reinterpret_cast<void*>(header), 1, size_in_bytes(), file) !=
|
||||
size_in_bytes()) {
|
||||
std::fprintf(stderr, "Webm2Pes: unable to write PES opt header to file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// BCMVHeader methods.
|
||||
//
|
||||
|
||||
bool BCMVHeader::Write(std::FILE* fileptr) const {
|
||||
if (fileptr == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: nullptr for file in BCMV Write.\n");
|
||||
return false;
|
||||
}
|
||||
if (std::fwrite(bcmv, 1, 4, fileptr) != 4) {
|
||||
std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n");
|
||||
}
|
||||
const std::size_t kRemainingBytes = 6;
|
||||
const uint8_t buffer[kRemainingBytes] = {(length >> 24) & 0xff,
|
||||
(length >> 16) & 0xff,
|
||||
(length >> 8) & 0xff,
|
||||
length & 0xff,
|
||||
0,
|
||||
0 /* 2 bytes 0 padding */};
|
||||
for (std::int8_t i = 0; i < kRemainingBytes; ++i) {
|
||||
if (WriteUint8(buffer[i], fileptr) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: BCMV remainder write failed.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// PesHeader methods.
|
||||
//
|
||||
|
||||
// Writes out the header to |file|. Calls PesOptionalHeader::Write() to write
|
||||
// |optional_header| contents. Returns true when successful, false otherwise.
|
||||
bool PesHeader::Write(std::FILE* file, bool write_pts) const {
|
||||
if (file == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: nullptr in header writer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write |start_code|.
|
||||
if (std::fwrite(reinterpret_cast<const void*>(start_code), 1, 4, file) != 4) {
|
||||
std::fprintf(stderr, "Webm2Pes: cannot write packet start code.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write |packet_length| as big endian.
|
||||
std::uint8_t byte = (packet_length >> 8) & 0xff;
|
||||
if (WriteUint8(byte, file) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: cannot write packet length (byte 0).\n");
|
||||
return false;
|
||||
}
|
||||
byte = packet_length & 0xff;
|
||||
if (WriteUint8(byte, file) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: cannot write packet length (byte 1).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the (not really) optional header.
|
||||
if (optional_header.Write(file, write_pts) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: PES optional header write failed.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Webm2Pes methods.
|
||||
//
|
||||
|
||||
bool Webm2Pes::Convert() {
|
||||
if (input_file_name_.empty() || output_file_name_.empty()) {
|
||||
std::fprintf(stderr, "Webm2Pes: input and/or output file name(s) empty.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Store timecode scale.
|
||||
timecode_scale_ = webm_parser_->GetInfo()->GetTimeCodeScale();
|
||||
|
||||
// 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;
|
||||
}
|
||||
for (int track_index = 0; track_index < tracks->GetTracksCount();
|
||||
++track_index) {
|
||||
const mkvparser::Track* track = tracks->GetTrackByIndex(track_index);
|
||||
if (track && track->GetType() == mkvparser::Track::kVideo) {
|
||||
if (std::string(track->GetCodecId()) == std::string("V_VP8"))
|
||||
codec_ = VP8;
|
||||
else if (std::string(track->GetCodecId()) == std::string("V_VP9"))
|
||||
codec_ = VP9;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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& frame = block->GetFrame(frame_num);
|
||||
|
||||
// Write frame out as PES packet(s).
|
||||
const bool pes_status =
|
||||
WritePesPacket(frame, block->GetTime(cluster));
|
||||
if (pes_status != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
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::WritePesPacket(const mkvparser::Block::Frame& vpx_frame,
|
||||
double nanosecond_pts) {
|
||||
// Read the input frame.
|
||||
std::unique_ptr<uint8_t[]> frame_data(new (std::nothrow)
|
||||
uint8_t[vpx_frame.len]);
|
||||
if (frame_data.get() == nullptr) {
|
||||
std::fprintf(stderr, "Webm2Pes: Out of memory.\n");
|
||||
return false;
|
||||
}
|
||||
if (vpx_frame.Read(&webm_reader_, frame_data.get()) != 0) {
|
||||
std::fprintf(stderr, "Webm2Pes: Error reading VPx frame!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
FrameRanges frame_ranges;
|
||||
if (codec_ == VP9) {
|
||||
bool has_superframe_index =
|
||||
ParseVP9SuperFrameIndex(frame_data.get(), vpx_frame.len, &frame_ranges);
|
||||
if (has_superframe_index == false) {
|
||||
frame_ranges.push_back(Range(0, vpx_frame.len));
|
||||
}
|
||||
} else {
|
||||
frame_ranges.push_back(Range(0, vpx_frame.len));
|
||||
}
|
||||
|
||||
PesHeader header;
|
||||
FrameRanges packet_payload_ranges;
|
||||
if (GetPacketPayloadRanges(header, frame_ranges, &packet_payload_ranges) !=
|
||||
true) {
|
||||
std::fprintf(stderr, "Webm2Pes: Error preparing packet payload ranges!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: DEBUG/REMOVE
|
||||
///
|
||||
printf("-FRAME TOTAL LENGTH %ld--\n", vpx_frame.len);
|
||||
for (const Range& frame_range : frame_ranges) {
|
||||
printf("--frame range: off:%lu len:%lu\n", frame_range.offset,
|
||||
frame_range.length);
|
||||
}
|
||||
for (const Range& payload_range : packet_payload_ranges) {
|
||||
printf("---payload range: off:%lu len:%lu\n", payload_range.offset,
|
||||
payload_range.length);
|
||||
}
|
||||
|
||||
const std::int64_t khz90_pts = NanosecondsTo90KhzTicks(nanosecond_pts);
|
||||
header.optional_header.SetPtsBits(khz90_pts);
|
||||
|
||||
bool write_pts = true;
|
||||
for (const Range& packet_payload_range : packet_payload_ranges) {
|
||||
header.packet_length =
|
||||
header.optional_header.size_in_bytes() + packet_payload_range.length;
|
||||
if (header.Write(output_file_.get(), write_pts) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: packet header write failed.\n");
|
||||
return false;
|
||||
}
|
||||
write_pts = false;
|
||||
|
||||
BCMVHeader bcmv_header(packet_payload_range.length);
|
||||
if (bcmv_header.Write(output_file_.get()) != true) {
|
||||
std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the payload.
|
||||
if (std::fwrite(frame_data.get() + packet_payload_range.offset, 1,
|
||||
packet_payload_range.length,
|
||||
output_file_.get()) != packet_payload_range.length) {
|
||||
std::fprintf(stderr, "Webm2Pes: packet payload write failed.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace libwebm
|
||||
|
||||
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.Convert() == true ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
209
webm2pes.h
Normal file
209
webm2pes.h
Normal file
@@ -0,0 +1,209 @@
|
||||
// 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_WEBM2PES_H_
|
||||
#define LIBWEBM_WEBM2PES_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mkvparser.hpp"
|
||||
#include "mkvreader.hpp"
|
||||
|
||||
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 std::uint32_t 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 std::uint8_t shift;
|
||||
};
|
||||
|
||||
// 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 |file| and returns true. Returns false when write or
|
||||
// field value validation fails.
|
||||
bool Write(std::FILE* file, bool write_pts) 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 the FILE stream.
|
||||
bool Write(std::FILE* fileptr) const;
|
||||
};
|
||||
|
||||
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() + BCMVHeader::size() +
|
||||
6 /* start_code + packet_length */ + packet_length;
|
||||
}
|
||||
|
||||
// Writes out the header to |file|. Calls PesOptionalHeader::Write() to write
|
||||
// |optional_header| contents. Returns true when successful, false otherwise.
|
||||
bool Write(std::FILE* file, bool write_pts) const;
|
||||
};
|
||||
|
||||
// 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:
|
||||
enum VideoCodec { VP8, VP9 };
|
||||
|
||||
Webm2Pes(const std::string& input_file, const std::string& output_file)
|
||||
: input_file_name_(input_file), output_file_name_(output_file) {}
|
||||
|
||||
Webm2Pes() = delete;
|
||||
Webm2Pes(const Webm2Pes&) = delete;
|
||||
Webm2Pes(Webm2Pes&&) = delete;
|
||||
~Webm2Pes() = default;
|
||||
|
||||
// Converts the VPx video stream to a PES and returns true. Returns false
|
||||
// to report failure.
|
||||
bool Convert();
|
||||
|
||||
private:
|
||||
// fclose functor for wrapping FILE in std::unique_ptr.
|
||||
struct FILEDeleter {
|
||||
int operator()(FILE* f) {
|
||||
if (f != nullptr)
|
||||
return fclose(f);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
typedef std::unique_ptr<FILE, FILEDeleter> FilePtr;
|
||||
|
||||
bool WritePesPacket(const mkvparser::Block::Frame& vpx_frame,
|
||||
double nanosecond_pts);
|
||||
|
||||
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.
|
||||
VideoCodec codec_;
|
||||
|
||||
// Input timecode scale.
|
||||
std::int64_t timecode_scale_ = 1000000;
|
||||
};
|
||||
} // namespace libwebm
|
||||
|
||||
#endif // LIBWEBM_WEBM2PES_H_
|
||||
155
webmids.hpp
Normal file
155
webmids.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
#ifndef WEBMIDS_HPP
|
||||
#define WEBMIDS_HPP
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
enum MkvId {
|
||||
kMkvEBML = 0x1A45DFA3,
|
||||
kMkvEBMLVersion = 0x4286,
|
||||
kMkvEBMLReadVersion = 0x42F7,
|
||||
kMkvEBMLMaxIDLength = 0x42F2,
|
||||
kMkvEBMLMaxSizeLength = 0x42F3,
|
||||
kMkvDocType = 0x4282,
|
||||
kMkvDocTypeVersion = 0x4287,
|
||||
kMkvDocTypeReadVersion = 0x4285,
|
||||
kMkvVoid = 0xEC,
|
||||
kMkvSignatureSlot = 0x1B538667,
|
||||
kMkvSignatureAlgo = 0x7E8A,
|
||||
kMkvSignatureHash = 0x7E9A,
|
||||
kMkvSignaturePublicKey = 0x7EA5,
|
||||
kMkvSignature = 0x7EB5,
|
||||
kMkvSignatureElements = 0x7E5B,
|
||||
kMkvSignatureElementList = 0x7E7B,
|
||||
kMkvSignedElement = 0x6532,
|
||||
// segment
|
||||
kMkvSegment = 0x18538067,
|
||||
// Meta Seek Information
|
||||
kMkvSeekHead = 0x114D9B74,
|
||||
kMkvSeek = 0x4DBB,
|
||||
kMkvSeekID = 0x53AB,
|
||||
kMkvSeekPosition = 0x53AC,
|
||||
// Segment Information
|
||||
kMkvInfo = 0x1549A966,
|
||||
kMkvTimecodeScale = 0x2AD7B1,
|
||||
kMkvDuration = 0x4489,
|
||||
kMkvDateUTC = 0x4461,
|
||||
kMkvTitle = 0x7BA9,
|
||||
kMkvMuxingApp = 0x4D80,
|
||||
kMkvWritingApp = 0x5741,
|
||||
// Cluster
|
||||
kMkvCluster = 0x1F43B675,
|
||||
kMkvTimecode = 0xE7,
|
||||
kMkvPrevSize = 0xAB,
|
||||
kMkvBlockGroup = 0xA0,
|
||||
kMkvBlock = 0xA1,
|
||||
kMkvBlockDuration = 0x9B,
|
||||
kMkvReferenceBlock = 0xFB,
|
||||
kMkvLaceNumber = 0xCC,
|
||||
kMkvSimpleBlock = 0xA3,
|
||||
kMkvBlockAdditions = 0x75A1,
|
||||
kMkvBlockMore = 0xA6,
|
||||
kMkvBlockAddID = 0xEE,
|
||||
kMkvBlockAdditional = 0xA5,
|
||||
kMkvDiscardPadding = 0x75A2,
|
||||
// Track
|
||||
kMkvTracks = 0x1654AE6B,
|
||||
kMkvTrackEntry = 0xAE,
|
||||
kMkvTrackNumber = 0xD7,
|
||||
kMkvTrackUID = 0x73C5,
|
||||
kMkvTrackType = 0x83,
|
||||
kMkvFlagEnabled = 0xB9,
|
||||
kMkvFlagDefault = 0x88,
|
||||
kMkvFlagForced = 0x55AA,
|
||||
kMkvFlagLacing = 0x9C,
|
||||
kMkvDefaultDuration = 0x23E383,
|
||||
kMkvMaxBlockAdditionID = 0x55EE,
|
||||
kMkvName = 0x536E,
|
||||
kMkvLanguage = 0x22B59C,
|
||||
kMkvCodecID = 0x86,
|
||||
kMkvCodecPrivate = 0x63A2,
|
||||
kMkvCodecName = 0x258688,
|
||||
kMkvCodecDelay = 0x56AA,
|
||||
kMkvSeekPreRoll = 0x56BB,
|
||||
// video
|
||||
kMkvVideo = 0xE0,
|
||||
kMkvFlagInterlaced = 0x9A,
|
||||
kMkvStereoMode = 0x53B8,
|
||||
kMkvAlphaMode = 0x53C0,
|
||||
kMkvPixelWidth = 0xB0,
|
||||
kMkvPixelHeight = 0xBA,
|
||||
kMkvPixelCropBottom = 0x54AA,
|
||||
kMkvPixelCropTop = 0x54BB,
|
||||
kMkvPixelCropLeft = 0x54CC,
|
||||
kMkvPixelCropRight = 0x54DD,
|
||||
kMkvDisplayWidth = 0x54B0,
|
||||
kMkvDisplayHeight = 0x54BA,
|
||||
kMkvDisplayUnit = 0x54B2,
|
||||
kMkvAspectRatioType = 0x54B3,
|
||||
kMkvFrameRate = 0x2383E3,
|
||||
// end video
|
||||
// audio
|
||||
kMkvAudio = 0xE1,
|
||||
kMkvSamplingFrequency = 0xB5,
|
||||
kMkvOutputSamplingFrequency = 0x78B5,
|
||||
kMkvChannels = 0x9F,
|
||||
kMkvBitDepth = 0x6264,
|
||||
// end audio
|
||||
// ContentEncodings
|
||||
kMkvContentEncodings = 0x6D80,
|
||||
kMkvContentEncoding = 0x6240,
|
||||
kMkvContentEncodingOrder = 0x5031,
|
||||
kMkvContentEncodingScope = 0x5032,
|
||||
kMkvContentEncodingType = 0x5033,
|
||||
kMkvContentCompression = 0x5034,
|
||||
kMkvContentCompAlgo = 0x4254,
|
||||
kMkvContentCompSettings = 0x4255,
|
||||
kMkvContentEncryption = 0x5035,
|
||||
kMkvContentEncAlgo = 0x47E1,
|
||||
kMkvContentEncKeyID = 0x47E2,
|
||||
kMkvContentSignature = 0x47E3,
|
||||
kMkvContentSigKeyID = 0x47E4,
|
||||
kMkvContentSigAlgo = 0x47E5,
|
||||
kMkvContentSigHashAlgo = 0x47E6,
|
||||
kMkvContentEncAESSettings = 0x47E7,
|
||||
kMkvAESSettingsCipherMode = 0x47E8,
|
||||
kMkvAESSettingsCipherInitData = 0x47E9,
|
||||
// end ContentEncodings
|
||||
// Cueing Data
|
||||
kMkvCues = 0x1C53BB6B,
|
||||
kMkvCuePoint = 0xBB,
|
||||
kMkvCueTime = 0xB3,
|
||||
kMkvCueTrackPositions = 0xB7,
|
||||
kMkvCueTrack = 0xF7,
|
||||
kMkvCueClusterPosition = 0xF1,
|
||||
kMkvCueBlockNumber = 0x5378,
|
||||
// Chapters
|
||||
kMkvChapters = 0x1043A770,
|
||||
kMkvEditionEntry = 0x45B9,
|
||||
kMkvChapterAtom = 0xB6,
|
||||
kMkvChapterUID = 0x73C4,
|
||||
kMkvChapterStringUID = 0x5654,
|
||||
kMkvChapterTimeStart = 0x91,
|
||||
kMkvChapterTimeEnd = 0x92,
|
||||
kMkvChapterDisplay = 0x80,
|
||||
kMkvChapString = 0x85,
|
||||
kMkvChapLanguage = 0x437C,
|
||||
kMkvChapCountry = 0x437E,
|
||||
// Tags
|
||||
kMkvTags = 0x1254C367,
|
||||
kMkvTag = 0x7373,
|
||||
kMkvSimpleTag = 0x67C8,
|
||||
kMkvTagName = 0x45A3,
|
||||
kMkvTagString = 0x4487
|
||||
};
|
||||
|
||||
} // end namespace mkvmuxer
|
||||
|
||||
#endif // WEBMIDS_HPP
|
||||
702
webvttparser.cc
Normal file
702
webvttparser.cc
Normal file
@@ -0,0 +1,702 @@
|
||||
// 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 "./webvttparser.h" // NOLINT
|
||||
#include <climits>
|
||||
|
||||
namespace libwebvtt {
|
||||
|
||||
// NOLINT'ing this enum because clang-format puts it in a single line which
|
||||
// makes it look really unreadable.
|
||||
enum {
|
||||
kNUL = '\x00',
|
||||
kSPACE = ' ',
|
||||
kTAB = '\x09',
|
||||
kLF = '\x0A',
|
||||
kCR = '\x0D'
|
||||
}; // NOLINT
|
||||
|
||||
Reader::~Reader() {}
|
||||
|
||||
LineReader::~LineReader() {}
|
||||
|
||||
int LineReader::GetLine(std::string* line_ptr) {
|
||||
if (line_ptr == NULL)
|
||||
return -1;
|
||||
|
||||
std::string& ln = *line_ptr;
|
||||
ln.clear();
|
||||
|
||||
// Consume characters from the stream, until we
|
||||
// reach end-of-line (or end-of-stream).
|
||||
|
||||
// The WebVTT spec states that lines may be
|
||||
// terminated in any of these three ways:
|
||||
// LF
|
||||
// CR
|
||||
// CR LF
|
||||
|
||||
// We interrogate each character as we read it from the stream.
|
||||
// If we detect an end-of-line character, we consume the full
|
||||
// end-of-line indication, and we're done; otherwise, accumulate
|
||||
// the character and repeat.
|
||||
|
||||
for (;;) {
|
||||
char c;
|
||||
const int e = GetChar(&c);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return (ln.empty()) ? 1 : 0;
|
||||
|
||||
// We have a character, so we must first determine
|
||||
// whether we have reached end-of-line.
|
||||
|
||||
if (c == kLF)
|
||||
return 0; // handle the easy end-of-line case immediately
|
||||
|
||||
if (c == kCR)
|
||||
break; // handle the hard end-of-line case outside of loop
|
||||
|
||||
if (c == '\xFE' || c == '\xFF') // not UTF-8
|
||||
return -1;
|
||||
|
||||
// To defend against pathological or malicious streams, we
|
||||
// cap the line length at some arbitrarily-large value:
|
||||
enum { kMaxLineLength = 10000 }; // arbitrary
|
||||
|
||||
if (ln.length() >= kMaxLineLength)
|
||||
return -1;
|
||||
|
||||
// We don't have an end-of-line character, so accumulate
|
||||
// the character in our line buffer.
|
||||
ln.push_back(c);
|
||||
}
|
||||
|
||||
// We detected a CR. We must interrogate the next character
|
||||
// in the stream, to determine whether we have a LF (which
|
||||
// would make it part of this same line).
|
||||
|
||||
char c;
|
||||
const int e = GetChar(&c);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return 0;
|
||||
|
||||
// If next character in the stream is not a LF, return it
|
||||
// to the stream (because it's part of the next line).
|
||||
if (c != kLF)
|
||||
UngetChar(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Parser::Parser(Reader* r) : reader_(r), unget_(-1) {}
|
||||
|
||||
Parser::~Parser() {}
|
||||
|
||||
int Parser::Init() {
|
||||
int e = ParseBOM();
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return -1;
|
||||
|
||||
// Parse "WEBVTT". We read from the stream one character at-a-time, in
|
||||
// order to defend against non-WebVTT streams (e.g. binary files) that don't
|
||||
// happen to comprise lines of text demarcated with line terminators.
|
||||
|
||||
const char kId[] = "WEBVTT";
|
||||
|
||||
for (const char* p = kId; *p; ++p) {
|
||||
char c;
|
||||
e = GetChar(&c);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return -1;
|
||||
|
||||
if (c != *p)
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
|
||||
e = GetLine(&line);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return 0; // weird but valid
|
||||
|
||||
if (!line.empty()) {
|
||||
// Parse optional characters that follow "WEBVTT"
|
||||
|
||||
const char c = line[0];
|
||||
|
||||
if (c != kSPACE && c != kTAB)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The WebVTT spec requires that the "WEBVTT" line
|
||||
// be followed by an empty line (to separate it from
|
||||
// first cue).
|
||||
|
||||
e = GetLine(&line);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return 0; // weird but we allow it
|
||||
|
||||
if (!line.empty())
|
||||
return -1;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
int Parser::Parse(Cue* cue) {
|
||||
if (cue == NULL)
|
||||
return -1;
|
||||
|
||||
// Parse first non-blank line
|
||||
|
||||
std::string line;
|
||||
int e;
|
||||
|
||||
for (;;) {
|
||||
e = GetLine(&line);
|
||||
|
||||
if (e) // EOF is OK here
|
||||
return e;
|
||||
|
||||
if (!line.empty())
|
||||
break;
|
||||
}
|
||||
|
||||
// A WebVTT cue comprises an optional cue identifier line followed
|
||||
// by a (non-optional) timings line. You determine whether you have
|
||||
// a timings line by scanning for the arrow token, the lexeme of which
|
||||
// may not appear in the cue identifier line.
|
||||
|
||||
const char kArrow[] = "-->";
|
||||
std::string::size_type arrow_pos = line.find(kArrow);
|
||||
|
||||
if (arrow_pos != std::string::npos) {
|
||||
// We found a timings line, which implies that we don't have a cue
|
||||
// identifier.
|
||||
|
||||
cue->identifier.clear();
|
||||
} else {
|
||||
// We did not find a timings line, so we assume that we have a cue
|
||||
// identifier line, and then try again to find the cue timings on
|
||||
// the next line.
|
||||
|
||||
cue->identifier.swap(line);
|
||||
|
||||
e = GetLine(&line);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return -1;
|
||||
|
||||
arrow_pos = line.find(kArrow);
|
||||
|
||||
if (arrow_pos == std::string::npos) // not a timings line
|
||||
return -1;
|
||||
}
|
||||
|
||||
e = ParseTimingsLine(&line, arrow_pos, &cue->start_time, &cue->stop_time,
|
||||
&cue->settings);
|
||||
|
||||
if (e) // error
|
||||
return e;
|
||||
|
||||
// The cue payload comprises all the non-empty
|
||||
// lines that follow the timings line.
|
||||
|
||||
Cue::payload_t& p = cue->payload;
|
||||
p.clear();
|
||||
|
||||
for (;;) {
|
||||
e = GetLine(&line);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (line.empty())
|
||||
break;
|
||||
|
||||
p.push_back(line);
|
||||
}
|
||||
|
||||
if (p.empty())
|
||||
return -1;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
int Parser::GetChar(char* c) {
|
||||
if (unget_ >= 0) {
|
||||
*c = static_cast<char>(unget_);
|
||||
unget_ = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reader_->GetChar(c);
|
||||
}
|
||||
|
||||
void Parser::UngetChar(char c) { unget_ = static_cast<unsigned char>(c); }
|
||||
|
||||
int Parser::ParseBOM() {
|
||||
// Explanation of UTF-8 BOM:
|
||||
// http://en.wikipedia.org/wiki/Byte_order_mark
|
||||
|
||||
static const char BOM[] = "\xEF\xBB\xBF"; // UTF-8 BOM
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
char c;
|
||||
int e = GetChar(&c);
|
||||
|
||||
if (e < 0) // error
|
||||
return e;
|
||||
|
||||
if (e > 0) // EOF
|
||||
return 1;
|
||||
|
||||
if (c != BOM[i]) {
|
||||
if (i == 0) { // we don't have a BOM
|
||||
UngetChar(c);
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
// We started a BOM, so we must finish the BOM.
|
||||
return -1; // error
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
int Parser::ParseTimingsLine(std::string* line_ptr,
|
||||
std::string::size_type arrow_pos, Time* start_time,
|
||||
Time* stop_time, Cue::settings_t* settings) {
|
||||
if (line_ptr == NULL)
|
||||
return -1;
|
||||
|
||||
std::string& line = *line_ptr;
|
||||
|
||||
if (arrow_pos == std::string::npos || arrow_pos >= line.length())
|
||||
return -1;
|
||||
|
||||
// Place a NUL character at the start of the arrow token, in
|
||||
// order to demarcate the start time from remainder of line.
|
||||
line[arrow_pos] = kNUL;
|
||||
std::string::size_type idx = 0;
|
||||
|
||||
int e = ParseTime(line, &idx, start_time);
|
||||
if (e) // error
|
||||
return e;
|
||||
|
||||
// Detect any junk that follows the start time,
|
||||
// but precedes the arrow symbol.
|
||||
|
||||
while (char c = line[idx]) {
|
||||
if (c != kSPACE && c != kTAB)
|
||||
return -1;
|
||||
++idx;
|
||||
}
|
||||
|
||||
// Place a NUL character at the end of the line,
|
||||
// so the scanner has a place to stop, and begin
|
||||
// the scan just beyond the arrow token.
|
||||
|
||||
line.push_back(kNUL);
|
||||
idx = arrow_pos + 3;
|
||||
|
||||
e = ParseTime(line, &idx, stop_time);
|
||||
if (e) // error
|
||||
return e;
|
||||
|
||||
e = ParseSettings(line, idx, settings);
|
||||
if (e) // error
|
||||
return e;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
int Parser::ParseTime(const std::string& line, std::string::size_type* idx_ptr,
|
||||
Time* time) {
|
||||
if (idx_ptr == NULL)
|
||||
return -1;
|
||||
|
||||
std::string::size_type& idx = *idx_ptr;
|
||||
|
||||
if (idx == std::string::npos || idx >= line.length())
|
||||
return -1;
|
||||
|
||||
if (time == NULL)
|
||||
return -1;
|
||||
|
||||
// Consume any whitespace that precedes the timestamp.
|
||||
|
||||
while (char c = line[idx]) {
|
||||
if (c != kSPACE && c != kTAB)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
|
||||
// WebVTT timestamp syntax comes in three flavors:
|
||||
// SS[.sss]
|
||||
// MM:SS[.sss]
|
||||
// HH:MM:SS[.sss]
|
||||
|
||||
// Parse a generic number value. We don't know which component
|
||||
// of the time we have yet, until we do more parsing.
|
||||
|
||||
int val = ParseNumber(line, &idx);
|
||||
|
||||
if (val < 0) // error
|
||||
return val;
|
||||
|
||||
Time& t = *time;
|
||||
|
||||
// The presence of a colon character indicates that we have
|
||||
// an [HH:]MM:SS style syntax.
|
||||
|
||||
if (line[idx] == ':') {
|
||||
// We have either HH:MM:SS or MM:SS
|
||||
|
||||
// The value we just parsed is either the hours or minutes.
|
||||
// It must be followed by another number value (that is
|
||||
// either minutes or seconds).
|
||||
|
||||
const int first_val = val;
|
||||
|
||||
++idx; // consume colon
|
||||
|
||||
// Parse second value
|
||||
|
||||
val = ParseNumber(line, &idx);
|
||||
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val >= 60) // either MM or SS
|
||||
return -1;
|
||||
|
||||
if (line[idx] == ':') {
|
||||
// We have HH:MM:SS
|
||||
|
||||
t.hours = first_val;
|
||||
t.minutes = val; // vetted above
|
||||
|
||||
++idx; // consume MM:SS colon
|
||||
|
||||
// We have parsed the hours and minutes.
|
||||
// We must now parse the seconds.
|
||||
|
||||
val = ParseNumber(line, &idx);
|
||||
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val >= 60) // SS part of HH:MM:SS
|
||||
return -1;
|
||||
|
||||
t.seconds = val;
|
||||
} else {
|
||||
// We have MM:SS
|
||||
|
||||
// The implication here is that the hour value was omitted
|
||||
// from the timestamp (because it was 0).
|
||||
|
||||
if (first_val >= 60) // minutes
|
||||
return -1;
|
||||
|
||||
t.hours = 0;
|
||||
t.minutes = first_val;
|
||||
t.seconds = val; // vetted above
|
||||
}
|
||||
} else {
|
||||
// We have SS (only)
|
||||
|
||||
// The time is expressed as total number of seconds,
|
||||
// so the seconds value has no upper bound.
|
||||
|
||||
t.seconds = val;
|
||||
|
||||
// Convert SS to HH:MM:SS
|
||||
|
||||
t.minutes = t.seconds / 60;
|
||||
t.seconds -= t.minutes * 60;
|
||||
|
||||
t.hours = t.minutes / 60;
|
||||
t.minutes -= t.hours * 60;
|
||||
}
|
||||
|
||||
// We have parsed the hours, minutes, and seconds.
|
||||
// We must now parse the milliseconds.
|
||||
|
||||
char c = line[idx];
|
||||
|
||||
// TODO(matthewjheaney): one option here is to slightly relax the
|
||||
// syntax rules for WebVTT timestamps, to permit the comma character
|
||||
// to also be used as the seconds/milliseconds separator. This
|
||||
// would handle streams that use localization conventions for
|
||||
// countries in Western Europe. For now we obey the rules specified
|
||||
// in the WebVTT spec (allow "full stop" only).
|
||||
|
||||
const bool have_milliseconds = (c == '.');
|
||||
|
||||
if (!have_milliseconds) {
|
||||
t.milliseconds = 0;
|
||||
} else {
|
||||
++idx; // consume FULL STOP
|
||||
|
||||
val = ParseNumber(line, &idx);
|
||||
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val >= 1000)
|
||||
return -1;
|
||||
|
||||
if (val < 10)
|
||||
t.milliseconds = val * 100;
|
||||
else if (val < 100)
|
||||
t.milliseconds = val * 10;
|
||||
else
|
||||
t.milliseconds = val;
|
||||
}
|
||||
|
||||
// We have parsed the time proper. We must check for any
|
||||
// junk that immediately follows the time specifier.
|
||||
|
||||
c = line[idx];
|
||||
|
||||
if (c != kNUL && c != kSPACE && c != kTAB)
|
||||
return -1;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
int Parser::ParseSettings(const std::string& line, std::string::size_type idx,
|
||||
Cue::settings_t* settings) {
|
||||
settings->clear();
|
||||
|
||||
if (idx == std::string::npos || idx >= line.length())
|
||||
return -1;
|
||||
|
||||
for (;;) {
|
||||
// We must parse a line comprising a sequence of 0 or more
|
||||
// NAME:VALUE pairs, separated by whitespace. The line iself is
|
||||
// terminated with a NUL char (indicating end-of-line).
|
||||
|
||||
for (;;) {
|
||||
const char c = line[idx];
|
||||
|
||||
if (c == kNUL) // end-of-line
|
||||
return 0; // success
|
||||
|
||||
if (c != kSPACE && c != kTAB)
|
||||
break;
|
||||
|
||||
++idx; // consume whitespace
|
||||
}
|
||||
|
||||
// We have consumed the whitespace, and have not yet reached
|
||||
// end-of-line, so there is something on the line for us to parse.
|
||||
|
||||
settings->push_back(Setting());
|
||||
Setting& s = settings->back();
|
||||
|
||||
// Parse the NAME part of the settings pair.
|
||||
|
||||
for (;;) {
|
||||
const char c = line[idx];
|
||||
|
||||
if (c == ':') // we have reached end of NAME part
|
||||
break;
|
||||
|
||||
if (c == kNUL || c == kSPACE || c == kTAB)
|
||||
return -1;
|
||||
|
||||
s.name.push_back(c);
|
||||
|
||||
++idx;
|
||||
}
|
||||
|
||||
if (s.name.empty())
|
||||
return -1;
|
||||
|
||||
++idx; // consume colon
|
||||
|
||||
// Parse the VALUE part of the settings pair.
|
||||
|
||||
for (;;) {
|
||||
const char c = line[idx];
|
||||
|
||||
if (c == kNUL || c == kSPACE || c == kTAB)
|
||||
break;
|
||||
|
||||
if (c == ':') // suspicious when part of VALUE
|
||||
return -1; // TODO(matthewjheaney): verify this behavior
|
||||
|
||||
s.value.push_back(c);
|
||||
|
||||
++idx;
|
||||
}
|
||||
|
||||
if (s.value.empty())
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int Parser::ParseNumber(const std::string& line,
|
||||
std::string::size_type* idx_ptr) {
|
||||
if (idx_ptr == NULL)
|
||||
return -1;
|
||||
|
||||
std::string::size_type& idx = *idx_ptr;
|
||||
|
||||
if (idx == std::string::npos || idx >= line.length())
|
||||
return -1;
|
||||
|
||||
if (!isdigit(line[idx]))
|
||||
return -1;
|
||||
|
||||
int result = 0;
|
||||
|
||||
while (isdigit(line[idx])) {
|
||||
const char c = line[idx];
|
||||
const int i = c - '0';
|
||||
|
||||
if (result > INT_MAX / 10)
|
||||
return -1;
|
||||
|
||||
result *= 10;
|
||||
|
||||
if (result > INT_MAX - i)
|
||||
return -1;
|
||||
|
||||
result += i;
|
||||
|
||||
++idx;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Time::operator==(const Time& rhs) const {
|
||||
if (hours != rhs.hours)
|
||||
return false;
|
||||
|
||||
if (minutes != rhs.minutes)
|
||||
return false;
|
||||
|
||||
if (seconds != rhs.seconds)
|
||||
return false;
|
||||
|
||||
return (milliseconds == rhs.milliseconds);
|
||||
}
|
||||
|
||||
bool Time::operator<(const Time& rhs) const {
|
||||
if (hours < rhs.hours)
|
||||
return true;
|
||||
|
||||
if (hours > rhs.hours)
|
||||
return false;
|
||||
|
||||
if (minutes < rhs.minutes)
|
||||
return true;
|
||||
|
||||
if (minutes > rhs.minutes)
|
||||
return false;
|
||||
|
||||
if (seconds < rhs.seconds)
|
||||
return true;
|
||||
|
||||
if (seconds > rhs.seconds)
|
||||
return false;
|
||||
|
||||
return (milliseconds < rhs.milliseconds);
|
||||
}
|
||||
|
||||
bool Time::operator>(const Time& rhs) const { return rhs.operator<(*this); }
|
||||
|
||||
bool Time::operator<=(const Time& rhs) const { return !this->operator>(rhs); }
|
||||
|
||||
bool Time::operator>=(const Time& rhs) const { return !this->operator<(rhs); }
|
||||
|
||||
presentation_t Time::presentation() const {
|
||||
const presentation_t h = 1000LL * 3600LL * presentation_t(hours);
|
||||
const presentation_t m = 1000LL * 60LL * presentation_t(minutes);
|
||||
const presentation_t s = 1000LL * presentation_t(seconds);
|
||||
const presentation_t result = h + m + s + milliseconds;
|
||||
return result;
|
||||
}
|
||||
|
||||
Time& Time::presentation(presentation_t d) {
|
||||
if (d < 0) { // error
|
||||
hours = 0;
|
||||
minutes = 0;
|
||||
seconds = 0;
|
||||
milliseconds = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
seconds = static_cast<int>(d / 1000);
|
||||
milliseconds = static_cast<int>(d - 1000 * seconds);
|
||||
|
||||
minutes = seconds / 60;
|
||||
seconds -= 60 * minutes;
|
||||
|
||||
hours = minutes / 60;
|
||||
minutes -= 60 * hours;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Time& Time::operator+=(presentation_t rhs) {
|
||||
const presentation_t d = this->presentation();
|
||||
const presentation_t dd = d + rhs;
|
||||
this->presentation(dd);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Time Time::operator+(presentation_t d) const {
|
||||
Time t(*this);
|
||||
t += d;
|
||||
return t;
|
||||
}
|
||||
|
||||
Time& Time::operator-=(presentation_t d) { return this->operator+=(-d); }
|
||||
|
||||
presentation_t Time::operator-(const Time& t) const {
|
||||
const presentation_t rhs = t.presentation();
|
||||
const presentation_t lhs = this->presentation();
|
||||
const presentation_t result = lhs - rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace libwebvtt
|
||||
158
webvttparser.h
Normal file
158
webvttparser.h
Normal file
@@ -0,0 +1,158 @@
|
||||
// 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 WEBVTTPARSER_H_ // NOLINT
|
||||
#define WEBVTTPARSER_H_
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
namespace libwebvtt {
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
// Fetch a character from the stream. Return
|
||||
// negative if error, positive if end-of-stream,
|
||||
// and 0 if a character is available.
|
||||
virtual int GetChar(char* c) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~Reader();
|
||||
};
|
||||
|
||||
class LineReader : protected Reader {
|
||||
public:
|
||||
// Consume a line of text from the stream, stripping off
|
||||
// the line terminator characters. Returns negative if error,
|
||||
// 0 on success, and positive at end-of-stream.
|
||||
int GetLine(std::string* line);
|
||||
|
||||
protected:
|
||||
virtual ~LineReader();
|
||||
|
||||
// Puts a character back into the stream.
|
||||
virtual void UngetChar(char c) = 0;
|
||||
};
|
||||
|
||||
// As measured in thousandths of a second,
|
||||
// e.g. a duration of 1 equals 0.001 seconds,
|
||||
// and a duration of 1000 equals 1 second.
|
||||
typedef long long presentation_t; // NOLINT
|
||||
|
||||
struct Time {
|
||||
int hours;
|
||||
int minutes;
|
||||
int seconds;
|
||||
int milliseconds;
|
||||
|
||||
bool operator==(const Time& rhs) const;
|
||||
bool operator<(const Time& rhs) const;
|
||||
bool operator>(const Time& rhs) const;
|
||||
bool operator<=(const Time& rhs) const;
|
||||
bool operator>=(const Time& rhs) const;
|
||||
|
||||
presentation_t presentation() const;
|
||||
Time& presentation(presentation_t);
|
||||
|
||||
Time& operator+=(presentation_t);
|
||||
Time operator+(presentation_t) const;
|
||||
|
||||
Time& operator-=(presentation_t);
|
||||
presentation_t operator-(const Time&) const;
|
||||
};
|
||||
|
||||
struct Setting {
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct Cue {
|
||||
std::string identifier;
|
||||
|
||||
Time start_time;
|
||||
Time stop_time;
|
||||
|
||||
typedef std::list<Setting> settings_t;
|
||||
settings_t settings;
|
||||
|
||||
typedef std::list<std::string> payload_t;
|
||||
payload_t payload;
|
||||
};
|
||||
|
||||
class Parser : private LineReader {
|
||||
public:
|
||||
explicit Parser(Reader* r);
|
||||
virtual ~Parser();
|
||||
|
||||
// Pre-parse enough of the stream to determine whether
|
||||
// this is really a WEBVTT file. Returns 0 on success,
|
||||
// negative if error.
|
||||
int Init();
|
||||
|
||||
// Parse the next WebVTT cue from the stream. Returns 0 if
|
||||
// an entire cue was parsed, negative if error, and positive
|
||||
// at end-of-stream.
|
||||
int Parse(Cue* cue);
|
||||
|
||||
private:
|
||||
// Returns the next character in the stream, using the look-back character
|
||||
// if present (as per Reader::GetChar).
|
||||
virtual int GetChar(char* c);
|
||||
|
||||
// Puts a character back into the stream (as per LineReader::UngetChar).
|
||||
virtual void UngetChar(char c);
|
||||
|
||||
// Check for presence of a UTF-8 BOM in the stream. Returns
|
||||
// negative if error, 0 on success, and positive at end-of-stream.
|
||||
int ParseBOM();
|
||||
|
||||
// Parse the distinguished "cue timings" line, which includes the start
|
||||
// and stop times and settings. Argument |line| contains the complete
|
||||
// line of text (as returned by ParseLine()), which the function is free
|
||||
// to modify as it sees fit, to facilitate scanning. Argument |arrow_pos|
|
||||
// is the offset of the arrow token ("-->"), which indicates that this is
|
||||
// the timings line. Returns negative if error, 0 on success.
|
||||
//
|
||||
static int ParseTimingsLine(std::string* line,
|
||||
std::string::size_type arrow_pos,
|
||||
Time* start_time, Time* stop_time,
|
||||
Cue::settings_t* settings);
|
||||
|
||||
// Parse a single time specifier (from the timings line), starting
|
||||
// at the given offset; lexical scanning stops when a NUL character
|
||||
// is detected. The function modifies offset |off| by the number of
|
||||
// characters consumed. Returns negative if error, 0 on success.
|
||||
//
|
||||
static int ParseTime(const std::string& line, std::string::size_type* off,
|
||||
Time* time);
|
||||
|
||||
// Parse the cue settings from the timings line, starting at the
|
||||
// given offset. Returns negative if error, 0 on success.
|
||||
//
|
||||
static int ParseSettings(const std::string& line, std::string::size_type off,
|
||||
Cue::settings_t* settings);
|
||||
|
||||
// Parse a non-negative integer from the characters in |line| beginning
|
||||
// at offset |off|. The function increments |off| by the number
|
||||
// of characters consumed. Returns the value, or negative if error.
|
||||
//
|
||||
static int ParseNumber(const std::string& line, std::string::size_type* off);
|
||||
|
||||
Reader* const reader_;
|
||||
|
||||
// Provides one character's worth of look-back, to facilitate scanning.
|
||||
int unget_;
|
||||
|
||||
// Disable copy ctor and copy assign for Parser.
|
||||
Parser(const Parser&);
|
||||
Parser& operator=(const Parser&);
|
||||
};
|
||||
|
||||
} // namespace libwebvtt
|
||||
|
||||
#endif // WEBVTTPARSER_H_ // NOLINT
|
||||
Reference in New Issue
Block a user