Compare commits
299 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 |
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
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
*.MKV
|
||||
core
|
||||
*.a
|
||||
*.d
|
||||
*.so*
|
||||
*.o
|
||||
*~
|
||||
@@ -13,3 +14,14 @@ core
|
||||
*.webm
|
||||
Debug
|
||||
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)
|
||||
25
Makefile
25
Makefile
@@ -1,25 +0,0 @@
|
||||
CXX := g++
|
||||
CXXFLAGS := -W -Wall -g
|
||||
LIBWEBM := libwebm.a
|
||||
WEBMOBJS := mkvparser.o mkvreader.o mkvmuxer.o mkvmuxerutil.o mkvwriter.o
|
||||
OBJECTS1 := $(PARSEOBJ) sample.o
|
||||
OBJECTS2 := $(PARSEOBJ) $(MUXEROBJ) sample_muxer/sample_muxer.o
|
||||
INCLUDES := -I.
|
||||
EXES := samplemuxer sample
|
||||
|
||||
all: $(EXES)
|
||||
|
||||
sample: sample.o $(LIBWEBM)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
samplemuxer: sample_muxer/sample_muxer.o $(LIBWEBM)
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
libwebm.a: $(WEBMOBJS)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) -f $(OBJECTS1) $(OBJECTS2) $(WEBMOBJS) $(LIBWEBM) $(EXES) Makefile.bak
|
||||
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}."
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwebm", "libwebm_2005.vcproj", "{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
ProjectSection(WebsiteProperties) = preProject
|
||||
Debug.AspNetCompiler.Debug = "True"
|
||||
Release.AspNetCompiler.Debug = "False"
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample", "sample_2005.vcproj", "{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4} = {F9128EC6-C008-41AD-B38F-0E70D549D9F4}
|
||||
EndProjectSection
|
||||
ProjectSection(WebsiteProperties) = preProject
|
||||
Debug.AspNetCompiler.Debug = "True"
|
||||
Release.AspNetCompiler.Debug = "False"
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{F9128EC6-C008-41AD-B38F-0E70D549D9F4}.Release|Win32.Build.0 = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,204 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="libwebm"
|
||||
ProjectGUID="{F9128EC6-C008-41AD-B38F-0E70D549D9F4}"
|
||||
RootNamespace="mkvparser"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxer.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxertypes.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\webmids.hpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample", "sample_2008.vcproj", "{0CB5681F-6065-490C-98C8-05531732ED7E}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA} = {7B1F12CA-0724-430B-B61A-1D357C912CBA}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwebm", "libwebm_2008.vcproj", "{7B1F12CA-0724-430B-B61A-1D357C912CBA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample_muxer", "sample_muxer\sample_muxer.vcproj", "{B407561F-1F5E-4798-B9C2-81AB09CFBC16}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA} = {7B1F12CA-0724-430B-B61A-1D357C912CBA}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0CB5681F-6065-490C-98C8-05531732ED7E}.Release|Win32.Build.0 = Release|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{7B1F12CA-0724-430B-B61A-1D357C912CBA}.Release|Win32.Build.0 = Release|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{B407561F-1F5E-4798-B9C2-81AB09CFBC16}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,209 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="libwebm"
|
||||
ProjectGUID="{7B1F12CA-0724-430B-B61A-1D357C912CBA}"
|
||||
RootNamespace="libwebm"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="196613"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
EnableIntrinsicFunctions="true"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxer.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mkvmuxer.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxertypes.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvmuxerutil.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvparser.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvreader.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mkvwriter.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\webmids.hpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
2062
mkvmuxer.cpp
2062
mkvmuxer.cpp
File diff suppressed because it is too large
Load Diff
802
mkvmuxer.hpp
802
mkvmuxer.hpp
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011 The WebM project authors. All Rights Reserved.
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
@@ -19,6 +19,7 @@
|
||||
namespace mkvmuxer {
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef short int16;
|
||||
typedef int int32;
|
||||
typedef unsigned int uint32;
|
||||
typedef long long int64;
|
||||
|
||||
353
mkvmuxerutil.cpp
353
mkvmuxerutil.cpp
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011 The WebM project authors. All Rights Reserved.
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
@@ -8,21 +8,174 @@
|
||||
|
||||
#include "mkvmuxerutil.hpp"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#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 {
|
||||
|
||||
int32 GetCodedUIntSize(uint64 value) {
|
||||
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)
|
||||
@@ -41,7 +194,6 @@ int32 GetCodedUIntSize(uint64 value) {
|
||||
}
|
||||
|
||||
int32 GetUIntSize(uint64 value) {
|
||||
|
||||
if (value < 0x0000000000000100ULL)
|
||||
return 1;
|
||||
else if (value < 0x0000000000010000ULL)
|
||||
@@ -59,6 +211,13 @@ int32 GetUIntSize(uint64 value) {
|
||||
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);
|
||||
@@ -69,6 +228,19 @@ uint64 EbmlMasterElementSize(uint64 type, uint64 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);
|
||||
@@ -82,12 +254,12 @@ uint64 EbmlElementSize(uint64 type, uint64 value) {
|
||||
return ebml_size;
|
||||
}
|
||||
|
||||
uint64 EbmlElementSize(uint64 type, float value) {
|
||||
uint64 EbmlElementSize(uint64 type, float /* value */) {
|
||||
// Size of EBML ID
|
||||
uint64 ebml_size = GetUIntSize(type);
|
||||
|
||||
// Datasize
|
||||
ebml_size += sizeof(value);
|
||||
ebml_size += sizeof(float);
|
||||
|
||||
// Size of Datasize
|
||||
ebml_size++;
|
||||
@@ -127,6 +299,19 @@ uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 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;
|
||||
@@ -151,16 +336,22 @@ int32 SerializeFloat(IMkvWriter* writer, float f) {
|
||||
if (!writer)
|
||||
return -1;
|
||||
|
||||
const uint32& val = reinterpret_cast<const uint32&>(f);
|
||||
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 uint32 bb = val >> bit_count;
|
||||
const uint8 b = static_cast<uint8>(bb);
|
||||
const uint8 byte = static_cast<uint8>(value.u32 >> bit_count);
|
||||
|
||||
const int32 status = writer->Write(&b, 1);
|
||||
const int32 status = writer->Write(&byte, 1);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
@@ -253,6 +444,23 @@ bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) {
|
||||
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;
|
||||
@@ -276,19 +484,17 @@ bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value) {
|
||||
if (WriteID(writer, type))
|
||||
return false;
|
||||
|
||||
const int32 length = strlen(value);
|
||||
const uint64 length = strlen(value);
|
||||
if (WriteUInt(writer, length))
|
||||
return false;
|
||||
|
||||
if (writer->Write(value, length))
|
||||
if (writer->Write(value, static_cast<const uint32>(length)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteEbmlElement(IMkvWriter* writer,
|
||||
uint64 type,
|
||||
const uint8* value,
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
|
||||
uint64 size) {
|
||||
if (!writer || !value || size < 1)
|
||||
return false;
|
||||
@@ -305,43 +511,41 @@ bool WriteEbmlElement(IMkvWriter* writer,
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64 WriteSimpleBlock(IMkvWriter* writer,
|
||||
const uint8* data,
|
||||
uint64 length,
|
||||
char track_number,
|
||||
short timecode,
|
||||
bool is_key) {
|
||||
|
||||
if (!writer || !data || length < 1 || track_number < 1 || timecode < 0)
|
||||
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) {
|
||||
if (!writer)
|
||||
return false;
|
||||
|
||||
if (WriteID(writer, kMkvSimpleBlock))
|
||||
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;
|
||||
|
||||
const int32 size = static_cast<int32>(length) + 4;
|
||||
if (WriteUInt(writer, size))
|
||||
// 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;
|
||||
|
||||
if (WriteUInt(writer, static_cast<uint64>(track_number)))
|
||||
return 0;
|
||||
|
||||
if (SerializeInt(writer, static_cast<uint64>(timecode), 2))
|
||||
return 0;
|
||||
|
||||
uint64 flags = 0;
|
||||
if(is_key)
|
||||
flags |= 0x80;
|
||||
|
||||
if (SerializeInt(writer, flags, 1))
|
||||
return 0;
|
||||
|
||||
if (writer->Write(data, static_cast<uint32>(length)))
|
||||
return 0;
|
||||
|
||||
const uint64 element_size =
|
||||
GetUIntSize(kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + length;
|
||||
|
||||
return element_size;
|
||||
return frame->CanBeSimpleBlock() ?
|
||||
WriteSimpleBlock(writer, frame, relative_timecode) :
|
||||
WriteBlock(writer, frame, relative_timecode,
|
||||
cluster->timecode_scale());
|
||||
}
|
||||
|
||||
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
|
||||
@@ -350,8 +554,8 @@ uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
|
||||
|
||||
// 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;
|
||||
uint64 void_size =
|
||||
EbmlMasterElementSize(kMkvVoid, void_entry_size) + void_entry_size;
|
||||
|
||||
if (void_size != size)
|
||||
return 0;
|
||||
@@ -380,11 +584,46 @@ uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
|
||||
return void_size;
|
||||
}
|
||||
|
||||
void GetVersion(int32& major, int32& minor, int32& build, int32& revision) {
|
||||
major = 0;
|
||||
minor = 0;
|
||||
build = 0;
|
||||
revision = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef MKVMUXERUTIL_HPP
|
||||
#define MKVMUXERUTIL_HPP
|
||||
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "mkvmuxertypes.hpp"
|
||||
|
||||
namespace mkvmuxer {
|
||||
@@ -16,16 +17,22 @@ 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
|
||||
@@ -46,27 +53,17 @@ 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,
|
||||
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
|
||||
uint64 size);
|
||||
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value);
|
||||
|
||||
// Output an Mkv Simple Block.
|
||||
// Inputs:
|
||||
// data: Pointer to the data.
|
||||
// length: Length of the data.
|
||||
// track_number: Track to add the data to. Value returned by Add track
|
||||
// functions.
|
||||
// timecode: Relative timecode of the Block.
|
||||
// is_key: Flag telling whether or not this frame is a key frame.
|
||||
uint64 WriteSimpleBlock(IMkvWriter* writer,
|
||||
const uint8* data,
|
||||
uint64 length,
|
||||
char track_number,
|
||||
short timecode,
|
||||
bool is_key);
|
||||
// Output a 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
|
||||
@@ -75,7 +72,11 @@ 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);
|
||||
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
|
||||
|
||||
|
||||
7861
mkvparser.cpp
7861
mkvparser.cpp
File diff suppressed because it is too large
Load Diff
629
mkvparser.hpp
629
mkvparser.hpp
File diff suppressed because it is too large
Load Diff
@@ -10,28 +10,28 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace mkvparser
|
||||
{
|
||||
namespace mkvparser {
|
||||
|
||||
MkvReader::MkvReader() :
|
||||
m_file(NULL)
|
||||
{
|
||||
MkvReader::MkvReader() : m_file(NULL), reader_owns_file_(true) {}
|
||||
|
||||
MkvReader::MkvReader(FILE* fp) : m_file(fp), reader_owns_file_(false) {
|
||||
GetFileSize();
|
||||
}
|
||||
|
||||
MkvReader::~MkvReader()
|
||||
{
|
||||
MkvReader::~MkvReader() {
|
||||
if (reader_owns_file_)
|
||||
Close();
|
||||
m_file = NULL;
|
||||
}
|
||||
|
||||
int MkvReader::Open(const char* fileName)
|
||||
{
|
||||
int MkvReader::Open(const char* fileName) {
|
||||
if (fileName == NULL)
|
||||
return -1;
|
||||
|
||||
if (m_file)
|
||||
return -1;
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _MSC_VER
|
||||
const errno_t e = fopen_s(&m_file, fileName, "rb");
|
||||
|
||||
if (e)
|
||||
@@ -42,12 +42,17 @@ int MkvReader::Open(const char* fileName)
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
#endif
|
||||
return !GetFileSize();
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
bool MkvReader::GetFileSize() {
|
||||
if (m_file == NULL)
|
||||
return false;
|
||||
#ifdef _MSC_VER
|
||||
int status = _fseeki64(m_file, 0L, SEEK_END);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
return false; // error
|
||||
|
||||
m_length = _ftelli64(m_file);
|
||||
#else
|
||||
@@ -56,29 +61,29 @@ int MkvReader::Open(const char* fileName)
|
||||
#endif
|
||||
assert(m_length >= 0);
|
||||
|
||||
#ifdef WIN32
|
||||
if (m_length < 0)
|
||||
return false;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
status = _fseeki64(m_file, 0L, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
return -1; //error
|
||||
return false; // error
|
||||
#else
|
||||
fseek(m_file, 0L, SEEK_SET);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MkvReader::Close()
|
||||
{
|
||||
if (m_file != NULL)
|
||||
{
|
||||
void MkvReader::Close() {
|
||||
if (m_file != NULL) {
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int MkvReader::Length(long long* total, long long* available)
|
||||
{
|
||||
int MkvReader::Length(long long* total, long long* available) {
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -91,8 +96,7 @@ int MkvReader::Length(long long* total, long long* available)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MkvReader::Read(long long offset, long len, unsigned char* buffer)
|
||||
{
|
||||
int MkvReader::Read(long long offset, long len, unsigned char* buffer) {
|
||||
if (m_file == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -108,7 +112,7 @@ int MkvReader::Read(long long offset, long len, unsigned char* buffer)
|
||||
if (offset >= m_length)
|
||||
return -1;
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _MSC_VER
|
||||
const int status = _fseeki64(m_file, offset, SEEK_SET);
|
||||
|
||||
if (status)
|
||||
|
||||
@@ -12,26 +12,32 @@
|
||||
#include "mkvparser.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
namespace mkvparser
|
||||
{
|
||||
namespace mkvparser {
|
||||
|
||||
class MkvReader : public IMkvReader
|
||||
{
|
||||
MkvReader(const MkvReader&);
|
||||
MkvReader& operator=(const MkvReader&);
|
||||
class MkvReader : public IMkvReader {
|
||||
public:
|
||||
MkvReader();
|
||||
explicit MkvReader(FILE* fp);
|
||||
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:
|
||||
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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011 The WebM project authors. All Rights Reserved.
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
namespace mkvmuxer {
|
||||
|
||||
MkvWriter::MkvWriter() : file_(NULL) {
|
||||
}
|
||||
MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
|
||||
|
||||
MkvWriter::~MkvWriter() {
|
||||
Close();
|
||||
}
|
||||
MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {}
|
||||
|
||||
MkvWriter::~MkvWriter() { Close(); }
|
||||
|
||||
int32 MkvWriter::Write(const void* buffer, uint32 length) {
|
||||
if (!file_)
|
||||
@@ -56,10 +55,10 @@ bool MkvWriter::Open(const char* filename) {
|
||||
}
|
||||
|
||||
void MkvWriter::Close() {
|
||||
if (file_) {
|
||||
if (file_ && writer_owns_file_) {
|
||||
fclose(file_);
|
||||
file_ = NULL;
|
||||
}
|
||||
file_ = NULL;
|
||||
}
|
||||
|
||||
int64 MkvWriter::Position() const {
|
||||
@@ -84,11 +83,8 @@ int32 MkvWriter::Position(int64 position) {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MkvWriter::Seekable() const {
|
||||
return true;
|
||||
}
|
||||
bool MkvWriter::Seekable() const { return true; }
|
||||
|
||||
void MkvWriter::ElementStartNotify(uint64, int64) {
|
||||
}
|
||||
void MkvWriter::ElementStartNotify(uint64, int64) {}
|
||||
|
||||
} // namespace mkvmuxer
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace mkvmuxer {
|
||||
class MkvWriter : public IMkvWriter {
|
||||
public:
|
||||
MkvWriter();
|
||||
explicit MkvWriter(FILE* fp);
|
||||
virtual ~MkvWriter();
|
||||
|
||||
// IMkvWriter interface
|
||||
@@ -40,6 +41,7 @@ class MkvWriter : public IMkvWriter {
|
||||
private:
|
||||
// File handle to output file.
|
||||
FILE* file_;
|
||||
bool writer_owns_file_;
|
||||
|
||||
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
|
||||
};
|
||||
|
||||
207
sample.cpp
207
sample.cpp
@@ -6,24 +6,20 @@
|
||||
// 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.
|
||||
// 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
|
||||
// Silences these warnings:
|
||||
// warning C4996: 'mbstowcs': This function or variable may be unsafe. Consider
|
||||
// using mbstowcs_s instead. To disable deprecation, use
|
||||
// _CRT_SECURE_NO_WARNINGS. See online help for details.
|
||||
// Fixing this warning requires use of a function available only on Windows,
|
||||
// and this sample code must support non-windows platforms.
|
||||
// Disable MSVC warnings that suggest making code non-portable.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
static const wchar_t* utf8towcs(const char* str)
|
||||
{
|
||||
static const wchar_t* utf8towcs(const char* str) {
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -32,10 +28,12 @@ static const wchar_t* utf8towcs(const char* str)
|
||||
|
||||
const size_t size = mbstowcs(NULL, str, 0);
|
||||
|
||||
if (size == 0)
|
||||
if (size == 0 || size == static_cast<size_t>(-1))
|
||||
return NULL;
|
||||
|
||||
wchar_t* const val = new wchar_t[size+1];
|
||||
wchar_t* const val = new (std::nothrow) wchar_t[size + 1];
|
||||
if (val == NULL)
|
||||
return NULL;
|
||||
|
||||
mbstowcs(val, str, size);
|
||||
val[size] = L'\0';
|
||||
@@ -43,11 +41,23 @@ static const wchar_t* utf8towcs(const char* str)
|
||||
return val;
|
||||
}
|
||||
|
||||
bool InputHasCues(const mkvparser::Segment* const segment) {
|
||||
const mkvparser::Cues* const cues = segment->GetCues();
|
||||
if (cues == NULL)
|
||||
return false;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
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");
|
||||
@@ -59,8 +69,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
MkvReader reader;
|
||||
|
||||
if (reader.Open(argv[1]))
|
||||
{
|
||||
if (reader.Open(argv[1])) {
|
||||
printf("\n Filename is invalid or error while opening.\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -74,7 +83,11 @@ int main(int argc, char* argv[])
|
||||
|
||||
EBMLHeader ebmlHeader;
|
||||
|
||||
ebmlHeader.Parse(&reader, pos);
|
||||
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);
|
||||
@@ -83,23 +96,28 @@ int main(int argc, char* argv[])
|
||||
printf("\t\tDoc Type\t\t: %s\n", ebmlHeader.m_docType);
|
||||
printf("\t\tPos\t\t\t: %lld\n", pos);
|
||||
|
||||
mkvparser::Segment* pSegment;
|
||||
typedef mkvparser::Segment seg_t;
|
||||
seg_t* pSegment_;
|
||||
|
||||
long long ret = mkvparser::Segment::CreateInstance(&reader, pos, pSegment);
|
||||
if (ret)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
@@ -123,25 +141,21 @@ int main(int argc, char* argv[])
|
||||
|
||||
if (pTitle == NULL)
|
||||
printf("\t\tTrack Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
else {
|
||||
printf("\t\tTrack Name\t\t: %ls\n", pTitle);
|
||||
delete[] pTitle;
|
||||
}
|
||||
|
||||
|
||||
if (pMuxingApp == NULL)
|
||||
printf("\t\tMuxing App\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
else {
|
||||
printf("\t\tMuxing App\t\t: %ls\n", pMuxingApp);
|
||||
delete[] pMuxingApp;
|
||||
}
|
||||
|
||||
if (pWritingApp == NULL)
|
||||
printf("\t\tWriting App\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
else {
|
||||
printf("\t\tWriting App\t\t: %ls\n", pWritingApp);
|
||||
delete[] pWritingApp;
|
||||
}
|
||||
@@ -154,33 +168,29 @@ int main(int argc, char* argv[])
|
||||
|
||||
const mkvparser::Tracks* pTracks = pSegment->GetTracks();
|
||||
|
||||
unsigned long i = 0;
|
||||
const unsigned long j = pTracks->GetTracksCount();
|
||||
|
||||
enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
|
||||
unsigned long track_num = 0;
|
||||
const unsigned long num_tracks = pTracks->GetTracksCount();
|
||||
|
||||
printf("\n\t\t\t Track Info\n");
|
||||
|
||||
while (i != j)
|
||||
{
|
||||
const Track* const pTrack = pTracks->GetTrackByIndex(i++);
|
||||
while (track_num != num_tracks) {
|
||||
const Track* const pTrack = pTracks->GetTrackByIndex(track_num++);
|
||||
|
||||
if (pTrack == NULL)
|
||||
continue;
|
||||
|
||||
const long long trackType = pTrack->GetType();
|
||||
const long long trackNumber = pTrack->GetNumber();
|
||||
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: %lld\n", trackType);
|
||||
printf("\t\tTrack Number\t\t: %lld\n", trackNumber);
|
||||
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
|
||||
{
|
||||
else {
|
||||
printf("\t\tTrack Name\t\t: %ls \n", pTrackName);
|
||||
delete[] pTrackName;
|
||||
}
|
||||
@@ -197,14 +207,12 @@ int main(int argc, char* argv[])
|
||||
|
||||
if (pCodecName == NULL)
|
||||
printf("\t\tCodec Name\t\t: NULL\n");
|
||||
else
|
||||
{
|
||||
else {
|
||||
printf("\t\tCodec Name\t\t: %ls\n", pCodecName);
|
||||
delete[] pCodecName;
|
||||
}
|
||||
|
||||
if (trackType == VIDEO_TRACK)
|
||||
{
|
||||
if (trackType == mkvparser::Track::kVideo) {
|
||||
const VideoTrack* const pVideoTrack =
|
||||
static_cast<const VideoTrack*>(pTrack);
|
||||
|
||||
@@ -218,8 +226,7 @@ int main(int argc, char* argv[])
|
||||
printf("\t\tVideo Rate\t\t: %f\n", rate);
|
||||
}
|
||||
|
||||
if (trackType == AUDIO_TRACK)
|
||||
{
|
||||
if (trackType == mkvparser::Track::kAudio) {
|
||||
const AudioTrack* const pAudioTrack =
|
||||
static_cast<const AudioTrack*>(pTrack);
|
||||
|
||||
@@ -231,6 +238,12 @@ int main(int argc, char* argv[])
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,55 +252,115 @@ int main(int argc, char* argv[])
|
||||
|
||||
printf("\t\tCluster Count\t: %ld\n\n", clusterCount);
|
||||
|
||||
if (clusterCount == 0)
|
||||
{
|
||||
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())
|
||||
{
|
||||
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();
|
||||
const BlockEntry* pBlockEntry;
|
||||
|
||||
while ((pBlockEntry != NULL) && !pBlockEntry->EOS())
|
||||
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\n",
|
||||
(trackType == VIDEO_TRACK) ? "V" : "A",
|
||||
pBlock->IsKey() ? "I" : "P",
|
||||
time_ns);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
delete pSegment;
|
||||
return 0;
|
||||
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,194 +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="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Debug\libwebm.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Release\libwebm.lib"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -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\libwebm.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=".\Release\libwebm.lib"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
// libwebm parser includes
|
||||
#include "mkvreader.hpp"
|
||||
@@ -19,6 +22,16 @@
|
||||
#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() {
|
||||
@@ -33,13 +46,13 @@ void Usage() {
|
||||
printf(" -output_cues <int> >0 outputs cues element\n");
|
||||
printf(" -cues_on_video_track <int> >0 outputs cues on video track\n");
|
||||
printf(" -cues_on_audio_track <int> >0 outputs cues on audio track\n");
|
||||
printf(" 0 outputs cues on audio track\n");
|
||||
printf(" -max_cluster_duration <double> in seconds\n");
|
||||
printf(" -max_cluster_size <int> in bytes\n");
|
||||
printf(" -switch_tracks <int> >0 switches tracks in output\n");
|
||||
printf(" -audio_track_number <int> >0 Changes the audio track number\n");
|
||||
printf(" -video_track_number <int> >0 Changes the video track number\n");
|
||||
printf(" -chunking <string> Chunk output\n");
|
||||
printf(" -copy_tags <int> >0 Copies the tags\n");
|
||||
printf("\n");
|
||||
printf("Video options:\n");
|
||||
printf(" -display_width <int> Display width in pixels\n");
|
||||
@@ -48,13 +61,89 @@ void Usage() {
|
||||
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[]) {
|
||||
using mkvmuxer::uint64;
|
||||
|
||||
char* input = NULL;
|
||||
char* output = NULL;
|
||||
|
||||
@@ -63,14 +152,16 @@ int main(int argc, char* argv[]) {
|
||||
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 = 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;
|
||||
@@ -79,52 +170,66 @@ int main(int argc, char* argv[]) {
|
||||
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])) {
|
||||
} else if (!strcmp("-i", argv[i]) && i < argc_check) {
|
||||
input = argv[++i];
|
||||
} else if (!strcmp("-o", argv[i])) {
|
||||
} else if (!strcmp("-o", argv[i]) && i < argc_check) {
|
||||
output = argv[++i];
|
||||
} else if (!strcmp("-video", 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])) {
|
||||
} 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])) {
|
||||
} 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])) {
|
||||
} else if (!strcmp("-output_cues", argv[i]) && i < argc_check) {
|
||||
output_cues = strtol(argv[++i], &end, 10) == 0 ? false : true;
|
||||
} else if (!strcmp("-cues_on_video_track", argv[i])) {
|
||||
} 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;
|
||||
} else if (!strcmp("-cues_on_audio_track", argv[i])) {
|
||||
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;
|
||||
} else if (!strcmp("-max_cluster_duration", argv[i])) {
|
||||
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])) {
|
||||
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])) {
|
||||
} 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])) {
|
||||
} 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])) {
|
||||
} 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])) {
|
||||
} else if (!strcmp("-chunking", argv[i]) && i < argc_check) {
|
||||
chunking = true;
|
||||
chunk_name = argv[++i];
|
||||
} else if (!strcmp("-display_width", 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])) {
|
||||
} else if (!strcmp("-display_height", argv[i]) && i < argc_check) {
|
||||
display_height = strtol(argv[++i], &end, 10);
|
||||
} else if (!strcmp("-stereo_mode", argv[i])) {
|
||||
} 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])) {
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,17 +248,20 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
long long pos = 0;
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
ebml_header.Parse(&reader, pos);
|
||||
long long ret = ebml_header.Parse(&reader, pos);
|
||||
if (ret) {
|
||||
printf("\n EBMLHeader::Parse() failed.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mkvparser::Segment* parser_segment;
|
||||
long long ret = mkvparser::Segment::CreateInstance(&reader,
|
||||
pos,
|
||||
parser_segment);
|
||||
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.");
|
||||
@@ -161,12 +269,17 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!writer.Open(output)) {
|
||||
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;
|
||||
}
|
||||
@@ -198,13 +311,29 @@ int main(int argc, char* argv[]) {
|
||||
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
|
||||
enum { kVideoTrack = 1, kAudioTrack = 2 };
|
||||
const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks();
|
||||
unsigned long i = 0;
|
||||
uint64 vid_track = 0; // no track added
|
||||
uint64 aud_track = 0; // no track added
|
||||
|
||||
using mkvparser::Track;
|
||||
|
||||
while (i != parser_tracks->GetTracksCount()) {
|
||||
int track_num = i++;
|
||||
if (switch_tracks)
|
||||
@@ -221,7 +350,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
const long long track_type = parser_track->GetType();
|
||||
|
||||
if (track_type == kVideoTrack && output_video) {
|
||||
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);
|
||||
@@ -237,8 +366,7 @@ int main(int argc, char* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mkvmuxer::VideoTrack* const video =
|
||||
static_cast<mkvmuxer::VideoTrack*>(
|
||||
mkvmuxer::VideoTrack* const video = static_cast<mkvmuxer::VideoTrack*>(
|
||||
muxer_segment.GetTrackByNumber(vid_track));
|
||||
if (!video) {
|
||||
printf("\n Could not get video track.\n");
|
||||
@@ -248,6 +376,8 @@ int main(int argc, char* argv[]) {
|
||||
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)
|
||||
@@ -259,7 +389,7 @@ int main(int argc, char* argv[]) {
|
||||
if (rate > 0.0) {
|
||||
video->set_frame_rate(rate);
|
||||
}
|
||||
} else if (track_type == kAudioTrack && output_audio) {
|
||||
} 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);
|
||||
@@ -275,8 +405,7 @@ int main(int argc, char* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mkvmuxer::AudioTrack* const audio =
|
||||
static_cast<mkvmuxer::AudioTrack*>(
|
||||
mkvmuxer::AudioTrack* const audio = static_cast<mkvmuxer::AudioTrack*>(
|
||||
muxer_segment.GetTrackByNumber(aud_track));
|
||||
if (!audio) {
|
||||
printf("\n Could not get audio track.\n");
|
||||
@@ -286,6 +415,8 @@ int main(int argc, char* argv[]) {
|
||||
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);
|
||||
@@ -299,9 +430,32 @@ int main(int argc, char* argv[]) {
|
||||
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);
|
||||
@@ -317,21 +471,39 @@ int main(int argc, char* argv[]) {
|
||||
const mkvparser::Cluster* cluster = parser_segment->GetFirst();
|
||||
|
||||
while ((cluster != NULL) && !cluster->EOS()) {
|
||||
const mkvparser::BlockEntry* block_entry = cluster->GetFirst();
|
||||
const mkvparser::BlockEntry* block_entry;
|
||||
|
||||
long status = cluster->GetFirst(block_entry);
|
||||
|
||||
if (status) {
|
||||
printf("\n Could not get first block of cluster.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((block_entry != NULL) && !block_entry->EOS()) {
|
||||
const mkvparser::Block* const block = block_entry->GetBlock();
|
||||
const long long trackNum = block->GetTrackNumber();
|
||||
const mkvparser::Track* const parser_track =
|
||||
parser_tracks->GetTrackByNumber(
|
||||
static_cast<unsigned long>(trackNum));
|
||||
const long long track_type = parser_track->GetType();
|
||||
parser_tracks->GetTrackByNumber(static_cast<unsigned long>(trackNum));
|
||||
|
||||
if ((track_type == kAudioTrack && output_audio) ||
|
||||
(track_type == kVideoTrack && output_video)) {
|
||||
const int frame_count = block->GetFrameCount();
|
||||
// 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);
|
||||
const bool is_key = block->IsKey();
|
||||
|
||||
// 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);
|
||||
@@ -347,37 +519,65 @@ int main(int argc, char* argv[]) {
|
||||
if (frame.Read(&reader, data))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
uint64 track_num = vid_track;
|
||||
if (track_type == kAudioTrack)
|
||||
track_num = aud_track;
|
||||
|
||||
if (!muxer_segment.AddFrame(data,
|
||||
frame.len,
|
||||
track_num,
|
||||
time_ns,
|
||||
is_key)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_entry = cluster->GetNext(block_entry);
|
||||
status = cluster->GetNext(block_entry, block_entry);
|
||||
|
||||
if (status) {
|
||||
printf("\n Could not get next block of cluster.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = parser_segment->GetNext(cluster);
|
||||
}
|
||||
|
||||
muxer_segment.Finalize();
|
||||
// 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;
|
||||
delete parser_segment;
|
||||
|
||||
writer.Close();
|
||||
reader.Close();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="sample_muxer"
|
||||
ProjectGUID="{B407561F-1F5E-4798-B9C2-81AB09CFBC16}"
|
||||
RootNamespace="sample_muxer"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="196613"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="../"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="..\Debug\libwebm.lib"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
EnableIntrinsicFunctions="true"
|
||||
AdditionalIncludeDirectories="../"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="..\Release\libwebm.lib"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\sample_muxer.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
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_
|
||||
40
webmids.hpp
40
webmids.hpp
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011 The WebM project authors. All Rights Reserved.
|
||||
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
@@ -41,6 +41,7 @@ enum MkvId {
|
||||
kMkvTimecodeScale = 0x2AD7B1,
|
||||
kMkvDuration = 0x4489,
|
||||
kMkvDateUTC = 0x4461,
|
||||
kMkvTitle = 0x7BA9,
|
||||
kMkvMuxingApp = 0x4D80,
|
||||
kMkvWritingApp = 0x5741,
|
||||
// Cluster
|
||||
@@ -53,6 +54,11 @@ enum MkvId {
|
||||
kMkvReferenceBlock = 0xFB,
|
||||
kMkvLaceNumber = 0xCC,
|
||||
kMkvSimpleBlock = 0xA3,
|
||||
kMkvBlockAdditions = 0x75A1,
|
||||
kMkvBlockMore = 0xA6,
|
||||
kMkvBlockAddID = 0xEE,
|
||||
kMkvBlockAdditional = 0xA5,
|
||||
kMkvDiscardPadding = 0x75A2,
|
||||
// Track
|
||||
kMkvTracks = 0x1654AE6B,
|
||||
kMkvTrackEntry = 0xAE,
|
||||
@@ -64,15 +70,19 @@ enum MkvId {
|
||||
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,
|
||||
@@ -98,9 +108,19 @@ enum MkvId {
|
||||
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,
|
||||
@@ -110,6 +130,24 @@ enum MkvId {
|
||||
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
|
||||
|
||||
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