diff --git a/Makefile b/Makefile
index 4819893..2b84add 100644
--- a/Makefile
+++ b/Makefile
@@ -1,39 +1,41 @@
-PROJECT_NAME = dantto4k
-
-SRC_DIR = src
-OBJ_DIR = build
-
-SRC_FILES = $(filter-out $(SRC_DIR)/bonTuner.cpp $(SRC_DIR)/dllmain.cpp, $(wildcard $(SRC_DIR)/*.cpp))
-
-OBJ_FILES = $(SRC_FILES:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o)
-
-TSDUCK_INC = $(shell pkg-config --cflags-only-I tsduck)
-TSDUCK_LIB = $(shell pkg-config --libs tsduck)
-
-PCSC_INC = $(shell pkg-config --cflags-only-I libpcsclite)
-PCSC_LIB = $(shell pkg-config --libs libpcsclite)
-
-CXX = g++
-CXXFLAGS = -std=c++20 -Wall -maes -msse4.1 $(TSDUCK_INC) $(PCSC_INC) -Ithirdparty/asio/asio/include
-LDFLAGS = $(TSDUCK_LIB) $(PCSC_LIB)
-
-EXEC = $(OBJ_DIR)/$(PROJECT_NAME)
-
-all: $(EXEC)
-
-$(OBJ_DIR):
- mkdir -p $(OBJ_DIR)
-
-$(EXEC): $(OBJ_FILES)
- $(CXX) $(OBJ_FILES) $(LDFLAGS) -o $(EXEC)
-
-$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
- $(CXX) $(CXXFLAGS) -c $< -o $@
-
-clean:
- rm -rf $(OBJ_DIR)
-
-install:
- cp $(EXEC) /usr/local/bin/$(PROJECT_NAME)
-
-.PHONY: all clean install
\ No newline at end of file
+PROJECT_NAME = dantto4k
+
+SRC_DIR = src
+OBJ_DIR = build
+
+SRC_FILES = $(filter-out $(SRC_DIR)/bonTuner.cpp $(SRC_DIR)/dllmain.cpp, \
+ $(wildcard $(SRC_DIR)/*.cpp) $(wildcard $(SRC_DIR)/ttml/*.cpp))
+
+OBJ_FILES = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES))
+
+TSDUCK_INC = $(shell pkg-config --cflags-only-I tsduck)
+TSDUCK_LIB = $(shell pkg-config --libs tsduck)
+
+PCSC_INC = $(shell pkg-config --cflags-only-I libpcsclite)
+PCSC_LIB = $(shell pkg-config --libs libpcsclite)
+
+CXX = g++
+CXXFLAGS = -std=c++20 -Wall -maes -msse4.1 $(TSDUCK_INC) $(PCSC_INC) -I$(SRC_DIR) -Ithirdparty/asio/asio/include
+LDFLAGS = $(TSDUCK_LIB) $(PCSC_LIB)
+
+EXEC = $(OBJ_DIR)/$(PROJECT_NAME)
+
+all: $(EXEC)
+
+$(OBJ_DIR):
+ mkdir -p $(OBJ_DIR)
+
+$(EXEC): $(OBJ_FILES)
+ $(CXX) $(OBJ_FILES) $(LDFLAGS) -o $(EXEC)
+
+$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
+ mkdir -p $(dir $@)
+ $(CXX) $(CXXFLAGS) -c $< -o $@
+
+clean:
+ rm -rf $(OBJ_DIR)
+
+install:
+ cp $(EXEC) /usr/local/bin/$(PROJECT_NAME)
+
+.PHONY: all clean install
diff --git a/msvc/dantto4k.vcxproj b/msvc/dantto4k.vcxproj
index 944df95..7a34c2d 100644
--- a/msvc/dantto4k.vcxproj
+++ b/msvc/dantto4k.vcxproj
@@ -30,26 +30,26 @@
Application
true
- v143
+ v145
Unicode
Application
false
- v143
+ v145
true
Unicode
Application
true
- v143
+ v145
MultiByte
Application
false
- v143
+ v145
true
MultiByte
@@ -67,19 +67,19 @@
-
-
-
-
-
-
-
-
- $(Platform)\$(Configuration)\dantto4k\
-
-
- $(Platform)\$(Configuration)\dantto4k\
-
+
+
+
+
+
+
+
+
+ $(Platform)\$(Configuration)\dantto4k\
+
+
+ $(Platform)\$(Configuration)\dantto4k\
+
Level3
@@ -112,38 +112,40 @@
Level3
true
- _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
- true
- stdcpp20
- MultiThreadedDebug
- /FS %(AdditionalOptions)
-
+ _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ true
+ stdcpp20
+ MultiThreadedDebugDLL
+ /FS %(AdditionalOptions)
+ $(ProjectDir)../src;%(AdditionalIncludeDirectories)
+
Console
true
- %(AdditionalDependencies)
-
-
+ %(AdditionalDependencies)
+
+
Level3
true
true
true
- _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
- true
- stdcpp20
- MultiThreaded
- /FS %(AdditionalOptions)
-
+ _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ true
+ stdcpp20
+ MultiThreaded
+ /FS %(AdditionalOptions)
+ $(ProjectDir)../src;%(AdditionalIncludeDirectories)
+
Console
true
true
true
- %(AdditionalDependencies)
-
-
+ %(AdditionalDependencies)
+
+
@@ -165,7 +167,8 @@
-
+
+
@@ -189,7 +192,8 @@
-
+
+
@@ -198,7 +202,6 @@
-
@@ -248,6 +251,10 @@
+
+
+
+
@@ -271,7 +278,8 @@
-
+
+
@@ -301,7 +309,8 @@
-
+
+
@@ -311,7 +320,6 @@
-
@@ -372,8 +380,16 @@
+
+
+
+
+
+
+
+
-
+
diff --git a/msvc/dantto4k.vcxproj.filters b/msvc/dantto4k.vcxproj.filters
index 6cae4f5..b0e8115 100644
--- a/msvc/dantto4k.vcxproj.filters
+++ b/msvc/dantto4k.vcxproj.filters
@@ -220,9 +220,12 @@
mmttlv\tlv\structs
-
- mmttlv\tlv\structs
-
+
+ mmttlv\tlv\structs
+
+
+ mmttlv\tlv\structs
+
mmttlv\mmt\descriptors
@@ -244,15 +247,12 @@
dantto4k\xml
-
+
dantto4k\subtitle
dantto4k\subtitle
-
- dantto4k\subtitle
-
dantto4k
@@ -304,12 +304,27 @@
dantto4k
-
+
dantto4k
-
+
dantto4k
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ mmttlv\mmt\structs
+
@@ -564,9 +579,12 @@
mmttlv\tlv\structs
-
- mmttlv\tlv\structs
-
+
+ mmttlv\tlv\structs
+
+
+ mmttlv\tlv\structs
+
mmttlv\mmt\descriptors
@@ -594,10 +612,7 @@
dantto4k\xml
-
- dantto4k\subtitle
-
-
+
dantto4k\subtitle
@@ -672,12 +687,39 @@
dantto4k
-
+
dantto4k
-
+
dantto4k
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ mmttlv\mmt\structs
+
@@ -734,5 +776,8 @@
{5dccb4d7-560f-4244-9d84-334de3cf19f2}
+
+ {84aaa3a8-9e4a-4085-9dc5-db67d16195cc}
+
diff --git a/msvc/dantto4k_dll.vcxproj b/msvc/dantto4k_dll.vcxproj
index 1a75873..4b14abe 100644
--- a/msvc/dantto4k_dll.vcxproj
+++ b/msvc/dantto4k_dll.vcxproj
@@ -29,26 +29,26 @@
DynamicLibrary
true
- v143
+ v145
Unicode
DynamicLibrary
false
- v143
+ v145
true
Unicode
DynamicLibrary
true
- v143
+ v145
MultiByte
DynamicLibrary
false
- v143
+ v145
true
MultiByte
@@ -66,17 +66,17 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
BonDriver_dantto4k
$(Platform)\$(Configuration)\dantto4k_dll\
@@ -125,40 +125,42 @@
Level3
true
- _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;_DEBUG;DANTTO4KDLL_EXPORTS;_WINDOWS;_USRDLL;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
- true
- stdcpp20
- MultiThreadedDebug
- /FS %(AdditionalOptions)
-
+ _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;_DEBUG;DANTTO4KDLL_EXPORTS;_WINDOWS;_USRDLL;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ true
+ stdcpp20
+ MultiThreadedDebugDLL
+ /FS %(AdditionalOptions)
+ $(ProjectDir)../src;%(AdditionalIncludeDirectories)
+
Windows
true
false
- %(AdditionalDependencies)
-
-
+ %(AdditionalDependencies)
+
+
Level3
true
true
true
- _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;NDEBUG;DANTTO4KDLL_EXPORTS;_WINDOWS;_USRDLL;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
- true
- stdcpp20
- MultiThreaded
- /FS %(AdditionalOptions)
-
+ _CRT_SECURE_NO_WARNINGS;TSDUCK_STATIC_LIBRARY;_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;NDEBUG;DANTTO4KDLL_EXPORTS;_WINDOWS;_USRDLL;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ true
+ stdcpp20
+ MultiThreaded
+ /FS %(AdditionalOptions)
+ $(ProjectDir)../src;%(AdditionalIncludeDirectories)
+
Windows
true
true
true
false
- %(AdditionalDependencies)
-
-
+ %(AdditionalDependencies)
+
+
@@ -175,13 +177,13 @@
-
+
-
-
-
-
+
+
+
+
@@ -204,7 +206,8 @@
-
+
+
@@ -213,7 +216,6 @@
-
@@ -262,7 +264,12 @@
-
+
+
+
+
+
+
@@ -280,13 +287,13 @@
-
+
-
-
-
-
+
+
+
+
@@ -316,7 +323,8 @@
-
+
+
@@ -326,7 +334,6 @@
-
@@ -384,9 +391,18 @@
-
+
+
+
+
+
+
+
+
+
+
diff --git a/msvc/dantto4k_dll.vcxproj.filters b/msvc/dantto4k_dll.vcxproj.filters
index 6cae4f5..9cf4f00 100644
--- a/msvc/dantto4k_dll.vcxproj.filters
+++ b/msvc/dantto4k_dll.vcxproj.filters
@@ -220,9 +220,12 @@
mmttlv\tlv\structs
-
- mmttlv\tlv\structs
-
+
+ mmttlv\tlv\structs
+
+
+ mmttlv\tlv\structs
+
mmttlv\mmt\descriptors
@@ -244,15 +247,12 @@
dantto4k\xml
-
- dantto4k\subtitle
-
-
- dantto4k\subtitle
-
-
- dantto4k\subtitle
-
+
+ dantto4k\subtitle
+
+
+ mmttlv\mmt\structs
+
dantto4k
@@ -304,12 +304,27 @@
dantto4k
-
+
dantto4k
-
+
dantto4k
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\subtitle
+
@@ -564,9 +579,12 @@
mmttlv\tlv\structs
-
- mmttlv\tlv\structs
-
+
+ mmttlv\tlv\structs
+
+
+ mmttlv\tlv\structs
+
mmttlv\mmt\descriptors
@@ -594,15 +612,12 @@
dantto4k\xml
-
- dantto4k\subtitle
-
-
- dantto4k\subtitle
-
-
- dantto4k\subtitle
-
+
+ dantto4k\subtitle
+
+
+ mmttlv\mmt\structs
+
dantto4k\subtitle
@@ -672,12 +687,39 @@
dantto4k
-
+
dantto4k
-
+
dantto4k
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\ttml
+
+
+ dantto4k\subtitle
+
@@ -734,5 +776,8 @@
{5dccb4d7-560f-4244-9d84-334de3cf19f2}
+
+ {e31c2a9a-285e-49f0-bf5f-da6ccd870531}
+
diff --git a/src/additionalAribSubtitleInfo.cpp b/src/additionalAribSubtitleInfo.cpp
new file mode 100644
index 0000000..a806ab1
--- /dev/null
+++ b/src/additionalAribSubtitleInfo.cpp
@@ -0,0 +1,49 @@
+#include "additionalAribSubtitleInfo.h"
+
+namespace MmtTlv {
+
+bool AdditionalAribSubtitleInfo::unpack(Common::ReadStream& stream) {
+ try {
+ subtitleTag = stream.get8U();
+
+ uint8_t uint8 = stream.get8U();
+ subtitle_info_version = (uint8 & 0b11110000) >> 4;
+ start_mpu_sequence_number_flag = (uint8 & 0b00001000) >> 3;
+
+ stream.read(languageCode.data(), languageCode.size());
+
+ uint8 = stream.get8U();
+ type = (uint8 & 0b11000000) >> 6;
+ subtitleFormat = (uint8 & 0b00111100) >> 2;
+ opm = uint8 & 0b00000011;
+
+ uint8 = stream.get8U();
+ tmd = (uint8 & 0b11110000) >> 4;
+ dmf = uint8 & 0b00001111;
+
+ uint8 = stream.get8U();
+ resolution = static_cast((uint8 & 0b11110000) >> 4);
+ compressionType = uint8 & 0b00001111;
+
+ startMpuSequenceNumber = 0;
+ if (start_mpu_sequence_number_flag) {
+ startMpuSequenceNumber = stream.getBe32U();
+ }
+
+ referenceStartTime = {};
+ referenceStartTimeLeapIndicator = 0;
+ if (tmd == 0b0010) {
+ if (!referenceStartTime.unpack(stream)) {
+ return false;
+ }
+ referenceStartTimeLeapIndicator = stream.get8U() >> 6;
+ }
+ }
+ catch (const std::out_of_range&) {
+ return false;
+ }
+
+ return true;
+}
+
+}
diff --git a/src/additionalAribSubtitleInfo.h b/src/additionalAribSubtitleInfo.h
new file mode 100644
index 0000000..d57561e
--- /dev/null
+++ b/src/additionalAribSubtitleInfo.h
@@ -0,0 +1,36 @@
+#pragma once
+#include
+#include
+#include "stream.h"
+#include "ntpTimestamp.h"
+
+namespace MmtTlv {
+
+enum class SubtitleResolution : uint8_t {
+ Resolution2K = 0, // 1920 x 1080
+ Resolution4K = 1, // 3840 x 2160
+ Resolution8K = 2, // 7680 x 4320
+};
+
+class AdditionalAribSubtitleInfo {
+public:
+ bool unpack(Common::ReadStream& stream);
+
+ uint8_t subtitleTag{};
+ uint8_t subtitle_info_version{};
+ bool start_mpu_sequence_number_flag{};
+ std::array languageCode;
+ uint8_t type{};
+ uint8_t subtitleFormat{};
+ uint8_t opm{};
+ uint8_t tmd{};
+ uint8_t dmf{};
+ SubtitleResolution resolution{SubtitleResolution::Resolution4K};
+ uint8_t compressionType{};
+ uint32_t startMpuSequenceNumber{};
+ NtpTimestamp referenceStartTime;
+ uint8_t referenceStartTimeLeapIndicator{};
+
+};
+
+}
diff --git a/src/aribCharsetEncoder.cpp b/src/aribCharsetEncoder.cpp
index 7b475a8..30742c7 100644
--- a/src/aribCharsetEncoder.cpp
+++ b/src/aribCharsetEncoder.cpp
@@ -9,7 +9,11 @@
#include
#include
-namespace arib::charset::detail {
+namespace arib {
+
+namespace charset {
+
+namespace detail {
struct CharLookupResult {
const Charset* charset;
@@ -73,7 +77,7 @@ const CharLookupTable& charLookupTable() {
return table;
}
-std::optional findChar(char32_t c, CharsetCode candidate1, CharsetCode candidate2) {
+std::optional findChar(char32_t c, CharsetCode candidate1, CharsetCode candidate2, EncodeMode encodeMode) {
const auto& table = charLookupTable();
auto it = table.find(c);
if (it == table.end()) {
@@ -82,52 +86,70 @@ std::optional findChar(char32_t c, CharsetCode candidate1, Cha
const auto& results = it->second;
if (results.empty()) {
- return std::nullopt;
+ return {};
}
- if (results.size() == 1) {
- return results[0];
+ if (encodeMode == EncodeMode::Caption) {
+ const auto additionalSymbol = std::find_if(results.begin(), results.end(), [](const auto& result) {
+ return result.charset->code == CharsetCode::AdditionalSymbols;
+ });
+ if (additionalSymbol != results.end()) {
+ return *additionalSymbol;
+ }
}
if (candidate1 != CharsetCode::None &&
- candidate1 != CharsetCode::JISKanjiPlane1 &&
- candidate1 != CharsetCode::JISKanjiPlane2) {
+ (encodeMode == EncodeMode::Caption ||
+ (candidate1 != CharsetCode::JISKanjiPlane1 &&
+ candidate1 != CharsetCode::JISKanjiPlane2))) {
for (const auto& res : results) {
- if (res.charset->code == candidate1) return res;
+ if (res.charset->code == candidate1) {
+ return res;
+ }
}
}
if (candidate2 != CharsetCode::None &&
- candidate2 != CharsetCode::JISKanjiPlane1 &&
- candidate2 != CharsetCode::JISKanjiPlane2) {
+ (encodeMode == EncodeMode::Caption ||
+ (candidate2 != CharsetCode::JISKanjiPlane1 &&
+ candidate2 != CharsetCode::JISKanjiPlane2))) {
for (const auto& res : results) {
- if (res.charset->code == candidate2) return res;
+ if (res.charset->code == candidate2) {
+ return res;
+ }
}
}
- return results[0];
+ return results.front();
}
-std::optional findCharsetBy2Char(char32_t c1, char32_t c2, CharsetCode candidate1, CharsetCode candidate2) {
+std::optional findCharsetBy2Char(char32_t c1, char32_t c2, CharsetCode candidate1, CharsetCode candidate2, EncodeMode encodeMode) {
const auto& table = charLookupTable();
auto it1 = table.find(c1);
- if (it1 == table.end()) return std::nullopt;
+ if (it1 == table.end()) {
+ return {};
+ }
const auto& results1 = it1->second;
auto it2 = table.find(c2);
- if (it2 == table.end()) return std::nullopt;
+ if (it2 == table.end()) {
+ return {};
+ }
const auto& results2 = it2->second;
auto hasCharset = [](const std::vector& list, const Charset* target) {
for (const auto& res : list) {
- if (res.charset == target) return true;
+ if (res.charset == target) {
+ return true;
+ }
}
return false;
};
if (candidate1 != CharsetCode::None &&
- candidate1 != CharsetCode::JISKanjiPlane1 &&
- candidate1 != CharsetCode::JISKanjiPlane2) {
+ (encodeMode == EncodeMode::Caption ||
+ (candidate1 != CharsetCode::JISKanjiPlane1 &&
+ candidate1 != CharsetCode::JISKanjiPlane2))) {
const Charset* cs = findCharset(candidate1);
if (cs != nullptr) {
bool found1 = false;
@@ -147,8 +169,9 @@ std::optional findCharsetBy2Char(char32_t c1, char32_t c2, Cha
}
if (candidate2 != CharsetCode::None &&
- candidate2 != CharsetCode::JISKanjiPlane1 &&
- candidate2 != CharsetCode::JISKanjiPlane2) {
+ (encodeMode == EncodeMode::Caption ||
+ (candidate2 != CharsetCode::JISKanjiPlane1 &&
+ candidate2 != CharsetCode::JISKanjiPlane2))) {
const Charset* cs = findCharset(candidate2);
if (cs != nullptr) {
bool found1 = false;
@@ -235,8 +258,8 @@ class Encoder {
};
const EncodeMode mode;
- State state;
-
+ State state;
+
bool isFullwidth(char32_t ch) {
for (const char32_t* p = fullwidthChars; *p != 0; ++p) {
if (*p == ch) {
@@ -292,7 +315,7 @@ class Encoder {
CharsetCode glCode = state.graphic[state.gl];
CharsetCode grCode = state.graphic[state.gr];
- auto find = findChar(c, glCode, grCode);
+ auto find = findChar(c, glCode, grCode, mode);
if (!find) {
++pos;
@@ -305,7 +328,7 @@ class Encoder {
// If the character is neither Hiragana nor Katakana nor AdditionalSymbol
if (!(c >= 0x3041 && c <= 0x3093) && !(c >= 0x30A1 && c <= 0x30F6) && find->charset->code != CharsetCode::AdditionalSymbols) {
// Check if the current and next characters belong to the same charset
- auto find2 = findCharsetBy2Char(c, state.u32[pos + 1], glCode, grCode);
+ auto find2 = findCharsetBy2Char(c, state.u32[pos + 1], glCode, grCode, mode);
if (find2) {
find = find2;
}
@@ -345,8 +368,15 @@ class Encoder {
}
}
- void selectCharset(const Charset* charset, size_t pos) {
- uint8_t graphicIndex = findGraphicByCharsetCode(charset->code);
+ void selectCharset(const Charset* charset, size_t pos) {
+ if (mode == EncodeMode::Caption && charset->code == CharsetCode::AdditionalSymbols) {
+ charset = findCharset(CharsetCode::JISKanjiPlane1);
+ if (charset == nullptr) {
+ return;
+ }
+ }
+
+ uint8_t graphicIndex = findGraphicByCharsetCode(charset->code);
// Check single shift
if (charset->code == CharsetCode::Hiragana || charset->code == CharsetCode::Katakana) {
@@ -444,17 +474,22 @@ class Encoder {
return;
}
+ CharsetCode newCharsetCode = charsetCode;
+ if (mode == EncodeMode::Caption && charsetCode == CharsetCode::JISKanjiPlane1) {
+ newCharsetCode = CharsetCode::Kanji;
+ }
+
state.output.push_back(ESC);
if (charset->is2Byte == false) {
state.output.push_back(0x28 + graphicIndex);
- state.output.push_back(static_cast(charsetCode));
+ state.output.push_back(static_cast(newCharsetCode));
}
else {
state.output.push_back(0x24);
if (graphicIndex >= 1 && graphicIndex <= 3) {
state.output.push_back(0x29 + graphicIndex);
}
- state.output.push_back(static_cast(charsetCode));
+ state.output.push_back(static_cast(newCharsetCode));
}
state.graphic[graphicIndex] = static_cast(charsetCode);
@@ -519,7 +554,7 @@ class Encoder {
size_t getCharsetRunLength(CharsetCode charsetCode, size_t pos) {
size_t count = 0;
for (size_t i = pos; i < state.u32.size(); i++) {
- auto result = findChar(state.u32[i], charsetCode, CharsetCode::None);
+ auto result = findChar(state.u32[i], charsetCode, CharsetCode::None, mode);
if (!result) {
return count;
}
@@ -536,7 +571,7 @@ class Encoder {
const Charset* getNextCharset(CharsetCode currentCharsetCode, size_t pos) {
for (size_t i = pos; i < state.u32.size(); i++) {
- auto result = findChar(state.u32[i], currentCharsetCode, CharsetCode::None);
+ auto result = findChar(state.u32[i], currentCharsetCode, CharsetCode::None, mode);
if (!result) {
return nullptr;
}
@@ -583,9 +618,7 @@ class Encoder {
};
-} // namespace arib::charset::detail
-
-namespace arib::charset {
+} // namespace detail
std::string encode(std::string_view input, EncodeMode mode) {
detail::Encoder encoder(mode);
@@ -598,4 +631,6 @@ std::string encode(std::u8string_view input, EncodeMode mode) {
return encoder.encode(input);
}
-} // namespace arib::charset
+} // namespace charset
+
+} // namespace arib
diff --git a/src/b24Color.cpp b/src/b24Color.cpp
index e8d786a..381d498 100644
--- a/src/b24Color.cpp
+++ b/src/b24Color.cpp
@@ -157,10 +157,10 @@ std::pair findClosestColor(const ColorRGBA& color) {
uint32_t distance = 0xFFFFFFFF;
for (uint8_t p = 0; p < 8; p++) {
for (uint8_t i = 0; i < 16; i++) {
- uint8_t r = color.r - kB24ColorCLUT[p][i].r;
- uint8_t g = color.g - kB24ColorCLUT[p][i].g;
- uint8_t b = color.b - kB24ColorCLUT[p][i].b;
- uint8_t a = color.a - kB24ColorCLUT[p][i].a;
+ int32_t r = color.r - kB24ColorCLUT[p][i].r;
+ int32_t g = color.g - kB24ColorCLUT[p][i].g;
+ int32_t b = color.b - kB24ColorCLUT[p][i].b;
+ int32_t a = color.a - kB24ColorCLUT[p][i].a;
uint32_t d = r * r + g * g + b * b + a * a;
if (d < distance) {
distance = d;
diff --git a/src/b24ControlSet.h b/src/b24ControlSet.h
index c310046..607c66a 100644
--- a/src/b24ControlSet.h
+++ b/src/b24ControlSet.h
@@ -54,6 +54,7 @@ constexpr uint8_t SPL = 0x99;
constexpr uint8_t STL = 0x9A;
constexpr uint8_t CSI = 0x9B;
constexpr uint8_t TIME = 0x9D;
-constexpr uint8_t ACPS = 0x61;
-
-}
\ No newline at end of file
+constexpr uint8_t ACPS = 0x61;
+constexpr uint8_t MDF = 0x64;
+
+}
diff --git a/src/b24SubtitleConverter.cpp b/src/b24SubtitleConverter.cpp
new file mode 100644
index 0000000..b1d8a21
--- /dev/null
+++ b/src/b24SubtitleConverter.cpp
@@ -0,0 +1,61 @@
+#include "b24SubtitleConverter.h"
+#include "aribTextEncoder.h"
+#include "b24Color.h"
+#include "b24ControlSet.h"
+#include "ttml/parser.h"
+#include "ttml/resolver.h"
+#include "ttml/b24_converter.h"
+#include
+#include
+
+bool B24SubtitleConverter::convert(const std::string& input, std::list& output, B24::PESData::PESType pesType, MmtTlv::SubtitleResolution resolution) {
+ const auto sync_mode = pesType == B24::PESData::PESType::Synchronized
+ ? arib::ttml::SyncMode::Sync
+ : arib::ttml::SyncMode::Async;
+ auto parse_result = arib::ttml::parse(input, sync_mode);
+ if (parse_result.error) {
+#ifdef _DEBUG
+ std::cerr << "[TTML] " << parse_result.error->message << std::endl;
+#endif
+ return false;
+ }
+ auto resolve_result = arib::ttml::resolve(*parse_result.document, sync_mode);
+ if (resolve_result.error) {
+#ifdef _DEBUG
+ std::cerr << "[TTML] " << resolve_result.error->message << std::endl;
+#endif
+ return false;
+ }
+ auto convert_result = arib::ttml::convert_to_b24(*resolve_result.document, sync_mode, resolution);
+ if (convert_result.error) {
+#ifdef _DEBUG
+ std::cerr << "[TTML] " << convert_result.error->message << std::endl;
+#endif
+ return false;
+ }
+
+ for (auto& converted : convert_result.outputs) {
+ B24::CaptionStatementData captionStatementData;
+ captionStatementData.dataUnits.push_back({ {B24ControlSet::CS} });
+ if (converted.data.size()) {
+ captionStatementData.dataUnits.push_back(std::move(converted.data));
+ }
+
+ B24::DataGroup dataGroup;
+ dataGroup.setGroupData(captionStatementData);
+
+ std::vector packedPesData;
+ B24::PESData pesData(dataGroup);
+ pesData.SetPESType(pesType);
+ pesData.pack(packedPesData);
+
+ output.push_back({
+ packedPesData,
+ converted.begin
+ ? std::optional{static_cast(converted.begin->count())}
+ : std::nullopt,
+ });
+ }
+
+ return true;
+}
diff --git a/src/b24SubtitleConvertor.h b/src/b24SubtitleConverter.h
similarity index 95%
rename from src/b24SubtitleConvertor.h
rename to src/b24SubtitleConverter.h
index 826390a..ba8690d 100644
--- a/src/b24SubtitleConvertor.h
+++ b/src/b24SubtitleConverter.h
@@ -6,8 +6,9 @@
#include
#include
#include
-#include
-#include "ttml.h"
+#include
+#include
+#include "additionalAribSubtitleInfo.h"
namespace B24 {
enum class DataUnitParameter : uint8_t {
@@ -99,7 +100,7 @@ namespace B24 {
uint8_t languageTag{0};
uint8_t dmf{0};
uint8_t dc{0};
- std::string languageCode;
+ std::array languageCode;
uint8_t format{0};
uint8_t tcs{0};
uint8_t rollupMode{0};
@@ -302,19 +303,12 @@ class B24SubtitleOutput {
B24SubtitleOutput(std::vector pesData, std::optional begin)
: pesData(pesData), begin(begin) {}
- uint64_t calcPts(uint64_t programStartTime) const {
- if (!begin) {
- return 0;
- }
- return (programStartTime * 1000 + *begin) * 90;
- }
-
std::vector pesData;
std::optional begin;
};
-class B24SubtitleConvertor {
-public:
- static bool convert(const std::string& input, std::list& output);
-
-};
\ No newline at end of file
+class B24SubtitleConverter {
+public:
+ static bool convert(const std::string& input, std::list& output, B24::PESData::PESType pesType, MmtTlv::SubtitleResolution resolution);
+
+};
diff --git a/src/b24SubtitleConvertor.cpp b/src/b24SubtitleConvertor.cpp
deleted file mode 100644
index dac4960..0000000
--- a/src/b24SubtitleConvertor.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-#include "b24SubtitleConvertor.h"
-#include "aribTextEncoder.h"
-#include "b24Color.h"
-#include "b24ControlSet.h"
-#include
-
-namespace {
-
-std::vector splitByNull(const std::string& data) {
- std::vector tokens;
- std::string current;
- for (auto byte : data) {
- if (byte == 0) {
- tokens.push_back(current);
- current.clear();
- }
- else {
- current.push_back(static_cast(byte));
- }
- }
- if (!current.empty()) {
- tokens.push_back(current);
- }
- return tokens;
-}
-
-void appendNumber(std::vector& output, int n) {
- if (n == 0) {
- output.push_back(0x30);
- return;
- }
- std::vector temp;
- while (n > 0) {
- temp.push_back(static_cast((n % 10) + 0x30));
- n /= 10;
- }
- std::reverse(temp.begin(), temp.end());
- output.insert(output.end(), temp.begin(), temp.end());
-}
-
-}
-
-bool B24SubtitleConvertor::convert(const std::string& input, std::list& output) {
- const TTML ttml = TTMLPaser::parse(input);
-
- uint8_t lastTextColorPalette = 0;
- uint8_t lastTextColorIndex = 7;
- uint8_t lastBackgroundColorPalette = 0;
- uint8_t lastBackgroundColorIndex = 8;
- uint8_t characterSize = B24ControlSet::NSZ;
- double fontHeight = 0;
-
- for (const auto& div : ttml.divTags) {
- B24::CaptionStatementData captionStatementData;
-
- std::string text;
- for (const auto& p : div.pTags) {
- for (const auto& span : p.spanTags) {
- text.append(span.text);
- text.push_back('\0');
- }
- }
-
- auto encoded = arib::text::encode(text, arib::charset::EncodeMode::Caption);
- auto encodedSplit = splitByNull(encoded);
- int encodedSplitIndex = 0;
-
- std::vector unitDataByte;
- unitDataByte.push_back(B24ControlSet::CS);
-
- // Set writing format 960 x 540
- unitDataByte.push_back(B24ControlSet::CSI);
- unitDataByte.push_back(0x37);
- unitDataByte.push_back(B24ControlSet::SP);
- unitDataByte.push_back(B24ControlSet::SWF);
-
- for (const auto& p : div.pTags) {
- if (p.spanTags.empty()) {
- continue;
- }
-
- // Set display format
- if (p.region.extent.has_value()) {
- unitDataByte.push_back(B24ControlSet::CSI);
- appendNumber(unitDataByte, static_cast(p.region.extent->first.getValue().value * 960 / 3840));
- unitDataByte.push_back(0x3B);
- appendNumber(unitDataByte, static_cast(p.region.extent->second.getValue().value * 540 / 2160));
- unitDataByte.push_back(B24ControlSet::SP);
- unitDataByte.push_back(B24ControlSet::SDF);
- }
-
- // Set character size
- const auto firstSpan = p.spanTags.begin();
- if (firstSpan != p.spanTags.end() && firstSpan->style.fontSize) {
- TTMLCssValueLength first = firstSpan->style.fontSize->first.getValue();
- TTMLCssValueLength second = firstSpan->style.fontSize->second.getValue();
-
- if (first.value == 144 && second.value == 144) {
- if (characterSize != B24ControlSet::NSZ) {
- unitDataByte.push_back(B24ControlSet::NSZ);
- characterSize = B24ControlSet::NSZ;
- }
- fontHeight = 240;
- }
- else if (first.value == 72 && second.value == 144) {
- if (characterSize != B24ControlSet::MSZ) {
- unitDataByte.push_back(B24ControlSet::MSZ);
- characterSize = B24ControlSet::MSZ;
- }
- fontHeight = 240;
- }
- else if (first.value == 72 && second.value == 72) {
- if (characterSize != B24ControlSet::SSZ) {
- unitDataByte.push_back(B24ControlSet::SSZ);
- characterSize = B24ControlSet::SSZ;
- }
- fontHeight = 120;
- }
- }
-
- // Set position
- if (p.region.origin.has_value()) {
- double offsetY = 0;
- if (p.spanTags.begin()->style.lineHeight.has_value() &&
- p.spanTags.begin()->style.fontSize) {
- double lineHeight = p.spanTags.begin()->style.lineHeight->getValue().value;
- if (p.region.extent.has_value()) {
- offsetY = (lineHeight - fontHeight) / 2;
- }
- }
-
- unitDataByte.push_back(B24ControlSet::CSI);
- appendNumber(unitDataByte, static_cast(p.region.origin->first.getValue().value * 960 / 3840));
- unitDataByte.push_back(0x3B);
- appendNumber(unitDataByte, static_cast((p.region.origin->second.getValue().value + offsetY) * 540 / 2160));
- unitDataByte.push_back(B24ControlSet::SP);
- unitDataByte.push_back(B24ControlSet::SDP);
- }
-
- // Set active position to 0 x 0
- unitDataByte.push_back(B24ControlSet::APS);
- unitDataByte.push_back(0x40);
- unitDataByte.push_back(0x40);
-
- TTMLStyle oldStyle;
- for (const auto& span : p.spanTags) {
- // Set character size
- if (span.style.fontSize) {
- TTMLCssValueLength first = span.style.fontSize->first.getValue();
- TTMLCssValueLength second = span.style.fontSize->second.getValue();
-
- if (first.value == 144 && second.value == 144) {
- if (characterSize != B24ControlSet::NSZ) {
- unitDataByte.push_back(B24ControlSet::NSZ);
- characterSize = B24ControlSet::NSZ;
- }
- }
- else if (first.value == 72 && second.value == 144) {
- if (characterSize != B24ControlSet::MSZ) {
- unitDataByte.push_back(B24ControlSet::MSZ);
- characterSize = B24ControlSet::MSZ;
- }
- }
- else if (first.value == 72 && second.value == 72) {
- if (characterSize != B24ControlSet::SSZ) {
- unitDataByte.push_back(B24ControlSet::SSZ);
- characterSize = B24ControlSet::SSZ;
- }
- }
- }
-
- // Set background color
- if (span.style.backgroundColor.has_value()) {
- TTMLCssValueColor color = span.style.backgroundColor->getValue();
- auto closetColor = findClosestColor(ColorRGBA{ color.r, color.g, color.b, color.a });
-
- if (lastBackgroundColorPalette != closetColor.first || lastBackgroundColorIndex != closetColor.second) {
- unitDataByte.push_back(B24ControlSet::COL);
- unitDataByte.push_back(0x20);
- unitDataByte.push_back(0x40 | closetColor.first);
- unitDataByte.push_back(B24ControlSet::COL);
- unitDataByte.push_back(0x50 | closetColor.second);
-
- lastBackgroundColorPalette = closetColor.first;
- lastBackgroundColorIndex = closetColor.second;
- }
- }
-
- // Set text color
- if (span.style.color.has_value()) {
- TTMLCssValueColor color = span.style.color->getValue();
- auto closetColor = findClosestColor(ColorRGBA{ color.r, color.g, color.b, color.a });
-
- if (lastTextColorPalette != closetColor.first || lastTextColorIndex != closetColor.second) {
- if (closetColor.first == 0 && closetColor.second >= 0 && closetColor.second <= 7) {
- unitDataByte.push_back(B24ControlSet::COL);
- unitDataByte.push_back(0x20);
- unitDataByte.push_back(0x40 | 0);
- unitDataByte.push_back(B24ControlSet::BKF + closetColor.second);
- }
- else {
- unitDataByte.push_back(B24ControlSet::COL);
- unitDataByte.push_back(0x20);
- unitDataByte.push_back(0x40 | closetColor.first);
- unitDataByte.push_back(B24ControlSet::COL);
- unitDataByte.push_back(0x40 | closetColor.second);
- }
-
- lastTextColorPalette = closetColor.first;
- lastTextColorIndex = closetColor.second;
- }
- }
-
-
- unitDataByte.insert(unitDataByte.end(), encodedSplit[encodedSplitIndex].begin(), encodedSplit[encodedSplitIndex].end());
- encodedSplitIndex++;
- }
- }
-
- captionStatementData.dataUnits.push_back({ unitDataByte });
-
- if (div.begin && div.end) {
- std::vector unitDataByte;
-
- uint64_t duration = (*div.end - *div.begin) / 100;
- while (duration > 0) {
- uint8_t value = static_cast(std::min(duration, static_cast(0x3F)));
- unitDataByte.push_back(B24ControlSet::TIME);
- unitDataByte.push_back(0x20);
- unitDataByte.push_back(0x40 | value);
- unitDataByte.push_back(0x0C);
- duration -= value;
- }
-
- captionStatementData.dataUnits.push_back({ unitDataByte });
- }
-
- B24::DataGroup dataGroup;
- dataGroup.setGroupData(captionStatementData);
-
- std::vector packedPesData;
- B24::PESData pesData(dataGroup);
- pesData.SetPESType(B24::PESData::PESType::Synchronized);
- pesData.pack(packedPesData);
-
- output.push_back({ packedPesData , div.begin });
- }
-
- if (output.size() == 0) {
- B24::CaptionStatementData captionStatementData;
- {
- // clear
- std::vector unitDataByte;
- unitDataByte.push_back(B24ControlSet::CS);
- captionStatementData.dataUnits.push_back({ unitDataByte });
- }
- B24::DataGroup dataGroup;
- dataGroup.setGroupData(captionStatementData);
-
- std::vector packedPesData;
- B24::PESData pesData(dataGroup);
- pesData.SetPESType(B24::PESData::PESType::Synchronized);
- pesData.pack(packedPesData);
-
- output.push_back({ packedPesData , 0 });
- }
-
- return true;
-}
diff --git a/src/dantto4k.cpp b/src/dantto4k.cpp
index 676385d..90535dd 100644
--- a/src/dantto4k.cpp
+++ b/src/dantto4k.cpp
@@ -7,16 +7,16 @@
#include "casProxyClient.h"
#include "acasHandler.h"
#include "smartCard.h"
-#include "bufferedOutput.h"
-#include "progressReporter.h"
-
-#ifdef WIN32
-#include
-#include
-#include
-#endif
-
-namespace {
+#include "bufferedOutput.h"
+#include "progressReporter.h"
+
+#ifdef WIN32
+#include
+#include
+#include
+#endif
+
+namespace {
struct Args {
std::string input;
@@ -165,25 +165,25 @@ int main(int argc, char* argv[]) {
bool useStdin = (args.input == "-");
bool useStdout = (args.output == "-");
- if (args.listSmartCardReader) {
- printReaderList(args);
- return 0;
- }
-
-#ifdef WIN32
- if (useStdin && _setmode(_fileno(stdin), _O_BINARY) == -1) {
- std::cerr << "Unable to set stdin to binary mode" << std::endl;
- return 1;
- }
- if (useStdout && _setmode(_fileno(stdout), _O_BINARY) == -1) {
- std::cerr << "Unable to set stdout to binary mode" << std::endl;
- return 1;
- }
-#endif
-
- std::istream* inputStream;
- std::unique_ptr inputFs;
- if (useStdin) {
+ if (args.listSmartCardReader) {
+ printReaderList(args);
+ return 0;
+ }
+
+#ifdef WIN32
+ if (useStdin && _setmode(_fileno(stdin), _O_BINARY) == -1) {
+ std::cerr << "Unable to set stdin to binary mode" << std::endl;
+ return 1;
+ }
+ if (useStdout && _setmode(_fileno(stdout), _O_BINARY) == -1) {
+ std::cerr << "Unable to set stdout to binary mode" << std::endl;
+ return 1;
+ }
+#endif
+
+ std::istream* inputStream;
+ std::unique_ptr inputFs;
+ if (useStdin) {
inputStream = &std::cin;
}
else {
@@ -261,30 +261,30 @@ int main(int argc, char* argv[]) {
}
}
- std::vector inputBuffer;
- std::vector readBuffer(chunkSize);
- inputBuffer.reserve(chunkSize * 2);
- bool inputEnded = false;
- while (true) {
- if (!inputEnded && inputBuffer.size() < chunkSize) {
- inputStream->read(reinterpret_cast(readBuffer.data()), static_cast(readBuffer.size()));
- std::streamsize bytesRead = inputStream->gcount();
- if (bytesRead > 0) {
- auto readSize = static_cast(bytesRead);
- inputBuffer.insert(inputBuffer.end(), readBuffer.data(), readBuffer.data() + readSize);
- }
- if (bytesRead == 0 && inputStream->eof()) {
- inputEnded = true;
- }
- }
-
- if (inputBuffer.empty() && inputEnded) {
- break;
- }
-
- MmtTlv::Common::ReadStream stream(inputBuffer);
- while (!stream.isEof()) {
- MmtTlv::DemuxStatus status = demuxer.demux(stream);
+ std::vector inputBuffer;
+ std::vector readBuffer(chunkSize);
+ inputBuffer.reserve(chunkSize * 2);
+ bool inputEnded = false;
+ while (true) {
+ if (!inputEnded && inputBuffer.size() < chunkSize) {
+ inputStream->read(reinterpret_cast(readBuffer.data()), static_cast(readBuffer.size()));
+ std::streamsize bytesRead = inputStream->gcount();
+ if (bytesRead > 0) {
+ auto readSize = static_cast(bytesRead);
+ inputBuffer.insert(inputBuffer.end(), readBuffer.data(), readBuffer.data() + readSize);
+ }
+ if (bytesRead == 0 && inputStream->eof()) {
+ inputEnded = true;
+ }
+ }
+
+ if (inputBuffer.empty() && inputEnded) {
+ break;
+ }
+
+ MmtTlv::Common::ReadStream stream(inputBuffer);
+ while (!stream.isEof()) {
+ MmtTlv::DemuxStatus status = demuxer.demux(stream);
if (status == MmtTlv::DemuxStatus::NotEnoughBuffer) {
break;
@@ -292,14 +292,14 @@ int main(int argc, char* argv[]) {
}
auto consumed = inputBuffer.size() - stream.leftBytes();
- if (consumed > 0) {
- progressReporter.update(consumed);
- }
- inputBuffer.erase(inputBuffer.begin(), inputBuffer.begin() + consumed);
- if (inputEnded && consumed == 0) {
- break;
- }
- }
+ if (consumed > 0) {
+ progressReporter.update(consumed);
+ }
+ inputBuffer.erase(inputBuffer.begin(), inputBuffer.begin() + consumed);
+ if (inputEnded && consumed == 0) {
+ break;
+ }
+ }
progressReporter.finish();
if (!args.noStats) {
diff --git a/src/mhDataComponentDescriptor.cpp b/src/mhDataComponentDescriptor.cpp
index 2deddc1..f25e1ef 100644
--- a/src/mhDataComponentDescriptor.cpp
+++ b/src/mhDataComponentDescriptor.cpp
@@ -2,8 +2,7 @@
namespace MmtTlv {
-bool MhDataComponentDescriptor::unpack(Common::ReadStream& stream)
-{
+bool MhDataComponentDescriptor::unpack(Common::ReadStream& stream) {
try {
if (!MmtDescriptorTemplate::unpack(stream)) {
return false;
diff --git a/src/mmtStream.cpp b/src/mmtStream.cpp
index cd65386..b7d6ea0 100644
--- a/src/mmtStream.cpp
+++ b/src/mmtStream.cpp
@@ -4,9 +4,9 @@
#include "timebase.h"
#include
-namespace MmtTlv {
-
-std::pair MmtStream::getNextPtsDts() {
+namespace MmtTlv {
+
+std::pair MmtStream::getNextPtsDts() {
const auto timestamp = getCurrentTimestamp();
int64_t ptime = av_rescale(timestamp.first.mpuPresentationTime, timeBase.den,
@@ -66,4 +66,4 @@ uint32_t MmtStream::getSamplingRate() const {
return mhAudioComponentDescriptor->getConvertedSamplingRate();
}
-}
\ No newline at end of file
+}
diff --git a/src/mmtStream.h b/src/mmtStream.h
index bbcd569..93f1fe5 100644
--- a/src/mmtStream.h
+++ b/src/mmtStream.h
@@ -1,12 +1,13 @@
#pragma once
-#include
-#include
-#include
+#include
+#include
+#include
#include "mpuExtendedTimestampDescriptor.h"
#include "mpuTimestampDescriptor.h"
#include "videoComponentDescriptor.h"
#include "mhAudioComponentDescriptor.h"
#include "mpuProcessorBase.h"
+#include "additionalAribSubtitleInfo.h"
namespace MmtTlv {
@@ -46,10 +47,22 @@ class MmtStream final {
const std::vector& getMpuExtendedTimestamps() const { return mpuExtendedTimestamps; }
const TimeBase& getTimeBase() const { return timeBase; }
-private:
- friend class MmtTlvDemuxer;
+ const std::optional& additionalAribSubtitleInfo() const noexcept {
+ return subtitleInfo;
+ }
+
+ bool isClosedCaption() const {
+ return assetType == AssetType::stpp && componentTag >= 0x30 && componentTag <= 0x37;
+ }
+
+ bool isSuperimposition() const {
+ return assetType == AssetType::stpp && componentTag >= 0x38 && componentTag <= 0x3F;
+ }
- std::pair getCurrentTimestamp() const;
+private:
+ friend class MmtTlvDemuxer;
+
+ std::pair getCurrentTimestamp() const;
uint16_t packetId;
uint32_t assetType{0};
@@ -63,6 +76,7 @@ class MmtStream final {
std::optional mhAudioComponentDescriptor;
std::vector mpuTimestamps;
std::vector mpuExtendedTimestamps;
+ std::optional subtitleInfo;
TimeBase timeBase{0, 0};
};
diff --git a/src/mmtTlvDemuxer.cpp b/src/mmtTlvDemuxer.cpp
index eaad600..5b166c6 100644
--- a/src/mmtTlvDemuxer.cpp
+++ b/src/mmtTlvDemuxer.cpp
@@ -24,14 +24,15 @@
#include "mhStreamIdentificationDescriptor.h"
#include "videoComponentDescriptor.h"
#include "mhAudioComponentDescriptor.h"
+#include "mhDataComponentDescriptor.h"
#include "ipv6.h"
#include "ntp.h"
#include "dataTransmissionMessage.h"
#include "caMessage.h"
#include "damt.h"
-#include
#include "ddmt.h"
#include "dcct.h"
+#include
namespace MmtTlv {
@@ -502,6 +503,10 @@ void MmtTlvDemuxer::processMmtPackageTable(const Mpt& mpt) {
continue;
}
+ if (asset.assetType == AssetType::stpp) {
+ mmtStream->subtitleInfo.reset();
+ }
+
for (const auto& descriptor : asset.descriptors.list) {
switch (descriptor->getDescriptorTag()) {
case MpuTimestampDescriptor::kDescriptorTag:
@@ -538,6 +543,17 @@ void MmtTlvDemuxer::processMmtPackageTable(const Mpt& mpt) {
statistics.getMmtStat(mmtStream->packetId)->audioSamplingRate = mmtDescriptor->samplingRate;
break;
}
+ case MhDataComponentDescriptor::kDescriptorTag:
+ {
+ const auto* mmtDescriptor = static_cast(descriptor.get());
+ if (asset.assetType == AssetType::stpp && mmtDescriptor->dataComponentId == 0x0020) {
+ AdditionalAribSubtitleInfo subtitleInfo;
+ Common::ReadStream stream(mmtDescriptor->additionalDataComponentInfo);
+ subtitleInfo.unpack(stream);
+ mmtStream->subtitleInfo = std::move(subtitleInfo);
+ }
+ break;
+ }
}
}
}
@@ -809,7 +825,7 @@ void MmtTlvDemuxer::processMfuData(Common::ReadStream& stream) {
std::vector data(stream.leftBytes());
stream.read(data.data(), stream.leftBytes());
- const auto ret = mmtStream->mpuProcessor->process(*mmtStream, data, mpu.fragmentationIndicator);
+ auto ret = mmtStream->mpuProcessor->process(*mmtStream, data, mpu.fragmentationIndicator);
if (ret) {
const auto& mfuData = ret.value();
auto* mmtStream = getStreamByIdx(mfuData.streamIndex);
@@ -870,7 +886,7 @@ void MmtTlvDemuxer::processSignalingMessages(Common::ReadStream& stream) {
std::vector message;
message.resize(length);
- stream.read(message.data(), length);
+ nstream.read(message.data(), length);
if (assembler->assemble(message, signalingMessage.fragmentationIndicator, mmtp.packetSequenceNumber)) {
Common::ReadStream messageStream(assembler->data);
diff --git a/src/mpuProcessorBase.h b/src/mpuProcessorBase.h
index 9b9c751..da40f59 100644
--- a/src/mpuProcessorBase.h
+++ b/src/mpuProcessorBase.h
@@ -24,8 +24,8 @@ constexpr uint64_t NOPTS_VALUE = 0x8000000000000000;
struct MfuData {
std::vector data;
uint64_t pts{NOPTS_VALUE};
- uint64_t dts{NOPTS_VALUE};
- int streamIndex{};
+ uint64_t dts{NOPTS_VALUE};
+ int streamIndex{};
bool keyframe{false};
bool isFirstFragment{false};
bool isLastFragment{false};
@@ -50,4 +50,4 @@ class MpuProcessorTemplate : public MpuProcessorBase {
};
-}
\ No newline at end of file
+}
diff --git a/src/ntp.cpp b/src/ntp.cpp
index a2cc73d..f94e971 100644
--- a/src/ntp.cpp
+++ b/src/ntp.cpp
@@ -1,7 +1,6 @@
#include "ntp.h"
-bool MmtTlv::NTPv4::unpack(Common::ReadStream& stream)
-{
+bool MmtTlv::NTPv4::unpack(Common::ReadStream& stream) {
try {
uint8_t uint8 = stream.get8U();
leap_indicator = uint8 & 0b11000000;
@@ -35,17 +34,3 @@ bool MmtTlv::NTPv4::unpack(Common::ReadStream& stream)
return true;
}
-
-
-bool MmtTlv::NtpTimestamp::unpack(Common::ReadStream& stream)
-{
- try {
- seconds = stream.getBe32U();
- fraction = stream.getBe32U();
- }
- catch (const std::out_of_range&) {
- return false;
- }
-
- return true;
-}
diff --git a/src/ntp.h b/src/ntp.h
index d5617f0..178c7b6 100644
--- a/src/ntp.h
+++ b/src/ntp.h
@@ -1,50 +1,31 @@
-#pragma once
-#include "stream.h"
-#include "timebase.h"
-
-namespace MmtTlv {
-
- class NtpTimestamp {
- public:
- bool unpack(Common::ReadStream& stream);
- int64_t toPcrValue() const {
- const uint32_t NTP_1970 = 2208988800U;
- int64_t unixTimestamp = static_cast(((seconds - NTP_1970) * 1000.0) + ((fraction / 4294967296.0) * 1000.0));
-
- const AVRational ntpTimeBase = { 1, 1000 };
- const AVRational pcrTimeBase = { 1, 27000000 };
-
- return av_rescale_q(unixTimestamp, ntpTimeBase, pcrTimeBase);
- }
-
- public:
- uint32_t seconds;
- uint32_t fraction;
-
- };
-
- class NTPv4 {
- public:
- bool unpack(Common::ReadStream& stream);
-
- public:
- uint8_t leap_indicator;
- uint8_t version_number;
- uint8_t mode;
-
- uint8_t stratum;
- uint8_t poll_interval;
- uint8_t precision;
-
- uint32_t root_delay;
- uint32_t root_dispersion;
- uint32_t reference_id;
-
- NtpTimestamp reference_timestamp;
- NtpTimestamp origin_timestamp;
- NtpTimestamp receive_timestamp;
- NtpTimestamp transmit_timestamp;
-
- };
-
-}
\ No newline at end of file
+#pragma once
+#include "stream.h"
+#include "ntpTimestamp.h"
+
+namespace MmtTlv {
+
+class NTPv4 {
+public:
+ bool unpack(Common::ReadStream& stream);
+
+public:
+ uint8_t leap_indicator;
+ uint8_t version_number;
+ uint8_t mode;
+
+ uint8_t stratum;
+ uint8_t poll_interval;
+ uint8_t precision;
+
+ uint32_t root_delay;
+ uint32_t root_dispersion;
+ uint32_t reference_id;
+
+ NtpTimestamp reference_timestamp;
+ NtpTimestamp origin_timestamp;
+ NtpTimestamp receive_timestamp;
+ NtpTimestamp transmit_timestamp;
+
+};
+
+}
diff --git a/src/ntpTimestamp.cpp b/src/ntpTimestamp.cpp
new file mode 100644
index 0000000..340f812
--- /dev/null
+++ b/src/ntpTimestamp.cpp
@@ -0,0 +1,31 @@
+#include "ntpTimestamp.h"
+#include "timebase.h"
+
+namespace MmtTlv {
+
+bool NtpTimestamp::unpack(Common::ReadStream& stream) {
+ try {
+ seconds = stream.getBe32U();
+ fraction = stream.getBe32U();
+ }
+ catch (const std::out_of_range&) {
+ return false;
+ }
+
+ return true;
+}
+
+int64_t NtpTimestamp::toPcrValue() const {
+ constexpr int64_t NTP_1970 = 2208988800LL;
+ const int64_t unixTimestamp = (static_cast(seconds) - NTP_1970) * 1000 +
+ av_rescale(fraction, 1000, UINT64_C(1) << 32);
+ const AVRational ntpTimeBase = { 1, 1000 };
+ const AVRational pcrTimeBase = { 1, 27000000 };
+ return av_rescale_q(unixTimestamp, ntpTimeBase, pcrTimeBase);
+}
+
+int64_t NtpTimestamp::toPtsValue() const {
+ return toPcrValue() / 300;
+}
+
+}
diff --git a/src/ntpTimestamp.h b/src/ntpTimestamp.h
new file mode 100644
index 0000000..2783f4f
--- /dev/null
+++ b/src/ntpTimestamp.h
@@ -0,0 +1,17 @@
+#pragma once
+#include
+#include "stream.h"
+
+namespace MmtTlv {
+
+class NtpTimestamp {
+public:
+ bool unpack(Common::ReadStream& stream);
+ int64_t toPcrValue() const;
+ int64_t toPtsValue() const;
+
+ uint32_t seconds{};
+ uint32_t fraction{};
+};
+
+}
diff --git a/src/remuxerHandler.cpp b/src/remuxerHandler.cpp
index 62c4e81..4070e91 100644
--- a/src/remuxerHandler.cpp
+++ b/src/remuxerHandler.cpp
@@ -37,7 +37,10 @@
#include "timebase.h"
#include "config.h"
#include "ntp.h"
-#include "b24SubtitleConvertor.h"
+#include "b24SubtitleConverter.h"
+#include
+#include
+#include
namespace {
@@ -141,16 +144,49 @@ void RemuxerHandler::onAudioData(const MmtTlv::MmtStream& mmtStream, const MmtTl
}
void RemuxerHandler::onSubtitleData(const MmtTlv::MmtStream& mmtStream, const struct MmtTlv::MfuData& mfuData) {
+ const auto& subtitleInfo = mmtStream.additionalAribSubtitleInfo();
+ if (!subtitleInfo) {
+ return;
+ }
+
+ // ARIB-TTML
+ if (subtitleInfo->subtitleFormat != 0) {
+ return;
+ }
+
+ const bool synchronized = mmtStream.isClosedCaption();
+ if ((!synchronized && !mmtStream.isSuperimposition()) ||
+ (!synchronized && subtitleInfo->tmd != 0b1111)) {
+ return;
+ }
+
std::string ttml(mfuData.data.begin(), mfuData.data.end());
std::list output;
- B24SubtitleConvertor::convert(ttml, output);
+ const auto pesType = synchronized ? B24::PESData::PESType::Synchronized : B24::PESData::PESType::Asynchronous;
+ B24SubtitleConverter::convert(ttml, output, pesType, subtitleInfo->resolution);
if (output.empty()) {
return;
}
+ const auto currentPts = lastPcr / 300;
+ if (subtitleInfo->tmd == 0b1111) {
+ for (const auto& pesData : output) {
+ writeSubtitle(mmtStream, pesData, currentPts);
+ }
+ return;
+ }
+
+ const auto referencePts = subtitleInfo->referenceStartTime.toPtsValue();
for (const auto& pesData : output) {
- writeSubtitle(mmtStream, pesData);
+ if (!pesData.begin) {
+ continue;
+ }
+ const auto pts = referencePts + static_cast(*pesData.begin * 90);
+ if (pts < 0) {
+ continue;
+ }
+ writeSubtitle(mmtStream, pesData, std::max(static_cast(pts), currentPts));
}
}
@@ -284,23 +320,27 @@ void RemuxerHandler::writeStream(const MmtTlv::MmtStream& mmtStream, const MmtTl
}
}
-void RemuxerHandler::writeSubtitle(const MmtTlv::MmtStream& mmtStream, const B24SubtitleOutput& subtitle) {
- std::vector pesOutput;
+void RemuxerHandler::writeSubtitle(const MmtTlv::MmtStream& mmtStream, const B24SubtitleOutput& subtitle, std::optional pts) {
+ const auto& subtitleInfo = mmtStream.additionalAribSubtitleInfo();
+ if (!subtitleInfo) {
+ return;
+ }
+ const bool synchronized = mmtStream.isClosedCaption();
+ if (!synchronized && !mmtStream.isSuperimposition()) {
+ return;
+ }
+
+ std::vector pesOutput;
PESPacket pes;
- if (mmtStream.getComponentTag() == 0x30) {
- uint64_t pts = subtitle.calcPts(programStartTime);
- pts = std::max(pts, lastPcr / 300);
- if (pts == 0) {
- return;
- }
- pes.setPts(pts);
+ if (synchronized && pts) {
+ pes.setPts(*pts);
}
- pes.setStreamId(componentTagToStreamId(mmtStream.getComponentTag()));
+ pes.setStreamId(synchronized ? STREAM_ID_PRIVATE_STREAM_1 : STREAM_ID_PRIVATE_STREAM_2);
pes.setPayload(&subtitle.pesData);
pes.setPayloadLength(subtitle.pesData.size());
- if (mmtStream.getComponentTag() == 0x30) {
+ if (synchronized) {
pes.setPrivateData(&ccis);
pes.setStuffingByteLength(1);
}
@@ -355,43 +395,40 @@ void RemuxerHandler::writeCaptionManagementData(uint64_t pts) {
if (stream.second.getAssetType() != MmtTlv::AssetType::stpp) {
continue;
}
+ const auto& subtitleInfo = stream.second.additionalAribSubtitleInfo();
+ if (!subtitleInfo) {
+ continue;
+ }
B24::CaptionManagementData captionManagementData;
B24::CaptionManagementData::Language language;
- language.languageCode = "jpn";
+ language.languageCode = subtitleInfo->languageCode;
language.format = 0b1000;
- if (stream.second.getComponentTag() == 0x30) {
- language.dmf = 0b1010;
- }
- else {
- language.dmf = 0;
- }
+ language.dmf = subtitleInfo->dmf;
captionManagementData.languages.push_back(language);
B24::DataGroup dataGroup;
dataGroup.setGroupData(captionManagementData);
B24::PESData pesData(dataGroup);
- if (stream.second.getComponentTag() == 0x30) {
- pesData.SetPESType(B24::PESData::PESType::Synchronized);
- }
- else {
- pesData.SetPESType(B24::PESData::PESType::Asynchronous);
+ const bool synchronized = stream.second.isClosedCaption();
+ if (!synchronized && !stream.second.isSuperimposition()) {
+ continue;
}
+ pesData.SetPESType(synchronized ? B24::PESData::PESType::Synchronized : B24::PESData::PESType::Asynchronous);
std::vector packedPesData;
-
pesData.pack(packedPesData);
std::vector pesOutput;
PESPacket pes;
- if (stream.second.getComponentTag() == 0x30) {
+ if (synchronized) {
pes.setPts(lastCaptionManagementDataPts);
}
- pes.setStreamId(componentTagToStreamId(stream.second.getComponentTag()));
+ pes.setStreamId(synchronized ? STREAM_ID_PRIVATE_STREAM_1 : STREAM_ID_PRIVATE_STREAM_2);
pes.setPayload(&packedPesData);
pes.setPayloadLength(packedPesData.size());
- if (stream.second.getComponentTag() == 0x30) {
+ if (synchronized) {
pes.setPrivateData(&ccis);
pes.setStuffingByteLength(1);
}
@@ -553,13 +590,6 @@ void RemuxerHandler::onMhBit(const MmtTlv::MhBit& mhBit) {
void RemuxerHandler::onMhEit(const MmtTlv::MhEit& mhEit) {
tsid = mhEit.tlvStreamId;
- if (mhEit.isPf() && mhEit.sectionNumber == 0 && !mhEit.events.empty()) {
- uint64_t startTime{};
- if (EITConvertStartTimeToUnixTime((mhEit.events.begin())->get()->startTime, &startTime)) {
- programStartTime = startTime;
- }
- }
-
ts::EIT tsEit(true, mhEit.isPf(), 0, mhEit.versionNumber, true, mhEit.serviceId, mhEit.tlvStreamId, mhEit.originalNetworkId);
for (const auto& mhEvent : mhEit.events) {
ts::EIT::Event tsEvent(&tsEit);
@@ -877,12 +907,7 @@ void RemuxerHandler::onMpt(const MmtTlv::Mpt& mpt) {
else if (asset.assetType == MmtTlv::AssetType::stpp) {
ts::DataComponentDescriptor descriptor;
descriptor.data_component_id = 0x0008;
- if (mmtStream->getComponentTag() == 0x30) {
- descriptor.additional_data_component_info.push_back(0x3D);
- }
- else {
- descriptor.additional_data_component_info.push_back(0x3C);
- }
+ descriptor.additional_data_component_info.push_back(mmtStream->isClosedCaption() ? 0x3D : 0x3C);
stream.descs.add(duck, descriptor);
}
@@ -1087,7 +1112,8 @@ void RemuxerHandler::onNtp(const MmtTlv::NTPv4& ntp) {
lastPcr = ntp.transmit_timestamp.toPcrValue();
- writeCaptionManagementData(ntp.transmit_timestamp.toPcrValue() / 300);
+ const auto pts = static_cast(ntp.transmit_timestamp.toPtsValue());
+ writeCaptionManagementData(pts);
}
void RemuxerHandler::clear() {
@@ -1099,5 +1125,4 @@ void RemuxerHandler::clear() {
tsid = -1;
lastPcr = 0;
lastCaptionManagementDataPts = 0;
- programStartTime = 0;
}
diff --git a/src/remuxerHandler.h b/src/remuxerHandler.h
index c25be3f..0e84746 100644
--- a/src/remuxerHandler.h
+++ b/src/remuxerHandler.h
@@ -1,10 +1,11 @@
#pragma once
#include "demuxerHandler.h"
-#include "b24SubtitleConvertor.h"
-#include "damt.h"
-#include
-#include
-#include
+#include "b24SubtitleConverter.h"
+#include "damt.h"
+#include
+#include
+#include
+#include
namespace StreamType {
@@ -78,26 +79,25 @@ class RemuxerHandler : public MmtTlv::DemuxerHandler {
void clear();
private:
- enum class PesState {
- Init,
- InFragment,
- };
-
- void writeStream(const MmtTlv::MmtStream& mmtStream, const MmtTlv::MfuData& mfuData, const std::vector& data);
- void writeSubtitle(const MmtTlv::MmtStream& mmtStream, const B24SubtitleOutput& subtitle);
- void writeCaptionManagementData(uint64_t pts);
+ enum class PesState {
+ Init,
+ InFragment,
+ };
+ void writeStream(const MmtTlv::MmtStream& mmtStream, const MmtTlv::MfuData& mfuData, const std::vector& data);
+ void writeSubtitle(const MmtTlv::MmtStream& mmtStream, const B24SubtitleOutput& subtitle,
+ std::optional pts = std::nullopt);
+ void writeCaptionManagementData(uint64_t pts);
MmtTlv::MmtTlvDemuxer& demuxer;
OutputCallback outputCallback;
std::unordered_map mapService2Pid;
std::unordered_map mapCC;
std::unordered_map> mapPesPendingData;
- std::unordered_map mapPesPacketIndex;
- std::unordered_map mapPesState;
- int tsid{-1};
- uint64_t lastPcr{};
- uint64_t lastCaptionManagementDataPts{};
- uint64_t programStartTime{};
- inline static const std::vector ccis = { 0x43, 0x43, 0x49, 0x53, 0x01, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, };
+ std::unordered_map mapPesPacketIndex;
+ std::unordered_map mapPesState;
+ int tsid{-1};
+ uint64_t lastPcr{};
+ uint64_t lastCaptionManagementDataPts{};
+ inline static const std::vector ccis = { 0x43, 0x43, 0x49, 0x53, 0x01, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, };
ts::DuckContext duck;
-};
\ No newline at end of file
+};
diff --git a/src/ttml.cpp b/src/ttml.cpp
deleted file mode 100644
index 5bdb1e2..0000000
--- a/src/ttml.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-#include "ttml.h"
-#include "pugixml.hpp"
-
-namespace {
-
-uint64_t parseTimestamp(const std::string& timestamp) {
- size_t dotPos = timestamp.find('.');
- uint64_t hours, minutes, seconds, millis = 0;
-
- if (dotPos != std::string::npos) {
- if (timestamp.size() < 10) {
- throw std::invalid_argument("Invalid timestamp format: " + timestamp);
- }
- if (timestamp[2] != ':' || timestamp[5] != ':') {
- throw std::invalid_argument("Invalid timestamp format: " + timestamp);
- }
- hours = std::stoull(timestamp.substr(0, 2));
- minutes = std::stoull(timestamp.substr(3, 2));
- seconds = std::stoull(timestamp.substr(6, 2));
- std::string frac = timestamp.substr(dotPos + 1);
- if (frac.empty() || frac.size() > 3) {
- throw std::invalid_argument("Invalid fractional seconds in timestamp: " + timestamp);
- }
- if (frac.size() == 1) {
- millis = std::stoull(frac) * 100;
- }
- else if (frac.size() == 2) {
- millis = std::stoull(frac) * 10;
- }
- else {
- millis = std::stoull(frac);
- }
- }
- else {
- if (timestamp.size() != 8 || timestamp[2] != ':' || timestamp[5] != ':') {
- throw std::invalid_argument("Invalid timestamp format: " + timestamp);
- }
- hours = std::stoull(timestamp.substr(0, 2));
- minutes = std::stoull(timestamp.substr(3, 2));
- seconds = std::stoull(timestamp.substr(6, 2));
- }
-
- return hours * 3600 * 1000ULL + minutes * 60 * 1000ULL + seconds * 1000ULL + millis;
-}
-
-}
-
-TTML TTMLPaser::parse(const std::string& input) {
- TTML output;
- pugi::xml_document doc;
- pugi::xml_parse_result result = doc.load_buffer(input.data(), input.size());
- if (result.status != pugi::status_ok) {
- return {};
- }
-
- for (pugi::xml_node p : doc.child("tt").child("head").child("layout").children("region")) {
- TTMLRegion region;
- region.id = p.attribute("xml:id").value();
- if (p.attribute("tts:extent")) {
- region.extent = TTMLCssValueParser::parsePair(p.attribute("tts:extent").value());
- }
- if (p.attribute("tts:origin")) {
- region.origin = TTMLCssValueParser::parsePair(p.attribute("tts:origin").value());
- }
- output.regions.push_back(region);
- }
-
- for (pugi::xml_node p : doc.child("tt").child("head").child("styling").children("style")) {
- TTMLStyle style;
- style.id = p.attribute("xml:id").value();
- if (p.attribute("tts:fontSize")) {
- style.fontSize = TTMLCssValueParser::parsePair(p.attribute("tts:fontSize").value());
- }
- if (p.attribute("tts:lineHeight")) {
- style.lineHeight = TTMLCssValueParser::parse(p.attribute("tts:lineHeight").value());
- }
- if (p.attribute("tts:fontWeight")) {
- style.fontWeight = TTMLCssValueParser::parse(p.attribute("tts:fontWeight").value());
- }
- if (p.attribute("tts:fontStyle")) {
- style.fontStyle = TTMLCssValueParser::parse(p.attribute("tts:fontStyle").value());
- }
- if (p.attribute("tts:color")) {
- style.color = TTMLCssValueParser::parse(p.attribute("tts:color").value());
- }
- if (p.attribute("tts:backgroundColor")) {
- style.backgroundColor = TTMLCssValueParser::parse(p.attribute("tts:backgroundColor").value());
- }
-
- output.styles.push_back(style);
- }
-
- for (pugi::xml_node div : doc.child("tt").child("body").children("div")) {
- TTMLDivTag divTag;
- if (div.attribute("begin")) {
- divTag.begin = parseTimestamp(div.attribute("begin").value());
- }
- if (div.attribute("end")) {
- divTag.end = parseTimestamp(div.attribute("end").value());
- }
-
- for (pugi::xml_node p : div.children("p")) {
- std::string regionId = p.attribute("region").value();
- auto region = std::find_if(output.regions.begin(), output.regions.end(), [regionId](const TTMLRegion& r) {
- return r.id == regionId;
- });
-
- TTMLPTag pTag;
- pTag.id = p.attribute("xml:id").value();
- if (region != output.regions.end()) {
- pTag.region = *region;
- }
-
- for (pugi::xml_node span : p.children("span")) {
- TTMLSpanTag spanTag;
- spanTag.id = span.attribute("xml:id").value();
- spanTag.text = span.text().get();
-
- std::string styleStr = span.attribute("style").value();
- std::istringstream styleStream(styleStr);
- std::string styleId;
- while (styleStream >> styleId) {
- auto style = std::find_if(output.styles.begin(), output.styles.end(), [styleId](const TTMLStyle& s) {
- return s.id == styleId;
- });
- if (style != output.styles.end()) {
- if (style->fontSize.has_value()) {
- spanTag.style.fontSize = style->fontSize;
- }
- if (style->lineHeight.has_value()) {
- spanTag.style.lineHeight = style->lineHeight;
- }
- if (style->fontWeight.has_value()) {
- spanTag.style.fontWeight = style->fontWeight;
- }
- if (style->fontStyle.has_value()) {
- spanTag.style.fontStyle = style->fontStyle;
- }
- if (style->color.has_value()) {
- spanTag.style.color = style->color;
- }
- if (style->backgroundColor.has_value()) {
- spanTag.style.backgroundColor = style->backgroundColor;
- }
- }
- }
-
- pTag.spanTags.push_back(spanTag);
- }
-
- divTag.pTags.push_back(pTag);
- }
-
- output.divTags.push_back(divTag);
- }
-
- return output;
-}
-
-TTMLCssValue TTMLCssValueParser::parse(const std::string& input) {
- try {
- if (input.find("px") != std::string::npos || input.find("em") != std::string::npos ||
- input.find("rem") != std::string::npos || input.find("%") != std::string::npos) {
- std::regex regex(R"(^([+-]?\d*\.?\d+)(px|em|rem|%)$)");
- std::smatch match;
- if (std::regex_match(input, match, regex)) {
- return TTMLCssValue(TTMLCssValueLength(std::stof(match[1]), match[2]));
- }
- else {
- throw std::invalid_argument("Invalid length value: " + input);
- }
- }
-
- if (input.find("#") == 0) {
- return TTMLCssValue(TTMLCssValueColor(input));
- }
-
- static const std::set validKeywords = { "bold", "italic", "normal", "none" };
- if (validKeywords.find(input) != validKeywords.end()) {
- return TTMLCssValue(TTMLCssValueKeyword(input));
- }
-
- return TTMLCssValue(TTMLCssValueNumber(std::stof(input)));
-
- }
- catch (const std::invalid_argument& e) {
- std::cerr << e.what() << std::endl;
- throw;
- }
-}
-
-TTMLCssValuePair TTMLCssValueParser::parsePair(const std::string& input) {
- std::istringstream iss(input);
- std::string token1, token2;
- if (!(iss >> token1 >> token2)) {
- throw std::invalid_argument("Failed to parse TTML value pair from: " + input);
- }
- TTMLCssValue value1 = parse(token1);
- TTMLCssValue value2 = parse(token2);
- return std::make_pair(value1, value2);
-}
\ No newline at end of file
diff --git a/src/ttml.h b/src/ttml.h
deleted file mode 100644
index aabd147..0000000
--- a/src/ttml.h
+++ /dev/null
@@ -1,136 +0,0 @@
-#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-class TTMLCssValueLength {
-public:
- TTMLCssValueLength(float v, const std::string& u) : value(v), unit(u) {}
-
- float value;
- std::string unit;
-
-};
-
-class TTMLCssValueColor {
-public:
- TTMLCssValueColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {}
-
- TTMLCssValueColor(const std::string& hex) {
- if (hex.size() != 9 || hex[0] != '#') {
- throw std::invalid_argument("Invalid color format. Expected format: #RRGGBBAA");
- }
-
- unsigned long value = std::stoul(hex.substr(1), nullptr, 16);
- r = static_cast((value >> 24) & 0xFF);
- g = static_cast((value >> 16) & 0xFF);
- b = static_cast((value >> 8) & 0xFF);
- a = static_cast(value & 0xFF);
- }
-
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
-};
-
-class TTMLCssValueKeyword {
-public:
- TTMLCssValueKeyword(const std::string& k) : keyword(k) {}
-
- std::string keyword;
-};
-
-class TTMLCssValueNumber {
-public:
- TTMLCssValueNumber(float n) : number(n) {}
-
- float number;
-};
-
-class TTMLCssValue {
-public:
- using ValueType = std::variant;
-
-private:
- ValueType value;
-
-public:
- TTMLCssValue(TTMLCssValueLength length) : value(length) {}
- TTMLCssValue(TTMLCssValueColor color) : value(color) {}
- TTMLCssValue(TTMLCssValueKeyword keyword) : value(keyword) {}
- TTMLCssValue(TTMLCssValueNumber number) : value(number) {}
-
- template
- T getValue() const {
- return std::get(value);
- }
-
- template
- void setValue(const T& new_value) {
- value = new_value;
- }
-};
-
-using TTMLCssValuePair = std::pair;
-
-class TTMLCssValueParser {
-public:
- static TTMLCssValue parse(const std::string& input);
- static TTMLCssValuePair parsePair(const std::string& input);
-
-};
-
-struct TTMLRegion {
- std::string id;
- std::optional> extent;
- std::optional> origin;
-};
-
-struct TTMLStyle {
- std::string id;
- std::optional fontSize;
- std::optional lineHeight;
- std::optional fontWeight;
- std::optional fontStyle;
- std::optional color;
- std::optional backgroundColor;
-};
-
-struct TTMLSpanTag {
- std::string id;
- std::string text;
- TTMLStyle style;
-};
-
-struct TTMLPTag {
- std::string id;
- TTMLRegion region;
- std::list spanTags;
-};
-
-struct TTMLDivTag {
- std::optional begin;
- std::optional end;
- std::list pTags;
-};
-
-struct TTML {
- std::list divTags;
- std::list regions;
- std::list styles;
-
-};
-
-class TTMLPaser {
-public:
- static TTML parse(const std::string& input);
-
-};
\ No newline at end of file
diff --git a/src/ttml/ast.h b/src/ttml/ast.h
new file mode 100644
index 0000000..4729390
--- /dev/null
+++ b/src/ttml/ast.h
@@ -0,0 +1,65 @@
+#pragma once
+#include "ttml/style.h"
+#include
+#include
+#include
+#include
+#include
+
+namespace arib {
+
+namespace ttml {
+
+namespace ast {
+
+struct Timing {
+ std::optional begin;
+ std::optional end;
+};
+
+struct StyleDefinition {
+ std::string id;
+ std::vector style_refs;
+ StyleProperties style;
+};
+
+struct RegionDefinition {
+ std::string id;
+ std::vector style_refs;
+ StyleProperties style;
+};
+
+struct LineBreak {};
+using SpanContent = std::variant;
+
+struct Span {
+ std::optional id;
+ Timing timing;
+ std::optional region;
+ std::vector style_refs;
+ std::vector content;
+};
+
+struct Paragraph {
+ std::optional id;
+ Timing timing;
+ std::optional region;
+ std::vector spans;
+};
+
+struct Division {
+ Timing timing;
+ std::vector paragraphs;
+};
+
+struct Document {
+ std::vector styles;
+ std::vector regions;
+ std::optional division;
+};
+
+} // namespace ast
+
+} // namespace ttml
+
+} // namespace arib
diff --git a/src/ttml/b24_converter.cpp b/src/ttml/b24_converter.cpp
new file mode 100644
index 0000000..5ef7d36
--- /dev/null
+++ b/src/ttml/b24_converter.cpp
@@ -0,0 +1,818 @@
+#include "ttml/b24_converter.h"
+#include "aribTextEncoder.h"
+#include "b24Color.h"
+#include "b24ControlSet.h"
+#include
+#include
+#include
+#include
+#include
+#include